"""Provenance chain: sensors → snapshots (text/blocks from the archive) → diffs → changes → events.""" from __future__ import annotations import pytest import test_api_support as support client = support.client fixture_data = support.fixture_data pytestmark = pytest.mark.asyncio(loop_scope="session") V = "/api/v1" async def test_sensor_detail_snapshots_changes(client, fixture_data): # type: ignore[no-untyped-def] sid = fixture_data["sensor_home"] r = await client.get(f"{V}/sensors/{sid}") body = r.json() assert r.status_code == 200 and body["company"]["slug"] == fixture_data["alpha_slug"] and body["latest_snapshot"]["id"] == fixture_data["snap3"] assert body["tier"] == "C" and body["surface"] == "pricing" snaps = (await client.get(f"{V}/sensors/{sid}/snapshots")).json()["items"] assert [s["version_no"] for s in snaps] == [3, 2, 1] assert {"id", "sensor_id", "version_no", "fetched_at", "title", "language", "text_length", "block_count", "extracted_summary", "content_hash", "previous_snapshot_id"} <= set(snaps[0]) assert snaps[1]["previous_snapshot_id"] == fixture_data["snap1"] changes = (await client.get(f"{V}/sensors/{sid}/changes")).json()["items"] assert changes[0]["id"] == fixture_data["change"] and changes[0]["kind"] == "major" and "diff" not in changes[0] assert (await client.get(f"{V}/sensors/{sid}/changes?min_significance=0.9")).json()["items"] == [] assert (await client.get(f"{V}/sensors/sen_nope")).status_code == 404 async def test_snapshot_detail_reads_archive(client, fixture_data): # type: ignore[no-untyped-def] r = await client.get(f"{V}/snapshots/{fixture_data['snap2']}") body = r.json() assert r.status_code == 200 assert "Starter plan $12 per month" in body["text"] and body["text_truncated"] is False assert [b["key"] for b in body["blocks"]] == ["b1", "b2", "b3", "b4"] and body["blocks"][1]["kind"] == "pricing_plan" assert body["extracted"] == {"plans": [{"plan_name": "Starter"}]} and body["extracted_summary"] == {"plan_count": 3} assert body["sensor"]["surface"] == "pricing" lite = (await client.get(f"{V}/snapshots/{fixture_data['snap2']}?include=extracted")).json() assert "text" not in lite and "blocks" not in lite assert (await client.get(f"{V}/snapshots/snap_nope")).status_code == 404 async def test_snapshot_diff_stored_and_computed(client, fixture_data): # type: ignore[no-untyped-def] r = await client.get(f"{V}/snapshots/{fixture_data['snap1']}/diff/{fixture_data['snap2']}") body = r.json() assert r.status_code == 200 and body["source"] == "change" and body["change_id"] == fixture_data["change"] assert body["before"]["id"] == fixture_data["snap1"] and body["after"]["id"] == fixture_data["snap2"] assert body["diff"]["counts"] == {"added": 1, "removed": 0, "modified": 1, "moved": 0} and body["diff"]["significance"] == 0.72 reverse = (await client.get(f"{V}/snapshots/{fixture_data['snap2']}/diff/{fixture_data['snap1']}")).json() assert reverse["before"]["id"] == fixture_data["snap1"] r = await client.get(f"{V}/snapshots/{fixture_data['snap2']}/diff/{fixture_data['snap3']}") assert r.status_code in (200, 501) if r.status_code == 200: d = r.json() assert d["source"] == "computed" for key in ("added", "removed", "modified", "moved", "counts", "text_delta_ratio", "similarity", "reasons"): assert key in d["diff"], key assert any(x["key"] == "b5" for x in d["diff"]["added"]) assert (await client.get(f"{V}/snapshots/{fixture_data['snap1']}/diff/snap_nope")).status_code == 404 async def test_change_detail(client, fixture_data): # type: ignore[no-untyped-def] r = await client.get(f"{V}/changes/{fixture_data['change']}") body = r.json() assert r.status_code == 200 for key in ("id", "sensor_id", "surface", "company_id", "detected_at", "significance", "kind", "blocks_added", "blocks_removed", "blocks_modified", "text_delta_ratio", "similarity", "snapshot_before", "snapshot_after", "diff", "structured_delta", "events"): assert key in body, key assert body["structured_delta"]["plans"]["price_changed"][0]["pct"] == 20 assert [e["id"] for e in body["events"]] == [fixture_data["ev_pricing"]] assert body["company"]["slug"] == fixture_data["alpha_slug"] assert (await client.get(f"{V}/changes/chg_nope")).status_code == 404