Files
rpa_vision_v3/tests/integration/test_pause_for_human.py
Dom ec1fb81054
Some checks failed
tests / Lint (ruff + black) (push) Failing after 1m46s
tests / Tests unitaires (sans GPU) (push) Failing after 2m0s
tests / Tests sécurité (critique) (push) Has been skipped
fix(dashboard,worker): vérité produit P0 — dashboard+worker+VWB export
War-room clôture DGX 2026-06-18 (recadrage Dom : graphe/apprentissage/mémoire/dashboard = surface produit P0).
Le dashboard et le statut worker affichaient des états faux ; corrige pour refléter la vérité du produit.

- dashboard FAISS: distingue index brut / metadata HMAC invalide / runtime / absent (plus de faux "inactif")
- dashboard process-mining: 503 explicite missing_dependency (plus de message trompeur)
- dashboard /api/workflows + system/status: lecture DB VWB v3 canonique (total réel = 24, plus de 0)
- worker /processing/status: véridique (lit _worker_health.json) + statut "idle/armé (lazy)" distinct de "dégradé (échec)"
- VWB export: N steps -> N actions/edges (dernière action n'est plus perdue)
- tests: dashboard routes, worker status truthfulness, export VWB

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 17:50:12 +02:00

134 lines
4.9 KiB
Python

"""Tests de l'action pause_for_human (C.5).
Vérifie la chaîne :
- Validation côté replay_engine accepte le nouveau type
- Conversion edge → action normalisée préserve le message
- Bridge VWB → core mappe correctement
- Le bridge VWB construit bien un edge avec action.type='pause_for_human'
"""
from agent_v0.server_v1.replay_engine import (
_ALLOWED_ACTION_TYPES,
_validate_replay_action,
_edge_to_normalized_actions,
)
from visual_workflow_builder.backend.services.learned_workflow_bridge import (
VWB_ACTION_TO_CORE,
convert_vwb_to_core_workflow,
_vwb_params_to_core,
)
# ----------------------------------------------------------------------
# Validation pipeline (replay_engine)
# ----------------------------------------------------------------------
def test_pause_for_human_in_allowed_types():
assert "pause_for_human" in _ALLOWED_ACTION_TYPES
def test_validate_pause_for_human_action_valid():
action = {"type": "pause_for_human", "parameters": {"message": "Valider UHCD ?"}}
assert _validate_replay_action(action) is None
def test_validate_pause_for_human_no_params_still_valid():
"""Le validateur ne doit pas exiger 'message' (fallback côté handler)."""
action = {"type": "pause_for_human"}
assert _validate_replay_action(action) is None
# ----------------------------------------------------------------------
# Conversion edge → action normalisée
# ----------------------------------------------------------------------
class _FakeAction:
def __init__(self, type_, parameters=None):
self.type = type_
self.target = None
self.parameters = parameters or {}
class _FakeEdge:
def __init__(self, action, edge_id="e1", from_node="n1", to_node="n2"):
self.edge_id = edge_id
self.from_node = from_node
self.to_node = to_node
self.action = action
def test_edge_to_action_pause_for_human_preserves_message():
edge = _FakeEdge(_FakeAction(
"pause_for_human",
parameters={"message": "Tu valides UHCD ?"},
))
actions = _edge_to_normalized_actions(edge, params={})
assert len(actions) == 1
a = actions[0]
assert a["type"] == "pause_for_human"
assert a["parameters"]["message"] == "Tu valides UHCD ?"
assert "x_pct" not in a # action logique, pas de coords
assert "y_pct" not in a
def test_edge_to_action_pause_for_human_default_message():
edge = _FakeEdge(_FakeAction("pause_for_human", parameters={}))
actions = _edge_to_normalized_actions(edge, params={})
assert actions[0]["parameters"]["message"] == "Validation requise"
def test_edge_to_action_pause_for_human_carries_edge_metadata():
edge = _FakeEdge(
_FakeAction("pause_for_human", parameters={"message": "x"}),
edge_id="edge_42", from_node="n_src", to_node="n_dst",
)
actions = _edge_to_normalized_actions(edge, params={})
a = actions[0]
assert a["edge_id"] == "edge_42"
assert a["from_node"] == "n_src"
assert a["to_node"] == "n_dst"
assert "action_id" in a
# ----------------------------------------------------------------------
# Bridge VWB → core
# ----------------------------------------------------------------------
def test_vwb_action_to_core_passthrough():
assert VWB_ACTION_TO_CORE["pause_for_human"] == "pause_for_human"
def test_vwb_params_to_core_preserves_message():
core_params = _vwb_params_to_core("pause_for_human", {"message": "Coucou"})
assert core_params == {"message": "Coucou"}
def test_vwb_params_to_core_default_message():
core_params = _vwb_params_to_core("pause_for_human", {})
assert core_params["message"] == "Validation requise"
def test_export_vwb_workflow_with_pause_step():
"""Un workflow VWB contenant une step pause_for_human doit produire un edge
avec action.type='pause_for_human' et message dans parameters."""
workflow_data = {"id": "wf_demo", "name": "Demo Urgences", "description": ""}
steps_data = [
{"id": "s1", "action_type": "click_anchor", "parameters": {"target_text": "25003284"}, "label": "Clic IPP"},
{"id": "s2", "action_type": "pause_for_human", "parameters": {"message": "Valider UHCD ?"}, "label": "Pause"},
{"id": "s3", "action_type": "click_anchor", "parameters": {"target_text": "Enregistrer"}, "label": "Clic Enregistrer"},
]
core = convert_vwb_to_core_workflow(workflow_data, steps_data)
assert core["learning_state"] == "COACHING"
assert len(core["nodes"]) == 4
assert len(core["edges"]) == 3
assert core["edges"][-1]["action"]["type"] == "mouse_click"
assert core["nodes"][-1]["is_end"] is True
# L'edge sortant du node de pause doit avoir le bon type + message
pause_edges = [
e for e in core["edges"]
if e["action"]["type"] == "pause_for_human"
]
assert len(pause_edges) == 1
assert pause_edges[0]["action"]["parameters"]["message"] == "Valider UHCD ?"