Files
rar-autopass/tests/test_integrare_api.py
Claude Agent f0786051f5 feat(web): hub integrare /integrare — exemple cod + retetar VFP + ping + export (PRD 5.1)
Pagina /integrare (tab autentificat, scoped pe cont): exemple cod multi-limbaj
(curl/Python/PHP/C#/Node) + retetar Visual FoxPro (MSXML2 + WinHttp) pe ambele
canale (prezentari JSON + import fisier), export Postman/OpenAPI/Swagger si buton
"Testeaza conexiunea".

- US-001: GET /v1/ping (readiness: account_id/mediu/autentificat_cu_cheie/
  are_creds_rar/ts) + GET /v1/integrare/postman.json (v2.1.0, allowlist 3 rute)
- US-002: app/web/integrare_examples.py pur (7 limbaje x 2 canale, drift-test
  is_required(), JSON compact pentru C#/VFP)
- US-003: tab "Integrare" IA pe 2 niveluri (limbaj->canal, VFP cu dialecte),
  copy din <pre><code>, empty-state CTA, export .cardlink, script scoped
- US-004: POST /integrare/test-cheie (account_for_key direct, scoped sesiune,
  no-echo cheie)

Backend trimitere (worker/masina stari/idempotenta/mapping) si schema neatinse.
568 teste pass. VERIFY context curat + E2E browser (Playwright) + code-review high.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 12:16:41 +00:00

216 lines
7.9 KiB
Python

"""Teste US-001: endpoint-uri de integrare (GET /v1/ping + export Postman).
TDD — toate testele RED inainte de implementare.
"""
from __future__ import annotations
import json
import os
import tempfile
import pytest
from fastapi.testclient import TestClient
# --------------------------------------------------------------------------- #
# Fixture client izolat #
# --------------------------------------------------------------------------- #
@pytest.fixture()
def client(monkeypatch):
"""Client FastAPI cu DB temporara izolata, require_api_key=False (dev)."""
tmp = tempfile.mkdtemp()
monkeypatch.setenv("AUTOPASS_DB_PATH", os.path.join(tmp, "t.db"))
monkeypatch.setenv("AUTOPASS_REQUIRE_API_KEY", "false")
monkeypatch.setenv("AUTOPASS_RAR_ENV", "test")
from app.config import get_settings
get_settings.cache_clear()
from app.main import app
with TestClient(app) as c:
yield c
get_settings.cache_clear()
@pytest.fixture()
def client_prod(monkeypatch):
"""Client cu require_api_key=True (mod prod)."""
tmp = tempfile.mkdtemp()
monkeypatch.setenv("AUTOPASS_DB_PATH", os.path.join(tmp, "t.db"))
monkeypatch.setenv("AUTOPASS_REQUIRE_API_KEY", "true")
monkeypatch.setenv("AUTOPASS_RAR_ENV", "prod")
from app.config import get_settings
get_settings.cache_clear()
from app.main import app
with TestClient(app) as c:
yield c
get_settings.cache_clear()
def _creeaza_cheie(monkeypatch) -> str:
"""Seed cont id=1 + creeaza cheie API; intoarce cheia in clar."""
from app.db import get_connection
from app.auth import create_api_key
conn = get_connection()
try:
cheie = create_api_key(conn, 1)
conn.commit()
finally:
conn.close()
return cheie
def _seteaza_rar_creds(monkeypatch=None) -> None:
"""Seteaza rar_creds_enc pe contul id=1."""
from app.db import get_connection
from app.crypto import encrypt_creds
conn = get_connection()
try:
enc = encrypt_creds({"email": "test@rar.ro", "password": "secret"})
conn.execute("UPDATE accounts SET rar_creds_enc=? WHERE id=1", (enc,))
conn.commit()
finally:
conn.close()
# --------------------------------------------------------------------------- #
# Teste ping #
# --------------------------------------------------------------------------- #
def test_ping_cu_cheie_valida_200(client, monkeypatch):
"""GET /v1/ping cu X-API-Key valida -> 200 cu campurile cerute."""
cheie = _creeaza_cheie(monkeypatch)
r = client.get("/v1/ping", headers={"X-API-Key": cheie})
assert r.status_code == 200, r.text
body = r.json()
assert "account_id" in body
assert "mediu" in body
assert "autentificat_cu_cheie" in body
assert "are_creds_rar" in body
assert "ts" in body
assert body["autentificat_cu_cheie"] is True
assert body["mediu"] == "test"
def test_ping_cu_bearer_valid_200(client, monkeypatch):
"""GET /v1/ping cu Authorization: Bearer valida -> 200, autentificat_cu_cheie=True."""
cheie = _creeaza_cheie(monkeypatch)
r = client.get("/v1/ping", headers={"Authorization": f"Bearer {cheie}"})
assert r.status_code == 200, r.text
body = r.json()
assert body["autentificat_cu_cheie"] is True
assert body["account_id"] == 1
def test_ping_fara_cheie_dev_cont_implicit(client, monkeypatch):
"""Fara cheie, require_api_key=False -> cont 1, autentificat_cu_cheie=False."""
r = client.get("/v1/ping")
assert r.status_code == 200, r.text
body = r.json()
assert body["account_id"] == 1
assert body["autentificat_cu_cheie"] is False
def test_ping_x_api_key_gol_in_dev_cont_implicit(client, monkeypatch):
"""X-API-Key cu doar spatii = lipsa cheie -> cont 1, autentificat_cu_cheie=False in dev."""
r = client.get("/v1/ping", headers={"X-API-Key": " "})
assert r.status_code == 200, r.text
body = r.json()
assert body["account_id"] == 1
assert body["autentificat_cu_cheie"] is False
def test_ping_are_creds_rar_reflecta_contul(client, monkeypatch):
"""Cont fara creds -> are_creds_rar=False; dupa setare -> True."""
# Fara creds
r1 = client.get("/v1/ping")
assert r1.status_code == 200
assert r1.json()["are_creds_rar"] is False
# Seteaza creds pe cont 1
_seteaza_rar_creds()
r2 = client.get("/v1/ping")
assert r2.status_code == 200
assert r2.json()["are_creds_rar"] is True
def test_ping_cheie_invalida_401(client, monkeypatch):
"""Cheie invalida -> 401, indiferent de require_api_key."""
r = client.get("/v1/ping", headers={"X-API-Key": "rfak_cheie_falsa_xxxxxxxx"})
assert r.status_code == 401
def test_ping_prod_fara_cheie_401(client_prod, monkeypatch):
"""require_api_key=True, fara cheie -> 401."""
r = client_prod.get("/v1/ping")
assert r.status_code == 401
def test_ruta_ping_inregistrata_o_singura_data(client, monkeypatch):
"""Ruta /v1/ping trebuie sa apara exact o data in app.routes."""
from app.main import app
rute_ping = [r for r in app.routes if hasattr(r, "path") and r.path == "/v1/ping"]
assert len(rute_ping) == 1, f"Asteptat 1 ruta /v1/ping, gasit: {len(rute_ping)}"
# --------------------------------------------------------------------------- #
# Teste export Postman #
# --------------------------------------------------------------------------- #
def test_postman_export_json_valid(client, monkeypatch):
"""GET /v1/integrare/postman.json -> 200, Content-Type JSON, structura Postman v2.1.0."""
r = client.get("/v1/integrare/postman.json")
assert r.status_code == 200, r.text
assert "application/json" in r.headers.get("content-type", "")
body = r.json()
assert "info" in body
assert "v2.1.0" in body["info"].get("schema", ""), f"Schema gresita: {body['info'].get('schema')}"
def test_postman_contine_exact_trei_requesturi(client, monkeypatch):
"""Colectia Postman trebuie sa contina exact 3 requesturi cu headers si url corecte."""
r = client.get("/v1/integrare/postman.json")
assert r.status_code == 200
body = r.json()
items = body.get("item", [])
assert len(items) == 3, f"Asteptat 3 requesturi, gasit: {len(items)}"
for item in items:
req = item.get("request", {})
# Fiecare trebuie sa aiba header X-API-Key cu valoarea {{api_key}}
headers = req.get("header", [])
cheie_header = [h for h in headers if h.get("key") == "X-API-Key"]
assert len(cheie_header) == 1, f"Header X-API-Key lipsa in request '{item.get('name')}'"
assert cheie_header[0].get("value") == "{{api_key}}", \
f"Valoare header gresita: {cheie_header[0].get('value')}"
# URL-ul trebuie sa contina {{base_url}}
url = req.get("url", {})
url_raw = url.get("raw", "") if isinstance(url, dict) else str(url)
assert "{{base_url}}" in url_raw, \
f"{{{{base_url}}}} lipsa in url pentru '{item.get('name')}': {url_raw}"
def test_postman_nu_deriva_din_app_routes(client, monkeypatch):
"""Colectia Postman contine EXACT cele 3 rute allowlist, nu mai mult."""
r = client.get("/v1/integrare/postman.json")
assert r.status_code == 200
body = r.json()
items = body.get("item", [])
# Extrage URL-urile / path-urile din colectie
cai_expuse = set()
for item in items:
req = item.get("request", {})
url = req.get("url", {})
url_raw = url.get("raw", "") if isinstance(url, dict) else str(url)
# Normalizeaza: scoate {{base_url}} si parametrii query
cale = url_raw.replace("{{base_url}}", "").split("?")[0]
cai_expuse.add(cale)
# Allowlist: exact acestea 3
allowlist = {"/v1/prezentari", "/v1/import", "/v1/ping"}
assert cai_expuse == allowlist, \
f"Colectia expune cai neasteptate: {cai_expuse - allowlist} sau lipsesc: {allowlist - cai_expuse}"