feat: add workflow output comparison tooling

This commit is contained in:
2026-04-07 11:45:28 +02:00
parent f43f1e7420
commit ffcaef4659
6 changed files with 368 additions and 1 deletions
@@ -4,6 +4,7 @@ import uuid
from pathlib import Path
import pytest
from PIL import Image
from sqlalchemy import select
from sqlalchemy.orm import selectinload
@@ -545,3 +546,90 @@ async def test_workflow_dispatch_endpoint_returns_workflow_run_with_node_results
assert node_results["template"]["status"] == "completed"
assert node_results["template"]["output"]["use_materials"] is False
assert node_results["output"]["status"] == "skipped"
@pytest.mark.asyncio
async def test_workflow_run_comparison_endpoint_reports_identical_shadow_output(
client,
db,
admin_user,
auth_headers,
tmp_path,
):
order_line = await _seed_renderable_order_line(db, admin_user, tmp_path)
workflow_run = WorkflowRun(
order_line_id=order_line.id,
execution_mode="shadow",
status="completed",
)
db.add(workflow_run)
await db.flush()
render_dir = tmp_path / "comparison" / str(order_line.id)
render_dir.mkdir(parents=True, exist_ok=True)
authoritative_path = render_dir / "authoritative.png"
shadow_path = render_dir / f"authoritative_shadow-{str(workflow_run.id)[:8]}.png"
Image.new("RGBA", (8, 8), (0, 128, 255, 255)).save(authoritative_path)
Image.new("RGBA", (8, 8), (0, 128, 255, 255)).save(shadow_path)
order_line.result_path = str(authoritative_path)
order_line.render_status = "completed"
await db.commit()
response = await client.get(
f"/api/workflows/runs/{workflow_run.id}/comparison",
headers=auth_headers,
)
assert response.status_code == 200
body = response.json()
assert body["workflow_run_id"] == str(workflow_run.id)
assert body["execution_mode"] == "shadow"
assert body["status"] == "matched"
assert body["exact_match"] is True
assert body["dimensions_match"] is True
assert body["mean_pixel_delta"] == 0.0
assert body["authoritative_output"]["path"] == str(authoritative_path)
assert body["observer_output"]["path"] == str(shadow_path)
assert body["authoritative_output"]["image_width"] == 8
assert body["observer_output"]["image_height"] == 8
@pytest.mark.asyncio
async def test_workflow_run_comparison_endpoint_reports_missing_shadow_output(
client,
db,
admin_user,
auth_headers,
tmp_path,
):
order_line = await _seed_renderable_order_line(db, admin_user, tmp_path)
workflow_run = WorkflowRun(
order_line_id=order_line.id,
execution_mode="shadow",
status="completed",
)
db.add(workflow_run)
await db.flush()
render_dir = tmp_path / "comparison-missing" / str(order_line.id)
render_dir.mkdir(parents=True, exist_ok=True)
authoritative_path = render_dir / "authoritative.png"
Image.new("RGBA", (4, 4), (255, 64, 64, 255)).save(authoritative_path)
order_line.result_path = str(authoritative_path)
order_line.render_status = "completed"
await db.commit()
response = await client.get(
f"/api/workflows/runs/{workflow_run.id}/comparison",
headers=auth_headers,
)
assert response.status_code == 200
body = response.json()
assert body["status"] == "missing_observer"
assert body["exact_match"] is None
assert body["observer_output"]["exists"] is False
assert body["authoritative_output"]["exists"] is True