"""Teste PRD 5.19 US-003 + US-008 — worker respecta flag `held`. US-003: claim_one sare peste randurile `queued AND held=1` (tinute manual). US-008: expire_held expira randurile tinute mai vechi de `held_retention_days` (default 90) -> status 'error', rar_error='TINUT_EXPIRAT', purge_after DIRECT la momentul expirarii (NU +30z retentie blocati -> nu 120z total), apoi purge_expired le sterge (PII purjat). TDD: testele se scriu INAINTE de modificarea worker-ului. """ from __future__ import annotations import json import os import tempfile import pytest # --- Fixture DB (pattern din test_worker_active_gate.py) --- @pytest.fixture() def env(monkeypatch): tmp = tempfile.mkdtemp() monkeypatch.setenv("AUTOPASS_DB_PATH", os.path.join(tmp, "t.db")) from app.config import get_settings get_settings.cache_clear() from app.db import get_connection, init_db init_db() conn = get_connection() yield conn, get_settings() conn.close() get_settings.cache_clear() # --- Helpers --- _CONTENT = { "vin": "WVWZZZ1KZAW000123", "nr_inmatriculare": "B999TST", "data_prestatie": "2026-06-15", "odometru_final": "123456", "prestatii": [{"cod_prestatie": "OE-1"}], "sistem_reparat": "null", } def _insert(conn, account_id=None, status="queued", held=0, content=None, created_at=None): content = content or _CONTENT cur = conn.execute( "INSERT INTO submissions (idempotency_key, status, payload_json, account_id, held) " "VALUES (?, ?, ?, ?, ?)", (f"key-{os.urandom(4).hex()}", status, json.dumps(content), account_id, held), ) sid = int(cur.lastrowid) if created_at is not None: conn.execute("UPDATE submissions SET created_at=? WHERE id=?", (created_at, sid)) return sid def _row(conn, sid): return conn.execute("SELECT * FROM submissions WHERE id=?", (sid,)).fetchone() def _row_status(conn, sid): r = _row(conn, sid) return r["status"] if r else None # --- US-003: claim_one sare peste randurile tinute --- def test_claim_sare_rand_tinut(env): """queued, held=1, cont activ, send on -> claim_one intoarce None; ramane queued.""" from app.accounts import create_account from app.worker.__main__ import claim_one conn, _ = env acct_id = create_account(conn, "Service Tinut", active=True) sid = _insert(conn, account_id=acct_id, held=1) result = claim_one(conn) assert result is None, "claim_one trebuia sa sara peste randul tinut (held=1)" assert _row_status(conn, sid) == "queued", "randul tinut trebuia sa ramana queued" def test_claim_ia_rand_neted(env): """Acelasi rand cu held=0 -> claim_one il ia si il marcheaza sending.""" from app.accounts import create_account from app.worker.__main__ import claim_one conn, _ = env acct_id = create_account(conn, "Service Liber", active=True) sid = _insert(conn, account_id=acct_id, held=0) result = claim_one(conn) assert result is not None, "claim_one trebuia sa ridice randul held=0" assert result["id"] == sid assert _row_status(conn, sid) == "sending" def test_eliberarea_deblocheaza_claim(env): """held=1 -> claim_one None; dupa held=0 -> claim_one il ridica.""" from app.accounts import create_account from app.worker.__main__ import claim_one conn, _ = env acct_id = create_account(conn, "Service Eliberat", active=True) sid = _insert(conn, account_id=acct_id, held=1) assert claim_one(conn) is None assert _row_status(conn, sid) == "queued" conn.execute("UPDATE submissions SET held=0 WHERE id=?", (sid,)) result = claim_one(conn) assert result is not None and result["id"] == sid assert _row_status(conn, sid) == "sending" # --- US-008: expirare randuri tinute --- def test_expire_held_expira_rand_vechi(env): """queued, held=1, created_at vechi (>90z) -> error + TINUT_EXPIRAT + purge_after ~now.""" from app.accounts import create_account from app.worker.__main__ import expire_held conn, settings = env acct_id = create_account(conn, "Service Vechi", active=True) sid = _insert(conn, account_id=acct_id, held=1, created_at="datetime('now','-100 days')") # created_at trebuie sa fie o valoare literala, nu expresie SQL text -> setam explicit. conn.execute("UPDATE submissions SET created_at=datetime('now','-100 days') WHERE id=?", (sid,)) n = expire_held(conn, settings) assert n == 1, "expire_held trebuia sa expire exact 1 rand" row = _row(conn, sid) assert row["status"] == "error" assert row["rar_error"] == "TINUT_EXPIRAT" assert row["purge_after"] is not None # purge_after ~ now (NU +30z): verificam ca e <= acum + o mica marja (sub 1 zi). aproape_acum = conn.execute( "SELECT purge_after <= datetime('now', '+1 day') AS ok FROM submissions WHERE id=?", (sid,), ).fetchone()["ok"] assert aproape_acum == 1, "purge_after trebuia setat ~now, nu +30z" assert row["sending_since"] is None def test_expire_held_ignora_rand_recent(env): """queued, held=1 recent (created_at now) -> neatins de expire_held.""" from app.accounts import create_account from app.worker.__main__ import expire_held conn, settings = env acct_id = create_account(conn, "Service Recent", active=True) sid = _insert(conn, account_id=acct_id, held=1) n = expire_held(conn, settings) assert n == 0 assert _row_status(conn, sid) == "queued" def test_expire_held_ignora_rand_neted_vechi(env): """queued, held=0, vechi -> NU e tinut -> neatins de expire_held.""" from app.accounts import create_account from app.worker.__main__ import expire_held conn, settings = env acct_id = create_account(conn, "Service NetedVechi", active=True) sid = _insert(conn, account_id=acct_id, held=0) conn.execute("UPDATE submissions SET created_at=datetime('now','-100 days') WHERE id=?", (sid,)) n = expire_held(conn, settings) assert n == 0 assert _row_status(conn, sid) == "queued" def test_expire_held_apoi_purge_sterge_randul(env): """Lant complet: expire_held (purge_after ~now) -> purge_expired sterge randul (PII purjat).""" from app.accounts import create_account from app.worker.__main__ import expire_held, purge_expired conn, settings = env acct_id = create_account(conn, "Service Purjat", active=True) sid = _insert(conn, account_id=acct_id, held=1) conn.execute("UPDATE submissions SET created_at=datetime('now','-100 days') WHERE id=?", (sid,)) assert expire_held(conn, settings) == 1 # purge_after=datetime('now') (moment expirarii). La URMATORUL ciclu de purjare # (o data pe ora) purge_after < now -> se sterge. Simulam trecerea timpului # (>= 1s) verificand ca purge_after e deja in trecutul acelui ciclu ulterior. assert conn.execute( "SELECT purge_after < datetime('now','+1 second') AS ok FROM submissions WHERE id=?", (sid,), ).fetchone()["ok"] == 1 conn.execute( "UPDATE submissions SET purge_after=datetime('now','-1 second') WHERE id=?", (sid,) ) stats = purge_expired(conn) assert stats["submissions_purged"] >= 1 assert _row(conn, sid) is None, "randul expirat trebuia sters de purge_expired la ciclul ulterior"