Test: quota NaN chiede il capoturno, migrazione 022 conserva le righe

Refs #125

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-28 22:07:18 +01:00
parent 2f0a5aaf1e
commit 17ea010f7e
2 changed files with 64 additions and 2 deletions
+52
View File
@@ -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()
+12 -2
View File
@@ -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(