Files
photoanalyzer/tests/unit/test_job_state_machine.py

52 lines
1.7 KiB
Python

"""Job/item state-machine rules: only declared transitions are legal, and terminal
states are truly terminal (no duplicate terminal transition)."""
import pytest
from photo_pipeline.services.jobs import (
ALLOWED_TRANSITIONS,
ITEM_TRANSITIONS,
TERMINAL_STATES,
ItemState,
JobState,
can_transition,
)
def test_happy_path_transitions_are_allowed():
assert can_transition(JobState.QUEUED, JobState.RUNNING)
assert can_transition(JobState.RUNNING, JobState.SUCCEEDED)
assert can_transition(JobState.RUNNING, JobState.FAILED)
assert can_transition(JobState.FAILED, JobState.RETRY_QUEUED)
assert can_transition(JobState.RETRY_QUEUED, JobState.RUNNING)
assert can_transition(JobState.RUNNING, JobState.CANCELLING)
assert can_transition(JobState.CANCELLING, JobState.CANCELLED)
@pytest.mark.parametrize(
"current,target",
[
(JobState.QUEUED, JobState.SUCCEEDED), # can't skip running
(JobState.SUCCEEDED, JobState.RUNNING), # terminal, no revive
(JobState.CANCELLED, JobState.RUNNING), # terminal
(JobState.RUNNING, JobState.QUEUED), # no backward
],
)
def test_invalid_transitions_are_rejected(current, target):
assert not can_transition(current, target)
def test_terminal_states_have_no_outgoing_transition():
for state in TERMINAL_STATES:
assert ALLOWED_TRANSITIONS[state] == set()
def test_duplicate_terminal_transition_is_rejected():
assert not can_transition(JobState.SUCCEEDED, JobState.SUCCEEDED)
assert not can_transition(JobState.CANCELLED, JobState.CANCELLED)
def test_item_states_are_consistent():
assert ItemState.SUCCEEDED in ITEM_TRANSITIONS[ItemState.RUNNING]
assert ITEM_TRANSITIONS[ItemState.SUCCEEDED] == set() # terminal item