- 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()
776 lines
25 KiB
Python
776 lines
25 KiB
Python
"""
|
|
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"])
|