From 3746c681ba1d2dc08ff93b682a12b3e1df9491ce Mon Sep 17 00:00:00 2001 From: Ivan Artemiev <29709626+iartemiev@users.noreply.github.com> Date: Fri, 16 Aug 2024 14:01:07 -0400 Subject: [PATCH] tests --- .../AWSAppSyncRealTimeProvider.test.ts | 54 ++++++++++++++++--- .../utils/convert/base64Encoder.test.ts | 8 +++ .../src/utils/convert/base64/base64Encoder.ts | 15 +++--- packages/core/src/utils/convert/types.ts | 2 +- 4 files changed, 66 insertions(+), 13 deletions(-) diff --git a/packages/api-graphql/__tests__/AWSAppSyncRealTimeProvider.test.ts b/packages/api-graphql/__tests__/AWSAppSyncRealTimeProvider.test.ts index 29563594a30..602364f4f3d 100644 --- a/packages/api-graphql/__tests__/AWSAppSyncRealTimeProvider.test.ts +++ b/packages/api-graphql/__tests__/AWSAppSyncRealTimeProvider.test.ts @@ -245,8 +245,8 @@ describe('AWSAppSyncRealTimeProvider', () => { expect(newSocketSpy).toHaveBeenNthCalledWith( 1, - 'ws://localhost:8080/realtime?header=&payload=e30=', - 'graphql-ws', + 'ws://localhost:8080/realtime', + ['graphql-ws', 'header-'], ); }); @@ -271,8 +271,8 @@ describe('AWSAppSyncRealTimeProvider', () => { expect(newSocketSpy).toHaveBeenNthCalledWith( 1, - 'wss://localhost:8080/realtime?header=&payload=e30=', - 'graphql-ws', + 'wss://localhost:8080/realtime', + ['graphql-ws', 'header-'], ); }); @@ -298,8 +298,50 @@ describe('AWSAppSyncRealTimeProvider', () => { expect(newSocketSpy).toHaveBeenNthCalledWith( 1, - 'wss://testaccounturl123456789123.appsync-realtime-api.us-east-1.amazonaws.com/graphql?header=&payload=e30=', - 'graphql-ws', + 'wss://testaccounturl123456789123.appsync-realtime-api.us-east-1.amazonaws.com/graphql', + ['graphql-ws', 'header-'], + ); + }); + + test('subscription generates expected auth token', async () => { + expect.assertions(1); + + const newSocketSpy = jest + .spyOn(provider, 'getNewWebSocket') + .mockImplementation(() => { + fakeWebSocketInterface.newWebSocket(); + return fakeWebSocketInterface.webSocket; + }); + + provider + .subscribe({ + appSyncGraphqlEndpoint: + 'https://testaccounturl123456789123.appsync-api.us-east-1.amazonaws.com/graphql', + // using custom auth instead of apiKey, because the latter inserts a timestamp header => expected value changes + authenticationType: 'lambda', + additionalHeaders: { + Authorization: 'my-custom-auth-token', + }, + }) + .subscribe({ error: () => {} }); + + // Wait for the socket to be initialize + await fakeWebSocketInterface.readyForUse; + + /* + Regular base64 encoding of auth header {"Authorization":"my-custom-auth-token","host":"testaccounturl123456789123.appsync-api.us-east-1.amazonaws.com"} + Is: `eyJBdXRob3JpemF0aW9uIjoibXktY3VzdG9tLWF1dGgtdG9rZW4iLCJob3N0IjoidGVzdGFjY291bnR1cmwxMjM0NTY3ODkxMjMuYXBwc3luYy1hcGkudXMtZWFzdC0xLmFtYXpvbmF3cy5jb20ifQ==` + (note `==` at the end of the string) + base64url encoding is expected to drop padding chars `=` + */ + + expect(newSocketSpy).toHaveBeenNthCalledWith( + 1, + 'wss://testaccounturl123456789123.appsync-realtime-api.us-east-1.amazonaws.com/graphql', + [ + 'graphql-ws', + 'header-eyJBdXRob3JpemF0aW9uIjoibXktY3VzdG9tLWF1dGgtdG9rZW4iLCJob3N0IjoidGVzdGFjY291bnR1cmwxMjM0NTY3ODkxMjMuYXBwc3luYy1hcGkudXMtZWFzdC0xLmFtYXpvbmF3cy5jb20ifQ', + ], ); }); diff --git a/packages/core/__tests__/utils/convert/base64Encoder.test.ts b/packages/core/__tests__/utils/convert/base64Encoder.test.ts index 14ffb0064b0..c1d1aeb203a 100644 --- a/packages/core/__tests__/utils/convert/base64Encoder.test.ts +++ b/packages/core/__tests__/utils/convert/base64Encoder.test.ts @@ -43,4 +43,12 @@ describe('base64Encoder (non-native)', () => { 'test-test_test', ); }); + + it('makes the result a base64url string with no padding chars', () => { + const mockResult = 'test+test/test=='; // = is the base64 padding char + mockBtoa.mockReturnValue(mockResult); + expect( + base64Encoder.convert('test', { urlSafe: true, skipPadding: true }), + ).toBe('test-test_test'); + }); }); diff --git a/packages/core/src/utils/convert/base64/base64Encoder.ts b/packages/core/src/utils/convert/base64/base64Encoder.ts index 7a216c22a3f..43184997819 100644 --- a/packages/core/src/utils/convert/base64/base64Encoder.ts +++ b/packages/core/src/utils/convert/base64/base64Encoder.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { getBtoa } from '../../globalHelpers'; -import { Base64Encoder } from '../types'; +import type { Base64Encoder, Base64EncoderConvertOptions } from '../types'; import { bytesToString } from './bytesToString'; @@ -10,23 +10,26 @@ export const base64Encoder: Base64Encoder = { /** * Convert input to base64-encoded string * @param input - string to convert to base64 - * @param param1 - - * @returns + * @param options - encoding options that can optionally produce a base64url string + * @returns base64-encoded string */ convert( input, - { urlSafe, skipPadding } = { urlSafe: false, skipPadding: false }, + options: Base64EncoderConvertOptions = { + urlSafe: false, + skipPadding: false, + }, ) { const inputStr = typeof input === 'string' ? input : bytesToString(input); let encodedStr = getBtoa()(inputStr); // urlSafe char replacement and skipPadding options conform to the base64url spec // https://datatracker.ietf.org/doc/html/rfc4648#section-5 - if (urlSafe) { + if (options.urlSafe) { encodedStr = encodedStr.replace(/\+/g, '-').replace(/\//g, '_'); } - if (skipPadding) { + if (options.skipPadding) { encodedStr = encodedStr.replace(/=/g, ''); } diff --git a/packages/core/src/utils/convert/types.ts b/packages/core/src/utils/convert/types.ts index a7c3e3db587..7a1c4d4d86d 100644 --- a/packages/core/src/utils/convert/types.ts +++ b/packages/core/src/utils/convert/types.ts @@ -3,7 +3,7 @@ export interface Base64EncoderConvertOptions { urlSafe: boolean; - skipPadding: boolean; + skipPadding?: boolean; } export interface Base64Encoder {