- Add socket handler and server implementations for real-time communication - Add Mattermost integration (auth service, push client, plugin client) - Add Mattermost push host integration tests - Update Flutter client app with new app structure and bootstrap - Update Android build configurations and add Google Services config - Add asset keep file for Flutter client - Update agent-roadmap with api-centered-proto-socket-rail milestone - Update agent-task with planning and code review documents for all groups - Update domain rules for api, client, worker and project rules
128 lines
3.5 KiB
Go
128 lines
3.5 KiB
Go
package socket
|
|
|
|
import (
|
|
"context"
|
|
"net"
|
|
"testing"
|
|
"time"
|
|
|
|
altv1 "git.toki-labs.com/toki/alt/packages/contracts/gen/go/alt/v1"
|
|
"git.toki-labs.com/toki/alt/services/api/internal/config"
|
|
apiContracts "git.toki-labs.com/toki/alt/services/api/internal/contracts"
|
|
protoSocket "git.toki-labs.com/toki/proto-socket/go"
|
|
)
|
|
|
|
func TestServerRespondsToHelloRequest(t *testing.T) {
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
cfg := config.Config{
|
|
Host: "127.0.0.1",
|
|
Port: freeTCPPort(t),
|
|
SocketPath: "/socket",
|
|
HeartbeatIntervalSec: 0,
|
|
HeartbeatWaitSec: 0,
|
|
}
|
|
server := NewServer(cfg)
|
|
if err := server.Start(ctx); err != nil {
|
|
t.Fatalf("failed to start server: %v", err)
|
|
}
|
|
defer server.Stop()
|
|
|
|
client, err := protoSocket.DialWsWithHeartbeat(ctx, cfg.Host, cfg.Port, cfg.SocketPath, 0, 0, apiContracts.ParserMap())
|
|
if err != nil {
|
|
t.Fatalf("failed to dial server: %v", err)
|
|
}
|
|
defer client.Close()
|
|
|
|
res, err := protoSocket.SendRequestTyped[*altv1.HelloRequest, *altv1.HelloResponse](
|
|
&client.Communicator,
|
|
&altv1.HelloRequest{
|
|
ClientName: "alt-test",
|
|
ClientVersion: "test",
|
|
AltProtocolVersion: "alt.v1",
|
|
},
|
|
2*time.Second,
|
|
)
|
|
if err != nil {
|
|
t.Fatalf("failed to send hello request: %v", err)
|
|
}
|
|
|
|
if res.GetServerName() != serverName {
|
|
t.Errorf("server name mismatch: expected %q, got %q", serverName, res.GetServerName())
|
|
}
|
|
if res.GetServerVersion() != serverVersion {
|
|
t.Errorf("server version mismatch: expected %q, got %q", serverVersion, res.GetServerVersion())
|
|
}
|
|
if res.GetAltProtocolVersion() != "alt.v1" {
|
|
t.Errorf("protocol version mismatch: expected %q, got %q", "alt.v1", res.GetAltProtocolVersion())
|
|
}
|
|
if len(res.GetCapabilities()) == 0 {
|
|
t.Fatal("expected capabilities to be set")
|
|
}
|
|
}
|
|
|
|
func TestSessionHandlersHaveUniqueRequestTypes(t *testing.T) {
|
|
seen := make(map[string]int)
|
|
for _, handler := range sessionHandlers() {
|
|
if handler.requestType == "" {
|
|
t.Errorf("session handler has empty request type")
|
|
continue
|
|
}
|
|
seen[handler.requestType]++
|
|
}
|
|
|
|
for requestType, count := range seen {
|
|
if count > 1 {
|
|
t.Errorf("request type %q registered %d times; duplicate handlers would panic the communicator", requestType, count)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestSessionHandlersCoverRequiredRequests(t *testing.T) {
|
|
registered := make(map[string]bool)
|
|
for _, handler := range sessionHandlers() {
|
|
registered[handler.requestType] = true
|
|
}
|
|
|
|
required := []string{
|
|
protoSocket.TypeNameOf(&altv1.HelloRequest{}),
|
|
}
|
|
for _, requestType := range required {
|
|
if !registered[requestType] {
|
|
t.Errorf("required handler for %q is not registered", requestType)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestRegisterSessionHandlersSkipsNilRegistrar(t *testing.T) {
|
|
// registerSessionHandlers must tolerate a malformed registry entry instead
|
|
// of panicking during connection setup.
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
t.Fatalf("registerSessionHandlers panicked on nil registrar: %v", r)
|
|
}
|
|
}()
|
|
|
|
handler := sessionHandler{requestType: "alt.v1.NilRegistrarProbe", register: nil}
|
|
if handler.register != nil {
|
|
t.Fatal("expected nil registrar for probe handler")
|
|
}
|
|
registerSessionHandlers(nil)
|
|
}
|
|
|
|
func freeTCPPort(t *testing.T) int {
|
|
t.Helper()
|
|
|
|
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
|
if err != nil {
|
|
t.Fatalf("failed to reserve TCP port: %v", err)
|
|
}
|
|
defer listener.Close()
|
|
|
|
addr, ok := listener.Addr().(*net.TCPAddr)
|
|
if !ok {
|
|
t.Fatalf("unexpected listener address type %T", listener.Addr())
|
|
}
|
|
return addr.Port
|
|
}
|