以下保留檔案原文,沒有改寫資料或結果。可使用「複製原始內容」取得完整檔案。
"""Run green → two expected business failures → restored green; all local."""
import argparse
from datetime import datetime, timezone
import hashlib
import json
from pathlib import Path
import platform
import subprocess
import sys
ROOT = Path(__file__).resolve().parent
SCENARIOS = [
("baseline", "none", set()),
("fault-duplicate-write", "duplicate-write", {"BE-PAID-REJECT", "BE-CONCURRENT"}),
("fault-wrong-error", "wrong-error", {"BE-PAID-REJECT"}),
("restored", "none", set()),
]
def main(output):
output.mkdir(parents=True, exist_ok=True)
manifest = {"kind": "local-teaching-execution", "product_enabled": False,
"generated_at": datetime.now(timezone.utc).isoformat(),
"python": platform.python_version(), "tested_surface": "HTTP API + SQLite",
"not_tested": ["FE DOM and keyboard", "BE + FE integration", "payment provider", "production deployment"],
"sources": {}, "runs": []}
files = [*ROOT.glob("demo_app/*.py"), *ROOT.glob("independent/*.py"), ROOT / "run_demo.py",
ROOT / "acceptance-plan.json", ROOT / "api-contract.json", ROOT / "acceptance.feature",
ROOT / "product-spec.md", ROOT / "product.gitlab-ci.yml"]
for source in sorted(files):
manifest["sources"][str(source.relative_to(ROOT))] = hashlib.sha256(source.read_bytes()).hexdigest()
valid = True
for name, fault, expected_failures in SCENARIOS:
report = output / (name + ".xml")
process = subprocess.run([
sys.executable, "-m", "independent.harness", "--fault", fault, "--junit", str(report.resolve()),
], cwd=ROOT, text=True, capture_output=True)
try:
summary = json.loads(process.stdout)
except json.JSONDecodeError:
print(process.stdout + process.stderr, file=sys.stderr)
return 1
expected_exit = 1 if expected_failures else 0
matches = (process.returncode == expected_exit and set(summary["failures"]) == expected_failures
and not summary["errors"] and not summary["skipped"] and summary["tests"] == 5)
valid = valid and matches
manifest["runs"].append({"name": name, "runner_exit": process.returncode,
"expected_exit": expected_exit, "expected_result_verified": matches,
"junit": report.name,
"junit_sha256": hashlib.sha256(report.read_bytes()).hexdigest(), **summary})
print(f"{name}: runner exit {process.returncode}; {summary['passed']}/5 passed; "
f"expected result {'verified' if matches else 'NOT verified'}")
manifest["demo_verified"] = valid
(output / "execution-manifest.json").write_text(
json.dumps(manifest, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
return 0 if valid else 1
if __name__ == "__main__":
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--output", type=Path, default=ROOT / "reports")
raise SystemExit(main(parser.parse_args().output.resolve()))