Files
cowork-local/tests/test_canonical_audit_logger.py
T
Hiep Ha VanandClaude Sonnet 5 40b12ecb15 refactor(monitoring): N2 - tach monitoring_tab.py, CanonicalAuditLogger, MonitoringQueryService, go circular import, sandbox matrix
- ui/monitoring_tab.py (1546 dong) tach thanh presentation/monitoring/**
  (container + 7 tab/card + shared helper), ui/monitoring_tab.py con lai
  re-export shim de app.py khong doi.
- infrastructure/telemetry/audit_logger.py: CanonicalAuditLogger, core/audit_log.py
  thanh wrapper mong, tuong thich nguoc 100% voi schema .jsonl cu.
- application/monitoring/monitoring_query_service.py: MonitoringQueryService
  read-only, filter/sort/pagination, khong import PySide6.
- Go circular import model_pricing<->usage_tracker va agent_security<->
  agent_security_alert (core/agent_security_types.py moi).
- infrastructure/sandbox/sandbox_capabilities.py: SandboxCapabilityMatrix
  theo OS (Windows/Linux/macOS), chua dau noi vao core/sandbox_manager.py.
- conftest.py: sua loi checkout khong ten cowork_local khien pytest import
  nham thu muc khac.
- 77 test moi, 167/167 pass. QA da xac nhan UI/business logic khong doi
  (xem evidence/report/unified_report.html).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-25 23:52:36 +09:00

96 lines
3.5 KiB
Python

"""Task 2 — CanonicalAuditLogger.
Verifies: (1) the infrastructure class itself round-trips events correctly
and mirrors to a shared dir, (2) ``core/audit_log.py``'s wrapper functions
still behave exactly as before (same signatures, same dict schema, same
never-raise guarantee), and (3) old-format raw dicts (as written by the
pre-refactor ``core/audit_log.py``) still load correctly for backward
compatibility.
"""
from __future__ import annotations
import json
from cowork_local.core import audit_log
from cowork_local.infrastructure.telemetry.audit_logger import (
KIND_MCP_CALL,
KIND_SECURITY_BLOCK,
CanonicalAuditEvent,
CanonicalAuditLogger,
)
def test_record_and_load_round_trip(tmp_path) -> None:
logger = CanonicalAuditLogger(tmp_path)
logger.set_identity("alice", "machine-1", role="admin")
logger.record(KIND_SECURITY_BLOCK, "run_command", False, detail="blocked it")
events = logger.load_events()
assert len(events) == 1
e = events[0]
assert e.kind == KIND_SECURITY_BLOCK
assert e.name == "run_command"
assert e.ok is False
assert e.detail == "blocked it"
assert e.account == "alice"
assert e.machine == "machine-1"
assert e.role == "admin"
def test_load_events_filters_by_kind(tmp_path) -> None:
logger = CanonicalAuditLogger(tmp_path)
logger.record(KIND_SECURITY_BLOCK, "a", False)
logger.record(KIND_MCP_CALL, "b", True)
only_mcp = logger.load_events(kind=KIND_MCP_CALL)
assert [e.name for e in only_mcp] == ["b"]
def test_shared_dir_mirroring(tmp_path) -> None:
shared = tmp_path / "shared"
logger = CanonicalAuditLogger(tmp_path / "audit")
logger.set_identity("bob", "machine-2", shared_dir=str(shared))
logger.record(KIND_MCP_CALL, "tool_x", True)
mirrored_files = list((shared / "telemetry" / "audit").glob("machine-2-*.jsonl"))
assert len(mirrored_files) == 1
def test_from_dict_is_tolerant_of_old_partial_rows() -> None:
old_row = {"ts": "2024-01-01T00:00:00", "kind": "tool_call", "name": "x", "ok": True}
event = CanonicalAuditEvent.from_dict(old_row)
assert event.detail == ""
assert event.account == ""
def test_record_never_raises_on_bad_directory(tmp_path) -> None:
bad_dir = tmp_path / "some_file.txt"
bad_dir.write_text("not a directory")
logger = CanonicalAuditLogger(bad_dir / "audit")
logger.record(KIND_SECURITY_BLOCK, "x", False) # must not raise
def test_core_audit_log_wrapper_same_schema_as_before(tmp_path, monkeypatch) -> None:
monkeypatch.setattr(audit_log, "AUDIT_DIR", tmp_path)
monkeypatch.setattr(audit_log, "_logger",
audit_log.CanonicalAuditLogger(tmp_path))
audit_log.set_identity("carol", "machine-3", role="user")
audit_log.record("permission", "install_package", True, detail="ok", agent_role="cowork")
events = audit_log.load_events()
assert len(events) == 1
e = events[0]
assert set(e.keys()) == {"ts", "kind", "agent_role", "name", "ok", "detail",
"account", "role", "machine"}
assert e["kind"] == "permission"
assert e["name"] == "install_package"
assert e["ok"] is True
assert e["agent_role"] == "cowork"
assert e["account"] == "carol"
# Raw file on disk still uses the exact pre-refactor schema/keys.
raw_line = next((tmp_path).glob("*.jsonl")).read_text(encoding="utf-8").splitlines()[0]
raw = json.loads(raw_line)
assert list(raw.keys()) == ["ts", "kind", "agent_role", "name", "ok", "detail",
"account", "role", "machine"]