Files
rar-autopass/tests/test_auto_send_schema.py
Claude Agent 3b5cf7a7d9 feat(5.19): auto-send toggle per cont + tinere manuala randuri (held)
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>
2026-07-06 08:34:40 +00:00

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