import pytest from fastapi.testclient import TestClient @pytest.fixture def app_db(tmp_path, monkeypatch): db = tmp_path / "t.sqlite3" monkeypatch.setenv("GOODNEWS_DB", str(db)) monkeypatch.setenv("GOODNEWS_SESSION_SECRET", "test-secret") import importlib import goodnews.api as api importlib.reload(api) from goodnews.db import connect, init_db connect(str(db)).close() # creates schema lazily? ensure init c = connect(str(db)); init_db(c); c.close() return api.create_app(), db def _count(db, **where): from goodnews.db import connect c = connect(str(db)) clause = " AND ".join(f"{k}=?" for k in where) sql = "SELECT COUNT(*) FROM events" + (f" WHERE {clause}" if where else "") n = c.execute(sql, tuple(where.values())).fetchone()[0] c.close() return n def test_event_recorded_and_deduped(app_db): app, db = app_db tc = TestClient(app) for _ in range(3): # same (kind, article, visitor, day) → one row assert tc.post("/api/events", json={"kind": "open", "article_id": 5, "visitor": "tok"}).json() == {"ok": True} assert _count(db, kind="open", article_id=5) == 1 # a different visitor is a distinct row tc.post("/api/events", json={"kind": "open", "article_id": 5, "visitor": "other"}) assert _count(db, kind="open", article_id=5) == 2 def test_visitor_token_is_hashed_not_stored_raw(app_db): app, db = app_db TestClient(app).post("/api/events", json={"kind": "visit", "visitor": "secret-token"}) from goodnews.db import connect c = connect(str(db)) vh = c.execute("SELECT visitor_hash FROM events").fetchone()[0] c.close() assert vh and vh != "secret-token" and len(vh) == 64 # sha256 hex def test_unknown_kind_is_ignored(app_db): app, db = app_db assert TestClient(app).post("/api/events", json={"kind": "evil", "visitor": "x"}).json() == {"ok": True} assert _count(db) == 0