Files
HartOMat/backend/tests/test_render_thumbnail_task.py
T

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 == []