@@ -11,15 +11,27 @@ import ( |
| 11 | 11 | "testing" |
| 12 | 12 | |
| 13 | 13 | "github.com/go-chi/chi/v5" |
| 14 | + |
| 15 | + "github.com/tenseleyFlow/shithub/internal/infra/metrics" |
| 14 | 16 | ) |
| 15 | 17 | |
| 16 | 18 | // /metrics MUST be served uncompressed even when the scraper advertises |
| 17 | 19 | // gzip support. Alloy 1.16 (and several other prom-compatible scrapers) |
| 18 | 20 | // mis-handle Content-Encoding: gzip and parse the raw 0x1f magic byte |
| 19 | 21 | // as text, failing the scrape silently with up=0. |
| 20 | | -func TestMetricsServedUncompressedWithGzipAccept(t *testing.T) { |
| 22 | +// |
| 23 | +// Two layers can produce gzip on this route: |
| 24 | +// 1. The chi Compress middleware in the public route group. |
| 25 | +// 2. promhttp's own DisableCompression knob (default false). |
| 26 | +// |
| 27 | +// Each test below pins one layer so a regression in either fires loud. |
| 28 | + |
| 29 | +// Layer 1: the route is mounted outside the Compress middleware group. |
| 30 | +func TestMetricsRouteBypassesCompressMiddleware(t *testing.T) { |
| 21 | 31 | t.Parallel() |
| 22 | 32 | |
| 33 | + // Stub handler — the test is about the middleware path, not the |
| 34 | + // real /metrics body shape. |
| 23 | 35 | handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
| 24 | 36 | w.Header().Set("Content-Type", "text/plain; version=0.0.4; charset=utf-8") |
| 25 | 37 | _, _ = io.WriteString(w, "# HELP test_metric A test metric\n# TYPE test_metric counter\ntest_metric 1\n") |
@@ -47,11 +59,31 @@ func TestMetricsServedUncompressedWithGzipAccept(t *testing.T) { |
| 47 | 59 | if enc := rec.Header().Get("Content-Encoding"); enc != "" { |
| 48 | 60 | t.Errorf("Content-Encoding = %q, want empty (Prometheus scrapers expect plain text)", enc) |
| 49 | 61 | } |
| 50 | | - body := rec.Body.String() |
| 51 | | - if !strings.Contains(body, "test_metric 1") { |
| 52 | | - t.Errorf("body missing metric text; got %q", body) |
| 62 | + if strings.HasPrefix(rec.Body.String(), "\x1f\x8b") { |
| 63 | + t.Error("body starts with gzip magic bytes — middleware compressed /metrics") |
| 64 | + } |
| 65 | +} |
| 66 | + |
| 67 | +// Layer 2: the real metrics.Handler() must have promhttp's compression |
| 68 | +// disabled. Without DisableCompression: true on HandlerOpts, promhttp |
| 69 | +// gzips when the client advertises Accept-Encoding: gzip — entirely |
| 70 | +// independent of our middleware. The post-hardening audit caught this: |
| 71 | +// the layer-1 test passed but the live droplet still emitted gzip. |
| 72 | +func TestMetricsHandlerPromhttpCompressionDisabled(t *testing.T) { |
| 73 | + t.Parallel() |
| 74 | + |
| 75 | + rec := httptest.NewRecorder() |
| 76 | + req := httptest.NewRequest(http.MethodGet, "/metrics", nil) |
| 77 | + req.Header.Set("Accept-Encoding", "gzip") |
| 78 | + metrics.Handler("", "").ServeHTTP(rec, req) |
| 79 | + |
| 80 | + if got := rec.Code; got != http.StatusOK { |
| 81 | + t.Fatalf("status = %d, want 200", got) |
| 82 | + } |
| 83 | + if enc := rec.Header().Get("Content-Encoding"); enc != "" { |
| 84 | + t.Errorf("Content-Encoding = %q, want empty (DisableCompression must be set on promhttp.HandlerOpts)", enc) |
| 53 | 85 | } |
| 54 | | - if strings.HasPrefix(body, "\x1f\x8b") { |
| 55 | | - t.Errorf("body starts with gzip magic bytes — middleware compressed /metrics") |
| 86 | + if strings.HasPrefix(rec.Body.String(), "\x1f\x8b") { |
| 87 | + t.Error("body starts with gzip magic bytes — promhttp compressed despite DisableCompression intent") |
| 56 | 88 | } |
| 57 | 89 | } |