Files
next-tjwater-server/tests/integration/test_peripheral_work_order_events.py
T

85 lines
4.1 KiB
Python

"""tjwater_v2 source/event atomicity checks; every write is rolled back."""
from contextlib import contextmanager
import os
from uuid import uuid4
import pytest
from samples.peripheral.auth import Identity
from samples.peripheral.config import SampleSettings
from samples.peripheral.errors import SampleFailure
from samples.peripheral.schemas import CreateWorkOrder, DeleteResource, UpdateWorkOrder
from samples.peripheral.store import PostgresStore
pytestmark = pytest.mark.skipif(
os.getenv("PERIPHERAL_SAMPLE_LIVE_TEST") != "1", reason="explicit live sample opt-in required",
)
def test_work_order_mutations_and_events_are_atomic_idempotent_and_rollback_cleanly(monkeypatch):
settings = SampleSettings()
assert settings.db_name == "tjwater_v2", "event verification is restricted to tjwater_v2"
store = PostgresStore(settings)
who = Identity(str(uuid4()), str(uuid4()), True)
with store.connection() as conn:
@contextmanager
def connection(*, readonly=False, timeseries=False):
assert timeseries is False
with conn.transaction():
yield conn
monkeypatch.setattr(store, "connection", connection)
with conn.transaction(force_rollback=True):
command = CreateWorkOrder(
request_id=uuid4(), title="AUTOTEST push work order", summary="isolated transaction",
)
created = store.create_work_order(who, command)
assert store.create_work_order(who, command) == created
order_id = created["work_order_id"]
store.update_work_order(who, order_id, UpdateWorkOrder(
request_id=uuid4(), expected_revision=1, status="in_progress",
))
store.update_work_order(who, order_id, UpdateWorkOrder(
request_id=uuid4(), expected_revision=2, progress_note="AUTOTEST field feedback",
))
with pytest.raises(SampleFailure) as stale:
store.update_work_order(who, order_id, UpdateWorkOrder(
request_id=uuid4(), expected_revision=1, status="completed",
))
assert stale.value.code == "REVISION_CONFLICT"
store.delete_work_order(who, order_id, DeleteResource(
request_id=uuid4(), expected_revision=3,
))
rows = conn.execute(
"""SELECT project_id,topic,aggregate_type,aggregate_id,aggregate_revision,metadata
FROM workbench.domain_event_log WHERE project_id=%s ORDER BY event_seq""",
(who.project_id,),
).fetchall()
assert len(rows) == 4
assert [row["aggregate_revision"] for row in rows] == [1, 2, 3, 4]
assert [row["metadata"]["change"] for row in rows] == ["created", "updated", "updated", "deleted"]
assert all(row["topic"] == "workbench.work_order.changed"
and row["aggregate_type"] == "peripheral_work_order"
and row["aggregate_id"] == order_id for row in rows)
assert store.list_work_orders(who, 20)["items"] == []
original = store._work_order_event
def fail_after_event(*args, **kwargs):
original(*args, **kwargs)
raise RuntimeError("AUTOTEST transaction failure")
monkeypatch.setattr(store, "_work_order_event", fail_after_event)
with pytest.raises(RuntimeError, match="transaction failure"):
store.create_work_order(who, command.model_copy(update={"request_id": uuid4()}))
assert store.list_work_orders(who, 20)["items"] == []
assert conn.execute(
"SELECT count(*) AS n FROM workbench.domain_event_log WHERE project_id=%s", (who.project_id,),
).fetchone()["n"] == 4
for table in ("workbench.domain_event_log", "peripheral_sample.work_orders",
"peripheral_sample.work_order_revisions", "peripheral_sample.requests"):
assert conn.execute(f"SELECT count(*) AS n FROM {table} WHERE project_id=%s",
(who.project_id,)).fetchone()["n"] == 0