215 lines
5.8 KiB
Go
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
|
|
}
|