go-ssb-room/muxrpc/test/go/attendants_test.go

198 lines
5.1 KiB
Go

// SPDX-FileCopyrightText: 2021 The NGI Pointer Secure-Scuttlebutt Team of 2020/2021
//
// SPDX-License-Identifier: MIT
package go_test
import (
"context"
"encoding/json"
"io"
"os"
"path/filepath"
"testing"
"time"
"github.com/ssbc/go-muxrpc/v2"
refs "github.com/ssbc/go-ssb-refs"
"github.com/ssbc/go-ssb-room/v2/muxrpc/handlers/tunnel/server"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// this tests the new room.attendants call
// basically the same test as endpoints_test
func TestRoomAttendants(t *testing.T) {
testInit(t)
r := require.New(t)
a := assert.New(t)
testPath := filepath.Join("testrun", t.Name())
os.RemoveAll(testPath)
ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
t.Cleanup(cancel)
// create the roomsrv
ts := makeNamedTestBot(t, "server", ctx, nil)
ctx = ts.ctx
// three new clients, connected to server automaticaly
alf := ts.makeTestClient("alf")
bre := ts.makeTestClient("bre")
carl := ts.makeTestClient("carl")
// start with carl
// ===============
carlsSource, err := carl.Source(ctx, muxrpc.TypeJSON, muxrpc.Method{"room", "attendants"})
r.NoError(err)
t.Log("carl opened attendants")
// first message should be initial state
a.True(carlsSource.Next(ctx))
var initState server.AttendantsInitialState
decodeJSONsrc(t, carlsSource, &initState)
a.Equal("state", initState.Type)
a.Len(initState.IDs, 1)
a.True(initState.IDs[0].Equal(carl.feed))
announcementsForCarl := make(announcements)
go logAttendantsStream(ts, carlsSource, "carl", announcementsForCarl)
time.Sleep(1 * time.Second) // give some time to process new events
a.Len(announcementsForCarl, 0, "none yet")
// let alf join the room
// =====================
alfsSource, err := alf.Source(ctx, muxrpc.TypeJSON, muxrpc.Method{"room", "attendants"})
r.NoError(err)
// first message should be initial state
a.True(alfsSource.Next(ctx))
decodeJSONsrc(t, alfsSource, &initState)
a.Equal("state", initState.Type)
a.Len(initState.IDs, 2)
assertListContains(t, initState.IDs, carl.feed)
assertListContains(t, initState.IDs, alf.feed)
announcementsForAlf := make(announcements)
go logAttendantsStream(ts, alfsSource, "alf", announcementsForAlf)
time.Sleep(1 * time.Second) // give some time to process new events
// assert what alf saw
var seen bool
a.Len(announcementsForAlf, 0, "none yet")
// assert what carl saw
_, seen = announcementsForCarl[alf.feed.String()]
a.True(seen, "carl saw alf")
// let bre join the room
bresSource, err := bre.Source(ctx, muxrpc.TypeJSON, muxrpc.Method{"room", "attendants"})
r.NoError(err)
// first message should be initial state
a.True(bresSource.Next(ctx))
decodeJSONsrc(t, bresSource, &initState)
a.Equal("state", initState.Type)
a.Len(initState.IDs, 3)
assertListContains(t, initState.IDs, alf.feed)
assertListContains(t, initState.IDs, bre.feed)
assertListContains(t, initState.IDs, carl.feed)
announcementsForBre := make(announcements)
go logAttendantsStream(ts, bresSource, "bre", announcementsForBre)
time.Sleep(1 * time.Second) // give some time to process new events
a.Len(announcementsForBre, 0, "none yet")
// the two present people saw her
_, seen = announcementsForAlf[bre.feed.String()]
a.True(seen, "alf saw bre")
_, seen = announcementsForCarl[bre.feed.String()]
a.True(seen, "carl saw alf")
// shutdown alf first
alf.Terminate()
time.Sleep(1 * time.Second) // give some time to process new events
// bre and arl should have removed him
_, seen = announcementsForBre[alf.feed.String()]
a.False(seen, "alf should be gone for bre")
_, seen = announcementsForCarl[alf.feed.String()]
a.False(seen, "alf should be gone for carl")
// terminate server and the clients
ts.srv.Shutdown()
bre.Terminate()
carl.Terminate()
ts.srv.Close()
// wait for all muxrpc serve()s to exit
r.NoError(ts.serveGroup.Wait())
cancel()
}
func assertListContains(t *testing.T, lst []refs.FeedRef, who refs.FeedRef) {
var found = false
for _, feed := range lst {
if feed.Equal(who) {
found = true
}
}
if !found {
t.Errorf("did not find %s in list of %d", who.ShortSigil(), len(lst))
}
}
func decodeJSONsrc(t *testing.T, src *muxrpc.ByteSource, val interface{}) {
err := src.Reader(func(rd io.Reader) error {
return json.NewDecoder(rd).Decode(val)
})
if err != nil {
t.Fatal(err)
}
}
// consume endpoint messaes and put each peer on the passed map
func logAttendantsStream(ts *testSession, src *muxrpc.ByteSource, who string, a announcements) {
var update server.AttendantsUpdate
for src.Next(ts.ctx) {
body, err := src.Bytes()
if err != nil {
panic(err)
}
// ts.t.Log(who, "got body:", string(body))
err = json.Unmarshal(body, &update)
if err != nil {
panic(err)
}
ts.t.Log(who, "got an update:", update.Type, update.ID.ShortSigil())
switch update.Type {
case "joined":
a[update.ID.String()] = struct{}{}
case "left":
delete(a, update.ID.String())
default:
ts.t.Fatalf("%s: unexpected update type: %v", who, update.Type)
}
}
if err := src.Err(); err != nil {
ts.t.Log(who, "source errored: ", err)
return
}
ts.t.Log(who, "stream closed")
}