658bda1d24
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
94 lines
2.5 KiB
Go
94 lines
2.5 KiB
Go
package events
|
|
|
|
import (
|
|
"fmt"
|
|
"testing"
|
|
|
|
panelv1 "github.com/dbledeez/panel/proto/panel/v1"
|
|
)
|
|
|
|
func stateEvent(instanceID string) *panelv1.Event {
|
|
return &panelv1.Event{Payload: &panelv1.Event_InstanceState{
|
|
InstanceState: &panelv1.InstanceStateUpdate{InstanceId: instanceID},
|
|
}}
|
|
}
|
|
|
|
func TestRingMonotonicIDsAndSince(t *testing.T) {
|
|
r := newReplayRing(8)
|
|
for i := 0; i < 5; i++ {
|
|
id := r.append(stateEvent(fmt.Sprintf("i%d", i)))
|
|
if want := uint64(i + 1); id != want {
|
|
t.Fatalf("append id = %d, want %d", id, want)
|
|
}
|
|
}
|
|
if r.lastID() != 5 {
|
|
t.Fatalf("lastID = %d, want 5", r.lastID())
|
|
}
|
|
out, ok := r.since(2)
|
|
if !ok {
|
|
t.Fatalf("since(2) reported a gap on an unfilled ring")
|
|
}
|
|
if len(out) != 3 || out[0].ID != 3 || out[2].ID != 5 {
|
|
t.Fatalf("since(2) = %+v, want ids 3..5", out)
|
|
}
|
|
// Fully caught up → empty, gapless.
|
|
if out, ok := r.since(5); len(out) != 0 || !ok {
|
|
t.Fatalf("since(lastID) = (%d events, ok=%v), want (0, true)", len(out), ok)
|
|
}
|
|
}
|
|
|
|
func TestRingOverflowReportsGap(t *testing.T) {
|
|
r := newReplayRing(4)
|
|
for i := 0; i < 10; i++ { // ids 1..10; ring retains 7..10
|
|
r.append(stateEvent("x"))
|
|
}
|
|
out, ok := r.since(3)
|
|
if ok {
|
|
t.Fatalf("since(3) after overflow should report a gap")
|
|
}
|
|
if len(out) != 4 || out[0].ID != 7 || out[3].ID != 10 {
|
|
t.Fatalf("since(3) = %d events (first=%d), want retained suffix 7..10", len(out), out[0].ID)
|
|
}
|
|
// Resume point exactly at oldest-1 is gapless.
|
|
if _, ok := r.since(6); !ok {
|
|
t.Fatalf("since(oldest-1) should be gapless")
|
|
}
|
|
}
|
|
|
|
func TestBusReplayFiltersAndStampedSubscribe(t *testing.T) {
|
|
b := New(16)
|
|
sub := b.SubscribeStamped(Filter{InstanceID: "a"})
|
|
defer sub.Close()
|
|
|
|
b.Publish(stateEvent("a")) // id 1
|
|
b.Publish(stateEvent("b")) // id 2
|
|
b.Publish(stateEvent("a")) // id 3
|
|
|
|
// Stamped subscription only sees matching events, with real ids.
|
|
got1 := <-sub.Stamped()
|
|
got2 := <-sub.Stamped()
|
|
if got1.ID != 1 || got2.ID != 3 {
|
|
t.Fatalf("stamped ids = %d,%d, want 1,3", got1.ID, got2.ID)
|
|
}
|
|
|
|
// Replay applies the same filter.
|
|
out, ok := b.Replay(0, Filter{InstanceID: "a"})
|
|
if !ok || len(out) != 2 || out[0].ID != 1 || out[1].ID != 3 {
|
|
t.Fatalf("Replay = %+v ok=%v, want ids 1,3 gapless", out, ok)
|
|
}
|
|
if b.LastID() != 3 {
|
|
t.Fatalf("LastID = %d, want 3", b.LastID())
|
|
}
|
|
}
|
|
|
|
func TestBusDroppedTotal(t *testing.T) {
|
|
b := New(1)
|
|
sub := b.Subscribe(Filter{})
|
|
defer sub.Close()
|
|
b.Publish(stateEvent("a")) // fills the 1-slot buffer
|
|
b.Publish(stateEvent("a")) // dropped
|
|
if b.DroppedTotal() != 1 {
|
|
t.Fatalf("DroppedTotal = %d, want 1", b.DroppedTotal())
|
|
}
|
|
}
|