Initial import of Politrack, a self-hosted service that tracks Austrian politicians from federal to municipal level - roles, votes, money flows, statements, meetings, social media and legal proceedings - with every claim linked to its sources. - backend: FastAPI + Postgres/pgvector; adapter framework (fetch -> parse -> load -> enrich) with robots.txt/TDM-aware fetching, a raw-response archive for reparsing, and a claim/evidence model with corroboration - sources: Parliament, Wikidata, EU Parliament/Commission, Council of Ministers, Rechnungshof party reports and donations, procurement, RTR media transparency, political ad libraries, WKStA, news feeds, Wikipedia edits, Bluesky, Mastodon, public Telegram channels, YouTube and X (official API) - web: Next.js frontend in German and English with typed API client - mcp: MCP server for AI agents and RAG - docs: architecture, data model, sources, legal notes, Unraid deployment and contributor guides; Docker Compose setup for Unraid Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
41 lines
2.1 KiB
Python
41 lines
2.1 KiB
Python
"""Golden tests: replay captured responses through adapter.parse() and compare with the expected records.
|
|
|
|
Fixtures live in tests/fixtures/<adapter>/<case>.request.json (+ the body file it names) and are captured
|
|
with `politrack fixture <adapter> <url> <case> --kind ...`. The expected output <case>.expected.json is
|
|
(re)generated with `UPDATE_GOLDEN=1 pytest tests/test_golden.py` - review the diff before committing it.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from politrack.core import registry
|
|
from politrack.core.records import to_jsonable
|
|
from politrack.core.runner import make_response, parse_offline
|
|
|
|
FIXTURES = Path(__file__).parent / "fixtures"
|
|
CASES = sorted(FIXTURES.glob("*/*.request.json"))
|
|
|
|
|
|
@pytest.mark.parametrize("request_file", CASES, ids=[f"{p.parent.name}:{p.name.removesuffix('.request.json')}"
|
|
for p in CASES])
|
|
def test_golden(request_file: Path) -> None:
|
|
spec = json.loads(request_file.read_text(encoding="utf-8"))
|
|
adapter = registry.get(request_file.parent.name)()
|
|
response = make_response(spec["url"], (request_file.parent / spec["body_file"]).read_bytes(), kind=spec["kind"],
|
|
meta=spec.get("meta"), content_type=spec.get("content_type", ""),
|
|
method=spec.get("method", "GET"), json_body=spec.get("json"), params=spec.get("params"))
|
|
actual = to_jsonable(parse_offline(adapter, response, spec.get("run_params")))
|
|
expected_file = request_file.with_name(request_file.name.replace(".request.json", ".expected.json"))
|
|
if os.environ.get("UPDATE_GOLDEN") or not expected_file.exists():
|
|
expected_file.write_text(json.dumps(actual, indent=1, ensure_ascii=False, sort_keys=True) + "\n",
|
|
encoding="utf-8")
|
|
if not os.environ.get("UPDATE_GOLDEN"):
|
|
pytest.skip(f"created {expected_file.name}; review it and re-run")
|
|
expected = json.loads(expected_file.read_text(encoding="utf-8"))
|
|
assert json.loads(json.dumps(actual, sort_keys=True)) == expected
|