multica/server/internal/analytics/client_test.go

120 lines
2.9 KiB
Go

package analytics
import (
"encoding/json"
"io"
"net/http"
"net/http/httptest"
"sync"
"testing"
"time"
)
func TestNoopClient(t *testing.T) {
c := NoopClient{}
c.Capture(Event{Name: "foo"})
c.Close()
}
func TestPostHogClient_Batching(t *testing.T) {
var (
mu sync.Mutex
received [][]captureItem
)
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/batch/" {
t.Errorf("unexpected path %s", r.URL.Path)
}
body, _ := io.ReadAll(r.Body)
var payload capturePayload
if err := json.Unmarshal(body, &payload); err != nil {
t.Errorf("decode payload: %v", err)
}
if payload.APIKey != "test-key" {
t.Errorf("api_key = %q, want test-key", payload.APIKey)
}
mu.Lock()
received = append(received, payload.Batch)
mu.Unlock()
w.WriteHeader(http.StatusOK)
}))
defer srv.Close()
c := NewPostHogClient(PostHogConfig{
APIKey: "test-key",
Host: srv.URL,
BatchSize: 2,
FlushEvery: time.Hour, // irrelevant, we hit the size trigger
})
c.Capture(Event{Name: "signup", DistinctID: "u1", WorkspaceID: "w1"})
c.Capture(Event{Name: "workspace_created", DistinctID: "u1", WorkspaceID: "w1"})
c.Close() // drains
mu.Lock()
defer mu.Unlock()
total := 0
for _, b := range received {
total += len(b)
}
if total != 2 {
t.Fatalf("received %d events, want 2 (batches=%d)", total, len(received))
}
// Both events should carry workspace_id in properties.
for _, batch := range received {
for _, item := range batch {
if item.Properties["workspace_id"] != "w1" {
t.Errorf("missing workspace_id on event %s", item.Event)
}
if item.DistinctID != "u1" {
t.Errorf("distinct_id = %q, want u1", item.DistinctID)
}
}
}
}
func TestPostHogClient_DropsWhenFull(t *testing.T) {
// Handler blocks so batches never flush — queue will fill up.
block := make(chan struct{})
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
<-block
}))
defer srv.Close()
defer close(block)
c := NewPostHogClient(PostHogConfig{
APIKey: "test-key",
Host: srv.URL,
QueueSize: 2,
BatchSize: 1,
FlushEvery: time.Hour,
})
defer c.Close()
// First event may be consumed by the worker (which is now blocked in send).
// Next events will sit in the queue (cap=2) until it's full and then drop.
for i := 0; i < 20; i++ {
c.Capture(Event{Name: "spam", DistinctID: "u"})
}
// Give the worker a chance to pick up at least one.
time.Sleep(50 * time.Millisecond)
if c.dropped.Load() == 0 {
t.Fatalf("expected some drops when queue saturated")
}
}
func TestEmailDomain(t *testing.T) {
cases := map[string]string{
"a@example.com": "example.com",
"user@Company.co.uk": "company.co.uk",
"": "",
"no-at": "",
"trailing@": "",
}
for in, want := range cases {
if got := emailDomain(in); got != want {
t.Errorf("emailDomain(%q) = %q, want %q", in, got, want)
}
}
}