package event import ( "strings" "sync" "testing" "time" ) var testNow = time.Date(2026, 8, 1, 9, 30, 0, 0, time.UTC) func TestNormalizeFillsDefaults(t *testing.T) { got := Event{Source: "poll:zenmoney", Title: " spent today "}.Normalize(testNow) if got.Title != "spent today" { t.Errorf("title = %q, want collapsed whitespace", got.Title) } if got.Priority != PriorityNormal { t.Errorf("priority = %q, want %q", got.Priority, PriorityNormal) } if got.Kind != KindFact { t.Errorf("kind = %q, want %q", got.Kind, KindFact) } if !got.OccurredAt.Equal(testNow) { t.Errorf("occurred_at = %v, want %v", got.OccurredAt, testNow) } } func TestNormalizeKeepsRealOccurredAt(t *testing.T) { // A wg handshake carries the handshake instant, not "now". Flattening that // would make every intake look like it happened at notice time. real := testNow.Add(-3 * time.Hour) got := Event{Source: "infer:wg", Title: "wg_handshake", OccurredAt: real}.Normalize(testNow) if !got.OccurredAt.Equal(real) { t.Errorf("occurred_at = %v, want the supplied %v", got.OccurredAt, real) } } func TestNormalizeTruncatesOnRuneBoundary(t *testing.T) { long := strings.Repeat("я", TitleMaxRunes+50) got := Event{Source: "rss:x", Title: long}.Normalize(testNow) r := []rune(got.Title) if len(r) != TitleMaxRunes+1 { // +1 for the ellipsis marker t.Fatalf("title runes = %d, want %d", len(r), TitleMaxRunes+1) } if r[len(r)-1] != '…' { t.Errorf("truncated title does not mark the cut: %q", string(r[len(r)-3:])) } for _, c := range r[:TitleMaxRunes] { if c != 'я' { t.Fatalf("truncation broke a rune: got %q", c) } } } func TestNormalizeRejectsUnknownPriority(t *testing.T) { got := Event{Source: "s", Title: "t", Priority: "URGENT!!"}.Normalize(testNow) if got.Priority != PriorityNormal { t.Errorf("priority = %q, want %q", got.Priority, PriorityNormal) } } func TestValid(t *testing.T) { base := Event{Source: "rss:tech", Kind: KindNote, Title: "заголовок", OccurredAt: testNow} if !base.Valid() { t.Fatal("well-formed event reported invalid") } for name, mut := range map[string]func(Event) Event{ "no source": func(e Event) Event { e.Source = ""; return e }, "no title": func(e Event) Event { e.Title = ""; return e }, "no time": func(e Event) Event { e.OccurredAt = time.Time{}; return e }, "bad kind": func(e Event) Event { e.Kind = "whatever"; return e }, } { if mut(base).Valid() { t.Errorf("%s: reported valid", name) } } } func TestSourceKind(t *testing.T) { cases := map[string]string{ "rss:tech": KindNote, "crawl:kernel": KindNote, "email:inbox": KindTask, "probe:netdata": KindHealth, "ambient:notif": KindFact, "tap:voice": KindFact, } for src, want := range cases { if got := SourceKind(src, KindFact); got != want { t.Errorf("SourceKind(%q) = %q, want %q", src, got, want) } } } func TestBusNilIsANoOp(t *testing.T) { // The whole adoption story depends on this: an intake path calls Publish // unconditionally, and a daemon with no bus behaves as it did before. var b *Bus b.Publish(Event{Source: "s", Kind: KindFact, Title: "t"}, testNow) b.Subscribe(func(Event) { t.Error("nil bus delivered to a subscriber") }) if got := b.Recent(10); got != nil { t.Errorf("Recent on nil bus = %v, want nil", got) } if got := b.Len(); got != 0 { t.Errorf("Len on nil bus = %d, want 0", got) } } func TestBusRecentIsNewestFirst(t *testing.T) { b := NewBus(8) for _, title := range []string{"one", "two", "three"} { b.Publish(Event{Source: "rss:t", Kind: KindNote, Title: title}, testNow) } got := b.Recent(0) if len(got) != 3 { t.Fatalf("len = %d, want 3", len(got)) } want := []string{"three", "two", "one"} for i, w := range want { if got[i].Title != w { t.Errorf("Recent()[%d] = %q, want %q", i, got[i].Title, w) } } if lim := b.Recent(2); len(lim) != 2 || lim[0].Title != "three" { t.Errorf("Recent(2) = %v, want the two newest", lim) } } func TestBusRingEvicts(t *testing.T) { b := NewBus(3) for _, title := range []string{"a", "b", "c", "d", "e"} { b.Publish(Event{Source: "s", Kind: KindFact, Title: title}, testNow) } if b.Len() != 3 { t.Fatalf("Len = %d, want the capacity 3", b.Len()) } got := b.Recent(0) want := []string{"e", "d", "c"} for i, w := range want { if got[i].Title != w { t.Errorf("Recent()[%d] = %q, want %q", i, got[i].Title, w) } } } func TestBusDropsInvalid(t *testing.T) { b := NewBus(4) b.Publish(Event{Kind: KindFact, Title: "no source"}, testNow) b.Publish(Event{Source: "s", Kind: KindFact}, testNow) if b.Len() != 0 { t.Errorf("Len = %d, want 0 — an envelope with no provenance must not be kept", b.Len()) } } func TestBusSubscriberPanicDoesNotBreakIntake(t *testing.T) { b := NewBus(4) var seen int b.Subscribe(func(Event) { panic("observer is broken") }) b.Subscribe(func(Event) { seen++ }) b.Publish(Event{Source: "s", Kind: KindFact, Title: "t"}, testNow) if seen != 1 { t.Errorf("healthy subscriber called %d times, want 1", seen) } if b.Len() != 1 { t.Errorf("event not recorded despite a panicking subscriber") } } func TestBusConcurrentPublish(t *testing.T) { b := NewBus(256) var wg sync.WaitGroup for i := 0; i < 16; i++ { wg.Add(1) go func() { defer wg.Done() for j := 0; j < 10; j++ { b.Publish(Event{Source: "s", Kind: KindFact, Title: "t"}, testNow) } }() } wg.Wait() if b.Len() != 160 { t.Errorf("Len = %d, want 160", b.Len()) } }