| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430 |
- """
- Tests for UI models - no GUI required.
- """
- import pytest
- from pathlib import Path
- from datetime import datetime, timedelta
- from src.ui.models import (
- FileItem,
- FileStatus,
- TranslationTask,
- TaskStatus,
- ProgressUpdate,
- Statistics,
- )
- class TestFileItem:
- """Test FileItem data model."""
- def test_file_item_creation_defaults(self):
- """Test creating a file item with default values."""
- item = FileItem(
- path=Path("/test/file.txt"),
- name="file.txt",
- size=2048,
- )
- assert item.name == "file.txt"
- assert item.path == Path("/test/file.txt")
- assert item.size == 2048
- assert item.status == FileStatus.PENDING
- assert item.chapters == 0
- assert item.total_words == 0
- assert item.translated_words == 0
- assert item.error_message is None
- assert isinstance(item.added_time, datetime)
- def test_file_item_creation_with_values(self):
- """Test creating a file item with specific values."""
- now = datetime.now()
- item = FileItem(
- path=Path("/test/novel.txt"),
- name="novel.txt",
- size=1024 * 100,
- status=FileStatus.TRANSLATING,
- chapters=50,
- total_words=100000,
- translated_words=25000,
- error_message=None,
- added_time=now,
- )
- assert item.name == "novel.txt"
- assert item.status == FileStatus.TRANSLATING
- assert item.chapters == 50
- assert item.total_words == 100000
- assert item.translated_words == 25000
- def test_progress_calculation(self):
- """Test progress percentage calculation."""
- item = FileItem(
- path=Path("/test/file.txt"),
- name="file.txt",
- size=1024,
- total_words=1000,
- translated_words=500,
- )
- assert item.progress == 50.0
- def test_progress_zero_when_no_total(self):
- """Test progress is zero when total_words is 0."""
- item = FileItem(
- path=Path("/test/file.txt"),
- name="file.txt",
- size=1024,
- total_words=0,
- translated_words=0,
- )
- assert item.progress == 0.0
- def test_progress_complete(self):
- """Test progress when translation is complete."""
- item = FileItem(
- path=Path("/test/file.txt"),
- name="file.txt",
- size=1024,
- total_words=1000,
- translated_words=1000,
- )
- assert item.progress == 100.0
- def test_size_formatting_bytes(self):
- """Test size formatting for bytes."""
- item = FileItem(
- path=Path("/test/small.txt"),
- name="small.txt",
- size=512,
- )
- assert "512" in item.size_formatted
- assert "B" in item.size_formatted
- def test_size_formatting_kilobytes(self):
- """Test size formatting for kilobytes."""
- item = FileItem(
- path=Path("/test/medium.txt"),
- name="medium.txt",
- size=2048,
- )
- assert "KB" in item.size_formatted
- def test_size_formatting_megabytes(self):
- """Test size formatting for megabytes."""
- item = FileItem(
- path=Path("/test/large.txt"),
- name="large.txt",
- size=1024 * 1024 * 5,
- )
- assert "MB" in item.size_formatted
- class TestTranslationTask:
- """Test TranslationTask data model."""
- def test_task_creation_defaults(self):
- """Test creating a task with default values."""
- task = TranslationTask(id="task-123")
- assert task.id == "task-123"
- assert task.status == TaskStatus.IDLE
- assert task.current_stage == ""
- assert task.current_chapter == 0
- assert task.total_chapters == 0
- assert task.start_time is None
- assert task.end_time is None
- assert task.error_message is None
- assert task.file_items == []
- def test_task_creation_with_file_items(self):
- """Test creating a task with file items."""
- file1 = FileItem(Path("/test/file1.txt"), "file1.txt", 1024)
- file2 = FileItem(Path("/test/file2.txt"), "file2.txt", 2048)
- task = TranslationTask(
- id="task-456",
- file_items=[file1, file2],
- status=TaskStatus.RUNNING,
- current_stage="translating",
- current_chapter=5,
- total_chapters=10,
- )
- assert len(task.file_items) == 2
- assert task.status == TaskStatus.RUNNING
- assert task.current_stage == "translating"
- assert task.current_chapter == 5
- assert task.total_chapters == 10
- def test_progress_calculation(self):
- """Test task progress calculation."""
- task = TranslationTask(
- id="task-1",
- current_chapter=25,
- total_chapters=100,
- )
- assert task.progress == 25.0
- def test_progress_zero_when_no_total(self):
- """Test progress is zero when total_chapters is 0."""
- task = TranslationTask(
- id="task-1",
- current_chapter=0,
- total_chapters=0,
- )
- assert task.progress == 0.0
- def test_elapsed_time_without_start(self):
- """Test elapsed time when task hasn't started."""
- task = TranslationTask(id="task-1")
- assert task.elapsed_time is None
- def test_elapsed_time_with_start(self):
- """Test elapsed time calculation."""
- start = datetime.now() - timedelta(seconds=60)
- task = TranslationTask(
- id="task-1",
- start_time=start,
- )
- # Should be approximately 60 seconds
- assert task.elapsed_time is not None
- assert 59 <= task.elapsed_time <= 61
- def test_elapsed_time_with_end(self):
- """Test elapsed time when task is complete."""
- start = datetime.now() - timedelta(seconds=120)
- end = start + timedelta(seconds=60)
- task = TranslationTask(
- id="task-1",
- start_time=start,
- end_time=end,
- )
- assert task.elapsed_time == 60.0
- def test_is_running(self):
- """Test is_running property."""
- task_running = TranslationTask(id="task-1", status=TaskStatus.RUNNING)
- task_idle = TranslationTask(id="task-2", status=TaskStatus.IDLE)
- assert task_running.is_running is True
- assert task_idle.is_running is False
- def test_is_paused(self):
- """Test is_paused property."""
- task_paused = TranslationTask(id="task-1", status=TaskStatus.PAUSED)
- task_running = TranslationTask(id="task-2", status=TaskStatus.RUNNING)
- assert task_paused.is_paused is True
- assert task_running.is_paused is False
- def test_can_start(self):
- """Test can_start property."""
- statuses_can_start = [
- TaskStatus.IDLE,
- TaskStatus.PAUSED,
- TaskStatus.FAILED,
- ]
- for status in statuses_can_start:
- task = TranslationTask(id=f"task-{status.value}", status=status)
- assert task.can_start is True, f"Status {status} should be startable"
- # These cannot be started
- task_running = TranslationTask(id="task-running", status=TaskStatus.RUNNING)
- task_completed = TranslationTask(id="task-completed", status=TaskStatus.COMPLETED)
- task_cancelled = TranslationTask(id="task-cancelled", status=TaskStatus.CANCELLED)
- assert task_running.can_start is False
- assert task_completed.can_start is False
- assert task_cancelled.can_start is False
- def test_can_pause(self):
- """Test can_pause property."""
- task_running = TranslationTask(id="task-1", status=TaskStatus.RUNNING)
- task_idle = TranslationTask(id="task-2", status=TaskStatus.IDLE)
- assert task_running.can_pause is True
- assert task_idle.can_pause is False
- def test_can_cancel(self):
- """Test can_cancel property."""
- task_running = TranslationTask(id="task-1", status=TaskStatus.RUNNING)
- task_paused = TranslationTask(id="task-2", status=TaskStatus.PAUSED)
- task_idle = TranslationTask(id="task-3", status=TaskStatus.IDLE)
- assert task_running.can_cancel is True
- assert task_paused.can_cancel is True
- assert task_idle.can_cancel is False
- class TestProgressUpdate:
- """Test ProgressUpdate data model."""
- def test_progress_update_creation(self):
- """Test creating a progress update."""
- update = ProgressUpdate(
- task_id="task-123",
- stage="translating",
- current=5,
- total=10,
- message="Processing chapter 5",
- elapsed_seconds=60,
- eta_seconds=120,
- )
- assert update.task_id == "task-123"
- assert update.stage == "translating"
- assert update.current == 5
- assert update.total == 10
- assert update.message == "Processing chapter 5"
- assert update.elapsed_seconds == 60
- assert update.eta_seconds == 120
- def test_progress_calculation(self):
- """Test progress percentage calculation."""
- update = ProgressUpdate(
- task_id="task-1",
- stage="translating",
- current=3,
- total=10,
- )
- assert update.progress == 30.0
- def test_progress_zero_when_no_total(self):
- """Test progress is zero when total is 0."""
- update = ProgressUpdate(
- task_id="task-1",
- stage="translating",
- current=0,
- total=0,
- )
- assert update.progress == 0.0
- def test_progress_complete(self):
- """Test progress when complete."""
- update = ProgressUpdate(
- task_id="task-1",
- stage="translating",
- current=10,
- total=10,
- )
- assert update.progress == 100.0
- class TestStatistics:
- """Test Statistics data model."""
- def test_statistics_defaults(self):
- """Test statistics with default values."""
- stats = Statistics()
- assert stats.total_translated_words == 0
- assert stats.total_chapters == 0
- assert stats.total_time_seconds == 0
- assert stats.successful_tasks == 0
- assert stats.failed_tasks == 0
- assert stats.terminology_usage_rate == 0.0
- def test_statistics_with_values(self):
- """Test statistics with specific values."""
- stats = Statistics(
- total_translated_words=50000,
- total_chapters=100,
- total_time_seconds=3000,
- successful_tasks=5,
- failed_tasks=1,
- terminology_usage_rate=85.5,
- )
- assert stats.total_translated_words == 50000
- assert stats.total_chapters == 100
- assert stats.total_time_seconds == 3000
- assert stats.successful_tasks == 5
- assert stats.failed_tasks == 1
- assert stats.terminology_usage_rate == 85.5
- def test_average_speed_calculation(self):
- """Test average translation speed calculation."""
- stats = Statistics(
- total_translated_words=6000, # 6000 words
- total_time_seconds=120, # 2 minutes
- )
- # 6000 words / 120 seconds * 60 = 3000 words/minute
- assert stats.average_speed == 3000.0
- def test_average_speed_zero_time(self):
- """Test average speed when time is zero."""
- stats = Statistics(
- total_translated_words=1000,
- total_time_seconds=0,
- )
- assert stats.average_speed == 0.0
- def test_completion_rate_calculation(self):
- """Test task completion rate calculation."""
- stats = Statistics(
- successful_tasks=9,
- failed_tasks=1,
- )
- # 9 / (9 + 1) * 100 = 90%
- assert stats.completion_rate == 90.0
- def test_completion_rate_no_tasks(self):
- """Test completion rate when no tasks."""
- stats = Statistics()
- assert stats.completion_rate == 0.0
- def test_total_tasks(self):
- """Test total tasks property."""
- stats = Statistics(
- successful_tasks=5,
- failed_tasks=2,
- )
- assert stats.total_tasks == 7
- class TestFileStatus:
- """Test FileStatus enum."""
- def test_all_status_values(self):
- """Test all status values exist."""
- assert FileStatus.PENDING.value == "pending"
- assert FileStatus.IMPORTING.value == "importing"
- assert FileStatus.READY.value == "ready"
- assert FileStatus.TRANSLATING.value == "translating"
- assert FileStatus.PAUSED.value == "paused"
- assert FileStatus.COMPLETED.value == "completed"
- assert FileStatus.FAILED.value == "failed"
- class TestTaskStatus:
- """Test TaskStatus enum."""
- def test_all_status_values(self):
- """Test all status values exist."""
- assert TaskStatus.IDLE.value == "idle"
- assert TaskStatus.RUNNING.value == "running"
- assert TaskStatus.PAUSED.value == "paused"
- assert TaskStatus.COMPLETED.value == "completed"
- assert TaskStatus.FAILED.value == "failed"
- assert TaskStatus.CANCELLED.value == "cancelled"
|