- apps/portal 디렉터리를 apps/client로 리팩터링 - ControlPlaneWireClient → WireClient 명명 변경 - 관련_proto import 정렬, struct protobuf 생성 제거 - README, docker-compose, 스크립트 등 일관성 개선
176 lines
4.4 KiB
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"
|
|
|
|
proto_socket "git.toki-labs.com/toki/proto-socket/go"
|
|
iop "iop/proto/gen/iop"
|
|
)
|
|
|
|
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 TestClientServerHandshake(t *testing.T) {
|
|
logger := zaptest.NewLogger(t)
|
|
port := getFreePort(t)
|
|
listenAddr := fmt.Sprintf("127.0.0.1:%d", port)
|
|
|
|
server, err := NewClientServer(listenAddr, logger)
|
|
if err != nil {
|
|
t.Fatalf("NewClientServer 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 client endpoint
|
|
parserMap := proto_socket.ParserMap{
|
|
proto_socket.TypeNameOf(&iop.ClientHelloRequest{}): func(b []byte) (proto.Message, error) {
|
|
req := &iop.ClientHelloRequest{}
|
|
return req, proto.Unmarshal(b, req)
|
|
},
|
|
proto_socket.TypeNameOf(&iop.ClientHelloResponse{}): func(b []byte) (proto.Message, error) {
|
|
res := &iop.ClientHelloResponse{}
|
|
return res, proto.Unmarshal(b, res)
|
|
},
|
|
}
|
|
|
|
// Dial using WS client
|
|
client, err := proto_socket.DialWsWithHeartbeat(
|
|
ctx,
|
|
"127.0.0.1",
|
|
port,
|
|
"/client",
|
|
proto_socket.DefaultHeartbeatIntervalSec,
|
|
proto_socket.DefaultHeartbeatWaitSec,
|
|
parserMap,
|
|
)
|
|
if err != nil {
|
|
t.Fatalf("failed to DialWs: %v", err)
|
|
}
|
|
defer client.Close()
|
|
|
|
// Send ClientHelloRequest and wait for ClientHelloResponse
|
|
req := &iop.ClientHelloRequest{
|
|
ClientId: "test-client-id",
|
|
ClientVersion: "1.0.0",
|
|
}
|
|
|
|
res, err := proto_socket.SendRequestTyped[*iop.ClientHelloRequest, *iop.ClientHelloResponse](
|
|
&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 TestClientServerHandshakeWithBrowserOrigin(t *testing.T) {
|
|
logger := zaptest.NewLogger(t)
|
|
port := getFreePort(t)
|
|
listenAddr := fmt.Sprintf("127.0.0.1:%d", port)
|
|
|
|
server, err := NewClientServer(listenAddr, logger)
|
|
if err != nil {
|
|
t.Fatalf("NewClientServer 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.ClientHelloRequest{}): func(b []byte) (proto.Message, error) {
|
|
req := &iop.ClientHelloRequest{}
|
|
return req, proto.Unmarshal(b, req)
|
|
},
|
|
proto_socket.TypeNameOf(&iop.ClientHelloResponse{}): func(b []byte) (proto.Message, error) {
|
|
res := &iop.ClientHelloResponse{}
|
|
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/client", 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 ClientHelloRequest
|
|
req := &iop.ClientHelloRequest{
|
|
ClientId: "browser-client",
|
|
ClientVersion: "1.0.0",
|
|
}
|
|
|
|
res, err := proto_socket.SendRequestTyped[*iop.ClientHelloRequest, *iop.ClientHelloResponse](
|
|
&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)
|
|
}
|
|
}
|