feat(S3): visitors blueprint with profiles, visits, labeling, privacy controls
Add /visitors/ blueprint with REST API endpoints for listing profiles and visits, labeling (with consent gate), household linking, ignore/unignore, and cascading forget. Register blueprint in app.py. Add dashboard and profile detail templates (Bootstrap 5 dark, tab navigation, fetch-based JS). All six API tests pass (339 total). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
69
tests/unit/test_visitors_api.py
Normal file
69
tests/unit/test_visitors_api.py
Normal file
@@ -0,0 +1,69 @@
|
||||
import time
|
||||
import pytest
|
||||
from vigilar.config import VigilarConfig
|
||||
from vigilar.storage.queries import create_face_profile, insert_visit
|
||||
from vigilar.web.app import create_app
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def visitor_app(test_db):
|
||||
cfg = VigilarConfig()
|
||||
app = create_app(cfg)
|
||||
app.config["TESTING"] = True
|
||||
app.config["DB_ENGINE"] = test_db
|
||||
return app
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def seeded_visitors(test_db):
|
||||
now = time.time()
|
||||
pid1 = create_face_profile(test_db, name="Bob", first_seen_at=now - 86400, last_seen_at=now)
|
||||
pid2 = create_face_profile(test_db, first_seen_at=now - 3600, last_seen_at=now)
|
||||
insert_visit(test_db, pid1, "front", now - 3600)
|
||||
insert_visit(test_db, pid2, "front", now - 1800)
|
||||
return pid1, pid2
|
||||
|
||||
|
||||
def test_get_profiles(visitor_app, seeded_visitors):
|
||||
with visitor_app.test_client() as c:
|
||||
rv = c.get("/visitors/api/profiles")
|
||||
assert rv.status_code == 200
|
||||
assert len(rv.get_json()["profiles"]) == 2
|
||||
|
||||
|
||||
def test_get_visits(visitor_app, seeded_visitors):
|
||||
with visitor_app.test_client() as c:
|
||||
assert len(c.get("/visitors/api/visits").get_json()["visits"]) == 2
|
||||
|
||||
|
||||
def test_label_with_consent(visitor_app, seeded_visitors, test_db):
|
||||
_, pid2 = seeded_visitors
|
||||
with visitor_app.test_client() as c:
|
||||
rv = c.post(f"/visitors/{pid2}/label", json={"name": "Alice", "consent": True})
|
||||
assert rv.status_code == 200
|
||||
from vigilar.storage.queries import get_face_profile
|
||||
assert get_face_profile(test_db, pid2)["name"] == "Alice"
|
||||
|
||||
|
||||
def test_label_requires_consent(visitor_app, seeded_visitors):
|
||||
_, pid2 = seeded_visitors
|
||||
with visitor_app.test_client() as c:
|
||||
assert c.post(
|
||||
f"/visitors/{pid2}/label", json={"name": "Alice", "consent": False}
|
||||
).status_code == 400
|
||||
|
||||
|
||||
def test_forget(visitor_app, seeded_visitors, test_db):
|
||||
pid1, _ = seeded_visitors
|
||||
with visitor_app.test_client() as c:
|
||||
assert c.delete(f"/visitors/{pid1}/forget").status_code == 200
|
||||
from vigilar.storage.queries import get_face_profile
|
||||
assert get_face_profile(test_db, pid1) is None
|
||||
|
||||
|
||||
def test_ignore(visitor_app, seeded_visitors, test_db):
|
||||
_, pid2 = seeded_visitors
|
||||
with visitor_app.test_client() as c:
|
||||
assert c.post(f"/visitors/{pid2}/ignore").status_code == 200
|
||||
from vigilar.storage.queries import get_face_profile
|
||||
assert get_face_profile(test_db, pid2)["ignored"] == 1
|
||||
Reference in New Issue
Block a user