Spaces:
Running
Running
| """Tests Sprint A5 — bascule à chaud du mode public (M-13). | |
| Le mode public est piloté par la variable d'environnement | |
| ``PICARONES_PUBLIC_MODE``. ``picarones.web.security.is_public_mode()`` | |
| la lit à **chaque appel** plutôt qu'au démarrage, ce qui permet à un | |
| opérateur de basculer le mode sans redémarrer le serveur. | |
| Cette suite vérifie que la bascule à chaud fonctionne : | |
| 1. Au démarrage en mode dev, ``assert_engines_allowed`` accepte les | |
| moteurs cloud ; après ``setenv PICARONES_PUBLIC_MODE=1``, le même | |
| appel les refuse. | |
| 2. Inversement : démarrage public → bascule dev → cloud autorisé. | |
| 3. Aucun cache global ne mémorise l'ancienne valeur. | |
| """ | |
| from __future__ import annotations | |
| import pytest | |
| from picarones.interfaces.web._legacy.security import ( | |
| assert_engines_allowed, | |
| assert_llm_provider_allowed, | |
| is_public_mode, | |
| ) | |
| def test_public_mode_off_allows_cloud_engines(monkeypatch) -> None: | |
| """Mode dev : moteurs cloud autorisés sans réserve.""" | |
| monkeypatch.delenv("PICARONES_PUBLIC_MODE", raising=False) | |
| assert is_public_mode() is False | |
| # Ne doit pas lever | |
| assert_engines_allowed(["mistral_ocr", "google_vision", "azure_doc_intel"]) | |
| def test_public_mode_on_blocks_cloud_engines(monkeypatch) -> None: | |
| """Mode public : moteurs cloud refusés (clés mutualisées côté serveur).""" | |
| monkeypatch.setenv("PICARONES_PUBLIC_MODE", "1") | |
| assert is_public_mode() is True | |
| with pytest.raises(PermissionError): | |
| assert_engines_allowed(["mistral_ocr"]) | |
| def test_hot_swap_dev_to_public(monkeypatch) -> None: | |
| """Bascule à chaud dev → public. Le même appel passe puis échoue | |
| sans redémarrage du process.""" | |
| monkeypatch.delenv("PICARONES_PUBLIC_MODE", raising=False) | |
| # Phase 1 : dev → cloud autorisé | |
| assert_engines_allowed(["mistral_ocr"]) # ne lève pas | |
| # Phase 2 : bascule à chaud | |
| monkeypatch.setenv("PICARONES_PUBLIC_MODE", "1") | |
| with pytest.raises(PermissionError): | |
| assert_engines_allowed(["mistral_ocr"]) | |
| def test_hot_swap_public_to_dev(monkeypatch) -> None: | |
| """Bascule inverse : public → dev. Le même cloud refusé puis accepté.""" | |
| monkeypatch.setenv("PICARONES_PUBLIC_MODE", "1") | |
| with pytest.raises(PermissionError): | |
| assert_engines_allowed(["google_vision"]) | |
| monkeypatch.delenv("PICARONES_PUBLIC_MODE", raising=False) | |
| assert_engines_allowed(["google_vision"]) # ne lève pas | |
| def test_hot_swap_llm_provider_check(monkeypatch) -> None: | |
| """``assert_llm_provider_allowed`` doit aussi être sensible à la | |
| bascule à chaud.""" | |
| monkeypatch.delenv("PICARONES_PUBLIC_MODE", raising=False) | |
| assert_llm_provider_allowed("openai") # dev : ok | |
| monkeypatch.setenv("PICARONES_PUBLIC_MODE", "1") | |
| with pytest.raises(PermissionError): | |
| assert_llm_provider_allowed("openai") | |
| def test_engines_allowed_partial_block(monkeypatch) -> None: | |
| """En mode public, si la liste contient cloud + local, l'erreur | |
| doit identifier précisément quel(s) moteur(s) sont refusés.""" | |
| monkeypatch.setenv("PICARONES_PUBLIC_MODE", "1") | |
| with pytest.raises(PermissionError) as exc_info: | |
| assert_engines_allowed(["tesseract", "mistral_ocr", "pero_ocr"]) | |
| msg = str(exc_info.value) | |
| # Le message doit mentionner le moteur cloud refusé (pour un | |
| # diagnostic clair côté frontend). | |
| assert "mistral_ocr" in msg | |
| def test_empty_engine_list_passes_in_both_modes(monkeypatch) -> None: | |
| """Une liste vide ne doit jamais lever (même en mode public).""" | |
| monkeypatch.delenv("PICARONES_PUBLIC_MODE", raising=False) | |
| assert_engines_allowed([]) | |
| monkeypatch.setenv("PICARONES_PUBLIC_MODE", "1") | |
| assert_engines_allowed([]) | |
| def test_local_engines_always_allowed(monkeypatch) -> None: | |
| """Tesseract / Pero (locaux) ne doivent jamais être bloqués.""" | |
| monkeypatch.setenv("PICARONES_PUBLIC_MODE", "1") | |
| assert_engines_allowed(["tesseract"]) | |
| assert_engines_allowed(["pero_ocr"]) | |
| assert_engines_allowed(["tesseract", "pero_ocr"]) | |