Files
gateway/cmd/gateway/main_test.go
Abhishek Krishna fbbeb606bf feat(gateway): KMS routes loader + configurable graceful-shutdown timeout
Closes the two PARTIAL/TODO lines that docs/SCALE_AUDIT.md flagged
after HIP-0110 landed:

1. router_engine.go — replace the TODO KMS stub in loadRoutesFromEnv
   with a pluggable KMSResolver interface (router_kms.go). The default
   resolver is a noop; an HTTP resolver auto-wires when
   GATEWAY_KMS_ENDPOINT + IAM_CLIENT_ID/SECRET (or the GATEWAY_KMS_*
   overrides) are present. The HTTP contract matches the one in
   hanzoai/base/plugins/platform/kms.go: Universal-Auth login, then
   GET /api/v3/secrets/raw/<path>, read .secret.secretValue. File
   loader path (GATEWAY_ROUTES_FILE → ./routes.yaml) is unchanged and
   remains the fallback when GATEWAY_ROUTES_KMS_PATH is unset.

2. cmd/gateway/main.go — make the existing 30s shutdown deadline
   operator-configurable via GATEWAY_SHUTDOWN_TIMEOUT (any
   time.ParseDuration string; falls back to default on empty /
   malformed / non-positive values). Surgical change: signal handling
   and the ShutdownWithContext drain that HIP-0110 introduced are
   untouched.

Tests:
- router_engine_kms_test.go — resolver injection, error surfacing,
  malformed-YAML guard, file fallback, HTTP round-trip stub
  (mimics the Universal-Auth + secrets/raw contract).
- cmd/gateway/main_test.go — shutdownGraceFromEnv table test +
  envOr smoke test. SIGTERM-triggered drain itself is exercised by
  deployment smoke tests (not safe to interrupt the in-process Go
  test runner with SIGTERM).

SCALE_AUDIT.md: graceful-shutdown row PARTIAL → PASS, new KMS routes
row added (PASS), with a post-HIP-0110 update section.

Verification:
- go vet ./...   clean
- go build ./... clean
- go test . -count=1 -short: 96/98 PASS (two pre-existing failures
  in config_loader_test.go::TestPortFromEnv* are unrelated to this
  change, repro on master)
- go test ./cmd/gateway/ -count=1 -short: 2/2 PASS
2026-06-01 11:17:42 +00:00

67 lines
1.8 KiB
Go

// Copyright © 2026 Hanzo AI. MIT License.
package main
import (
"os"
"testing"
"time"
)
// TestShutdownGraceFromEnv verifies the GATEWAY_SHUTDOWN_TIMEOUT parsing
// helper. The full SIGTERM-triggered drain path is exercised by deployment
// smoke tests (not safe to interrupt the in-process Go test runner with
// SIGTERM), so this isolated unit test guarantees the helper's contract:
// env-driven override + safe fallback on malformed/zero/negative values.
func TestShutdownGraceFromEnv(t *testing.T) {
def := 30 * time.Second
cases := []struct {
name string
env string
want time.Duration
}{
{"unset", "", def},
{"valid_seconds", "45s", 45 * time.Second},
{"valid_minutes", "2m", 2 * time.Minute},
{"malformed", "not-a-duration", def},
{"zero_rejected", "0s", def},
{"negative_rejected", "-5s", def},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
prev, hadPrev := os.LookupEnv("GATEWAY_SHUTDOWN_TIMEOUT")
t.Cleanup(func() {
if hadPrev {
os.Setenv("GATEWAY_SHUTDOWN_TIMEOUT", prev)
} else {
os.Unsetenv("GATEWAY_SHUTDOWN_TIMEOUT")
}
})
if tc.env == "" {
os.Unsetenv("GATEWAY_SHUTDOWN_TIMEOUT")
} else {
os.Setenv("GATEWAY_SHUTDOWN_TIMEOUT", tc.env)
}
got := shutdownGraceFromEnv(def)
if got != tc.want {
t.Fatalf("env=%q: want %v got %v", tc.env, tc.want, got)
}
})
}
}
// TestEnvOr smoke-tests the envOr helper since the file is otherwise
// shutdown-focused and a green build benefits from a second covered helper.
func TestEnvOr(t *testing.T) {
const key = "GATEWAY_TEST_ENV_OR_UNIQUE"
os.Unsetenv(key)
if got := envOr(key, "fallback"); got != "fallback" {
t.Fatalf("unset: want fallback, got %q", got)
}
os.Setenv(key, "set")
t.Cleanup(func() { os.Unsetenv(key) })
if got := envOr(key, "fallback"); got != "set" {
t.Fatalf("set: want set, got %q", got)
}
}