Files
galaxy-game/gateway/cmd/gateway/main_test.go
T
2026-05-06 10:14:55 +03:00

215 lines
5.8 KiB
Go

package main
import (
"context"
"crypto/ed25519"
"crypto/sha256"
"crypto/x509"
"encoding/pem"
"net"
"os"
"path/filepath"
"testing"
"time"
"galaxy/gateway/internal/backendclient"
"galaxy/gateway/internal/config"
"galaxy/redisconn"
"github.com/alicebob/miniredis/v2"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
)
func testRedisConn(masterAddr string, opTimeout time.Duration) redisconn.Config {
cfg := redisconn.DefaultConfig()
cfg.MasterAddr = masterAddr
cfg.Password = "integration"
if opTimeout > 0 {
cfg.OperationTimeout = opTimeout
}
return cfg
}
func newTestBackendConfig() config.BackendConfig {
return config.BackendConfig{
HTTPBaseURL: "http://127.0.0.1:8080",
GRPCPushURL: "127.0.0.1:8081",
GatewayClientID: "gw-test",
HTTPTimeout: 250 * time.Millisecond,
PushReconnectBaseBackoff: 100 * time.Millisecond,
PushReconnectMaxBackoff: time.Second,
}
}
func newTestBackendClient(t *testing.T) *backendclient.Client {
t.Helper()
cfg := newTestBackendConfig()
client, err := backendclient.NewClient(backendclient.Config{
HTTPBaseURL: cfg.HTTPBaseURL,
GRPCPushURL: cfg.GRPCPushURL,
GatewayClientID: cfg.GatewayClientID,
HTTPTimeout: cfg.HTTPTimeout,
PushReconnectBaseBackoff: cfg.PushReconnectBaseBackoff,
PushReconnectMaxBackoff: cfg.PushReconnectMaxBackoff,
})
require.NoError(t, err)
t.Cleanup(func() { _ = client.Close() })
return client
}
func TestNewAuthenticatedGRPCDependenciesSuccess(t *testing.T) {
t.Parallel()
server := miniredis.RunT(t)
responseSignerPEMPath := writeTestResponseSignerPEMFile(t)
backend := newTestBackendClient(t)
cfg := config.Config{
Redis: testRedisConn(server.Addr(), 250*time.Millisecond),
ReplayRedis: config.ReplayRedisConfig{
KeyPrefix: "gateway:replay:",
ReserveTimeout: 250 * time.Millisecond,
},
Backend: newTestBackendConfig(),
ResponseSigner: config.ResponseSignerConfig{
PrivateKeyPEMPath: responseSignerPEMPath,
},
}
deps, components, cleanup, err := newAuthenticatedGRPCDependencies(context.Background(), cfg, zap.NewNop(), nil, backend)
require.NoError(t, err)
require.NotNil(t, deps.SessionCache)
require.NotNil(t, deps.ReplayStore)
require.NotNil(t, deps.ResponseSigner)
require.NotNil(t, deps.Router)
require.NotNil(t, deps.Service)
require.Len(t, components, 1)
require.NotNil(t, cleanup)
assert.NoError(t, cleanup())
}
func TestNewAuthenticatedGRPCDependenciesPingFailure(t *testing.T) {
t.Parallel()
responseSignerPEMPath := writeTestResponseSignerPEMFile(t)
backend := newTestBackendClient(t)
cfg := config.Config{
Redis: testRedisConn(unusedTCPAddr(t), 100*time.Millisecond),
ReplayRedis: config.ReplayRedisConfig{
KeyPrefix: "gateway:replay:",
ReserveTimeout: 100 * time.Millisecond,
},
Backend: newTestBackendConfig(),
ResponseSigner: config.ResponseSignerConfig{
PrivateKeyPEMPath: responseSignerPEMPath,
},
}
_, _, _, err := newAuthenticatedGRPCDependencies(context.Background(), cfg, zap.NewNop(), nil, backend)
require.Error(t, err)
assert.ErrorContains(t, err, "ping redis")
}
func TestNewAuthenticatedGRPCDependenciesInvalidReplayConfig(t *testing.T) {
t.Parallel()
server := miniredis.RunT(t)
responseSignerPEMPath := writeTestResponseSignerPEMFile(t)
backend := newTestBackendClient(t)
cfg := config.Config{
Redis: testRedisConn(server.Addr(), 250*time.Millisecond),
ReplayRedis: config.ReplayRedisConfig{
ReserveTimeout: 250 * time.Millisecond,
},
Backend: newTestBackendConfig(),
ResponseSigner: config.ResponseSignerConfig{
PrivateKeyPEMPath: responseSignerPEMPath,
},
}
_, _, _, err := newAuthenticatedGRPCDependencies(context.Background(), cfg, zap.NewNop(), nil, backend)
require.Error(t, err)
assert.ErrorContains(t, err, "replay key prefix must not be empty")
}
func TestNewAuthenticatedGRPCDependenciesMissingResponseSigner(t *testing.T) {
t.Parallel()
backend := newTestBackendClient(t)
cfg := config.Config{
Backend: newTestBackendConfig(),
}
_, _, _, err := newAuthenticatedGRPCDependencies(context.Background(), cfg, zap.NewNop(), nil, backend)
require.Error(t, err)
assert.ErrorContains(t, err, "load response signer")
}
func TestNewAuthenticatedGRPCDependenciesInvalidResponseSignerPEM(t *testing.T) {
t.Parallel()
backend := newTestBackendClient(t)
server := miniredis.RunT(t)
cfg := config.Config{
Redis: testRedisConn(server.Addr(), 250*time.Millisecond),
ReplayRedis: config.ReplayRedisConfig{
KeyPrefix: "gateway:replay:",
ReserveTimeout: 250 * time.Millisecond,
},
Backend: newTestBackendConfig(),
ResponseSigner: config.ResponseSignerConfig{
PrivateKeyPEMPath: writeInvalidPEMFile(t),
},
}
_, _, _, err := newAuthenticatedGRPCDependencies(context.Background(), cfg, zap.NewNop(), nil, backend)
require.Error(t, err)
assert.ErrorContains(t, err, "response signer private key")
}
func unusedTCPAddr(t *testing.T) string {
t.Helper()
listener, err := net.Listen("tcp", "127.0.0.1:0")
require.NoError(t, err)
addr := listener.Addr().String()
require.NoError(t, listener.Close())
return addr
}
func writeTestResponseSignerPEMFile(t *testing.T) string {
t.Helper()
seed := sha256.Sum256([]byte("gateway-main-test-response-signer"))
privateKey := ed25519.NewKeyFromSeed(seed[:])
encodedPrivateKey, err := x509.MarshalPKCS8PrivateKey(privateKey)
require.NoError(t, err)
path := filepath.Join(t.TempDir(), "response-signer.pem")
err = os.WriteFile(path, pem.EncodeToMemory(&pem.Block{
Type: "PRIVATE KEY",
Bytes: encodedPrivateKey,
}), 0o600)
require.NoError(t, err)
return path
}
func writeInvalidPEMFile(t *testing.T) string {
t.Helper()
path := filepath.Join(t.TempDir(), "invalid-response-signer.pem")
require.NoError(t, os.WriteFile(path, []byte("not a valid pem"), 0o600))
return path
}