fix(workbench): remove synthetic check projections

This commit is contained in:
2026-09-24 12:01:01 +08:00
parent 1a6ff64bbe
commit e374b5b3cb
25 changed files with 953 additions and 935 deletions
+102 -11
View File
@@ -5,7 +5,12 @@ import httpx
import pytest
from scripts.run_operational_worker import CredentialProvider, WorkerSettings
from scripts.sync_peripheral_work_orders import sync_once, work_order_snapshot
from scripts.sync_peripheral_work_orders import (
should_project_work_order,
sync_once,
work_order_snapshot,
)
from samples.peripheral.seed_identity import SEED_ACTOR_ID
from app.domain.schemas.operational_task import ExternalFeedbackCommand
@@ -14,6 +19,29 @@ def anyio_backend():
return "asyncio"
def test_unlinked_synthetic_checks_are_not_operational_tasks():
order_id = str(uuid4())
order = {
"work_order_id": order_id,
"origin": "synthetic",
"created_by": str(SEED_ACTOR_ID),
"title": "合成压力偏差复核",
"work_order_type": "general",
}
assert not should_project_work_order(order, set())
assert should_project_work_order(order, {order_id})
assert should_project_work_order(
{**order, "work_order_type": "water_quality_complaint"}, set()
)
assert should_project_work_order(
{**order, "title": "现场采样:二次供水出水及末端用户水样"}, set()
) is False
assert should_project_work_order(
{**order, "created_by": str(uuid4()), "title": "合成管材接驳预约"}, set()
)
@pytest.mark.anyio
async def test_sync_creates_one_task_and_only_applies_new_external_revisions():
project_id, order_id, task_id = map(str, (uuid4(), uuid4(), uuid4()))
@@ -55,12 +83,52 @@ async def test_sync_creates_one_task_and_only_applies_new_external_revisions():
assert snapshot["assignee"] == "测试班组"
assert snapshot["location"] == "测试位置"
assert snapshot["scheduled_at"] == "2026-09-24T08:00:00Z"
assert snapshot["subtasks"] is None
assert snapshot["feedback_requirements"] == []
assert snapshot["activity"] == []
# Exercise the real API schema, including the prefixed external identity.
accepted = ExternalFeedbackCommand.model_validate(writes[-1][1])
assert str(accepted.work_order_snapshot.work_order_id) == order_id
@pytest.mark.anyio
async def test_sync_skips_unlinked_synthetic_check_before_writing():
project_id, order_id = str(uuid4()), str(uuid4())
order = {
"project_id": project_id,
"work_order_id": order_id,
"origin": "synthetic",
"created_by": str(SEED_ACTOR_ID),
"revision": 1,
"title": "合成通信核查",
"summary": "仅用于外围样板联调",
"priority": "normal",
"status": "open",
"created_at": "2026-09-23T08:00:00Z",
"updated_at": "2026-09-23T08:00:00Z",
"scheduled_at": None,
"work_order_type": "general",
}
def handler(request):
assert request.method == "GET"
if request.url.host == "sample.test":
return httpx.Response(200, json={"items": [order], "next_cursor": None})
return httpx.Response(200, json={"items": [], "next_offset": None})
settings = WorkerSettings(
"http://server.test", "http://agent.test", project_id, bearer="TEST_ONLY"
)
async with httpx.AsyncClient(transport=httpx.MockTransport(handler)) as client:
synchronized = await sync_once(
client,
settings,
"http://sample.test",
CredentialProvider(settings),
)
assert synchronized == 0
@pytest.mark.anyio
async def test_sample_sync_rejects_cross_project_data_before_writing():
def handler(request):
@@ -74,17 +142,29 @@ async def test_sample_sync_rejects_cross_project_data_before_writing():
@pytest.mark.anyio
@pytest.mark.parametrize("investigation_status", ["waiting_human", "queued"])
async def test_created_work_order_receipt_routes_feedback_back_to_investigation(investigation_status):
@pytest.mark.parametrize(
("investigation_status", "expected_feedback_count", "routes_to_investigation"),
[
("waiting_human", 2, True),
("queued", 2, True),
("completed", 1, False),
],
)
async def test_created_work_order_receipt_preserves_projection_and_routes_active_feedback(
investigation_status,
expected_feedback_count,
routes_to_investigation,
):
project_id, order_id, investigation_id, projection_id = map(str, (uuid4(), uuid4(), uuid4(), uuid4()))
received = []
def handler(request):
if request.url.host == "sample.test":
return httpx.Response(200, json={"items": [{
"project_id": project_id, "work_order_id": order_id, "origin": "synthetic",
"created_by": str(uuid4()),
"revision": 2, "summary": "采样已完成", "status": "completed", "updated_at": "2026-09-23T09:00:00Z",
"title": "合成采样工单", "priority": "normal",
"work_order_type": "water_quality_complaint", "created_at": "2026-09-23T08:00:00Z",
"work_order_type": "general", "created_at": "2026-09-23T08:00:00Z",
"scheduled_at": None,
}], "next_cursor": None})
if request.method == "POST":
@@ -106,8 +186,10 @@ async def test_created_work_order_receipt_routes_feedback_back_to_investigation(
], "next_offset": None})
settings = WorkerSettings("http://server.test", "http://agent.test", project_id, bearer="TEST_ONLY")
async with httpx.AsyncClient(transport=httpx.MockTransport(handler)) as client:
assert await sync_once(client, settings, "http://sample.test", CredentialProvider(settings)) == 2
assert any(investigation_id in path for path in received)
assert await sync_once(
client, settings, "http://sample.test", CredentialProvider(settings)
) == expected_feedback_count
assert any(investigation_id in path for path in received) is routes_to_investigation
assert any(projection_id in path for path in received)
@@ -142,11 +224,20 @@ async def test_external_events_start_investigations_without_browser(event_kind):
assert investigation["trigger"]["occurred_at"] == "2026-09-23T07:30:00Z"
def test_snapshot_preserves_nested_process_without_inventing_completed_stages():
def test_snapshot_preserves_recorded_activity_without_legacy_subtasks():
order = {
"work_order_id": str(uuid4()), "revision": 4, "title": "合成预约", "summary": "仅测试过程同步",
"status": "in_progress", "priority": "normal", "origin": "synthetic",
"updated_at": "2026-09-23T09:00:00Z", "work_order_type": "general",
"feedback_requirements": ["回填现场检查结果"],
"activity": [{
"sequence": 1,
"occurred_at": "2026-09-23T08:00:00Z",
"actor": "测试调度",
"kind": "created",
"title": "工单创建",
"detail": "已创建现场检查工单。",
}],
"subtasks": [{
"id": "visit", "title": "现场检查", "summary": "合成测试,未现场执行",
"status": "pending", "scheduled_at": "2026-09-24T08:00:00Z",
@@ -157,6 +248,6 @@ def test_snapshot_preserves_nested_process_without_inventing_completed_stages():
}],
}
snapshot = work_order_snapshot(order)
assert snapshot["subtasks"] == order["subtasks"]
assert snapshot["subtasks"][0]["stages"][0]["status"] == "pending"
assert work_order_snapshot({key: value for key, value in order.items() if key != "subtasks"})["subtasks"] is None
assert snapshot["feedback_requirements"] == ["回填现场检查结果"]
assert snapshot["activity"] == order["activity"]
assert "subtasks" not in snapshot