package config_test import ( "testing" "time" "github.com/Riotpiaole/homelab-frontend/internal/config" ) // TestLoadRoutesValidConfig tests that a valid configuration loads correctly. func TestLoadRoutesValidConfig(t *testing.T) { routes, err := config.LoadRoutesFromFile("../../testdata/config/valid.yaml") if err != nil { t.Fatalf("unexpected error loading valid config: %v", err) } // Verify all three routes are present if len(routes) != 3 { t.Errorf("expected 3 routes, got %d", len(routes)) } // Verify reasoning-chat route reasoningRoute, ok := routes["reasoning-chat"] if !ok { t.Fatal("reasoning-chat route not found") } if reasoningRoute.Name != "reasoning-chat" { t.Errorf("route name mismatch: expected 'reasoning-chat', got %q", reasoningRoute.Name) } if reasoningRoute.Upstream.Address != "reasoning-predictor.llm-serving:80" { t.Errorf("upstream address mismatch: expected 'reasoning-predictor.llm-serving:80', got %q", reasoningRoute.Upstream.Address) } if reasoningRoute.Upstream.ConnectTimeout != 10*time.Second { t.Errorf("connect timeout mismatch: expected 10s, got %v", reasoningRoute.Upstream.ConnectTimeout) } if reasoningRoute.Upstream.ReadTimeout != time.Hour { t.Errorf("read timeout mismatch: expected 1h, got %v", reasoningRoute.Upstream.ReadTimeout) } if reasoningRoute.Upstream.WriteTimeout != time.Hour { t.Errorf("write timeout mismatch: expected 1h, got %v", reasoningRoute.Upstream.WriteTimeout) } if reasoningRoute.Upstream.MaxBodySize != 10485760 { t.Errorf("max body size mismatch: expected 10485760, got %d", reasoningRoute.Upstream.MaxBodySize) } if !reasoningRoute.Upstream.AuthRequired { t.Error("auth required should be true") } // Verify ornith-chat route ornithRoute, ok := routes["ornith-chat"] if !ok { t.Fatal("ornith-chat route not found") } if ornithRoute.Upstream.ReadTimeout != 10*time.Minute { t.Errorf("ornith read timeout mismatch: expected 10m, got %v", ornithRoute.Upstream.ReadTimeout) } if ornithRoute.Upstream.AuthRequired { t.Error("ornith auth required should be false") } // Verify embeddings route embeddingsRoute, ok := routes["embeddings"] if !ok { t.Fatal("embeddings route not found") } if embeddingsRoute.Upstream.MaxBodySize != 5242880 { t.Errorf("embeddings max body size mismatch: expected 5242880, got %d", embeddingsRoute.Upstream.MaxBodySize) } } // TestLoadRoutesMissingAddress tests that missing address field is caught. func TestLoadRoutesMissingAddress(t *testing.T) { routes, err := config.LoadRoutesFromFile("../../testdata/config/missing-address.yaml") if err == nil { t.Fatal("expected error for missing address, got nil") } if routes != nil { t.Error("expected nil routes on error") } if errMsg := err.Error(); errMsg != "route \"test-route\": field 'address' is required" { t.Errorf("expected error about missing address, got: %s", errMsg) } } // TestLoadRoutesMissingConnectTimeout tests that missing connectTimeout field is caught. func TestLoadRoutesMissingConnectTimeout(t *testing.T) { routes, err := config.LoadRoutesFromFile("../../testdata/config/missing-connectTimeout.yaml") if err == nil { t.Fatal("expected error for missing connectTimeout, got nil") } if routes != nil { t.Error("expected nil routes on error") } if errMsg := err.Error(); errMsg != "route \"test-route\": field 'connectTimeout' is required" { t.Errorf("expected error about missing connectTimeout, got: %s", errMsg) } } // TestLoadRoutesMissingReadTimeout tests that missing readTimeout field is caught. func TestLoadRoutesMissingReadTimeout(t *testing.T) { routes, err := config.LoadRoutesFromFile("../../testdata/config/missing-readTimeout.yaml") if err == nil { t.Fatal("expected error for missing readTimeout, got nil") } if routes != nil { t.Error("expected nil routes on error") } if errMsg := err.Error(); errMsg != "route \"test-route\": field 'readTimeout' is required" { t.Errorf("expected error about missing readTimeout, got: %s", errMsg) } } // TestLoadRoutesMissingWriteTimeout tests that missing writeTimeout field is caught. func TestLoadRoutesMissingWriteTimeout(t *testing.T) { routes, err := config.LoadRoutesFromFile("../../testdata/config/missing-writeTimeout.yaml") if err == nil { t.Fatal("expected error for missing writeTimeout, got nil") } if routes != nil { t.Error("expected nil routes on error") } if errMsg := err.Error(); errMsg != "route \"test-route\": field 'writeTimeout' is required" { t.Errorf("expected error about missing writeTimeout, got: %s", errMsg) } } // TestLoadRoutesMissingMaxBodySize tests that missing maxBodySize field is caught. func TestLoadRoutesMissingMaxBodySize(t *testing.T) { routes, err := config.LoadRoutesFromFile("../../testdata/config/missing-maxBodySize.yaml") if err == nil { t.Fatal("expected error for missing maxBodySize, got nil") } if routes != nil { t.Error("expected nil routes on error") } if errMsg := err.Error(); errMsg != "route \"test-route\": field 'maxBodySize' is required and must be > 0" { t.Errorf("expected error about missing maxBodySize, got: %s", errMsg) } } // TestLoadRoutesMalformedConnectTimeout tests that malformed connectTimeout is caught. func TestLoadRoutesMalformedConnectTimeout(t *testing.T) { routes, err := config.LoadRoutesFromFile("../../testdata/config/malformed-connectTimeout.yaml") if err == nil { t.Fatal("expected error for malformed connectTimeout, got nil") } if routes != nil { t.Error("expected nil routes on error") } if errMsg := err.Error(); errMsg != "route \"test-route\": invalid connectTimeout \"not-a-duration\": time: invalid duration \"not-a-duration\"" { t.Errorf("expected error about malformed connectTimeout, got: %s", errMsg) } } // TestLoadRoutesMalformedReadTimeout tests that malformed readTimeout is caught. func TestLoadRoutesMalformedReadTimeout(t *testing.T) { routes, err := config.LoadRoutesFromFile("../../testdata/config/malformed-readTimeout.yaml") if err == nil { t.Fatal("expected error for malformed readTimeout, got nil") } if routes != nil { t.Error("expected nil routes on error") } if errMsg := err.Error(); errMsg != "route \"test-route\": invalid readTimeout \"invalid\": time: invalid duration \"invalid\"" { t.Errorf("expected error about malformed readTimeout, got: %s", errMsg) } } // TestLoadRoutesMalformedWriteTimeout tests that malformed writeTimeout is caught. func TestLoadRoutesMalformedWriteTimeout(t *testing.T) { routes, err := config.LoadRoutesFromFile("../../testdata/config/malformed-writeTimeout.yaml") if err == nil { t.Fatal("expected error for malformed writeTimeout, got nil") } if routes != nil { t.Error("expected nil routes on error") } if errMsg := err.Error(); errMsg != "route \"test-route\": invalid writeTimeout \"bad\": time: invalid duration \"bad\"" { t.Errorf("expected error about malformed writeTimeout, got: %s", errMsg) } } // TestLoadRoutesInvalidAddressNoPort tests that address without port is caught. func TestLoadRoutesInvalidAddressNoPort(t *testing.T) { routes, err := config.LoadRoutesFromFile("../../testdata/config/invalid-address-no-port.yaml") if err == nil { t.Fatal("expected error for invalid address (no port), got nil") } if routes != nil { t.Error("expected nil routes on error") } if errMsg := err.Error(); errMsg != "route \"test-route\": invalid address \"localhost\": address localhost: missing port in address" { t.Errorf("expected error about invalid address, got: %s", errMsg) } } // TestLoadRoutesDuplicateRouteNames tests that duplicate route names are caught. func TestLoadRoutesDuplicateRouteNames(t *testing.T) { routes, err := config.LoadRoutesFromFile("../../testdata/config/duplicate-routes.yaml") if err == nil { t.Fatal("expected error for duplicate routes, got nil") } if routes != nil { t.Error("expected nil routes on error") } if errMsg := err.Error(); errMsg != "duplicate route: \"test-route\"" { t.Errorf("expected error about duplicate routes, got: %s", errMsg) } }