Skip to content

Commit b1d54a1

Browse files
committed
Added test for controller reuse across multiple NinjaExtraAPI instances #293. Thanks to @jdiego
1 parent 669333b commit b1d54a1

File tree

1 file changed

+87
-0
lines changed

1 file changed

+87
-0
lines changed

tests/test_api_instance.py

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import typing as t
12
from unittest import mock
23

34
import pytest
@@ -84,3 +85,89 @@ def example(self):
8485
res = client.get("/another/example")
8586
assert res.status_code == 200
8687
assert res.content == b'"Create Response Works"'
88+
89+
90+
def test_same_controller_two_apis_works():
91+
@api_controller("/ping")
92+
class P:
93+
@http_get("")
94+
def ping(self):
95+
return {"ok": True}
96+
97+
a = NinjaExtraAPI(urls_namespace="a")
98+
b = NinjaExtraAPI(urls_namespace="b")
99+
100+
a.register_controllers(P)
101+
b.register_controllers(P) # triggers clone path
102+
103+
assert TestClient(a).get("/ping").json() == {"ok": True}
104+
assert TestClient(b).get("/ping").json() == {"ok": True}
105+
106+
107+
def test_openapi_schema_params_are_correct_on_two_apis():
108+
@api_controller("/")
109+
class ItemsController:
110+
@http_get("/items_1")
111+
def items_1(self, ordering: t.Optional[str] = None):
112+
return {"ok": True}
113+
114+
# Two independent API instances
115+
api_a = NinjaExtraAPI(title="A")
116+
api_b = NinjaExtraAPI(title="B")
117+
118+
api_a.register_controllers(ItemsController)
119+
api_b.register_controllers(ItemsController)
120+
121+
expected_params = [
122+
{
123+
"in": "query",
124+
"name": "ordering",
125+
"required": False,
126+
"schema": {
127+
"anyOf": [{"type": "string"}, {"type": "null"}],
128+
"title": "Ordering",
129+
},
130+
}
131+
]
132+
133+
# Check API A schema
134+
schema_a = api_a.get_openapi_schema()
135+
op_a = schema_a["paths"]["/api/items_1"]["get"]
136+
assert op_a["parameters"] == expected_params
137+
138+
# Check API B schema
139+
schema_b = api_b.get_openapi_schema()
140+
op_b = schema_b["paths"]["/api/items_1"]["get"]
141+
assert op_b["parameters"] == expected_params
142+
143+
# (Optional) also confirm the route actually works on both APIs
144+
ca = TestClient(api_a)
145+
cb = TestClient(api_b)
146+
assert ca.get("/items_1").status_code == 200
147+
assert cb.get("/items_1").status_code == 200
148+
149+
150+
def test_clone_is_cached_per_api_not_recreated():
151+
"""Register the same original class twice on the same API -> reuse cached clone, no new routers."""
152+
153+
@api_controller("/x")
154+
class X:
155+
@http_get("")
156+
def ok(self):
157+
return {"ok": True}
158+
159+
a = NinjaExtraAPI(urls_namespace="a")
160+
b = NinjaExtraAPI(urls_namespace="b")
161+
162+
# Mount on A (original)
163+
a.register_controllers(X)
164+
# Mount on B (clone)
165+
b.register_controllers(X)
166+
# Re-register same original on B (should reuse the cached clone; no new routers added)
167+
before = len(b._routers)
168+
b.register_controllers(X)
169+
after = len(b._routers)
170+
assert before == after
171+
172+
# Optional: ensure path exists and works
173+
assert TestClient(b).get("/x").json() == {"ok": True}

0 commit comments

Comments
 (0)