From 17ea010f7ef61d0cd94e04c63ac79fcff234259e Mon Sep 17 00:00:00 2001 From: "Adriano (daitarn)" Date: Wed, 28 Oct 2026 22:07:18 +0100 Subject: [PATCH] Test: quota NaN chiede il capoturno, migrazione 022 conserva le righe Refs #125 Co-Authored-By: Claude Sonnet 5.5 --- src/backend/tests/test_migrations.py | 52 ++++++++++++++++++++++++ src/backend/tests/test_vision_execute.py | 14 ++++++- 2 files changed, 64 insertions(+), 2 deletions(-) diff --git a/src/backend/tests/test_migrations.py b/src/backend/tests/test_migrations.py index 57b8b30..e15acf7 100644 --- a/src/backend/tests/test_migrations.py +++ b/src/backend/tests/test_migrations.py @@ -379,3 +379,55 @@ class TestMeasurementValueNullableMigration: assert value_nullable() is False command.upgrade(cfg, "head") # e di nuovo su assert value_nullable() is True + + def test_022_keeps_rows(self, sqlite_database_url): + """#125: la ricostruzione della tabella conserva righe, somma dei value e indici.""" + import sqlite3 + + db_path, _ = sqlite_database_url + cfg = _alembic_config() + command.upgrade(cfg, "021_task_acquisition") + + def indexes(con): + return sorted(r[0] for r in con.execute( + "select name from sqlite_master where type='index' and tbl_name='measurements'" + )) + + con = sqlite3.connect(db_path) + # Colonne NOT NULL senza default riempite per tipo (stessa idea di mig_check.py) + vals = {} + for _cid, name, typ, notnull, dflt, pk in con.execute("pragma table_info(measurements)").fetchall(): + if pk or not notnull or dflt is not None: + continue + t = typ.upper() + vals[name] = ( + 1 if "INT" in t + else 1.5 if ("DEC" in t or "FLOAT" in t or "NUM" in t) + else "2026-01-01 00:00:00" if ("DATE" in t or "TIME" in t) + else "x" + ) + vals["pass_fail"] = "pass" + vals["value"] = 12.5 + sql = f"insert into measurements ({','.join(vals)}) values ({','.join('?' * len(vals))})" + for _ in range(3): + con.execute(sql, list(vals.values())) + con.commit() + before_idx = indexes(con) + before = con.execute("select count(*), sum(value) from measurements").fetchone() + con.close() + assert before == (3, 37.5) + + command.upgrade(cfg, "head") + + con = sqlite3.connect(db_path) + try: + assert con.execute("select count(*), sum(value) from measurements").fetchone() == before + assert indexes(con) == before_idx + assert con.execute("pragma integrity_check").fetchone()[0] == "ok" + vals["value"] = None + vals["pass_fail"] = "fail" + con.execute(sql, list(vals.values())) # una riga NaN (value NULL) ora e' accettata + con.commit() + assert con.execute("select count(*) from measurements where value is null").fetchone()[0] == 1 + finally: + con.close() diff --git a/src/backend/tests/test_vision_execute.py b/src/backend/tests/test_vision_execute.py index 49b3d6b..6d3be04 100644 --- a/src/backend/tests/test_vision_execute.py +++ b/src/backend/tests/test_vision_execute.py @@ -216,10 +216,11 @@ async def test_a_non_finite_output_is_saved_as_nan( @pytest.mark.asyncio -async def test_a_nan_quote_still_blocks_the_next_acquisition( +async def test_a_nan_quote_needs_supervisor_authorisation( client, db_session, measurement_tec_user, worker_says, ): - """#118: la quota NaN e' un fail come gli altri, il blocco di tolleranza resta.""" + """#125: la quota NaN e' un fail come gli altri e chiede il capoturno (pending_authorisation); + la prossima acquisizione non e' bloccata.""" recipe = await create_test_recipe(db_session, measurement_tec_user.id) version, task, first, second = await two_vision_quotes(db_session, recipe.id) worker_says({"t1.radius": GOOD}) @@ -230,6 +231,15 @@ async def test_a_nan_quote_still_blocks_the_next_acquisition( ) assert blocking is not None and blocking.subtask_id == first.id and blocking.value is None + # #125: una seconda acquisizione sullo stesso task risponde 200, non e' bloccata + again = await client.post( + "/api/vision/execute", + headers=auth_headers(measurement_tec_user), + files={"image": ("p.png", an_image(), "image/png")}, + data={"task_id": str(task.id)}, + ) + assert again.status_code == 200, again.text + @pytest.mark.asyncio async def test_check_lists_a_missing_quote_as_nan(