2021-10-08 12:39:31 +00:00
// SPDX-FileCopyrightText: 2021 The NGI Pointer Secure-Scuttlebutt Team of 2020/2021
//
2021-01-26 17:33:29 +00:00
// SPDX-License-Identifier: MIT
// Package nodejs_test contains test scenarios and helpers to run interoparability tests against the javascript implementation.
2021-01-25 10:39:05 +00:00
package nodejs_test
2021-01-26 17:33:29 +00:00
import (
"bufio"
"context"
"crypto/rand"
"encoding/base64"
2021-01-27 09:27:13 +00:00
"errors"
2021-01-26 17:33:29 +00:00
"fmt"
2021-03-17 14:01:34 +00:00
"io"
2021-01-26 17:33:29 +00:00
mrand "math/rand"
"net"
"os"
"os/exec"
"path/filepath"
"testing"
2021-03-17 14:01:34 +00:00
"time"
2021-01-26 18:03:31 +00:00
"github.com/stretchr/testify/assert"
2021-01-26 17:33:29 +00:00
"github.com/stretchr/testify/require"
"go.cryptoscope.co/muxrpc/v2/debug"
2021-01-27 09:02:27 +00:00
"go.cryptoscope.co/netwrap"
2021-05-21 07:27:12 +00:00
"go.mindeco.de/log"
2021-03-23 15:50:25 +00:00
"golang.org/x/sync/errgroup"
2021-01-26 17:33:29 +00:00
2021-05-31 12:50:44 +00:00
"github.com/ssb-ngi-pointer/go-ssb-room/v2/internal/maybemod/testutils"
"github.com/ssb-ngi-pointer/go-ssb-room/v2/internal/network"
"github.com/ssb-ngi-pointer/go-ssb-room/v2/internal/signinwithssb"
"github.com/ssb-ngi-pointer/go-ssb-room/v2/roomdb"
"github.com/ssb-ngi-pointer/go-ssb-room/v2/roomdb/mockdb"
"github.com/ssb-ngi-pointer/go-ssb-room/v2/roomsrv"
2021-03-23 15:50:25 +00:00
refs "go.mindeco.de/ssb-refs"
2021-01-26 17:33:29 +00:00
)
func init ( ) {
err := os . RemoveAll ( "testrun" )
if err != nil {
fmt . Println ( "failed to clean testrun dir" )
panic ( err )
}
}
type testSession struct {
t * testing . T
info log . Logger
repo string
2021-01-27 09:02:27 +00:00
keySHS [ ] byte
2021-01-26 17:33:29 +00:00
done errgroup . Group
ctx context . Context
cancel context . CancelFunc
}
// TODO: restrucuture so that we can test both (default and random net keys) with the same Code
// rolls random values for secret-handshake app-key and HMAC
func newRandomSession ( t * testing . T ) * testSession {
appKey := make ( [ ] byte , 32 )
rand . Read ( appKey )
2021-01-27 09:02:27 +00:00
return newSession ( t , appKey )
2021-01-26 17:33:29 +00:00
}
// if appKey is nil, the default value is used
// if hmac is nil, the object string is signed instead
2021-01-27 09:02:27 +00:00
func newSession ( t * testing . T , appKey [ ] byte ) * testSession {
2021-01-26 17:33:29 +00:00
repo := filepath . Join ( "testrun" , t . Name ( ) )
err := os . RemoveAll ( repo )
if err != nil {
t . Errorf ( "remove testrun folder (%s) for this test: %s" , repo , err )
}
ts := & testSession {
2021-01-27 09:02:27 +00:00
info : testutils . NewRelativeTimeLogger ( nil ) ,
repo : repo ,
t : t ,
keySHS : appKey ,
2021-01-26 17:33:29 +00:00
}
// todo: hook into deadline
ts . ctx , ts . cancel = context . WithCancel ( context . Background ( ) )
return ts
}
2021-03-11 17:40:33 +00:00
func ( ts * testSession ) startGoServer (
2021-03-19 10:51:06 +00:00
membersDB roomdb . MembersService ,
aliasDB roomdb . AliasesService ,
2021-03-17 14:01:34 +00:00
opts ... roomsrv . Option ,
) * roomsrv . Server {
2021-01-26 17:33:29 +00:00
r := require . New ( ts . t )
2021-04-19 12:52:12 +00:00
netInfo := network . ServerEndpointDetails {
Domain : "go.test.room.server" ,
ListenAddressMUXRPC : "localhost:0" ,
}
2021-01-26 17:33:29 +00:00
// prepend defaults
opts = append ( [ ] roomsrv . Option {
roomsrv . WithLogger ( ts . info ) ,
roomsrv . WithRepoPath ( ts . repo ) ,
roomsrv . WithContext ( ts . ctx ) ,
} , opts ... )
if ts . keySHS != nil {
opts = append ( opts , roomsrv . WithAppKey ( ts . keySHS ) )
}
opts = append ( opts ,
roomsrv . WithPostSecureConnWrapper ( func ( conn net . Conn ) ( net . Conn , error ) {
2021-03-17 14:01:34 +00:00
ref , err := network . GetFeedRefFromAddr ( conn . RemoteAddr ( ) )
if err != nil {
return nil , err
}
fname := filepath . Join ( "testrun" , ts . t . Name ( ) , "muxdump" , ref . ShortRef ( ) )
return debug . WrapDump ( fname , conn )
2021-01-26 17:33:29 +00:00
} ) ,
)
2021-03-24 17:31:37 +00:00
// not needed for testing yet
sb := signinwithssb . NewSignalBridge ( )
authSessionsDB := new ( mockdb . FakeAuthWithSSBService )
2021-04-06 15:21:41 +00:00
fakeConfig := new ( mockdb . FakeRoomConfig )
2021-04-07 10:57:16 +00:00
deniedKeysDB := new ( mockdb . FakeDeniedKeysService )
2021-04-06 15:21:41 +00:00
2021-04-19 12:52:12 +00:00
srv , err := roomsrv . New ( membersDB , deniedKeysDB , aliasDB , authSessionsDB , sb , fakeConfig , netInfo , opts ... )
2021-01-26 17:33:29 +00:00
r . NoError ( err , "failed to init tees a server" )
2021-01-27 09:27:13 +00:00
ts . t . Logf ( "go server: %s" , srv . Whoami ( ) . Ref ( ) )
2021-01-26 17:33:29 +00:00
ts . t . Cleanup ( func ( ) {
2021-01-27 09:27:13 +00:00
ts . t . Log ( "bot close:" , srv . Close ( ) )
2021-01-26 17:33:29 +00:00
} )
2021-01-27 09:27:13 +00:00
2021-01-26 17:33:29 +00:00
ts . done . Go ( func ( ) error {
err := srv . Network . Serve ( ts . ctx )
2021-03-22 10:51:59 +00:00
// if the muxrpc protocol fucks up by e.g. unpacking body data into a header, this type of error will be surfaced here and look scary in the test output
2021-05-31 12:53:53 +00:00
// example: https://github.com/ssb-ngi-pointer/go-ssb-room/pull/85#issuecomment-801106687
2021-01-27 09:27:13 +00:00
if err != nil && ! errors . Is ( err , context . Canceled ) {
err = fmt . Errorf ( "go server exited: %w" , err )
2021-01-26 17:33:29 +00:00
ts . t . Log ( err )
return err
}
return nil
} )
2021-01-26 18:03:31 +00:00
return srv
2021-01-26 17:33:29 +00:00
}
var jsBotCnt = 0
2021-03-17 14:01:34 +00:00
// starts a node process in the client role. returns the jsbots pubkey
func ( ts * testSession ) startJSClient (
name ,
testScript string ,
// the perr the client should connect to at first (here usually the room server)
peerAddr net . Addr ,
peerRef refs . FeedRef ,
) refs . FeedRef {
2021-01-26 17:33:29 +00:00
ts . t . Log ( "starting client" , name )
r := require . New ( ts . t )
2021-01-27 12:44:17 +00:00
cmd := exec . CommandContext ( ts . ctx , "node" , "../../../sbot_client.js" )
2021-01-26 17:33:29 +00:00
cmd . Stderr = os . Stderr
outrc , err := cmd . StdoutPipe ( )
r . NoError ( err )
if name == "" {
2021-01-27 12:44:17 +00:00
name = fmt . Sprintf ( "jsbot-%d" , jsBotCnt )
2021-01-26 17:33:29 +00:00
}
jsBotCnt ++
2021-01-27 09:02:27 +00:00
2021-01-27 12:44:17 +00:00
// copy test scripts (maybe later with templates if we need to)
cmd . Dir = filepath . Join ( "testrun" , ts . t . Name ( ) , name )
os . MkdirAll ( cmd . Dir , 0700 )
err = exec . Command ( "cp" , "-r" , "testscripts" , cmd . Dir ) . Run ( )
r . NoError ( err )
2021-01-27 09:27:13 +00:00
2021-01-26 17:33:29 +00:00
env := [ ] string {
"TEST_NAME=" + name ,
2021-01-27 12:44:17 +00:00
"TEST_REPO=" + cmd . Dir ,
2021-01-27 09:27:13 +00:00
"TEST_PEERADDR=" + netwrap . GetAddr ( peerAddr , "tcp" ) . String ( ) ,
"TEST_PEERREF=" + peerRef . Ref ( ) ,
"TEST_SESSIONSCRIPT=" + testScript ,
2021-01-27 14:42:20 +00:00
// "DEBUG=ssb:room:tunnel:*",
2021-01-26 17:33:29 +00:00
}
if ts . keySHS != nil {
env = append ( env , "TEST_APPKEY=" + base64 . StdEncoding . EncodeToString ( ts . keySHS ) )
}
2021-01-27 09:02:27 +00:00
2021-01-26 17:33:29 +00:00
cmd . Env = env
2021-03-17 14:01:34 +00:00
started := time . Now ( )
2021-01-26 17:33:29 +00:00
r . NoError ( cmd . Start ( ) , "failed to init test js-sbot" )
2021-01-26 18:03:31 +00:00
ts . done . Go ( func ( ) error {
err := cmd . Wait ( )
2021-03-17 14:01:34 +00:00
ts . t . Logf ( "node client %s: exited with %v (after %s)" , name , err , time . Since ( started ) )
// we need to return the error code to have an idea if any of the tape assertions failed
2021-01-26 18:03:31 +00:00
if err != nil {
2021-03-17 14:01:34 +00:00
return fmt . Errorf ( "node client %s exited with %s" , name , err )
2021-01-26 18:03:31 +00:00
}
2021-03-17 14:01:34 +00:00
return nil
2021-01-26 18:03:31 +00:00
} )
2021-01-26 17:33:29 +00:00
ts . t . Cleanup ( func ( ) {
cmd . Process . Kill ( )
} )
pubScanner := bufio . NewScanner ( outrc ) // TODO muxrpc comms?
r . True ( pubScanner . Scan ( ) , "multiple lines of output from js - expected #1 to be %s pubkey/id" , name )
2021-03-17 14:01:34 +00:00
go io . Copy ( os . Stderr , outrc ) // restore node stdout to stderr behavior
2021-01-26 17:33:29 +00:00
jsBotRef , err := refs . ParseFeedRef ( pubScanner . Text ( ) )
r . NoError ( err , "failed to get %s key from JS process" )
ts . t . Logf ( "JS %s:%d %s" , name , jsBotCnt , jsBotRef . Ref ( ) )
return * jsBotRef
}
2021-03-17 14:01:34 +00:00
// startJSBotAsServer returns the servers public key and it's TCP port on localhost.
// This is only here to check compliance against the old javascript server.
// We don't care so much about it's internal behavior, just that clients can connect through it.
2021-01-26 17:33:29 +00:00
func ( ts * testSession ) startJSBotAsServer ( name , testScriptFileName string ) ( * refs . FeedRef , int ) {
r := require . New ( ts . t )
2021-01-27 12:44:17 +00:00
cmd := exec . CommandContext ( ts . ctx , "node" , "../../../sbot_serv.js" )
2021-01-26 17:33:29 +00:00
cmd . Stderr = os . Stderr
outrc , err := cmd . StdoutPipe ( )
r . NoError ( err )
if name == "" {
name = fmt . Sprintf ( "jsbot-%d" , jsBotCnt )
}
jsBotCnt ++
2021-01-27 12:44:17 +00:00
// copy test scripts (maybe later with templates if we need to)
cmd . Dir = filepath . Join ( "testrun" , ts . t . Name ( ) , name )
os . MkdirAll ( cmd . Dir , 0700 )
err = exec . Command ( "cp" , "-r" , "testscripts" , cmd . Dir ) . Run ( )
r . NoError ( err )
2021-01-26 17:33:29 +00:00
var port = 1024 + mrand . Intn ( 23000 )
env := [ ] string {
2021-01-27 12:44:17 +00:00
"TEST_NAME=jsbot-" + name ,
"TEST_REPO=" + cmd . Dir ,
2021-01-26 17:33:29 +00:00
fmt . Sprintf ( "TEST_PORT=%d" , port ) ,
2021-01-27 09:27:13 +00:00
"TEST_SESSIONSCRIPT=" + testScriptFileName ,
2021-01-27 14:42:20 +00:00
// "DEBUG=ssb:room:tunnel:*",
2021-01-26 17:33:29 +00:00
}
2021-01-27 09:02:27 +00:00
if ts . keySHS != nil {
env = append ( env , "TEST_APPKEY=" + base64 . StdEncoding . EncodeToString ( ts . keySHS ) )
}
2021-01-26 17:33:29 +00:00
cmd . Env = env
2021-03-17 14:01:34 +00:00
started := time . Now ( )
2021-01-26 17:33:29 +00:00
r . NoError ( cmd . Start ( ) , "failed to init test js-sbot" )
2021-01-26 18:03:31 +00:00
ts . done . Go ( func ( ) error {
err := cmd . Wait ( )
2021-03-17 14:01:34 +00:00
ts . t . Logf ( "node server %s: exited with %v (after %s)" , name , err , time . Since ( started ) )
return nil
2021-01-26 18:03:31 +00:00
} )
2021-01-26 17:33:29 +00:00
ts . t . Cleanup ( func ( ) {
cmd . Process . Kill ( )
} )
pubScanner := bufio . NewScanner ( outrc ) // TODO muxrpc comms?
r . True ( pubScanner . Scan ( ) , "multiple lines of output from js - expected #1 to be %s pubkey/id" , name )
srvRef , err := refs . ParseFeedRef ( pubScanner . Text ( ) )
r . NoError ( err , "failed to get srvRef key from JS process" )
ts . t . Logf ( "JS %s: %s port: %d" , name , srvRef . Ref ( ) , port )
return srvRef , port
}
func ( ts * testSession ) wait ( ) {
2021-01-26 18:03:31 +00:00
ts . cancel ( )
2021-01-26 17:33:29 +00:00
2021-01-26 18:03:31 +00:00
assert . NoError ( ts . t , ts . done . Wait ( ) )
2021-01-26 17:33:29 +00:00
}