"""Tests for the Child service. Demonstrates service-layer testing patterns: test business logic, validation, and error handling. Aim for >=90% coverage on new code (ARCHITECTURE.md ยง10). """ from datetime import date from uuid import uuid4 import pytest from app.entities.child import Child from app.services.child_service import ChildService from app.utils.errors import NotFoundError, ValidationError from tests.conftest import SeededIds class TestChildService: """Test ChildService business logic.""" async def test_create_with_valid_inputs( self, child_service: ChildService, seeded_ids: SeededIds, ) -> None: """Create a child with valid inputs.""" result = await child_service.create( workspace_id=seeded_ids.workspace_id, name="Alice", birth_date=date(2024, 6, 15), preschool_start_date=date(2027, 8, 1), ) assert result.name == "Alice" assert result.birth_date == date(2024, 6, 15) assert result.preschool_start_date == date(2027, 8, 1) async def test_create_without_preschool_start_date( self, child_service: ChildService, seeded_ids: SeededIds, ) -> None: """Create a child without preschool start date (optional).""" result = await child_service.create( workspace_id=seeded_ids.workspace_id, name="Bob", birth_date=date(2024, 3, 10), preschool_start_date=None, ) assert result.name == "Bob" assert result.preschool_start_date is None async def test_create_with_preschool_before_birth_raises( self, child_service: ChildService, seeded_ids: SeededIds, ) -> None: """Creating with preschool_start_date <= birth_date raises ValidationError.""" with pytest.raises(ValidationError, match="preschool_start_date must be after birth_date"): await child_service.create( workspace_id=seeded_ids.workspace_id, name="Charlie", birth_date=date(2024, 6, 15), preschool_start_date=date(2024, 6, 15), ) async def test_create_with_preschool_same_date_raises( self, child_service: ChildService, seeded_ids: SeededIds, ) -> None: """Creating with preschool_start_date == birth_date raises ValidationError.""" with pytest.raises(ValidationError): await child_service.create( workspace_id=seeded_ids.workspace_id, name="Charlie", birth_date=date(2024, 6, 15), preschool_start_date=date(2024, 6, 14), ) async def test_get_existing_child( self, child_service: ChildService, seeded_ids: SeededIds, ) -> None: """Get an existing child by ID.""" created = await child_service.create( workspace_id=seeded_ids.workspace_id, name="Diana", birth_date=date(2025, 1, 1), ) fetched = await child_service.get(created.id) assert fetched.id == created.id assert fetched.name == "Diana" async def test_get_missing_child_raises( self, child_service: ChildService, ) -> None: """Getting a missing child raises NotFoundError.""" with pytest.raises(NotFoundError, match="not found"): await child_service.get(uuid4()) async def test_list_by_workspace( self, child_service: ChildService, seeded_ids: SeededIds, ) -> None: """List all children in a workspace.""" ws = seeded_ids.workspace_id c1 = await child_service.create( workspace_id=ws, name="Child1", birth_date=date(2024, 1, 1), ) c2 = await child_service.create( workspace_id=ws, name="Child2", birth_date=date(2024, 6, 1), ) result = await child_service.list_by_workspace(ws) assert len(result) >= 2 ids = {c.id for c in result} assert c1.id in ids assert c2.id in ids async def test_list_by_workspace_empty( self, child_service: ChildService, ) -> None: """Listing children for workspace with none returns empty list.""" result = await child_service.list_by_workspace(uuid4()) assert result == [] async def test_update_name_only( self, child_service: ChildService, seeded_ids: SeededIds, ) -> None: """Update only the name.""" created = await child_service.create( workspace_id=seeded_ids.workspace_id, name="Old Name", birth_date=date(2024, 6, 15), ) updated = await child_service.update( child_id=created.id, name="New Name", ) assert updated.name == "New Name" assert updated.birth_date == date(2024, 6, 15) async def test_update_birth_date_only( self, child_service: ChildService, seeded_ids: SeededIds, ) -> None: """Update only the birth date.""" created = await child_service.create( workspace_id=seeded_ids.workspace_id, name="Emma", birth_date=date(2024, 6, 15), ) updated = await child_service.update( child_id=created.id, birth_date=date(2024, 7, 20), ) assert updated.name == "Emma" assert updated.birth_date == date(2024, 7, 20) async def test_update_add_preschool_date( self, child_service: ChildService, seeded_ids: SeededIds, ) -> None: """Add preschool start date to a child that didn't have one.""" created = await child_service.create( workspace_id=seeded_ids.workspace_id, name="Frank", birth_date=date(2024, 6, 15), preschool_start_date=None, ) updated = await child_service.update( child_id=created.id, preschool_start_date=date(2027, 8, 1), ) assert updated.preschool_start_date == date(2027, 8, 1) async def test_update_with_invalid_preschool_date_raises( self, child_service: ChildService, seeded_ids: SeededIds, ) -> None: """Updating with invalid preschool date raises ValidationError.""" created = await child_service.create( workspace_id=seeded_ids.workspace_id, name="Grace", birth_date=date(2024, 6, 15), ) with pytest.raises(ValidationError): await child_service.update( child_id=created.id, preschool_start_date=date(2024, 6, 15), ) async def test_update_missing_child_raises( self, child_service: ChildService, ) -> None: """Updating a missing child raises NotFoundError.""" with pytest.raises(NotFoundError): await child_service.update( child_id=uuid4(), name="Henry", ) async def test_delete_existing_child( self, child_service: ChildService, seeded_ids: SeededIds, ) -> None: """Delete an existing child.""" created = await child_service.create( workspace_id=seeded_ids.workspace_id, name="Iris", birth_date=date(2024, 6, 15), ) await child_service.delete(created.id) with pytest.raises(NotFoundError): await child_service.get(created.id) async def test_delete_missing_child_raises( self, child_service: ChildService, ) -> None: """Deleting a missing child raises NotFoundError.""" with pytest.raises(NotFoundError): await child_service.delete(uuid4()) async def test_update_multiple_fields( self, child_service: ChildService, seeded_ids: SeededIds, ) -> None: """Update multiple fields at once.""" created = await child_service.create( workspace_id=seeded_ids.workspace_id, name="Jack", birth_date=date(2024, 6, 15), preschool_start_date=date(2027, 8, 1), ) updated = await child_service.update( child_id=created.id, name="Jake", birth_date=date(2024, 7, 1), preschool_start_date=date(2027, 9, 1), ) assert updated.name == "Jake" assert updated.birth_date == date(2024, 7, 1) assert updated.preschool_start_date == date(2027, 9, 1)