feat(web): self-service cheie/creds + admin web + email signup (PRD 3.3b)
US-007: rute web proprii /cont/roteste-cheie + /cont/rar-creds scoped pe sesiune (C13), sectiune "Contul meu" cu cheie afisata o data. US-010: rol admin (users.is_admin) + require_admin->403 + CLI set-admin + bootstrap primul cont=admin (count_admins in BEGIN IMMEDIATE, anti-race). US-011: panou /admin (activare/dezactivare conturi, CSRF + PRG), link admin + logout pe dashboard. US-012: app/email.py notify_signup best-effort degradat fara SMTP + config smtp_*. Fix: migrare defensiva users.is_admin/email_verified in _migrate. VERIFY x2 context curat (PASS) + /code-review high. 393 teste pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
124
tests/test_migrate_users.py
Normal file
124
tests/test_migrate_users.py
Normal file
@@ -0,0 +1,124 @@
|
||||
"""Teste migrare defensiva coloane users (is_admin, email_verified).
|
||||
|
||||
TDD: RED -> implementare in _migrate -> GREEN.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import sqlite3
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from app.db import _migrate, init_db, get_connection
|
||||
from app.config import get_settings
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Test 1: _migrate adauga is_admin si email_verified pe o tabela users minima
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def test_migrate_adauga_is_admin_pe_users_veche() -> None:
|
||||
"""Pe un DB cu tabela users creata fara is_admin/email_verified,
|
||||
_migrate trebuie sa adauge ambele coloane."""
|
||||
with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as f:
|
||||
db_path = Path(f.name)
|
||||
|
||||
conn = sqlite3.connect(str(db_path), isolation_level=None)
|
||||
conn.row_factory = sqlite3.Row
|
||||
conn.execute("PRAGMA journal_mode = WAL")
|
||||
conn.execute("PRAGMA foreign_keys = OFF") # evitam FK checks pe schema minima
|
||||
|
||||
# Tabela accounts minima (necesara pentru FK la users).
|
||||
# Trebuie sa aiba cui + rar_creds_enc + active ca _migrate sa nu crape
|
||||
# pe ALTER/CREATE INDEX care le refera inainte de blocul users.
|
||||
conn.execute("""
|
||||
CREATE TABLE accounts (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
cui TEXT,
|
||||
active INTEGER NOT NULL DEFAULT 1,
|
||||
rar_creds_enc TEXT
|
||||
)
|
||||
""")
|
||||
conn.execute("INSERT INTO accounts (id, name) VALUES (1, 'default')")
|
||||
|
||||
# Tabela submissions minima (necesara pentru _migrate -- coloane submissions)
|
||||
conn.execute("""
|
||||
CREATE TABLE submissions (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
idempotency_key TEXT NOT NULL UNIQUE,
|
||||
account_id INTEGER,
|
||||
status TEXT NOT NULL DEFAULT 'queued',
|
||||
payload_json TEXT NOT NULL,
|
||||
rar_creds_enc TEXT,
|
||||
rar_status_code INTEGER,
|
||||
rar_error TEXT,
|
||||
id_prezentare INTEGER,
|
||||
retry_count INTEGER NOT NULL DEFAULT 0,
|
||||
next_attempt_at TEXT,
|
||||
sending_since TEXT,
|
||||
purge_after TEXT,
|
||||
batch_id INTEGER,
|
||||
row_index INTEGER,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
)
|
||||
""")
|
||||
|
||||
# Tabela users MINIMA: fara is_admin, fara email_verified
|
||||
conn.execute("""
|
||||
CREATE TABLE users (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
account_id INTEGER NOT NULL,
|
||||
email TEXT NOT NULL UNIQUE,
|
||||
password_hash TEXT NOT NULL,
|
||||
salt TEXT NOT NULL,
|
||||
scrypt_params TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
)
|
||||
""")
|
||||
|
||||
try:
|
||||
# Rulam migrarea
|
||||
_migrate(conn)
|
||||
|
||||
# Verificam ca ambele coloane au fost adaugate
|
||||
cols = {r["name"] for r in conn.execute("PRAGMA table_info(users)").fetchall()}
|
||||
assert "is_admin" in cols, f"is_admin lipseste dupa migrare; coloane prezente: {cols}"
|
||||
assert "email_verified" in cols, f"email_verified lipseste dupa migrare; coloane prezente: {cols}"
|
||||
finally:
|
||||
conn.close()
|
||||
db_path.unlink(missing_ok=True)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Test 2: _migrate este idempotent pe un DB initializat normal cu init_db()
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def test_migrate_idempotent_pe_users_curenta(tmp_path: pytest.TempPathFactory, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
"""Pe un DB initializat normal, re-apelarea _migrate nu ridica exceptie
|
||||
si coloanele is_admin/email_verified raman prezente."""
|
||||
db_file = tmp_path / "test_idem.db"
|
||||
monkeypatch.setenv("AUTOPASS_DB_PATH", str(db_file))
|
||||
|
||||
# Resetam settings-ul cached ca sa preia noul AUTOPASS_DB_PATH
|
||||
get_settings.cache_clear() # type: ignore[attr-defined]
|
||||
|
||||
try:
|
||||
# Initializare normala
|
||||
init_db()
|
||||
|
||||
# Re-apelam _migrate direct (idempotenta)
|
||||
conn = get_connection()
|
||||
try:
|
||||
_migrate(conn) # nu trebuie sa ridice
|
||||
|
||||
cols = {r["name"] for r in conn.execute("PRAGMA table_info(users)").fetchall()}
|
||||
assert "is_admin" in cols
|
||||
assert "email_verified" in cols
|
||||
finally:
|
||||
conn.close()
|
||||
finally:
|
||||
get_settings.cache_clear() # type: ignore[attr-defined]
|
||||
Reference in New Issue
Block a user