feat(workbench): persist operational workflows and events
This commit is contained in:
@@ -0,0 +1,147 @@
|
||||
from contextlib import contextmanager
|
||||
from copy import deepcopy
|
||||
from uuid import uuid4
|
||||
|
||||
import pytest
|
||||
|
||||
from samples.peripheral.auth import Identity
|
||||
from samples.peripheral.config import SampleSettings
|
||||
from samples.peripheral.schemas import TestDataRecord as DataRecordSchema
|
||||
from samples.peripheral.schemas import WorkOrder
|
||||
from samples.peripheral.store import PostgresStore
|
||||
|
||||
|
||||
class FakeResult:
|
||||
def __init__(self, *, row=None, rows=None):
|
||||
self.row = row
|
||||
self.rows = rows or []
|
||||
|
||||
def fetchone(self):
|
||||
return self.row
|
||||
|
||||
def fetchall(self):
|
||||
return self.rows
|
||||
|
||||
|
||||
class ManagedProjectionConnection:
|
||||
def __init__(self, table: str, document: dict):
|
||||
self.table = table
|
||||
self.document = document
|
||||
|
||||
def execute(self, statement, parameters):
|
||||
normalized = " ".join(statement.split())
|
||||
if "FROM peripheral_sample.scenarios" in normalized:
|
||||
return FakeResult(row=None)
|
||||
if f"FROM peripheral_sample.{self.table}" in normalized:
|
||||
row = {"document": self.document}
|
||||
return FakeResult(row=row, rows=[row])
|
||||
raise AssertionError(f"unexpected query: {normalized}")
|
||||
|
||||
|
||||
class ManagedProjectionStore(PostgresStore):
|
||||
def __init__(self, table: str, document: dict):
|
||||
super().__init__(SampleSettings())
|
||||
self.fake_connection = ManagedProjectionConnection(table, document)
|
||||
|
||||
@contextmanager
|
||||
def connection(self, *, readonly=False, timeseries=False):
|
||||
assert readonly is True
|
||||
assert timeseries is False
|
||||
yield self.fake_connection
|
||||
|
||||
|
||||
def work_order_document() -> dict:
|
||||
now = "2026-09-18T00:00:00Z"
|
||||
return {
|
||||
"work_order_id": str(uuid4()),
|
||||
"project_id": str(uuid4()),
|
||||
"revision": 2,
|
||||
"title": "恢复后的演示工单",
|
||||
"summary": "当前行已恢复,但旧实现留下 tombstone 字段。",
|
||||
"status": "open",
|
||||
"priority": "normal",
|
||||
"assignee": None,
|
||||
"scheduled_at": None,
|
||||
"origin": "synthetic",
|
||||
"created_at": now,
|
||||
"updated_at": now,
|
||||
"created_by": str(uuid4()),
|
||||
"updated_by": str(uuid4()),
|
||||
"work_order_type": "general",
|
||||
"location": None,
|
||||
"scenario_code": None,
|
||||
"deleted_at": now,
|
||||
"deleted_by": str(uuid4()),
|
||||
}
|
||||
|
||||
|
||||
def test_active_work_order_projection_removes_only_tombstone_fields():
|
||||
document = work_order_document()
|
||||
stored_document = deepcopy(document)
|
||||
store = ManagedProjectionStore("work_orders", stored_document)
|
||||
who = Identity(document["project_id"], str(uuid4()), False)
|
||||
|
||||
result = store.get_work_order(who, document["work_order_id"])
|
||||
|
||||
assert "deleted_at" not in result
|
||||
assert "deleted_by" not in result
|
||||
assert stored_document == document
|
||||
WorkOrder.model_validate(result)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("table", "id_column", "document", "model"),
|
||||
[
|
||||
("work_orders", "work_order_id", work_order_document(), WorkOrder),
|
||||
(
|
||||
"test_data_records",
|
||||
"record_id",
|
||||
{
|
||||
"record_id": str(uuid4()),
|
||||
"project_id": str(uuid4()),
|
||||
"revision": 2,
|
||||
"label": "恢复后的演示数据",
|
||||
"point_id": "DEMO-P001",
|
||||
"observed_at": "2026-09-18T00:00:00Z",
|
||||
"value": 1.0,
|
||||
"unit": "m",
|
||||
"quality": "valid",
|
||||
"notes": "",
|
||||
"origin": "synthetic",
|
||||
"created_at": "2026-09-18T00:00:00Z",
|
||||
"updated_at": "2026-09-18T00:00:00Z",
|
||||
"created_by": str(uuid4()),
|
||||
"updated_by": str(uuid4()),
|
||||
"deleted_at": "2026-09-18T00:00:00Z",
|
||||
"deleted_by": str(uuid4()),
|
||||
},
|
||||
DataRecordSchema,
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_active_managed_list_projection_removes_tombstone_fields(
|
||||
table, id_column, document, model
|
||||
):
|
||||
store = ManagedProjectionStore(table, document)
|
||||
who = Identity(document["project_id"], str(uuid4()), False)
|
||||
|
||||
page = store._list_managed(who, table, id_column, limit=10, cursor=None)
|
||||
|
||||
assert len(page["items"]) == 1
|
||||
assert "deleted_at" not in page["items"][0]
|
||||
assert "deleted_by" not in page["items"][0]
|
||||
model.model_validate(page["items"][0])
|
||||
|
||||
|
||||
def test_active_projection_does_not_hide_unrelated_fields():
|
||||
document = {
|
||||
"deleted_at": "2026-09-18T00:00:00Z",
|
||||
"deleted_by": str(uuid4()),
|
||||
"unexpected_field": "must remain visible to schema validation",
|
||||
}
|
||||
|
||||
result = PostgresStore._active_managed_document(document)
|
||||
|
||||
assert result == {"unexpected_field": "must remain visible to schema validation"}
|
||||
assert "deleted_at" in document
|
||||
assert "deleted_by" in document
|
||||
Reference in New Issue
Block a user