Implements 72 tests covering: - Model tests (Project, Session, Note, Change) - ProjectService tests (create, get, list, ensure structure) - SessionService tests (active session management) - FileStorage tests (read/write operations) - Complete flow tests (init -> start -> note -> stop -> show) - Note consolidation tests Uses pytest with tmp_path fixtures for isolated testing.
194 lines
6.6 KiB
Python
194 lines
6.6 KiB
Python
"""Tests for FileStorage."""
|
|
|
|
import json
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
import yaml
|
|
|
|
from tracker.models import Session
|
|
from tracker.storage import FileStorage
|
|
|
|
|
|
class TestReadWriteProjectMeta:
|
|
"""Tests for read/write project meta operations."""
|
|
|
|
def test_write_and_read_project_meta(self, tmp_project_dir):
|
|
"""Test writing and reading project meta."""
|
|
storage = FileStorage(tmp_project_dir)
|
|
slug = "test-project"
|
|
|
|
meta_data = {
|
|
"id": "test-id",
|
|
"name": "Test Project",
|
|
"slug": slug,
|
|
"description": "A test project",
|
|
"type": "code",
|
|
"status": "active",
|
|
"tags": ["python"],
|
|
"created_at": datetime(2024, 1, 15, 10, 0, 0).isoformat(),
|
|
"updated_at": datetime(2024, 1, 15, 10, 0, 0).isoformat(),
|
|
}
|
|
|
|
storage.write_project_meta(slug, meta_data)
|
|
|
|
result = storage.read_project_meta(slug)
|
|
assert result["name"] == "Test Project"
|
|
assert result["slug"] == slug
|
|
assert result["type"] == "code"
|
|
|
|
def test_read_project_meta_creates_parent_directories(self, tmp_project_dir):
|
|
"""Test that write_project_meta creates parent directories."""
|
|
storage = FileStorage(tmp_project_dir)
|
|
slug = "new-project"
|
|
|
|
meta_data = {"id": "test-id", "name": "Test", "slug": slug}
|
|
storage.write_project_meta(slug, meta_data)
|
|
|
|
meta_path = tmp_project_dir / slug / "meta" / "project.yaml"
|
|
assert meta_path.exists()
|
|
|
|
def test_read_project_meta_raises_for_nonexistent(self, tmp_project_dir):
|
|
"""Test that reading nonexistent project raises error."""
|
|
storage = FileStorage(tmp_project_dir)
|
|
|
|
with pytest.raises(FileNotFoundError):
|
|
storage.read_project_meta("nonexistent")
|
|
|
|
|
|
class TestAppendToLog:
|
|
"""Tests for append_to_log operations."""
|
|
|
|
def test_append_to_log_creates_log_file(self, tmp_project_dir):
|
|
"""Test that append_to_log creates LOG.md if it doesn't exist."""
|
|
storage = FileStorage(tmp_project_dir)
|
|
slug = "test-project"
|
|
|
|
# Create project directory first
|
|
(tmp_project_dir / slug).mkdir(parents=True)
|
|
|
|
storage.append_to_log(slug, "# Test Log Entry\n")
|
|
|
|
log_path = tmp_project_dir / slug / "LOG.md"
|
|
assert log_path.exists()
|
|
|
|
def test_append_to_log_appends_content(self, tmp_project_dir):
|
|
"""Test that append_to_log appends content to LOG.md."""
|
|
storage = FileStorage(tmp_project_dir)
|
|
slug = "test-project"
|
|
|
|
# Create project directory first
|
|
(tmp_project_dir / slug).mkdir(parents=True)
|
|
|
|
storage.append_to_log(slug, "# First Entry\n")
|
|
storage.append_to_log(slug, "# Second Entry\n")
|
|
|
|
content = storage.read_log(slug)
|
|
assert "# First Entry" in content
|
|
assert "# Second Entry" in content
|
|
|
|
def test_read_log_returns_empty_string_for_nonexistent(self, tmp_project_dir):
|
|
"""Test that read_log returns empty string for nonexistent log."""
|
|
storage = FileStorage(tmp_project_dir)
|
|
|
|
result = storage.read_log("nonexistent")
|
|
assert result == ""
|
|
|
|
|
|
class TestActiveSessionStorage:
|
|
"""Tests for active session storage operations."""
|
|
|
|
def test_write_and_read_active_session(self, tmp_project_dir, mock_session):
|
|
"""Test writing and reading active session."""
|
|
storage = FileStorage(tmp_project_dir)
|
|
|
|
session_data = mock_session.model_dump(mode="json")
|
|
session_data["started_at"] = mock_session.started_at.isoformat()
|
|
if mock_session.ended_at:
|
|
session_data["ended_at"] = mock_session.ended_at.isoformat()
|
|
|
|
storage.write_active_session(session_data)
|
|
|
|
result = storage.read_active_session()
|
|
assert result is not None
|
|
assert result["id"] == mock_session.id
|
|
|
|
def test_read_active_session_returns_none_when_not_exists(self, tmp_project_dir):
|
|
"""Test that read_active_session returns None when file doesn't exist."""
|
|
storage = FileStorage(tmp_project_dir)
|
|
|
|
result = storage.read_active_session()
|
|
assert result is None
|
|
|
|
def test_delete_active_session(self, tmp_project_dir, mock_session):
|
|
"""Test deleting active session."""
|
|
storage = FileStorage(tmp_project_dir)
|
|
|
|
session_data = mock_session.model_dump(mode="json")
|
|
session_data["started_at"] = mock_session.started_at.isoformat()
|
|
storage.write_active_session(session_data)
|
|
|
|
storage.delete_active_session()
|
|
|
|
result = storage.read_active_session()
|
|
assert result is None
|
|
|
|
def test_delete_active_session_when_not_exists(self, tmp_project_dir):
|
|
"""Test deleting active session when it doesn't exist doesn't error."""
|
|
storage = FileStorage(tmp_project_dir)
|
|
|
|
storage.delete_active_session()
|
|
|
|
|
|
class TestProjectExistence:
|
|
"""Tests for project existence checks."""
|
|
|
|
def test_project_exists_returns_true_for_existing_project(self, tmp_project_dir):
|
|
"""Test that project_exists returns True for existing project."""
|
|
storage = FileStorage(tmp_project_dir)
|
|
slug = "test-project"
|
|
|
|
(tmp_project_dir / slug).mkdir()
|
|
|
|
assert storage.project_exists(slug) is True
|
|
|
|
def test_project_exists_returns_false_for_nonexistent(self, tmp_project_dir):
|
|
"""Test that project_exists returns False for nonexistent project."""
|
|
storage = FileStorage(tmp_project_dir)
|
|
|
|
assert storage.project_exists("nonexistent") is False
|
|
|
|
|
|
class TestListProjects:
|
|
"""Tests for listing projects."""
|
|
|
|
def test_list_projects_returns_all_projects(self, tmp_project_dir):
|
|
"""Test that list_projects returns all project slugs."""
|
|
storage = FileStorage(tmp_project_dir)
|
|
|
|
(tmp_project_dir / "project-1").mkdir()
|
|
(tmp_project_dir / "project-2").mkdir()
|
|
|
|
projects = storage.list_projects()
|
|
assert "project-1" in projects
|
|
assert "project-2" in projects
|
|
|
|
def test_list_projects_excludes_hidden_directories(self, tmp_project_dir):
|
|
"""Test that list_projects excludes hidden directories."""
|
|
storage = FileStorage(tmp_project_dir)
|
|
|
|
(tmp_project_dir / "project-1").mkdir()
|
|
(tmp_project_dir / ".hidden").mkdir()
|
|
|
|
projects = storage.list_projects()
|
|
assert "project-1" in projects
|
|
assert ".hidden" not in projects
|
|
|
|
def test_list_projects_returns_empty_list_when_no_projects(self, tmp_project_dir):
|
|
"""Test that list_projects returns empty list when no projects exist."""
|
|
storage = FileStorage(tmp_project_dir)
|
|
|
|
projects = storage.list_projects()
|
|
assert projects == []
|