"""Opt-in read-only JSONB probes; no tables or business records are written.""" import asyncio import os from copy import deepcopy import pytest from psycopg import AsyncConnection from psycopg.rows import dict_row from psycopg.types.json import Jsonb from app.infra.db.postgresql import read_queries @pytest.mark.parametrize("section", ["progress", "report"]) def test_reference_annotations_are_normalized_before_nested_paging(section): dsn = os.environ.get("MCP_QUERY_TEST_DSN") if not dsn: pytest.skip("MCP_QUERY_TEST_DSN is required for a read-only PostgreSQL JSONB probe") async def check(): statuses = ["running", "completed", "waiting", "waiting_human", "skipped", "cancelled", "failed", None] original = {"section": section, "data": { "revision": 3, "execution_generation": 2, section: {"diagram": {"mermaid": "flowchart TD\nA[原图]", "node_refs": [ {"node_id": f"n{index}", "status": status, "detail": "保留原说明", "evidence_refs": ["e1"]} for index, status in enumerate(statuses) ] + [{"node_id": "unset"}]}}, "status": "running", "action": {"status": "completed"}, }} untouched = deepcopy(original) async with await AsyncConnection.connect( dsn, row_factory=dict_row, options="-c default_transaction_read_only=on", ) as conn: async def page(path=(), *, limit=50, offset=0, full=False): statement, params = read_queries._reference_diagram_projection( "SELECT %s::jsonb AS snapshot", (Jsonb(original),), section, ) return await read_queries._snapshot_page( conn, stored_sql=statement, stored_params=params, data_path=list(path), limit=limit, offset=offset, mcp_sources=[], full_snapshot=full, max_bytes=8000, ) scalar = await page([section, "diagram", "node_refs", "0", "status"]) assert scalar["value"] == "reference" second = await page([section, "diagram", "node_refs"], limit=1, offset=1) assert second["value"][0]["status"] == "recorded" assert second["total"] == 9 and second["nested"] assert second["value"][0]["evidence_refs"]["data_path"] == [section, "diagram", "node_refs", "1", "evidence_refs"] snapshot = await page(full=True) nodes = snapshot["data"][section]["diagram"]["node_refs"] assert [node.get("status") for node in nodes] == [ "reference", "recorded", "needs_input", "needs_input", "not_taken", "not_taken", "failed", None, None, ] assert "status" not in nodes[-1] assert nodes[6]["detail"] == "保留原说明" and nodes[6]["evidence_refs"] == ["e1"] assert snapshot["data"]["status"] == "running" assert snapshot["data"]["action"]["status"] == "completed" assert snapshot["data"][section]["diagram"]["mermaid"] == original["data"][section]["diagram"]["mermaid"] assert original == untouched asyncio.run(check()) @pytest.mark.parametrize("diagram", [None, {}, {"node_refs": []}]) def test_reference_projection_preserves_empty_and_missing_legacy_diagrams(diagram): dsn = os.environ.get("MCP_QUERY_TEST_DSN") if not dsn: pytest.skip("MCP_QUERY_TEST_DSN is required for a read-only PostgreSQL JSONB probe") async def check(): original = {"section": "progress", "data": {"progress": {"diagram": diagram}, "status": "running"}} async with await AsyncConnection.connect( dsn, row_factory=dict_row, options="-c default_transaction_read_only=on", ) as conn: statement, params = read_queries._reference_diagram_projection( "SELECT %s::jsonb AS snapshot", (Jsonb(original),), "progress", ) result = await read_queries._snapshot_page( conn, stored_sql=statement, stored_params=params, data_path=[], limit=20, offset=0, mcp_sources=[], full_snapshot=True, ) assert result == original asyncio.run(check())