import pytest from datetime import datetime, timezone, timedelta # Import necessary classes from models.py from models import Metadata, TrackState, Ticket # --- Pytest Tests --- def test_track_state_instantiation() -> None: """Test creating a TrackState object.""" now = datetime.now(timezone.utc) metadata = Metadata( id="track-123", name="Initial Setup", status="in_progress", created_at=now - timedelta(days=1), updated_at=now, ) discussion = [ {"role": "user", "content": "Hello", "ts": now - timedelta(hours=1)}, {"role": "assistant", "content": "Hi there!", "ts": now - timedelta(hours=2)}, ] # Update Ticket instantiation to match models.py fields (description, assigned_to) tasks = [ Ticket(id="task-a", description="Design UI", status="todo", assigned_to="dev1"), Ticket(id="task-b", description="Implement Backend", status="todo", assigned_to="dev2"), ] track_state = TrackState( metadata=metadata, discussion=discussion, tasks=tasks, ) assert track_state.metadata.id == "track-123" assert len(track_state.discussion) == 2 assert len(track_state.tasks) == 2 assert isinstance(track_state.tasks[0], Ticket) assert track_state.tasks[0].description == "Design UI" assert track_state.tasks[0].assigned_to == "dev1" def test_track_state_to_dict() -> None: """Test the to_dict() method for serialization.""" now = datetime.now(timezone.utc) metadata = Metadata( id="track-456", name="Refinement Phase", status="completed", created_at=now - timedelta(days=5), updated_at=now - timedelta(days=2), ) discussion = [ {"role": "user", "content": "Need changes", "ts": now - timedelta(hours=3)}, {"role": "assistant", "content": "Understood.", "ts": now - timedelta(hours=4)}, ] # Update Ticket instantiation tasks = [ Ticket(id="task-c", description="Add feature X", status="in_progress", assigned_to="dev3"), ] track_state = TrackState( metadata=metadata, discussion=discussion, tasks=tasks, ) track_dict = track_state.to_dict() assert track_dict["metadata"]["id"] == "track-456" assert track_dict["metadata"]["created_at"] == metadata.created_at.isoformat() assert track_dict["metadata"]["updated_at"] == metadata.updated_at.isoformat() assert len(track_dict["discussion"]) == 2 assert track_dict["discussion"][0]["ts"] == discussion[0]["ts"].isoformat() assert len(track_dict["tasks"]) == 1 # Use the Ticket's to_dict method for serialization assert track_dict["tasks"][0]["id"] == "task-c" assert track_dict["tasks"][0]["description"] == "Add feature X" assert track_dict["tasks"][0]["assigned_to"] == "dev3" def test_track_state_from_dict() -> None: """Test the from_dict() class method for deserialization.""" now = datetime.now(timezone.utc) track_dict_data = { "metadata": { "id": "track-789", "name": "Final Review", "status": "pending", "created_at": (now - timedelta(days=10)).isoformat(), "updated_at": (now - timedelta(days=9)).isoformat(), }, "discussion": [ {"role": "user", "content": "Review complete.", "ts": (now - timedelta(hours=5)).isoformat()}, ], "tasks": [ # Use fields from models.py Ticket definition for deserialization {"id": "task-d", "description": "Deploy", "status": "completed", "assigned_to": "ops1"}, ], } track_state = TrackState.from_dict(track_dict_data) assert isinstance(track_state, TrackState) assert track_state.metadata.id == "track-789" assert isinstance(track_state.metadata.created_at, datetime) assert track_state.metadata.created_at.isoformat() == track_dict_data["metadata"]["created_at"] assert len(track_state.discussion) == 1 assert isinstance(track_state.discussion[0]["ts"], datetime) assert track_state.discussion[0]["ts"].isoformat() == track_dict_data["discussion"][0]["ts"] assert len(track_state.tasks) == 1 assert isinstance(track_state.tasks[0], Ticket) assert track_state.tasks[0].id == "task-d" assert track_state.tasks[0].description == "Deploy" assert track_state.tasks[0].assigned_to == "ops1" # Test case for empty lists and missing keys for robustness def test_track_state_from_dict_empty_and_missing() -> None: """Test from_dict with empty lists and missing optional keys.""" track_dict_data = { "metadata": { "id": "track-empty", "name": "Empty State", # created_at, updated_at, status are optional in from_dict logic }, "discussion": [], # Empty discussion list "tasks": [], # Empty tasks list } track_state = TrackState.from_dict(track_dict_data) assert isinstance(track_state, TrackState) assert track_state.metadata.id == "track-empty" assert track_state.metadata.name == "Empty State" assert track_state.metadata.created_at is None assert track_state.metadata.updated_at is None assert track_state.metadata.status is None assert len(track_state.discussion) == 0 assert len(track_state.tasks) == 0 # Test case for to_dict with None values or missing optional data def test_track_state_to_dict_with_none() -> None: """Test to_dict with None values in optional fields.""" now = datetime.now(timezone.utc) metadata = Metadata( id="track-none", name="None Test", status=None, # None status created_at=now, updated_at=None, # None updated_at ) discussion = [ {"role": "system", "content": "Info", "ts": None}, # None timestamp ] # Update Ticket instantiation tasks = [ Ticket(id="task-none", description="Task None", status="pending", assigned_to="anon"), ] track_state = TrackState( metadata=metadata, discussion=discussion, tasks=tasks, ) track_dict = track_state.to_dict() assert track_dict["metadata"]["status"] is None # Check that isoformat was called on datetime object, not None assert track_dict["metadata"]["created_at"] == now.isoformat() assert track_dict["metadata"]["updated_at"] is None # This should be None as it's passed as None assert track_dict["discussion"][0]["ts"] is None assert track_dict["tasks"][0]["description"] == "Task None" assert track_dict["tasks"][0]["assigned_to"] == "anon"