| |
| from pathlib import Path |
| import json |
| import re |
|
|
| root = Path(__file__).resolve().parents[1] |
| readme = (root / "README.md").read_text(encoding="utf-8") |
| index = (root / "index.html").read_text(encoding="utf-8") |
|
|
| required = [ |
| root / "README.md", |
| root / "index.html", |
| root / "LICENSE", |
| root / "assets/graph-classifier-map.png", |
| root / "data/classification-network.json", |
| root / "data/classification-network.graphml", |
| root / "data/scenario-summary.json", |
| ] |
|
|
| for path in required: |
| if not path.is_file() or path.stat().st_size == 0: |
| raise SystemExit(f"FAIL: missing or empty file: {path}") |
|
|
| assert "sdk: static" in readme |
| assert "app_file: index.html" in readme |
|
|
| short_description = next( |
| line.split(":", 1)[1].strip() |
| for line in readme.splitlines() |
| if line.startswith("short_description:") |
| ) |
| assert len(short_description) <= 60, ( |
| f"short_description is {len(short_description)} characters; " |
| "maximum is 60" |
| ) |
| assert "GraphShieldMistral interactive policy-graph explorer" in index |
| assert '<script src=' not in index |
| assert 'rel="stylesheet"' not in index |
|
|
| node_count = len(re.findall(r'data-node-id="', index)) |
| scenario_count = len(re.findall(r'data-node-id="DOC[0-9]+"', index)) |
| assert node_count >= 90, node_count |
| assert scenario_count == 5, scenario_count |
|
|
| payload = json.loads( |
| (root / "data/classification-network.json").read_text( |
| encoding="utf-8" |
| ) |
| ) |
| assert len(payload["nodes"]) >= 90 |
| assert len(payload["edges"]) >= 78 |
|
|
| print("PASS: self-contained static HTML") |
| print(f"PASS: graph nodes in HTML: {node_count}") |
| print(f"PASS: recorded scenarios: {scenario_count}") |
| print(f"PASS: exported nodes: {len(payload['nodes'])}") |
| print(f"PASS: exported edges: {len(payload['edges'])}") |
| print("PASS: GraphShieldMistral static Space package") |
|
|