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>
This commit is contained in:
@@ -114,6 +114,37 @@ class TestDashboardRoutes:
|
||||
'timeout': 30,
|
||||
}]
|
||||
|
||||
def test_streaming_status_snapshot_aggregates_existing_endpoints(self, monkeypatch):
|
||||
"""Le proxy legacy /status agrège les endpoints streaming reels."""
|
||||
calls = []
|
||||
|
||||
def fake_fetch(endpoint, query_string=''):
|
||||
calls.append((endpoint, query_string))
|
||||
if endpoint == 'stats':
|
||||
return {'active_sessions': 1, 'total_events': 7}
|
||||
if endpoint == 'sessions':
|
||||
return {'sessions': [{'session_id': 's1'}]}
|
||||
if endpoint == 'processing/status':
|
||||
return {'status': 'degraded', 'components_ready': False}
|
||||
if endpoint == 'replays':
|
||||
return {'replays': [{'replay_id': 'r1', 'active': True}]}
|
||||
raise AssertionError(endpoint)
|
||||
|
||||
monkeypatch.setattr(dashboard_app, '_fetch_streaming_json', fake_fetch)
|
||||
|
||||
snapshot = dashboard_app._streaming_status_snapshot()
|
||||
|
||||
assert snapshot['active_sessions'] == 1
|
||||
assert snapshot['sessions'] == [{'session_id': 's1'}]
|
||||
assert snapshot['processing']['status'] == 'degraded'
|
||||
assert snapshot['replay']['replay_id'] == 'r1'
|
||||
assert calls == [
|
||||
('stats', ''),
|
||||
('sessions', ''),
|
||||
('processing/status', ''),
|
||||
('replays', ''),
|
||||
]
|
||||
|
||||
def test_dashboard_submit_competence_verdict(self, client, monkeypatch):
|
||||
"""Le dashboard journalise un verdict sans write-back YAML."""
|
||||
import core.competences.verdicts as verdicts_module
|
||||
@@ -212,6 +243,58 @@ class TestDashboardRoutes:
|
||||
data = resp.get_json()
|
||||
assert 'workflows' in data
|
||||
|
||||
def test_workflows_list_reads_vwb_db(self, client, monkeypatch, tmp_path):
|
||||
"""Régression red-gate : /api/workflows reflète la base VWB v3, pas 0.
|
||||
|
||||
Avant correctif l'endpoint globait un store JSON vide et renvoyait
|
||||
toujours total:0. On construit une DB VWB minimale (schéma canonique
|
||||
workflows + steps) et on vérifie que l'endpoint expose le compte réel.
|
||||
"""
|
||||
import sqlite3
|
||||
from pathlib import Path
|
||||
|
||||
db_path = tmp_path / "instance" / "workflows.db"
|
||||
db_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
conn = sqlite3.connect(str(db_path))
|
||||
conn.execute(
|
||||
"CREATE TABLE workflows (id VARCHAR(64) PRIMARY KEY, name VARCHAR(255), "
|
||||
"description TEXT, created_at DATETIME, updated_at DATETIME, "
|
||||
"is_active BOOLEAN, source VARCHAR(64), review_status VARCHAR(32))"
|
||||
)
|
||||
conn.execute(
|
||||
"CREATE TABLE steps (id VARCHAR(64) PRIMARY KEY, workflow_id VARCHAR(64), "
|
||||
"action_type VARCHAR(64))"
|
||||
)
|
||||
conn.execute(
|
||||
"INSERT INTO workflows VALUES (?,?,?,?,?,?,?,?)",
|
||||
("wf_aiva", "Urgence_aiva_demo", "demo", "2026-06-01", "2026-06-18",
|
||||
1, "manual", ""),
|
||||
)
|
||||
conn.execute(
|
||||
"INSERT INTO workflows VALUES (?,?,?,?,?,?,?,?)",
|
||||
("wf_learned", "Learned_flow", "", "2026-06-02", "2026-06-17",
|
||||
1, "learned_import", "pending"),
|
||||
)
|
||||
# 3 steps pour wf_aiva → nodes_count attendu = 3
|
||||
for i in range(3):
|
||||
conn.execute(
|
||||
"INSERT INTO steps VALUES (?,?,?)", (f"s{i}", "wf_aiva", "click")
|
||||
)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
monkeypatch.setattr(dashboard_app, "VWB_DB_PATH", Path(db_path))
|
||||
|
||||
resp = client.get('/api/workflows')
|
||||
assert resp.status_code == 200
|
||||
data = resp.get_json()
|
||||
assert data['total'] == 2, f"attendu 2 workflows, obtenu {data['total']}"
|
||||
names = {w['name'] for w in data['workflows']}
|
||||
assert 'Urgence_aiva_demo' in names
|
||||
aiva = next(w for w in data['workflows'] if w['name'] == 'Urgence_aiva_demo')
|
||||
assert aiva['nodes_count'] == 3
|
||||
assert aiva['source'] == 'manual'
|
||||
|
||||
def test_sessions_list(self, client):
|
||||
"""L'API sessions retourne la liste."""
|
||||
resp = client.get('/api/agent/sessions')
|
||||
|
||||
Reference in New Issue
Block a user