package integration_test import ( "context" "crypto/sha256" "strings" "testing" "time" "galaxy/integration/testenv" usermodel "galaxy/model/user" "galaxy/transcoder" "github.com/google/uuid" ) // TestGatewayEdge_PublicBodyTooLarge tightens the public body size // limit and asserts that the gateway rejects an oversize public auth // payload before reaching backend. func TestGatewayEdge_PublicBodyTooLarge(t *testing.T) { plat := testenv.Bootstrap(t, testenv.BootstrapOptions{ GatewayExtra: map[string]string{ "GATEWAY_PUBLIC_HTTP_ANTI_ABUSE_PUBLIC_AUTH_MAX_BODY_BYTES": "256", }, }) ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() huge := strings.Repeat("x", 4096) public := testenv.NewPublicRESTClient(plat.Gateway.HTTPURL) _, _, err := public.SendEmailCode(ctx, huge+"@example.com", "") if err == nil { t.Fatalf("expected error for oversize public payload, got nil") } if !strings.Contains(err.Error(), "413") && !strings.Contains(err.Error(), "request_too_large") { t.Fatalf("expected 413 or request_too_large, got: %v", err) } } // TestGatewayEdge_BadSignature corrupts the request signature and // asserts the gateway rejects it as Unauthenticated. func TestGatewayEdge_BadSignature(t *testing.T) { plat := testenv.Bootstrap(t, testenv.BootstrapOptions{}) ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) defer cancel() sess := testenv.RegisterSession(t, plat, "pilot+badsig@example.com") gw, err := sess.DialAuthenticated(ctx, plat) if err != nil { t.Fatalf("dial: %v", err) } defer gw.Close() payload, err := transcoder.GetMyAccountRequestToPayload(&usermodel.GetMyAccountRequest{}) if err != nil { t.Fatalf("encode payload: %v", err) } bogus := make([]byte, 64) _, err = gw.Execute(ctx, usermodel.MessageTypeGetMyAccount, payload, testenv.ExecuteOptions{ OverrideSignature: bogus, }) if err == nil { t.Fatalf("expected Unauthenticated for bad signature") } if !testenv.IsUnauthenticated(err) { t.Fatalf("expected Unauthenticated, got: %v", err) } } // TestGatewayEdge_PayloadHashMismatch sends a request whose // payload_hash is not the SHA-256 of payload_bytes and asserts the // gateway rejects it. func TestGatewayEdge_PayloadHashMismatch(t *testing.T) { plat := testenv.Bootstrap(t, testenv.BootstrapOptions{}) ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) defer cancel() sess := testenv.RegisterSession(t, plat, "pilot+hash@example.com") gw, err := sess.DialAuthenticated(ctx, plat) if err != nil { t.Fatalf("dial: %v", err) } defer gw.Close() payload, err := transcoder.GetMyAccountRequestToPayload(&usermodel.GetMyAccountRequest{}) if err != nil { t.Fatalf("encode payload: %v", err) } // The signed canonical bytes still use this wrong hash; gateway // recomputes and should detect the mismatch independently of the // signature check. wrong := sha256.Sum256([]byte("not-the-payload")) _, err = gw.Execute(ctx, usermodel.MessageTypeGetMyAccount, payload, testenv.ExecuteOptions{ OverridePayloadHash: wrong[:], }) if err == nil { t.Fatalf("expected rejection for payload_hash mismatch") } if !testenv.IsUnauthenticated(err) && !testenv.IsInvalidArgument(err) { t.Fatalf("expected Unauthenticated or InvalidArgument, got: %v", err) } } // TestGatewayEdge_StaleTimestamp tightens freshness window to 1 // second, then submits a request whose timestamp is 30 seconds in // the past, and asserts the gateway rejects it as stale. func TestGatewayEdge_StaleTimestamp(t *testing.T) { plat := testenv.Bootstrap(t, testenv.BootstrapOptions{ GatewayExtra: map[string]string{ "GATEWAY_AUTHENTICATED_GRPC_FRESHNESS_WINDOW": "1s", }, }) ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) defer cancel() sess := testenv.RegisterSession(t, plat, "pilot+stale@example.com") gw, err := sess.DialAuthenticated(ctx, plat) if err != nil { t.Fatalf("dial: %v", err) } defer gw.Close() payload, err := transcoder.GetMyAccountRequestToPayload(&usermodel.GetMyAccountRequest{}) if err != nil { t.Fatalf("encode payload: %v", err) } stale := time.Now().Add(-30 * time.Second).UnixMilli() _, err = gw.Execute(ctx, usermodel.MessageTypeGetMyAccount, payload, testenv.ExecuteOptions{ TimestampMS: stale, }) if err == nil { t.Fatalf("expected rejection for stale timestamp") } if !testenv.IsUnauthenticated(err) && !testenv.IsInvalidArgument(err) && !testenv.IsFailedPrecondition(err) { t.Fatalf("expected Unauthenticated, InvalidArgument or FailedPrecondition, got: %v", err) } } // TestGatewayEdge_UnknownSession addresses a session id that backend // has never seen; gateway must reject before forwarding. func TestGatewayEdge_UnknownSession(t *testing.T) { plat := testenv.Bootstrap(t, testenv.BootstrapOptions{}) ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) defer cancel() sess := testenv.RegisterSession(t, plat, "pilot+unknown@example.com") gw, err := sess.DialAuthenticated(ctx, plat) if err != nil { t.Fatalf("dial: %v", err) } defer gw.Close() payload, err := transcoder.GetMyAccountRequestToPayload(&usermodel.GetMyAccountRequest{}) if err != nil { t.Fatalf("encode payload: %v", err) } _, err = gw.Execute(ctx, usermodel.MessageTypeGetMyAccount, payload, testenv.ExecuteOptions{ OverrideSessionID: uuid.NewString(), }) if err == nil { t.Fatalf("expected rejection for unknown session") } if !testenv.IsUnauthenticated(err) { t.Fatalf("expected Unauthenticated, got: %v", err) } } // TestGatewayEdge_UnsupportedProtocolVersion sets protocol_version // to an unknown literal and asserts gateway rejection. func TestGatewayEdge_UnsupportedProtocolVersion(t *testing.T) { plat := testenv.Bootstrap(t, testenv.BootstrapOptions{}) ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) defer cancel() sess := testenv.RegisterSession(t, plat, "pilot+protover@example.com") gw, err := sess.DialAuthenticated(ctx, plat) if err != nil { t.Fatalf("dial: %v", err) } defer gw.Close() payload, err := transcoder.GetMyAccountRequestToPayload(&usermodel.GetMyAccountRequest{}) if err != nil { t.Fatalf("encode payload: %v", err) } _, err = gw.Execute(ctx, usermodel.MessageTypeGetMyAccount, payload, testenv.ExecuteOptions{ OverrideProtocolVersion: "v999", }) if err == nil { t.Fatalf("expected rejection for unsupported protocol_version") } if !testenv.IsInvalidArgument(err) && !testenv.IsUnauthenticated(err) && !testenv.IsFailedPrecondition(err) { t.Fatalf("expected InvalidArgument, Unauthenticated or FailedPrecondition, got: %v", err) } }