feat: standardize transport contracts across Gin and nethttp

This commit is contained in:
2026-09-21 00:10:44 +08:00
parent affd02d5f7
commit 5561795d80
22 changed files with 1137 additions and 205 deletions

View File

@@ -145,7 +145,7 @@ class Server:
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--go", default="go")
parser.add_argument("--version", default="v0.2.0")
parser.add_argument("--version", default="v0.3.0")
parser.add_argument("--cli", help="Use a locally built CLI")
parser.add_argument("--framework-dir", help="Local framework source for pre-release checks")
parser.add_argument("--image", default="mysql:8.4")

View File

@@ -91,7 +91,7 @@ def exerciseServer(binary, directory, env, stopSignal, cleanupMarker=None):
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--go", default="go", help="Go executable")
parser.add_argument("--version", default="v0.2.0", help="Released CLI version to install")
parser.add_argument("--version", default="v0.3.0", help="Released CLI version to install")
parser.add_argument("--cli", help="Use a locally built CLI instead of installing")
parser.add_argument("--framework-version", help="Explicit version passed to the CLI")
parser.add_argument("--framework-dir", help="Local checkout for pre-release verification")

View File

@@ -0,0 +1,66 @@
#!/usr/bin/env python3
"""Run the public adapter contract from an independent consumer module."""
import argparse
import json
import os
from pathlib import Path
import tempfile
from verifyScaffold import MODULE, run
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--go", default="go")
parser.add_argument("--version", default="v0.3.0")
parser.add_argument("--framework-dir", help="Use a local checkout before release")
args = parser.parse_args()
env = dict(os.environ, GOWORK="off")
env["GOPRIVATE"] = ",".join(filter(None, [env.get("GOPRIVATE", ""), "git.yuntuo.hk"]))
with tempfile.TemporaryDirectory(prefix="go omni transport ") as temporary:
directory = Path(temporary).resolve()
module = f"module example.com/adaptercheck\n\ngo 1.22\n\nrequire {MODULE} {args.version}\n"
if args.framework_dir:
module += f"\nreplace {MODULE} => {json.dumps(str(Path(args.framework_dir).resolve()), ensure_ascii=False)}\n"
(directory / "go.mod").write_text(module)
(directory / "adapter_test.go").write_text(f'''package adaptercheck_test
import (
"testing"
"{MODULE}/httpx"
ginrouter "{MODULE}/transport/gin"
netrouter "{MODULE}/transport/nethttp"
"{MODULE}/transport/transporttest"
)
func TestContract(t *testing.T) {{
t.Run("gin", func(t *testing.T) {{
transporttest.Run(t, func() httpx.Router {{ return ginrouter.New("test") }})
}})
t.Run("nethttp", func(t *testing.T) {{
transporttest.Run(t, func() httpx.Router {{ return netrouter.New() }})
}})
}}
''')
source = "local checkout" if args.framework_dir else f"remote {args.version}"
print(f"Testing {source} in an independent consumer module", flush=True)
run([args.go, "mod", "tidy"], directory, env)
if not args.framework_dir and "replace " in (directory / "go.mod").read_text():
raise AssertionError("remote verification must not contain replace")
output = run([args.go, "test", "-race", "-count=1", "-json", "./..."], directory, env)
events = [json.loads(line) for line in output.splitlines() if line.startswith("{")]
if any(event.get("Action") == "skip" for event in events):
raise AssertionError("contract suite unexpectedly skipped tests")
for adapter in ("gin", "nethttp"):
name = f"TestContract/{adapter}"
if not any(event.get("Action") == "pass" and event.get("Test") == name for event in events):
raise AssertionError(f"{adapter} contract did not pass")
count = sum(event.get("Action") == "pass" and event.get("Test", "").startswith(name + "/")
for event in events)
print(f"PASS {adapter}: {count} contract checks, including business handlers, cancellation and concurrent requests", flush=True)
print("PASS independent module: both adapters accepted by the same public contract suite", flush=True)
if __name__ == "__main__":
main()