Some checks failed
check / check (push) Has been cancelled
## Summary Expands the `/.well-known/healthcheck.json` endpoint with runtime statistics, giving operators visibility into server load and usage patterns. closes #74 ## New healthcheck fields | Field | Source | Description | |-------|--------|-------------| | `sessions` | DB | Current active session count | | `clients` | DB | Current connected client count | | `queuedLines` | DB | Total entries in client output queues | | `channels` | DB | Current channel count | | `connectionsSinceBoot` | Memory | Total client connections since server start | | `sessionsSinceBoot` | Memory | Total sessions created since server start | | `messagesSinceBoot` | Memory | Total PRIVMSG/NOTICE messages since server start | ## Implementation - **New `internal/stats` package** — atomic counters for boot-scoped metrics (`connectionsSinceBoot`, `sessionsSinceBoot`, `messagesSinceBoot`). Thread-safe via `sync/atomic`. - **New DB queries** — `GetClientCount()` and `GetQueueEntryCount()` for current snapshot counts. - **Healthcheck changes** — `Healthcheck()` now accepts `context.Context` to query the database. Response struct extended with all 7 new fields. DB-derived stats populated with graceful error handling (logged, not fatal). - **Counter instrumentation** — Increments added at: - `handleCreateSession` → `IncrSessions` + `IncrConnections` - `handleRegister` → `IncrSessions` + `IncrConnections` - `handleLogin` → `IncrConnections` (new client for existing session) - `handlePrivmsg` → `IncrMessages` (covers both PRIVMSG and NOTICE) - **Wired via fx** — `stats.Tracker` provided through Uber fx DI in both production and test setups. ## Tests - `internal/stats/stats_test.go` — 5 tests covering all counter operations (100% coverage) - `TestHealthcheckRuntimeStatsFields` — verifies all 7 new fields are present in the response - `TestHealthcheckRuntimeStatsValues` — end-to-end: creates a session, joins a channel, sends a message, then verifies counts are nonzero ## README Updated healthcheck documentation with full response shape, field descriptions, and project structure listing for `internal/stats/`. Co-authored-by: user <user@Mac.lan guest wan> Reviewed-on: #80 Co-authored-by: clawbot <clawbot@noreply.example.org> Co-committed-by: clawbot <clawbot@noreply.example.org>
118 lines
1.9 KiB
Go
118 lines
1.9 KiB
Go
package stats_test
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"git.eeqj.de/sneak/neoirc/internal/stats"
|
|
)
|
|
|
|
func TestNew(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tracker := stats.New()
|
|
if tracker == nil {
|
|
t.Fatal("expected non-nil tracker")
|
|
}
|
|
|
|
if tracker.ConnectionsSinceBoot() != 0 {
|
|
t.Errorf(
|
|
"expected 0 connections, got %d",
|
|
tracker.ConnectionsSinceBoot(),
|
|
)
|
|
}
|
|
|
|
if tracker.SessionsSinceBoot() != 0 {
|
|
t.Errorf(
|
|
"expected 0 sessions, got %d",
|
|
tracker.SessionsSinceBoot(),
|
|
)
|
|
}
|
|
|
|
if tracker.MessagesSinceBoot() != 0 {
|
|
t.Errorf(
|
|
"expected 0 messages, got %d",
|
|
tracker.MessagesSinceBoot(),
|
|
)
|
|
}
|
|
}
|
|
|
|
func TestIncrConnections(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tracker := stats.New()
|
|
|
|
tracker.IncrConnections()
|
|
tracker.IncrConnections()
|
|
tracker.IncrConnections()
|
|
|
|
got := tracker.ConnectionsSinceBoot()
|
|
if got != 3 {
|
|
t.Errorf(
|
|
"expected 3 connections, got %d", got,
|
|
)
|
|
}
|
|
}
|
|
|
|
func TestIncrSessions(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tracker := stats.New()
|
|
|
|
tracker.IncrSessions()
|
|
tracker.IncrSessions()
|
|
|
|
got := tracker.SessionsSinceBoot()
|
|
if got != 2 {
|
|
t.Errorf(
|
|
"expected 2 sessions, got %d", got,
|
|
)
|
|
}
|
|
}
|
|
|
|
func TestIncrMessages(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tracker := stats.New()
|
|
|
|
tracker.IncrMessages()
|
|
|
|
got := tracker.MessagesSinceBoot()
|
|
if got != 1 {
|
|
t.Errorf(
|
|
"expected 1 message, got %d", got,
|
|
)
|
|
}
|
|
}
|
|
|
|
func TestCountersAreIndependent(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tracker := stats.New()
|
|
|
|
tracker.IncrConnections()
|
|
tracker.IncrSessions()
|
|
tracker.IncrMessages()
|
|
tracker.IncrMessages()
|
|
|
|
if tracker.ConnectionsSinceBoot() != 1 {
|
|
t.Errorf(
|
|
"expected 1 connection, got %d",
|
|
tracker.ConnectionsSinceBoot(),
|
|
)
|
|
}
|
|
|
|
if tracker.SessionsSinceBoot() != 1 {
|
|
t.Errorf(
|
|
"expected 1 session, got %d",
|
|
tracker.SessionsSinceBoot(),
|
|
)
|
|
}
|
|
|
|
if tracker.MessagesSinceBoot() != 2 {
|
|
t.Errorf(
|
|
"expected 2 messages, got %d",
|
|
tracker.MessagesSinceBoot(),
|
|
)
|
|
}
|
|
}
|