fischer-agentkit/tests/unit/test_scim_router.py

168 lines
5.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""SCIM 2.0 router 单元测试 (U4 SSO 集成)。
验证 POST / PATCH / GET /scim/v2/Users
- POST 创建用户
- PATCH 禁用用户 (active=false)
- GET 列表 / 过滤
- Bearer token 认证(缺 token / 错误 token → 401
"""
from __future__ import annotations
from pathlib import Path
import pytest
from fastapi import FastAPI
from fastapi.testclient import TestClient
from agentkit.server.auth.models import init_auth_db
from agentkit.server.auth.scim.router import (
register_scim_exception_handlers,
router as scim_router,
)
SCIM_TOKEN = "test-scim-token-secret"
@pytest.fixture
async def scim_app(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> FastAPI:
"""创建含 SCIM router 的 TestApp + 初始化 auth DB。"""
db_path = tmp_path / "auth.db"
await init_auth_db(db_path)
monkeypatch.setenv("AGENTKIT_AUTH_DB", str(db_path))
monkeypatch.setenv("AGENTKIT_SCIM_TOKEN", SCIM_TOKEN)
app = FastAPI()
app.state.auth_db_path = str(db_path)
app.state.scim_token = SCIM_TOKEN
# STAND-3: 按 RFC 7644 挂载在 /scim/v2非 /api/v1
app.include_router(scim_router, prefix="/scim/v2")
register_scim_exception_handlers(app)
return app
@pytest.fixture
def client(scim_app: FastAPI) -> TestClient:
return TestClient(scim_app)
def _auth_headers(token: str = SCIM_TOKEN) -> dict[str, str]:
return {"Authorization": f"Bearer {token}"}
# ---------------------------------------------------------------------------
# Tests
# ---------------------------------------------------------------------------
class TestCreateUser:
def test_creates_user(self, client: TestClient) -> None:
resp = client.post(
"/scim/v2/Users",
json={
"userName": "scim.user1",
"active": True,
"emails": [{"value": "scim1@example.com", "primary": True}],
},
headers=_auth_headers(),
)
assert resp.status_code == 201
data = resp.json()
assert data["userName"] == "scim.user1"
assert data["active"] is True
assert data["emails"][0]["value"] == "scim1@example.com"
assert data["schemas"] == ["urn:ietf:params:scim:schemas:core:2.0:User"]
def test_duplicate_username_conflict(self, client: TestClient) -> None:
client.post(
"/scim/v2/Users",
json={"userName": "dup.user", "emails": [{"value": "dup@example.com"}]},
headers=_auth_headers(),
)
resp = client.post(
"/scim/v2/Users",
json={"userName": "dup.user", "emails": [{"value": "other@example.com"}]},
headers=_auth_headers(),
)
assert resp.status_code == 409
def test_no_token_unauthorized(self, client: TestClient) -> None:
resp = client.post("/scim/v2/Users", json={"userName": "x"})
assert resp.status_code == 401
def test_wrong_token_unauthorized(self, client: TestClient) -> None:
resp = client.post(
"/scim/v2/Users",
json={"userName": "x"},
headers={"Authorization": "Bearer wrong-token"},
)
assert resp.status_code == 401
class TestPatchUser:
def test_disable_user(self, client: TestClient) -> None:
# 先创建
create = client.post(
"/scim/v2/Users",
json={"userName": "patch.user", "active": True, "emails": [{"value": "p@example.com"}]},
headers=_auth_headers(),
)
user_id = create.json()["id"]
# PATCH 禁用
resp = client.patch(
f"/scim/v2/Users/{user_id}",
json={"Operations": [{"op": "replace", "path": "active", "value": False}]},
headers=_auth_headers(),
)
assert resp.status_code == 200
assert resp.json()["active"] is False
def test_patch_unknown_user_404(self, client: TestClient) -> None:
resp = client.patch(
"/scim/v2/Users/nonexistent-id",
json={"Operations": [{"op": "replace", "path": "active", "value": False}]},
headers=_auth_headers(),
)
assert resp.status_code == 404
class TestListUsers:
def test_list_all(self, client: TestClient) -> None:
client.post(
"/scim/v2/Users",
json={"userName": "list.user1", "emails": [{"value": "l1@example.com"}]},
headers=_auth_headers(),
)
client.post(
"/scim/v2/Users",
json={"userName": "list.user2", "emails": [{"value": "l2@example.com"}]},
headers=_auth_headers(),
)
resp = client.get("/scim/v2/Users", headers=_auth_headers())
assert resp.status_code == 200
data = resp.json()
assert data["totalResults"] >= 2
assert len(data["Resources"]) >= 2
def test_filter_by_username(self, client: TestClient) -> None:
client.post(
"/scim/v2/Users",
json={"userName": "filter.user", "emails": [{"value": "f@example.com"}]},
headers=_auth_headers(),
)
resp = client.get(
"/scim/v2/Users",
params={"filter": 'userName eq "filter.user"'},
headers=_auth_headers(),
)
assert resp.status_code == 200
data = resp.json()
assert data["totalResults"] == 1
assert data["Resources"][0]["userName"] == "filter.user"
def test_no_token_unauthorized(self, client: TestClient) -> None:
resp = client.get("/scim/v2/Users")
assert resp.status_code == 401