"""稽核日誌後台 endpoint 測試。""" from __future__ import annotations import pytest from fastapi.testclient import TestClient from app.auth.passwords import hash_password from app.db import _utcnow, get_conn from app.main import app PWD = "TestPw123!" @pytest.fixture() def client(): return TestClient(app) def _mkuser(role, name, dept="業務部"): c = get_conn() c.execute( "INSERT INTO users (username, email, password_hash, role, department, " "created_at, is_active) VALUES (?,?,?,?,?,?,1)", (name, f"{name}@x.tw", hash_password(PWD), role, dept, _utcnow()), ) return c.execute("SELECT id FROM users WHERE username=?", (name,)).fetchone()["id"] def _mkaudit(user_id, action, ts=None, *, result="success", detail=None): get_conn().execute( "INSERT INTO audit_log (user_id, action, resource_type, resource_id, " "detail, result, timestamp) VALUES (?,?,?,?,?,?,?)", (user_id, action, "x", "0", detail, result, ts or _utcnow()), ) def _login(c, u): return c.post("/api/auth/login", json={"username": u, "password": PWD}).json()["access_token"] def _auth(t): return {"Authorization": f"Bearer {t}"} # ============ list 端點 ============ def test_admin_can_list_audit(client): uid = _mkuser("admin", "adm") _mkaudit(uid, "login_success") _mkaudit(uid, "view_document") tok = _login(client, "adm") r = client.get("/api/admin/audit-log", headers=_auth(tok)) assert r.status_code == 200 j = r.json() assert "items" in j and "total" in j assert j["total"] >= 2 # 衍生欄位 assert all("category" in it and "risk" in it for it in j["items"]) def test_user_blocked(client): _mkuser("admin", "adm") _mkuser("user", "u1") tok = _login(client, "u1") r = client.get("/api/admin/audit-log", headers=_auth(tok)) assert r.status_code in (401, 403) def test_filter_by_user(client): a = _mkuser("admin", "adm") b = _mkuser("user", "u1") _mkaudit(a, "view_document") _mkaudit(b, "view_document") tok = _login(client, "adm") r = client.get(f"/api/admin/audit-log?user_id={b}", headers=_auth(tok)) items = r.json()["items"] assert all(it["user_id"] == b for it in items) def test_filter_by_action(client): uid = _mkuser("admin", "adm") _mkaudit(uid, "login_success") _mkaudit(uid, "delete_document") tok = _login(client, "adm") r = client.get("/api/admin/audit-log?action=delete_document", headers=_auth(tok)) items = r.json()["items"] assert all(it["action"] == "delete_document" for it in items) def test_filter_by_category_security(client): uid = _mkuser("admin", "adm") _mkaudit(uid, "login_failed", result="fail") _mkaudit(uid, "view_document") tok = _login(client, "adm") r = client.get("/api/admin/audit-log?category=security", headers=_auth(tok)) items = r.json()["items"] assert all(it["category"] == "security" for it in items) assert any(it["action"] == "login_failed" for it in items) def test_filter_by_risk_high(client): uid = _mkuser("admin", "adm") _mkaudit(uid, "delete_user") _mkaudit(uid, "view_document") tok = _login(client, "adm") r = client.get("/api/admin/audit-log?risk=high", headers=_auth(tok)) items = r.json()["items"] assert all(it["risk"] == "high" for it in items) def test_pagination(client): uid = _mkuser("admin", "adm") for i in range(10): _mkaudit(uid, f"action_{i}") tok = _login(client, "adm") r1 = client.get("/api/admin/audit-log?limit=5&offset=0", headers=_auth(tok)) r2 = client.get("/api/admin/audit-log?limit=5&offset=5", headers=_auth(tok)) assert len(r1.json()["items"]) == 5 assert len(r2.json()["items"]) >= 1 ids1 = {it["id"] for it in r1.json()["items"]} ids2 = {it["id"] for it in r2.json()["items"]} assert not (ids1 & ids2) # 兩頁不重疊 # ============ stats 端點 ============ def test_stats_keys(client): _mkuser("admin", "adm") tok = _login(client, "adm") r = client.get("/api/admin/audit-log/stats", headers=_auth(tok)) assert r.status_code == 200 j = r.json() assert set(j) == {"today_events", "high_risk_7d", "failed_login_7d", "confidential_access_7d"} def test_stats_today_count(client): uid = _mkuser("admin", "adm") _mkaudit(uid, "view_document") _mkaudit(uid, "view_document") tok = _login(client, "adm") j = client.get("/api/admin/audit-log/stats", headers=_auth(tok)).json() assert j["today_events"] >= 2 def test_stats_high_risk_count(client): uid = _mkuser("admin", "adm") _mkaudit(uid, "delete_user") _mkaudit(uid, "delete_document") _mkaudit(uid, "view_document") tok = _login(client, "adm") j = client.get("/api/admin/audit-log/stats", headers=_auth(tok)).json() assert j["high_risk_7d"] >= 2