"""Tests for V1 tenant admin service."""
from __future__ import annotations
from pathlib import Path
import pytest
from core.models import RequestContext, Role
from server.api_keys import APIKeyManager
from server.audit import AuditService
from server.control_plane_store import ControlPlaneStore
from server.tenant_admin import TenantAdminService
@pytest.fixture()
def services(tmp_path: Path):
store = ControlPlaneStore(mount_prefix="", local_root=str(tmp_path))
key_manager = APIKeyManager(store)
audit = AuditService(store)
admin = TenantAdminService(key_manager, store, audit)
key_manager.create_account("acct-1", "alice")
root_ctx = RequestContext("acct-1", "root", "agent", "s", "t", role=Role.ROOT)
admin_ctx = RequestContext("acct-1", "alice", "agent", "s", "t", role=Role.ADMIN)
member_ctx = RequestContext("acct-1", "bob", "agent", "s", "t", role=Role.MEMBER)
return key_manager, audit, admin, root_ctx, admin_ctx, member_ctx
def test_list_accounts_for_root(services):
_, _, admin, root_ctx, _, _ = services
result = admin.list_accounts(root_ctx)
assert result["accounts"][0]["account_id"] == "acct-1"
def test_create_user_and_list_roles(services):
_, _, admin, _, admin_ctx, _ = services
created = admin.create_user(admin_ctx, "acct-1", "bob", "MEMBER")
roles = admin.list_roles(admin_ctx, "acct-1")
assert created["user_id"] == "bob"
assert created["role"] == "MEMBER"
assert {"user_id": "bob", "role": "MEMBER"} in roles["roles"]
def test_set_role_requires_root(services):
_, _, admin, _, _, member_ctx = services
with pytest.raises(PermissionError):
admin.set_role(member_ctx, "acct-1", "alice", "admin")
def test_create_user_rejects_unknown_account(services):
_, _, admin, root_ctx, _, _ = services
with pytest.raises(ValueError, match="account not found"):
admin.create_user(root_ctx, "missing-account", "bob", "user")
def test_create_user_rejects_duplicate_user(services):
_, _, admin, _, admin_ctx, _ = services
admin.create_user(admin_ctx, "acct-1", "bob", "user")
with pytest.raises(ValueError, match="user already exists"):
admin.create_user(admin_ctx, "acct-1", "bob", "user")
def test_create_and_update_agent_writes_audit(services):
_, audit, admin, _, admin_ctx, _ = services
admin.create_user(admin_ctx, "acct-1", "bob", "user")
created = admin.create_agent(admin_ctx, "acct-1", "agent-x", owner_user_id="alice")
updated = admin.update_agent(admin_ctx, "acct-1", "agent-x", owner_user_id="bob")
logs = audit.list_logs("acct-1")
assert created["owner_user_id"] == "alice"
assert updated["owner_user_id"] == "bob"
assert any(row["action"] == "agent_created" for row in logs)
assert any(row["action"] == "agent_updated" for row in logs)
def test_create_agent_rejects_unknown_owner(services):
_, _, admin, _, admin_ctx, _ = services
with pytest.raises(ValueError, match="owner user not found"):
admin.create_agent(admin_ctx, "acct-1", "agent-x", owner_user_id="missing-user")
def test_create_agent_rejects_duplicate_agent(services):
_, _, admin, _, admin_ctx, _ = services
admin.create_agent(admin_ctx, "acct-1", "agent-x", owner_user_id="alice")
with pytest.raises(ValueError, match="agent already exists"):
admin.create_agent(admin_ctx, "acct-1", "agent-x", owner_user_id="alice")
def test_update_agent_rejects_unknown_owner(services):
_, _, admin, _, admin_ctx, _ = services
admin.create_agent(admin_ctx, "acct-1", "agent-x", owner_user_id="alice")
with pytest.raises(ValueError, match="owner user not found"):
admin.update_agent(admin_ctx, "acct-1", "agent-x", owner_user_id="missing-user")
def test_update_agent_rejects_missing_agent(services):
_, _, admin, _, admin_ctx, _ = services
with pytest.raises(FileNotFoundError, match="agent not found"):
admin.update_agent(admin_ctx, "acct-1", "missing-agent", owner_user_id="alice")
def test_list_roles_preserves_member_mapping(services):
key_manager, _, admin, root_ctx, admin_ctx, _ = services
key_manager.register_user("acct-1", "bob", "MEMBER")
admin.set_role(root_ctx, "acct-1", "alice", "MEMBER")
roles = admin.list_roles(admin_ctx, "acct-1")
mapping = {row["user_id"]: row["role"] for row in roles["roles"]}
assert mapping["alice"] == "MEMBER"
assert mapping["bob"] == "MEMBER"
def test_set_role_normalizes_admin_for_storage(services):
key_manager, _, admin, root_ctx, _, _ = services
result = admin.set_role(root_ctx, "acct-1", "alice", "ADMIN")
users = key_manager.get_users("acct-1")
mapping = {row["user_id"]: row["role"] for row in users}
assert result["role"] == "ADMIN"
assert mapping["alice"] == "admin"