feat(5.15+5.14): CLOSE — fix-uri code-review + embeddings functional
5.15 (propagare design + dashboard editare) si 5.14 (mapare LLM distilata) inchise dupa /code-review high. 8 buguri reparate TDD: - HIGH modal nu se deschidea pe randul slim (base.html: trimitere-slim) - HIGH /repune trunchia prestatii (declaratie incompleta la RAR) -> iterare peste existing, codes pozitional - HIGH embeddings incarca model ~230MB degeaba pe corpus gol -> poarta has_corpus() - HIGH picker chips gol pe re-render eroare -> conn/account_id pe toate ramurile - MED obs re-derivat dupa stergere explicita -> _merge_override pastreaza obs='' - MED mapare salvata fara denumire poluă GOLD -> _record_gold_validation guard - MED typo nome_prestatie -> nume_prestatie in select /repune - MED bucketare timp +3h gresita iarna -> SQLite localtime + TZ=Europe/Bucharest Embeddings WIRE-uit functional (PRD #15, decizie user): ensure_embeddings_corpus construieste corpus din nomenclator, gated pe AUTOPASS_EMBEDDINGS_ENABLED (default off). Marime model corectata ~50MB->~230MB (estimare PRD gresita). Cleanup: hoist load_* din bucla bulk-fix; import re la top. Regresie: 1256 passed, 1 deselected (live), 0 failed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
152
tests/test_device_mix.py
Normal file
152
tests/test_device_mix.py
Normal file
@@ -0,0 +1,152 @@
|
||||
"""Teste US-012 (PRD 5.15): Analytics device-mix — validare premisa mobil, fara PII.
|
||||
|
||||
TDD: RED inainte de implementare.
|
||||
|
||||
Semnal: la acces dashboard -> eveniment 'device_mix' in app_events cu cod 'desktop'/'mobil'.
|
||||
Zero PII: nu se stocheaza UA brut, IP sau VIN.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Fixture #
|
||||
# --------------------------------------------------------------------------- #
|
||||
|
||||
@pytest.fixture()
|
||||
def client(monkeypatch):
|
||||
tmp = tempfile.mkdtemp()
|
||||
monkeypatch.setenv("AUTOPASS_DB_PATH", os.path.join(tmp, "device_mix.db"))
|
||||
monkeypatch.setenv("AUTOPASS_LOG_DIR", os.path.join(tmp, "logs"))
|
||||
monkeypatch.setenv("AUTOPASS_WEB_AUTH_REQUIRED", "false")
|
||||
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 _events_device_mix():
|
||||
from app.db import get_connection
|
||||
conn = get_connection()
|
||||
try:
|
||||
return conn.execute(
|
||||
"SELECT * FROM app_events WHERE tip='device_mix' ORDER BY id"
|
||||
).fetchall()
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# test_device_mix_inregistrat #
|
||||
# --------------------------------------------------------------------------- #
|
||||
|
||||
UA_DESKTOP = (
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
|
||||
"AppleWebKit/537.36 (KHTML, like Gecko) "
|
||||
"Chrome/125.0 Safari/537.36"
|
||||
)
|
||||
UA_MOBIL_ANDROID = (
|
||||
"Mozilla/5.0 (Linux; Android 13; Pixel 7) "
|
||||
"AppleWebKit/537.36 (KHTML, like Gecko) "
|
||||
"Chrome/125.0 Mobile Safari/537.36"
|
||||
)
|
||||
UA_MOBIL_IPHONE = (
|
||||
"Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) "
|
||||
"AppleWebKit/605.1.15 (KHTML, like Gecko) "
|
||||
"Version/17.0 Mobile/15E148 Safari/604.1"
|
||||
)
|
||||
|
||||
|
||||
def test_device_mix_inregistrat_desktop(client):
|
||||
"""Acces dashboard cu UA desktop -> eveniment device_mix cod='desktop'."""
|
||||
r = client.get("/", headers={"User-Agent": UA_DESKTOP})
|
||||
assert r.status_code == 200
|
||||
|
||||
events = _events_device_mix()
|
||||
assert len(events) >= 1, "Trebuie cel putin un eveniment device_mix dupa acces dashboard"
|
||||
# ultimul eveniment clasificat ca desktop
|
||||
ev = events[-1]
|
||||
assert ev["tip"] == "device_mix"
|
||||
assert ev["cod"] == "desktop", f"Clasificare gresita: {ev['cod']!r}"
|
||||
|
||||
|
||||
def test_device_mix_inregistrat_mobil_android(client):
|
||||
"""Acces dashboard cu UA Android Mobile -> eveniment device_mix cod='mobil'."""
|
||||
r = client.get("/", headers={"User-Agent": UA_MOBIL_ANDROID})
|
||||
assert r.status_code == 200
|
||||
|
||||
events = _events_device_mix()
|
||||
assert len(events) >= 1
|
||||
ev = events[-1]
|
||||
assert ev["tip"] == "device_mix"
|
||||
assert ev["cod"] == "mobil", f"Clasificare gresita Android: {ev['cod']!r}"
|
||||
|
||||
|
||||
def test_device_mix_inregistrat_mobil_iphone(client):
|
||||
"""Acces dashboard cu UA iPhone -> eveniment device_mix cod='mobil'."""
|
||||
r = client.get("/", headers={"User-Agent": UA_MOBIL_IPHONE})
|
||||
assert r.status_code == 200
|
||||
|
||||
events = _events_device_mix()
|
||||
assert len(events) >= 1
|
||||
ev = events[-1]
|
||||
assert ev["tip"] == "device_mix"
|
||||
assert ev["cod"] == "mobil", f"Clasificare gresita iPhone: {ev['cod']!r}"
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# test_device_mix_fara_pii #
|
||||
# --------------------------------------------------------------------------- #
|
||||
|
||||
def test_device_mix_fara_pii(client):
|
||||
"""Evenimentul device_mix nu contine UA brut, IP sau alte PII."""
|
||||
r = client.get("/", headers={"User-Agent": UA_MOBIL_ANDROID})
|
||||
assert r.status_code == 200
|
||||
|
||||
events = _events_device_mix()
|
||||
assert len(events) >= 1
|
||||
ev = events[-1]
|
||||
|
||||
# Campul mesaj: doar eticheta grosiera, nu UA brut
|
||||
mesaj = ev["mesaj"] or ""
|
||||
assert UA_MOBIL_ANDROID not in mesaj, "UA brut nu trebuie stocat in mesaj"
|
||||
assert "Android" not in mesaj, "Fragment UA nu trebuie stocat in mesaj"
|
||||
assert "Mozilla" not in mesaj, "Fragment UA nu trebuie stocat in mesaj"
|
||||
|
||||
# context_json: daca exista, nu contine UA brut / IP
|
||||
ctx_raw = ev["context_json"]
|
||||
if ctx_raw:
|
||||
ctx = json.loads(ctx_raw)
|
||||
ctx_str = json.dumps(ctx)
|
||||
assert UA_MOBIL_ANDROID not in ctx_str, "UA brut nu trebuie in context_json"
|
||||
assert "Mozilla" not in ctx_str, "Fragment UA nu trebuie in context_json"
|
||||
# IP-uri tipice nu apar (testclient trimite 127.0.0.1/testclient)
|
||||
for ip_fragment in ["127.0.0.1", "testclient", "192.168."]:
|
||||
assert ip_fragment not in ctx_str, f"IP {ip_fragment!r} nu trebuie in context_json"
|
||||
|
||||
# codul este doar eticheta grosiera
|
||||
assert ev["cod"] in ("desktop", "mobil"), f"Cod neasteptat: {ev['cod']!r}"
|
||||
|
||||
|
||||
def test_device_mix_fara_pii_desktop(client):
|
||||
"""Evenimentul device_mix pentru desktop nu contine UA brut."""
|
||||
r = client.get("/", headers={"User-Agent": UA_DESKTOP})
|
||||
assert r.status_code == 200
|
||||
|
||||
events = _events_device_mix()
|
||||
assert len(events) >= 1
|
||||
ev = events[-1]
|
||||
|
||||
mesaj = ev["mesaj"] or ""
|
||||
assert UA_DESKTOP not in mesaj, "UA brut desktop nu trebuie in mesaj"
|
||||
assert "Windows NT" not in mesaj, "Fragment UA nu trebuie in mesaj"
|
||||
assert ev["cod"] == "desktop"
|
||||
Reference in New Issue
Block a user