"""Write-path key handling. There is only one key now. It gates writes, it is public by design, and reads are partitioned by session rather than gated -- see test_session_isolation.py for the rules that actually keep one visitor's data away from another's. """ from __future__ import annotations WRITE_BODY = {"dominant_emotion": "Happy", "duration_seconds": 10} SESSION = {"X-Session-Id": "auth-test-session-01"} class TestWritePath: def test_requires_a_key(self, client): response = client.post("/api/logs", json=WRITE_BODY, headers=SESSION) assert response.status_code == 401 def test_rejects_a_wrong_key(self, client): response = client.post( "/api/logs", json=WRITE_BODY, headers={**SESSION, "X-API-Key": "nope"} ) assert response.status_code == 401 def test_accepts_the_ingest_key(self, client, ingest_headers): response = client.post("/api/logs", json=WRITE_BODY, headers=ingest_headers) assert response.status_code == 201 def test_error_body_does_not_describe_the_expected_key(self, client): body = client.post( "/api/logs", json=WRITE_BODY, headers={**SESSION, "X-API-Key": "nope"} ).json() assert "ingest" not in body["message"].lower() class TestReadPath: """Reads carry a session id and no credential at all.""" def test_reads_need_no_key(self, client, session_headers): assert client.get("/api/logs", headers=session_headers).status_code == 200 def test_stats_need_no_key(self, client, session_headers): assert client.get("/api/logs/stats", headers=session_headers).status_code == 200 def test_delete_needs_no_key(self, client, session_headers, log_factory): row = log_factory(count=1)[0] assert client.delete(f"/api/logs/{row.id}", headers=session_headers).status_code == 200