"""Tests intégration HTTP de POST /api/v1/dialog/resolve — R2 MVP P0. Vérifie : - flag OFF par défaut → 503. - flag ON + match catalogue → 200 + payload conforme. - flag ON + pas de match → 200 + matched=False + policy=pause. """ from __future__ import annotations import sys from pathlib import Path import pytest _ROOT = str(Path(__file__).resolve().parents[2]) if _ROOT not in sys.path: sys.path.insert(0, _ROOT) pytestmark = pytest.mark.integration _TEST_API_TOKEN = "test_dialog_resolver_endpoint_token" @pytest.fixture def client(monkeypatch): """TestClient FastAPI avec token. Le flag RPA_DIALOG_RESOLVER_ENABLED est géré par chaque test (par défaut absent → 503).""" monkeypatch.setenv("RPA_API_TOKEN", _TEST_API_TOKEN) from fastapi.testclient import TestClient from agent_v0.server_v1 import api_stream monkeypatch.setattr(api_stream, "API_TOKEN", _TEST_API_TOKEN) return TestClient(api_stream.app, raise_server_exceptions=False) def _auth_headers(): return {"Authorization": f"Bearer {_TEST_API_TOKEN}"} class TestDialogResolveEndpointFlag: """Flag OFF par défaut — protection anti-régression.""" def test_disabled_by_default_returns_503(self, client, monkeypatch): # flag explicitement absent → 503 attendu. monkeypatch.delenv("RPA_DIALOG_RESOLVER_ENABLED", raising=False) resp = client.post( "/api/v1/dialog/resolve", json={"current_title": "Confirmer l'enregistrement", "evidence_texts": []}, headers=_auth_headers(), ) assert resp.status_code == 503 assert "RPA_DIALOG_RESOLVER_ENABLED" in resp.text class TestDialogResolveEndpointEnabled: """Flag ON : l'endpoint retourne une résolution conforme.""" @pytest.fixture(autouse=True) def _enable_flag(self, monkeypatch): monkeypatch.setenv("RPA_DIALOG_RESOLVER_ENABLED", "true") def test_match_confirm_save_overwrite(self, client): resp = client.post( "/api/v1/dialog/resolve", json={ "current_title": "Confirmer l'enregistrement", "evidence_texts": [], "machine_id": "pc-alpha", }, headers=_auth_headers(), ) assert resp.status_code == 200 body = resp.json() assert body["matched"] is True assert body["dialog_id"] == "confirm-save-overwrite" assert body["policy"] == "auto" assert body["action"] is not None assert body["action"]["button_label"] == "Oui" def test_match_notepad_with_evidence(self, client): resp = client.post( "/api/v1/dialog/resolve", json={ "current_title": "Bloc-notes", "evidence_texts": ["Ne pas enregistrer"], "machine_id": "pc-alpha", }, headers=_auth_headers(), ) assert resp.status_code == 200 body = resp.json() assert body["matched"] is True assert body["dialog_id"] == "notepad-unsaved-changes" assert body["policy"] == "auto" def test_no_match_returns_pause(self, client): resp = client.post( "/api/v1/dialog/resolve", json={ "current_title": "Fenêtre inconnue XYZ", "evidence_texts": [], "machine_id": "pc-alpha", }, headers=_auth_headers(), ) assert resp.status_code == 200 body = resp.json() assert body["matched"] is False assert body["dialog_id"] == "" assert body["policy"] == "pause" assert body["action"] is None def test_match_windows_uac_returns_pause(self, client): resp = client.post( "/api/v1/dialog/resolve", json={ "current_title": "Contrôle de compte d'utilisateur", "evidence_texts": ["Voulez-vous autoriser cette application"], "machine_id": "pc-alpha", }, headers=_auth_headers(), ) assert resp.status_code == 200 body = resp.json() assert body["matched"] is True assert body["dialog_id"] == "windows-uac" assert body["policy"] == "pause" assert body["action"] is None def test_requires_auth(self, client): resp = client.post( "/api/v1/dialog/resolve", json={"current_title": "Confirmer l'enregistrement", "evidence_texts": []}, # pas de header → 401. ) assert resp.status_code == 401