Skip to content

Commit a366385

Browse files
committed
✨ User management: Model list needs tenant isolation (unit test)
1 parent f968d98 commit a366385

1 file changed

Lines changed: 14 additions & 20 deletions

File tree

test/backend/services/test_model_management_service.py

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -287,26 +287,25 @@ async def _clear_model_memories(**kwargs):
287287
nexent_memory_mod.clear_model_memories = _clear_model_memories
288288
sys.modules["nexent.memory.memory_service"] = nexent_memory_mod
289289

290-
# Stub services.tenant_service required by list_models_for_admin
290+
# Stub services.tenant_service required by list_models_for_admin BEFORE any imports
291291
services_tenant_mod = types.ModuleType("services.tenant_service")
292292

293293

294294
def _get_tenant_info(tenant_id):
295+
"""Mock implementation of get_tenant_info for testing."""
296+
# Raise exception for empty tenant to test error handling
297+
if tenant_id == "empty_tenant":
298+
raise Exception("Tenant not found")
295299
return {"tenant_name": "Test Tenant"}
296300

297301

298302
services_tenant_mod.get_tenant_info = _get_tenant_info
299303
sys.modules["services.tenant_service"] = services_tenant_mod
300304

301-
# Also stub the backend-level import path
302-
backend_services_tenant_mod = types.ModuleType("backend.services.tenant_service")
303-
backend_services_tenant_mod.get_tenant_info = _get_tenant_info
304-
sys.modules["backend.services.tenant_service"] = backend_services_tenant_mod
305305

306-
# Stub parent 'services' package to prevent attribute access error
307-
services_pkg = types.ModuleType("services")
308-
services_pkg.tenant_service = services_tenant_mod
309-
sys.modules["services"] = services_pkg
306+
def _add_repo_to_name(model_repo, model_name):
307+
"""Mock implementation of add_repo_to_name for testing."""
308+
return f"{model_repo}/{model_name}" if model_repo else model_name
310309

311310

312311
def import_svc():
@@ -1130,8 +1129,7 @@ async def test_list_models_for_admin_success():
11301129

11311130
with mock.patch.object(svc, "get_model_records", return_value=records), \
11321131
mock.patch.object(svc, "add_repo_to_name", side_effect=lambda model_repo, model_name: f"{model_repo}/{model_name}" if model_repo else model_name), \
1133-
mock.patch.object(svc.ModelConnectStatusEnum, "get_value", side_effect=lambda s: s or "not_detected"), \
1134-
mock.patch.object(svc, "get_tenant_info", return_value={"tenant_name": "Test Tenant"}):
1132+
mock.patch.object(svc.ModelConnectStatusEnum, "get_value", side_effect=lambda s: s or "not_detected"):
11351133
out = await svc.list_models_for_admin("t1")
11361134
assert out["tenant_id"] == "t1"
11371135
assert out["tenant_name"] == "Test Tenant"
@@ -1154,9 +1152,8 @@ async def test_list_models_for_admin_with_pagination():
11541152
]
11551153

11561154
with mock.patch.object(svc, "get_model_records", return_value=records), \
1157-
mock.patch.object(svc, "add_repo_to_name", side_effect=lambda model_repo, model_name: f"{model_repo}/{model_name}" if model_repo else model_name), \
1158-
mock.patch.object(svc.ModelConnectStatusEnum, "get_value", side_effect=lambda s: s or "not_detected"), \
1159-
mock.patch.object(svc, "get_tenant_info", return_value={"tenant_name": "Test Tenant"}):
1155+
mock.patch.object(svc, "add_repo_to_name", side_effect=_add_repo_to_name), \
1156+
mock.patch.object(svc.ModelConnectStatusEnum, "get_value", side_effect=lambda s: s or "not_detected"):
11601157
# Page 1, page_size 10
11611158
out = await svc.list_models_for_admin("t1", page=1, page_size=10)
11621159
assert out["page"] == 1
@@ -1191,8 +1188,7 @@ async def test_list_models_for_admin_with_model_type_filter():
11911188

11921189
with mock.patch.object(svc, "get_model_records", return_value=records) as mock_get_records, \
11931190
mock.patch.object(svc, "add_repo_to_name", side_effect=lambda model_repo, model_name: f"{model_repo}/{model_name}" if model_repo else model_name), \
1194-
mock.patch.object(svc.ModelConnectStatusEnum, "get_value", side_effect=lambda s: s or "not_detected"), \
1195-
mock.patch.object(svc, "get_tenant_info", return_value={"tenant_name": "Test Tenant"}):
1191+
mock.patch.object(svc.ModelConnectStatusEnum, "get_value", side_effect=lambda s: s or "not_detected"):
11961192
# Filter by llm
11971193
out = await svc.list_models_for_admin("t1", model_type="llm")
11981194
mock_get_records.assert_called_once_with({"model_type": "llm"}, "t1")
@@ -1204,8 +1200,7 @@ async def test_list_models_for_admin_empty_tenant():
12041200
"""Test list_models_for_tenant handles empty tenant gracefully."""
12051201
svc = import_svc()
12061202

1207-
with mock.patch.object(svc, "get_model_records", return_value=[]), \
1208-
mock.patch.object(svc, "get_tenant_info", return_value={"tenant_name": ""}):
1203+
with mock.patch.object(svc, "get_model_records", return_value=[]):
12091204
out = await svc.list_models_for_admin("t1")
12101205
assert out["tenant_id"] == "t1"
12111206
assert out["tenant_name"] == ""
@@ -1241,8 +1236,7 @@ async def test_list_models_for_admin_type_mapping():
12411236

12421237
with mock.patch.object(svc, "get_model_records", return_value=records), \
12431238
mock.patch.object(svc, "add_repo_to_name", side_effect=lambda model_repo, model_name: f"{model_repo}/{model_name}" if model_repo else model_name), \
1244-
mock.patch.object(svc.ModelConnectStatusEnum, "get_value", side_effect=lambda s: s or "not_detected"), \
1245-
mock.patch.object(svc, "get_tenant_info", return_value={"tenant_name": "Test Tenant"}):
1239+
mock.patch.object(svc.ModelConnectStatusEnum, "get_value", side_effect=lambda s: s or "not_detected"):
12461240
out = await svc.list_models_for_admin("t1")
12471241

12481242
assert len(out["models"]) == 1

0 commit comments

Comments
 (0)