package chat
import (
"reflect"
"testing"
"time"
"craftdesign.group/quarry/internal/events"
)
// Presence is an open stream and nothing else: joining puts you in the
// room at once, leaving takes you out at once, and the two are visible
// to every instance on the bus — which here is one instance with two
// Presence values, the shape two servers behind a balancer would have.
func TestPresenceIsSharedAndImmediate(t *testing.T) {
ev, err := events.Start(t.TempDir())
if err != nil {
t.Fatal(err)
}
defer ev.Close()
a, err := NewPresence(ev.Conn())
if err != nil {
t.Fatal(err)
}
defer a.Close()
b, err := NewPresence(ev.Conn())
if err != nil {
t.Fatal(err)
}
defer b.Close()
tick, stop := b.Changed("A")
defer stop()
leave := a.Join("A", "dan")
waitTick(t, tick, "dan's arrival")
if got := b.Present("A"); !reflect.DeepEqual(got, []string{"dan"}) {
t.Fatalf("the other instance sees %v, want [dan]", got)
}
// Another channel is another room.
if got := b.Present("B"); len(got) != 0 {
t.Fatalf("B sees A's reader: %v", got)
}
leave2 := a.Join("A", "eve")
waitTick(t, tick, "eve's arrival")
if got := b.Present("A"); !reflect.DeepEqual(got, []string{"dan", "eve"}) {
t.Fatalf("after two joins %v, want [dan eve] (sorted)", got)
}
leave()
waitTick(t, tick, "dan's departure")
if got := b.Present("A"); !reflect.DeepEqual(got, []string{"eve"}) {
t.Fatalf("after dan left %v, want [eve]", got)
}
leave2()
// A second leave is a no-op, not a second goodbye.
leave2()
waitTick(t, tick, "eve's departure")
if got := b.Present("A"); len(got) != 0 {
t.Fatalf("after everybody left %v, want empty", got)
}
}
// Nobody signed in joins nowhere: an anonymous stream must not put an
// empty name in the room.
func TestPresenceIgnoresAnonymous(t *testing.T) {
ev, err := events.Start(t.TempDir())
if err != nil {
t.Fatal(err)
}
defer ev.Close()
p, err := NewPresence(ev.Conn())
if err != nil {
t.Fatal(err)
}
defer p.Close()
leave := p.Join("A", "")
leave()
time.Sleep(100 * time.Millisecond)
if got := p.Present("A"); len(got) != 0 {
t.Fatalf("an anonymous join was recorded: %v", got)
}
}
func waitTick(t *testing.T, tick <-chan struct{}, what string) {
t.Helper()
select {
case <-tick:
case <-time.After(3 * time.Second):
t.Fatalf("no change notified for %s", what)
}
}