feat: journée 17 avril — tests E2E validés, dashboard fleet+audit, VWB bridge, cleaner C2
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 14s
security-audit / pip-audit (CVE dépendances) (push) Successful in 10s
security-audit / Scan secrets (grep) (push) Successful in 8s
tests / Lint (ruff + black) (push) Successful in 13s
tests / Tests unitaires (sans GPU) (push) Failing after 14s
tests / Tests sécurité (critique) (push) Has been skipped
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 14s
security-audit / pip-audit (CVE dépendances) (push) Successful in 10s
security-audit / Scan secrets (grep) (push) Successful in 8s
tests / Lint (ruff + black) (push) Successful in 13s
tests / Tests unitaires (sans GPU) (push) Failing after 14s
tests / Tests sécurité (critique) (push) Has been skipped
Pipeline E2E complet validé : Capture VM → streaming → serveur → cleaner → replay → audit trail Mode apprentissage supervisé fonctionne (Léa échoue → humain → reprise) Dashboard : - Cleanup 14→10 onglets (RCE supprimée) - Fleet : enregistrer/révoquer agents, tokens, ZIP pré-configuré téléchargeable - Audit trail MVP (/audit) : filtres, tableau, export CSV, conformité AI Act/RGPD - Formulaire Fleet simplifié (nom + email, machine_id auto) VWB bridge Léa→VWB : - Compound décomposés en N steps (saisie + raccourci visibles) - Layout serpentin 3 colonnes (plus colonne verticale) - Badge OS 🪟/🐧, filtre OS retiré (admin Linux voit Windows) - Fix import SQLite readonly Cleaner intelligent : - Descriptions lisibles (UIA/C2) + détection doublons - Logique C2 : UIElement identifié = jamais parasite - Patterns parasites resserrés - Message Léa : "Je n'y arrive pas, montrez-moi comment faire" Config agent (INC-1 à INC-7) : - SERVER_URL + SERVER_BASE unifiés - RPA_OLLAMA_HOST séparé - allow_redirects=False sur POST - Middleware réécriture URL serveur CI Gitea : fix token + Flask-SocketIO + ruff propre Fleet endpoints : /agents/enroll|uninstall|fleet + agent_registry SQLite Backup : script quotidien workflows.db + audit Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
195
tests/unit/test_agent_config.py
Normal file
195
tests/unit/test_agent_config.py
Normal file
@@ -0,0 +1,195 @@
|
||||
# tests/unit/test_agent_config.py
|
||||
"""
|
||||
Tests unitaires pour la convention de configuration agent (INC-1 a INC-7).
|
||||
|
||||
Verifie que :
|
||||
- STREAMING_ENDPOINT contient /api/v1/traces/stream
|
||||
- SERVER_BASE est l'URL sans /api/v1 (pour /health)
|
||||
- Le health check utilise la racine, pas /api/v1
|
||||
- OLLAMA_HOST est separe de SERVER_URL
|
||||
"""
|
||||
|
||||
import os
|
||||
import pytest
|
||||
|
||||
|
||||
class TestAgentConfig:
|
||||
"""Tests de la resolution d'URL dans agent_v1.config."""
|
||||
|
||||
def test_streaming_endpoint_includes_api_v1(self, monkeypatch):
|
||||
"""STREAMING_ENDPOINT doit contenir /api/v1/traces/stream."""
|
||||
monkeypatch.setenv("RPA_SERVER_URL", "http://192.168.1.40:5005/api/v1")
|
||||
# Recharger le module config pour prendre en compte la variable
|
||||
import importlib
|
||||
from agent_v0.agent_v1 import config
|
||||
importlib.reload(config)
|
||||
|
||||
assert "/api/v1/traces/stream" in config.STREAMING_ENDPOINT
|
||||
assert config.STREAMING_ENDPOINT == "http://192.168.1.40:5005/api/v1/traces/stream"
|
||||
|
||||
def test_streaming_endpoint_default(self, monkeypatch):
|
||||
"""Endpoint par defaut (localhost:5005)."""
|
||||
monkeypatch.delenv("RPA_SERVER_URL", raising=False)
|
||||
import importlib
|
||||
from agent_v0.agent_v1 import config
|
||||
importlib.reload(config)
|
||||
|
||||
assert config.STREAMING_ENDPOINT == "http://localhost:5005/api/v1/traces/stream"
|
||||
|
||||
def test_server_base_strips_api_v1(self, monkeypatch):
|
||||
"""SERVER_BASE doit etre l'URL sans /api/v1."""
|
||||
monkeypatch.setenv("RPA_SERVER_URL", "http://192.168.1.40:5005/api/v1")
|
||||
import importlib
|
||||
from agent_v0.agent_v1 import config
|
||||
importlib.reload(config)
|
||||
|
||||
assert config.SERVER_BASE == "http://192.168.1.40:5005"
|
||||
assert "/api/v1" not in config.SERVER_BASE
|
||||
|
||||
def test_server_base_https_domain(self, monkeypatch):
|
||||
"""SERVER_BASE avec un domaine HTTPS (reverse proxy)."""
|
||||
monkeypatch.setenv(
|
||||
"RPA_SERVER_URL", "https://lea.labs.laurinebazin.design/api/v1"
|
||||
)
|
||||
import importlib
|
||||
from agent_v0.agent_v1 import config
|
||||
importlib.reload(config)
|
||||
|
||||
assert config.SERVER_BASE == "https://lea.labs.laurinebazin.design"
|
||||
|
||||
def test_health_url_is_root_not_api_v1(self, monkeypatch):
|
||||
"""Le health check doit etre sur SERVER_BASE/health (racine)."""
|
||||
monkeypatch.setenv(
|
||||
"RPA_SERVER_URL", "https://lea.labs.laurinebazin.design/api/v1"
|
||||
)
|
||||
import importlib
|
||||
from agent_v0.agent_v1 import config
|
||||
importlib.reload(config)
|
||||
|
||||
health_url = f"{config.SERVER_BASE}/health"
|
||||
assert health_url == "https://lea.labs.laurinebazin.design/health"
|
||||
assert "/api/v1/health" not in health_url
|
||||
|
||||
def test_ollama_host_separate_from_server(self, monkeypatch):
|
||||
"""OLLAMA_HOST est independant de SERVER_URL."""
|
||||
monkeypatch.setenv(
|
||||
"RPA_SERVER_URL", "https://lea.labs.laurinebazin.design/api/v1"
|
||||
)
|
||||
monkeypatch.delenv("RPA_OLLAMA_HOST", raising=False)
|
||||
import importlib
|
||||
from agent_v0.agent_v1 import config
|
||||
importlib.reload(config)
|
||||
|
||||
# Par defaut, Ollama est en local
|
||||
assert config.OLLAMA_HOST == "localhost"
|
||||
|
||||
def test_ollama_host_custom(self, monkeypatch):
|
||||
"""OLLAMA_HOST peut etre configure separement."""
|
||||
monkeypatch.setenv("RPA_OLLAMA_HOST", "192.168.1.40")
|
||||
import importlib
|
||||
from agent_v0.agent_v1 import config
|
||||
importlib.reload(config)
|
||||
|
||||
assert config.OLLAMA_HOST == "192.168.1.40"
|
||||
|
||||
def test_no_double_api_v1(self, monkeypatch):
|
||||
"""Aucune URL ne doit contenir /api/v1/api/v1 (double prefixe)."""
|
||||
monkeypatch.setenv("RPA_SERVER_URL", "http://192.168.1.40:5005/api/v1")
|
||||
import importlib
|
||||
from agent_v0.agent_v1 import config
|
||||
importlib.reload(config)
|
||||
|
||||
all_urls = [
|
||||
config.SERVER_URL,
|
||||
config.SERVER_BASE,
|
||||
config.STREAMING_ENDPOINT,
|
||||
config.UPLOAD_ENDPOINT,
|
||||
]
|
||||
for url in all_urls:
|
||||
assert "/api/v1/api/v1" not in url, f"Double /api/v1 dans : {url}"
|
||||
|
||||
|
||||
class TestServerClientUrls:
|
||||
"""Tests de la resolution d'URL dans lea_ui.server_client."""
|
||||
|
||||
def test_stream_url_includes_api_v1(self, monkeypatch):
|
||||
"""_stream_url doit contenir /api/v1."""
|
||||
monkeypatch.setenv("RPA_SERVER_URL", "http://192.168.1.40:5005/api/v1")
|
||||
from agent_v0.lea_ui.server_client import LeaServerClient
|
||||
|
||||
client = LeaServerClient()
|
||||
assert "/api/v1" in client._stream_url
|
||||
assert client._stream_url.endswith("/api/v1")
|
||||
|
||||
def test_stream_base_no_api_v1(self, monkeypatch):
|
||||
"""_stream_base ne doit PAS contenir /api/v1."""
|
||||
monkeypatch.setenv("RPA_SERVER_URL", "http://192.168.1.40:5005/api/v1")
|
||||
from agent_v0.lea_ui.server_client import LeaServerClient
|
||||
|
||||
client = LeaServerClient()
|
||||
assert "/api/v1" not in client._stream_base
|
||||
|
||||
def test_health_on_root(self, monkeypatch):
|
||||
"""Le health check doit pointer sur la racine."""
|
||||
monkeypatch.setenv(
|
||||
"RPA_SERVER_URL", "https://lea.labs.laurinebazin.design/api/v1"
|
||||
)
|
||||
from agent_v0.lea_ui.server_client import LeaServerClient
|
||||
|
||||
client = LeaServerClient()
|
||||
health_url = f"{client._stream_base}/health"
|
||||
assert health_url == "https://lea.labs.laurinebazin.design/health"
|
||||
|
||||
def test_workflows_url_no_double_api_v1(self, monkeypatch):
|
||||
"""L'URL workflows ne doit pas avoir /api/v1/api/v1."""
|
||||
monkeypatch.setenv("RPA_SERVER_URL", "http://192.168.1.40:5005/api/v1")
|
||||
from agent_v0.lea_ui.server_client import LeaServerClient
|
||||
|
||||
client = LeaServerClient()
|
||||
workflows_url = f"{client._stream_url}/traces/stream/workflows"
|
||||
assert "/api/v1/api/v1" not in workflows_url
|
||||
assert workflows_url == "http://192.168.1.40:5005/api/v1/traces/stream/workflows"
|
||||
|
||||
|
||||
class TestScenarios:
|
||||
"""Validation des 3 scenarios de deploiement."""
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"server_url,expected_stream,expected_base,expected_health",
|
||||
[
|
||||
# Scenario 1 : LAN interne
|
||||
(
|
||||
"http://192.168.1.40:5005/api/v1",
|
||||
"http://192.168.1.40:5005/api/v1/traces/stream",
|
||||
"http://192.168.1.40:5005",
|
||||
"http://192.168.1.40:5005/health",
|
||||
),
|
||||
# Scenario 2 : Internet via NPM
|
||||
(
|
||||
"https://lea.labs.laurinebazin.design/api/v1",
|
||||
"https://lea.labs.laurinebazin.design/api/v1/traces/stream",
|
||||
"https://lea.labs.laurinebazin.design",
|
||||
"https://lea.labs.laurinebazin.design/health",
|
||||
),
|
||||
# Scenario 3 : Dev local (defaut)
|
||||
(
|
||||
"http://localhost:5005/api/v1",
|
||||
"http://localhost:5005/api/v1/traces/stream",
|
||||
"http://localhost:5005",
|
||||
"http://localhost:5005/health",
|
||||
),
|
||||
],
|
||||
ids=["lan", "internet", "localhost"],
|
||||
)
|
||||
def test_scenario_urls(
|
||||
self, monkeypatch, server_url, expected_stream, expected_base, expected_health
|
||||
):
|
||||
"""Valider la matrice URL pour chaque scenario de deploiement."""
|
||||
monkeypatch.setenv("RPA_SERVER_URL", server_url)
|
||||
import importlib
|
||||
from agent_v0.agent_v1 import config
|
||||
importlib.reload(config)
|
||||
|
||||
assert config.STREAMING_ENDPOINT == expected_stream
|
||||
assert config.SERVER_BASE == expected_base
|
||||
assert f"{config.SERVER_BASE}/health" == expected_health
|
||||
Reference in New Issue
Block a user