[TEST] Test Runner v2: aggiunto L6 AI Features (Cross-Analysis, Normative, Whistleblowing)
- Livello L6 con 6 test: portfolio, history, analyze AI, 403 role check, normative, whistleblowing - CSS badge lvl-l6 verde smeraldo - Coverage table: 27 → 35 endpoint (8 nuovi L6) - Full Suite aggiornata L1→L6 con step L3 e L6 funzionanti - Header doc aggiornato Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
19a9e5622d
commit
fca3ab3cf8
@ -9,6 +9,7 @@
|
||||
* L3 Compliance Core
|
||||
* L4 B2B & Services API
|
||||
* L5 Export & Reports
|
||||
* L6 AI Features (Cross-Analysis, Workflow, Normative)
|
||||
* SIM Simulazioni scenari reali
|
||||
*/
|
||||
|
||||
@ -187,6 +188,50 @@ function getCommands(): array
|
||||
'cwd' => $root, 'timeout' => 60, 'continue_on_fail' => true,
|
||||
],
|
||||
|
||||
// ── L6: AI FEATURES ─────────────────────────────────────────────────
|
||||
'l6-ai' => [
|
||||
'label' => 'L6 — AI Features (Cross-Analysis)',
|
||||
'level' => 'l6',
|
||||
'bash' => implode(' && ', [
|
||||
// Login come consultant (ha accesso a cross-analysis)
|
||||
"echo '━━━ L6.0 Setup: login consultant ━━━'",
|
||||
"TOKEN_CONS=\$(curl -sf -X POST {$api}/auth/login -H 'Content-Type: application/json' -d '{\"email\":\"consultant@nis2agile.demo\",\"password\":\"Demo2026!\"}' 2>/dev/null | python3 -c \"import sys,json; d=json.load(sys.stdin); print(d.get('data',{}).get('access_token',''))\" 2>/dev/null) && [ -n \"\$TOKEN_CONS\" ] && echo \"Token consultant: \${TOKEN_CONS:0:25}...\" || echo '[SKIP] consultant non trovato — eseguire SIM-01 prima'",
|
||||
"echo ''",
|
||||
|
||||
// L6.1: portfolio (senza AI, solo dati aggregati)
|
||||
"echo '━━━ L6.1 Cross-Analysis Portfolio (no AI) ━━━'",
|
||||
"TOKEN_CONS=\$(curl -sf -X POST {$api}/auth/login -H 'Content-Type: application/json' -d '{\"email\":\"consultant@nis2agile.demo\",\"password\":\"Demo2026!\"}' 2>/dev/null | python3 -c \"import sys,json; d=json.load(sys.stdin); print(d.get('data',{}).get('access_token',''))\" 2>/dev/null) && [ -n \"\$TOKEN_CONS\" ] && curl -sf -H \"Authorization: Bearer \$TOKEN_CONS\" {$api}/cross-analysis/portfolio | python3 -m json.tool || echo '[SKIP] token non disponibile'",
|
||||
"echo ''",
|
||||
|
||||
// L6.2: history (vuota, ma endpoint deve rispondere 200)
|
||||
"echo '━━━ L6.2 Cross-Analysis History ━━━'",
|
||||
"TOKEN_CONS=\$(curl -sf -X POST {$api}/auth/login -H 'Content-Type: application/json' -d '{\"email\":\"consultant@nis2agile.demo\",\"password\":\"Demo2026!\"}' 2>/dev/null | python3 -c \"import sys,json; d=json.load(sys.stdin); print(d.get('data',{}).get('access_token',''))\" 2>/dev/null) && [ -n \"\$TOKEN_CONS\" ] && curl -sf -H \"Authorization: Bearer \$TOKEN_CONS\" {$api}/cross-analysis/history | python3 -c \"import sys,json; d=json.load(sys.stdin); h=d.get('data',{}).get('history',[]); print(f'History entries: {len(h)} — HTTP OK')\" || echo '[SKIP]'",
|
||||
"echo ''",
|
||||
|
||||
// L6.3: analyze con domanda breve (chiama AI Anthropic)
|
||||
"echo '━━━ L6.3 Cross-Analysis AI Analyze ━━━'",
|
||||
"TOKEN_CONS=\$(curl -sf -X POST {$api}/auth/login -H 'Content-Type: application/json' -d '{\"email\":\"consultant@nis2agile.demo\",\"password\":\"Demo2026!\"}' 2>/dev/null | python3 -c \"import sys,json; d=json.load(sys.stdin); print(d.get('data',{}).get('access_token',''))\" 2>/dev/null) && [ -n \"\$TOKEN_CONS\" ] && curl -sf -X POST -H \"Authorization: Bearer \$TOKEN_CONS\" -H 'Content-Type: application/json' -d '{\"question\":\"Qual e il livello medio di compliance e quali sono le categorie NIS2 piu deboli nel portfolio?\"}' {$api}/cross-analysis/analyze | python3 -c \"import sys,json; d=json.load(sys.stdin); r=d.get('data',{}); ans=r.get('result',{}).get('answer',''); orgs=r.get('org_count',0); print(f'Org analizzate: {orgs}'); print(f'Risposta AI ({len(ans)} chars): {ans[:300]}...' if len(ans)>300 else f'Risposta: {ans}')\" || echo '[SKIP/ERRORE]'",
|
||||
"echo ''",
|
||||
|
||||
// L6.4: accesso negato a utente normale (403)
|
||||
"echo '━━━ L6.4 Cross-Analysis 403 per utente non-consultant ━━━'",
|
||||
"TOKEN_EMP=\$(curl -sf -X POST {$api}/auth/login -H 'Content-Type: application/json' -d '{\"email\":\"admin@datacore.demo\",\"password\":\"Demo2026!\"}' 2>/dev/null | python3 -c \"import sys,json; d=json.load(sys.stdin); print(d.get('data',{}).get('access_token',''))\" 2>/dev/null) && [ -n \"\$TOKEN_EMP\" ] && curl -sf -o /dev/null -w 'GET /cross-analysis/portfolio (org_admin) → HTTP %{http_code}\\n' -H \"Authorization: Bearer \$TOKEN_EMP\" {$api}/cross-analysis/portfolio || echo '[SKIP]'",
|
||||
"echo ''",
|
||||
|
||||
// L6.5: normative feed
|
||||
"echo '━━━ L6.5 Normative Feed ━━━'",
|
||||
"TOKEN={$getToken} && ORG_ID=\$(curl -sf -H \"Authorization: Bearer \$TOKEN\" {$api}/organizations/list 2>/dev/null | python3 -c \"import sys,json; d=json.load(sys.stdin); orgs=d.get('data',[]); print(orgs[0]['id'] if orgs else '')\" 2>/dev/null) && [ -n \"\$ORG_ID\" ] && curl -sf -H \"Authorization: Bearer \$TOKEN\" -H \"X-Organization-Id: \$ORG_ID\" {$api}/normative/list | python3 -c \"import sys,json; d=json.load(sys.stdin); items=d.get('data',[]); print(f'Aggiornamenti normativi: {len(items)}')\" || echo '[SKIP]'",
|
||||
"echo ''",
|
||||
|
||||
// L6.6: whistleblowing stats
|
||||
"echo '━━━ L6.6 Whistleblowing Stats ━━━'",
|
||||
"TOKEN={$getToken} && ORG_ID=\$(curl -sf -H \"Authorization: Bearer \$TOKEN\" {$api}/organizations/list 2>/dev/null | python3 -c \"import sys,json; d=json.load(sys.stdin); orgs=d.get('data',[]); print(orgs[0]['id'] if orgs else '')\" 2>/dev/null) && [ -n \"\$ORG_ID\" ] && curl -sf -H \"Authorization: Bearer \$TOKEN\" -H \"X-Organization-Id: \$ORG_ID\" {$api}/whistleblowing/stats | python3 -m json.tool || echo '[SKIP]'",
|
||||
|
||||
"echo '[OK] L6 AI Features completato'",
|
||||
]),
|
||||
'cwd' => $root, 'timeout' => 120, 'continue_on_fail' => true,
|
||||
],
|
||||
|
||||
// ── SMOKE ───────────────────────────────────────────────────────────
|
||||
'smoke' => [
|
||||
'label' => 'Smoke Tests (curl rapido)',
|
||||
@ -259,16 +304,20 @@ function getCommands(): array
|
||||
'cwd' => $root, 'timeout' => 30, 'continue_on_fail' => false,
|
||||
],
|
||||
'full-suite' => [
|
||||
'label' => 'Full Suite L1+L2+L3+L4+L5',
|
||||
'label' => 'Full Suite L1+L2+L3+L4+L5+L6',
|
||||
'level' => 'infra',
|
||||
'bash' => implode(' && ', [
|
||||
"echo '════════════ L1 AUTH ════════════'",
|
||||
"curl -sf -X POST {$api}/auth/login -H 'Content-Type: application/json' -d '{\"email\":\"admin@datacore.demo\",\"password\":\"Demo2026!\"}' | python3 -c \"import sys,json; d=json.load(sys.stdin); print('Login:', 'OK' if d.get('success') else 'FAIL')\" || echo 'L1 SKIP'",
|
||||
"echo '════════════ L2 TENANT ════════════'",
|
||||
"curl -sf {$api}/../api-status.php | python3 -c \"import sys,json; d=json.load(sys.stdin); print('API:', d.get('status','?'))\"",
|
||||
"TOKEN={$getToken} && curl -sf -H \"Authorization: Bearer \$TOKEN\" {$api}/organizations/list | python3 -c \"import sys,json; d=json.load(sys.stdin); orgs=d.get('data',[]); print(f'Orgs: {len(orgs)}')\" || echo 'L2 SKIP'",
|
||||
"echo '════════════ L3 COMPLIANCE ════════════'",
|
||||
"TOKEN={$getToken} && ORG_ID=\$(curl -sf -H \"Authorization: Bearer \$TOKEN\" {$api}/organizations/list 2>/dev/null | python3 -c \"import sys,json; d=json.load(sys.stdin); orgs=d.get('data',[]); print(orgs[0]['id'] if orgs else '')\" 2>/dev/null) && [ -n \"\$ORG_ID\" ] && curl -sf -H \"Authorization: Bearer \$TOKEN\" -H \"X-Organization-Id: \$ORG_ID\" {$api}/dashboard/compliance-score | python3 -c \"import sys,json; d=json.load(sys.stdin); print('Score:', d.get('data',{}).get('score','?'))\" || echo 'L3 SKIP'",
|
||||
"echo '════════════ L5 EXPORT ════════════'",
|
||||
"curl -sf {$api}/../api-status.php | python3 -m json.tool",
|
||||
"echo '[OK] Full Suite completata'",
|
||||
"curl -sf {$api}/../api-status.php | python3 -c \"import sys,json; d=json.load(sys.stdin); print('API:', d.get('status','?'))\"",
|
||||
"echo '════════════ L6 AI CROSS ════════════'",
|
||||
"TOKEN_CONS=\$(curl -sf -X POST {$api}/auth/login -H 'Content-Type: application/json' -d '{\"email\":\"consultant@nis2agile.demo\",\"password\":\"Demo2026!\"}' 2>/dev/null | python3 -c \"import sys,json; d=json.load(sys.stdin); print(d.get('data',{}).get('access_token',''))\" 2>/dev/null) && [ -n \"\$TOKEN_CONS\" ] && curl -sf -H \"Authorization: Bearer \$TOKEN_CONS\" {$api}/cross-analysis/portfolio | python3 -c \"import sys,json; d=json.load(sys.stdin); print('Portfolio orgs:', d.get('data',{}).get('org_count','?'))\" || echo 'L6 SKIP (eseguire SIM-01 prima)'",
|
||||
"echo '[OK] Full Suite L1→L6 completata'",
|
||||
]),
|
||||
'cwd' => $root, 'timeout' => 300, 'continue_on_fail' => true,
|
||||
],
|
||||
@ -576,8 +625,9 @@ function serveUI(): void
|
||||
'l3-compliance' => ['L3', 'Compliance Core', 'l3'],
|
||||
'l4-b2b' => ['L4', 'B2B & Services', 'l4'],
|
||||
'l5-export' => ['L5', 'Export & Reports', 'l5'],
|
||||
'l6-ai' => ['L6', 'AI Features', 'l6'],
|
||||
'chain-verify' => ['—', 'Hash Chain Verify', 'infra'],
|
||||
'full-suite' => ['ALL', 'Full Suite L1→L5', 'infra'],
|
||||
'full-suite' => ['ALL', 'Full Suite L1→L6', 'infra'],
|
||||
'reset' => ['⚠', 'Reset Dati Demo', 'danger'],
|
||||
];
|
||||
foreach ($levelDefs as $id => [$badge, $label, $cls]) {
|
||||
@ -615,6 +665,13 @@ function serveUI(): void
|
||||
['NCR', 'GET /api/ncr/stats', 'L4', 'ok'],
|
||||
['Onboard', 'POST /api/onboarding/complete', 'sim','ok'],
|
||||
['Admin', 'GET /api/admin/stats', 'admin','skip'],
|
||||
['CrossAI', 'GET /api/cross-analysis/portfolio', 'L6', 'ok'],
|
||||
['CrossAI', 'GET /api/cross-analysis/history', 'L6', 'ok'],
|
||||
['CrossAI', 'POST /api/cross-analysis/analyze', 'L6', 'ok'],
|
||||
['Normative','GET /api/normative/list', 'L6', 'ok'],
|
||||
['Normative','POST /api/normative/{id}/ack', 'L6', 'sim'],
|
||||
['Whistle', 'GET /api/whistleblowing/stats', 'L6', 'ok'],
|
||||
['Whistle', 'POST /api/whistleblowing/submit', 'L6', 'sim'],
|
||||
];
|
||||
$covRows = '';
|
||||
foreach ($coverageEndpoints as [$module, $ep, $level, $status]) {
|
||||
@ -728,6 +785,7 @@ body { display: flex; height: 100vh; background: var(--navy); color: var(--text)
|
||||
.lvl-l3 { background: rgba(249,115,22,.15); color: var(--orange); }
|
||||
.lvl-l4 { background: rgba(168,85,247,.15); color: var(--purple); }
|
||||
.lvl-l5 { background: rgba(234,179,8,.15); color: var(--yellow); }
|
||||
.lvl-l6 { background: rgba(16,185,129,.15); color: #10b981; }
|
||||
.lvl-infra { background: rgba(100,116,139,.15);color: var(--muted); }
|
||||
.lvl-sim { background: rgba(6,182,212,.1); color: var(--cyan); }
|
||||
.lvl-admin { background: rgba(239,68,68,.1); color: var(--red); }
|
||||
@ -885,7 +943,7 @@ body { display: flex; height: 100vh; background: var(--navy); color: var(--text)
|
||||
<div id="tab-cov" class="tab-panel">
|
||||
<div style="padding:.5rem .65rem; overflow-x:auto;">
|
||||
<p style="font-size:.68rem;color:var(--muted);margin-bottom:.5rem">
|
||||
Copertura endpoint API (27 totali)
|
||||
Copertura endpoint API (35 totali)
|
||||
</p>
|
||||
<table class="cov-table">
|
||||
<thead><tr><th>Modulo</th><th>Endpoint</th><th>Lvl</th><th>Status</th></tr></thead>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user