Files
go-mana-prism/clerk_test.go
T
2026-05-09 20:15:00 -04:00

172 lines
3.8 KiB
Go

package prism
import (
"context"
"git.wisehodl.dev/jay/go-honeybee"
"github.com/stretchr/testify/assert"
"testing"
"time"
)
// ----------------------------------------------------------------------------
// Helpers
// ----------------------------------------------------------------------------
func mockInbox() (chan honeybee.InboxMessage, func(label string)) {
ch := make(chan honeybee.InboxMessage, 8)
inject := func(label string) {
ch <- honeybee.InboxMessage{
ID: "wss://test",
Data: []byte(`["` + label + `","payload"]`),
ReceivedAt: time.Now(),
}
}
return ch, inject
}
func makeClerk(inbox chan honeybee.InboxMessage) *Clerk {
known := map[string]struct{}{
"EVENT": {},
"EOSE": {},
"CLOSE": {},
}
return NewClerk(context.Background(), inbox, known, nil)
}
// ----------------------------------------------------------------------------
// Tests
// ----------------------------------------------------------------------------
func TestClerkRouting(t *testing.T) {
inbox, inject := mockInbox()
c := makeClerk(inbox)
subA, err := c.Subscribe(map[string]struct{}{"EVENT": {}}, 4)
assert.NoError(t, err)
subB, err := c.Subscribe(map[string]struct{}{"EVENT": {}, "EOSE": {}}, 4)
assert.NoError(t, err)
assert.NoError(t, c.Start())
inject("EVENT")
inject("EOSE")
// A receives exactly one letter (EVENT only)
Eventually(t, func() bool {
select {
case l := <-subA:
return string(l.Data) == `["EVENT","payload"]`
default:
return false
}
}, "subA should receive the EVENT letter")
Never(t, func() bool {
select {
case <-subA:
return true
default:
return false
}
}, "subA should receive no further letters")
// B receives two letters (EVENT and EOSE)
count := 0
Eventually(t, func() bool {
select {
case <-subB:
count++
default:
}
return count == 2
}, "subB should receive both letters")
}
func TestClerkStartup(t *testing.T) {
inbox, _ := mockInbox()
c := makeClerk(inbox)
assert.NoError(t, c.Start())
_, err := c.Subscribe(map[string]struct{}{"EVENT": {}}, 4)
assert.ErrorIs(t, err, ErrAlreadyStarted)
c.Close()
}
func TestClerkUnknownSubscriptionLabel(t *testing.T) {
inbox, _ := mockInbox()
c := makeClerk(inbox)
_, err := c.Subscribe(map[string]struct{}{"UNKNOWN": {}}, 4)
assert.ErrorIs(t, err, ErrUnknownLabel)
}
func TestClerkUnknownInboxLabel(t *testing.T) {
inbox, inject := mockInbox()
c := makeClerk(inbox)
// subscribe to every known label
sub, err := c.Subscribe(
map[string]struct{}{"EVENT": {}, "EOSE": {}, "CLOSE": {}}, 4)
assert.NoError(t, err)
assert.NoError(t, c.Start())
// inject a valid nostr label, but is not in the test label set
inject("NOTICE")
Never(t, func() bool {
select {
case <-sub:
return true
default:
return false
}
}, "no subscriber should receive an unknown label")
}
func TestClerkInboxClose(t *testing.T) {
inbox, _ := mockInbox()
c := makeClerk(inbox)
sub, err := c.Subscribe(map[string]struct{}{"EVENT": {}}, 4)
assert.NoError(t, err)
assert.NoError(t, c.Start())
// close the inbox as the pool would on shutdown
close(inbox)
// internal waitgroup should clear
Eventually(t, func() bool {
c.wg.Wait()
return true
}, "wg should clear")
// subscriptions remain open. Close() must be called to completely shut down
Never(t, func() bool {
_, ok := <-sub
return !ok
}, "sub should remain open")
}
func TestClerkClose(t *testing.T) {
inbox, _ := mockInbox()
c := makeClerk(inbox)
subA, err := c.Subscribe(map[string]struct{}{"EVENT": {}}, 4)
assert.NoError(t, err)
subB, err := c.Subscribe(map[string]struct{}{"EOSE": {}}, 4)
assert.NoError(t, err)
assert.NoError(t, c.Start())
c.Close()
Eventually(t, func() bool {
_, okA := <-subA
_, okB := <-subB
return !okA && !okB
}, "all subscriber channels should be closed after Close()")
}