feat: extend operational investigation workflows
This commit is contained in:
@@ -14,7 +14,6 @@ from scripts.sync_peripheral_work_orders import (
|
||||
sync_once,
|
||||
work_order_snapshot,
|
||||
)
|
||||
from samples.peripheral.seed_identity import SEED_ACTOR_ID
|
||||
from app.domain.schemas.operational_task import ExternalFeedbackCommand
|
||||
|
||||
|
||||
@@ -36,27 +35,21 @@ def test_documented_sync_command_can_start_from_repository_root():
|
||||
assert "--once" in result.stdout
|
||||
|
||||
|
||||
def test_unlinked_synthetic_checks_are_not_operational_tasks():
|
||||
def test_all_active_work_orders_are_operational_tasks():
|
||||
order_id = str(uuid4())
|
||||
order = {
|
||||
"work_order_id": order_id,
|
||||
"origin": "synthetic",
|
||||
"created_by": str(SEED_ACTOR_ID),
|
||||
"title": "合成压力偏差复核",
|
||||
"origin": "system_data",
|
||||
"title": "压力偏差复核",
|
||||
"work_order_type": "general",
|
||||
}
|
||||
|
||||
assert not should_project_work_order(order, set())
|
||||
assert 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()
|
||||
)
|
||||
assert should_project_work_order({**order, "title": "现场采样"}, set())
|
||||
|
||||
|
||||
def test_peripheral_order_id_ignores_foreign_and_malformed_namespaces():
|
||||
@@ -71,7 +64,7 @@ def test_peripheral_order_id_ignores_foreign_and_malformed_namespaces():
|
||||
async def test_sync_creates_one_task_and_only_applies_new_external_revisions():
|
||||
project_id, order_id, task_id = map(str, (uuid4(), uuid4(), uuid4()))
|
||||
tasks, writes = [], []
|
||||
order = {"project_id": project_id, "work_order_id": order_id, "origin": "synthetic",
|
||||
order = {"project_id": project_id, "work_order_id": order_id, "origin": "system_data",
|
||||
"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"}
|
||||
@@ -116,16 +109,15 @@ async def test_sync_creates_one_task_and_only_applies_new_external_revisions():
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_sync_skips_unlinked_synthetic_check_before_writing():
|
||||
async def test_sync_rejects_non_system_work_order_origin():
|
||||
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": "仅用于外围样板联调",
|
||||
"title": "来源异常工单",
|
||||
"summary": "验证来源边界",
|
||||
"priority": "normal",
|
||||
"status": "open",
|
||||
"created_at": "2026-09-23T08:00:00Z",
|
||||
@@ -135,7 +127,6 @@ async def test_sync_skips_unlinked_synthetic_check_before_writing():
|
||||
}
|
||||
|
||||
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})
|
||||
@@ -144,14 +135,8 @@ async def test_sync_skips_unlinked_synthetic_check_before_writing():
|
||||
"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
|
||||
with pytest.raises(ValueError, match="origin mismatch"):
|
||||
await sync_once(client, settings, "http://sample.test", CredentialProvider(settings))
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
@@ -231,7 +216,7 @@ async def test_sync_does_not_withdraw_when_missing_order_is_not_confirmed_delete
|
||||
async def test_sample_sync_rejects_cross_project_data_before_writing():
|
||||
def handler(request):
|
||||
assert request.method == "GET"
|
||||
return httpx.Response(200, json={"items": ([{"project_id": str(uuid4()), "origin": "synthetic"}]
|
||||
return httpx.Response(200, json={"items": ([{"project_id": str(uuid4()), "origin": "system_data"}]
|
||||
if request.url.host == "sample.test" else []), "next_offset": None})
|
||||
settings = WorkerSettings("http://server.test", "http://agent.test", str(uuid4()), bearer="TEST_ONLY")
|
||||
async with httpx.AsyncClient(transport=httpx.MockTransport(handler)) as client:
|
||||
@@ -263,11 +248,11 @@ async def test_created_work_order_receipt_preserves_projection_and_routes_active
|
||||
def handler(request):
|
||||
if request.url.host == "sample.test":
|
||||
order = {
|
||||
"project_id": project_id, "work_order_id": order_id, "origin": "synthetic",
|
||||
"project_id": project_id, "work_order_id": order_id, "origin": "system_data",
|
||||
"created_by": str(uuid4()),
|
||||
"revision": order_revision, "summary": "采样任务", "status": "completed" if order_revision > 1 else "open",
|
||||
"updated_at": "2026-09-23T09:00:00Z",
|
||||
"title": "合成采样工单", "priority": "normal",
|
||||
"title": "采样工单", "priority": "normal",
|
||||
"work_order_type": "general", "created_at": "2026-09-23T08:00:00Z",
|
||||
"scheduled_at": None,
|
||||
"activity": ([{"sequence": 2, "occurred_at": "2026-09-23T09:00:00Z", "actor": "现场人员",
|
||||
@@ -320,13 +305,15 @@ async def test_external_events_start_investigations_without_browser(event_kind):
|
||||
return httpx.Response(200, json={"items": [], "next_offset": None})
|
||||
if request.url.host == "sample.test":
|
||||
return httpx.Response(200, json={"items": [{
|
||||
"project_id": project_id, "work_order_id": order_id, "origin": "synthetic", "revision": 1,
|
||||
"project_id": project_id, "work_order_id": order_id, "origin": "system_data", "revision": 1,
|
||||
"title": "外围测试事件", "summary": "模拟外部系统上报", "priority": "urgent", "status": "open",
|
||||
"created_at": "2026-09-23T08:00:00Z", "updated_at": "2026-09-23T08:00:00Z",
|
||||
"scheduled_at": "2026-09-23T07:30:00Z", "work_order_type": event_kind, "location": "测试位置",
|
||||
}], "next_cursor": None})
|
||||
payload = json.loads(request.content)
|
||||
if request.url.path.endswith("external-feedback"):
|
||||
assert payload["evidence"]["kind"] == "external_feedback"
|
||||
assert payload["work_order_snapshot"]["origin"] == "system_data"
|
||||
return httpx.Response(200, json={})
|
||||
task = {**payload, "task_id": str(uuid4())}
|
||||
created.append(task)
|
||||
@@ -339,6 +326,7 @@ async def test_external_events_start_investigations_without_browser(event_kind):
|
||||
assert investigation["workflow"] == event_kind
|
||||
assert investigation["source"]["external_id"] == order_id
|
||||
assert investigation["trigger"]["occurred_at"] == "2026-09-23T07:30:00Z"
|
||||
assert all("合成" not in item["summary"] for item in created)
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
@@ -349,7 +337,7 @@ async def test_child_feedback_retry_keeps_projection_cursor_behind_all_parent_de
|
||||
projection = {"task_id": projection_id, "task_type": "external_work_order", "source": {
|
||||
"external_id": f"peripheral_sample:{order_id}", "external_revision": 1,
|
||||
}}
|
||||
order = {"project_id": project_id, "work_order_id": order_id, "origin": "synthetic",
|
||||
order = {"project_id": project_id, "work_order_id": order_id, "origin": "system_data",
|
||||
"revision": 2, "title": "采样", "summary": "复测通过", "status": "completed", "priority": "normal",
|
||||
"created_at": "2026-10-10T00:00:00Z", "updated_at": "2026-10-10T01:00:00Z", "scheduled_at": None,
|
||||
"work_order_type": "general"}
|
||||
@@ -398,8 +386,8 @@ async def test_child_feedback_retry_keeps_projection_cursor_behind_all_parent_de
|
||||
|
||||
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",
|
||||
"work_order_id": str(uuid4()), "revision": 4, "title": "预约工单", "summary": "仅测试过程同步",
|
||||
"status": "in_progress", "priority": "normal", "origin": "system_data",
|
||||
"updated_at": "2026-09-23T09:00:00Z", "work_order_type": "general",
|
||||
"feedback_requirements": ["回填现场检查结果"],
|
||||
"activity": [{
|
||||
@@ -411,7 +399,7 @@ def test_snapshot_preserves_recorded_activity_without_legacy_subtasks():
|
||||
"detail": "已创建现场检查工单。",
|
||||
}],
|
||||
"subtasks": [{
|
||||
"id": "visit", "title": "现场检查", "summary": "合成测试,未现场执行",
|
||||
"id": "visit", "title": "现场检查", "summary": "测试数据,未现场执行",
|
||||
"status": "pending", "scheduled_at": "2026-09-24T08:00:00Z",
|
||||
"location": "测试位置", "assignee": "测试班组", "duration_minutes": 30,
|
||||
"reply_window_minutes": 15, "reply_requirements": ["检查记录"],
|
||||
@@ -420,6 +408,7 @@ def test_snapshot_preserves_recorded_activity_without_legacy_subtasks():
|
||||
}],
|
||||
}
|
||||
snapshot = work_order_snapshot(order)
|
||||
assert snapshot["origin"] == "system_data"
|
||||
assert snapshot["feedback_requirements"] == ["回填现场检查结果"]
|
||||
assert snapshot["activity"] == order["activity"]
|
||||
assert "subtasks" not in snapshot
|
||||
|
||||
Reference in New Issue
Block a user