Add server-side web usage metrics with !petestats command
Track per-page/per-channel view counts and a privacy-preserving daily unique-visitor estimate (salted IP+UA hash, salt rotated daily and never persisted). No third-party analytics, no JS beacon. Surfaced via the admin-gated !petestats Matrix command (named to avoid an existing !stats bot in the rooms).
This commit is contained in:
42
internal/storage/metrics_test.go
Normal file
42
internal/storage/metrics_test.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package storage
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestMetrics_ViewsAndUniques(t *testing.T) {
|
||||
setupTestDB(t)
|
||||
|
||||
// Three views on home, two on gaming.
|
||||
RecordPageView("home")
|
||||
RecordPageView("home")
|
||||
RecordPageView("home")
|
||||
RecordPageView("gaming")
|
||||
RecordPageView("gaming")
|
||||
|
||||
// Two distinct visitors today; the repeat token must not double-count.
|
||||
RecordVisitor("tok-a")
|
||||
RecordVisitor("tok-a")
|
||||
RecordVisitor("tok-b")
|
||||
|
||||
m := GetMetricsSummary()
|
||||
|
||||
if m.TotalViews != 5 {
|
||||
t.Errorf("TotalViews = %d, want 5", m.TotalViews)
|
||||
}
|
||||
if m.ViewsToday != 5 {
|
||||
t.Errorf("ViewsToday = %d, want 5", m.ViewsToday)
|
||||
}
|
||||
if m.UniquesToday != 2 {
|
||||
t.Errorf("UniquesToday = %d, want 2 (deduped)", m.UniquesToday)
|
||||
}
|
||||
if len(m.Pages) == 0 || m.Pages[0].Path != "home" || m.Pages[0].Total != 3 {
|
||||
t.Errorf("Pages[0] = %+v, want home with 3 views first", m.Pages)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMetrics_EmptyIsZero(t *testing.T) {
|
||||
setupTestDB(t)
|
||||
m := GetMetricsSummary()
|
||||
if m.TotalViews != 0 || m.ViewsToday != 0 || m.UniquesToday != 0 || len(m.Pages) != 0 {
|
||||
t.Errorf("expected zero-value summary, got %+v", m)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user