#!/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()