iop/apps/control-plane/internal/wire/portal_test.go

176 lines
4.4 KiB
Go

package wire
import (
"context"
"fmt"
"net"
"testing"
"time"
"go.uber.org/zap/zaptest"
"google.golang.org/protobuf/proto"
"nhooyr.io/websocket"
iop "iop/proto/gen/iop"
proto_socket "git.toki-labs.com/toki/proto-socket/go"
)
func getFreePort(t *testing.T) int {
t.Helper()
ln, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("failed to find free port: %v", err)
}
defer ln.Close()
return ln.Addr().(*net.TCPAddr).Port
}
func TestPortalServerHandshake(t *testing.T) {
logger := zaptest.NewLogger(t)
port := getFreePort(t)
listenAddr := fmt.Sprintf("127.0.0.1:%d", port)
server, err := NewPortalServer(listenAddr, logger)
if err != nil {
t.Fatalf("NewPortalServer failed: %v", err)
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
if err := server.Start(ctx); err != nil {
t.Fatalf("server Start failed: %v", err)
}
defer func() {
_ = server.Stop()
}()
// Give a tiny moment for server to bind
time.Sleep(50 * time.Millisecond)
// Dial to the portal endpoint
parserMap := proto_socket.ParserMap{
proto_socket.TypeNameOf(&iop.PortalHelloRequest{}): func(b []byte) (proto.Message, error) {
req := &iop.PortalHelloRequest{}
return req, proto.Unmarshal(b, req)
},
proto_socket.TypeNameOf(&iop.PortalHelloResponse{}): func(b []byte) (proto.Message, error) {
res := &iop.PortalHelloResponse{}
return res, proto.Unmarshal(b, res)
},
}
// Dial using WS client
client, err := proto_socket.DialWsWithHeartbeat(
ctx,
"127.0.0.1",
port,
"/portal",
proto_socket.DefaultHeartbeatIntervalSec,
proto_socket.DefaultHeartbeatWaitSec,
parserMap,
)
if err != nil {
t.Fatalf("failed to DialWs: %v", err)
}
defer client.Close()
// Send PortalHelloRequest and wait for PortalHelloResponse
req := &iop.PortalHelloRequest{
ClientId: "test-client-id",
ClientVersion: "1.0.0",
}
res, err := proto_socket.SendRequestTyped[*iop.PortalHelloRequest, *iop.PortalHelloResponse](
&client.Communicator,
req,
2*time.Second,
)
if err != nil {
t.Fatalf("SendRequestTyped failed: %v", err)
}
if !res.Ready {
t.Errorf("expected response.Ready to be true, got false")
}
if res.Protocol != Protocol {
t.Errorf("expected response.Protocol to be %q, got %q", Protocol, res.Protocol)
}
if res.ServerTimeUnixNano <= 0 {
t.Errorf("expected server time unix nano to be positive, got %d", res.ServerTimeUnixNano)
}
if res.Message != "Hello from IOP Control Plane" {
t.Errorf("expected message to be %q, got %q", "Hello from IOP Control Plane", res.Message)
}
}
func TestPortalServerHandshakeWithBrowserOrigin(t *testing.T) {
logger := zaptest.NewLogger(t)
port := getFreePort(t)
listenAddr := fmt.Sprintf("127.0.0.1:%d", port)
server, err := NewPortalServer(listenAddr, logger)
if err != nil {
t.Fatalf("NewPortalServer failed: %v", err)
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
if err := server.Start(ctx); err != nil {
t.Fatalf("server Start failed: %v", err)
}
defer func() {
_ = server.Stop()
}()
time.Sleep(50 * time.Millisecond)
parserMap := proto_socket.ParserMap{
proto_socket.TypeNameOf(&iop.PortalHelloRequest{}): func(b []byte) (proto.Message, error) {
req := &iop.PortalHelloRequest{}
return req, proto.Unmarshal(b, req)
},
proto_socket.TypeNameOf(&iop.PortalHelloResponse{}): func(b []byte) (proto.Message, error) {
res := &iop.PortalHelloResponse{}
return res, proto.Unmarshal(b, res)
},
}
// Dial specifying browser Origin
dialOpts := &websocket.DialOptions{
HTTPHeader: map[string][]string{
"Origin": {"http://localhost:3000"},
},
}
conn, _, err := websocket.Dial(ctx, fmt.Sprintf("ws://127.0.0.1:%d/portal", port), dialOpts)
if err != nil {
t.Fatalf("failed to Dial with Origin: %v", err)
}
// Wrap in WsClient
client := proto_socket.NewWsClient(conn, proto_socket.DefaultHeartbeatIntervalSec, proto_socket.DefaultHeartbeatWaitSec, parserMap)
defer client.Close()
// Send PortalHelloRequest
req := &iop.PortalHelloRequest{
ClientId: "browser-client",
ClientVersion: "1.0.0",
}
res, err := proto_socket.SendRequestTyped[*iop.PortalHelloRequest, *iop.PortalHelloResponse](
&client.Communicator,
req,
2*time.Second,
)
if err != nil {
t.Fatalf("SendRequestTyped failed: %v", err)
}
if !res.Ready {
t.Errorf("expected response.Ready to be true, got false")
}
if res.Protocol != Protocol {
t.Errorf("expected response.Protocol to be %q, got %q", Protocol, res.Protocol)
}
}