mirror of
https://github.com/lukaszraczylo/traefik-llmgateway.git
synced 2026-10-18 05:47:38 +00:00
fix: config validation hardening and integration polish
- providers.go: validate provider names against ^[a-zA-Z0-9._-]+$ and reject reserved names (v1, mcp, a2a) that would shadow gateway routes; reject nil ProviderConfig map values at construction instead of panicking. - mcp_a2a.go: apply the same name-pattern and reserved-name validation to mcpServers/agents keys, and reject nil TargetConfig/AgentConfig map values at construction. - auth.go: reject nil GroupConfig map values at construction. - provider_openai_test.go, mcp_a2a_test.go, auth_test.go: table-driven constructor-error coverage for the above. - integration/integration_test.go: point the package doc at `make integration-up`/`integration-keep` instead of a bare `docker compose up -d --build`, which skips the dynamic.yml.tmpl render step and can run against a stale or absent dynamic.yml; add max_tokens to the gemini chat request so translate_gemini.go's hasMaxTokens=true branch is actually exercised under real Yaegi. - translate_gemini.go: soften the max_tokens comment's verification claim to match what the integration suite actually now exercises.
This commit is contained in:
@@ -657,4 +657,67 @@ func TestBuildAdapters(t *testing.T) {
|
||||
t.Fatalf("buildAdapters: want error for keyless gemini provider, got nil")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("bad: nil provider config value is a constructor error, not a panic", func(t *testing.T) {
|
||||
cfg := &Config{Providers: map[string]*ProviderConfig{"p1": nil}}
|
||||
if _, err := buildAdapters(cfg); err == nil {
|
||||
t.Fatalf("buildAdapters: want error for a nil ProviderConfig value, got nil")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// TestValidateConfigName covers validateConfigName's character-set and
|
||||
// reserved-name rules, shared by buildAdapters (provider names) and
|
||||
// validateTargetURLs (mcpServers/agents names, mcp_a2a_test.go) — a
|
||||
// provider, MCP server, or agent named "v1", "mcp", or "a2a" would shadow
|
||||
// one of the gateway's own fixed top-level routes.
|
||||
func TestValidateConfigName(t *testing.T) {
|
||||
tests := []struct {
|
||||
kind string
|
||||
name string
|
||||
wantErr bool
|
||||
}{
|
||||
{"provider", "openai", false},
|
||||
{"provider", "my-provider_1.local", false},
|
||||
{"provider", "openai grok", true}, // space
|
||||
{"provider", "openai/grok", true}, // slash: would break passthroughRoute's own path splitting
|
||||
{"provider", "", true}, // empty never matches configNamePattern
|
||||
{"provider", "v1", true}, // reserved: unified API namespace
|
||||
{"provider", "mcp", true}, // reserved: MCP target-proxy prefix
|
||||
{"provider", "a2a", true}, // reserved: A2A target-proxy prefix
|
||||
{"mcpServers", "v1", true}, // same reserved set applies to every kind
|
||||
{"agents", "a2a", true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.kind+"/"+tt.name, func(t *testing.T) {
|
||||
err := validateConfigName(tt.kind, tt.name)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("validateConfigName(%q, %q) error = %v, wantErr %v", tt.kind, tt.name, err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestBuildAdapters_RejectsReservedOrInvalidProviderName covers buildAdapters'
|
||||
// own call to validateConfigName, end to end through the map key rather than
|
||||
// validateConfigName in isolation.
|
||||
func TestBuildAdapters_RejectsReservedOrInvalidProviderName(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
}{
|
||||
{"v1"},
|
||||
{"mcp"},
|
||||
{"a2a"},
|
||||
{"has a space"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cfg := &Config{Providers: map[string]*ProviderConfig{
|
||||
tt.name: {Type: "openai", APIKey: "sk-test"},
|
||||
}}
|
||||
if _, err := buildAdapters(cfg); err == nil {
|
||||
t.Fatalf("buildAdapters: want error for provider name %q, got nil", tt.name)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user