""" Unit tests for data model mappers Tests mapper conversion correctness between entities and schemas. """ import pytest from datetime import datetime import uuid from src.models.entities import ( ProjectDB, AssetDB, EpisodeDB, StoryboardDB, TaskDB, CanvasMetadataDB, ) from src.models.schemas import ( CreateProjectRequest, UpdateProjectRequest, CreateCharacterAssetRequest, CreateSceneAssetRequest, CreatePropAssetRequest, UpdateAssetRequest, CreateEpisodeRequest, UpdateEpisodeRequest, CreateStoryboardRequest, UpdateStoryboardRequest, ) from src.mappers import ( ProjectMapper, AssetMapper, EpisodeMapper, StoryboardMapper, TaskMapper, CanvasMetadataMapper, ) class TestProjectMapper: """Test ProjectMapper conversion correctness""" def test_to_entity_from_create_request(self): """Test converting CreateProjectRequest to ProjectDB entity""" request = CreateProjectRequest( name="Test Project", description="Test Description", type="video", chapters=[{"title": "Chapter 1"}], assets=[{"name": "Asset 1"}] ) entity = ProjectMapper.to_entity(request) assert entity.name == "Test Project" assert entity.description == "Test Description" assert entity.type == "video" assert entity.status == "active" assert entity.id is not None assert entity.created_at is not None assert entity.updated_at is not None assert entity.chapters == [{"title": "Chapter 1"}] def test_to_entity_with_custom_id(self): """Test converting with custom ID""" request = CreateProjectRequest(name="Test Project") custom_id = str(uuid.uuid4()) entity = ProjectMapper.to_entity(request, project_id=custom_id) assert entity.id == custom_id def test_to_schema_from_entity(self): """Test converting ProjectDB entity to schema""" now = datetime.now().timestamp() entity = ProjectDB( id="proj_123", name="Test Project", description="Test Description", type="video", status="active", created_at=now, updated_at=now, resolution="1920x1080", ratio="16:9" ) schema = ProjectMapper.to_schema(entity) assert schema.id == "proj_123" assert schema.name == "Test Project" assert schema.description == "Test Description" assert schema.type == "video" assert schema.status == "active" assert schema.resolution == "1920x1080" assert schema.ratio == "16:9" def test_update_entity(self): """Test updating ProjectDB entity from UpdateProjectRequest""" entity = ProjectDB( name="Original Name", description="Original Description", resolution="1280x720" ) update_request = UpdateProjectRequest( name="Updated Name", resolution="1920x1080" ) updated = ProjectMapper.update_entity(entity, update_request) assert updated.name == "Updated Name" assert updated.resolution == "1920x1080" assert updated.description == "Original Description" # unchanged assert updated.updated_at > entity.created_at def test_update_entity_partial(self): """Test partial update of ProjectDB entity""" entity = ProjectDB( name="Original Name", description="Original Description", resolution="1280x720" ) update_request = UpdateProjectRequest(name="Updated Name") updated = ProjectMapper.update_entity(entity, update_request) assert updated.name == "Updated Name" assert updated.description == "Original Description" assert updated.resolution == "1280x720" def test_roundtrip_conversion(self): """Test roundtrip conversion: Request -> Entity -> Schema""" request = CreateProjectRequest( name="Test Project", description="Test Description", type="video" ) entity = ProjectMapper.to_entity(request) schema = ProjectMapper.to_schema(entity) assert schema.name == request.name assert schema.description == request.description assert schema.type == request.type class TestAssetMapper: """Test AssetMapper conversion correctness""" def test_to_entity_character_asset(self): """Test converting CreateCharacterAssetRequest to AssetDB""" request = CreateCharacterAssetRequest( type="character", name="Hero", desc="Main character", tags=["protagonist"], age="25", gender="male", role="hero", appearance="Tall and strong" ) entity = AssetMapper.to_entity(request, project_id="proj_123") assert entity.project_id == "proj_123" assert entity.type == "character" assert entity.name == "Hero" assert entity.desc == "Main character" assert entity.tags == ["protagonist"] assert entity.extra_data["age"] == "25" assert entity.extra_data["gender"] == "male" assert entity.extra_data["role"] == "hero" assert entity.extra_data["appearance"] == "Tall and strong" def test_to_entity_scene_asset(self): """Test converting CreateSceneAssetRequest to AssetDB""" request = CreateSceneAssetRequest( type="scene", name="Forest", desc="Dark forest", location="Northern Woods", time_of_day="night", atmosphere="mysterious" ) entity = AssetMapper.to_entity(request, project_id="proj_123") assert entity.type == "scene" assert entity.name == "Forest" assert entity.extra_data["location"] == "Northern Woods" assert entity.extra_data["time_of_day"] == "night" assert entity.extra_data["atmosphere"] == "mysterious" def test_to_entity_prop_asset(self): """Test converting CreatePropAssetRequest to AssetDB""" request = CreatePropAssetRequest( type="prop", name="Magic Sword", desc="Ancient sword", usage="weapon" ) entity = AssetMapper.to_entity(request, project_id="proj_123") assert entity.type == "prop" assert entity.name == "Magic Sword" assert entity.extra_data["usage"] == "weapon" def test_to_schema_character_asset(self): """Test converting AssetDB to CharacterAsset schema""" entity = AssetDB( id="asset_123", project_id="proj_123", type="character", name="Hero", desc="Main character", tags=["protagonist"], extra_data={"age": "25", "gender": "male", "role": "hero"} ) schema = AssetMapper.to_schema(entity) assert schema.id == "asset_123" assert schema.type == "character" assert schema.name == "Hero" assert schema.age == "25" assert schema.gender == "male" assert schema.role == "hero" def test_to_schema_scene_asset(self): """Test converting AssetDB to SceneAsset schema""" entity = AssetDB( id="asset_123", project_id="proj_123", type="scene", name="Forest", desc="Dark forest", extra_data={"location": "Northern Woods", "time_of_day": "night"} ) schema = AssetMapper.to_schema(entity) assert schema.type == "scene" assert schema.name == "Forest" assert schema.location == "Northern Woods" assert schema.time_of_day == "night" def test_update_entity(self): """Test updating AssetDB entity""" entity = AssetDB( project_id="proj_123", type="character", name="Original Name", desc="Original Description", extra_data={"age": "25"} ) update_request = UpdateAssetRequest( name="Updated Name", age="26" ) updated = AssetMapper.update_entity(entity, update_request) assert updated.name == "Updated Name" assert updated.extra_data["age"] == "26" assert updated.desc == "Original Description" def test_roundtrip_conversion_character(self): """Test roundtrip conversion for character asset""" request = CreateCharacterAssetRequest( type="character", name="Hero", desc="Main character", age="25", gender="male" ) entity = AssetMapper.to_entity(request, project_id="proj_123") schema = AssetMapper.to_schema(entity) assert schema.name == request.name assert schema.desc == request.desc assert schema.age == request.age assert schema.gender == request.gender class TestEpisodeMapper: """Test EpisodeMapper conversion correctness""" def test_to_entity(self): """Test converting CreateEpisodeRequest to EpisodeDB""" request = CreateEpisodeRequest( title="Episode 1", order=1, desc="First episode", status="draft" ) entity = EpisodeMapper.to_entity(request, project_id="proj_123") assert entity.project_id == "proj_123" assert entity.title == "Episode 1" assert entity.order_index == 1 assert entity.desc == "First episode" assert entity.status == "draft" assert entity.id is not None def test_to_schema(self): """Test converting EpisodeDB to Episode schema""" entity = EpisodeDB( id="ep_123", project_id="proj_123", order_index=1, title="Episode 1", desc="First episode", content="Episode content", status="draft" ) schema = EpisodeMapper.to_schema(entity) assert schema.id == "ep_123" assert schema.title == "Episode 1" assert schema.order == 1 assert schema.desc == "First episode" assert schema.content == "Episode content" assert schema.status == "draft" def test_update_entity(self): """Test updating EpisodeDB entity""" entity = EpisodeDB( project_id="proj_123", order_index=1, title="Original Title", status="draft" ) update_request = UpdateEpisodeRequest( title="Updated Title", status="production" ) updated = EpisodeMapper.update_entity(entity, update_request) assert updated.title == "Updated Title" assert updated.status == "production" assert updated.order_index == 1 # unchanged def test_roundtrip_conversion(self): """Test roundtrip conversion for episode""" request = CreateEpisodeRequest( title="Episode 1", order=1, desc="First episode" ) entity = EpisodeMapper.to_entity(request, project_id="proj_123") schema = EpisodeMapper.to_schema(entity) assert schema.title == request.title assert schema.order == request.order assert schema.desc == request.desc class TestStoryboardMapper: """Test StoryboardMapper conversion correctness""" def test_to_entity(self): """Test converting CreateStoryboardRequest to StoryboardDB""" request = CreateStoryboardRequest( episode_id="ep_123", order=1, shot="Shot 1", desc="Opening scene", duration="5s", type="image", scene_id="scene_123", character_ids=["char_1", "char_2"], prop_ids=["prop_1"], camera_angle="wide", lens="50mm", location="forest", time="morning" ) entity = StoryboardMapper.to_entity(request, project_id="proj_123") assert entity.project_id == "proj_123" assert entity.episode_id == "ep_123" assert entity.order_index == 1 assert entity.shot == "Shot 1" assert entity.desc == "Opening scene" assert entity.duration == "5s" assert entity.type == "image" assert entity.scene_id == "scene_123" assert entity.character_ids == ["char_1", "char_2"] assert entity.prop_ids == ["prop_1"] assert entity.camera_angle == "wide" assert entity.lens == "50mm" assert entity.location == "forest" assert entity.time == "morning" def test_to_schema(self): """Test converting StoryboardDB to Storyboard schema""" entity = StoryboardDB( id="sb_123", project_id="proj_123", episode_id="ep_123", order_index=1, shot="Shot 1", desc="Opening scene", duration="5s", type="image", scene_id="scene_123", character_ids=["char_1"], camera_angle="wide", location="forest" ) schema = StoryboardMapper.to_schema(entity) assert schema.id == "sb_123" assert schema.episode_id == "ep_123" assert schema.order == 1 assert schema.shot == "Shot 1" assert schema.scene_id == "scene_123" assert schema.character_ids == ["char_1"] assert schema.camera_angle == "wide" assert schema.location == "forest" def test_update_entity(self): """Test updating StoryboardDB entity""" entity = StoryboardDB( project_id="proj_123", episode_id="ep_123", order_index=1, shot="Original Shot", desc="Original Description", duration="5s", type="image" ) update_request = UpdateStoryboardRequest( shot="Updated Shot", duration="10s", camera_angle="close-up" ) updated = StoryboardMapper.update_entity(entity, update_request) assert updated.shot == "Updated Shot" assert updated.duration == "10s" assert updated.camera_angle == "close-up" assert updated.desc == "Original Description" # unchanged def test_roundtrip_conversion(self): """Test roundtrip conversion for storyboard""" request = CreateStoryboardRequest( episode_id="ep_123", order=1, shot="Shot 1", desc="Opening scene", duration="5s", type="image", camera_angle="wide" ) entity = StoryboardMapper.to_entity(request, project_id="proj_123") schema = StoryboardMapper.to_schema(entity) assert schema.episode_id == request.episode_id assert schema.order == request.order assert schema.shot == request.shot assert schema.camera_angle == request.camera_angle class TestTaskMapper: """Test TaskMapper conversion correctness""" def test_to_entity(self): """Test creating TaskDB entity from parameters""" entity = TaskMapper.to_entity( task_type="image", model="flux-dev", params={"prompt": "test"}, status="pending", user_id="user_123", project_id="proj_123", max_retries=5 ) assert entity.type == "image" assert entity.model == "flux-dev" assert entity.params == {"prompt": "test"} assert entity.status == "pending" assert entity.user_id == "user_123" assert entity.project_id == "proj_123" assert entity.max_retries == 5 assert entity.retry_count == 0 assert entity.id is not None def test_to_entity_with_custom_id(self): """Test creating TaskDB with custom ID""" custom_id = str(uuid.uuid4()) entity = TaskMapper.to_entity( task_type="video", model="kling-v1", params={"prompt": "test"}, task_id=custom_id ) assert entity.id == custom_id def test_to_schema(self): """Test converting TaskDB to Task schema""" now = datetime.now().timestamp() entity = TaskDB( id="task_123", type="image", status="success", created_at=now, updated_at=now, model="flux-dev", params={"prompt": "test"}, provider_task_id="provider_123", result={"url": "https://example.com/image.png"}, retry_count=1, max_retries=3, started_at=now, completed_at=now + 10, user_id="user_123", project_id="proj_123" ) schema = TaskMapper.to_schema(entity) assert schema.id == "task_123" assert schema.type == "image" assert schema.status == "success" assert schema.model == "flux-dev" assert schema.provider_task_id == "provider_123" assert schema.result["url"] == "https://example.com/image.png" assert schema.retry_count == 1 assert schema.user_id == "user_123" def test_update_status_to_processing(self): """Test updating task status to processing""" entity = TaskDB( type="image", status="pending", model="flux-dev", params={"prompt": "test"} ) updated = TaskMapper.update_status( entity, status="processing", provider_task_id="provider_123" ) assert updated.status == "processing" assert updated.provider_task_id == "provider_123" assert updated.started_at is not None assert updated.completed_at is None def test_update_status_to_success(self): """Test updating task status to success""" entity = TaskDB( type="image", status="processing", model="flux-dev", params={"prompt": "test"} ) result = {"url": "https://example.com/image.png"} updated = TaskMapper.update_status( entity, status="success", result=result ) assert updated.status == "success" assert updated.result == result assert updated.completed_at is not None def test_update_status_to_failed(self): """Test updating task status to failed""" entity = TaskDB( type="image", status="processing", model="flux-dev", params={"prompt": "test"} ) updated = TaskMapper.update_status( entity, status="failed", error="Generation failed" ) assert updated.status == "failed" assert updated.error == "Generation failed" assert updated.completed_at is not None def test_increment_retry(self): """Test incrementing retry count""" entity = TaskDB( type="image", status="failed", model="flux-dev", params={"prompt": "test"}, retry_count=0 ) updated = TaskMapper.increment_retry(entity) assert updated.retry_count == 1 assert updated.updated_at > entity.created_at def test_multiple_retry_increments(self): """Test multiple retry increments""" entity = TaskDB( type="image", status="failed", model="flux-dev", params={"prompt": "test"}, retry_count=0, max_retries=3 ) # Increment 3 times for i in range(3): entity = TaskMapper.increment_retry(entity) assert entity.retry_count == i + 1 assert entity.retry_count == 3 assert entity.retry_count == entity.max_retries class TestCanvasMetadataMapper: """Test CanvasMetadataMapper conversion correctness""" def test_to_entity_general_canvas(self): """Test creating general canvas metadata entity""" from src.models.schemas import CreateGeneralCanvasRequest request = CreateGeneralCanvasRequest( name="Main Canvas", description="Main project canvas" ) entity = CanvasMetadataMapper.to_entity( schema=request, project_id="proj_123" ) assert entity.project_id == "proj_123" assert entity.canvas_type == "general" assert entity.name == "Main Canvas" assert entity.description == "Main project canvas" assert entity.order_index == 0 assert entity.is_pinned is False assert entity.related_entity_type is None assert entity.related_entity_id is None def test_create_asset_canvas(self): """Test creating asset canvas metadata entity""" entity = CanvasMetadataMapper.create_asset_canvas( project_id="proj_123", asset_id="asset_123", asset_name="Hero" ) assert entity.project_id == "proj_123" assert entity.canvas_type == "asset" assert entity.related_entity_type == "asset" assert entity.related_entity_id == "asset_123" assert entity.name == "Hero Canvas" def test_create_storyboard_canvas(self): """Test creating storyboard canvas metadata entity""" entity = CanvasMetadataMapper.create_storyboard_canvas( project_id="proj_123", storyboard_id="sb_123", storyboard_shot="Shot 1" ) assert entity.project_id == "proj_123" assert entity.canvas_type == "storyboard" assert entity.related_entity_type == "storyboard" assert entity.related_entity_id == "sb_123" assert entity.name == "Shot 1 Canvas" def test_to_schema(self): """Test converting CanvasMetadataDB to schema""" now = datetime.now().timestamp() entity = CanvasMetadataDB( id="canvas_123", project_id="proj_123", canvas_type="general", name="Main Canvas", description="Main project canvas", order_index=0, is_pinned=True, tags=["main", "primary"], node_count=5, last_accessed_at=now, access_count=10, created_at=now, updated_at=now ) schema = CanvasMetadataMapper.to_schema(entity) assert schema.id == "canvas_123" assert schema.project_id == "proj_123" assert schema.canvas_type == "general" assert schema.name == "Main Canvas" assert schema.is_pinned is True assert len(schema.tags) == 2 assert schema.node_count == 5 assert schema.access_count == 10 def test_update_entity(self): """Test updating canvas metadata""" from src.models.schemas import UpdateCanvasMetadataRequest entity = CanvasMetadataDB( project_id="proj_123", canvas_type="general", name="Original Name", description="Original Description", order_index=0, is_pinned=False, tags=["old"] ) update_request = UpdateCanvasMetadataRequest( name="Updated Name", isPinned=True, tags=["new", "updated"] ) updated = CanvasMetadataMapper.update_entity(entity, update_request) assert updated.name == "Updated Name" assert updated.is_pinned is True assert updated.tags == ["new", "updated"] assert updated.description == "Original Description" # unchanged def test_update_access(self): """Test updating canvas access tracking""" entity = CanvasMetadataDB( project_id="proj_123", canvas_type="general", name="Main Canvas", order_index=0, is_pinned=False, access_count=5 ) initial_access_count = entity.access_count updated = CanvasMetadataMapper.update_access(entity) assert updated.access_count == initial_access_count + 1 assert updated.last_accessed_at is not None if __name__ == "__main__": pytest.main([__file__, "-v"])