173 lines
5.5 KiB
Python
173 lines
5.5 KiB
Python
from pathlib import Path
|
|
|
|
from argon2 import PasswordHasher
|
|
from fastapi.testclient import TestClient
|
|
|
|
from app import main
|
|
from app.auth import COOKIE_NAME
|
|
from app.config import Settings, UserSeed
|
|
from app.curator import Curator
|
|
from app.db import Database
|
|
|
|
|
|
WRITE_HEADERS = {"X-Note-Client": "xuxiang-web"}
|
|
|
|
|
|
def test_login_isolation_roles_and_debug_privacy(
|
|
tmp_path: Path, monkeypatch
|
|
):
|
|
hasher = PasswordHasher()
|
|
admin_key = "key-admin-test"
|
|
member_key = "key-member-test"
|
|
users = (
|
|
UserSeed(
|
|
id="admin",
|
|
label="管理员",
|
|
role="admin",
|
|
access_key_hash=hasher.hash(admin_key),
|
|
),
|
|
UserSeed(
|
|
id="member",
|
|
label="用户二",
|
|
role="member",
|
|
access_key_hash=hasher.hash(member_key),
|
|
),
|
|
)
|
|
settings = Settings(
|
|
data_dir=tmp_path,
|
|
database_path=tmp_path / "api.sqlite3",
|
|
users=users,
|
|
session_secret="session-secret-for-tests",
|
|
deepseek_api_key=None,
|
|
deepseek_base_url="https://api.deepseek.com",
|
|
deepseek_model="deepseek-v4-pro",
|
|
cookie_secure=False,
|
|
auth_disabled=False,
|
|
)
|
|
database = Database(settings.database_path)
|
|
monkeypatch.setattr(main, "settings", settings)
|
|
monkeypatch.setattr(main, "db", database)
|
|
monkeypatch.setattr(main, "curator", Curator(database, settings))
|
|
|
|
with TestClient(main.app) as client:
|
|
assert client.post(
|
|
"/api/login", json={"access_key": "wrong"}
|
|
).status_code == 401
|
|
|
|
admin_login = client.post(
|
|
"/api/login", json={"access_key": admin_key}
|
|
)
|
|
assert admin_login.status_code == 200
|
|
assert admin_login.json()["user"]["role"] == "admin"
|
|
admin_cookie = client.cookies.get(COOKIE_NAME)
|
|
|
|
member_login = client.post(
|
|
"/api/login", json={"access_key": member_key}
|
|
)
|
|
assert member_login.status_code == 200
|
|
member_cookie = client.cookies.get(COOKIE_NAME)
|
|
|
|
client.cookies.set(COOKIE_NAME, admin_cookie)
|
|
admin_fragment = client.post(
|
|
"/api/fragments",
|
|
json={"content": "管理员的记录"},
|
|
headers=WRITE_HEADERS,
|
|
)
|
|
assert admin_fragment.status_code == 201
|
|
|
|
client.cookies.set(COOKIE_NAME, member_cookie)
|
|
member_fragment = client.post(
|
|
"/api/fragments",
|
|
json={"content": "用户二的记录"},
|
|
headers=WRITE_HEADERS,
|
|
)
|
|
assert member_fragment.status_code == 201
|
|
assert [
|
|
item["content"]
|
|
for item in client.get("/api/fragments").json()["items"]
|
|
] == ["用户二的记录"]
|
|
assert client.get("/api/admin/overview").status_code == 403
|
|
|
|
run_id = database.start_agent_run(
|
|
"member",
|
|
member_fragment.json()["id"],
|
|
"deepseek-v4-pro",
|
|
"prompt.v1",
|
|
"high",
|
|
)
|
|
database.add_agent_event(
|
|
run_id,
|
|
"member",
|
|
"decision_committed",
|
|
{
|
|
"standalone": False,
|
|
"assessments": [{"title": "私密产物", "maturity": 42}],
|
|
},
|
|
)
|
|
|
|
client.cookies.set(COOKIE_NAME, admin_cookie)
|
|
assert [
|
|
item["content"]
|
|
for item in client.get("/api/fragments").json()["items"]
|
|
] == ["管理员的记录"]
|
|
redacted = client.get(f"/api/admin/runs/{run_id}").json()
|
|
assert redacted["fragment"]["content_visible"] is False
|
|
assert redacted["events"][-1]["payload"] == {
|
|
"standalone": False,
|
|
"assessment_count": 1,
|
|
}
|
|
|
|
client.cookies.set(COOKIE_NAME, member_cookie)
|
|
shared = client.patch(
|
|
"/api/account/debug-sharing",
|
|
json={"enabled": True},
|
|
headers=WRITE_HEADERS,
|
|
)
|
|
assert shared.status_code == 200
|
|
|
|
client.cookies.set(COOKIE_NAME, admin_cookie)
|
|
visible = client.get(f"/api/admin/runs/{run_id}").json()
|
|
assert visible["fragment"]["content_visible"] is True
|
|
assert visible["fragment"]["content"] == "用户二的记录"
|
|
|
|
retry_error = RuntimeError("tool loop exceeded")
|
|
database.finish_agent_run(
|
|
run_id,
|
|
status="error",
|
|
duration_ms=100,
|
|
attempt_count=1,
|
|
model_rounds=4,
|
|
tool_calls=4,
|
|
search_calls=3,
|
|
inspection_calls=1,
|
|
input_tokens=400,
|
|
output_tokens=80,
|
|
reasoning_tokens=60,
|
|
cached_tokens=0,
|
|
error=retry_error,
|
|
)
|
|
database.set_fragment_status(
|
|
member_fragment.json()["id"], "error", str(retry_error)
|
|
)
|
|
queued: list[str] = []
|
|
monkeypatch.setattr(main.curator, "enqueue", queued.append)
|
|
|
|
client.cookies.set(COOKIE_NAME, member_cookie)
|
|
assert client.post(
|
|
f"/api/admin/runs/{run_id}/retry",
|
|
headers=WRITE_HEADERS,
|
|
).status_code == 403
|
|
|
|
client.cookies.set(COOKIE_NAME, admin_cookie)
|
|
retried = client.post(
|
|
f"/api/admin/runs/{run_id}/retry",
|
|
headers=WRITE_HEADERS,
|
|
)
|
|
assert retried.status_code == 202
|
|
assert retried.json() == {"queued": True}
|
|
assert queued == [member_fragment.json()["id"]]
|
|
assert client.post(
|
|
f"/api/admin/runs/{run_id}/retry",
|
|
headers=WRITE_HEADERS,
|
|
).status_code == 409
|