Skip to content
This repository was archived by the owner on May 5, 2025. It is now read-only.

Commit acca45b

Browse files
Run django db migrations for tests (#729)
1 parent 1e2c27a commit acca45b

33 files changed

+369
-253
lines changed

conftest.py

Lines changed: 76 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,8 @@
99
from shared.storage.memory import MemoryStorageService
1010
from shared.torngit import Github as GithubHandler
1111
from sqlalchemy import event
12-
from sqlalchemy.exc import OperationalError
1312
from sqlalchemy.orm import Session
14-
from sqlalchemy_utils import create_database, database_exists
13+
from sqlalchemy_utils import database_exists
1514

1615
from celery_config import initialize_logging
1716
from database.base import Base
@@ -40,7 +39,7 @@ def pytest_itemcollected(item):
4039

4140

4241
@pytest.fixture(scope="session")
43-
def engine(request, sqlalchemy_connect_url, app_config):
42+
def engine(request, sqlalchemy_db, sqlalchemy_connect_url, app_config):
4443
"""Engine configuration.
4544
See http://docs.sqlalchemy.org/en/latest/core/engines.html
4645
for more details.
@@ -67,30 +66,87 @@ def engine(request, sqlalchemy_connect_url, app_config):
6766
engine.url.database = "{}_{}".format(engine.url.database, xdist_suffix)
6867
engine = create_engine(engine.url) # override engine
6968

70-
def fin():
71-
print("Disposing engine")
72-
engine.dispose()
69+
# Check that the DB exist and migrate the unmigrated SQLALchemy models as a stop-gap
70+
database_url = sqlalchemy_connect_url
71+
if not database_exists(database_url):
72+
raise RuntimeError(f"SQLAlchemy cannot connect to DB at {database_url}")
73+
74+
Base.metadata.tables["profiling_profilingcommit"].create(bind=engine)
75+
Base.metadata.tables["profiling_profilingupload"].create(bind=engine)
76+
Base.metadata.tables["timeseries_measurement"].create(bind=engine)
77+
Base.metadata.tables["timeseries_dataset"].create(bind=engine)
78+
79+
Base.metadata.tables["compare_commitcomparison"].create(bind=engine)
80+
Base.metadata.tables["compare_flagcomparison"].create(bind=engine)
81+
Base.metadata.tables["compare_componentcomparison"].create(bind=engine)
82+
83+
Base.metadata.tables["labelanalysis_labelanalysisrequest"].create(bind=engine)
84+
Base.metadata.tables["labelanalysis_labelanalysisprocessingerror"].create(
85+
bind=engine
86+
)
87+
88+
Base.metadata.tables["staticanalysis_staticanalysissuite"].create(bind=engine)
89+
Base.metadata.tables["staticanalysis_staticanalysissinglefilesnapshot"].create(
90+
bind=engine
91+
)
92+
Base.metadata.tables["staticanalysis_staticanalysissuitefilepath"].create(
93+
bind=engine
94+
)
95+
96+
yield engine
7397

74-
request.addfinalizer(fin)
75-
return engine
98+
print("Disposing engine")
99+
engine.dispose()
76100

77101

78102
@pytest.fixture(scope="session")
79-
def db(engine, sqlalchemy_connect_url):
80-
database_url = sqlalchemy_connect_url
103+
def sqlalchemy_db(request: pytest.FixtureRequest, django_db_blocker, django_db_setup):
104+
# Bootstrap the DB by running the Django bootstrap version.
105+
from django.conf import settings
106+
from django.db import connections
107+
from django.test.utils import setup_databases, teardown_databases
108+
109+
with django_db_blocker.unblock():
110+
# Temporarily reset the database to the SQLAlchemy DBs to run the migrations.
111+
original_test_name = settings.DATABASES["default"]["TEST"]["NAME"]
112+
settings.DATABASES["default"]["TEST"]["NAME"] = "test_postgres_sqlalchemy"
113+
db_cfg = setup_databases(
114+
verbosity=request.config.option.verbose,
115+
interactive=False,
116+
)
117+
settings.DATABASES["default"]["TEST"]["NAME"] = original_test_name
118+
119+
# Hack to get the default connection for the test database to _actually_ be the
120+
# Django database that the django_db should actually use. It was set to the SQLAlchemy database,
121+
# but this makes sure that the default Django DB connection goes to the Django database.
122+
# Since the database was already created and migrated in the django_db_setup fixture,
123+
# we set keepdb=True to avoid recreating the database and rerunning the migrations.
124+
connections.configure_settings(settings.DATABASES)
125+
connections["default"].creation.create_test_db(
126+
verbosity=request.config.option.verbose,
127+
autoclobber=True,
128+
keepdb=True,
129+
)
130+
131+
yield
132+
133+
# Cleanup with Django version as well
81134
try:
82-
if not database_exists(database_url):
83-
create_database(database_url)
84-
except OperationalError:
85-
pytest.skip("No available db")
86-
connection = engine.connect()
87-
connection.execute("DROP SCHEMA IF EXISTS public CASCADE;")
88-
connection.execute("CREATE SCHEMA public;")
89-
Base.metadata.create_all(engine)
135+
with django_db_blocker.unblock():
136+
settings.DATABASES["default"]["TEST"]["NAME"] = "test_postgres_sqlalchemy"
137+
teardown_databases(db_cfg, verbosity=request.config.option.verbose)
138+
settings.DATABASES["default"]["TEST"]["NAME"] = original_test_name
139+
except Exception as exc: # noqa: BLE001
140+
request.node.warn(
141+
pytest.PytestWarning(
142+
f"Error when trying to teardown test databases: {exc!r}"
143+
)
144+
)
90145

91146

92147
@pytest.fixture
93-
def dbsession(db, engine):
148+
def dbsession(sqlalchemy_db, engine):
149+
"""Sets up the SQLAlchemy dbsession."""
94150
connection = engine.connect()
95151

96152
connection_transaction = connection.begin()
@@ -239,7 +295,7 @@ def side_effect(*args, **kwargs):
239295
@pytest.fixture
240296
def with_sql_functions(dbsession):
241297
dbsession.execute(
242-
"""CREATE FUNCTION array_append_unique(anyarray, anyelement) RETURNS anyarray
298+
"""CREATE OR REPLACE FUNCTION array_append_unique(anyarray, anyelement) RETURNS anyarray
243299
LANGUAGE sql IMMUTABLE
244300
AS $_$
245301
select case when $2 is null

database/tests/factories/core.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from datetime import datetime
1+
from datetime import datetime, timezone
22
from hashlib import sha1
33
from uuid import uuid4
44

@@ -71,9 +71,11 @@ class Meta:
7171

7272
owner = factory.SubFactory(OwnerFactory)
7373
bot = None
74-
updatestamp = factory.LazyAttribute(lambda o: datetime.now())
74+
updatestamp = factory.LazyAttribute(lambda o: datetime.now(tz=timezone.utc))
7575
languages = []
76-
languages_last_updated = factory.LazyAttribute(lambda o: datetime.now())
76+
languages_last_updated = factory.LazyAttribute(
77+
lambda o: datetime.now(tz=timezone.utc)
78+
)
7779
bundle_analysis_enabled = False
7880

7981

@@ -113,7 +115,7 @@ class Meta:
113115
)
114116
ci_passed = True
115117
pullid = None
116-
timestamp = datetime(2019, 2, 1, 17, 59, 47)
118+
timestamp = datetime(2019, 2, 1, 17, 59, 47, tzinfo=timezone.utc)
117119
author = factory.SubFactory(OwnerFactory)
118120
repository = factory.SubFactory(RepositoryFactory)
119121
totals = factory.LazyFunction(

database/tests/unit/test_model_utils.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,14 +56,14 @@ def test_subclass_validation(self, mocker):
5656
self.ClassWithArchiveFieldMissingMethods, ArchiveFieldInterface
5757
)
5858

59-
def test_archive_getter_db_field_set(self, db):
59+
def test_archive_getter_db_field_set(self, sqlalchemy_db):
6060
commit = CommitFactory()
6161
test_class = self.ClassWithArchiveField(commit, "db_value", "gcs_path")
6262
assert test_class._archive_field == "db_value"
6363
assert test_class._archive_field_storage_path == "gcs_path"
6464
assert test_class.archive_field == "db_value"
6565

66-
def test_archive_getter_archive_field_set(self, db, mocker):
66+
def test_archive_getter_archive_field_set(self, sqlalchemy_db, mocker):
6767
some_json = {"some": "data"}
6868
mock_read_file = mocker.MagicMock(return_value=json.dumps(some_json))
6969
mock_archive_service = mocker.patch("database.utils.ArchiveService")
@@ -81,7 +81,7 @@ def test_archive_getter_archive_field_set(self, db, mocker):
8181
assert test_class.archive_field == some_json
8282
assert mock_read_file.call_count == 1
8383

84-
def test_archive_getter_file_not_in_storage(self, db, mocker):
84+
def test_archive_getter_file_not_in_storage(self, sqlalchemy_db, mocker):
8585
mocker.patch(
8686
"database.utils.ArchiveField.read_timeout",
8787
new_callable=PropertyMock,
@@ -99,7 +99,7 @@ def test_archive_getter_file_not_in_storage(self, db, mocker):
9999
mock_read_file.assert_called_with("gcs_path")
100100
mock_archive_service.assert_called_with(repository=commit.repository)
101101

102-
def test_archive_setter_db_field(self, db, mocker):
102+
def test_archive_setter_db_field(self, sqlalchemy_db, mocker):
103103
commit = CommitFactory()
104104
test_class = self.ClassWithArchiveField(commit, "db_value", "gcs_path", False)
105105
assert test_class._archive_field == "db_value"
@@ -111,7 +111,7 @@ def test_archive_setter_db_field(self, db, mocker):
111111
assert test_class._archive_field == "batata frita"
112112
assert test_class.archive_field == "batata frita"
113113

114-
def test_archive_setter_archive_field(self, db, mocker):
114+
def test_archive_setter_archive_field(self, sqlalchemy_db, mocker):
115115
commit = CommitFactory()
116116
test_class = self.ClassWithArchiveField(commit, "db_value", None, True)
117117
some_json = {"some": "data"}

django_scaffold/tests_settings.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,5 @@
2222
},
2323
}
2424
]
25+
26+
DATABASES["default"]["TEST"] = {"NAME": "test_postgres_django"}

helpers/tests/unit/test_github_installation.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ def test_get_installation_name_for_owner_for_task(dbsession: Session):
1818
installation_name="my_installation",
1919
task_name=task_name,
2020
)
21-
dbsession.add_all([owner, installation_task_config])
21+
dbsession.add_all([owner, other_owner, installation_task_config])
2222
dbsession.flush()
2323
assert (
2424
get_installation_name_for_owner_for_task(task_name, owner) == "my_installation"
Lines changed: 18 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -1,85 +1,17 @@
11
import datetime as dt
22

33
import pytest
4-
import time_machine
5-
from shared.django_apps.core.tests.factories import RepositoryFactory
64
from shared.django_apps.reports.models import DailyTestRollup
7-
from shared.django_apps.reports.tests.factories import (
8-
DailyTestRollupFactory,
9-
FlakeFactory,
10-
TestFactory,
11-
TestInstanceFactory,
12-
)
135

146
from one_off_scripts.backfill_daily_test_rollups import backfill_test_rollups
15-
16-
17-
@pytest.fixture
18-
def setup_tests(transactional_db):
19-
repo_1 = RepositoryFactory(test_analytics_enabled=True)
20-
21-
test_1 = TestFactory(repository=repo_1)
22-
23-
_ = FlakeFactory(
24-
test=test_1,
25-
repository=repo_1,
26-
start_date=dt.datetime.fromisoformat("1970-01-02T00:00:00Z"),
27-
end_date=dt.datetime.fromisoformat("1970-01-04T00:00:00Z"),
28-
)
29-
30-
_ = FlakeFactory(
31-
test=test_1,
32-
repository=repo_1,
33-
start_date=dt.datetime.fromisoformat("1970-01-04T12:00:00Z"),
34-
end_date=None,
35-
)
36-
37-
traveller = time_machine.travel("1970-01-01T00:00:00Z", tick=False)
38-
traveller.start()
39-
ti = TestInstanceFactory(test=test_1, duration_seconds=10.0)
40-
traveller.stop()
41-
42-
traveller = time_machine.travel("1970-01-03T00:00:00Z", tick=False)
43-
traveller.start()
44-
ti = TestInstanceFactory(test=test_1, duration_seconds=10.0)
45-
traveller.stop()
46-
47-
traveller = time_machine.travel("1970-01-05T00:00:00Z", tick=False)
48-
traveller.start()
49-
ti = TestInstanceFactory(
50-
test=test_1,
51-
duration_seconds=10000.0,
52-
)
53-
traveller.stop()
54-
55-
_ = DailyTestRollupFactory(
56-
test=test_1,
57-
date=dt.date.fromisoformat("1970-01-03"),
58-
fail_count=10,
59-
pass_count=5,
60-
last_duration_seconds=10.0,
61-
avg_duration_seconds=1.0,
62-
latest_run=dt.datetime.fromisoformat("1970-01-01T00:00:00Z"),
63-
)
64-
65-
_ = DailyTestRollupFactory(
66-
test=test_1,
67-
date=dt.date.fromisoformat("1970-01-05"),
68-
fail_count=10,
69-
pass_count=5,
70-
last_duration_seconds=10.0,
71-
avg_duration_seconds=1.0,
72-
latest_run=dt.datetime.fromisoformat("1970-01-01T00:00:00Z"),
73-
)
74-
75-
_ = RepositoryFactory(test_analytics_enabled=False)
76-
77-
return repo_1
7+
from one_off_scripts.tests.utils import setup_one_off_tests
788

799

8010
@pytest.mark.django_db(transaction=True)
81-
def test_backfill_test_rollups(setup_tests):
82-
rollups = DailyTestRollup.objects.all()
11+
def test_backfill_test_rollups():
12+
setup_one_off_tests()
13+
14+
rollups = DailyTestRollup.objects.all().order_by("date")
8315
assert [
8416
{
8517
"date": r.date,
@@ -123,7 +55,7 @@ def test_backfill_test_rollups(setup_tests):
12355
end_date="1970-01-04",
12456
)
12557

126-
rollups = DailyTestRollup.objects.all()
58+
rollups = DailyTestRollup.objects.all().order_by("date")
12759

12860
assert [
12961
{
@@ -138,16 +70,6 @@ def test_backfill_test_rollups(setup_tests):
13870
}
13971
for r in rollups
14072
] == [
141-
{
142-
"avg_duration_seconds": 1.0,
143-
"date": dt.date(1970, 1, 5),
144-
"fail_count": 10,
145-
"flaky_fail_count": 0,
146-
"last_duration_seconds": 10.0,
147-
"latest_run": dt.datetime.fromisoformat("1970-01-01T00:00:00Z"),
148-
"pass_count": 5,
149-
"skip_count": 0,
150-
},
15173
{
15274
"avg_duration_seconds": 10.0,
15375
"date": dt.date(1970, 1, 3),
@@ -158,16 +80,26 @@ def test_backfill_test_rollups(setup_tests):
15880
"pass_count": 0,
15981
"skip_count": 0,
16082
},
83+
{
84+
"avg_duration_seconds": 1.0,
85+
"date": dt.date(1970, 1, 5),
86+
"fail_count": 10,
87+
"flaky_fail_count": 0,
88+
"last_duration_seconds": 10.0,
89+
"latest_run": dt.datetime.fromisoformat("1970-01-01T00:00:00Z"),
90+
"pass_count": 5,
91+
"skip_count": 0,
92+
},
16193
]
16294

163-
assert len(rollups[1].commits_where_fail) == 1
95+
assert len(rollups[0].commits_where_fail) == 1
16496

16597
backfill_test_rollups(
16698
start_date="1970-01-02",
16799
end_date="1970-01-05",
168100
)
169101

170-
rollups = DailyTestRollup.objects.all()
102+
rollups = DailyTestRollup.objects.all().order_by("date")
171103

172104
assert [
173105
{
@@ -203,6 +135,5 @@ def test_backfill_test_rollups(setup_tests):
203135
"skip_count": 0,
204136
},
205137
]
206-
207138
for r in rollups:
208139
assert len(r.commits_where_fail) == 1

0 commit comments

Comments
 (0)