test(sync): prove local sync journal semantics
This commit is contained in:
parent
b9cc7d8988
commit
7e091525bf
1 changed files with 135 additions and 0 deletions
135
tests/test_edge_sync_journal.py
Normal file
135
tests/test_edge_sync_journal.py
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from core.sync.journal import JournalDirection, JournalStatus, LocalSyncJournal
|
||||
|
||||
|
||||
def test_append_upload_creates_pending_entry() -> None:
|
||||
journal = LocalSyncJournal()
|
||||
decision = journal.append_upload(
|
||||
entry_id="u1",
|
||||
artifact_id="trace-1",
|
||||
artifact_type="trace",
|
||||
target_uri="s3://bucket/traces/trace-1.jsonl.zst",
|
||||
)
|
||||
|
||||
assert decision.accepted
|
||||
assert decision.reason == "pending"
|
||||
assert decision.entry is not None
|
||||
assert decision.entry.direction is JournalDirection.UPLOAD
|
||||
assert decision.entry.status is JournalStatus.PENDING
|
||||
assert journal.pending() == (decision.entry,)
|
||||
|
||||
|
||||
def test_append_download_creates_pending_entry() -> None:
|
||||
journal = LocalSyncJournal()
|
||||
decision = journal.append_download(
|
||||
entry_id="d1",
|
||||
artifact_id="pack-v1",
|
||||
artifact_type="pack_release",
|
||||
)
|
||||
|
||||
assert decision.accepted
|
||||
assert decision.entry is not None
|
||||
assert decision.entry.direction is JournalDirection.DOWNLOAD
|
||||
assert decision.entry.status is JournalStatus.PENDING
|
||||
|
||||
|
||||
def test_duplicate_entry_rejects_without_overwrite() -> None:
|
||||
journal = LocalSyncJournal()
|
||||
first = journal.append_upload(entry_id="u1", artifact_id="a1", artifact_type="trace")
|
||||
second = journal.append_upload(entry_id="u1", artifact_id="a2", artifact_type="trace")
|
||||
|
||||
assert first.accepted
|
||||
assert not second.accepted
|
||||
assert second.reason == "duplicate_entry_id"
|
||||
assert journal.get("u1").artifact_id == "a1" # type: ignore[union-attr]
|
||||
|
||||
|
||||
def test_mark_completed_preserves_entry_metadata() -> None:
|
||||
journal = LocalSyncJournal()
|
||||
journal.append_upload(entry_id="u1", artifact_id="trace-1", artifact_type="trace")
|
||||
decision = journal.mark_completed("u1")
|
||||
|
||||
assert decision.accepted
|
||||
assert decision.reason == "completed"
|
||||
assert decision.entry is not None
|
||||
assert decision.entry.status is JournalStatus.COMPLETED
|
||||
assert decision.entry.artifact_id == "trace-1"
|
||||
assert journal.pending() == ()
|
||||
|
||||
|
||||
def test_mark_failed_preserves_reason_and_increments_attempts() -> None:
|
||||
journal = LocalSyncJournal()
|
||||
journal.append_upload(entry_id="u1", artifact_id="trace-1", artifact_type="trace")
|
||||
failure = journal.mark_failed("u1", "object_store_unavailable")
|
||||
|
||||
assert failure.accepted
|
||||
assert failure.entry is not None
|
||||
assert failure.entry.status is JournalStatus.FAILED
|
||||
assert failure.entry.reason == "object_store_unavailable"
|
||||
assert failure.entry.attempts == 1
|
||||
|
||||
|
||||
def test_failed_upload_does_not_clear_journal_state() -> None:
|
||||
journal = LocalSyncJournal()
|
||||
journal.append_upload(entry_id="u1", artifact_id="trace-1", artifact_type="trace")
|
||||
journal.mark_failed("u1", "timeout")
|
||||
|
||||
entry = journal.get("u1")
|
||||
assert entry is not None
|
||||
assert entry.artifact_id == "trace-1"
|
||||
assert entry.status is JournalStatus.FAILED
|
||||
|
||||
|
||||
def test_retry_failed_entry_returns_to_pending_without_incrementing_attempts() -> None:
|
||||
journal = LocalSyncJournal()
|
||||
journal.append_upload(entry_id="u1", artifact_id="trace-1", artifact_type="trace")
|
||||
journal.mark_failed("u1", "timeout")
|
||||
retry = journal.retry("u1")
|
||||
|
||||
assert retry.accepted
|
||||
assert retry.reason == "pending"
|
||||
assert retry.entry is not None
|
||||
assert retry.entry.status is JournalStatus.PENDING
|
||||
assert retry.entry.attempts == 1
|
||||
assert retry.entry.reason is None
|
||||
|
||||
|
||||
def test_rejected_download_can_be_retried() -> None:
|
||||
journal = LocalSyncJournal()
|
||||
journal.append_download(entry_id="d1", artifact_id="pack-v1", artifact_type="pack_release")
|
||||
journal.mark_rejected("d1", "hash_mismatch")
|
||||
retry = journal.retry("d1")
|
||||
|
||||
assert retry.accepted
|
||||
assert retry.entry is not None
|
||||
assert retry.entry.status is JournalStatus.PENDING
|
||||
assert retry.entry.reason is None
|
||||
|
||||
|
||||
def test_unknown_entry_transition_rejects() -> None:
|
||||
journal = LocalSyncJournal()
|
||||
decision = journal.mark_completed("missing")
|
||||
|
||||
assert not decision.accepted
|
||||
assert decision.reason == "unknown_entry"
|
||||
|
||||
|
||||
def test_journal_serializes_to_dicts() -> None:
|
||||
journal = LocalSyncJournal()
|
||||
journal.append_upload(entry_id="u1", artifact_id="trace-1", artifact_type="trace")
|
||||
journal.mark_failed("u1", "timeout")
|
||||
|
||||
payload = journal.as_dicts()
|
||||
assert payload == (
|
||||
{
|
||||
"entry_id": "u1",
|
||||
"direction": "upload",
|
||||
"artifact_id": "trace-1",
|
||||
"artifact_type": "trace",
|
||||
"status": "failed",
|
||||
"target_uri": None,
|
||||
"reason": "timeout",
|
||||
"attempts": 1,
|
||||
},
|
||||
)
|
||||
Loading…
Reference in a new issue