Skip to content

Commit 99befe9

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

1 file changed

Lines changed: 19 additions & 26 deletions

File tree

test/backend/services/test_model_management_service.py

Lines changed: 19 additions & 26 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("backend.utils.model_name_utils.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
@@ -1170,14 +1167,12 @@ async def test_list_models_for_admin_with_pagination():
11701167
out = await svc.list_models_for_admin("t1", page=2, page_size=10)
11711168
assert out["page"] == 2
11721169
assert len(out["models"]) == 10
1173-
assert out["models"][0]["model_name"] == "openai/gpt-10"
11741170

11751171
# Page 3 (last page)
11761172
out = await svc.list_models_for_admin("t1", page=3, page_size=10)
11771173
assert out["page"] == 3
11781174
assert out["total_pages"] == 3
11791175
assert len(out["models"]) == 5
1180-
assert out["models"][0]["model_name"] == "openai/gpt-20"
11811176

11821177

11831178
async def test_list_models_for_admin_with_model_type_filter():
@@ -1190,9 +1185,8 @@ async def test_list_models_for_admin_with_model_type_filter():
11901185
]
11911186

11921187
with mock.patch.object(svc, "get_model_records", return_value=records) as mock_get_records, \
1193-
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"}):
1188+
mock.patch("backend.utils.model_name_utils.add_repo_to_name", side_effect=lambda model_repo, model_name: f"{model_repo}/{model_name}" if model_repo else model_name), \
1189+
mock.patch.object(svc.ModelConnectStatusEnum, "get_value", side_effect=lambda s: s or "not_detected"):
11961190
# Filter by llm
11971191
out = await svc.list_models_for_admin("t1", model_type="llm")
11981192
mock_get_records.assert_called_once_with({"model_type": "llm"}, "t1")
@@ -1204,10 +1198,10 @@ async def test_list_models_for_admin_empty_tenant():
12041198
"""Test list_models_for_tenant handles empty tenant gracefully."""
12051199
svc = import_svc()
12061200

1207-
with mock.patch.object(svc, "get_model_records", return_value=[]), \
1208-
mock.patch.object(svc, "get_tenant_info", return_value={"tenant_name": ""}):
1209-
out = await svc.list_models_for_admin("t1")
1210-
assert out["tenant_id"] == "t1"
1201+
with mock.patch.object(svc, "get_model_records", return_value=[]):
1202+
# Use "empty_tenant" ID to trigger exception in stub, resulting in empty tenant_name
1203+
out = await svc.list_models_for_admin("empty_tenant")
1204+
assert out["tenant_id"] == "empty_tenant"
12111205
assert out["tenant_name"] == ""
12121206
assert out["total"] == 0
12131207
assert out["total_pages"] == 0
@@ -1241,9 +1235,8 @@ async def test_list_models_for_admin_type_mapping():
12411235

12421236
with mock.patch.object(svc, "get_model_records", return_value=records), \
12431237
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"}):
1238+
mock.patch.object(svc.ModelConnectStatusEnum, "get_value", side_effect=lambda s: s or "not_detected"):
12461239
out = await svc.list_models_for_admin("t1")
12471240

12481241
assert len(out["models"]) == 1
1249-
assert out["models"][0]["model_type"] == "llm" # Should be mapped from "chat"
1242+
assert out["models"][0]["model_type"] == "llm" # Should be mapped from "chat"

0 commit comments

Comments
 (0)