mirror of
https://github.com/Dvorinka/Containr.git
synced 2026-10-28 07:41:27 +00:00
The admin surface under /admin/gateway managed api_services and api_keys, but no public route ever consumed them. Mounts /g/:slug and /g/:slug/* on the root router (outside /api/v1 so route_prefix stays clean) and runs the full pipeline: slug lookup -> X-API-Key/Bearer extraction -> prefix-indexed bcrypt key auth -> allowed_service_ids scope -> per-minute buckets (key and service caps) -> monthly usage_counters quotas -> gateway.ProxyRequest with per-service timeout and upstream auth headers -> usage/metrics/incident recording. Live-verified: proxied GET/POST, 401/404/429 boundaries, counter upserts, UPSTREAM_5XX incidents. Unit tests cover URL building, the rpm bucket, key extraction, and the service allowlist. Generated with [Devin](https://devin.ai) Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
65 lines
1.6 KiB
Go
65 lines
1.6 KiB
Go
package gateway
|
|
|
|
import "testing"
|
|
|
|
func TestBuildTargetURL(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
upstream, prefix, path, query, want string
|
|
}{
|
|
{
|
|
name: "strips route prefix",
|
|
upstream: "https://api.internal:8080",
|
|
prefix: "/g/weather",
|
|
path: "/g/weather/v1/current",
|
|
query: "city=oslo",
|
|
want: "https://api.internal:8080/v1/current?city=oslo",
|
|
},
|
|
{
|
|
name: "prefix root maps to upstream root",
|
|
upstream: "https://api.internal",
|
|
prefix: "/g/weather",
|
|
path: "/g/weather",
|
|
want: "https://api.internal/",
|
|
},
|
|
{
|
|
name: "upstream base path is preserved",
|
|
upstream: "https://api.internal/base",
|
|
prefix: "/g/x",
|
|
path: "/g/x/ping",
|
|
want: "https://api.internal/base/ping",
|
|
},
|
|
{
|
|
name: "root prefix leaves path untouched",
|
|
upstream: "https://api.internal",
|
|
prefix: "/",
|
|
path: "/g/raw",
|
|
want: "https://api.internal/g/raw",
|
|
},
|
|
{
|
|
name: "path without prefix is still proxied",
|
|
upstream: "https://api.internal",
|
|
prefix: "/g/x",
|
|
path: "/other",
|
|
want: "https://api.internal/other",
|
|
},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
got, err := BuildTargetURL(tc.upstream, tc.prefix, tc.path, tc.query)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if got != tc.want {
|
|
t.Fatalf("got %q, want %q", got, tc.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestBuildTargetURL_InvalidUpstream(t *testing.T) {
|
|
if _, err := BuildTargetURL("://bad", "/g/x", "/g/x/y", ""); err == nil {
|
|
t.Fatal("expected error for unparsable upstream")
|
|
}
|
|
}
|