chore: sync testing plan with gateway

This commit is contained in:
IliaDenisov
2026-04-09 12:34:55 +02:00
parent c64c298d06
commit 9065b82fe2
5 changed files with 262 additions and 11 deletions
@@ -234,16 +234,27 @@ func (g runningAuthenticatedGateway) stop(t *testing.T) {
func dialGatewayClient(t *testing.T, addr string) *grpc.ClientConn {
t.Helper()
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
var conn *grpc.ClientConn
require.Eventually(t, func() bool {
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
defer cancel()
conn, err := grpc.DialContext(
ctx,
addr,
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithBlock(),
)
require.NoError(t, err)
candidate, err := grpc.DialContext(
ctx,
addr,
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithBlock(),
)
if err != nil {
if candidate != nil {
_ = candidate.Close()
}
return false
}
conn = candidate
return true
}, 2*time.Second, 10*time.Millisecond, "gateway did not accept gRPC connections")
return conn
}
@@ -8,6 +8,7 @@ import (
"time"
"galaxy/gateway/internal/authn"
"galaxy/gateway/internal/config"
"galaxy/gateway/internal/downstream"
"galaxy/gateway/internal/testutil"
gatewayv1 "galaxy/gateway/proto/galaxy/gateway/v1"
@@ -143,6 +144,78 @@ func TestExecuteCommandMapsDownstreamUnavailableToUnavailable(t *testing.T) {
assert.Equal(t, 1, failingClient.executeCalls)
}
func TestExecuteCommandMapsDownstreamTimeoutToUnavailable(t *testing.T) {
t.Parallel()
stallingClient := &recordingDownstreamClient{
executeFunc: func(ctx context.Context, _ downstream.AuthenticatedCommand) (downstream.UnaryResult, error) {
<-ctx.Done()
return downstream.UnaryResult{}, ctx.Err()
},
}
server, runGateway := newTestGatewayWithGRPCConfig(t, newAuthenticatedGRPCConfigForTest(func(cfg *config.AuthenticatedGRPCConfig) {
cfg.DownstreamTimeout = 50 * time.Millisecond
}), ServerDependencies{
Router: downstream.NewStaticRouter(map[string]downstream.Client{
"fleet.move": stallingClient,
}),
SessionCache: userMappedSessionCache(map[string]string{"device-session-123": "user-123"}),
ReplayStore: staticReplayStore{},
ResponseSigner: newTestResponseSigner(),
})
defer runGateway.stop(t)
addr := waitForListenAddr(t, server)
conn := dialGatewayClient(t, addr)
defer func() {
require.NoError(t, conn.Close())
}()
client := gatewayv1.NewEdgeGatewayClient(conn)
_, err := client.ExecuteCommand(context.Background(), newValidExecuteCommandRequest())
require.Error(t, err)
assert.Equal(t, codes.Unavailable, status.Code(err))
assert.Equal(t, "downstream service is unavailable", status.Convert(err).Message())
assert.Equal(t, 1, stallingClient.executeCalls)
}
func TestExecuteCommandFailsClosedWhenResponseSignerUnavailable(t *testing.T) {
t.Parallel()
successClient := &recordingDownstreamClient{
executeFunc: func(context.Context, downstream.AuthenticatedCommand) (downstream.UnaryResult, error) {
return downstream.UnaryResult{
ResultCode: "accepted",
PayloadBytes: []byte("downstream-response"),
}, nil
},
}
server, runGateway := newTestGateway(t, ServerDependencies{
Router: downstream.NewStaticRouter(map[string]downstream.Client{
"fleet.move": successClient,
}),
ResponseSigner: unavailableResponseSigner{},
SessionCache: userMappedSessionCache(map[string]string{"device-session-123": "user-123"}),
ReplayStore: staticReplayStore{},
})
defer runGateway.stop(t)
addr := waitForListenAddr(t, server)
conn := dialGatewayClient(t, addr)
defer func() {
require.NoError(t, conn.Close())
}()
client := gatewayv1.NewEdgeGatewayClient(conn)
_, err := client.ExecuteCommand(context.Background(), newValidExecuteCommandRequest())
require.Error(t, err)
assert.Equal(t, codes.Unavailable, status.Code(err))
assert.Equal(t, "response signer is unavailable", status.Convert(err).Message())
assert.Equal(t, 1, successClient.executeCalls)
}
func TestExecuteCommandPropagatesOTelSpanContextToDownstream(t *testing.T) {
t.Parallel()
+27
View File
@@ -216,6 +216,33 @@ func TestSubscribeEventsMissingReplayStoreFailsClosed(t *testing.T) {
assert.Equal(t, "replay store is unavailable", status.Convert(err).Message())
}
func TestSubscribeEventsFailsClosedWhenResponseSignerUnavailable(t *testing.T) {
t.Parallel()
server, runGateway := newTestGateway(t, ServerDependencies{
ResponseSigner: unavailableResponseSigner{},
SessionCache: staticSessionCache{
lookupFunc: func(context.Context, string) (session.Record, error) {
return newActiveSessionRecord(), nil
},
},
ReplayStore: staticReplayStore{},
})
defer runGateway.stop(t)
addr := waitForListenAddr(t, server)
conn := dialGatewayClient(t, addr)
defer func() {
require.NoError(t, conn.Close())
}()
client := gatewayv1.NewEdgeGatewayClient(conn)
err := subscribeEventsError(t, context.Background(), client, newValidSubscribeEventsRequest())
require.Error(t, err)
assert.Equal(t, codes.Unavailable, status.Code(err))
assert.Equal(t, "response signer is unavailable", status.Convert(err).Message())
}
func TestServerLifecycle(t *testing.T) {
t.Parallel()
@@ -216,6 +216,83 @@ func TestPublicAntiAbuseBrowserClassBucketsStayIsolatedFromPublicAuth(t *testing
}
}
func TestPublicAntiAbuseUsesRemoteAddrInsteadOfForwardedHeaders(t *testing.T) {
t.Parallel()
tests := []struct {
name string
headerKey string
firstHeader string
secondHeader string
firstRemote string
secondRemote string
wantSecondCode int
}{
{
name: "same remote addr ignores x-forwarded-for changes",
headerKey: "X-Forwarded-For",
firstHeader: "198.51.100.10",
secondHeader: "198.51.100.11",
firstRemote: "192.0.2.10:1234",
secondRemote: "192.0.2.10:1234",
wantSecondCode: http.StatusTooManyRequests,
},
{
name: "different remote addr wins over shared forwarded header",
headerKey: "Forwarded",
firstHeader: "for=198.51.100.10",
secondHeader: "for=198.51.100.10",
firstRemote: "192.0.2.10:1234",
secondRemote: "192.0.2.11:1234",
wantSecondCode: http.StatusOK,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
cfg := config.DefaultPublicHTTPConfig()
cfg.AntiAbuse.PublicAuth.RateLimit = config.PublicRateLimitConfig{
Requests: 1,
Window: time.Hour,
Burst: 1,
}
cfg.AntiAbuse.SendEmailCodeIdentity.RateLimit = config.PublicRateLimitConfig{
Requests: 100,
Window: time.Hour,
Burst: 100,
}
authService := &recordingAuthServiceClient{
sendEmailCodeResult: SendEmailCodeResult{
ChallengeID: "challenge-123",
},
}
handler := newPublicHandlerWithConfig(cfg, ServerDependencies{AuthService: authService})
first := sendEmailCodeRequest(`{"email":"pilot-one@example.com"}`)
first.RemoteAddr = tt.firstRemote
first.Header.Set(tt.headerKey, tt.firstHeader)
second := sendEmailCodeRequest(`{"email":"pilot-two@example.com"}`)
second.RemoteAddr = tt.secondRemote
second.Header.Set(tt.headerKey, tt.secondHeader)
firstResp := httptest.NewRecorder()
handler.ServeHTTP(firstResp, first)
secondResp := httptest.NewRecorder()
handler.ServeHTTP(secondResp, second)
assert.Equal(t, http.StatusOK, firstResp.Code)
assert.Equal(t, tt.wantSecondCode, secondResp.Code)
})
}
}
func TestPublicAntiAbuseSendEmailIdentityThrottle(t *testing.T) {
t.Parallel()