Skip to content

Commit

Permalink
test(storage-browser) Shore up action handler unit tests (#6207)
Browse files Browse the repository at this point in the history
  • Loading branch information
cshfang authored Nov 27, 2024
1 parent 3bedfeb commit 4279e9f
Show file tree
Hide file tree
Showing 8 changed files with 432 additions and 248 deletions.
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import * as StorageModule from '../../../storage-internal';

import { copy, CopyInput } from '../../../storage-internal';
import { copyHandler, CopyHandlerInput } from '../copy';

const copySpy = jest.spyOn(StorageModule, 'copy');
jest.mock('../../../storage-internal');

const baseInput: CopyHandlerInput = {
destinationPrefix: 'destination/',
Expand All @@ -25,8 +24,14 @@ const baseInput: CopyHandlerInput = {
};

describe('copyHandler', () => {
const mockCopy = jest.mocked(copy);

beforeEach(() => {
mockCopy.mockResolvedValue({ path: '' });
});

afterEach(() => {
copySpy.mockClear();
mockCopy.mockReset();
});

it('calls `copy` wth the expected values', () => {
Expand All @@ -37,7 +42,7 @@ describe('copyHandler', () => {
region: `${baseInput.config.region}`,
};

const expected: StorageModule.CopyInput = {
const expected: CopyInput = {
destination: {
expectedBucketOwner: baseInput.config.accountId,
bucket,
Expand All @@ -56,7 +61,7 @@ describe('copyHandler', () => {
},
};

expect(copySpy).toHaveBeenCalledWith(expected);
expect(mockCopy).toHaveBeenCalledWith(expected);
});

it('provides eTag and notModifiedSince to copy for durableness', () => {
Expand All @@ -67,7 +72,7 @@ describe('copyHandler', () => {
region: `${baseInput.config.region}`,
};

const copyInput = copySpy.mock.lastCall?.[0];
const copyInput = mockCopy.mock.lastCall?.[0];
expect(copyInput).toHaveProperty('source', {
expectedBucketOwner: `${baseInput.config.accountId}`,
bucket,
Expand Down Expand Up @@ -100,6 +105,23 @@ describe('copyHandler', () => {
}),
});

expect(copySpy).toHaveBeenCalledWith(expected);
expect(mockCopy).toHaveBeenCalledWith(expected);
});

it('returns a complete status', async () => {
const { result } = copyHandler(baseInput);

expect(await result).toEqual({ status: 'COMPLETE' });
});

it('returns failed status', async () => {
const errorMessage = 'error-message';
mockCopy.mockRejectedValue(new Error(errorMessage));
const { result } = copyHandler(baseInput);

expect(await result).toEqual({
status: 'FAILED',
message: errorMessage,
});
});
});
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { createFolderHandler, CreateFolderHandlerInput } from '../createFolder';

import * as InternalStorageModule from '../../../storage-internal';
import { uploadData, UploadDataInput } from '../../../storage-internal';

const uploadDataSpy = jest.spyOn(InternalStorageModule, 'uploadData');
jest.mock('../../../storage-internal');

const credentials = jest.fn();

Expand All @@ -22,22 +22,28 @@ const baseInput: CreateFolderHandlerInput = {
destinationPrefix: 'prefix/',
};

const error = new Error('Failed!');

describe('createFolderHandler', () => {
const mockUploadDataReturnValue = {
cancel: jest.fn(),
pause: jest.fn(),
resume: jest.fn(),
result: Promise.resolve({ path: '' }),
state: 'SUCCESS' as const,
};
const mockUploadData = jest.mocked(uploadData);

beforeEach(() => {
jest.clearAllMocks();
mockUploadData.mockReturnValue(mockUploadDataReturnValue);
});

it('behaves as expected in the happy path', async () => {
uploadDataSpy.mockReturnValueOnce({
cancel: jest.fn(),
pause: jest.fn(),
resume: jest.fn(),
result: Promise.resolve({ path: '' }),
state: 'SUCCESS',
});
afterEach(() => {
mockUploadData.mockReset();
});

beforeEach(() => {});

it('behaves as expected in the happy path', async () => {
const { result } = createFolderHandler(baseInput);

expect(await result).toStrictEqual({ status: 'COMPLETE' });
Expand All @@ -46,7 +52,7 @@ describe('createFolderHandler', () => {
it('calls `uploadData` with the expected values', () => {
createFolderHandler({ ...baseInput, options: { preventOverwrite: true } });

const expected: InternalStorageModule.UploadDataInput = {
const expected: UploadDataInput = {
data: '',
options: {
expectedBucketOwner: config.accountId,
Expand All @@ -62,21 +68,14 @@ describe('createFolderHandler', () => {
path: `${baseInput.destinationPrefix}${baseInput.data.key}`,
};

expect(uploadDataSpy).toHaveBeenCalledWith(expected);
expect(mockUploadData).toHaveBeenCalledWith(expected);
});

it('calls provided onProgress callback as expected in the happy path', async () => {
uploadDataSpy.mockImplementation(({ options }) => {
// @ts-expect-error - `options` is potentially `undefined` in the `uploadData` input interface
options.onProgress({ totalBytes: 23, transferredBytes: 23 });

return {
cancel: jest.fn(),
pause: jest.fn(),
resume: jest.fn(),
result: Promise.resolve({ path: '' }),
state: 'SUCCESS',
};
mockUploadData.mockImplementation(({ options }) => {
options?.onProgress?.({ totalBytes: 23, transferredBytes: 23 });

return mockUploadDataReturnValue;
});

const { result } = createFolderHandler({
Expand All @@ -91,17 +90,10 @@ describe('createFolderHandler', () => {
});

it('calls provided onProgress callback as expected when `totalBytes` is `undefined`', async () => {
uploadDataSpy.mockImplementation(({ options }) => {
// @ts-expect-error - `options` is potentially `undefined` in the `uploadData` input interface
options.onProgress({ transferredBytes: 23 });

return {
cancel: jest.fn(),
pause: jest.fn(),
resume: jest.fn(),
result: Promise.resolve({ path: '' }),
state: 'SUCCESS',
};
mockUploadData.mockImplementation(({ options }) => {
options?.onProgress?.({ transferredBytes: 23 });

return mockUploadDataReturnValue;
});

const { result } = createFolderHandler({
Expand All @@ -116,18 +108,18 @@ describe('createFolderHandler', () => {
});

it('handles a failure as expected', async () => {
uploadDataSpy.mockReturnValueOnce({
cancel: jest.fn(),
pause: jest.fn(),
resume: jest.fn(),
result: Promise.reject(error),
const errorMessage = 'error-message';

mockUploadData.mockReturnValue({
...mockUploadDataReturnValue,
result: Promise.reject(new Error(errorMessage)),
state: 'ERROR',
});

const { result } = createFolderHandler(baseInput);

expect(await result).toStrictEqual({
message: error.message,
message: errorMessage,
status: 'FAILED',
});
});
Expand All @@ -137,10 +129,8 @@ describe('createFolderHandler', () => {
const overwritePreventedError = new Error(message);
overwritePreventedError.name = 'PreconditionFailed';

uploadDataSpy.mockReturnValueOnce({
cancel: jest.fn(),
pause: jest.fn(),
resume: jest.fn(),
mockUploadData.mockReturnValue({
...mockUploadDataReturnValue,
result: Promise.reject(overwritePreventedError),
state: 'ERROR',
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import * as StorageModule from '../../../storage-internal';
import { remove, RemoveInput } from '../../../storage-internal';

import { deleteHandler, DeleteHandlerInput } from '../delete';

const removeSpy = jest.spyOn(StorageModule, 'remove');
jest.mock('../../../storage-internal');

const baseInput: DeleteHandlerInput = {
config: {
Expand All @@ -23,10 +23,20 @@ const baseInput: DeleteHandlerInput = {
};

describe('deleteHandler', () => {
const mockRemove = jest.mocked(remove);

beforeEach(() => {
mockRemove.mockResolvedValue({ path: '' });
});

afterEach(() => {
mockRemove.mockReset();
});

it('calls `remove` and returns the expected `key`', () => {
deleteHandler(baseInput);

const expected: StorageModule.RemoveInput = {
const expected: RemoveInput = {
path: baseInput.data.key,
options: {
expectedBucketOwner: baseInput.config.accountId,
Expand All @@ -39,6 +49,23 @@ describe('deleteHandler', () => {
},
};

expect(removeSpy).toHaveBeenCalledWith(expected);
expect(mockRemove).toHaveBeenCalledWith(expected);
});

it('returns a complete status', async () => {
const { result } = deleteHandler(baseInput);

expect(await result).toEqual({ status: 'COMPLETE' });
});

it('returns failed status', async () => {
const errorMessage = 'error-message';
mockRemove.mockRejectedValue(new Error(errorMessage));
const { result } = deleteHandler(baseInput);

expect(await result).toEqual({
status: 'FAILED',
message: errorMessage,
});
});
});
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import * as StorageModule from '../../../storage-internal';
import { getUrl, GetUrlInput } from '../../../storage-internal';

import { downloadHandler, DownloadHandlerInput } from '../download';

const downloadSpy = jest.spyOn(StorageModule, 'getUrl');
jest.mock('../../../storage-internal');

const baseInput: DownloadHandlerInput = {
config: {
Expand All @@ -23,10 +23,23 @@ const baseInput: DownloadHandlerInput = {
};

describe('downloadHandler', () => {
const url = new URL('mock://fake.url');
const mockGetUrl = jest.mocked(getUrl);

beforeEach(() => {
const expiresAt = new Date();
expiresAt.setDate(expiresAt.getDate() + 1);
mockGetUrl.mockResolvedValue({ expiresAt, url });
});

afterEach(() => {
mockGetUrl.mockReset();
});

it('calls `getUrl` with the expected values', () => {
downloadHandler(baseInput);

const expected: StorageModule.GetUrlInput = {
const expected: GetUrlInput = {
path: baseInput.data.key,
options: {
bucket: {
Expand All @@ -41,6 +54,23 @@ describe('downloadHandler', () => {
},
};

expect(downloadSpy).toHaveBeenCalledWith(expected);
expect(mockGetUrl).toHaveBeenCalledWith(expected);
});

it('returns a complete status', async () => {
const { result } = downloadHandler(baseInput);

expect(await result).toEqual({ status: 'COMPLETE' });
});

it('returns failed status', async () => {
const errorMessage = 'error-message';
mockGetUrl.mockRejectedValue(new Error(errorMessage));
const { result } = downloadHandler(baseInput);

expect(await result).toEqual({
status: 'FAILED',
message: errorMessage,
});
});
});
Loading

0 comments on commit 4279e9f

Please sign in to comment.