feat: complete production workflow migration
This commit is contained in:
+57
-2
@@ -8,7 +8,7 @@ import pytest
|
||||
|
||||
from gyxx_flow.core.context import RunContext
|
||||
from gyxx_flow.core.layout import DataLayout
|
||||
from gyxx_flow.core.records import RunJournal
|
||||
from gyxx_flow.core.records import RunJournal, RunMode
|
||||
from gyxx_flow.ops import IdempotencyConflict, Outbox, RunIndex
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ def _journal(
|
||||
workflow_id: str = "shop.weekly",
|
||||
business_date: str = "2026-07-27",
|
||||
suffix: str = "abc123",
|
||||
mode: RunMode = "unknown",
|
||||
) -> RunJournal:
|
||||
context = RunContext.create(
|
||||
workflow_id,
|
||||
@@ -25,7 +26,7 @@ def _journal(
|
||||
now=datetime(2026, 7, 27, 4, 0, tzinfo=timezone.utc),
|
||||
random_suffix=suffix,
|
||||
)
|
||||
return RunJournal.create(DataLayout(root), context)
|
||||
return RunJournal.create(DataLayout(root), context, mode=mode)
|
||||
|
||||
|
||||
def test_run_index_snapshots_journals_and_supports_structured_queries(tmp_path: Path) -> None:
|
||||
@@ -43,6 +44,7 @@ def test_run_index_snapshots_journals_and_supports_structured_queries(tmp_path:
|
||||
first_record = index.index_journal(first)
|
||||
second_record = index.index_journal(second.path)
|
||||
|
||||
assert first_record.mode == "unknown"
|
||||
assert first_record.status == "success"
|
||||
assert second_record.error == "collector failed"
|
||||
assert index.get(first_record.run_id) == first_record
|
||||
@@ -80,6 +82,59 @@ def test_run_index_rejects_malformed_journal_without_writing_index(tmp_path: Pat
|
||||
assert index.query() == ()
|
||||
|
||||
|
||||
def test_run_index_preserves_mode_and_accepts_legacy_records(tmp_path: Path) -> None:
|
||||
journal = _journal(tmp_path, mode="dry_run")
|
||||
journal.start_step("collect.attempt-1", attempt=1)
|
||||
journal.finish_step("collect.attempt-1", status="skipped", error="dry-run")
|
||||
journal.finalize("success")
|
||||
index = RunIndex(tmp_path)
|
||||
|
||||
current = index.index_journal(journal)
|
||||
|
||||
assert current.mode == "dry_run"
|
||||
assert current.schema_version == 2
|
||||
current_payload = json.loads(
|
||||
(tmp_path / "state" / "ops" / "run-index" / f"{current.run_id}.json").read_text(
|
||||
encoding="utf-8"
|
||||
)
|
||||
)
|
||||
assert current_payload["mode"] == "dry_run"
|
||||
assert current_payload["schema_version"] == 2
|
||||
|
||||
current_payload.pop("mode")
|
||||
current_payload["schema_version"] = 1
|
||||
(tmp_path / "state" / "ops" / "run-index" / f"{current.run_id}.json").write_text(
|
||||
json.dumps(current_payload),
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
legacy = index.get(current.run_id)
|
||||
assert legacy is not None
|
||||
assert legacy.mode == "unknown"
|
||||
assert legacy.schema_version == 1
|
||||
|
||||
|
||||
def test_run_index_treats_a_legacy_journal_without_mode_as_unknown(tmp_path: Path) -> None:
|
||||
journal = _journal(tmp_path, suffix="legacy1", mode="execute")
|
||||
payload = json.loads(journal.path.read_text(encoding="utf-8"))
|
||||
payload.pop("mode")
|
||||
journal.path.write_text(json.dumps(payload), encoding="utf-8")
|
||||
|
||||
record = RunIndex(tmp_path).index_journal(journal)
|
||||
|
||||
assert record.mode == "unknown"
|
||||
assert record.schema_version == 2
|
||||
|
||||
|
||||
def test_run_journal_rejects_an_invalid_mode_before_creating_files(tmp_path: Path) -> None:
|
||||
context = RunContext.create("shop.weekly", "2026-07-27", random_suffix="badmode")
|
||||
|
||||
with pytest.raises(ValueError, match="run mode"):
|
||||
RunJournal.create(DataLayout(tmp_path), context, mode="preview") # type: ignore[arg-type]
|
||||
|
||||
assert not (tmp_path / "runs").exists()
|
||||
|
||||
|
||||
def test_outbox_enqueue_is_atomic_and_idempotent(tmp_path: Path) -> None:
|
||||
outbox = Outbox(tmp_path)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user