Files
next-tjwater-server/tests/integration/test_investigation_reference_projection.py
T
jiang b853b61a0b feat(investigation): preserve cumulative reference graphs
Accumulate investigation paths and preserve failed attempts across rounds. Keep graph annotations separate from execution state, synchronize the API contract, and consolidate documentation.
2026-11-11 19:10:41 +08:00

85 lines
4.1 KiB
Python

"""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())