221 lines
7.3 KiB
Python
221 lines
7.3 KiB
Python
from __future__ import annotations
|
|
|
|
import uuid
|
|
from contextlib import contextmanager
|
|
from pathlib import Path
|
|
from types import SimpleNamespace
|
|
|
|
|
|
def _patch_render_thumbnail_dependencies(monkeypatch, tmp_path: Path):
|
|
from app.domains.pipeline.tasks.render_thumbnail import render_graph_thumbnail, render_step_thumbnail
|
|
|
|
step_path = tmp_path / "bearing.step"
|
|
step_path.write_text("STEP", encoding="utf-8")
|
|
usd_path = tmp_path / "bearing.usdc"
|
|
usd_path.write_text("USD", encoding="utf-8")
|
|
|
|
cad_file = SimpleNamespace(
|
|
id=uuid.uuid4(),
|
|
stored_path=str(step_path),
|
|
step_file_hash=None,
|
|
mesh_attributes={},
|
|
tenant_id=uuid.uuid4(),
|
|
)
|
|
|
|
class _FakeSession:
|
|
def get(self, _model, _object_id):
|
|
return cad_file
|
|
|
|
def commit(self):
|
|
return None
|
|
|
|
@contextmanager
|
|
def _fake_pipeline_session(_tenant_id=None):
|
|
yield _FakeSession()
|
|
|
|
@contextmanager
|
|
def _fake_sample_cap():
|
|
yield
|
|
|
|
queued_glb_exports: list[str] = []
|
|
workflow_updates: list[tuple[str, str, str | None, str | None]] = []
|
|
postprocess_calls: list[str] = []
|
|
regenerate_calls: list[tuple[tuple, dict]] = []
|
|
|
|
monkeypatch.setattr(
|
|
"app.domains.pipeline.tasks.render_thumbnail._pipeline_session",
|
|
_fake_pipeline_session,
|
|
)
|
|
monkeypatch.setattr(
|
|
"app.domains.pipeline.tasks.render_thumbnail._capped_thumbnail_samples",
|
|
_fake_sample_cap,
|
|
)
|
|
monkeypatch.setattr(
|
|
"app.core.tenant_context.resolve_tenant_id_for_cad",
|
|
lambda cad_file_id: "tenant-1",
|
|
)
|
|
monkeypatch.setattr(
|
|
"app.domains.products.cache_service.compute_step_hash",
|
|
lambda _path: "hash-123",
|
|
)
|
|
monkeypatch.setattr(
|
|
"app.services.step_processor.regenerate_cad_thumbnail",
|
|
lambda *args, **kwargs: (regenerate_calls.append((args, kwargs)), True)[1],
|
|
)
|
|
monkeypatch.setattr(
|
|
"app.domains.pipeline.tasks.render_thumbnail._resolve_thumbnail_render_context",
|
|
lambda _session, _cad: {
|
|
"material_library_path": "/tmp/materials.blend",
|
|
"material_map": {"part-a": "HARTOMAT_010101_Steel-Bare"},
|
|
"part_names_ordered": ["part-a", "part-b"],
|
|
"usd_path": usd_path,
|
|
},
|
|
)
|
|
monkeypatch.setattr(
|
|
"app.services.step_processor.extract_mesh_edge_data",
|
|
lambda _step_path: {"sharp_edge_pairs": []},
|
|
)
|
|
monkeypatch.setattr(
|
|
"app.domains.rendering.workflow_runtime_services.resolve_cad_bbox",
|
|
lambda step_path, glb_path=None: (
|
|
postprocess_calls.append("bbox"),
|
|
SimpleNamespace(
|
|
bbox_data={
|
|
"dimensions_mm": {"x": 1.0, "y": 2.0, "z": 3.0},
|
|
"bbox_center_mm": {"x": 0.5, "y": 1.0, "z": 1.5},
|
|
}
|
|
),
|
|
)[1],
|
|
)
|
|
monkeypatch.setattr(
|
|
"app.domains.pipeline.tasks.extract_metadata._auto_populate_materials_for_cad",
|
|
lambda cad_file_id, tenant_id=None: postprocess_calls.append("auto_populate"),
|
|
)
|
|
monkeypatch.setattr(
|
|
"app.core.websocket.publish_event_sync",
|
|
lambda tenant_id, payload: postprocess_calls.append("websocket"),
|
|
)
|
|
monkeypatch.setattr(
|
|
"app.domains.pipeline.tasks.export_glb.generate_gltf_geometry_task.delay",
|
|
lambda cad_file_id: queued_glb_exports.append(cad_file_id),
|
|
)
|
|
monkeypatch.setattr(
|
|
"app.domains.rendering.tasks._update_workflow_run_status",
|
|
lambda order_line_id, status, error=None, *, workflow_run_id=None, workflow_node_id=None: workflow_updates.append(
|
|
(order_line_id, status, workflow_run_id, workflow_node_id)
|
|
),
|
|
)
|
|
|
|
return (
|
|
render_step_thumbnail,
|
|
render_graph_thumbnail,
|
|
queued_glb_exports,
|
|
workflow_updates,
|
|
postprocess_calls,
|
|
regenerate_calls,
|
|
)
|
|
|
|
|
|
def test_render_step_thumbnail_skips_legacy_glb_follow_up_for_graph_runs(monkeypatch, tmp_path):
|
|
render_step_thumbnail, _render_graph_thumbnail, queued_glb_exports, workflow_updates, postprocess_calls, regenerate_calls = _patch_render_thumbnail_dependencies(
|
|
monkeypatch,
|
|
tmp_path,
|
|
)
|
|
|
|
render_step_thumbnail.run(
|
|
"cad-123",
|
|
workflow_run_id="run-123",
|
|
workflow_node_id="save-thumb",
|
|
renderer="threejs",
|
|
width=512,
|
|
height=512,
|
|
transparent_bg=True,
|
|
)
|
|
|
|
assert queued_glb_exports == []
|
|
assert workflow_updates == [("cad-123", "completed", "run-123", "save-thumb")]
|
|
assert postprocess_calls == ["bbox", "auto_populate", "websocket"]
|
|
assert regenerate_calls == [(
|
|
("cad-123",),
|
|
{
|
|
"part_colors": {},
|
|
"renderer": "threejs",
|
|
"render_engine": None,
|
|
"samples": None,
|
|
"width": 512,
|
|
"height": 512,
|
|
"transparent_bg": True,
|
|
"material_library_path": "/tmp/materials.blend",
|
|
"material_map": {"part-a": "HARTOMAT_010101_Steel-Bare"},
|
|
"part_names_ordered": ["part-a", "part-b"],
|
|
"usd_path": tmp_path / "bearing.usdc",
|
|
},
|
|
)]
|
|
|
|
|
|
def test_render_step_thumbnail_keeps_legacy_glb_follow_up_without_workflow_run(monkeypatch, tmp_path):
|
|
render_step_thumbnail, _render_graph_thumbnail, queued_glb_exports, workflow_updates, postprocess_calls, _regenerate_calls = _patch_render_thumbnail_dependencies(
|
|
monkeypatch,
|
|
tmp_path,
|
|
)
|
|
|
|
render_step_thumbnail.run("cad-123")
|
|
|
|
assert queued_glb_exports == ["cad-123"]
|
|
assert workflow_updates == [("cad-123", "completed", None, None)]
|
|
assert postprocess_calls == ["bbox", "auto_populate", "websocket"]
|
|
|
|
|
|
def test_render_graph_thumbnail_skips_legacy_postprocess_and_glb_follow_up(monkeypatch, tmp_path):
|
|
_render_step_thumbnail, render_graph_thumbnail, queued_glb_exports, workflow_updates, postprocess_calls, _regenerate_calls = _patch_render_thumbnail_dependencies(
|
|
monkeypatch,
|
|
tmp_path,
|
|
)
|
|
|
|
render_graph_thumbnail.run(
|
|
"cad-123",
|
|
workflow_run_id="run-123",
|
|
workflow_node_id="save-thumb",
|
|
renderer="threejs",
|
|
width=512,
|
|
height=512,
|
|
transparent_bg=True,
|
|
)
|
|
|
|
assert queued_glb_exports == []
|
|
assert workflow_updates == [("cad-123", "completed", "run-123", "save-thumb")]
|
|
assert postprocess_calls == []
|
|
|
|
|
|
def test_regenerate_thumbnail_skips_retry_for_missing_cad_resource(monkeypatch):
|
|
from app.domains.pipeline.tasks.render_thumbnail import regenerate_thumbnail
|
|
from app.services.step_processor import MissingCadResourceError
|
|
|
|
retry_calls: list[tuple[tuple, dict]] = []
|
|
|
|
monkeypatch.setattr("app.core.task_logs.log_task_event", lambda *args, **kwargs: None)
|
|
monkeypatch.setattr(
|
|
"app.core.tenant_context.resolve_tenant_id_for_cad",
|
|
lambda cad_file_id: "tenant-1",
|
|
)
|
|
monkeypatch.setattr(
|
|
"app.services.step_processor.regenerate_cad_thumbnail",
|
|
lambda *args, **kwargs: (_ for _ in ()).throw(MissingCadResourceError("CAD file not found: cad-123")),
|
|
)
|
|
|
|
monkeypatch.setattr(
|
|
regenerate_thumbnail,
|
|
"retry",
|
|
lambda *args, **kwargs: retry_calls.append((args, kwargs)),
|
|
)
|
|
|
|
regenerate_thumbnail.run(
|
|
"cad-123",
|
|
{},
|
|
renderer="blender",
|
|
width=512,
|
|
height=512,
|
|
)
|
|
|
|
assert retry_calls == []
|