Files
go-omni/scripts/verifyWebsocketRelease.py

92 lines
5.9 KiB
Python

#!/usr/bin/env python3
"""Verify a fixed WebSocket release and generated routes from an empty module cache."""
import argparse
import hashlib
import json
import os
from pathlib import Path
import re
import tempfile
import time
from verifyErrorInheritanceRelease import run
ROOT = Path(__file__).resolve().parents[1]
MODULE = "git.yuntuo.hk/k2safe/go-omni"
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--version", required=True)
parser.add_argument("--report", type=Path, default=ROOT / "reports/websocket-remote.json")
args = parser.parse_args()
if not re.fullmatch(r"v\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?", args.version):
parser.error("a fixed release version is required")
env = {k: v for k, v in os.environ.items() if not k.startswith("GO_OMNI_")}
env.update(GOWORK="off", GOFLAGS="", GOPRIVATE="git.yuntuo.hk", GONOSUMDB="git.yuntuo.hk")
started = time.monotonic()
with tempfile.TemporaryDirectory(prefix="goOmniWebsocketRelease-") as directory:
temp = Path(directory)
(temp / "bin").mkdir()
env.update(GOBIN=str(temp / "bin"), GOMODCACHE=str(temp / "modcache"))
print(f"Installing remote CLI {args.version} in an empty cache", flush=True)
run(["go", "install", MODULE + "/cmd/go-omni@" + args.version], temp, env)
cli = temp / "bin/go-omni"
starter = temp / "starter"
# Verify the installed default, without --framework-version or a local replace.
run([cli, "new", "--module", "example.com/websocketRelease", starter], temp, env)
run(["go", "mod", "tidy"], starter, env)
dependency = json.loads(run(["go", "list", "-m", "-json", MODULE], starter, env))
assert dependency["Version"] == args.version and dependency.get("Sum") and not dependency.get("Replace")
downloaded = Path(dependency["Dir"])
# Add the optional WS dependency only after checking the CLI's default.
run(["go", "get", MODULE + "/ws@" + args.version], starter, env)
print("Testing downloaded WS, Hub, RPC, observation and App integration with race detection", flush=True)
output = run(["go", "test", "-mod=mod", "-race", "-count=1", "-json", "-timeout=180s",
MODULE + "/ws/...", MODULE + "/examples/websocketRPC"], starter, env)
events = [json.loads(line) for line in output.splitlines() if line.startswith("{")]
packages = sorted({e["Package"] for e in events if e.get("Action") == "pass" and "Test" not in e})
expected = [MODULE + "/ws" + suffix for suffix in ("", "/hub", "/observe", "/omni", "/rpc")]
assert set(expected + [MODULE + "/examples/websocketRPC"]) == set(packages), output
tests = sorted(e["Package"] + ":" + e["Test"] for e in events
if e.get("Action") == "pass" and "Test" in e and "/" not in e["Test"])
echo = run(["go", "run", "-mod=mod", MODULE + "/examples/websocket"], starter, env)
assert "hello websocket" in echo and "hello group" in echo, echo
rpc = run(["go", "run", "-mod=mod", MODULE + "/examples/websocketRPC"], starter, env)
for generation in (1, 2):
assert f'connection {generation}: double=42, remote callback="generic Go client", restored=1, event={generation}' in rpc, rpc
env.update(GO_OMNI_SCAFFOLD_INTEGRATION="1", GO_OMNI_RELEASE_CLI=str(cli), GO_OMNI_RELEASE_VERSION=args.version)
print("Testing generated custom routes and failure cleanup against the fixed remote release", flush=True)
output = run(["go", "test", "-race", "-count=1", "-json", "-timeout=180s", "./cmd/go-omni/apigen",
"-run", "^TestGeneratedCustomRoutesConsumer$"], downloaded, env)
events = [json.loads(line) for line in output.splitlines() if line.startswith("{")]
assert any(e.get("Action") == "pass" and e.get("Test") == "TestGeneratedCustomRoutesConsumer" for e in events), output
evidence = "".join(e.get("Output", "") for e in events)
assert f'checksum {dependency["Sum"]}; no local replace' in evidence, evidence
final_dependency = json.loads(run(["go", "list", "-m", "-json", MODULE], starter, env))
assert final_dependency["Version"] == args.version and final_dependency["Sum"] == dependency["Sum"]
assert not final_dependency.get("Replace")
report = {
"passed": True, "frameworkVersion": args.version, "frameworkSum": dependency["Sum"],
"toolchain": run(["go", "version"], temp, env).strip(),
"freshModuleCache": True, "noLocalReplace": True,
"durationSeconds": round(time.monotonic() - started, 2),
"packages": packages, "tests": tests,
"checks": ["remoteCLIInstall", "installedVersionDefault", "fixedRemoteModuleChecksum",
"downloadedWebsocketRaceTests", "remoteEchoAndGroupExample", "remoteBidirectionalRPCExample",
"remoteTwoConnectionSubscriptionRestore", "generatedCustomRoutesConsumer",
"oldServiceWithoutHook", "handwrittenHookPreserved", "customRouteFailureCleanup"],
"examples": {"websocket": echo.strip(), "websocketRPC": rpc.strip()},
"sourceHashes": {name: hashlib.sha256((downloaded / name).read_bytes()).hexdigest() for name in
("scripts/verifyWebsocketRelease.py", "cmd/go-omni/apigen/customRoutes_test.go")},
"limitations": ["Functional release acceptance; not a vulnerability scan or browser UI test.",
"Known framework HTTP/2 dependency vulnerabilities remain documented in the validation report."],
}
args.report.parent.mkdir(parents=True, exist_ok=True)
args.report.write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n")
print(f"PASS remote WebSocket release {args.version}; temporary files removed", flush=True)
if __name__ == "__main__":
main()