fix(api): allow MeasurementTec to list measurements for task_complete

GET /api/measurements required Metrologist, but the operator workflow
calls it from task_complete.html to render the post-recipe riepilogo —
silently empty for every non-Metrologist user. Open to any authenticated
user; payload carries no PII beyond numeric values + recipe ref.

Regression test added.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-04 10:42:16 +02:00
parent e4eb4cd932
commit 85a00dea1b
2 changed files with 46 additions and 2 deletions
+9 -2
View File
@@ -97,10 +97,17 @@ async def get_measurements(
pass_fail: str | None = Query(None, pattern="^(pass|warning|fail)$"), pass_fail: str | None = Query(None, pattern="^(pass|warning|fail)$"),
page: int = Query(1, ge=1), page: int = Query(1, ge=1),
per_page: int = Query(50, ge=1, le=500), per_page: int = Query(50, ge=1, le=500),
user: User = Depends(require_metrologist), user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db), db: AsyncSession = Depends(get_db),
): ):
"""Query measurements with filters and pagination.""" """Query measurements with filters and pagination.
Available to any authenticated user. The MeasurementTec workflow needs
this endpoint to render `task_complete.html` after running through a
recipe; the Metrologist dashboard uses the same endpoint with broader
filters. Measurements carry no PII beyond numeric values + a recipe
reference, so role-gating beyond authentication isn't justified.
"""
# Build filter conditions # Build filter conditions
filters = [] filters = []
if recipe_id is not None: if recipe_id is not None:
+37
View File
@@ -211,6 +211,43 @@ class TestListMeasurements:
assert "total" in data assert "total" in data
assert data["total"] >= 3 assert data["total"] >= 3
async def test_measurement_tec_can_list_measurements(
self,
client: AsyncClient,
measurement_tec_user: User,
db_session: AsyncSession,
):
"""Regression: MeasurementTec must be able to list measurements.
The Flask client renders task_complete.html by calling GET
/api/measurements?version_id=X right after the operator finishes a
recipe. The endpoint used to require Metrologist, which silently
produced an empty riepilogo for every operator without that role.
"""
recipe = await create_test_recipe(db_session, measurement_tec_user.id)
subtask_id, version_id = await _get_subtask_and_version(
client, measurement_tec_user, recipe.id
)
for val in [9.8, 10.0, 10.2]:
await client.post(
"/api/measurements/",
headers=auth_headers(measurement_tec_user),
json={
"subtask_id": subtask_id,
"version_id": version_id,
"value": val,
},
)
resp = await client.get(
f"/api/measurements/?version_id={version_id}",
headers=auth_headers(measurement_tec_user),
)
assert resp.status_code == 200, resp.text
data = resp.json()
assert data["total"] >= 3
async def test_measurement_by_recipe_filter( async def test_measurement_by_recipe_filter(
self, self,
client: AsyncClient, client: AsyncClient,