Files
gpt-load/internal/state/runtime_settings_test.go
T

467 lines
15 KiB
Go

package state
import (
"encoding/json"
"reflect"
"strings"
"testing"
"time"
"gpt-load/internal/channel"
"gpt-load/internal/platform/config"
)
func TestCompilePublishesDefaultRuntimeSettingsWithoutGroups(t *testing.T) {
snapshot, err := Compile(CompileInput{})
if err != nil {
t.Fatal(err)
}
want := RuntimeSettings{
FirstByteTimeout: 120 * time.Second,
RequestTimeout: 600 * time.Second,
StreamIdleTimeout: 300 * time.Second,
HeaderRules: HeaderRules{Set: map[string]string{}},
InjectUsageOptions: true,
ValidationInterval: 10 * time.Minute,
RequestLogRetentionDays: 7,
ModelsDevAutoSyncEnabled: true,
}
if !reflect.DeepEqual(snapshot.Settings, want) {
t.Fatalf("Settings = %#v, want %#v", snapshot.Settings, want)
}
}
func TestModelsDevAutoSyncSettingDefaultsTrueAndIsSystemOnly(t *testing.T) {
defaults, err := ResolveRuntimeSettings(nil)
if err != nil {
t.Fatal(err)
}
if !defaults.ModelsDevAutoSyncEnabled {
t.Fatal("ModelsDevAutoSyncEnabled = false, want default true")
}
disabled, err := ResolveRuntimeSettings(config.Settings{
SettingModelsDevAutoSyncEnabled: false,
})
if err != nil {
t.Fatal(err)
}
if disabled.ModelsDevAutoSyncEnabled {
t.Fatal("ModelsDevAutoSyncEnabled = true, want persisted false")
}
if !IsRuntimeSettingKey(SettingModelsDevAutoSyncEnabled) {
t.Fatal("models_dev_auto_sync_enabled is not a public runtime setting")
}
if _, err := ResolveGroupRuntimeSettings(
defaults,
config.Settings{SettingModelsDevAutoSyncEnabled: false},
); err == nil {
t.Fatal("Group override accepted system-only models_dev_auto_sync_enabled")
}
}
func TestValidationIntervalDefaultsToTenMinutesAndIsSystemOnly(t *testing.T) {
defaults, err := ResolveRuntimeSettings(nil)
if err != nil {
t.Fatal(err)
}
if defaults.ValidationInterval != 10*time.Minute {
t.Fatalf("ValidationInterval = %v, want 10m", defaults.ValidationInterval)
}
overridden, err := ResolveRuntimeSettings(config.Settings{
SettingValidationInterval: json.Number("900"),
})
if err != nil {
t.Fatal(err)
}
if overridden.ValidationInterval != 15*time.Minute {
t.Fatalf("ValidationInterval = %v, want 15m", overridden.ValidationInterval)
}
if !IsRuntimeSettingKey(SettingValidationInterval) {
t.Fatal("validation_interval is not a public runtime setting")
}
if _, err := ResolveGroupRuntimeSettings(
defaults,
config.Settings{SettingValidationInterval: json.Number("900")},
); err == nil {
t.Fatal("Group override accepted system-only validation_interval")
}
}
func TestCompileRejectsInvalidGlobalSettingsWithoutGroups(t *testing.T) {
for _, input := range []config.Settings{
{"request_log_retention_days": json.Number("0")},
{"request_log_retention_days": json.Number("366")},
{"request_timeout": json.Number("1.5")},
{"unknown_public_setting": true},
} {
if _, err := Compile(CompileInput{SystemSettings: input}); err == nil {
t.Fatalf("Compile(%#v) error = nil", input)
}
}
}
func TestConnectTimeoutIsNotAPublicRuntimeSetting(t *testing.T) {
if IsRuntimeSettingKey("connect_timeout") {
t.Fatal("connect_timeout remains a public runtime setting")
}
if err := ValidateRuntimeSetting("connect_timeout", json.Number("15")); err == nil {
t.Fatal("connect_timeout was accepted even though the SDK cannot enforce it")
}
}
func TestCompileValidatesDisabledGroupSettings(t *testing.T) {
_, err := Compile(CompileInput{ChannelRegistry: channel.NewRegistry(), Groups: []GroupConfig{{
ID: 1, ChannelID: channel.OpenAI, Params: json.RawMessage(`{}`),
Settings: config.Settings{"request_log_retention_days": 30},
Enabled: false,
}}})
if err == nil || !strings.Contains(err.Error(), "unknown group setting") {
t.Fatalf("Compile() error = %v", err)
}
}
func TestResolveRuntimeSettingsRejectsPresentNullHeaderRules(t *testing.T) {
_, err := ResolveRuntimeSettings(config.Settings{SettingHeaderRules: nil})
if err == nil {
t.Fatal("ResolveRuntimeSettings() accepted present null header_rules")
}
}
func TestParseHeaderRulesRejectsUnsafeNames(t *testing.T) {
names := []string{
"Connection",
"proxy-connection",
"KEEP-ALIVE",
"te",
"Trailer",
"transfer-encoding",
"Upgrade",
"cookie",
"Cookie2",
"Proxy-Authorization",
"pRoXy-Custom",
}
for _, name := range names {
for _, section := range []string{"set", "remove"} {
values := []string{"ordinary"}
if section == "set" {
values = append(values, "Bearer ${API_KEY}")
}
for _, value := range values {
setting := map[string]any{}
if section == "set" {
setting[section] = map[string]any{name: value}
} else {
setting[section] = []any{name}
}
if err := ValidateRuntimeSetting(SettingHeaderRules, setting); err == nil {
t.Errorf("parseHeaderRules accepted unsafe %s %q", section, name)
}
}
}
}
}
func TestParseHeaderRulesRejectsReservedContentCodingNames(t *testing.T) {
for _, section := range []string{"set", "remove"} {
for _, name := range []string{
"Accept-Encoding",
"aCcEpT-eNcOdInG",
"Content-Encoding",
"cOnTeNt-EnCoDiNg",
} {
t.Run(section+"/"+name, func(t *testing.T) {
value := map[string]any{}
if section == "set" {
value[section] = map[string]any{name: "gzip"}
} else {
value[section] = []any{name}
}
if err := ValidateRuntimeSetting(SettingHeaderRules, value); err == nil {
t.Fatalf("parseHeaderRules accepted header_rules.%s reserved name %q", section, name)
}
})
}
}
}
func TestValidateRuntimeSettingRejectsDuplicateHeaderRuleIdentities(t *testing.T) {
tests := []struct {
name string
value map[string]any
}{
{
name: "duplicate remove names ignore ASCII case",
value: map[string]any{
"remove": []any{"X-Trace-ID", "x-trace-id"},
},
},
{
name: "set and remove names share one identity",
value: map[string]any{
"set": map[string]any{"X-Trace-ID": "trace"},
"remove": []any{"x-trace-id"},
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
if err := ValidateRuntimeSetting(SettingHeaderRules, test.value); err == nil {
t.Fatal("ValidateRuntimeSetting() accepted duplicate Header Rule identity")
}
})
}
}
func TestParseHeaderRulesRejectsSDKOwnedCredentialHeaders(t *testing.T) {
for _, name := range []string{
"Authorization",
"Api-Key",
"x-api-key",
"X-Goog-Api-Key",
} {
for _, value := range []string{"secret-canary", "Bearer ${API_KEY}"} {
err := ValidateRuntimeSetting(SettingHeaderRules, map[string]any{
"set": map[string]any{name: value},
})
if err == nil {
t.Errorf("parseHeaderRules accepted SDK-owned credential header %q", name)
}
}
if err := ValidateRuntimeSetting(SettingHeaderRules, map[string]any{
"remove": []any{name},
}); err == nil {
t.Errorf("parseHeaderRules accepted removal of SDK-owned credential header %q", name)
}
}
err := ValidateRuntimeSetting(SettingHeaderRules, map[string]any{
"set": map[string]any{
"Accept": "application/json",
"X-Custom": "ordinary",
"X-Custom-Auth": "Token ${API_KEY}",
},
})
if err != nil {
t.Errorf("parseHeaderRules rejected ordinary values: %v", err)
}
}
func TestResolveRuntimeSettingsAppliesSystemOverrides(t *testing.T) {
got, err := ResolveRuntimeSettings(config.Settings{
SettingFirstByteTimeout: json.Number("180"),
SettingRequestTimeout: json.Number("900"),
SettingStreamIdleTimeout: json.Number("45"),
SettingValidationInterval: json.Number("900"),
SettingHeaderRules: map[string]any{
"set": map[string]any{"x-test": "value"},
"remove": []any{"x-old"},
},
SettingRequestLogRetentionDays: json.Number("30"),
})
if err != nil {
t.Fatal(err)
}
if got.FirstByteTimeout != 180*time.Second ||
got.RequestTimeout != 900*time.Second ||
got.StreamIdleTimeout != 45*time.Second ||
got.ValidationInterval != 15*time.Minute ||
got.RequestLogRetentionDays != 30 {
t.Fatalf("settings = %#v", got)
}
wantRules := HeaderRules{Set: map[string]string{"X-Test": "value"}, Remove: []string{"X-Old"}}
if !reflect.DeepEqual(got.HeaderRules, wantRules) {
t.Fatalf("HeaderRules = %#v, want %#v", got.HeaderRules, wantRules)
}
}
func TestResolveGroupRuntimeSettingsUsesGroupPrecedence(t *testing.T) {
system, err := ResolveRuntimeSettings(config.Settings{
SettingRequestTimeout: json.Number("700"),
SettingHeaderRules: map[string]any{"set": map[string]any{"X-System": "system"}},
})
if err != nil {
t.Fatal(err)
}
resolved, err := ResolveGroupRuntimeSettings(system, config.Settings{
SettingFirstByteTimeout: json.Number("180"),
SettingHeaderRules: map[string]any{"set": map[string]any{"X-Group": "group"}},
})
if err != nil {
t.Fatal(err)
}
if resolved.Timeouts.Request != 700*time.Second || resolved.Timeouts.FirstByte != 180*time.Second {
t.Fatalf("timeouts = %#v", resolved.Timeouts)
}
wantRules := HeaderRules{Set: map[string]string{"X-Group": "group"}}
if !reflect.DeepEqual(resolved.HeaderRules, wantRules) {
t.Fatalf("rules = %#v, want replacement %#v", resolved.HeaderRules, wantRules)
}
}
func TestResolveGroupRuntimeSettingsRejectsPresentNullHeaderRules(t *testing.T) {
_, err := ResolveGroupRuntimeSettings(
DefaultRuntimeSettings(),
config.Settings{SettingHeaderRules: nil},
)
if err == nil {
t.Fatal("ResolveGroupRuntimeSettings() accepted present null header_rules")
}
}
func TestValidateRuntimeSettingBoundaries(t *testing.T) {
for _, value := range []any{json.Number("1"), json.Number("365")} {
if err := ValidateRuntimeSetting(SettingRequestLogRetentionDays, value); err != nil {
t.Fatalf("valid retention %v: %v", value, err)
}
}
for _, value := range []any{json.Number("0"), json.Number("366"), json.Number("1.5"), "7"} {
if err := ValidateRuntimeSetting(SettingRequestLogRetentionDays, value); err == nil {
t.Fatalf("invalid retention %v accepted", value)
}
}
}
func TestValidateRuntimeSettingAcceptsSupportedWholeNumberTypes(t *testing.T) {
for _, value := range []any{1, int64(365), float64(30), json.Number("3e1")} {
if err := ValidateRuntimeSetting(SettingRequestLogRetentionDays, value); err != nil {
t.Fatalf("valid retention %#v: %v", value, err)
}
}
}
func TestIsRuntimeSettingKeyRecognizesOnlyPublicRuntimeKeys(t *testing.T) {
for _, key := range []string{
SettingFirstByteTimeout,
SettingRequestTimeout,
SettingStreamIdleTimeout,
SettingHeaderRules,
SettingInjectUsageOptions,
SettingValidationInterval,
SettingRequestLogRetentionDays,
} {
if !IsRuntimeSettingKey(key) {
t.Errorf("IsRuntimeSettingKey(%q) = false", key)
}
}
for _, key := range []string{"", "retry_count", "_internal.bootstrap"} {
if IsRuntimeSettingKey(key) {
t.Errorf("IsRuntimeSettingKey(%q) = true", key)
}
}
}
func TestRuntimeSettingsOwnHeaderRuleCopies(t *testing.T) {
set := map[string]any{"X-Test": "original"}
remove := []any{"X-Old"}
got, err := ResolveRuntimeSettings(config.Settings{
SettingHeaderRules: map[string]any{"set": set, "remove": remove},
})
if err != nil {
t.Fatal(err)
}
set["X-Test"] = "mutated"
remove[0] = "X-Mutated"
if got.HeaderRules.Set["X-Test"] != "original" || got.HeaderRules.Remove[0] != "X-Old" {
t.Fatalf("HeaderRules changed with input: %#v", got.HeaderRules)
}
}
func TestResolveGroupRuntimeSettingsOwnsSystemHeaderRuleCopy(t *testing.T) {
system, err := ResolveRuntimeSettings(config.Settings{
SettingHeaderRules: map[string]any{
"set": map[string]any{"X-System": "system"},
"remove": []any{"X-Old"},
},
})
if err != nil {
t.Fatal(err)
}
resolved, err := ResolveGroupRuntimeSettings(system, nil)
if err != nil {
t.Fatal(err)
}
system.HeaderRules.Set["X-System"] = "mutated"
system.HeaderRules.Remove[0] = "X-Mutated"
if resolved.HeaderRules.Set["X-System"] != "system" || resolved.HeaderRules.Remove[0] != "X-Old" {
t.Fatalf("group rules changed with system settings: %#v", resolved.HeaderRules)
}
}
func TestDefaultRuntimeSettingsInjectUsageOptions(t *testing.T) {
if !DefaultRuntimeSettings().InjectUsageOptions {
t.Fatal("default inject_usage_options = false, want true")
}
}
func TestResolveRuntimeSettingsInjectUsageOptionsRequiresBoolean(t *testing.T) {
for _, value := range []any{true, false} {
got, err := ResolveRuntimeSettings(config.Settings{SettingInjectUsageOptions: value})
if err != nil || got.InjectUsageOptions != value {
t.Fatalf("ResolveRuntimeSettings(%#v) = %#v, %v", value, got, err)
}
}
for _, value := range []any{0, 1, "true", nil, []any{}, map[string]any{}} {
if _, err := ResolveRuntimeSettings(config.Settings{SettingInjectUsageOptions: value}); err == nil {
t.Fatalf("ResolveRuntimeSettings(%#v) accepted non-boolean", value)
}
}
}
func TestResolveGroupRuntimeSettingsInjectUsagePrecedence(t *testing.T) {
tests := []struct {
name string
system config.Settings
group config.Settings
want bool
}{
{name: "default", want: true},
{name: "system false", system: config.Settings{SettingInjectUsageOptions: false}, want: false},
{name: "group true", system: config.Settings{SettingInjectUsageOptions: false}, group: config.Settings{SettingInjectUsageOptions: true}, want: true},
{name: "group false", system: config.Settings{SettingInjectUsageOptions: true}, group: config.Settings{SettingInjectUsageOptions: false}, want: false},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
base, err := ResolveRuntimeSettings(test.system)
if err != nil {
t.Fatal(err)
}
resolved, err := ResolveGroupRuntimeSettings(base, test.group)
if err != nil || resolved.InjectUsageOptions != test.want {
t.Fatalf("ResolveGroupRuntimeSettings() = %#v, %v; want %t", resolved, err, test.want)
}
})
}
for _, value := range []any{nil, 0, 1, "true", []any{}, map[string]any{}} {
if _, err := ResolveGroupRuntimeSettings(DefaultRuntimeSettings(), config.Settings{SettingInjectUsageOptions: value}); err == nil {
t.Fatalf("ResolveGroupRuntimeSettings(%#v) accepted non-boolean", value)
}
}
}
func TestResolvedGroupSettingsOwnsHeaderRuleCopies(t *testing.T) {
base, err := ResolveRuntimeSettings(config.Settings{
SettingHeaderRules: map[string]any{"set": map[string]any{"X-System": "system"}, "remove": []any{"X-Old"}},
})
if err != nil {
t.Fatal(err)
}
first, err := ResolveGroupRuntimeSettings(base, nil)
if err != nil {
t.Fatal(err)
}
first.HeaderRules.Set["X-System"] = "changed"
first.HeaderRules.Remove[0] = "X-Changed"
second, err := ResolveGroupRuntimeSettings(base, nil)
if err != nil {
t.Fatal(err)
}
if base.HeaderRules.Set["X-System"] != "system" || base.HeaderRules.Remove[0] != "X-Old" ||
second.HeaderRules.Set["X-System"] != "system" || second.HeaderRules.Remove[0] != "X-Old" {
t.Fatalf("header rules aliased: base=%#v second=%#v", base.HeaderRules, second.HeaderRules)
}
}