feat(taskflow): add core task API, storage persistence, csv export, stats page, and test coverage
This commit is contained in:
102
tests/test_repository_recovery.py
Normal file
102
tests/test_repository_recovery.py
Normal file
@@ -0,0 +1,102 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from datetime import UTC, datetime
|
||||
from uuid import uuid4
|
||||
|
||||
from app.storage import DATA_FORMAT_VERSION, StoredTask
|
||||
from app.storage.repository import JsonFileTaskRepository
|
||||
|
||||
|
||||
def make_task(title: str = "Task") -> StoredTask:
|
||||
return StoredTask(
|
||||
id=uuid4(),
|
||||
title=title,
|
||||
created_at=datetime.now(UTC),
|
||||
started_at=None,
|
||||
done_at=None,
|
||||
)
|
||||
|
||||
|
||||
def test_corrupted_json_triggers_backup_and_reset(tmp_path):
|
||||
file_path = tmp_path / "tasks.json"
|
||||
file_path.write_text('{"version": 1, "tasks": [', encoding="utf-8")
|
||||
|
||||
repo = JsonFileTaskRepository(file_path)
|
||||
|
||||
assert repo.list_tasks() == []
|
||||
|
||||
backups = list(tmp_path.glob("tasks.json.corrupted*"))
|
||||
assert len(backups) == 1
|
||||
assert backups[0].read_text(encoding="utf-8") == '{"version": 1, "tasks": ['
|
||||
|
||||
restored = json.loads(file_path.read_text(encoding="utf-8"))
|
||||
assert restored["version"] == DATA_FORMAT_VERSION
|
||||
assert restored["tasks"] == []
|
||||
|
||||
|
||||
def test_invalid_schema_triggers_backup_and_reset(tmp_path):
|
||||
file_path = tmp_path / "tasks.json"
|
||||
file_path.write_text(
|
||||
json.dumps(
|
||||
{
|
||||
"version": 1,
|
||||
"tasks": [
|
||||
{
|
||||
"id": str(uuid4()),
|
||||
"created_at": datetime.now(UTC).isoformat(),
|
||||
},
|
||||
],
|
||||
},
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
repo = JsonFileTaskRepository(file_path)
|
||||
|
||||
assert repo.list_tasks() == []
|
||||
backups = list(tmp_path.glob("tasks.json.corrupted*"))
|
||||
assert len(backups) == 1
|
||||
|
||||
|
||||
def test_repository_remains_writable_after_recovery(tmp_path):
|
||||
file_path = tmp_path / "tasks.json"
|
||||
file_path.write_text("not valid json", encoding="utf-8")
|
||||
repo = JsonFileTaskRepository(file_path)
|
||||
|
||||
task = make_task("Recovered")
|
||||
repo.create_task(task)
|
||||
|
||||
assert repo.list_tasks() == [task]
|
||||
|
||||
|
||||
def test_unsupported_version_creates_backup_and_resets_state(tmp_path):
|
||||
file_path = tmp_path / "tasks.json"
|
||||
file_path.write_text(
|
||||
json.dumps({"version": 999, "tasks": []}),
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
repo = JsonFileTaskRepository(file_path)
|
||||
|
||||
assert repo.list_tasks() == []
|
||||
backups = list(tmp_path.glob("tasks.json.corrupted*"))
|
||||
assert len(backups) == 1
|
||||
|
||||
restored = json.loads(file_path.read_text(encoding="utf-8"))
|
||||
assert restored["version"] == DATA_FORMAT_VERSION
|
||||
assert restored["tasks"] == []
|
||||
|
||||
|
||||
def test_restart_after_corrupted_persisted_state_does_not_crash(tmp_path):
|
||||
file_path = tmp_path / "tasks.json"
|
||||
repo_1 = JsonFileTaskRepository(file_path)
|
||||
repo_1.create_task(make_task("Initial"))
|
||||
|
||||
file_path.write_text("{broken", encoding="utf-8")
|
||||
|
||||
repo_2 = JsonFileTaskRepository(file_path)
|
||||
|
||||
assert repo_2.list_tasks() == []
|
||||
assert len(list(tmp_path.glob("tasks.json.corrupted*"))) == 1
|
||||
|
||||
Reference in New Issue
Block a user