""" Tests for the saving-scan funnel endpoint. Focus: input validation + lead persistence + rate-limit error path. The actual compliance check is mocked — we only verify the route layer. """ import os import sys from unittest.mock import patch import pytest from fastapi import FastAPI from fastapi.testclient import TestClient sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) # Use a temp SQLite for the sidecar os.environ["COMPLIANCE_AUDIT_DB"] = "/tmp/test_saving_scan.db" if os.path.exists("/tmp/test_saving_scan.db"): os.remove("/tmp/test_saving_scan.db") from compliance.api.saving_scan_routes import router # noqa: E402 app = FastAPI() app.include_router(router, prefix="/api") client = TestClient(app) class TestStartSavingScanValidation: def test_missing_email_returns_422(self): resp = client.post("/api/compliance/agent/saving-scan/start", json={"url": "https://example.de"}) assert resp.status_code == 422 def test_invalid_email_returns_400(self): with patch("compliance.api.saving_scan_routes.asyncio.create_task"): resp = client.post( "/api/compliance/agent/saving-scan/start", json={"url": "https://example.de", "email": "kein-email", "consent": True}, ) assert resp.status_code == 400 assert "E-Mail" in resp.json()["detail"] def test_invalid_url_returns_400(self): with patch("compliance.api.saving_scan_routes.asyncio.create_task"): resp = client.post( "/api/compliance/agent/saving-scan/start", json={"url": "ftp://wrong.de", "email": "u@x.de", "consent": True}, ) assert resp.status_code == 400 def test_consent_required(self): with patch("compliance.api.saving_scan_routes.asyncio.create_task"): resp = client.post( "/api/compliance/agent/saving-scan/start", json={"url": "https://example.de", "email": "u@x.de", "consent": False}, ) assert resp.status_code == 400 assert "Consent" in resp.json()["detail"] def _patch_check_runner(): """Stub the lazy-imported worker — avoids loading smtp_sender (Py3.10+).""" import sys, types fake = types.ModuleType("compliance.api.agent_compliance_check_routes") class _DocInput: def __init__(self, doc_type="other", url=""): self.doc_type, self.url = doc_type, url class _Req: def __init__(self, **kw): self.__dict__.update(kw) async def _runner(*_a, **_kw): pass fake.DocumentInput = _DocInput fake.ComplianceCheckRequest = _Req fake._run_compliance_check = _runner fake._compliance_check_jobs = {} sys.modules["compliance.api.agent_compliance_check_routes"] = fake class TestStartSavingScanSuccess: def test_valid_request_starts_check(self): _patch_check_runner() resp = client.post( "/api/compliance/agent/saving-scan/start", json={"url": "https://example-newdomain.de", "email": "user@example.de", "consent": True}, ) assert resp.status_code == 200, resp.text data = resp.json() assert "check_id" in data assert data["status"] == "running" assert "example-newdomain.de" in data["message"] class TestLeadCount: def test_lead_count_after_submit(self): _patch_check_runner() client.post( "/api/compliance/agent/saving-scan/start", json={"url": "https://abc-leadtest.de", "email": "lead@x.de", "consent": True}, ) resp = client.get("/api/compliance/agent/saving-scan/lead-count") assert resp.status_code == 200 data = resp.json() assert data["total_leads"] >= 1 assert "abc-leadtest.de" in str(data["top_domains"])