feat: extract workflow output save phase 3

This commit is contained in:
2026-04-07 09:50:58 +02:00
parent 9c93ecef49
commit 160c198bb3
5 changed files with 232 additions and 81 deletions
@@ -5,7 +5,7 @@ import uuid
from pathlib import Path
import pytest
from sqlalchemy import create_engine, text
from sqlalchemy import create_engine, select, text
from sqlalchemy.orm import Session
from app.database import Base
@@ -17,6 +17,7 @@ from app.domains.products.models import CadFile, Product
from app.domains.rendering.models import OutputType, RenderTemplate
from app.domains.rendering.workflow_runtime_services import (
auto_populate_materials_for_cad,
persist_order_line_output,
resolve_cad_bbox,
prepare_order_line_render_context,
resolve_order_line_material_map,
@@ -431,3 +432,114 @@ def test_extract_metadata_bbox_wrappers_delegate_to_runtime_services(monkeypatch
assert _bbox_from_step_cadquery("/tmp/a.step") == {
"dimensions_mm": {"x": 4.0, "y": 5.0, "z": 6.0}
}
def test_persist_order_line_output_saves_success_and_creates_media_asset(sync_session, tmp_path, monkeypatch):
from app.config import settings
upload_dir = tmp_path / "uploads"
monkeypatch.setattr(settings, "upload_dir", str(upload_dir))
line = _seed_order_line_graph(sync_session, tmp_path)
output_path = upload_dir / "renders" / str(line.id) / "bearing.png"
output_path.parent.mkdir(parents=True, exist_ok=True)
output_path.write_text("PNGDATA", encoding="utf-8")
result = persist_order_line_output(
sync_session,
line,
success=True,
output_path=str(output_path),
render_log={
"renderer": "blender",
"engine": "cycles",
"engine_used": "cycles",
"samples": 64,
"total_duration_s": 1.23,
},
)
sync_session.refresh(line)
asset = sync_session.execute(
select(MediaAsset).where(MediaAsset.order_line_id == line.id)
).scalar_one_or_none()
assert result.status == "completed"
assert result.result_path == str(output_path)
assert result.storage_key == f"renders/{line.id}/bearing.png"
assert result.asset_type == MediaAssetType.still
assert line.render_status == "completed"
assert line.result_path == str(output_path)
assert asset is not None
assert asset.storage_key == f"renders/{line.id}/bearing.png"
assert asset.asset_type == MediaAssetType.still
assert asset.file_size_bytes == output_path.stat().st_size
assert asset.mime_type == "image/png"
assert asset.render_config == {
"renderer": "blender",
"engine": "cycles",
"engine_used": "cycles",
"samples": 64,
"total_duration_s": 1.23,
}
def test_persist_order_line_output_reuses_existing_asset(sync_session, tmp_path, monkeypatch):
from app.config import settings
upload_dir = tmp_path / "uploads"
monkeypatch.setattr(settings, "upload_dir", str(upload_dir))
line = _seed_order_line_graph(sync_session, tmp_path)
output_path = upload_dir / "renders" / str(line.id) / "bearing.mp4"
output_path.parent.mkdir(parents=True, exist_ok=True)
output_path.write_text("MP4DATA", encoding="utf-8")
existing = MediaAsset(
id=uuid.uuid4(),
order_line_id=line.id,
product_id=line.product_id,
asset_type=MediaAssetType.turntable,
storage_key=f"renders/{line.id}/bearing.mp4",
)
sync_session.add(existing)
sync_session.commit()
result = persist_order_line_output(
sync_session,
line,
success=True,
output_path=str(output_path),
render_log={"renderer": "blender"},
)
assets = sync_session.execute(
select(MediaAsset).where(MediaAsset.storage_key == f"renders/{line.id}/bearing.mp4")
).scalars().all()
assert result.asset_id == str(existing.id)
assert result.asset_type == MediaAssetType.turntable
assert len(assets) == 1
def test_persist_order_line_output_marks_failure_without_result_path(sync_session, tmp_path):
line = _seed_order_line_graph(sync_session, tmp_path)
result = persist_order_line_output(
sync_session,
line,
success=False,
output_path=str(tmp_path / "renders" / "failed.png"),
render_log={"error": "boom"},
)
sync_session.refresh(line)
assets = sync_session.execute(
select(MediaAsset).where(MediaAsset.order_line_id == line.id)
).scalars().all()
assert result.status == "failed"
assert result.result_path is None
assert result.asset_id is None
assert line.render_status == "failed"
assert line.result_path is None
assert line.render_log == {"error": "boom"}
assert assets == []