package main import ( "encoding/json" "net/http" "net/http/httptest" "os" "path/filepath" "testing" ) func TestWriteError(t *testing.T) { w := httptest.NewRecorder() writeError(w, 404, "Not found") if w.Code != 404 { t.Errorf("expected 404, got %d", w.Code) } ct := w.Header().Get("Content-Type") if ct != "application/json" { t.Errorf("expected application/json, got %s", ct) } var body map[string]string json.Unmarshal(w.Body.Bytes(), &body) if body["error"] != "Not found" { t.Errorf("expected 'Not found', got %s", body["error"]) } } func TestWriteErrorVariousCodes(t *testing.T) { tests := []struct { code int msg string }{ {400, "Bad request"}, {500, "Internal error"}, {403, "Forbidden"}, } for _, tc := range tests { w := httptest.NewRecorder() writeError(w, tc.code, tc.msg) if w.Code != tc.code { t.Errorf("expected %d, got %d", tc.code, w.Code) } } } func TestQueryInt(t *testing.T) { tests := []struct { name string url string key string def int expected int }{ {"valid", "/?limit=25", "limit", 50, 25}, {"missing", "/?other=5", "limit", 50, 50}, {"empty", "/?limit=", "limit", 50, 50}, {"invalid", "/?limit=abc", "limit", 50, 50}, {"zero", "/?limit=0", "limit", 50, 0}, {"negative", "/?limit=-1", "limit", 50, -1}, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { r := httptest.NewRequest("GET", tc.url, nil) got := queryInt(r, tc.key, tc.def) if got != tc.expected { t.Errorf("expected %d, got %d", tc.expected, got) } }) } } func TestMergeMap(t *testing.T) { t.Run("basic merge", func(t *testing.T) { base := map[string]interface{}{"a": 1, "b": 2} overlay := map[string]interface{}{"b": 3, "c": 4} result := mergeMap(base, overlay) if result["a"] != 1 { t.Errorf("expected 1, got %v", result["a"]) } if result["b"] != 3 { t.Errorf("expected 3 (overridden), got %v", result["b"]) } if result["c"] != 4 { t.Errorf("expected 4, got %v", result["c"]) } }) t.Run("nil overlay", func(t *testing.T) { base := map[string]interface{}{"a": 1} result := mergeMap(base, nil) if result["a"] != 1 { t.Errorf("expected 1, got %v", result["a"]) } }) t.Run("multiple overlays", func(t *testing.T) { base := map[string]interface{}{"a": 1} o1 := map[string]interface{}{"b": 2} o2 := map[string]interface{}{"c": 3, "a": 10} result := mergeMap(base, o1, o2) if result["a"] != 10 { t.Errorf("expected 10, got %v", result["a"]) } if result["b"] != 2 { t.Errorf("expected 2, got %v", result["b"]) } if result["c"] != 3 { t.Errorf("expected 3, got %v", result["c"]) } }) t.Run("empty base", func(t *testing.T) { result := mergeMap(map[string]interface{}{}, map[string]interface{}{"x": 5}) if result["x"] != 5 { t.Errorf("expected 5, got %v", result["x"]) } }) } func TestSafeAvg(t *testing.T) { tests := []struct { total, count float64 expected float64 }{ {100, 10, 10.0}, {0, 0, 0}, {33, 3, 11.0}, {10, 3, 3.3}, } for _, tc := range tests { got := safeAvg(tc.total, tc.count) if got != tc.expected { t.Errorf("safeAvg(%v, %v) = %v, want %v", tc.total, tc.count, got, tc.expected) } } } func TestRound(t *testing.T) { tests := []struct { val float64 places int want float64 }{ {3.456, 1, 3.5}, {3.444, 1, 3.4}, {3.456, 2, 3.46}, {0, 1, 0}, {100.0, 0, 100.0}, } for _, tc := range tests { got := round(tc.val, tc.places) if got != tc.want { t.Errorf("round(%v, %d) = %v, want %v", tc.val, tc.places, got, tc.want) } } } func TestPercentile(t *testing.T) { t.Run("empty", func(t *testing.T) { if percentile([]float64{}, 0.5) != 0 { t.Error("expected 0 for empty slice") } }) t.Run("single element", func(t *testing.T) { if percentile([]float64{42}, 0.5) != 42 { t.Error("expected 42") } }) t.Run("p50", func(t *testing.T) { sorted := []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} got := percentile(sorted, 0.5) if got != 6 { t.Errorf("expected 6 for p50, got %v", got) } }) t.Run("p95", func(t *testing.T) { sorted := []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} got := percentile(sorted, 0.95) if got != 10 { t.Errorf("expected 10 for p95, got %v", got) } }) t.Run("p100 clamps", func(t *testing.T) { sorted := []float64{1, 2, 3} got := percentile(sorted, 1.0) if got != 3 { t.Errorf("expected 3 for p100, got %v", got) } }) } func TestSortedCopy(t *testing.T) { original := []float64{5, 3, 1, 4, 2} sorted := sortedCopy(original) // Original should be unchanged if original[0] != 5 { t.Error("original should not be modified") } expected := []float64{1, 2, 3, 4, 5} for i, v := range sorted { if v != expected[i] { t.Errorf("index %d: expected %v, got %v", i, expected[i], v) } } // Empty slice empty := sortedCopy([]float64{}) if len(empty) != 0 { t.Error("expected empty slice") } } func TestLastN(t *testing.T) { arr := []map[string]interface{}{ {"id": 1}, {"id": 2}, {"id": 3}, {"id": 4}, {"id": 5}, } t.Run("n less than length", func(t *testing.T) { result := lastN(arr, 3) if len(result) != 3 { t.Errorf("expected 3, got %d", len(result)) } if result[0]["id"] != 3 { t.Errorf("expected id 3, got %v", result[0]["id"]) } }) t.Run("n greater than length", func(t *testing.T) { result := lastN(arr, 10) if len(result) != 5 { t.Errorf("expected 5, got %d", len(result)) } }) t.Run("n equals length", func(t *testing.T) { result := lastN(arr, 5) if len(result) != 5 { t.Errorf("expected 5, got %d", len(result)) } }) t.Run("empty", func(t *testing.T) { result := lastN([]map[string]interface{}{}, 5) if len(result) != 0 { t.Errorf("expected 0, got %d", len(result)) } }) } func TestSpaHandler(t *testing.T) { // Create a temp directory with test files dir := t.TempDir() os.WriteFile(filepath.Join(dir, "index.html"), []byte("SPA"), 0644) os.WriteFile(filepath.Join(dir, "app.js"), []byte("console.log('app')"), 0644) os.WriteFile(filepath.Join(dir, "style.css"), []byte("body{}"), 0644) fs := http.FileServer(http.Dir(dir)) handler := spaHandler(dir, fs) t.Run("existing JS file with cache control", func(t *testing.T) { req := httptest.NewRequest("GET", "/app.js", nil) w := httptest.NewRecorder() handler.ServeHTTP(w, req) if w.Code != 200 { t.Errorf("expected 200, got %d", w.Code) } cc := w.Header().Get("Cache-Control") if cc != "no-cache, no-store, must-revalidate" { t.Errorf("expected no-cache header for .js, got %s", cc) } }) t.Run("existing CSS file with cache control", func(t *testing.T) { req := httptest.NewRequest("GET", "/style.css", nil) w := httptest.NewRecorder() handler.ServeHTTP(w, req) if w.Code != 200 { t.Errorf("expected 200, got %d", w.Code) } cc := w.Header().Get("Cache-Control") if cc != "no-cache, no-store, must-revalidate" { t.Errorf("expected no-cache header for .css, got %s", cc) } }) t.Run("non-existent file falls back to index.html", func(t *testing.T) { req := httptest.NewRequest("GET", "/some/spa/route", nil) w := httptest.NewRecorder() handler.ServeHTTP(w, req) if w.Code != 200 { t.Errorf("expected 200, got %d", w.Code) } body := w.Body.String() if body != "SPA" { t.Errorf("expected SPA index.html content, got %s", body) } }) t.Run("existing HTML file", func(t *testing.T) { // Subdirectory with HTML file to avoid redirect from root /index.html subDir := filepath.Join(dir, "sub") os.Mkdir(subDir, 0755) os.WriteFile(filepath.Join(subDir, "page.html"), []byte("page"), 0644) req := httptest.NewRequest("GET", "/sub/page.html", nil) w := httptest.NewRecorder() handler.ServeHTTP(w, req) if w.Code != 200 { t.Errorf("expected 200, got %d", w.Code) } cc := w.Header().Get("Cache-Control") if cc != "no-cache, no-store, must-revalidate" { t.Errorf("expected no-cache header for .html, got %s", cc) } }) } func TestWriteJSON(t *testing.T) { w := httptest.NewRecorder() writeJSON(w, map[string]interface{}{"key": "value"}) if w.Code != 200 { t.Errorf("expected 200, got %d", w.Code) } ct := w.Header().Get("Content-Type") if ct != "application/json" { t.Errorf("expected application/json, got %s", ct) } var body map[string]interface{} json.Unmarshal(w.Body.Bytes(), &body) if body["key"] != "value" { t.Errorf("expected 'value', got %v", body["key"]) } }