feat(workbench): add dynamic task approval lifecycle

This commit is contained in:
2026-09-24 16:36:12 +08:00
parent db092b0f5e
commit ad813a7c16
35 changed files with 2956 additions and 564 deletions
+98
View File
@@ -1,4 +1,7 @@
import json
import subprocess
import sys
from pathlib import Path
from uuid import uuid4
import httpx
@@ -6,6 +9,7 @@ import pytest
from scripts.run_operational_worker import CredentialProvider, WorkerSettings
from scripts.sync_peripheral_work_orders import (
peripheral_order_id,
should_project_work_order,
sync_once,
work_order_snapshot,
@@ -19,6 +23,19 @@ def anyio_backend():
return "asyncio"
def test_documented_sync_command_can_start_from_repository_root():
repository_root = Path(__file__).resolve().parents[2]
result = subprocess.run(
[sys.executable, str(repository_root / "scripts/sync_peripheral_work_orders.py"), "--help"],
cwd=repository_root,
capture_output=True,
text=True,
check=False,
)
assert result.returncode == 0, result.stderr
assert "--once" in result.stdout
def test_unlinked_synthetic_checks_are_not_operational_tasks():
order_id = str(uuid4())
order = {
@@ -42,6 +59,14 @@ def test_unlinked_synthetic_checks_are_not_operational_tasks():
)
def test_peripheral_order_id_ignores_foreign_and_malformed_namespaces():
order_id = str(uuid4())
assert peripheral_order_id(f"peripheral_sample:{order_id}") == order_id
assert peripheral_order_id("peripheral_sample:not-a-uuid") is None
assert peripheral_order_id(f"other_system:{order_id}") is None
@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()))
@@ -129,6 +154,79 @@ async def test_sync_skips_unlinked_synthetic_check_before_writing():
assert synchronized == 0
@pytest.mark.anyio
async def test_sync_withdraws_only_deleted_peripheral_projection():
project_id, deleted_id, deleted_task_id, other_task_id = map(
str, (uuid4(), uuid4(), uuid4(), uuid4())
)
deleted_source = f"peripheral_sample:{deleted_id}"
tasks = [
{"task_id": deleted_task_id, "task_type": "external_work_order",
"source": {"external_id": deleted_source, "external_revision": 1}},
{"task_id": other_task_id, "task_type": "external_work_order",
"source": {"external_id": f"other_system:{uuid4()}", "external_revision": 1}},
]
withdrawals = []
def handler(request):
if request.url.host == "sample.test":
if request.url.path.endswith("/work-orders"):
return httpx.Response(200, json={"items": [], "next_cursor": None})
assert request.url.path.endswith(f"/work-orders/{deleted_id}")
return httpx.Response(404, json={"code": "WORK_ORDER_NOT_FOUND"})
if request.method == "GET":
return httpx.Response(200, json={"items": tasks, "next_offset": None})
assert request.url.path.endswith(f"/{deleted_task_id}/withdraw-external-source")
withdrawals.append(json.loads(request.content))
tasks.pop(0)
return httpx.Response(200, json={})
settings = WorkerSettings(
"http://server.test", "http://agent.test", project_id, bearer="TEST_ONLY"
)
async with httpx.AsyncClient(transport=httpx.MockTransport(handler)) as client:
credentials = CredentialProvider(settings)
assert await sync_once(client, settings, "http://sample.test", credentials) == 1
assert await sync_once(client, settings, "http://sample.test", credentials) == 0
assert len(withdrawals) == 1
assert withdrawals[0]["source_event_id"] == deleted_source
@pytest.mark.anyio
@pytest.mark.parametrize("lookup_status", [200, 503])
async def test_sync_does_not_withdraw_when_missing_order_is_not_confirmed_deleted(lookup_status):
project_id, order_id, task_id = map(str, (uuid4(), uuid4(), uuid4()))
source_id = f"peripheral_sample:{order_id}"
writes = []
def handler(request):
if request.url.host == "sample.test":
if request.url.path.endswith("/work-orders"):
return httpx.Response(200, json={"items": [], "next_cursor": None})
return httpx.Response(lookup_status, json={"work_order_id": order_id})
if request.method == "GET":
return httpx.Response(200, json={"items": [{
"task_id": task_id, "task_type": "external_work_order",
"source": {"external_id": source_id, "external_revision": 1},
}], "next_offset": None})
writes.append(request.url.path)
return httpx.Response(200, json={})
settings = WorkerSettings(
"http://server.test", "http://agent.test", project_id, bearer="TEST_ONLY"
)
async with httpx.AsyncClient(transport=httpx.MockTransport(handler)) as client:
if lookup_status == 503:
with pytest.raises(httpx.HTTPStatusError):
await sync_once(client, settings, "http://sample.test", CredentialProvider(settings))
else:
assert await sync_once(
client, settings, "http://sample.test", CredentialProvider(settings)
) == 0
assert writes == []
@pytest.mark.anyio
async def test_sample_sync_rejects_cross_project_data_before_writing():
def handler(request):