package monitoring import ( "encoding/json" "net/http" "net/http/httptest" "testing" "time" "github.com/iwasforcedtobehere/goRZ/internal/config" "github.com/iwasforcedtobehere/goRZ/internal/logger" ) func TestMonitorStartStop(t *testing.T) { cfg := &config.Config{ Monitor: config.MonitorConfig{ Enabled: true, Port: 9090, Path: "/metrics", Auth: false, }, } logger := logger.NewLogger() monitor := NewMonitor(cfg, logger) // Start the monitor err := monitor.Start() if err != nil { t.Fatalf("Failed to start monitor: %v", err) } // Stop the monitor monitor.Stop() // Test that we can start and stop again err = monitor.Start() if err != nil { t.Fatalf("Failed to start monitor: %v", err) } monitor.Stop() } func TestMonitorDisabled(t *testing.T) { cfg := &config.Config{ Monitor: config.MonitorConfig{ Enabled: false, }, } logger := logger.NewLogger() monitor := NewMonitor(cfg, logger) // Start the monitor err := monitor.Start() if err != nil { t.Fatalf("Failed to start monitor: %v", err) } // Stop the monitor monitor.Stop() } func TestMonitorMetricsHandler(t *testing.T) { cfg := &config.Config{ Monitor: config.MonitorConfig{ Enabled: true, Port: 9090, Path: "/metrics", Auth: false, }, Proxy: config.ProxyConfig{ Targets: []config.TargetConfig{ {Name: "target1", Address: "http://localhost:8081", Protocol: "http", Weight: 1, Healthy: true}, {Name: "target2", Address: "http://localhost:8082", Protocol: "http", Weight: 1, Healthy: false}, }, }, } logger := logger.NewLogger() monitor := NewMonitor(cfg, logger) // Create a test HTTP server server := httptest.NewServer(monitor.authHandler) defer server.Close() // Test metrics endpoint resp, err := http.Get(server.URL + cfg.Monitor.Path) if err != nil { t.Fatalf("Failed to get metrics: %v", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { t.Errorf("Expected status code %d, got %d", http.StatusOK, resp.StatusCode) } // Decode and check metrics var metrics Metrics if err := json.NewDecoder(resp.Body).Decode(&metrics); err != nil { t.Fatalf("Failed to decode metrics: %v", err) } if metrics.RequestsTotal != 0 { t.Errorf("Expected requests total 0, got %d", metrics.RequestsTotal) } if metrics.RequestsActive != 0 { t.Errorf("Expected requests active 0, got %d", metrics.RequestsActive) } if len(metrics.TargetMetrics) != 2 { t.Errorf("Expected 2 target metrics, got %d", len(metrics.TargetMetrics)) } if !metrics.TargetMetrics["target1"].Healthy { t.Error("Expected target1 to be healthy") } if metrics.TargetMetrics["target2"].Healthy { t.Error("Expected target2 to be unhealthy") } } func TestMonitorBasicAuth(t *testing.T) { cfg := &config.Config{ Monitor: config.MonitorConfig{ Enabled: true, Port: 9090, Path: "/metrics", Auth: true, Username: "admin", Password: "secret", }, } logger := logger.NewLogger() monitor := NewMonitor(cfg, logger) // Create a test HTTP server server := httptest.NewServer(monitor.authHandler) defer server.Close() // Test without authentication resp, err := http.Get(server.URL + cfg.Monitor.Path) if err != nil { t.Fatalf("Failed to get metrics: %v", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusUnauthorized { t.Errorf("Expected status code %d, got %d", http.StatusUnauthorized, resp.StatusCode) } // Test with incorrect authentication req, err := http.NewRequest("GET", server.URL+cfg.Monitor.Path, nil) if err != nil { t.Fatalf("Failed to create request: %v", err) } req.SetBasicAuth("wrong", "credentials") client := &http.Client{} resp, err = client.Do(req) if err != nil { t.Fatalf("Failed to send request: %v", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusUnauthorized { t.Errorf("Expected status code %d, got %d", http.StatusUnauthorized, resp.StatusCode) } // Test with correct authentication req, err = http.NewRequest("GET", server.URL+cfg.Monitor.Path, nil) if err != nil { t.Fatalf("Failed to create request: %v", err) } req.SetBasicAuth(cfg.Monitor.Username, cfg.Monitor.Password) resp, err = client.Do(req) if err != nil { t.Fatalf("Failed to send request: %v", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { t.Errorf("Expected status code %d, got %d", http.StatusOK, resp.StatusCode) } } func TestMonitorMetricsTracking(t *testing.T) { cfg := &config.Config{ Monitor: config.MonitorConfig{ Enabled: true, Port: 9090, Path: "/metrics", Auth: false, }, Proxy: config.ProxyConfig{ Targets: []config.TargetConfig{ {Name: "target1", Address: "http://localhost:8081", Protocol: "http", Weight: 1, Healthy: true}, }, }, } logger := logger.NewLogger() monitor := NewMonitor(cfg, logger) // Test incrementing request count monitor.IncrementRequest() metrics := monitor.GetMetrics() if metrics.RequestsTotal != 1 { t.Errorf("Expected requests total 1, got %d", metrics.RequestsTotal) } // Test incrementing active request count monitor.IncrementActiveRequest() metrics = monitor.GetMetrics() if metrics.RequestsActive != 1 { t.Errorf("Expected requests active 1, got %d", metrics.RequestsActive) } // Test decrementing active request count monitor.DecrementActiveRequest() metrics = monitor.GetMetrics() if metrics.RequestsActive != 0 { t.Errorf("Expected requests active 0, got %d", metrics.RequestsActive) } // Test recording response monitor.RecordResponse(http.StatusOK, "target1", 100*time.Millisecond) metrics = monitor.GetMetrics() if metrics.ResponsesByStatus["200"] != 1 { t.Errorf("Expected 1 response with status 200, got %d", metrics.ResponsesByStatus["200"]) } if metrics.TargetMetrics["target1"].RequestsTotal != 1 { t.Errorf("Expected target1 to have 1 request, got %d", metrics.TargetMetrics["target1"].RequestsTotal) } if metrics.TargetMetrics["target1"].ResponsesByStatus["200"] != 1 { t.Errorf("Expected target1 to have 1 response with status 200, got %d", metrics.TargetMetrics["target1"].ResponsesByStatus["200"]) } if len(metrics.TargetMetrics["target1"].ResponseTimes) != 1 { t.Errorf("Expected target1 to have 1 response time, got %d", len(metrics.TargetMetrics["target1"].ResponseTimes)) } if metrics.TargetMetrics["target1"].AvgResponseTime != 100*time.Millisecond { t.Errorf("Expected target1 to have average response time 100ms, got %v", metrics.TargetMetrics["target1"].AvgResponseTime) } // Test updating target health monitor.UpdateTargetHealth("target1", false) metrics = monitor.GetMetrics() if metrics.TargetMetrics["target1"].Healthy { t.Error("Expected target1 to be unhealthy") } } func TestMonitorMultipleResponses(t *testing.T) { cfg := &config.Config{ Monitor: config.MonitorConfig{ Enabled: true, Port: 9090, Path: "/metrics", Auth: false, }, Proxy: config.ProxyConfig{ Targets: []config.TargetConfig{ {Name: "target1", Address: "http://localhost:8081", Protocol: "http", Weight: 1, Healthy: true}, }, }, } logger := logger.NewLogger() monitor := NewMonitor(cfg, logger) // Record multiple responses with different status codes and response times monitor.RecordResponse(http.StatusOK, "target1", 100*time.Millisecond) monitor.RecordResponse(http.StatusOK, "target1", 200*time.Millisecond) monitor.RecordResponse(http.StatusNotFound, "target1", 50*time.Millisecond) monitor.RecordResponse(http.StatusInternalServerError, "target1", 300*time.Millisecond) metrics := monitor.GetMetrics() // Check overall metrics if metrics.RequestsTotal != 0 { t.Errorf("Expected requests total 0, got %d", metrics.RequestsTotal) } if metrics.ResponsesByStatus["200"] != 2 { t.Errorf("Expected 2 responses with status 200, got %d", metrics.ResponsesByStatus["200"]) } if metrics.ResponsesByStatus["404"] != 1 { t.Errorf("Expected 1 response with status 404, got %d", metrics.ResponsesByStatus["404"]) } if metrics.ResponsesByStatus["500"] != 1 { t.Errorf("Expected 1 response with status 500, got %d", metrics.ResponsesByStatus["500"]) } // Check target metrics if metrics.TargetMetrics["target1"].RequestsTotal != 4 { t.Errorf("Expected target1 to have 4 requests, got %d", metrics.TargetMetrics["target1"].RequestsTotal) } if metrics.TargetMetrics["target1"].ResponsesByStatus["200"] != 2 { t.Errorf("Expected target1 to have 2 responses with status 200, got %d", metrics.TargetMetrics["target1"].ResponsesByStatus["200"]) } if metrics.TargetMetrics["target1"].ResponsesByStatus["404"] != 1 { t.Errorf("Expected target1 to have 1 response with status 404, got %d", metrics.TargetMetrics["target1"].ResponsesByStatus["404"]) } if metrics.TargetMetrics["target1"].ResponsesByStatus["500"] != 1 { t.Errorf("Expected target1 to have 1 response with status 500, got %d", metrics.TargetMetrics["target1"].ResponsesByStatus["500"]) } // Check average response time expectedAvg := time.Duration((100 + 200 + 50 + 300) / 4) if metrics.TargetMetrics["target1"].AvgResponseTime != expectedAvg { t.Errorf("Expected target1 to have average response time %v, got %v", expectedAvg, metrics.TargetMetrics["target1"].AvgResponseTime) } }