From d4caf45b2b9ea6a3276de792cf6f73085e66b1ae Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 16 Feb 2026 18:15:47 +0000 Subject: Add performance benchmarks, stress tests, and frontend perf tests Go benchmarks cover item CRUD/filter/sanitization, API endpoints (stream, item update, feed list), middleware stack (gzip, security headers, CSRF), and crawler pipeline (feed parsing, mocked crawl). Stress tests verify concurrent reads/writes and large dataset handling. Frontend perf tests measure template generation, DOM insertion, and store event throughput. New Makefile targets: bench, bench-short, stress, test-perf. https://claude.ai/code/session_01ChDVWFDrQoFjMYHpaLGr9s --- web/web_bench_test.go | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 web/web_bench_test.go (limited to 'web/web_bench_test.go') diff --git a/web/web_bench_test.go b/web/web_bench_test.go new file mode 100644 index 0000000..7897fc7 --- /dev/null +++ b/web/web_bench_test.go @@ -0,0 +1,92 @@ +package web + +import ( + "net/http" + "net/http/httptest" + "strings" + "testing" + + "adammathes.com/neko/config" +) + +func BenchmarkGzipMiddleware(b *testing.B) { + // Simulate a JSON API response + jsonPayload := `[` + strings.Repeat(`{"_id":"1","title":"Test Item","url":"https://example.com","description":"

This is a test description with enough content to be worth compressing in a real scenario

","read":false,"starred":false},`, 14) + + `{"_id":"15","title":"Last Item","url":"https://example.com/15","description":"

Final item

","read":false,"starred":false}]` + + handler := GzipMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.Write([]byte(jsonPayload)) + })) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + req := httptest.NewRequest("GET", "/api/stream", nil) + req.Header.Set("Accept-Encoding", "gzip") + rr := httptest.NewRecorder() + handler.ServeHTTP(rr, req) + } +} + +func BenchmarkSecurityHeaders(b *testing.B) { + handler := SecurityHeadersMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + })) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + req := httptest.NewRequest("GET", "/", nil) + rr := httptest.NewRecorder() + handler.ServeHTTP(rr, req) + } +} + +func BenchmarkCSRFMiddleware(b *testing.B) { + cfg := &config.Settings{SecureCookies: false} + handler := CSRFMiddleware(cfg, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + })) + + // Pre-generate a CSRF token by doing an initial GET + initReq := httptest.NewRequest("GET", "/", nil) + initRR := httptest.NewRecorder() + handler.ServeHTTP(initRR, initReq) + + var csrfCookie *http.Cookie + for _, c := range initRR.Result().Cookies() { + if c.Name == "csrf_token" { + csrfCookie = c + break + } + } + if csrfCookie == nil { + b.Fatal("no csrf cookie set") + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + req := httptest.NewRequest("POST", "/api/stream", nil) + req.AddCookie(csrfCookie) + req.Header.Set("X-CSRF-Token", csrfCookie.Value) + rr := httptest.NewRecorder() + handler.ServeHTTP(rr, req) + } +} + +func BenchmarkFullMiddlewareStack(b *testing.B) { + cfg := &config.Settings{SecureCookies: false} + inner := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.Write([]byte(`{"status":"ok"}`)) + }) + + handler := SecurityHeadersMiddleware(CSRFMiddleware(cfg, GzipMiddleware(inner))) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + req := httptest.NewRequest("GET", "/", nil) + req.Header.Set("Accept-Encoding", "gzip") + rr := httptest.NewRecorder() + handler.ServeHTTP(rr, req) + } +} -- cgit v1.2.3