"""Opt-in boundary checks for the synthetic-check cleanup migration.""" import json import os from datetime import datetime, timezone from pathlib import Path from uuid import UUID, uuid4 import psycopg import pytest from psycopg.rows import dict_row from app.core.config import get_pgconn_string from samples.peripheral.seed_identity import SEED_ACTOR_ID pytestmark = pytest.mark.skipif( os.environ.get("OPERATIONAL_TASKS_STORAGE_TEST") != "1", reason="explicit PostgreSQL test opt-in required", ) @pytest.fixture def anyio_backend(): return "asyncio" def _migration_sql(*names: str) -> str: root = Path(__file__).parents[2] sql = "\n".join((root / f"resources/sql/{name}").read_text() for name in names) return sql.replace("\nBEGIN;", "").replace("\nCOMMIT;", "") async def _insert_task( conn: psycopg.AsyncConnection, *, project_id: UUID, external_id: str, event_kind: str, title: str, task_type: str = "external_work_order", ) -> UUID: task_id = uuid4() request_id = uuid4() execution_request_id = uuid4() now = datetime.now(timezone.utc) document = { "project_id": str(project_id), "task_id": str(task_id), "task_type": task_type, "workflow": "external-work-order" if task_type == "external_work_order" else event_kind, "status": "completed", "lifecycle_status": "completed", "priority": "normal", "revision": 1, "source": { "kind": "external_system", "external_id": external_id, "event_kind": event_kind, }, "title": title, } await conn.execute( """INSERT INTO workbench.operational_tasks ( project_id,task_id,create_request_id,task_type,workflow,status,priority, revision,execution_generation,execution_request_id,created_at,updated_at, document,lifecycle_status ) VALUES (%s,%s,%s,%s,%s,'completed','normal',1,1,%s,%s,%s,%s::jsonb,'completed')""", ( project_id, task_id, request_id, task_type, document["workflow"], execution_request_id, now, now, json.dumps(document), ), ) return task_id async def _insert_source_order( conn: psycopg.AsyncConnection, *, project_id: UUID, work_order_id: UUID, title: str, created_by: UUID = SEED_ACTOR_ID, deleted: bool = True, ) -> None: now = datetime.now(timezone.utc) document = { "project_id": str(project_id), "work_order_id": str(work_order_id), "revision": 1, "title": title, "created_by": str(created_by), "origin": "synthetic", } await conn.execute( """INSERT INTO peripheral_sample.work_orders ( project_id,work_order_id,revision,document,deleted_at ) VALUES (%s,%s,1,%s::jsonb,%s)""", (project_id, work_order_id, json.dumps(document), now if deleted else None), ) @pytest.mark.anyio async def test_cleanup_deletes_only_unlinked_exact_namespace_and_is_idempotent(): base_migration = _migration_sql( "014_operational_tasks.sql", "015_workbench_events_and_task_runs.sql", ) cleanup_migration = _migration_sql("018_remove_synthetic_check_task_projections.sql") project_id = uuid4() conn = await psycopg.AsyncConnection.connect( get_pgconn_string(), row_factory=dict_row, connect_timeout=3 ) try: async with conn.transaction(force_rollback=True): await conn.execute(base_migration) await conn.execute("CREATE SCHEMA IF NOT EXISTS peripheral_sample") await conn.execute( """CREATE TABLE IF NOT EXISTS peripheral_sample.work_orders ( project_id uuid NOT NULL, work_order_id uuid NOT NULL, revision bigint NOT NULL, document jsonb NOT NULL, deleted_at timestamptz, PRIMARY KEY (project_id,work_order_id) )""" ) removed_order_id = uuid4() await _insert_source_order( conn, project_id=project_id, work_order_id=removed_order_id, title="合成通信核查", deleted=False, ) removed_id = await _insert_task( conn, project_id=project_id, external_id=f"peripheral_sample:{removed_order_id}", event_kind="general", title=" 合成通信核查", ) deleted_artifact_order_id = uuid4() await _insert_source_order( conn, project_id=project_id, work_order_id=deleted_artifact_order_id, title="合成联调:预约工单编辑已对齐", created_by=uuid4(), ) deleted_user_order_id = await _insert_task( conn, project_id=project_id, external_id=f"peripheral_sample:{deleted_artifact_order_id}", event_kind="general", title="合成联调:预约工单编辑已对齐", ) suffix_alias_id = await _insert_task( conn, project_id=project_id, external_id=f"peripheral_sample:{removed_order_id}:suffix", event_kind="general", title="合成但不是规范来源标识", ) similar_namespace_id = await _insert_task( conn, project_id=project_id, external_id=f"peripheralXsample:{uuid4()}", event_kind="general", title="合成但属于其他命名空间", ) event_order_id = uuid4() await _insert_source_order( conn, project_id=project_id, work_order_id=event_order_id, title="合成水质投诉", ) event_id = await _insert_task( conn, project_id=project_id, external_id=f"peripheral_sample:{event_order_id}", event_kind="water_quality_complaint", title="合成水质投诉", ) linked_order_id = uuid4() await _insert_source_order( conn, project_id=project_id, work_order_id=linked_order_id, title="合成现场采样", ) linked_id = await _insert_task( conn, project_id=project_id, external_id=f"peripheral_sample:{linked_order_id}", event_kind="general", title="合成现场采样", ) normal_order_id = uuid4() await _insert_source_order( conn, project_id=project_id, work_order_id=normal_order_id, title="常规预约", created_by=uuid4(), ) normal_id = await _insert_task( conn, project_id=project_id, external_id=f"peripheral_sample:{normal_order_id}", event_kind="general", title="常规预约", ) action_id = uuid4() now = datetime.now(timezone.utc) action_document = { "action_id": str(action_id), "version": 1, "kind": "create_work_order", "approval_status": "approved", "execution_status": "succeeded", "receipt": { "adapter": "peripheral_sample", "receipt_id": str(linked_order_id), }, } await conn.execute( """INSERT INTO workbench.operational_task_actions ( project_id,task_id,action_id,version,approval_status,execution_status, document,created_at,updated_at ) VALUES (%s,%s,%s,1,'approved','succeeded',%s::jsonb,%s,%s)""", (project_id, linked_id, action_id, json.dumps(action_document), now, now), ) await conn.execute( """INSERT INTO workbench.operational_task_requests ( project_id,scope,request_id,request_digest,response ) VALUES (%s,%s,%s,'test',%s::jsonb)""", ( project_id, f"task:{removed_id}:create", uuid4(), json.dumps({"task_id": str(removed_id)}), ), ) await conn.execute( """INSERT INTO workbench.operational_task_requests ( project_id,scope,request_id,request_digest,response ) VALUES (%s,%s,%s,'test',%s::jsonb)""", ( project_id, f"notification:{uuid4()}:ack", uuid4(), json.dumps({"notification": {"task_id": str(removed_id)}}), ), ) await conn.execute(cleanup_migration) rows = await ( await conn.execute( """SELECT task_id FROM workbench.operational_tasks WHERE project_id=%s AND task_id=ANY(%s)""", ( project_id, [ removed_id, deleted_user_order_id, suffix_alias_id, similar_namespace_id, event_id, linked_id, normal_id, ], ), ) ).fetchall() assert {row["task_id"] for row in rows} == { similar_namespace_id, deleted_user_order_id, suffix_alias_id, event_id, linked_id, normal_id, } request_count = await ( await conn.execute( """SELECT count(*) AS count FROM workbench.operational_task_requests WHERE project_id=%s AND ( response->>'task_id'=%s OR response#>>'{notification,task_id}'=%s )""", (project_id, str(removed_id), str(removed_id)), ) ).fetchone() assert request_count["count"] == 0 cleanup_event_count = await ( await conn.execute( """SELECT count(*) AS count FROM workbench.domain_event_log WHERE project_id=%s AND metadata->>'reason'='synthetic_check_projection_cleanup'""", (project_id,), ) ).fetchone() assert cleanup_event_count["count"] == 1 await conn.execute("DROP TABLE synthetic_check_task_cleanup") await conn.execute(cleanup_migration) repeated_event_count = await ( await conn.execute( """SELECT count(*) AS count FROM workbench.domain_event_log WHERE project_id=%s AND metadata->>'reason'='synthetic_check_projection_cleanup'""", (project_id,), ) ).fetchone() assert repeated_event_count["count"] == 1 finally: await conn.close()