"""Unit tests for routine API endpoints.""" import pytest from datetime import datetime, timezone from tinydb import Query from models.routine import Routine from models.routine_item import RoutineItem from models.routine_schedule import RoutineSchedule from models.routine_extension import RoutineExtension from models.pending_confirmation import PendingConfirmation from api.error_codes import ErrorCodes from db.db import routine_db, routine_items_db, routine_schedules_db, routine_extensions_db, pending_confirmations_db class TestRoutineModel: """Test Routine model serialization and validation.""" def test_routine_creation(self): """Test creating a routine instance.""" routine = Routine(name="Morning Routine", points=50, image_id="img123", user_id="user1") assert routine.name == "Morning Routine" assert routine.points == 50 assert routine.image_id == "img123" assert routine.user_id == "user1" assert routine.id is not None def test_routine_to_dict(self): """Test routine serialization.""" routine = Routine(name="Test", points=10, image_id=None, user_id="user1") data = routine.to_dict() assert data["name"] == "Test" assert data["points"] == 10 assert data["id"] == routine.id def test_routine_from_dict(self): """Test routine deserialization.""" routine_dict = {"id": "r1", "name": "Test", "points": 20, "image_id": "img1", "user_id": "u1"} routine = Routine.from_dict(routine_dict) assert routine.id == "r1" assert routine.name == "Test" assert routine.points == 20 class TestRoutineItemModel: """Test RoutineItem model.""" def test_routine_item_creation(self): """Test creating a routine item.""" item = RoutineItem(routine_id="r1", name="Make Bed", image_id=None, order=0) assert item.routine_id == "r1" assert item.name == "Make Bed" assert item.order == 0 def test_routine_item_to_dict(self): """Test item serialization.""" item = RoutineItem(routine_id="r1", name="Get Dressed", image_id="img1", order=1) data = item.to_dict() assert data["routine_id"] == "r1" assert data["name"] == "Get Dressed" assert data["order"] == 1 class TestRoutineScheduleModel: """Test RoutineSchedule model.""" def test_days_mode_schedule_creation(self): """Test creating days mode schedule.""" day_configs = [ {"day": 0, "hour": 8, "minute": 0}, {"day": 1, "hour": 9, "minute": 30}, ] schedule = RoutineSchedule( child_id="c1", routine_id="r1", mode="days", enabled=True, day_configs=day_configs, default_hour=8, default_minute=0, default_has_deadline=True, ) assert schedule.mode == "days" assert len(schedule.day_configs) == 2 assert schedule.enabled is True def test_interval_mode_schedule_creation(self): """Test creating interval mode schedule.""" schedule = RoutineSchedule( child_id="c1", routine_id="r1", mode="interval", enabled=True, interval_days=3, anchor_date="2026-05-01", interval_has_deadline=True, interval_hour=10, interval_minute=30, ) assert schedule.mode == "interval" assert schedule.interval_days == 3 assert schedule.interval_hour == 10 class TestRoutineExtensionModel: """Test RoutineExtension model.""" def test_extension_creation(self): """Test creating a routine extension.""" extension = RoutineExtension(child_id="c1", routine_id="r1", date="2026-05-10") assert extension.child_id == "c1" assert extension.routine_id == "r1" assert extension.date == "2026-05-10" class TestRoutineDB: """Test routine database operations.""" def setup_method(self): """Clear routine db before each test.""" routine_db.truncate() def test_add_routine(self): """Test adding a routine to database.""" routine = Routine(name="Test Routine", points=50, image_id=None, user_id="user1") routine_db.insert(routine.to_dict()) result = routine_db.search(Query().id == routine.id) assert len(result) == 1 assert result[0]["name"] == "Test Routine" def test_get_routine_by_id(self): """Test fetching routine by ID.""" routine = Routine(name="Fetch Test", points=30, image_id=None, user_id="user1") routine_db.insert(routine.to_dict()) result = routine_db.get(Query().id == routine.id) assert result is not None assert result["name"] == "Fetch Test" def test_list_user_routines(self): """Test listing routines for a user.""" r1 = Routine(name="R1", points=10, image_id=None, user_id="user1") r2 = Routine(name="R2", points=20, image_id=None, user_id="user1") r3 = Routine(name="R3", points=15, image_id=None, user_id="user2") routine_db.insert(r1.to_dict()) routine_db.insert(r2.to_dict()) routine_db.insert(r3.to_dict()) results = routine_db.search(Query().user_id == "user1") assert len(results) == 2 def test_update_routine(self): """Test updating a routine.""" routine = Routine(name="Original", points=50, image_id=None, user_id="user1") routine_db.insert(routine.to_dict()) routine_db.update({"name": "Updated", "points": 100}, Query().id == routine.id) result = routine_db.get(Query().id == routine.id) assert result["name"] == "Updated" assert result["points"] == 100 class TestRoutineItemDB: """Test routine item database operations.""" def setup_method(self): """Clear item db before each test.""" routine_items_db.truncate() def test_add_routine_item(self): """Test adding a routine item.""" item = RoutineItem(routine_id="r1", name="Make Bed", image_id=None, order=0) routine_items_db.insert(item.to_dict()) result = routine_items_db.search(Query().routine_id == "r1") assert len(result) == 1 assert result[0]["name"] == "Make Bed" def test_get_items_for_routine(self): """Test fetching all items for a routine.""" i1 = RoutineItem(routine_id="r1", name="Item1", image_id=None, order=0) i2 = RoutineItem(routine_id="r1", name="Item2", image_id=None, order=1) i3 = RoutineItem(routine_id="r2", name="Item3", image_id=None, order=0) routine_items_db.insert(i1.to_dict()) routine_items_db.insert(i2.to_dict()) routine_items_db.insert(i3.to_dict()) results = routine_items_db.search(Query().routine_id == "r1") assert len(results) == 2 def test_delete_item(self): """Test deleting a routine item.""" item = RoutineItem(routine_id="r1", name="Test", image_id=None, order=0) routine_items_db.insert(item.to_dict()) routine_items_db.remove(Query().id == item.id) result = routine_items_db.search(Query().id == item.id) assert len(result) == 0 class TestRoutineScheduleDB: """Test routine schedule database operations.""" def setup_method(self): """Clear schedule db before each test.""" routine_schedules_db.truncate() def test_upsert_schedule(self): """Test upserting a routine schedule.""" day_configs = [{"day": 0, "hour": 8, "minute": 0}] schedule = RoutineSchedule( child_id="c1", routine_id="r1", mode="days", enabled=True, day_configs=day_configs, default_hour=8, default_minute=0, default_has_deadline=True, ) routine_schedules_db.insert(schedule.to_dict()) result = routine_schedules_db.get( (Query().child_id == "c1") & (Query().routine_id == "r1") ) assert result is not None assert result["mode"] == "days" def test_delete_schedule(self): """Test deleting a schedule.""" schedule = RoutineSchedule( child_id="c1", routine_id="r1", mode="interval", enabled=True, interval_days=2, anchor_date="2026-05-01", interval_has_deadline=True, interval_hour=10, interval_minute=0, ) routine_schedules_db.insert(schedule.to_dict()) routine_schedules_db.remove( (Query().child_id == "c1") & (Query().routine_id == "r1") ) result = routine_schedules_db.search(Query().child_id == "c1") assert len(result) == 0 class TestPendingRoutineConfirmation: """Test pending routine confirmation workflow.""" def setup_method(self): """Clear db before each test.""" pending_confirmations_db.truncate() def test_create_routine_confirmation(self): """Test creating a pending routine confirmation.""" confirmation = PendingConfirmation( child_id="c1", entity_id="r1", entity_type="routine", user_id="u1", status="pending", ) pending_confirmations_db.insert(confirmation.to_dict()) result = pending_confirmations_db.get( (Query().child_id == "c1") & (Query().entity_id == "r1") & (Query().entity_type == "routine") ) assert result is not None assert result["status"] == "pending" def test_approve_routine_confirmation(self): """Test approving a routine confirmation.""" confirmation = PendingConfirmation( child_id="c1", entity_id="r1", entity_type="routine", user_id="u1", status="pending", ) pending_confirmations_db.insert(confirmation.to_dict()) today_utc = datetime.now(timezone.utc).isoformat() pending_confirmations_db.update( {"status": "approved", "approved_at": today_utc}, Query().id == confirmation.id, ) result = pending_confirmations_db.get(Query().id == confirmation.id) assert result["status"] == "approved" assert result["approved_at"] is not None