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

Commit 6c6d5a1

Browse files
feat: create test_analytics app and database (#474)
* feat: create test_analytics app and database - creates a new test_analytics django app - modifies the db_settings to declare a new test analytics database - modifies the db router to route the models from the new django app to the new database * test: fix tests * remove logs * fix dummy_settings * create migration for test_analytics app
1 parent d1762e3 commit 6c6d5a1

File tree

8 files changed

+187
-34
lines changed

8 files changed

+187
-34
lines changed

shared/django_apps/db_routers/__init__.py

Lines changed: 40 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -12,46 +12,52 @@ class MultiDatabaseRouter:
1212
"""
1313

1414
def db_for_read(self, model, **hints):
15-
if model._meta.app_label == "timeseries":
16-
if settings.TIMESERIES_DATABASE_READ_REPLICA_ENABLED:
17-
return "timeseries_read"
18-
else:
19-
return "timeseries"
20-
else:
21-
if settings.DATABASE_READ_REPLICA_ENABLED:
22-
return "default_read"
23-
else:
24-
return "default"
15+
match model._meta.app_label:
16+
case "timeseries":
17+
if settings.TIMESERIES_DATABASE_READ_REPLICA_ENABLED:
18+
return "timeseries_read"
19+
else:
20+
return "timeseries"
21+
case "test_analytics":
22+
return "test_analytics"
23+
case _:
24+
if settings.DATABASE_READ_REPLICA_ENABLED:
25+
return "default_read"
26+
else:
27+
return "default"
2528

2629
def db_for_write(self, model, **hints):
27-
if model._meta.app_label == "timeseries":
28-
return "timeseries"
29-
else:
30-
return "default"
30+
match model._meta.app_label:
31+
case "timeseries":
32+
return "timeseries"
33+
case "test_analytics":
34+
return "test_analytics"
35+
case _:
36+
return "default"
3137

3238
def allow_migrate(self, db, app_label, model_name=None, **hints):
33-
if (
34-
db == "timeseries" or db == "timeseries_read"
35-
) and not settings.TIMESERIES_ENABLED:
36-
log.warning("Skipping timeseries migration")
37-
return False
38-
if db == "default_read" or db == "timeseries_read":
39-
log.warning("Skipping migration of read-only database")
40-
return False
41-
if app_label == "timeseries":
42-
return db == "timeseries"
43-
else:
44-
return db == "default"
39+
match db:
40+
case "timeseries_read" | "test_analytics_read" | "default_read":
41+
return False
42+
case "timeseries":
43+
if not settings.TIMESERIES_ENABLED:
44+
return False
45+
return app_label == "timeseries"
46+
case "test_analytics":
47+
if not settings.TEST_ANALYTICS_DATABASE_ENABLED:
48+
return False
49+
return app_label == "test_analytics"
50+
case _:
51+
return app_label not in {"timeseries", "test_analytics"}
4552

4653
def allow_relation(self, obj1, obj2, **hints):
4754
obj1_app = obj1._meta.app_label
4855
obj2_app = obj2._meta.app_label
4956

50-
# cannot form relationship across default <-> timeseries dbs
51-
if obj1_app == "timeseries" and obj2_app != "timeseries":
52-
return False
53-
if obj1_app != "timeseries" and obj2_app == "timeseries":
54-
return False
55-
56-
# otherwise we allow it
57-
return True
57+
if obj1_app in {"timeseries", "test_analytics"} or obj2_app in {
58+
"timeseries",
59+
"test_analytics",
60+
}:
61+
return obj1_app == obj2_app
62+
else:
63+
return True

shared/django_apps/db_settings.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,48 @@
158158
"CONN_MAX_AGE": CONN_MAX_AGE,
159159
}
160160

161+
TEST_ANALYTICS_DATABASE_ENABLED = get_config(
162+
"setup", "test_analytics_database", "enabled", default=True
163+
)
164+
165+
test_analytics_database_url = get_config("services", "test_analytics_database_url")
166+
if test_analytics_database_url:
167+
test_analytics_database_conf = urlparse(test_analytics_database_url)
168+
TEST_ANALYTICS_DATABASE_NAME = test_analytics_database_conf.path.replace("/", "")
169+
TEST_ANALYTICS_DATABASE_USER = test_analytics_database_conf.username
170+
TEST_ANALYTICS_DATABASE_PASSWORD = test_analytics_database_conf.password
171+
TEST_ANALYTICS_DATABASE_HOST = test_analytics_database_conf.hostname
172+
TEST_ANALYTICS_DATABASE_PORT = test_analytics_database_conf.port
173+
else:
174+
TEST_ANALYTICS_DATABASE_NAME = get_config(
175+
"services", "test_analytics_database", "name", default="test_analytics"
176+
)
177+
TEST_ANALYTICS_DATABASE_USER = get_config(
178+
"services", "test_analytics_database", "username", default="postgres"
179+
)
180+
TEST_ANALYTICS_DATABASE_PASSWORD = get_config(
181+
"services", "test_analytics_database", "password", default="postgres"
182+
)
183+
TEST_ANALYTICS_DATABASE_HOST = get_config(
184+
"services", "test_analytics_database", "host", default="postgres"
185+
)
186+
TEST_ANALYTICS_DATABASE_PORT = get_config(
187+
"services", "test_analytics_database", "port", default=5432
188+
)
189+
190+
191+
if TEST_ANALYTICS_DATABASE_ENABLED:
192+
DATABASES["test_analytics"] = {
193+
"ENGINE": "psqlextra.backend",
194+
"NAME": TEST_ANALYTICS_DATABASE_NAME,
195+
"USER": TEST_ANALYTICS_DATABASE_USER,
196+
"PASSWORD": TEST_ANALYTICS_DATABASE_PASSWORD,
197+
"HOST": TEST_ANALYTICS_DATABASE_HOST,
198+
"PORT": TEST_ANALYTICS_DATABASE_PORT,
199+
"CONN_MAX_AGE": CONN_MAX_AGE,
200+
}
201+
202+
161203
# See https://django-postgres-extra.readthedocs.io/en/main/settings.html
162204
POSTGRES_EXTRA_DB_BACKEND_BASE: "django_prometheus.db.backends.postgresql" # type: ignore
163205

shared/django_apps/dummy_settings.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
"shared.django_apps.profiling",
3434
"shared.django_apps.reports",
3535
"shared.django_apps.staticanalysis",
36+
"shared.django_apps.test_analytics",
3637
]
3738

3839
# Needed for makemigrations to work
@@ -90,6 +91,14 @@
9091
"HOST": "timescale",
9192
"PORT": 5432,
9293
},
94+
"test_analytics": {
95+
"ENGINE": "psqlextra.backend",
96+
"NAME": "test_analytics",
97+
"USER": "postgres",
98+
"PASSWORD": "postgres",
99+
"HOST": "postgres",
100+
"PORT": 5432,
101+
},
93102
}
94103

95104
# Password validation

shared/django_apps/test_analytics/__init__.py

Whitespace-only changes.
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Generated by Django 4.2.16 on 2025-01-17 22:07
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
initial = True
8+
9+
dependencies = []
10+
11+
operations = [
12+
migrations.CreateModel(
13+
name="Flake",
14+
fields=[
15+
("id", models.BigAutoField(primary_key=True, serialize=False)),
16+
("repoid", models.IntegerField()),
17+
("test_id", models.BinaryField()),
18+
("recent_passes_count", models.IntegerField()),
19+
("count", models.IntegerField()),
20+
("fail_count", models.IntegerField()),
21+
("start_date", models.DateTimeField()),
22+
("end_date", models.DateTimeField(null=True)),
23+
],
24+
options={
25+
"db_table": "test_analytics_flake",
26+
"indexes": [
27+
models.Index(
28+
fields=["repoid"], name="test_analyt_repoid_fcd881_idx"
29+
),
30+
models.Index(
31+
fields=["test_id"], name="test_analyt_test_id_f504a1_idx"
32+
),
33+
models.Index(
34+
fields=["repoid", "test_id"],
35+
name="test_analyt_repoid_0690c3_idx",
36+
),
37+
models.Index(
38+
fields=["repoid", "end_date"],
39+
name="test_analyt_repoid_9e2402_idx",
40+
),
41+
],
42+
},
43+
),
44+
]

shared/django_apps/test_analytics/migrations/__init__.py

Whitespace-only changes.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
from django.db import models
2+
3+
# Added to avoid 'doesn't declare an explicit app_label and isn't in an application in INSTALLED_APPS' error\
4+
# Needs to be called the same as the API app
5+
TEST_ANALYTICS_APP_LABEL = "test_analytics"
6+
7+
8+
class Flake(models.Model):
9+
id = models.BigAutoField(primary_key=True)
10+
11+
repoid = models.IntegerField()
12+
test_id = models.BinaryField()
13+
14+
recent_passes_count = models.IntegerField()
15+
count = models.IntegerField()
16+
fail_count = models.IntegerField()
17+
start_date = models.DateTimeField()
18+
end_date = models.DateTimeField(null=True)
19+
20+
class Meta:
21+
app_label = TEST_ANALYTICS_APP_LABEL
22+
db_table = "test_analytics_flake"
23+
indexes = [
24+
models.Index(fields=["repoid"]),
25+
models.Index(fields=["test_id"]),
26+
models.Index(fields=["repoid", "test_id"]),
27+
models.Index(fields=["repoid", "end_date"]),
28+
]

tests/unit/django_apps/test_db_routers.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,30 @@ def test_allow_migrate_timeseries_disabled(self):
6565
assert router.allow_migrate("default", "timeseries") == False
6666
assert router.allow_migrate("default_read", "timeseries") == False
6767

68+
@override_settings(TEST_ANALYTICS_DATABASE_ENABLED=False)
69+
def test_allow_migrate_test_analytics_disabled(self):
70+
router = MultiDatabaseRouter()
71+
assert router.allow_migrate("test_analytics", "test_analytics") == False
72+
assert router.allow_migrate("test_analytics_read", "test_analytics") == False
73+
assert router.allow_migrate("test_analytics", "default") == False
74+
assert router.allow_migrate("test_analytics_read", "default") == False
75+
assert router.allow_migrate("default", "default") == True
76+
assert router.allow_migrate("default_read", "default") == False
77+
assert router.allow_migrate("default", "test_analytics") == False
78+
assert router.allow_migrate("default_read", "test_analytics") == False
79+
80+
@override_settings(TEST_ANALYTICS_DATABASE_ENABLED=True)
81+
def test_allow_migrate_test_analytics_enabled(self):
82+
router = MultiDatabaseRouter()
83+
assert router.allow_migrate("test_analytics", "test_analytics") == True
84+
assert router.allow_migrate("test_analytics_read", "test_analytics") == False
85+
assert router.allow_migrate("test_analytics", "default") == False
86+
assert router.allow_migrate("test_analytics_read", "default") == False
87+
assert router.allow_migrate("default", "default") == True
88+
assert router.allow_migrate("default_read", "default") == False
89+
assert router.allow_migrate("default", "test_analytics") == False
90+
assert router.allow_migrate("default_read", "test_analytics") == False
91+
6892
def test_allow_relation(self, mocker):
6993
# At time of writing, the Django timeseries models don't live in this
7094
# repo so we're pretending a different model is from the timeseries app

0 commit comments

Comments
 (0)