Files
the-nexus/tests/test_mempalace_audit_privacy.py
Alexander Whitestone c05febf86f feat: MemPalace fleet memory scaffold — taxonomy, Evennia plugin, privacy tools
Delivers milestone artifacts for #1075 (MemPalace × Evennia — Fleet Memory):

**#1082 — Palace taxonomy standard**
- `mempalace/rooms.yaml` — fleet-wide room vocabulary (5 core rooms: forge,
  hermes, nexus, issues, experiments) with optional domain rooms and tunnel
  routing table
- `mempalace/validate_rooms.py` — validates a wizard's mempalace.yaml against
  the standard; exits non-zero on missing core rooms (CI-ready)

**#1077 — Evennia plugin scaffold**
- `mempalace/evennia_mempalace/` — contrib module connecting Evennia to MemPalace
  - `CmdRecall` — `recall <query>` / `recall <query> --fleet` in-world commands
  - `CmdEnterRoom` — teleport to semantic room by topic
  - `MemPalaceRoom` — typeclass whose description auto-populates from palace search
  - `searcher.py` — thin subprocess wrapper around the mempalace binary
  - `settings.py` — MEMPALACE_PATH / MEMPALACE_WING configuration bridge

**#1083 — Privacy boundary tools**
- `mempalace/export_closets.sh` — closet-only export enforcing policy that raw
  drawers never leave the local VPS; aborts on violations
- `mempalace/audit_privacy.py` — weekly audit of fleet palace for raw drawers,
  full-text closets, and private source_file paths

**Tests:** 21 new tests covering validate_rooms and audit_privacy logic.

Refs #1075, #1077, #1082, #1083

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 10:09:56 -04:00

130 lines
4.1 KiB
Python

"""
Tests for mempalace/audit_privacy.py — fleet palace privacy auditor.
Refs: #1083, #1075
"""
import json
from pathlib import Path
import pytest
from mempalace.audit_privacy import (
Violation,
audit_file,
audit_palace,
_is_private_path,
)
# ---------------------------------------------------------------------------
# _is_private_path
# ---------------------------------------------------------------------------
def test_private_path_root():
assert _is_private_path("/root/wizards/bezalel/workspace.md") is True
def test_private_path_home():
assert _is_private_path("/home/apayne/projects/nexus") is True
def test_private_path_users():
assert _is_private_path("/Users/apayne/worktrees/nexus/foo.py") is True
def test_non_private_path():
assert _is_private_path("/var/lib/mempalace/fleet/bezalel/forge.closet.json") is False
assert _is_private_path("relative/path.md") is False
# ---------------------------------------------------------------------------
# audit_file — clean closet
# ---------------------------------------------------------------------------
def _write_closet(tmp_path: Path, name: str, drawers: list) -> Path:
p = tmp_path / name
p.write_text(json.dumps({"drawers": drawers}))
return p
def test_clean_closet_has_no_violations(tmp_path):
f = _write_closet(tmp_path, "forge.closet.json", [
{"text": "Build succeeded on commit abc123.", "closet": True},
])
assert audit_file(f) == []
# ---------------------------------------------------------------------------
# audit_file — raw drawer violation
# ---------------------------------------------------------------------------
def test_raw_drawer_file_is_violation(tmp_path):
f = tmp_path / "workspace.drawer.json"
f.write_text(json.dumps({"text": "some private content"}))
violations = audit_file(f)
assert len(violations) == 1
assert violations[0].rule == "RAW_DRAWER"
# ---------------------------------------------------------------------------
# audit_file — full text in closet
# ---------------------------------------------------------------------------
def test_full_text_closet_is_violation(tmp_path):
long_text = "x" * 3000 # exceeds 2000 char limit
f = _write_closet(tmp_path, "nexus.closet.json", [
{"text": long_text, "closet": True},
])
violations = audit_file(f)
assert any(v.rule == "FULL_TEXT_IN_CLOSET" for v in violations)
# ---------------------------------------------------------------------------
# audit_file — private source_file path
# ---------------------------------------------------------------------------
def test_private_source_file_is_violation(tmp_path):
f = _write_closet(tmp_path, "hermes.closet.json", [
{
"text": "Short summary.",
"source_file": "/root/wizards/bezalel/secret.md",
"closet": True,
}
])
violations = audit_file(f)
assert any(v.rule == "PRIVATE_SOURCE_PATH" for v in violations)
def test_fleet_source_file_is_ok(tmp_path):
f = _write_closet(tmp_path, "hermes.closet.json", [
{
"text": "Short summary.",
"source_file": "/var/lib/mempalace/fleet/bezalel/hermes.closet.json",
"closet": True,
}
])
violations = audit_file(f)
assert violations == []
# ---------------------------------------------------------------------------
# audit_palace
# ---------------------------------------------------------------------------
def test_audit_palace_clean(tmp_path):
_write_closet(tmp_path, "forge.closet.json", [{"text": "ok", "closet": True}])
_write_closet(tmp_path, "nexus.closet.json", [{"text": "ok", "closet": True}])
result = audit_palace(tmp_path)
assert result.clean
assert result.scanned == 2
def test_audit_palace_finds_violations(tmp_path):
_write_closet(tmp_path, "forge.closet.json", [{"text": "ok", "closet": True}])
bad = tmp_path / "secret.drawer.json"
bad.write_text(json.dumps({"text": "raw private data"}))
result = audit_palace(tmp_path)
assert not result.clean
assert any(v.rule == "RAW_DRAWER" for v in result.violations)