Files
CASAN/packages/casan-harness/tests/core-local-viewer-artifact-tests.sh
T

183 lines
5.7 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
# Proves that the release artifact—not the source checkout—contains a complete,
# offline, read-only Local Assurance Viewer.
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)"
WORK="$(mktemp -d)"
DIST="$WORK/dist"
PROJECT="$WORK/clean-project"
VIEWER_STARTED=0
cleanup() {
if [[ "$VIEWER_STARTED" == "1" && -n "${CASAN_BIN:-}" ]]; then
CASAN_APP_ROOT="$PROJECT" "$CASAN_BIN" dashboard stop >/dev/null 2>&1 || true
fi
rm -rf "$WORK"
}
trap cleanup EXIT
CASAN_DIST_DIR="$DIST" bash "$ROOT/scripts/package-release.sh" core >/dev/null
ARTIFACT="$DIST/casan-core-v$(cat "$ROOT/VERSION").tar.gz"
tar -xzf "$ARTIFACT" -C "$WORK"
BUNDLE="$WORK/casan-core-v$(cat "$ROOT/VERSION")"
CASAN_BIN="$BUNDLE/bin/casan"
test -f "$BUNDLE/packages/casan-harness/assets/local-viewer/index.html"
test -f "$BUNDLE/packages/casan-harness/scripts/python/local_report.py"
test -f "$BUNDLE/packages/casan-harness/scripts/python/local_viewer.py"
test ! -e "$BUNDLE/packages/casan-control-panel"
test ! -e "$BUNDLE/package.json"
mkdir -p \
"$PROJECT/.casan" \
"$PROJECT/.specify/state" \
"$PROJECT/.specify/logs/trace" \
"$PROJECT/.specify/logs/trace-events" \
"$PROJECT/.specify/logs/cost"
python3 - "$PROJECT" <<'PY'
import json
from pathlib import Path
import sys
root = Path(sys.argv[1])
(root / ".casan/config.json").write_text(json.dumps({
"schema_version": "21.3",
"project_id": "artifact-project",
"project_name": "Artifact Project",
"edition": "core",
"enforcement_mode": "enforce",
}), encoding="utf-8")
trace_id = "artifact-run-1"
trace = {
"schema_version": "20.1",
"trace_id": trace_id,
"project_id": "artifact-project",
"certified": True,
"certification_strength": "project_hook",
"finalized_at": "2026-07-28T10:00:07Z",
"tool_calls": 1,
"failures": 0,
"evidence": [
{"h": f"H{i}", "at": f"2026-07-28T10:00:0{i}Z", "decision": "pass", "kind": "gate", "detail": f"H{i} passed"}
for i in range(1, 8)
],
}
(root / f".specify/logs/trace/agentic-{trace_id}.json").write_text(
json.dumps(trace), encoding="utf-8")
metric = {
"timestamp": "2026-07-28T10:00:06Z",
"trace_id": trace_id,
"project_id": "artifact-project",
"harness": "H6-agentic",
"step": "agentic-turn",
"status": "success",
"latency_ms": 812,
"retry_count": 0,
"input_tokens": None,
"output_tokens": None,
"total_tokens": None,
"cost_estimate": None,
"telemetry_quality": "insufficient",
"alerts": [],
}
(root / ".specify/logs/cost/metrics.jsonl").write_text(
json.dumps(metric) + "\n", encoding="utf-8")
(root / ".specify/state/latest-run.json").write_text(json.dumps({
"schema_version": 1,
"trace_id": trace_id,
"project_id": "artifact-project",
"certified": True,
"telemetry_quality": "insufficient",
"duration_ms": 812,
"tool_calls": 1,
"failures": 0,
"finalized_at": "2026-07-28T10:00:07Z",
}), encoding="utf-8")
PY
(
cd "$PROJECT"
CASAN_APP_ROOT="$PROJECT" "$CASAN_BIN" report latest | grep -q "view_command=casan view artifact-run-1"
CASAN_APP_ROOT="$PROJECT" "$CASAN_BIN" report export artifact-run-1 \
--format html --output "$WORK/run.html" >/dev/null
CASAN_APP_ROOT="$PROJECT" "$CASAN_BIN" report export --h6 \
--format json --output "$WORK/h6.json" >/dev/null
CASAN_APP_ROOT="$PROJECT" "$CASAN_BIN" report export --h6 \
--format html --output "$WORK/h6.html" >/dev/null
)
grep -q "Independent assurance dossier" "$WORK/run.html"
grep -q "H6 · AgentOps Report" "$WORK/h6.html"
python3 - "$WORK/h6.json" <<'PY'
import json, sys
d = json.load(open(sys.argv[1], encoding="utf-8"))
assert d["harness"] == "H6"
assert d["summary"]["runs"] == 1
assert d["summary"]["tokens"]["total"] is None
assert d["summary"]["cost_usd"]["estimated"] is None
PY
URL="$(CASAN_APP_ROOT="$PROJECT" "$CASAN_BIN" view artifact-run-1 --no-open)"
VIEWER_STARTED=1
python3 - "$URL" <<'PY'
import json
import sys
from urllib.error import HTTPError
from urllib.parse import parse_qs, urlparse
from urllib.request import Request, urlopen
url = urlparse(sys.argv[1])
token = parse_qs(url.query)["token"][0]
base = f"{url.scheme}://{url.netloc}"
def get(path):
request = Request(base + path, headers={"X-CASAN-Viewer-Token": token})
return json.loads(urlopen(request, timeout=3).read())
project = get("/api/v1/project")
assert project["project_id"] == "artifact-project"
assert project["viewer"] == {
"mode": "local_core",
"scope": "single_project",
"read_only": True,
"network": "loopback_only",
}
run = get("/api/v1/reports/run/artifact-run-1")
assert run["verdict"] == "certified"
assert run["summary"]["gates_observed"] == 7
h6 = get("/api/v1/reports/h6?run=artifact-run-1")
assert h6["summary"]["runs"] == 1
try:
urlopen(Request(base + "/api/v1/project"), timeout=3)
raise AssertionError("API accepted a request without the session token")
except HTTPError as error:
assert error.code == 401
try:
urlopen(Request(
base + "/api/v1/project",
method="POST",
headers={"X-CASAN-Viewer-Token": token},
), timeout=3)
raise AssertionError("read-only viewer accepted POST")
except HTTPError as error:
assert error.code == 405
try:
urlopen(Request(
base + "/api/v1/project",
headers={"X-CASAN-Viewer-Token": token, "Host": "attacker.example"},
), timeout=3)
raise AssertionError("viewer accepted a non-loopback Host header")
except HTTPError as error:
assert error.code == 403
PY
CASAN_APP_ROOT="$PROJECT" "$CASAN_BIN" dashboard status | grep -q '"mode": "local_core"'
CASAN_APP_ROOT="$PROJECT" "$CASAN_BIN" dashboard stop | grep -q "CASAN_VIEW_STOPPED"
VIEWER_STARTED=0
echo "PASS: Core release artifact provides offline visual reports without Platform or Node"