fix(workbench): remove synthetic check projections
This commit is contained in:
@@ -647,27 +647,9 @@ def _work_order_snapshot(work_order_id, now):
|
||||
"work_order_type": "water_quality_complaint",
|
||||
"location": "联调小区",
|
||||
"scenario_code": "WQ-SECONDARY-SUPPLY-001",
|
||||
"subtasks": [{
|
||||
"id": "sampling",
|
||||
"title": "现场采样",
|
||||
"summary": "采集并复测",
|
||||
"status": "running",
|
||||
"scheduled_at": (now + timedelta(hours=1)).isoformat(),
|
||||
"location": "联调小区",
|
||||
"assignee": "水质班组",
|
||||
"duration_minutes": 60,
|
||||
"reply_window_minutes": 120,
|
||||
"stages": [{
|
||||
"id": "reply",
|
||||
"sequence": 1,
|
||||
"title": "等待回令",
|
||||
"status": "current",
|
||||
"owner": "水质班组",
|
||||
"time_label": "待反馈",
|
||||
"description": "上传复测结果",
|
||||
}],
|
||||
"reply_requirements": ["上传检测结果"],
|
||||
}],
|
||||
"feedback_requirements": ["上传检测结果"],
|
||||
"activity": [{"sequence": 1, "occurred_at": now.isoformat(), "actor": "水质班组",
|
||||
"kind": "feedback", "title": "现场反馈", "detail": "上传复测结果"}],
|
||||
"origin": "synthetic",
|
||||
"updated_at": now.isoformat(),
|
||||
}
|
||||
@@ -707,7 +689,8 @@ async def test_external_feedback_exposes_validated_work_order_snapshot_and_refre
|
||||
assert result.task.scheduled_for == now + timedelta(hours=1)
|
||||
assert result.task.priority == "urgent"
|
||||
assert result.task.work_order_snapshot is not None
|
||||
assert result.task.work_order_snapshot.subtasks[0].stages[0].status == "current"
|
||||
assert result.task.work_order_snapshot.activity[0].title == "现场反馈"
|
||||
assert result.task.work_order_snapshot.feedback_requirements == ["上传检测结果"]
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
|
||||
@@ -25,7 +25,7 @@ class ManagedStore:
|
||||
return command.model_dump(mode="json", exclude={"request_id"}) | {
|
||||
"work_order_id": str(uuid4()), "project_id": str(PROJECT_ID), "revision": 1,
|
||||
"origin": "synthetic", "created_at": now, "updated_at": now,
|
||||
"created_by": str(USER_ID), "updated_by": str(USER_ID),
|
||||
"created_by": str(USER_ID), "updated_by": str(USER_ID), "activity": [],
|
||||
}
|
||||
|
||||
def update_work_order(self, who, work_order_id, command):
|
||||
@@ -35,7 +35,7 @@ class ManagedStore:
|
||||
"status": command.status or "open", "priority": command.priority or "normal",
|
||||
"assignee": None, "scheduled_at": None, "work_order_type": "general",
|
||||
"location": command.location, "scenario_code": command.scenario_code,
|
||||
"subtasks": command.subtasks}})()) | {"revision": command.expected_revision + 1,
|
||||
"feedback_requirements": command.feedback_requirements or []}})()) | {"revision": command.expected_revision + 1,
|
||||
"work_order_id": str(work_order_id)}
|
||||
|
||||
def delete_work_order(self, who, work_order_id, command):
|
||||
@@ -86,32 +86,25 @@ def test_managed_work_order_and_data_routes_are_typed_crud():
|
||||
"status": "open", "priority": "high", "assignee": None, "scheduled_at": None,
|
||||
"work_order_type": "water_quality_complaint", "location": "演示小区",
|
||||
"scenario_code": "WQ-SECONDARY-SUPPLY-001",
|
||||
"subtasks": [{
|
||||
"id": "sampling", "title": "现场采样", "summary": "采集二次供水样本",
|
||||
"status": "running", "scheduled_at": "2026-09-18T01:00:00Z",
|
||||
"location": "演示小区", "assignee": "水质班组", "duration_minutes": 60,
|
||||
"reply_window_minutes": 120, "reply_requirements": ["上传检测结果"],
|
||||
"stages": [{"id": "dispatch", "sequence": 1, "title": "派发",
|
||||
"status": "current", "owner": "调度员", "time_label": "待执行",
|
||||
"description": "确认采样人员"}],
|
||||
}],
|
||||
"feedback_requirements": ["上传检测结果"],
|
||||
})
|
||||
assert created.status_code == 201 and created.json()["origin"] == "synthetic"
|
||||
assert created.json()["work_order_type"] == "water_quality_complaint"
|
||||
assert created.json()["location"] == "演示小区"
|
||||
assert created.json()["scenario_code"] == "WQ-SECONDARY-SUPPLY-001"
|
||||
assert created.json()["subtasks"][0]["stages"][0]["status"] == "current"
|
||||
assert created.json()["feedback_requirements"] == ["上传检测结果"]
|
||||
assert "subtasks" not in created.json()
|
||||
work_order_id = created.json()["work_order_id"]
|
||||
changed = client.patch(f"/api/sample/v1/work-orders/{work_order_id}", json={
|
||||
"request_id": str(uuid4()), "expected_revision": 1, "status": "awaiting_reply",
|
||||
"location": "复测点", "clear_scenario_code": True, "clear_subtasks": True,
|
||||
"location": "复测点", "clear_scenario_code": True, "progress_note": "等待现场检测结果",
|
||||
"clear_assignee": False, "clear_scheduled_at": False,
|
||||
})
|
||||
assert changed.status_code == 200 and changed.json()["revision"] == 2
|
||||
assert changed.json()["status"] == "awaiting_reply"
|
||||
update_command = next(call[3] for call in fake.calls if call[0] == "update_work_order")
|
||||
assert update_command.location == "复测点"
|
||||
assert update_command.clear_scenario_code and update_command.clear_subtasks
|
||||
assert update_command.clear_scenario_code and update_command.progress_note == "等待现场检测结果"
|
||||
deleted = client.request("DELETE", f"/api/sample/v1/work-orders/{work_order_id}", json={
|
||||
"request_id": str(uuid4()), "expected_revision": 2,
|
||||
})
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
from uuid import uuid4
|
||||
|
||||
from samples.peripheral.auth import Identity
|
||||
from samples.peripheral.config import SampleSettings
|
||||
from samples.peripheral.schemas import CreateWorkOrder, UpdateWorkOrder, WorkOrder
|
||||
from samples.peripheral.store import PostgresStore
|
||||
|
||||
|
||||
def test_create_and_update_record_flat_order_activity(monkeypatch):
|
||||
store = PostgresStore(SampleSettings())
|
||||
actor = Identity(str(uuid4()), str(uuid4()), True)
|
||||
saved = {}
|
||||
|
||||
monkeypatch.setattr(store, "_mutate", lambda who, key, command, operation: operation(None, "normal"))
|
||||
|
||||
def save(_conn, _who, document):
|
||||
saved.clear()
|
||||
saved.update(WorkOrder.model_validate(document).model_dump(mode="json"))
|
||||
return dict(saved)
|
||||
|
||||
monkeypatch.setattr(store, "_save_work_order", save)
|
||||
monkeypatch.setattr(store, "_managed_document", lambda *_args: dict(saved))
|
||||
|
||||
created = store.create_work_order(actor, CreateWorkOrder(
|
||||
request_id=uuid4(), title="阀门预约复核", summary="现场复核开度",
|
||||
location="北区阀门", assignee="北区班组",
|
||||
feedback_requirements=["回传开度照片"],
|
||||
))
|
||||
assert created["feedback_requirements"] == ["回传开度照片"]
|
||||
assert created["activity"][0]["kind"] == "created"
|
||||
assert "subtasks" not in created
|
||||
|
||||
updated = store.update_work_order(actor, created["work_order_id"], UpdateWorkOrder(
|
||||
request_id=uuid4(), expected_revision=1, status="awaiting_reply",
|
||||
progress_note="已复核开度,等待回执确认",
|
||||
))
|
||||
assert updated["status"] == "awaiting_reply"
|
||||
assert [entry["sequence"] for entry in updated["activity"]] == [1, 2]
|
||||
assert updated["activity"][1]["kind"] == "feedback"
|
||||
assert updated["activity"][1]["detail"] == "已复核开度,等待回执确认"
|
||||
assert "progress_note" not in updated
|
||||
|
||||
|
||||
def test_legacy_order_reads_flat_without_claiming_unrecorded_progress():
|
||||
legacy = {"work_order_id": str(uuid4()), "subtasks": [{"stages": [{"status": "done"}]}]}
|
||||
projected = PostgresStore._active_managed_document(legacy)
|
||||
assert "subtasks" not in projected
|
||||
assert projected["feedback_requirements"] == []
|
||||
assert projected["activity"] == []
|
||||
@@ -1,12 +1,10 @@
|
||||
from contextlib import contextmanager
|
||||
from uuid import uuid4
|
||||
|
||||
import pytest
|
||||
|
||||
from scripts.seed_peripheral_sample import (
|
||||
ACTOR_ID,
|
||||
previous_seed_result,
|
||||
restore_seeded_resources,
|
||||
restore_seeded_test_data,
|
||||
)
|
||||
|
||||
|
||||
@@ -42,8 +40,8 @@ class FakeStore:
|
||||
def test_previous_seed_result_reuses_legacy_receipt():
|
||||
resource_id = str(uuid4())
|
||||
store = FakeStore({
|
||||
"command_key": "create_work_order",
|
||||
"result": {"work_order_id": resource_id},
|
||||
"command_key": "create_test_data",
|
||||
"result": {"record_id": resource_id},
|
||||
})
|
||||
seed_request_id = uuid4()
|
||||
|
||||
@@ -51,10 +49,10 @@ def test_previous_seed_result_reuses_legacy_receipt():
|
||||
store,
|
||||
"project-1",
|
||||
seed_request_id=seed_request_id,
|
||||
command_key="create_work_order",
|
||||
command_key="create_test_data",
|
||||
)
|
||||
|
||||
assert result == {"work_order_id": resource_id}
|
||||
assert result == {"record_id": resource_id}
|
||||
assert store.readonly is True
|
||||
assert store.connection_value.calls[0][1] == (
|
||||
"project-1",
|
||||
@@ -63,29 +61,14 @@ def test_previous_seed_result_reuses_legacy_receipt():
|
||||
)
|
||||
|
||||
|
||||
def test_restore_seeded_resources_only_restores_allowed_tables():
|
||||
def test_restore_seeded_test_data_clears_soft_delete_fields():
|
||||
store = FakeStore()
|
||||
resource_ids = [str(uuid4()), str(uuid4())]
|
||||
|
||||
restore_seeded_resources(
|
||||
store,
|
||||
"project-1",
|
||||
table="work_orders",
|
||||
id_column="work_order_id",
|
||||
resource_ids=resource_ids,
|
||||
)
|
||||
restore_seeded_test_data(store, "project-1", resource_ids)
|
||||
|
||||
statement, parameters = store.connection_value.calls[0]
|
||||
assert "UPDATE peripheral_sample.work_orders" in statement
|
||||
assert "UPDATE peripheral_sample.test_data_records" in statement
|
||||
assert "document=document - 'deleted_at' - 'deleted_by'" in statement
|
||||
assert "deleted_at=NULL, deleted_by=NULL" in statement
|
||||
assert parameters == ("project-1", resource_ids)
|
||||
|
||||
with pytest.raises(ValueError, match="unsupported seeded resource"):
|
||||
restore_seeded_resources(
|
||||
store,
|
||||
"project-1",
|
||||
table="requests",
|
||||
id_column="request_id",
|
||||
resource_ids=resource_ids,
|
||||
)
|
||||
|
||||
@@ -28,25 +28,18 @@ def test_build_demo_work_orders_restores_original_mock_tasks():
|
||||
"running",
|
||||
"awaiting_reply",
|
||||
]
|
||||
assert all(len(document["subtasks"]) == 3 for document in documents)
|
||||
assert all(len(document["feedback_requirements"]) == 3 for document in documents)
|
||||
assert all("subtasks" not in document for document in documents)
|
||||
assert all("合成" not in str(document) for document in documents)
|
||||
|
||||
|
||||
def test_demo_work_orders_keep_one_current_stage_per_active_task():
|
||||
def test_demo_work_orders_show_recorded_activity_without_synthetic_stages():
|
||||
documents = build_demo_work_orders()
|
||||
|
||||
for document in documents:
|
||||
current_stages = [
|
||||
stage
|
||||
for subtask in document["subtasks"]
|
||||
for stage in subtask["stages"]
|
||||
if stage["status"] == "current"
|
||||
]
|
||||
assert len(current_stages) == 1
|
||||
|
||||
assert documents[0]["subtasks"][0]["stages"][0]["status"] == "current"
|
||||
assert documents[1]["subtasks"][1]["stages"][1]["status"] == "current"
|
||||
assert documents[2]["subtasks"][2]["stages"][2]["status"] == "current"
|
||||
assert document["activity"]
|
||||
assert all(entry["kind"] in {"created", "updated"} for entry in document["activity"])
|
||||
assert "stages" not in document
|
||||
|
||||
|
||||
def test_replace_removes_legacy_projection_before_inserting_demo_tasks():
|
||||
|
||||
@@ -201,47 +201,11 @@ async def test_snapshot_projects_persisted_work_orders_and_alerts():
|
||||
"source": "生产工单系统",
|
||||
"dispatcher": "调度中心",
|
||||
"priority": "urgent",
|
||||
"subtasks": [{
|
||||
"id": "inspect-valve",
|
||||
"title": "阀门开度复核",
|
||||
"summary": "现场复核阀门开度。",
|
||||
"status": "running",
|
||||
"scheduled_at": NOW.replace(hour=3),
|
||||
"location": "北区阀门 V-1",
|
||||
"assignee": "北区班组",
|
||||
"duration_minutes": 45,
|
||||
"reply_window_minutes": 20,
|
||||
"stages": [
|
||||
{
|
||||
"id": "dispatch",
|
||||
"sequence": 1,
|
||||
"title": "派发",
|
||||
"status": "done",
|
||||
"owner": "调度中心",
|
||||
"time_label": "10:20",
|
||||
"description": "已派发。",
|
||||
},
|
||||
{
|
||||
"id": "execution",
|
||||
"sequence": 2,
|
||||
"title": "执行中",
|
||||
"status": "current",
|
||||
"owner": "北区班组",
|
||||
"time_label": "10:30—11:15",
|
||||
"description": "现场执行中。",
|
||||
},
|
||||
{
|
||||
"id": "reply",
|
||||
"sequence": 3,
|
||||
"title": "待复令",
|
||||
"status": "pending",
|
||||
"owner": "北区班组 → 调度中心",
|
||||
"time_label": "11:15—11:35",
|
||||
"description": "等待执行结果。",
|
||||
},
|
||||
],
|
||||
"reply_requirements": ["回传阀门开度", "复核压力"],
|
||||
}],
|
||||
"location": "北区阀门 V-1",
|
||||
"assignee": "北区班组",
|
||||
"feedback_requirements": ["回传阀门开度", "复核压力"],
|
||||
"activity": [{"sequence": 1, "occurred_at": NOW, "actor": "调度中心",
|
||||
"kind": "created", "title": "工单创建", "detail": "等待现场复核。"}],
|
||||
}
|
||||
persisted_alert = {
|
||||
"alert_id": "alarm-1",
|
||||
@@ -282,3 +246,5 @@ async def test_snapshot_projects_persisted_work_orders_and_alerts():
|
||||
"severity": "warning",
|
||||
}]
|
||||
assert snapshot["work_orders"][0]["code"] == "WO-20260917-001"
|
||||
assert snapshot["work_orders"][0]["activity"][0]["title"] == "工单创建"
|
||||
assert "subtasks" not in snapshot["work_orders"][0]
|
||||
|
||||
Reference in New Issue
Block a user