from __future__ import annotations from datetime import UTC, datetime, timedelta from uuid import uuid4 from app.storage import DATA_FORMAT_VERSION, StoredTask from app.storage.repository import JsonFileTaskRepository def make_task( *, title: str = "Task", started: bool = False, done: bool = False, ) -> StoredTask: now = datetime.now(UTC) started_at = now + timedelta(seconds=1) if started else None done_at = now + timedelta(seconds=2) if done else None return StoredTask( id=uuid4(), title=title, created_at=now, started_at=started_at, done_at=done_at, ) def test_replace_all_replaces_empty_state_with_provided_tasks(tmp_path): repo = JsonFileTaskRepository(tmp_path / "tasks.json") task_1 = make_task(title="One") task_2 = make_task(title="Two") repo.replace_all([task_1, task_2]) assert repo.list_tasks() == [task_1, task_2] def test_replace_all_fully_overwrites_previous_content(tmp_path): repo = JsonFileTaskRepository(tmp_path / "tasks.json") old_task = make_task(title="Old") new_task = make_task(title="New") repo.create_task(old_task) repo.replace_all([new_task]) assert repo.list_tasks() == [new_task] def test_data_format_version_matches_constant(tmp_path): repo = JsonFileTaskRepository(tmp_path / "tasks.json") assert repo.data_format_version == DATA_FORMAT_VERSION def test_data_format_version_is_stable_across_instances(tmp_path): file_path = tmp_path / "tasks.json" repo_1 = JsonFileTaskRepository(file_path) repo_2 = JsonFileTaskRepository(file_path) assert repo_1.data_format_version == repo_2.data_format_version == DATA_FORMAT_VERSION def test_missing_file_is_created_automatically(tmp_path): file_path = tmp_path / "nested" / "tasks.json" repo = JsonFileTaskRepository(file_path) assert repo.list_tasks() == [] assert file_path.exists() def test_existing_valid_file_is_loaded_on_new_instance(tmp_path): file_path = tmp_path / "tasks.json" repo_1 = JsonFileTaskRepository(file_path) task = make_task(title="Persisted") repo_1.create_task(task) repo_2 = JsonFileTaskRepository(file_path) assert repo_2.list_tasks() == [task] def test_parent_directory_is_created_if_missing(tmp_path): file_path = tmp_path / "deep" / "nested" / "data" / "tasks.json" repo = JsonFileTaskRepository(file_path) assert file_path.parent.exists() assert repo.list_tasks() == [] def test_current_version_payload_loads_as_is(tmp_path): file_path = tmp_path / "tasks.json" repo_1 = JsonFileTaskRepository(file_path) task = make_task(title="Versioned") repo_1.create_task(task) repo_2 = JsonFileTaskRepository(file_path) assert repo_2.data_format_version == DATA_FORMAT_VERSION assert repo_2.list_tasks() == [task] def test_empty_current_version_payload_loads_without_backup(tmp_path): file_path = tmp_path / "tasks.json" repo = JsonFileTaskRepository(file_path) assert repo.list_tasks() == [] assert not list(tmp_path.glob("tasks.json.corrupted*")) def test_persistence_between_repository_instances(tmp_path): file_path = tmp_path / "tasks.json" repo_1 = JsonFileTaskRepository(file_path) task = make_task(title="Cross-session") repo_1.create_task(task) repo_2 = JsonFileTaskRepository(file_path) assert repo_2.get_task(task.id) == task def test_multiple_operations_survive_restart(tmp_path): file_path = tmp_path / "tasks.json" repo_1 = JsonFileTaskRepository(file_path) task_1 = make_task(title="First") task_2 = make_task(title="Second") repo_1.create_task(task_1) repo_1.create_task(task_2) repo_1.delete_task(task_1.id) updated_task_2 = StoredTask( id=task_2.id, title="Second updated", created_at=task_2.created_at, started_at=datetime.now(UTC), done_at=None, ) repo_1.update_task(updated_task_2) repo_2 = JsonFileTaskRepository(file_path) assert repo_2.list_tasks() == [updated_task_2]