""" Unit tests for the resume tracker module. Tests cover resume state tracking and loading. """ import sys from unittest.mock import Mock from pathlib import Path import json import tempfile # Mock torch and transformers before importing sys_mock = Mock() sys.modules["torch"] = sys_mock sys.modules["transformers"] = sys_mock import pytest from src.translator.resume_tracker import ResumeTracker, ResumeState class TestResumeState: """Test cases for ResumeState dataclass.""" def test_create_resume_state(self): """Test creating a resume state.""" state = ResumeState( work_id="test_work", last_completed_index=10, last_failed_index=None, total_chapters=100, ) assert state.work_id == "test_work" assert state.last_completed_index == 10 assert state.last_failed_index is None assert state.total_chapters == 100 def test_to_dict(self): """Test converting resume state to dictionary.""" state = ResumeState( work_id="test_work", last_completed_index=10, last_failed_index=None, total_chapters=100, resume_message="Test message", ) data = state.to_dict() assert data["work_id"] == "test_work" assert data["last_completed_index"] == 10 assert data["last_failed_index"] is None assert data["total_chapters"] == 100 assert data["resume_message"] == "Test message" assert "timestamp" in data def test_from_dict(self): """Test creating resume state from dictionary.""" data = { "work_id": "test_work", "last_completed_index": 10, "last_failed_index": None, "total_chapters": 100, "timestamp": "2024-01-01T00:00:00", "resume_message": "Test message", } state = ResumeState.from_dict(data) assert state.work_id == "test_work" assert state.last_completed_index == 10 assert state.total_chapters == 100 def test_get_resume_index_with_failure(self): """Test getting resume index when there's a failed chapter.""" state = ResumeState( work_id="test_work", last_completed_index=10, last_failed_index=15, total_chapters=100, ) assert state.get_resume_index() == 15 def test_get_resume_index_without_failure(self): """Test getting resume index when there's no failed chapter.""" state = ResumeState( work_id="test_work", last_completed_index=10, last_failed_index=None, total_chapters=100, ) assert state.get_resume_index() == 11 def test_is_resumable_with_remaining(self): """Test is_resumable when there are remaining chapters.""" state = ResumeState( work_id="test_work", last_completed_index=10, last_failed_index=None, total_chapters=100, ) assert state.is_resumable() is True def test_is_resumable_complete(self): """Test is_resumable when all chapters are complete.""" state = ResumeState( work_id="test_work", last_completed_index=99, last_failed_index=None, total_chapters=100, ) assert state.is_resumable() is False class TestResumeTracker: """Test cases for ResumeTracker class.""" @pytest.fixture def temp_dir(self): """Create a temporary directory for testing.""" with tempfile.TemporaryDirectory() as tmp: yield Path(tmp) @pytest.fixture def tracker(self, temp_dir): """Create a resume tracker for testing.""" return ResumeTracker(temp_dir) def test_init(self, temp_dir): """Test ResumeTracker initialization.""" tracker = ResumeTracker(temp_dir) assert tracker.storage_dir == temp_dir assert tracker.resume_dir == temp_dir / "resume" assert tracker.resume_dir.exists() def test_save_resume_state(self, tracker): """Test saving resume state.""" state = tracker.save_resume_state( work_id="test_work", last_completed_index=10, last_failed_index=None, total_chapters=100, ) assert state.work_id == "test_work" assert state.last_completed_index == 10 assert "Resume from chapter" in state.resume_message # Check file was created resume_file = tracker._get_resume_file_path("test_work") assert resume_file.exists() def test_load_resume_state(self, tracker): """Test loading resume state.""" # Save state first tracker.save_resume_state( work_id="test_work", last_completed_index=10, last_failed_index=None, total_chapters=100, ) # Load it back loaded = tracker.load_resume_state("test_work") assert loaded is not None assert loaded.work_id == "test_work" assert loaded.last_completed_index == 10 assert loaded.total_chapters == 100 def test_load_nonexistent_resume_state(self, tracker): """Test loading a non-existent resume state.""" loaded = tracker.load_resume_state("nonexistent") assert loaded is None def test_delete_resume_state(self, tracker): """Test deleting resume state.""" # Save state first tracker.save_resume_state( work_id="test_work", last_completed_index=10, last_failed_index=None, total_chapters=100, ) # Delete it tracker.delete_resume_state("test_work") # Check it's gone resume_file = tracker._get_resume_file_path("test_work") assert not resume_file.exists() def test_get_resume_index(self, tracker): """Test getting resume index.""" # Save state first tracker.save_resume_state( work_id="test_work", last_completed_index=10, last_failed_index=None, total_chapters=100, ) # Get resume index index = tracker.get_resume_index("test_work", 100) assert index == 11 def test_get_resume_index_nonexistent(self, tracker): """Test getting resume index for non-existent state.""" index = tracker.get_resume_index("nonexistent", 100) assert index is None def test_update_on_chapter_complete(self, tracker): """Test updating state on chapter completion.""" tracker.update_on_chapter_complete( work_id="test_work", chapter_index=5, total_chapters=100, ) loaded = tracker.load_resume_state("test_work") assert loaded is not None assert loaded.last_completed_index == 5 assert loaded.last_failed_index is None def test_update_on_chapter_failed(self, tracker): """Test updating state on chapter failure.""" tracker.update_on_chapter_failed( work_id="test_work", chapter_index=5, total_chapters=100, ) loaded = tracker.load_resume_state("test_work") assert loaded is not None assert loaded.last_failed_index == 5 def test_generate_resume_message_with_failure(self): """Test generating resume message with failed chapter.""" state = ResumeState( work_id="test_work", last_completed_index=10, last_failed_index=15, total_chapters=100, ) tracker = ResumeTracker(Path("/tmp")) message = tracker._generate_resume_message(state) assert "failed chapter 15" in message assert "85" in message # remaining chapters def test_generate_resume_message_without_failure(self): """Test generating resume message without failed chapter.""" state = ResumeState( work_id="test_work", last_completed_index=10, last_failed_index=None, total_chapters=100, ) tracker = ResumeTracker(Path("/tmp")) message = tracker._generate_resume_message(state) assert "Resume from chapter 11" in message assert "89" in message # remaining chapters def test_get_all_resume_states(self, tracker): """Test getting all resume states.""" # Save multiple states tracker.save_resume_state("work1", 10, None, 100) tracker.save_resume_state("work2", 20, None, 200) states = tracker.get_all_resume_states() assert len(states) == 2 work_ids = {s.work_id for s in states} assert work_ids == {"work1", "work2"} def test_clear_all_resume_states(self, tracker): """Test clearing all resume states.""" # Save multiple states tracker.save_resume_state("work1", 10, None, 100) tracker.save_resume_state("work2", 20, None, 200) tracker.clear_all_resume_states() states = tracker.get_all_resume_states() assert len(states) == 0 def test_update_existing_state_on_complete(self, tracker): """Test that updating state preserves existing data.""" # Save initial state tracker.save_resume_state( work_id="test_work", last_completed_index=5, last_failed_index=7, total_chapters=100, ) # Update on completion tracker.update_on_chapter_complete( work_id="test_work", chapter_index=10, total_chapters=100, ) loaded = tracker.load_resume_state("test_work") assert loaded.last_completed_index == 10 assert loaded.last_failed_index is None # Should be cleared def test_custom_resume_message(self, tracker): """Test saving state with custom resume message.""" state = tracker.save_resume_state( work_id="test_work", last_completed_index=10, last_failed_index=None, total_chapters=100, resume_message="Custom message here", ) assert state.resume_message == "Custom message here" def test_get_resume_file_path(self, tracker): """Test getting resume file path.""" path = tracker._get_resume_file_path("test_work") assert path.name == "test_work_resume.json" assert path.parent == tracker.resume_dir