"""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"