123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496 |
- package upstream
- import (
- "testing"
- )
- // TestParseProxyTargetsWithConsulResolver tests parsing of nginx config with consul DNS resolver
- func TestParseProxyTargetsWithConsulResolver(t *testing.T) {
- config := `upstream redacted-net {
- zone upstream_web 128k;
- resolver 127.0.0.1:8600 valid=5s;
- resolver_timeout 2s;
- server service.consul service=redacted-net resolve;
- }
- server {
- listen 80;
- listen [::]:80;
- server_name redacted.net;
- location / {
- proxy_pass http://redacted-net;
- }
- }`
- targets := ParseProxyTargetsFromRawContent(config)
- // Print actual results for debugging
- t.Logf("Found %d targets:", len(targets))
- for i, target := range targets {
- t.Logf("Target %d: Host=%s, Port=%s, Type=%s, Resolver=%s, IsConsul=%v",
- i+1, target.Host, target.Port, target.Type, target.Resolver, target.IsConsul)
- }
- // Expected behavior:
- // - Should parse "service.consul" as host with dynamic port
- // - Should identify this as an upstream target with consul service discovery
- // - Should capture resolver information
- // - proxy_pass http://redacted-net should be ignored since it references upstream
- expectedTargets := []ProxyTarget{
- {
- Host: "service.consul",
- Port: "dynamic",
- Type: "upstream",
- Resolver: "127.0.0.1:8600",
- IsConsul: true,
- ServiceURL: "service.consul service=redacted-net resolve",
- },
- }
- if len(targets) != len(expectedTargets) {
- t.Errorf("Expected %d targets, got %d", len(expectedTargets), len(targets))
- return
- }
- // Create a map for easier comparison
- targetMap := make(map[string]ProxyTarget)
- for _, target := range targets {
- key := target.Host + ":" + target.Port + ":" + target.Type + ":" + target.Resolver
- if target.IsConsul {
- key += ":consul:" + target.ServiceURL
- }
- targetMap[key] = target
- }
- for _, expected := range expectedTargets {
- key := expected.Host + ":" + expected.Port + ":" + expected.Type + ":" + expected.Resolver
- if expected.IsConsul {
- key += ":consul:" + expected.ServiceURL
- }
- if _, found := targetMap[key]; !found {
- t.Errorf("Expected target not found: %+v", expected)
- }
- }
- }
- // TestConsulResolverExtractServiceName tests service name extraction
- func TestConsulResolverExtractServiceName(t *testing.T) {
- resolver := NewConsulResolver("127.0.0.1:8600")
- tests := []struct {
- serviceURL string
- expectedName string
- }{
- {
- serviceURL: "service.consul service=redacted-net resolve",
- expectedName: "redacted-net",
- },
- {
- serviceURL: "service.consul service=web-service resolve",
- expectedName: "web-service",
- },
- {
- serviceURL: "service.consul service=api-backend resolve",
- expectedName: "api-backend",
- },
- {
- serviceURL: "my-service.service.consul",
- expectedName: "my-service",
- },
- {
- serviceURL: "invalid-format",
- expectedName: "",
- },
- }
- for _, test := range tests {
- result := resolver.extractServiceName(test.serviceURL)
- if result != test.expectedName {
- t.Errorf("extractServiceName(%q) = %q, expected %q", test.serviceURL, result, test.expectedName)
- }
- }
- }
- // TestConsulResolverResolveService tests the actual resolution functionality
- func TestConsulResolverResolveService(t *testing.T) {
- // Test with a mock resolver that should fail (127.0.0.1:8600 is consul default but likely not running)
- t.Run("ResolutionWithMockConsul", func(t *testing.T) {
- resolver := NewConsulResolver("127.0.0.1:8600")
- addresses, err := resolver.ResolveService("service.consul service=test-service resolve")
- // We expect this to fail since there's no real consul server
- if err == nil {
- t.Logf("Unexpected success: resolved addresses %v (maybe there's a real consul server?)", addresses)
- } else {
- t.Logf("Expected failure: %v", err)
- }
- })
- // Test with invalid service URL
- t.Run("InvalidServiceURL", func(t *testing.T) {
- resolver := NewConsulResolver("127.0.0.1:8600")
- addresses, err := resolver.ResolveService("invalid-service-url")
- if err == nil {
- t.Errorf("Expected error for invalid service URL, got addresses: %v", addresses)
- }
- if len(addresses) != 0 {
- t.Errorf("Expected no addresses for invalid service URL, got %v", addresses)
- }
- })
- // Test with invalid resolver address
- t.Run("InvalidResolverAddress", func(t *testing.T) {
- resolver := NewConsulResolver("192.168.254.254:8600") // Unreachable IP
- addresses, err := resolver.ResolveService("service.consul service=test-service resolve")
- // Should fail due to unreachable resolver
- if err == nil {
- t.Errorf("Expected error for unreachable resolver, got addresses: %v", addresses)
- }
- })
- // Test service name extraction edge cases
- t.Run("ServiceNameExtractionEdgeCases", func(t *testing.T) {
- resolver := NewConsulResolver("127.0.0.1:8600")
- testCases := []struct {
- serviceURL string
- expectedName string
- }{
- {"", ""},
- {"service.consul", ""},
- {"service.consul resolve", ""},
- {"service.consul service= resolve", ""},
- {"service.consul service= resolve", ""}, // Empty service name
- {"my-service.service.consul", "my-service"},
- {"complex-service-name.service.consul", "complex-service-name"},
- }
- for _, tc := range testCases {
- result := resolver.extractServiceName(tc.serviceURL)
- if result != tc.expectedName {
- t.Errorf("extractServiceName(%q) = %q, expected %q", tc.serviceURL, result, tc.expectedName)
- }
- }
- })
- }
- // TestTestConsulTargets tests the new dedicated consul testing function
- func TestTestConsulTargets(t *testing.T) {
- // Test 1: Valid consul targets with resolver
- t.Run("ValidConsulTargets", func(t *testing.T) {
- consulTargets := []ProxyTarget{
- {
- Host: "service.consul",
- Port: "dynamic",
- Type: "upstream",
- Resolver: "127.0.0.1:8600",
- IsConsul: true,
- ServiceURL: "service.consul service=test-service resolve",
- },
- }
- results := TestConsulTargets(consulTargets)
- // Should have exactly 1 result
- if len(results) != 1 {
- t.Errorf("Expected 1 result, got %d", len(results))
- }
- // Check the result exists with correct key
- key := "service.consul:dynamic"
- if status, found := results[key]; found {
- // The status doesn't matter much (likely offline without real consul)
- // What matters is that the function processes the target correctly
- t.Logf("Consul target %s processed: Online=%v, Latency=%.2f", key, status.Online, status.Latency)
- } else {
- t.Errorf("Expected result for key %s not found", key)
- }
- })
- // Test 2: Consul target without resolver should be marked offline
- t.Run("ConsulTargetWithoutResolver", func(t *testing.T) {
- consulTargets := []ProxyTarget{
- {
- Host: "service.consul",
- Port: "dynamic",
- Type: "upstream",
- // No resolver - should be marked offline immediately
- IsConsul: true,
- ServiceURL: "service.consul service=no-resolver resolve",
- },
- }
- results := TestConsulTargets(consulTargets)
- if len(results) != 1 {
- t.Errorf("Expected 1 result, got %d", len(results))
- }
- key := "service.consul:dynamic"
- if status, found := results[key]; found {
- if status.Online {
- t.Errorf("Expected consul target without resolver to be offline, but it's online")
- }
- if status.Latency != 0 {
- t.Errorf("Expected latency to be 0 for offline target, got %.2f", status.Latency)
- }
- } else {
- t.Errorf("Expected result for key %s not found", key)
- }
- })
- // Test 3: Multiple consul targets with different resolvers
- t.Run("MultipleConsulTargetsWithDifferentResolvers", func(t *testing.T) {
- consulTargets := []ProxyTarget{
- {
- Host: "web-service.consul",
- Port: "dynamic",
- Type: "upstream",
- Resolver: "127.0.0.1:8600",
- IsConsul: true,
- ServiceURL: "service.consul service=web-service resolve",
- },
- {
- Host: "api-service.consul",
- Port: "dynamic",
- Type: "upstream",
- Resolver: "127.0.0.1:8500", // Different resolver
- IsConsul: true,
- ServiceURL: "service.consul service=api-service resolve",
- },
- }
- results := TestConsulTargets(consulTargets)
- // Should have 2 results
- if len(results) != 2 {
- t.Errorf("Expected 2 results, got %d", len(results))
- }
- // Check both results exist
- expectedKeys := []string{
- "web-service.consul:dynamic",
- "api-service.consul:dynamic",
- }
- for _, key := range expectedKeys {
- if _, found := results[key]; !found {
- t.Errorf("Expected result for key %s not found", key)
- }
- }
- })
- // Test 4: Empty consul targets should return empty results
- t.Run("EmptyConsulTargets", func(t *testing.T) {
- consulTargets := []ProxyTarget{}
- results := TestConsulTargets(consulTargets)
- if len(results) != 0 {
- t.Errorf("Expected 0 results for empty targets, got %d", len(results))
- }
- })
- }
- // TestSimplifiedArchitecture tests the new simplified architecture
- func TestSimplifiedArchitecture(t *testing.T) {
- service := GetUpstreamService()
- service.ClearTargets()
- // Mix of traditional and consul targets
- mixedTargets := []ProxyTarget{
- // Traditional targets
- {Host: "127.0.0.1", Port: "80", Type: "upstream"},
- {Host: "192.168.1.100", Port: "8080", Type: "upstream"},
- // Consul targets
- {
- Host: "service.consul",
- Port: "dynamic",
- Type: "upstream",
- Resolver: "127.0.0.1:8600",
- IsConsul: true,
- ServiceURL: "service.consul service=my-service resolve",
- },
- }
- service.updateTargetsFromConfig("test-config.conf", mixedTargets)
- // Verify targets are correctly stored
- service.targetsMutex.RLock()
- traditionalCount := 0
- consulCount := 0
- for _, targetInfo := range service.targets {
- if targetInfo.ProxyTarget.IsConsul {
- consulCount++
- } else {
- traditionalCount++
- }
- }
- service.targetsMutex.RUnlock()
- if traditionalCount != 2 {
- t.Errorf("Expected 2 traditional targets, got %d", traditionalCount)
- }
- if consulCount != 1 {
- t.Errorf("Expected 1 consul target, got %d", consulCount)
- }
- t.Logf("Architecture correctly separated %d traditional and %d consul targets", traditionalCount, consulCount)
- // Clean up
- service.ClearTargets()
- }
- // TestEnhancedAvailabilityTest tests the enhanced availability testing with mixed targets
- func TestEnhancedAvailabilityTest(t *testing.T) {
- targets := []ProxyTarget{
- // Regular target
- {
- Host: "127.0.0.1",
- Port: "22", // SSH port might be available
- Type: "upstream",
- },
- // Consul target (will fail since no real consul)
- {
- Host: "service.consul",
- Port: "dynamic",
- Type: "upstream",
- Resolver: "127.0.0.1:8600",
- IsConsul: true,
- ServiceURL: "service.consul service=test-service resolve",
- },
- // Invalid regular target
- {
- Host: "192.168.254.254",
- Port: "9999",
- Type: "upstream",
- },
- }
- results := EnhancedAvailabilityTest(targets)
- t.Logf("Found %d test results:", len(results))
- for key, status := range results {
- t.Logf("Target %s: Online=%v, Latency=%.2f", key, status.Online, status.Latency)
- }
- // Verify we have results for all targets
- expectedKeys := []string{
- "127.0.0.1:22",
- "service.consul:dynamic",
- "192.168.254.254:9999",
- }
- for _, key := range expectedKeys {
- if _, found := results[key]; !found {
- t.Errorf("Expected result for key %s not found", key)
- }
- }
- // Test that consul target is processed (result doesn't matter, we just verify the flow works)
- if status, found := results["service.consul:dynamic"]; found {
- t.Logf("Consul target processed: Online=%v, Latency=%.2f", status.Online, status.Latency)
- }
- // Test that unreachable target is properly handled
- if status, found := results["192.168.254.254:9999"]; found {
- // This should be offline due to unreachable IP, but we verify the function handles it
- t.Logf("Unreachable target processed: Online=%v, Latency=%.2f", status.Online, status.Latency)
- // Most likely offline, but we don't assume - we just verify it was processed
- }
- }
- // TestTraditionalAvailabilityTestUsage tests that traditional AvailabilityTest is used when no consul targets
- func TestTraditionalAvailabilityTestUsage(t *testing.T) {
- // Test with only traditional targets
- targets := []ProxyTarget{
- {
- Host: "127.0.0.1",
- Port: "80",
- Type: "upstream",
- },
- {
- Host: "192.168.254.254",
- Port: "9999",
- Type: "upstream",
- },
- }
- // Test enhanced version with traditional targets only
- enhancedResults := EnhancedAvailabilityTest(targets)
- // Test traditional version directly
- traditionalKeys := []string{"127.0.0.1:80", "192.168.254.254:9999"}
- traditionalResults := AvailabilityTest(traditionalKeys)
- t.Logf("Enhanced results: %d items", len(enhancedResults))
- t.Logf("Traditional results: %d items", len(traditionalResults))
- // Both should have same number of results
- if len(enhancedResults) != len(traditionalResults) {
- t.Errorf("Expected same number of results, enhanced=%d, traditional=%d",
- len(enhancedResults), len(traditionalResults))
- return
- }
- // Results should be consistent
- for key, traditionalStatus := range traditionalResults {
- if enhancedStatus, found := enhancedResults[key]; found {
- if traditionalStatus.Online != enhancedStatus.Online {
- t.Errorf("Inconsistent online status for %s: traditional=%v, enhanced=%v",
- key, traditionalStatus.Online, enhancedStatus.Online)
- }
- } else {
- t.Errorf("Key %s missing in enhanced results", key)
- }
- }
- t.Logf("Enhanced test correctly delegated to traditional test for non-consul targets")
- }
- // TestUpstreamServiceSimplifiedFlow tests the new simplified flow in UpstreamService
- func TestUpstreamServiceSimplifiedFlow(t *testing.T) {
- service := GetUpstreamService()
- service.ClearTargets()
- // Add mixed targets
- mixedTargets := []ProxyTarget{
- {Host: "127.0.0.1", Port: "80", Type: "upstream"},
- {
- Host: "service.consul",
- Port: "dynamic",
- Type: "upstream",
- Resolver: "127.0.0.1:8600",
- IsConsul: true,
- ServiceURL: "service.consul service=test resolve",
- },
- }
- service.updateTargetsFromConfig("test-config.conf", mixedTargets)
- // This would trigger the simplified flow
- service.PerformAvailabilityTest()
- results := service.GetAvailabilityMap()
- t.Logf("Simplified flow generated %d results:", len(results))
- for key, status := range results {
- t.Logf("Result %s: Online=%v, Latency=%.2f", key, status.Online, status.Latency)
- }
- // Should have results for both targets
- expectedKeys := []string{"127.0.0.1:80", "service.consul:dynamic"}
- for _, key := range expectedKeys {
- if _, found := results[key]; !found {
- t.Errorf("Expected result for key %s not found", key)
- }
- }
- t.Logf("Simplified architecture correctly processed mixed targets")
- // Clean up
- service.ClearTargets()
- }
|