From c9c5def76c3a3340373143f846454b795d296c82 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 16 Feb 2026 17:47:50 +0000 Subject: Add performance and stress test plan Comprehensive plan covering Go backend benchmarks (item/filter/API/middleware/crawler), stress tests (concurrent reads/writes, large datasets), and frontend perf tests (render throughput, store event cascades). Saved to both PLAN.md and DOCS/ for reference by other agents. https://claude.ai/code/session_01ChDVWFDrQoFjMYHpaLGr9s --- PLAN.md | 143 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100644 PLAN.md (limited to 'PLAN.md') diff --git a/PLAN.md b/PLAN.md new file mode 100644 index 0000000..7eab452 --- /dev/null +++ b/PLAN.md @@ -0,0 +1,143 @@ +# Performance & Stress Test Plan for Neko + +## Overview + +This plan adds Go benchmarks and frontend performance tests that establish +baselines for key operations and catch regressions over time. The tests are +designed to run quickly in CI (`make bench` for Go, `npm run test:perf` for +frontend) while also supporting longer stress runs via flags. + +--- + +## 1. Go Backend Benchmarks (`*_bench_test.go`) + +### 1a. `models/item/item_bench_test.go` — Item / Filter / Sanitization + +| Benchmark | What it measures | +|---|---| +| `BenchmarkItemCreate` | Single item INSERT (baseline write speed) | +| `BenchmarkItemCreateBatch100` | 100 sequential inserts (crawler-like workload) | +| `BenchmarkFilter_Empty` | Filter query on empty result set | +| `BenchmarkFilter_15Items` | Filter returning 15 items (page size) with sanitization | +| `BenchmarkFilter_WithFTS` | Filter with full-text search query | +| `BenchmarkFilter_WithImageProxy` | Filter with image-proxy rewriting enabled | +| `BenchmarkFilterPolicy` | Isolated `filterPolicy()` creation + sanitize call | +| `BenchmarkRewriteImages` | Isolated `rewriteImages()` on HTML with 5 images | +| `BenchmarkItemSave` | Single item UPDATE (read_state/starred toggle) | + +### 1b. `api/api_bench_test.go` — HTTP API Endpoints + +| Benchmark | What it measures | +|---|---| +| `BenchmarkHandleStream` | Full `/stream` request→response cycle (httptest) | +| `BenchmarkHandleStreamWithSearch` | `/stream?q=...` with FTS | +| `BenchmarkHandleItemUpdate` | `PUT /item/{id}` toggle read state | +| `BenchmarkHandleFeedList` | `GET /feed` list all feeds | + +### 1c. `web/web_bench_test.go` — Middleware + +| Benchmark | What it measures | +|---|---| +| `BenchmarkGzipMiddleware` | Gzip compression of a JSON response | +| `BenchmarkSecurityHeaders` | Security header injection overhead | +| `BenchmarkCSRFMiddleware` | CSRF token check overhead | + +### 1d. `internal/crawler/crawler_bench_test.go` — Crawl Pipeline + +| Benchmark | What it measures | +|---|---| +| `BenchmarkParseFeed` | gofeed.Parse on a realistic RSS feed | +| `BenchmarkCrawlFeedMocked` | Full CrawlFeed with mocked HTTP (measures parse + DB insert pipeline) | + +--- + +## 2. Go Stress / Regression Tests + +### 2a. `api/api_stress_test.go` + +| Test | What it measures | +|---|---| +| `TestStress_ConcurrentStreamReads` | 50 concurrent goroutines hitting `/stream` | +| `TestStress_ConcurrentItemUpdates` | 50 goroutines toggling read/starred on different items | +| `TestStress_LargeDataset` | Seed 1000 items, verify filter/pagination still works correctly and under threshold | + +These use `testing.Short()` to skip in normal `go test` runs and only execute +during `go test -run Stress` or `make stress`. + +--- + +## 3. Frontend Performance Tests (`frontend-vanilla/src/perf/`) + +### 3a. `renderItems.perf.test.ts` + +| Test | What it measures | +|---|---| +| `renderItems with 100 items completes under 50ms` | DOM rendering throughput | +| `renderItems with 500 items completes under 200ms` | Stress DOM rendering | +| `createFeedItem renders 1000 items under 100ms` | Template generation throughput | + +### 3b. `store.perf.test.ts` + +| Test | What it measures | +|---|---| +| `store.setItems with 500 items + event dispatch under 10ms` | Store update + event overhead | +| `store.setFeeds with 200 feeds under 5ms` | Feed list update | +| `rapid filter changes (100 toggles) under 50ms` | Event cascade throughput | + +--- + +## 4. Makefile Integration + +```makefile +bench: + $(GO) test -bench=. -benchmem -count=3 -run=^$$ ./... + +bench-short: + $(GO) test -bench=. -benchmem -count=1 -run=^$$ ./... + +stress: + $(GO) test -run=TestStress -count=1 -timeout=120s ./... + +test-perf: + cd frontend-vanilla && $(NPM) test -- --run src/perf/ +``` + +--- + +## 5. Files to Create + +``` +models/item/item_bench_test.go +api/api_bench_test.go +web/web_bench_test.go +internal/crawler/crawler_bench_test.go +api/api_stress_test.go +frontend-vanilla/src/perf/renderItems.perf.test.ts +frontend-vanilla/src/perf/store.perf.test.ts +Makefile (add bench/stress/test-perf targets) +``` + +## 6. Files to Modify + +- `Makefile` — add `bench`, `bench-short`, `stress`, `test-perf` targets + +--- + +## Key Design Decisions + +1. **Go benchmarks use in-memory SQLite** — each benchmark creates a temp DB, + seeds data, then runs `b.ResetTimer()` so setup isn't counted. + +2. **API benchmarks use `httptest`** — no network overhead, measures pure + handler performance. + +3. **Frontend perf tests use `performance.now()`** — jsdom doesn't have real + rendering but we can measure template generation and DOM manipulation time. + Thresholds are generous to avoid flaky CI but tight enough to catch 10x + regressions. + +4. **Stress tests are opt-in** — behind `TestStress_` prefix and skipped with + `testing.Short()` so they don't slow normal test runs. + +5. **`-benchmem` is default** — allocation tracking catches memory regressions + even when wall-clock time looks fine. -- cgit v1.2.3