Comutator accounts.auto_send_enabled per cont: Auto OFF (default) tine randurile
la ingestie (submissions.held=1), worker-ul (claim_one AND held=0) le sare pana la
eliberare umana (per rand/bulk/auto-release OFF->ON). Snapshot held prin chokepoint
unic held_for_account pe toate caile de ingestie (API, import, reresolve, reactivare).
- schema/migrare: coloana held + index partial idx_submissions_held; auto_send_enabled
- API: echo onest held+motiv (US-010), ruta /prezentari/{id}/trimite-acum
- web: toggle header, modal confirmare tipata, buton Trimite per rand + Trimite toate,
banner coada tinuta imbatranita (L.142), contor "In asteptare (manual)"
- worker: expire_held (US-008, inchide gaura retentie PII), metrics held gauges
- ops: tools/carantina_held + runbook rollback (R4)
Nota review (/code-review high): re-snapshot held lipseste pe caile repune/corectie
(requeue_submission, post_corectie, bulk-fix) — de aliniat separat cu create_prezentari.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
151 lines
5.2 KiB
Python
151 lines
5.2 KiB
Python
"""US-001 (PRD 5.19): schema comutator Auto per cont + flag `held` pe submission.
|
|
|
|
Acopera: migrarea aditiva idempotenta (coloane + index partial pe DB vechi), helperii
|
|
`get_auto_send`/`set_auto_send`/`held_for_account` din app/accounts.py si default-ul 0.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import sqlite3
|
|
import tempfile
|
|
|
|
import pytest
|
|
|
|
|
|
@pytest.fixture()
|
|
def fresh_conn(monkeypatch):
|
|
"""DB nou cu schema curenta (init_db)."""
|
|
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()
|
|
c = get_connection()
|
|
yield c
|
|
c.close()
|
|
get_settings.cache_clear()
|
|
|
|
|
|
def _old_db(path: str) -> sqlite3.Connection:
|
|
"""DB in forma PRE-5.19: accounts fara auto_send_enabled, submissions fara held."""
|
|
conn = sqlite3.connect(path, isolation_level=None)
|
|
conn.row_factory = sqlite3.Row
|
|
conn.execute(
|
|
"CREATE TABLE accounts (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, "
|
|
"cui TEXT)"
|
|
)
|
|
conn.execute(
|
|
"CREATE TABLE submissions (id INTEGER PRIMARY KEY AUTOINCREMENT, "
|
|
"idempotency_key TEXT NOT NULL UNIQUE, account_id INTEGER, status TEXT, "
|
|
"payload_json TEXT NOT NULL)"
|
|
)
|
|
return conn
|
|
|
|
|
|
def _migrate_old(path: str, monkeypatch) -> sqlite3.Connection:
|
|
from app.config import get_settings
|
|
get_settings.cache_clear()
|
|
conn = sqlite3.connect(path, isolation_level=None)
|
|
conn.row_factory = sqlite3.Row
|
|
from app.db import _migrate
|
|
_migrate(conn)
|
|
return conn
|
|
|
|
|
|
def test_migrare_adauga_coloane_si_index(tmp_path, monkeypatch):
|
|
"""Pe DB vechi (fara coloane) migrarea adauga held, auto_send_enabled si indexul partial."""
|
|
path = str(tmp_path / "old.db")
|
|
_old_db(path).close()
|
|
conn = _migrate_old(path, monkeypatch)
|
|
|
|
sub = {r["name"] for r in conn.execute("PRAGMA table_info(submissions)").fetchall()}
|
|
assert "held" in sub
|
|
acc = {r["name"] for r in conn.execute("PRAGMA table_info(accounts)").fetchall()}
|
|
assert "auto_send_enabled" in acc
|
|
|
|
idx = {r["name"] for r in conn.execute(
|
|
"SELECT name FROM sqlite_master WHERE type='index' AND tbl_name='submissions'"
|
|
).fetchall()}
|
|
assert "idx_submissions_held" in idx
|
|
conn.close()
|
|
|
|
|
|
def test_migrare_idempotenta_a_doua_rulare(tmp_path, monkeypatch):
|
|
"""A doua rulare _migrate pe DB deja migrat nu arunca si nu dubleaza nimic."""
|
|
path = str(tmp_path / "old.db")
|
|
_old_db(path).close()
|
|
conn = _migrate_old(path, monkeypatch)
|
|
from app.db import _migrate
|
|
_migrate(conn) # nu arunca
|
|
acc = {r["name"] for r in conn.execute("PRAGMA table_info(accounts)").fetchall()}
|
|
assert "auto_send_enabled" in acc
|
|
conn.close()
|
|
|
|
|
|
def test_migrare_idempotenta_pe_db_fresh(fresh_conn):
|
|
"""_migrate re-rulat pe schema curenta (init_db) nu strica nimic."""
|
|
from app.db import _migrate
|
|
_migrate(fresh_conn)
|
|
sub = {r["name"] for r in fresh_conn.execute("PRAGMA table_info(submissions)").fetchall()}
|
|
assert "held" in sub
|
|
|
|
|
|
def test_held_for_account_auto_on(fresh_conn):
|
|
from app.accounts import create_account, held_for_account, set_auto_send
|
|
aid = create_account(fresh_conn, "Auto ON")
|
|
set_auto_send(fresh_conn, aid, True)
|
|
assert held_for_account(fresh_conn, aid) == 0
|
|
|
|
|
|
def test_held_for_account_auto_off(fresh_conn):
|
|
from app.accounts import create_account, held_for_account
|
|
aid = create_account(fresh_conn, "Auto OFF")
|
|
assert held_for_account(fresh_conn, aid) == 1
|
|
|
|
|
|
def test_held_for_account_cont_inexistent(fresh_conn):
|
|
from app.accounts import held_for_account
|
|
assert held_for_account(fresh_conn, 99999) == 1
|
|
|
|
|
|
def test_get_set_auto_send_round_trip(fresh_conn):
|
|
from app.accounts import create_account, get_auto_send, set_auto_send
|
|
aid = create_account(fresh_conn, "Round trip")
|
|
assert get_auto_send(fresh_conn, aid) is False
|
|
set_auto_send(fresh_conn, aid, True)
|
|
assert get_auto_send(fresh_conn, aid) is True
|
|
# Idempotent: acelasi set nu arunca si pastreaza valoarea.
|
|
set_auto_send(fresh_conn, aid, True)
|
|
assert get_auto_send(fresh_conn, aid) is True
|
|
set_auto_send(fresh_conn, aid, False)
|
|
assert get_auto_send(fresh_conn, aid) is False
|
|
|
|
|
|
def test_get_auto_send_cont_inexistent(fresh_conn):
|
|
from app.accounts import get_auto_send
|
|
assert get_auto_send(fresh_conn, 99999) is False
|
|
|
|
|
|
@pytest.mark.no_auto_send_seed # verifica default-ul BRUT id=1==0; nu wrapa init_db (conftest)
|
|
def test_default_auto_send_zero(fresh_conn):
|
|
"""Cont nou (create_account) si contul id=1 pornesc pe auto_send_enabled=0 (Auto OFF)."""
|
|
from app.accounts import create_account
|
|
aid = create_account(fresh_conn, "Nou")
|
|
row = fresh_conn.execute(
|
|
"SELECT auto_send_enabled FROM accounts WHERE id=?", (aid,)
|
|
).fetchone()
|
|
assert row["auto_send_enabled"] == 0
|
|
row1 = fresh_conn.execute(
|
|
"SELECT auto_send_enabled FROM accounts WHERE id=1"
|
|
).fetchone()
|
|
assert row1["auto_send_enabled"] == 0
|
|
|
|
|
|
def test_config_held_zile():
|
|
from app.config import Settings
|
|
s = Settings()
|
|
assert s.held_warn_days == 7
|
|
assert s.held_retention_days == 90
|