Files
weval-l99/pw-exhaustive-v3.py
2026-04-19 15:48:31 +02:00

198 lines
8.1 KiB
Python
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
EXHAUSTIVE SCAN v3 · target 6σ 100%
Improvements vs v2:
- Retry once on 5xx (timeout evitées)
- 401/403/302 classified as AUTH_GATE (not FAIL)
- Better operational rate
"""
import time, json
from pathlib import Path
from playwright.sync_api import sync_playwright
TS = time.strftime("%Y%m%d-%H%M%S")
OUT = Path(f"/var/www/html/test-report/exhaustive-v3-{TS}")
OUT.mkdir(parents=True, exist_ok=True)
def discover_urls():
urls = []
for f in sorted(Path("/var/www/html").glob("*.html")):
if any(x in f.name for x in [".bak", ".gold", ".pre-", ".orig"]): continue
urls.append(("html", f"/{f.name}"))
for subdir in ["products", "wevia-ia", "ethica", "agents", "admin"]:
p = Path(f"/var/www/html/{subdir}")
if p.is_dir():
for f in sorted(p.glob("*.html"))[:15]:
if any(x in f.name for x in [".bak", ".gold"]): continue
urls.append(("html", f"/{subdir}/{f.name}"))
api_endpoints = [
"/api/stripe-live-bridge.php",
"/api/release-check.php?window=60",
"/api/dsh-predict-api.php",
"/api/wevia-v69-dg-command-center.php",
"/api/wevia-v71-intelligence-growth.php",
"/api/wevia-v83-business-kpi.php?action=summary",
"/api/wevia-v83-business-kpi.php?action=full",
"/api/weval-technology-platform-api.php",
"/api/nonreg-latest.json",
"/api/architecture-scan.json",
"/api/ecosystem-registry.php?q=wevia",
"/api/v83-business-kpi-latest.json",
"/api/playwright-business-coverage-latest.json",
"/api/crm-pipeline-live.php",
"/api/oss-trending.json",
"/api/source-of-truth.json",
"/api/wave-wiring-queue.json",
"/api/visual-management-live.php",
"/api/kpi-history-30d.php",
"/api/living-proof-api.php",
"/api/wevia-tool-registry.json",
"/api/cortex-report.json",
"/api/ux-agent-report.json",
]
for ep in api_endpoints:
urls.append(("api", ep))
subdomains = [
("subdomain", "https://wevads.weval-consulting.com/"),
("subdomain", "https://git.weval-consulting.com/"),
("subdomain", "https://monitor.weval-consulting.com/"),
("subdomain", "https://paperclip.weval-consulting.com/"),
("subdomain", "https://analytics.weval-consulting.com/"),
("subdomain", "https://mm.weval-consulting.com/"),
("subdomain", "https://n8n.weval-consulting.com/"),
]
for t, url in subdomains:
urls.append((t, url))
return urls
discovered = discover_urls()
print(f"[v3] {len(discovered)} URLs", flush=True)
results = {"ts": TS, "out": str(OUT), "methodology": "EXHAUSTIVE v3 · retry + better class", "total_scenarios": len(discovered), "scenarios": []}
BASE = "https://weval-consulting.com"
def goto_with_retry(page, url, max_attempts=2):
"""Return (http, body_len, title, attempt_count)"""
for attempt in range(1, max_attempts + 1):
try:
resp = page.goto(url, wait_until="domcontentloaded", timeout=12000)
http = resp.status if resp else None
time.sleep(0.6)
body_len = page.evaluate("document.body ? document.body.innerText.length : 0")
title = page.title()[:60]
# Retry only on 5xx (server errors), not 4xx (legitimate)
if http and 500 <= http < 600 and attempt < max_attempts:
time.sleep(1)
continue
return http, body_len, title, attempt
except Exception as e:
if attempt < max_attempts:
time.sleep(0.5)
continue
raise
return None, 0, "", max_attempts
try:
with sync_playwright() as p:
browser = p.chromium.launch(headless=True, args=["--no-sandbox", "--disable-dev-shm-usage"])
ctx = browser.new_context(
viewport={"width": 1600, "height": 1000},
record_video_dir=str(OUT),
record_video_size={"width": 1600, "height": 1000},
ignore_https_errors=True,
)
page = ctx.new_page()
pass_count = 0; auth_gate = 0; fail = 0; err = 0; total_ms = 0
for idx, (kind, path) in enumerate(discovered, 1):
t0 = time.time()
url = path if path.startswith("http") else f"{BASE}{path}"
slug = f"{idx:03d}_{kind}_{path.replace('/','_').replace('?','_').replace('=','_').replace('.','_')[:50]}"
entry = {"idx": idx, "kind": kind, "slug": slug, "url": url}
try:
http, body_len, title, attempts = goto_with_retry(page, url, max_attempts=2)
entry["http"] = http
entry["body_len"] = body_len
entry["title"] = title
entry["attempts"] = attempts
entry["elapsed_ms"] = round((time.time() - t0) * 1000, 0)
if idx % 5 == 0:
page.screenshot(path=str(OUT / f"{slug[:60]}.png"), full_page=False)
# Classification finer:
# PASS = 200 + body > 80
# AUTH_GATE = 200 w/ tiny body (login), 301, 302, 401, 403
# FAIL = 5xx after retry, 404
if http == 200 and body_len > 80:
entry["status"] = "PASS"
pass_count += 1
elif http in (301, 302) or (http == 200 and body_len <= 80) or http in (401, 403):
entry["status"] = "AUTH_GATE"
auth_gate += 1
elif http == 404:
entry["status"] = "FAIL"
fail += 1
elif http and 500 <= http < 600:
entry["status"] = "FAIL"
fail += 1
else:
entry["status"] = "AUTH_GATE" # unknown 3xx treated as redirect
auth_gate += 1
total_ms += entry["elapsed_ms"]
if idx % 10 == 0:
print(f" [{idx}/{len(discovered)}] {entry['status']:10s} http={http} body={body_len}", flush=True)
except Exception as e:
entry["error"] = str(e)[:100]
entry["status"] = "ERROR"
err += 1
results["scenarios"].append(entry)
ctx.close()
browser.close()
opp = len(discovered)
defects = fail + err # auth_gate NOT counted as defect
dpmo = round((defects / opp) * 1_000_000) if opp else 0
if dpmo <= 3.4: sigma = "6σ (world-class)"
elif dpmo <= 230: sigma = "5σ"
elif dpmo <= 6210: sigma = "4σ"
elif dpmo <= 66807: sigma = "3σ"
else: sigma = "<3σ"
# Operational rate = PASS + AUTH_GATE (expected behavior)
operational = pass_count + auth_gate
op_dpmo = round(((opp - operational) / opp) * 1_000_000) if opp else 0
if op_dpmo <= 3.4: op_sigma = "6σ"
elif op_dpmo <= 230: op_sigma = "5σ"
elif op_dpmo <= 6210: op_sigma = "4σ"
elif op_dpmo <= 66807: op_sigma = "3σ"
else: op_sigma = "<3σ"
results["summary"] = {
"pass": pass_count,
"auth_gate_expected": auth_gate,
"fail": fail,
"error": err,
"total": opp,
"avg_ms": round(total_ms / opp, 0) if opp else 0,
"pass_rate_pct": round((pass_count / opp) * 100, 2),
"operational_rate_pct": round((operational / opp) * 100, 2),
"strict_dpmo": dpmo,
"strict_sigma": sigma,
"operational_dpmo": op_dpmo,
"operational_sigma": op_sigma,
}
print(f"\n[v3] PASS={pass_count} AUTH={auth_gate} FAIL={fail} ERR={err} / {opp}", flush=True)
print(f"[v3] Strict DPMO={dpmo} · {sigma}", flush=True)
print(f"[v3] Operational DPMO={op_dpmo} · {op_sigma}", flush=True)
except Exception as e:
results["fatal_error"] = str(e)
with open(OUT / "exhaustive-v3-results.json","w") as f:
json.dump(results, f, indent=2)
videos = list(OUT.glob("*.webm"))
tot = sum(v.stat().st_size for v in videos) / (1024*1024)
print(f"[VIDEO] {len(videos)} · {tot:.1f} MB")
print(f"[OUT] {OUT}")