fix(measure): riepilogo fine task filtrato per lotto/seriale/operatore, pagina oltre 500 (#30)

task_complete(): GET /api/measurements ora porta lot_number/serial_number
(sessione) + measured_by (utente corrente), invece del solo version_id -
una seconda produzione sulla stessa versione non pesca più le misure di
altre stazioni/giorni. Risposte oltre 500 misure vengono cicliche per
pagina invece di troncate al primo per_page=500.
production_run_id sarebbe il filtro più preciso ma GET /api/measurements
non lo accetta (lotto A3) e task_execute.html non lo passa in query
(lotto B1): MANCA, annotato nel codice.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-21 22:49:59 +02:00
parent e20299548f
commit 1bb00b14f2
2 changed files with 115 additions and 20 deletions
@@ -264,6 +264,77 @@ class TestTaskComplete:
assert resp.status_code == 200
assert b"productionClock(" in resp.data
def test_measurements_filtered_by_lot_serial_and_operator(
self, logged_in_client, mock_api_client,
):
"""#30: a second production on the same version must not pull in every
other station's/day's readings - only this lot/serial and operator."""
with logged_in_client.session_transaction() as sess:
sess["lot_number"] = "L1"
sess["serial_number"] = "S1"
mock_api_client.get.side_effect = [
{"id": 1, "code": "REC-001", "name": "Test Recipe", "version": 1},
[{"id": 1, "title": "Task 1", "order_index": 0, "subtasks": []}],
{"items": [], "pages": 1},
]
resp = logged_in_client.get("/measure/complete/1?version_id=5")
assert resp.status_code == 200
endpoint, kwargs = mock_api_client.get.call_args_list[2]
assert endpoint[0] == "/api/measurements"
params = kwargs["params"]
assert params["version_id"] == "5"
assert params["lot_number"] == "L1"
assert params["serial_number"] == "S1"
assert params["measured_by"] == 1 # session["user_id"] from the fixture
def test_measurements_without_lot_or_serial_still_scope_to_the_operator(
self, logged_in_client, mock_api_client,
):
"""No lot/serial in session (recipe does not require them): the
operator filter alone still narrows the summary to this run."""
mock_api_client.get.side_effect = [
{"id": 1, "code": "REC-001", "name": "Test Recipe", "version": 1},
[{"id": 1, "title": "Task 1", "order_index": 0, "subtasks": []}],
{"items": [], "pages": 1},
]
resp = logged_in_client.get("/measure/complete/1?version_id=5")
assert resp.status_code == 200
endpoint, kwargs = mock_api_client.get.call_args_list[2]
params = kwargs["params"]
assert "lot_number" not in params
assert "serial_number" not in params
assert params["measured_by"] == 1
def test_measurements_beyond_500_are_paged_not_truncated(
self, logged_in_client, mock_api_client,
):
"""#30: a cycle over pages instead of a single capped per_page=500 call."""
subtask = {"id": 1, "nominal": 5.0, "unit": "mm"}
page1 = {
"items": [{"id": i, "subtask_id": 1, "value": 5.0} for i in range(500)],
"pages": 2,
}
page2 = {"items": [{"id": 500, "subtask_id": 1, "value": 5.0}], "pages": 2}
mock_api_client.get.side_effect = [
{"id": 1, "code": "REC-001", "name": "Test Recipe", "version": 1},
[{"id": 1, "title": "Task 1", "order_index": 0, "subtasks": [subtask]}],
page1,
page2,
]
resp = logged_in_client.get("/measure/complete/1?version_id=5")
assert resp.status_code == 200
assert mock_api_client.get.call_count == 4
first_page_call = mock_api_client.get.call_args_list[2]
second_page_call = mock_api_client.get.call_args_list[3]
assert first_page_call.kwargs["params"]["page"] == 1
assert second_page_call.kwargs["params"]["page"] == 2
class TestOutOfTolerance:
"""Proxies for point 5: the capoturno's answer has to reach the measurement."""