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

163 lines
3.8 KiB
Go

// SPDX-FileCopyrightText: 2021 The NGI Pointer Secure-Scuttlebutt Team of 2020/2021
//
// SPDX-License-Identifier: MIT
package go_test
import (
"bytes"
"context"
"fmt"
"os"
"path/filepath"
"testing"
"time"
"github.com/ssbc/go-muxrpc/v2"
"github.com/stretchr/testify/require"
"github.com/ssbc/go-ssb-room/v2/muxrpc/handlers/tunnel/server"
)
// peers not on the members list can't connect
func TestStaleMembers(t *testing.T) {
testInit(t)
r := require.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
// two new clients, connected to server automaticaly
// default to member during makeNamedTestBot
tal := ts.makeTestClient("tal") // https://en.wikipedia.org/wiki/Tal_(name)
srh := ts.makeTestClient("srh") // https://en.wikipedia.org/wiki/Sarah
// announce srh so that tal could connect
_, has := ts.srv.StateManager.Has(srh.feed)
// shut down srh
srh.Terminate()
time.Sleep(1 * time.Second)
_, has = ts.srv.StateManager.Has(srh.feed)
r.False(has, "srh shouldn't be connected")
// try to connect srh
var arg server.ConnectArg
arg.Portal = ts.srv.Whoami()
arg.Target = srh.feed
src, snk, err := tal.Duplex(ctx, muxrpc.TypeBinary, muxrpc.Method{"room", "connect"}, arg)
r.NoError(err)
time.Sleep(1 * time.Second) // let server respond
// assert cant read
r.False(src.Next(ctx), "source should be cancled")
r.Error(src.Err(), "source should have an error")
// assert cant write
testKexMsg := []byte("fake keyexchange")
_, err = snk.Write(testKexMsg)
r.Error(err, "stream should should be canceled")
// restart srh
oldSrh := srh.feed
srh = ts.makeTestClient("srh")
r.True(oldSrh.Equal(srh.feed))
t.Log("restarted srh")
time.Sleep(1 * time.Second) // let server respond
// announce srh so that tal can connect
var ok bool
err = srh.Async(ctx, &ok, muxrpc.TypeJSON, muxrpc.Method{"tunnel", "announce"})
r.NoError(err)
r.True(ok)
t.Log("announced srh again")
testKexReply := []byte("fake kex reply")
// prepare srh for incoming call
receivedCall := make(chan struct{})
receivedKexMessage := make(chan struct{})
srh.mockedHandler.HandledCalls(func(m muxrpc.Method) bool { return m.String() == "tunnel.connect" })
srh.mockedHandler.HandleCallCalls(func(ctx context.Context, req *muxrpc.Request) {
m := req.Method.String()
t.Log("received call: ", m)
if m != "tunnel.connect" {
return
}
t.Log("correct call received")
close(receivedCall)
// receive a msg
src, err := req.ResponseSource()
if err != nil {
panic(fmt.Errorf("expected source for duplex call: %s", err))
}
if !src.Next(ctx) {
err = fmt.Errorf("did not get message from source: %v", src.Err())
panic(err)
}
gotKexMsg, err := src.Bytes()
if err != nil {
panic(err)
}
if !bytes.Equal(testKexMsg, gotKexMsg) {
panic(fmt.Sprintf("wrong kex message: %q", gotKexMsg))
}
close(receivedKexMessage)
// send a msg
snk, err := req.ResponseSink()
if err != nil {
panic(fmt.Errorf("expected sink for duplex call: %s", err))
}
snk.Write(testKexReply)
})
// 2nd try to connect (should work)
src, snk, err = tal.Duplex(ctx, muxrpc.TypeBinary, muxrpc.Method{"tunnel", "connect"}, arg)
r.NoError(err)
<-receivedCall
_, err = snk.Write(testKexMsg)
r.NoError(err, "stream should should be canceled")
<-receivedKexMessage
// assert can read
r.True(src.Next(ctx), "source should be cancled")
r.NoError(src.Err(), "source should have an error")
gotKexMsgFromSrh, err := src.Bytes()
r.NoError(err)
r.Equal(testKexReply, gotKexMsgFromSrh)
// shut everythign down
ts.srv.Shutdown()
tal.Terminate()
srh.Terminate()
ts.srv.Close()
// wait for all muxrpc serve()s to exit
r.NoError(ts.serveGroup.Wait())
cancel()
}