Initial commit: Pixel AI comic/video creation platform
- FastAPI backend with SQLModel, Alembic migrations, AgentScope agents - Next.js 15 frontend with React 19, Tailwind, Zustand, React Flow - Multi-provider AI system (DashScope, Kling, MiniMax, Volcengine, OpenAI, etc.) - All HTTP clients migrated from sync requests to async httpx - Admin-managed API keys via environment variables - SSRF vulnerability fixed in ensure_url()
This commit is contained in:
775
backend/tests/test_mappers_unit.py
Normal file
775
backend/tests/test_mappers_unit.py
Normal file
@@ -0,0 +1,775 @@
|
||||
"""
|
||||
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"])
|
||||
Reference in New Issue
Block a user