Kansho/backend/tests/test_identity_registry.py
Lars 9d79e3e92f
Some checks failed
Deploy Development / deploy (push) Successful in 51s
Test Suite / pytest-backend (push) Failing after 2m32s
Test Suite / smoke-dev (push) Successful in 0s
Test Suite / frontend-build (push) Successful in 15s
Run backend tests with pytest after Dev deploy.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-07 14:47:04 +02:00

126 lines
4.7 KiB
Python

"""Admin identity registry stays local. Run from backend/: python tests/test_identity_registry.py"""
from __future__ import annotations
import os
import sys
import tempfile
from pathlib import Path
from unittest.mock import patch
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT))
os.environ["KANSHO_FAKE_PROVIDER"] = "1"
os.environ["KANSHO_FAKE_DETECT"] = "1"
from fastapi.testclient import TestClient
from identity_store import STATUS_LEGACY, list_confirmed_identities, list_registry
from main import app
from privacy_gateway import reset_debug
def expect(ok: bool, message: str) -> None:
if not ok:
raise SystemExit(f"FAIL: {message}")
print(f"OK {message}")
def main() -> None:
reset_debug()
import sqlite3
from db import _IDENTITY_MAPPING_COLUMNS, _ensure_columns
from identity_store import migrate_legacy_identity_rows as migrate_legacy
conn = sqlite3.connect(":memory:")
conn.row_factory = sqlite3.Row
conn.executescript(
"""
CREATE TABLE identity_mappings (
id TEXT PRIMARY KEY,
profile_id TEXT NOT NULL,
token TEXT NOT NULL,
local_label TEXT NOT NULL DEFAULT '',
created TEXT NOT NULL DEFAULT '2026-08-01 00:00:00'
);
INSERT INTO identity_mappings (id, profile_id, token, local_label)
VALUES ('m1', 'p1', 'PERSON:01', 'Altbau');
"""
)
_ensure_columns(conn, "identity_mappings", _IDENTITY_MAPPING_COLUMNS)
migrate_legacy(conn)
legacy = dict(conn.execute("SELECT * FROM identity_mappings WHERE id = 'm1'").fetchone())
expect(legacy["status"] == STATUS_LEGACY, "existing rows become legacy_review_required")
expect(legacy["origin"] == "legacy_auto", "existing rows keep legacy origin")
expect(legacy["canonical_label"] == "Altbau", "canonical_label is copied from local_label")
expect(bool(legacy["updated"]), "updated is populated after ALTER")
conn.close()
with TestClient(app) as client:
setup = client.post(
"/api/auth/setup",
json={"email": "lars@example.test", "name": "Lars", "password": "test-pass"},
)
headers = {"X-Auth-Token": setup.json()["token"]}
profile_id = setup.json()["profile_id"]
from db import get_db
import uuid
legacy_id = str(uuid.uuid4())
with get_db() as conn:
conn.execute(
"""
INSERT INTO identity_mappings
(id, profile_id, token, local_label, canonical_label, entity_type, status, origin)
VALUES (?, ?, ?, ?, ?, 'PERSON', ?, 'legacy_auto')
""",
(legacy_id, profile_id, "PERSON:70", "Altbau", "Altbau", STATUS_LEGACY),
)
expect(
not any(item["id"] == legacy_id for item in list_confirmed_identities(profile_id)),
"legacy row is not confirmed",
)
expect(any(item["id"] == legacy_id for item in list_registry(profile_id)), "legacy row remains reviewable")
patched = client.patch(
f"/api/admin/identities/{legacy_id}",
headers=headers,
json={"status": "confirmed", "entity_type": "PLACE", "canonical_label": "Altbau"},
)
expect(patched.status_code == 200, patched.text)
expect(patched.json()["status"] == "confirmed", "admin can confirm a legacy row")
expect(patched.json()["entity_type"] == "PLACE", "admin can correct the type")
created = client.post(
"/api/admin/identities",
headers=headers,
json={"canonical_label": "Nordwerk", "entity_type": "ORG", "aliases": ["Nordwerk AG"]},
)
expect(created.status_code == 200, created.text)
identity_id = created.json()["id"]
aliases = client.patch(
f"/api/admin/identities/{identity_id}",
headers=headers,
json={"aliases": ["Nordwerk AG", "Nordwerk"]},
)
expect(aliases.status_code == 200, aliases.text)
expect("Nordwerk AG" in (aliases.json().get("aliases") or []), "confirmed alias can be stored")
calls = {"n": 0}
def boom(*_args, **_kwargs):
calls["n"] += 1
raise AssertionError("no egress")
with patch("entity_detect.complete_chat", boom), patch("providers.complete_chat", boom):
gone = client.delete(f"/api/admin/identities/{identity_id}", headers=headers)
expect(gone.status_code == 200, "admin delete stays local")
expect(calls["n"] == 0, "registry actions do not call detect or generate")
expect(not any(item["id"] == identity_id for item in list_registry(profile_id)), "deleted row is gone")
print("All identity registry tests passed.")
if __name__ == "__main__":
main()