""" Unit tests for the state machine module. Tests cover all state transitions, persistence, and recovery scenarios. """ import pytest from datetime import datetime from src.pipeline.state_machine import PipelineStateMachine, PipelineState class TestPipelineStateMachine: """Test cases for PipelineStateMachine.""" def test_initial_state_is_idle(self): """Test that the machine starts in IDLE state.""" machine = PipelineStateMachine() assert machine.state == PipelineState.IDLE def test_valid_transition_idle_to_preparing(self): """Test valid transition from IDLE to PREPARING.""" machine = PipelineStateMachine() assert machine.transition_to(PipelineState.PREPARING) assert machine.state == PipelineState.PREPARING def test_valid_transition_preparing_to_cleaning(self): """Test valid transition from PREPARING to CLEANING.""" machine = PipelineStateMachine() machine.transition_to(PipelineState.PREPARING) assert machine.transition_to(PipelineState.CLEANING) assert machine.state == PipelineState.CLEANING def test_valid_transition_cleaning_to_translating(self): """Test valid transition from CLEANING to TRANSLATING.""" machine = PipelineStateMachine() machine.transition_to(PipelineState.PREPARING) machine.transition_to(PipelineState.CLEANING) assert machine.transition_to(PipelineState.TRANSLATING) assert machine.state == PipelineState.TRANSLATING def test_valid_transition_translating_to_uploading(self): """Test valid transition from TRANSLATING to UPLOADING.""" machine = PipelineStateMachine() for state in [ PipelineState.PREPARING, PipelineState.CLEANING, PipelineState.TRANSLATING, ]: machine.transition_to(state) assert machine.transition_to(PipelineState.UPLOADING) assert machine.state == PipelineState.UPLOADING def test_valid_transition_uploading_to_completed(self): """Test valid transition from UPLOADING to COMPLETED.""" machine = PipelineStateMachine() for state in [ PipelineState.PREPARING, PipelineState.CLEANING, PipelineState.TRANSLATING, PipelineState.UPLOADING, ]: machine.transition_to(state) assert machine.transition_to(PipelineState.COMPLETED) assert machine.state == PipelineState.COMPLETED def test_invalid_transition_rejected(self): """Test that invalid transitions are rejected.""" machine = PipelineStateMachine() # Can't skip from IDLE to TRANSLATING assert not machine.transition_to(PipelineState.TRANSLATING) assert machine.state == PipelineState.IDLE def test_pause_from_any_active_state(self): """Test pausing from any active state.""" active_states = [ PipelineState.PREPARING, PipelineState.CLEANING, PipelineState.TRANSLATING, PipelineState.UPLOADING, ] for state in active_states: machine = PipelineStateMachine() machine.transition_to(PipelineState.PREPARING) machine.transition_to(state) assert machine.transition_to(PipelineState.PAUSED) assert machine.state == PipelineState.PAUSED def test_resume_from_pause(self): """Test resuming from PAUSED back to active state.""" machine = PipelineStateMachine() machine.transition_to(PipelineState.PREPARING) machine.transition_to(PipelineState.TRANSLATING) machine.transition_to(PipelineState.PAUSED) # Can resume to TRANSLATING assert machine.transition_to(PipelineState.TRANSLATING) assert machine.state == PipelineState.TRANSLATING def test_failed_state_only_goes_to_idle(self): """Test that FAILED state can only transition to IDLE.""" machine = PipelineStateMachine() machine.transition_to(PipelineState.PREPARING) machine.transition_to(PipelineState.FAILED) # Can go to IDLE assert machine.transition_to(PipelineState.IDLE) assert machine.state == PipelineState.IDLE # Can't go directly to another state machine.transition_to(PipelineState.PREPARING) machine.transition_to(PipelineState.FAILED) assert not machine.transition_to(PipelineState.TRANSLATING) def test_completed_goes_to_idle(self): """Test that COMPLETED transitions to IDLE.""" machine = PipelineStateMachine() for state in [ PipelineState.PREPARING, PipelineState.CLEANING, PipelineState.TRANSLATING, PipelineState.UPLOADING, PipelineState.COMPLETED, ]: machine.transition_to(state) assert machine.transition_to(PipelineState.IDLE) assert machine.state == PipelineState.IDLE def test_state_history_tracking(self): """Test that state history is tracked.""" machine = PipelineStateMachine() machine.transition_to(PipelineState.PREPARING, reason="Starting task") machine.transition_to(PipelineState.CLEANING) history = machine.get_state_history() assert len(history) == 3 # IDLE + PREPARING + CLEANING assert history[0]["state"] == "idle" assert history[1]["state"] == "preparing" assert history[1]["reason"] == "Starting task" assert history[2]["state"] == "cleaning" def test_can_transition_to_check(self): """Test the can_transition_to method.""" machine = PipelineStateMachine() assert machine.can_transition_to(PipelineState.PREPARING) assert not machine.can_transition_to(PipelineState.TRANSLATING) machine.transition_to(PipelineState.PREPARING) assert machine.can_transition_to(PipelineState.CLEANING) assert not machine.can_transition_to(PipelineState.IDLE) def test_reset(self): """Test resetting the state machine.""" machine = PipelineStateMachine() machine.transition_to(PipelineState.PREPARING) machine.transition_to(PipelineState.CLEANING) machine.reset() assert machine.state == PipelineState.IDLE assert len(machine.get_state_history()) == 1 def test_full_workflow(self): """Test a complete workflow from start to finish.""" machine = PipelineStateMachine() workflow = [ PipelineState.PREPARING, PipelineState.CLEANING, PipelineState.TRANSLATING, PipelineState.UPLOADING, PipelineState.COMPLETED, ] for state in workflow: assert machine.transition_to(state), f"Failed to transition to {state}" assert machine.state == state # Should be able to start a new task assert machine.transition_to(PipelineState.IDLE) assert machine.transition_to(PipelineState.PREPARING)