package main import ( "net" "net/http" "net/http/httptest" "os/exec" "strings" "testing" ) // spec ยง17 step 6 (codex R4-3, plan-R1 4): the gate fails on a 404 and on a // version mismatch, and a proxy in the environment cannot route around the // edge it names. func TestRegionServiceGate(t *testing.T) { const version = "abc1234" ok := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("X-Carlos-Version", version) })) defer ok.Close() unknown := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { http.NotFound(w, r) })) defer unknown.Close() hostPort := func(s *httptest.Server) (string, string) { h, p, _ := net.SplitHostPort(s.Listener.Addr().String()) return h, p } run := func(srv *httptest.Server, want string, extraEnv ...string) (string, error) { h, p := hostPort(srv) cmd := exec.Command("./scripts/region-service-gate.sh", "probe.example", want, h) cmd.Env = append(cmd.Environ(), append([]string{"GATE_PORT=" + p, "GATE_INSECURE=1", "GATE_SKIP_PUBLIC=1"}, extraEnv...)...) out, err := cmd.CombinedOutput() return string(out), err } if out, err := run(ok, version); err != nil { t.Fatalf("a serving edge failed the gate: %v %s", err, out) } if out, err := run(unknown, version); err == nil || !strings.Contains(out, "FAIL") { t.Fatalf("an unknown-host 404 passed: %s", out) } if out, err := run(ok, "other"); err == nil || !strings.Contains(out, "FAIL") { t.Fatalf("a version mismatch passed: %s", out) } // A proxy that refuses everything: honoured, it would fail every check. dead := "http://127.0.0.1:9" if out, err := run(ok, version, "HTTPS_PROXY="+dead, "https_proxy="+dead, "ALL_PROXY="+dead, "all_proxy="+dead); err != nil { t.Fatalf("the gate went through a proxy: %v %s", err, out) } } // Review fix round 1: the gate judges the final response only. The status is // the final status line, not any header that starts with "HTTP", and // X-Carlos-Version is read from the final response's header block, not from // an informational response ahead of it. func TestRegionServiceGateJudgesTheFinalResponse(t *testing.T) { const version = "abc1234" gate := func(h http.HandlerFunc) (string, error) { srv := httptest.NewTLSServer(h) defer srv.Close() host, port, _ := net.SplitHostPort(srv.Listener.Addr().String()) cmd := exec.Command("./scripts/region-service-gate.sh", "probe.example", version, host) cmd.Env = append(cmd.Environ(), "GATE_PORT="+port, "GATE_INSECURE=1", "GATE_SKIP_PUBLIC=1") out, err := cmd.CombinedOutput() return string(out), err } // A 500 that carries a header looking like a status line and the right version. if out, err := gate(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("X-Carlos-Version", version) w.Header().Set("HTTP-Debug", "200") w.WriteHeader(http.StatusInternalServerError) }); err == nil || !strings.Contains(out, "FAIL") { t.Errorf("a 500 with an HTTP-Debug header passed: %s", out) } // 103 Early Hints carries the version; the final 200 does not. if out, err := gate(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("X-Carlos-Version", version) w.WriteHeader(http.StatusEarlyHints) w.Header().Del("X-Carlos-Version") w.WriteHeader(http.StatusOK) }); err == nil || !strings.Contains(out, "FAIL") { t.Errorf("a version only on the 103 passed: %s", out) } // Two version headers in the final response: an empty one and the right // one. A duplicate fails whatever either value is. if out, err := gate(func(w http.ResponseWriter, r *http.Request) { w.Header()["X-Carlos-Version"] = []string{"", version} w.WriteHeader(http.StatusOK) }); err == nil || !strings.Contains(out, "FAIL") { t.Errorf("an empty and a correct version header on the final 200 passed: %s", out) } // The same duplicate with identical values is still a duplicate. if out, err := gate(func(w http.ResponseWriter, r *http.Request) { w.Header()["X-Carlos-Version"] = []string{version, version} w.WriteHeader(http.StatusOK) }); err == nil || !strings.Contains(out, "FAIL") { t.Errorf("two identical version headers on the final 200 passed: %s", out) } // Both blocks carry the version: the final one is right, so it passes. if out, err := gate(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("X-Carlos-Version", version) w.WriteHeader(http.StatusEarlyHints) w.WriteHeader(http.StatusOK) }); err != nil { t.Errorf("a 103 and a 200 that both carry the version failed: %v %s", err, out) } }