Files
meshcore-analyzer/cmd/server/multi_node_cap_test.go
T
nullrouten0andClaude Mythos 5.1 548e4cd4a4 fix(api): cap the nodes= list on /api/packets at 50 entries (#2120)
Each entry in the comma-separated `nodes=` list on `GET /api/packets`
costs one SQLite lookup (`resolveNodePubkey`) while the packet store's
read lock is held. A 1 MB URL fits about 15,000 entries. On a test
instance 12,000 entries took 1.3 s per request, against 0.9 ms for one
entry, and the lock stalls the poller's writes for that long. A few
parallel clients can keep the site busy and the live feed stale.

**Fix:** lists longer than 50 entries get HTTP 400 with a clear message.
No UI page sends more than a handful.

**Tests:** `multi_node_cap_test.go` — 50 entries return 200, 51 return
400. Full `go test ./...` in `cmd/server` passes.

Running in production on our instance since 2026-10-07.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Mythos 5.1 <noreply@anthropic.com>
2026-10-08 15:58:07 +02:00

39 lines
1.1 KiB
Go

package main
import (
"fmt"
"net/http"
"net/http/httptest"
"strings"
"testing"
)
// GET /api/packets?nodes=... resolves every entry with a SQLite lookup while
// holding the packet store's read lock, so the list must be capped.
func TestMultiNodePacketsListCap(t *testing.T) {
_, router := setupTestServerWithAPIKey(t, "")
get := func(n int) *httptest.ResponseRecorder {
keys := make([]string, n)
for i := range keys {
keys[i] = fmt.Sprintf("%064x", i+1)
}
req := httptest.NewRequest("GET", "/api/packets?nodes="+strings.Join(keys, ",")+"&limit=1", nil)
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
return w
}
t.Run("at the cap is accepted", func(t *testing.T) {
if w := get(maxMultiNodePubkeys); w.Code != http.StatusOK {
t.Fatalf("expected 200 for %d nodes, got %d (body: %s)", maxMultiNodePubkeys, w.Code, w.Body.String())
}
})
t.Run("one over the cap is rejected with 400", func(t *testing.T) {
if w := get(maxMultiNodePubkeys + 1); w.Code != http.StatusBadRequest {
t.Fatalf("expected 400 for %d nodes, got %d (body: %s)", maxMultiNodePubkeys+1, w.Code, w.Body.String())
}
})
}