| package model |
|
|
| import ( |
| "testing" |
| "time" |
| ) |
|
|
| func TestModelEntry_Validate(t *testing.T) { |
| t.Parallel() |
|
|
| t.Run("trim_and_accept", func(t *testing.T) { |
| entry := &ModelEntry{Model: " gpt-4 ", RedirectModel: " "} |
| if err := entry.Validate(); err != nil { |
| t.Fatalf("Validate() unexpected error: %v", err) |
| } |
| if entry.Model != "gpt-4" { |
| t.Fatalf("Model not trimmed: %q", entry.Model) |
| } |
| if entry.RedirectModel != "" { |
| t.Fatalf("RedirectModel not trimmed: %q", entry.RedirectModel) |
| } |
| }) |
|
|
| t.Run("reject_empty", func(t *testing.T) { |
| entry := &ModelEntry{Model: " "} |
| if err := entry.Validate(); err == nil { |
| t.Fatal("expected error for empty model") |
| } |
| }) |
|
|
| t.Run("reject_illegal_model_chars", func(t *testing.T) { |
| entry := &ModelEntry{Model: "gpt-4\nx"} |
| if err := entry.Validate(); err == nil { |
| t.Fatal("expected error for illegal chars in model") |
| } |
| }) |
|
|
| t.Run("reject_illegal_redirect_chars", func(t *testing.T) { |
| entry := &ModelEntry{Model: "gpt-4", RedirectModel: "x\ry"} |
| if err := entry.Validate(); err == nil { |
| t.Fatal("expected error for illegal chars in redirect_model") |
| } |
| }) |
| } |
|
|
| func TestConfig_SupportsModel(t *testing.T) { |
| t.Parallel() |
|
|
| cfg := &Config{ |
| ModelEntries: []ModelEntry{ |
| {Model: "m1"}, |
| {Model: "m2"}, |
| }, |
| } |
|
|
| if !cfg.SupportsModel("m2") { |
| t.Fatal("expected SupportsModel(m2)=true") |
| } |
| if cfg.SupportsModel("none") { |
| t.Fatal("expected SupportsModel(none)=false") |
| } |
| } |
|
|
| func TestConfig_IsCoolingDown(t *testing.T) { |
| t.Parallel() |
|
|
| now := time.Unix(1000, 0) |
| cfg := &Config{CooldownUntil: 1001} |
| if !cfg.IsCoolingDown(now) { |
| t.Fatal("expected cooling down when cooldown_until is in the future") |
| } |
|
|
| cfg.CooldownUntil = 1000 |
| if cfg.IsCoolingDown(now) { |
| t.Fatal("expected not cooling down when cooldown_until equals now") |
| } |
| } |
|
|
| func TestIsValidKeyStrategy(t *testing.T) { |
| t.Parallel() |
|
|
| for _, tc := range []struct { |
| in string |
| want bool |
| }{ |
| {in: "", want: true}, |
| {in: KeyStrategySequential, want: true}, |
| {in: KeyStrategyRoundRobin, want: true}, |
| {in: "random", want: false}, |
| } { |
| if got := IsValidKeyStrategy(tc.in); got != tc.want { |
| t.Fatalf("IsValidKeyStrategy(%q) = %v, want %v", tc.in, got, tc.want) |
| } |
| } |
| } |
|
|
| func TestAPIKey_IsCoolingDown(t *testing.T) { |
| t.Parallel() |
|
|
| now := time.Unix(1000, 0) |
| key := &APIKey{CooldownUntil: 1001} |
| if !key.IsCoolingDown(now) { |
| t.Fatal("expected cooling down for APIKey") |
| } |
| key.CooldownUntil = 1000 |
| if key.IsCoolingDown(now) { |
| t.Fatal("expected not cooling down when equals now") |
| } |
| } |
|
|
| func TestDefaultHealthScoreConfig(t *testing.T) { |
| t.Parallel() |
|
|
| cfg := DefaultHealthScoreConfig() |
| if cfg.Enabled { |
| t.Fatal("default health score config should be disabled") |
| } |
| if cfg.SuccessRatePenaltyWeight <= 0 || cfg.WindowMinutes <= 0 || cfg.UpdateIntervalSeconds <= 0 || cfg.MinConfidentSample <= 0 { |
| t.Fatalf("unexpected default config: %+v", cfg) |
| } |
| } |
|
|
| func TestGetURLs_SingleURL(t *testing.T) { |
| c := &Config{URL: "https://api.openai.com"} |
| urls := c.GetURLs() |
| if len(urls) != 1 || urls[0] != "https://api.openai.com" { |
| t.Errorf("expected [https://api.openai.com], got %v", urls) |
| } |
| } |
|
|
| func TestGetURLs_MultipleURLs(t *testing.T) { |
| c := &Config{URL: "https://us.api.openai.com\nhttps://eu.api.openai.com"} |
| urls := c.GetURLs() |
| if len(urls) != 2 { |
| t.Fatalf("expected 2 urls, got %d", len(urls)) |
| } |
| if urls[0] != "https://us.api.openai.com" || urls[1] != "https://eu.api.openai.com" { |
| t.Errorf("unexpected urls: %v", urls) |
| } |
| } |
|
|
| func TestGetURLs_EmptyLinesIgnored(t *testing.T) { |
| c := &Config{URL: "https://a.com\n\n \nhttps://b.com\n"} |
| urls := c.GetURLs() |
| if len(urls) != 2 { |
| t.Fatalf("expected 2 urls (skip blanks), got %d: %v", len(urls), urls) |
| } |
| } |
|
|
| func TestGetURLs_DuplicateURLsDeduped(t *testing.T) { |
| c := &Config{URL: "https://a.com\nhttps://b.com\nhttps://a.com\nhttps://b.com"} |
| urls := c.GetURLs() |
| if len(urls) != 2 { |
| t.Fatalf("expected 2 unique urls, got %d: %v", len(urls), urls) |
| } |
| if urls[0] != "https://a.com" || urls[1] != "https://b.com" { |
| t.Fatalf("unexpected urls order/content: %v", urls) |
| } |
| } |
|
|
| func TestGetURLs_TrailingSlashPreserved(t *testing.T) { |
| c := &Config{URL: "https://api.openai.com/v1/"} |
| urls := c.GetURLs() |
| if urls[0] != "https://api.openai.com/v1/" { |
| t.Errorf("trailing slash should be preserved, got %q", urls[0]) |
| } |
| } |
|
|
| func TestGetURLs_SingleURLTrimmed(t *testing.T) { |
| c := &Config{URL: " https://api.openai.com/v1 "} |
| urls := c.GetURLs() |
| if len(urls) != 1 || urls[0] != "https://api.openai.com/v1" { |
| t.Fatalf("expected trimmed single url, got %v", urls) |
| } |
| } |
|
|
| func TestGetURLs_WhitespaceOnlyReturnsEmpty(t *testing.T) { |
| c := &Config{URL: "\n \n\t\n"} |
| urls := c.GetURLs() |
| if len(urls) != 0 { |
| t.Fatalf("expected empty urls for whitespace-only input, got %v", urls) |
| } |
| } |
|
|