Skip to content

Commit 8b8fe7a

Browse files
authored
Add tests for additional Django database interactions (#2)
* add tests for basic model CRUD operations * add migration tests * add tests for db operations like joins, aggregations, and more
1 parent afb8071 commit 8b8fe7a

File tree

3 files changed

+399
-0
lines changed

3 files changed

+399
-0
lines changed

tests/test_integration.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import pytest
22
from django.contrib.auth.models import User
33
from django.contrib.contenttypes.models import ContentType
4+
from django.db.models.sql import DeleteQuery, InsertQuery, UpdateQuery
45

56
from django_fqq.schema import clear_schema, set_schema
67

@@ -42,6 +43,56 @@ def test_schema_switching_between_queries():
4243
assert '"beta"."django_content_type"' in sql_beta
4344

4445

46+
def test_delete_query_has_schema():
47+
set_schema("tenant1")
48+
dq = DeleteQuery(ContentType)
49+
sql = str(dq)
50+
assert '"tenant1"."django_content_type"' in sql
51+
52+
53+
def test_delete_query_no_schema():
54+
dq = DeleteQuery(ContentType)
55+
sql = str(dq)
56+
assert '"django_content_type"' in sql
57+
assert '"public"."django_content_type"' not in sql
58+
59+
60+
def test_update_query_has_schema():
61+
set_schema("tenant1")
62+
uq = UpdateQuery(ContentType)
63+
uq.add_update_values({"app_label": "test"})
64+
sql = str(uq)
65+
assert '"tenant1"."django_content_type"' in sql
66+
67+
68+
def test_update_query_no_schema():
69+
uq = UpdateQuery(ContentType)
70+
uq.add_update_values({"app_label": "test"})
71+
sql = str(uq)
72+
assert '"django_content_type"' in sql
73+
assert '"public"."django_content_type"' not in sql
74+
75+
76+
def test_insert_query_has_schema():
77+
set_schema("tenant1")
78+
iq = InsertQuery(ContentType)
79+
obj = ContentType(app_label="test", model="testmodel")
80+
iq.insert_values(ContentType._meta.local_concrete_fields, [obj])
81+
compiler = iq.get_compiler("default")
82+
sql, _ = compiler.as_sql()[0]
83+
assert '"tenant1"."django_content_type"' in sql
84+
85+
86+
def test_insert_query_no_schema():
87+
iq = InsertQuery(ContentType)
88+
obj = ContentType(app_label="test", model="testmodel")
89+
iq.insert_values(ContentType._meta.local_concrete_fields, [obj])
90+
compiler = iq.get_compiler("default")
91+
sql, _ = compiler.as_sql()[0]
92+
assert '"django_content_type"' in sql
93+
assert '"public"."django_content_type"' not in sql
94+
95+
4596
# --- Shared app: auth ---
4697

4798

@@ -59,3 +110,28 @@ def test_shared_app_ignores_schema_switch():
59110
set_schema("beta")
60111
sql_beta = str(User.objects.all().query)
61112
assert '"public"."auth_user"' in sql_beta
113+
114+
115+
def test_shared_delete_uses_public_schema():
116+
set_schema("tenant1")
117+
dq = DeleteQuery(User)
118+
sql = str(dq)
119+
assert '"public"."auth_user"' in sql
120+
121+
122+
def test_shared_update_uses_public_schema():
123+
set_schema("tenant1")
124+
uq = UpdateQuery(User)
125+
uq.add_update_values({"username": "test"})
126+
sql = str(uq)
127+
assert '"public"."auth_user"' in sql
128+
129+
130+
def test_shared_insert_uses_public_schema():
131+
set_schema("tenant1")
132+
iq = InsertQuery(User)
133+
obj = User(username="test")
134+
iq.insert_values(User._meta.local_concrete_fields, [obj])
135+
compiler = iq.get_compiler("default")
136+
sql, _ = compiler.as_sql()[0]
137+
assert '"public"."auth_user"' in sql

tests/test_migrations.py

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
from unittest.mock import patch
2+
3+
import pytest
4+
from django.db import connection, models
5+
from django.db.models import Index
6+
7+
from django_fqq.backend.operations import DatabaseOperations
8+
from django_fqq.schema import clear_schema, set_schema
9+
10+
11+
class TenantModel(models.Model):
12+
name = models.CharField(max_length=100)
13+
14+
class Meta:
15+
app_label = "testapp"
16+
db_table = "testapp_tenantmodel"
17+
18+
19+
class TenantModelWithAge(models.Model):
20+
name = models.CharField(max_length=100)
21+
age = models.IntegerField(default=0)
22+
23+
class Meta:
24+
app_label = "testapp"
25+
db_table = "testapp_tenantmodel"
26+
27+
28+
@pytest.fixture(autouse=True)
29+
def schema_cleanup():
30+
yield
31+
clear_schema()
32+
33+
34+
@pytest.fixture(autouse=True)
35+
def register_table():
36+
DatabaseOperations._table_names.add("testapp_tenantmodel")
37+
yield
38+
DatabaseOperations._table_names.discard("testapp_tenantmodel")
39+
DatabaseOperations._table_names.discard("testapp_newname")
40+
41+
42+
def _noop_compose_sql(sql, params, connection):
43+
"""Substitute params into SQL without needing a real DB cursor."""
44+
if params:
45+
return sql % tuple(repr(p) for p in params)
46+
return sql
47+
48+
49+
def _collect_sql(fn):
50+
"""Run schema editor operation and return collected SQL statements."""
51+
with patch(
52+
"django.db.backends.postgresql.operations.mogrify", side_effect=_noop_compose_sql
53+
):
54+
editor = connection.schema_editor(collect_sql=True, atomic=False)
55+
with editor:
56+
fn(editor)
57+
return editor.collected_sql
58+
59+
60+
# --- CREATE TABLE ---
61+
62+
63+
def test_create_table_has_schema():
64+
set_schema("tenant1")
65+
sqls = _collect_sql(lambda e: e.create_model(TenantModel))
66+
assert any('"tenant1"."testapp_tenantmodel"' in s for s in sqls)
67+
68+
69+
def test_create_table_no_schema():
70+
sqls = _collect_sql(lambda e: e.create_model(TenantModel))
71+
assert any('"testapp_tenantmodel"' in s for s in sqls)
72+
assert not any('"public"."testapp_tenantmodel"' in s for s in sqls)
73+
74+
75+
# --- DROP TABLE ---
76+
77+
78+
def test_drop_table_has_schema():
79+
set_schema("tenant1")
80+
sqls = _collect_sql(lambda e: e.delete_model(TenantModel))
81+
assert any('"tenant1"."testapp_tenantmodel"' in s for s in sqls)
82+
83+
84+
def test_drop_table_no_schema():
85+
sqls = _collect_sql(lambda e: e.delete_model(TenantModel))
86+
assert any('"testapp_tenantmodel"' in s for s in sqls)
87+
assert not any('"public"."testapp_tenantmodel"' in s for s in sqls)
88+
89+
90+
# --- ADD COLUMN ---
91+
92+
93+
def test_add_column_has_schema():
94+
set_schema("tenant1")
95+
field = TenantModelWithAge._meta.get_field("age")
96+
sqls = _collect_sql(lambda e: e.add_field(TenantModelWithAge, field))
97+
assert any('"tenant1"."testapp_tenantmodel"' in s for s in sqls)
98+
99+
100+
def test_add_column_no_schema():
101+
field = TenantModelWithAge._meta.get_field("age")
102+
sqls = _collect_sql(lambda e: e.add_field(TenantModelWithAge, field))
103+
assert any('"testapp_tenantmodel"' in s for s in sqls)
104+
assert not any('"public"."testapp_tenantmodel"' in s for s in sqls)
105+
106+
107+
# --- DROP COLUMN ---
108+
109+
110+
def test_drop_column_has_schema():
111+
set_schema("tenant1")
112+
field = TenantModelWithAge._meta.get_field("age")
113+
sqls = _collect_sql(lambda e: e.remove_field(TenantModelWithAge, field))
114+
assert any('"tenant1"."testapp_tenantmodel"' in s for s in sqls)
115+
116+
117+
# --- ADD INDEX ---
118+
119+
120+
def test_add_index_has_schema():
121+
set_schema("tenant1")
122+
idx = Index(fields=["name"], name="testapp_tenantmodel_name_idx")
123+
sqls = _collect_sql(lambda e: e.add_index(TenantModel, idx))
124+
assert any('"tenant1"."testapp_tenantmodel"' in s for s in sqls)
125+
126+
127+
def test_add_index_no_schema():
128+
idx = Index(fields=["name"], name="testapp_tenantmodel_name_idx")
129+
sqls = _collect_sql(lambda e: e.add_index(TenantModel, idx))
130+
assert any('"testapp_tenantmodel"' in s for s in sqls)
131+
assert not any('"public"."testapp_tenantmodel"' in s for s in sqls)
132+
133+
134+
# --- RENAME TABLE ---
135+
136+
137+
def test_rename_table_has_schema():
138+
set_schema("tenant1")
139+
DatabaseOperations._table_names.add("testapp_newname")
140+
sqls = _collect_sql(
141+
lambda e: e.alter_db_table(TenantModel, "testapp_tenantmodel", "testapp_newname")
142+
)
143+
assert any('"tenant1"."testapp_tenantmodel"' in s for s in sqls)
144+
145+
146+
def test_rename_table_no_schema():
147+
DatabaseOperations._table_names.add("testapp_newname")
148+
sqls = _collect_sql(
149+
lambda e: e.alter_db_table(TenantModel, "testapp_tenantmodel", "testapp_newname")
150+
)
151+
assert any('"testapp_tenantmodel"' in s for s in sqls)
152+
assert not any('"public"."testapp_tenantmodel"' in s for s in sqls)
153+
154+
155+
# --- Schema switching across operations ---
156+
157+
158+
def test_schema_switch_between_operations():
159+
set_schema("alpha")
160+
sqls_alpha = _collect_sql(lambda e: e.create_model(TenantModel))
161+
assert any('"alpha"."testapp_tenantmodel"' in s for s in sqls_alpha)
162+
163+
set_schema("beta")
164+
sqls_beta = _collect_sql(lambda e: e.create_model(TenantModel))
165+
assert any('"beta"."testapp_tenantmodel"' in s for s in sqls_beta)

0 commit comments

Comments
 (0)