From 03a876f0a785c86438c4912a1bad2340c51569dc Mon Sep 17 00:00:00 2001 From: Bethany Date: Wed, 23 Aug 2023 13:54:31 -0700 Subject: [PATCH 1/2] add tests for upload --- .../__tests__/upload-artifact.test.ts | 375 ++++++++++++++++++ 1 file changed, 375 insertions(+) create mode 100644 packages/artifact/__tests__/upload-artifact.test.ts diff --git a/packages/artifact/__tests__/upload-artifact.test.ts b/packages/artifact/__tests__/upload-artifact.test.ts new file mode 100644 index 00000000..808f2c1d --- /dev/null +++ b/packages/artifact/__tests__/upload-artifact.test.ts @@ -0,0 +1,375 @@ +import * as core from '@actions/core' +import * as uploadZipSpecification from '../src/internal/upload/upload-zip-specification' +import * as zip from '../src/internal/upload/zip' +import * as util from '../src/internal/shared/util' +import * as retention from '../src/internal/upload/retention' +import * as config from '../src/internal/shared/config' +import {Timestamp} from '../src/generated' +import {ArtifactServiceClientJSON} from '../src/generated' +import * as blobUpload from '../src/internal/upload/blob-upload' +import {uploadArtifact} from '../src/internal/upload/upload-artifact' + +describe('upload-artifact', () => { + beforeEach(() => { + // mock all output so that there is less noise when running tests + jest.spyOn(console, 'log').mockImplementation(() => {}) + jest.spyOn(core, 'debug').mockImplementation(() => {}) + jest.spyOn(core, 'info').mockImplementation(() => {}) + jest.spyOn(core, 'warning').mockImplementation(() => {}) + }) + + afterEach(() => { + jest.restoreAllMocks() + }) + + it('should successfully upload an artifact', () => { + const mockDate = new Date('2020-01-01') + jest + .spyOn(uploadZipSpecification, 'validateRootDirectory') + .mockReturnValue() + jest + .spyOn(uploadZipSpecification, 'getUploadZipSpecification') + .mockReturnValue([ + { + sourcePath: '/home/user/files/plz-upload/file1.txt', + destinationPath: 'file1.txt' + }, + { + sourcePath: '/home/user/files/plz-upload/file2.txt', + destinationPath: 'file2.txt' + }, + { + sourcePath: '/home/user/files/plz-upload/dir/file3.txt', + destinationPath: 'dir/file3.txt' + } + ]) + + jest + .spyOn(zip, 'createZipUploadStream') + .mockReturnValue(Promise.resolve(new zip.ZipUploadStream(1))) + jest + .spyOn(util, 'getBackendIdsFromToken') + .mockReturnValue({ + workflowRunBackendId: '1234', + workflowJobRunBackendId: '5678' + }) + jest + .spyOn(retention, 'getExpiration') + .mockReturnValue(Timestamp.fromDate(mockDate)) + jest + .spyOn(ArtifactServiceClientJSON.prototype, 'CreateArtifact') + .mockReturnValue( + Promise.resolve({ + ok: true, + signedUploadUrl: 'https://signed-upload-url.com' + }) + ) + jest + .spyOn(blobUpload, 'uploadZipToBlobStorage') + .mockReturnValue( + Promise.resolve({ + isSuccess: true, + uploadSize: 1234, + md5Hash: 'test-md5-hash' + }) + ) + jest + .spyOn(ArtifactServiceClientJSON.prototype, 'FinalizeArtifact') + .mockReturnValue(Promise.resolve({ok: true, artifactId: '1'})) + + // ArtifactHttpClient mocks + jest.spyOn(config, 'getRuntimeToken').mockReturnValue('test-token') + jest + .spyOn(config, 'getResultsServiceUrl') + .mockReturnValue('https://test-url.com') + + const uploadResp = uploadArtifact( + 'test-artifact', + [ + '/home/user/files/plz-upload/file1.txt', + '/home/user/files/plz-upload/file2.txt', + '/home/user/files/plz-upload/dir/file3.txt' + ], + '/home/user/files/plz-upload' + ) + + expect(uploadResp).resolves.toEqual({success: true, size: 1234, id: 1}) + }) + + it('should throw an error if the root directory is invalid', () => { + jest + .spyOn(uploadZipSpecification, 'validateRootDirectory') + .mockImplementation(() => { + throw new Error('Invalid root directory') + }) + + const uploadResp = uploadArtifact( + 'test-artifact', + [ + '/home/user/files/plz-upload/file1.txt', + '/home/user/files/plz-upload/file2.txt', + '/home/user/files/plz-upload/dir/file3.txt' + ], + '/home/user/files/plz-upload' + ) + + expect(uploadResp).rejects.toThrow('Invalid root directory') + }) + + it('should return false if there are no files to upload', () => { + jest + .spyOn(uploadZipSpecification, 'validateRootDirectory') + .mockReturnValue() + jest + .spyOn(uploadZipSpecification, 'getUploadZipSpecification') + .mockReturnValue([]) + + const uploadResp = uploadArtifact( + 'test-artifact', + [ + '/home/user/files/plz-upload/file1.txt', + '/home/user/files/plz-upload/file2.txt', + '/home/user/files/plz-upload/dir/file3.txt' + ], + '/home/user/files/plz-upload' + ) + expect(uploadResp).resolves.toEqual({success: false}) + }) + + it('should return false if no backend IDs are found', () => { + jest + .spyOn(uploadZipSpecification, 'validateRootDirectory') + .mockReturnValue() + jest + .spyOn(uploadZipSpecification, 'getUploadZipSpecification') + .mockReturnValue([ + { + sourcePath: '/home/user/files/plz-upload/file1.txt', + destinationPath: 'file1.txt' + }, + { + sourcePath: '/home/user/files/plz-upload/file2.txt', + destinationPath: 'file2.txt' + }, + { + sourcePath: '/home/user/files/plz-upload/dir/file3.txt', + destinationPath: 'dir/file3.txt' + } + ]) + + jest + .spyOn(zip, 'createZipUploadStream') + .mockReturnValue(Promise.resolve(new zip.ZipUploadStream(1))) + jest + .spyOn(util, 'getBackendIdsFromToken') + .mockReturnValue({workflowRunBackendId: '', workflowJobRunBackendId: ''}) + + const uploadResp = uploadArtifact( + 'test-artifact', + [ + '/home/user/files/plz-upload/file1.txt', + '/home/user/files/plz-upload/file2.txt', + '/home/user/files/plz-upload/dir/file3.txt' + ], + '/home/user/files/plz-upload' + ) + + expect(uploadResp).resolves.toEqual({success: false}) + }) + + it('should return false if the creation request fails', () => { + const mockDate = new Date('2020-01-01') + jest + .spyOn(uploadZipSpecification, 'validateRootDirectory') + .mockReturnValue() + jest + .spyOn(uploadZipSpecification, 'getUploadZipSpecification') + .mockReturnValue([ + { + sourcePath: '/home/user/files/plz-upload/file1.txt', + destinationPath: 'file1.txt' + }, + { + sourcePath: '/home/user/files/plz-upload/file2.txt', + destinationPath: 'file2.txt' + }, + { + sourcePath: '/home/user/files/plz-upload/dir/file3.txt', + destinationPath: 'dir/file3.txt' + } + ]) + + jest + .spyOn(zip, 'createZipUploadStream') + .mockReturnValue(Promise.resolve(new zip.ZipUploadStream(1))) + jest + .spyOn(util, 'getBackendIdsFromToken') + .mockReturnValue({ + workflowRunBackendId: '1234', + workflowJobRunBackendId: '5678' + }) + jest + .spyOn(retention, 'getExpiration') + .mockReturnValue(Timestamp.fromDate(mockDate)) + jest + .spyOn(ArtifactServiceClientJSON.prototype, 'CreateArtifact') + .mockReturnValue(Promise.resolve({ok: false, signedUploadUrl: ''})) + + // ArtifactHttpClient mocks + jest.spyOn(config, 'getRuntimeToken').mockReturnValue('test-token') + jest + .spyOn(config, 'getResultsServiceUrl') + .mockReturnValue('https://test-url.com') + + const uploadResp = uploadArtifact( + 'test-artifact', + [ + '/home/user/files/plz-upload/file1.txt', + '/home/user/files/plz-upload/file2.txt', + '/home/user/files/plz-upload/dir/file3.txt' + ], + '/home/user/files/plz-upload' + ) + + expect(uploadResp).resolves.toEqual({success: false}) + }) + + it('should return false if blob storage upload is unsuccessful', () => { + const mockDate = new Date('2020-01-01') + jest + .spyOn(uploadZipSpecification, 'validateRootDirectory') + .mockReturnValue() + jest + .spyOn(uploadZipSpecification, 'getUploadZipSpecification') + .mockReturnValue([ + { + sourcePath: '/home/user/files/plz-upload/file1.txt', + destinationPath: 'file1.txt' + }, + { + sourcePath: '/home/user/files/plz-upload/file2.txt', + destinationPath: 'file2.txt' + }, + { + sourcePath: '/home/user/files/plz-upload/dir/file3.txt', + destinationPath: 'dir/file3.txt' + } + ]) + + jest + .spyOn(zip, 'createZipUploadStream') + .mockReturnValue(Promise.resolve(new zip.ZipUploadStream(1))) + jest + .spyOn(util, 'getBackendIdsFromToken') + .mockReturnValue({ + workflowRunBackendId: '1234', + workflowJobRunBackendId: '5678' + }) + jest + .spyOn(retention, 'getExpiration') + .mockReturnValue(Timestamp.fromDate(mockDate)) + jest + .spyOn(ArtifactServiceClientJSON.prototype, 'CreateArtifact') + .mockReturnValue( + Promise.resolve({ + ok: true, + signedUploadUrl: 'https://signed-upload-url.com' + }) + ) + jest + .spyOn(blobUpload, 'uploadZipToBlobStorage') + .mockReturnValue(Promise.resolve({isSuccess: false})) + + // ArtifactHttpClient mocks + jest.spyOn(config, 'getRuntimeToken').mockReturnValue('test-token') + jest + .spyOn(config, 'getResultsServiceUrl') + .mockReturnValue('https://test-url.com') + + const uploadResp = uploadArtifact( + 'test-artifact', + [ + '/home/user/files/plz-upload/file1.txt', + '/home/user/files/plz-upload/file2.txt', + '/home/user/files/plz-upload/dir/file3.txt' + ], + '/home/user/files/plz-upload' + ) + + expect(uploadResp).resolves.toEqual({success: false}) + }) + + it('should return false if finalize artifact fails', () => { + const mockDate = new Date('2020-01-01') + jest + .spyOn(uploadZipSpecification, 'validateRootDirectory') + .mockReturnValue() + jest + .spyOn(uploadZipSpecification, 'getUploadZipSpecification') + .mockReturnValue([ + { + sourcePath: '/home/user/files/plz-upload/file1.txt', + destinationPath: 'file1.txt' + }, + { + sourcePath: '/home/user/files/plz-upload/file2.txt', + destinationPath: 'file2.txt' + }, + { + sourcePath: '/home/user/files/plz-upload/dir/file3.txt', + destinationPath: 'dir/file3.txt' + } + ]) + + jest + .spyOn(zip, 'createZipUploadStream') + .mockReturnValue(Promise.resolve(new zip.ZipUploadStream(1))) + jest + .spyOn(util, 'getBackendIdsFromToken') + .mockReturnValue({ + workflowRunBackendId: '1234', + workflowJobRunBackendId: '5678' + }) + jest + .spyOn(retention, 'getExpiration') + .mockReturnValue(Timestamp.fromDate(mockDate)) + jest + .spyOn(ArtifactServiceClientJSON.prototype, 'CreateArtifact') + .mockReturnValue( + Promise.resolve({ + ok: true, + signedUploadUrl: 'https://signed-upload-url.com' + }) + ) + jest + .spyOn(blobUpload, 'uploadZipToBlobStorage') + .mockReturnValue( + Promise.resolve({ + isSuccess: true, + uploadSize: 1234, + md5Hash: 'test-md5-hash' + }) + ) + jest + .spyOn(ArtifactServiceClientJSON.prototype, 'FinalizeArtifact') + .mockReturnValue(Promise.resolve({ok: false, artifactId: ''})) + + // ArtifactHttpClient mocks + jest.spyOn(config, 'getRuntimeToken').mockReturnValue('test-token') + jest + .spyOn(config, 'getResultsServiceUrl') + .mockReturnValue('https://test-url.com') + + const uploadResp = uploadArtifact( + 'test-artifact', + [ + '/home/user/files/plz-upload/file1.txt', + '/home/user/files/plz-upload/file2.txt', + '/home/user/files/plz-upload/dir/file3.txt' + ], + '/home/user/files/plz-upload' + ) + + expect(uploadResp).resolves.toEqual({success: false}) + }) +}) From 3b44a4cc2301c10b001dec479101ca9d1bf17979 Mon Sep 17 00:00:00 2001 From: Bethany Date: Wed, 23 Aug 2023 13:55:26 -0700 Subject: [PATCH 2/2] prettier --- .../__tests__/upload-artifact.test.ts | 72 ++++++++----------- .../internal/download/download-artifact.ts | 4 +- .../src/internal/find/list-artifacts.ts | 4 +- 3 files changed, 36 insertions(+), 44 deletions(-) diff --git a/packages/artifact/__tests__/upload-artifact.test.ts b/packages/artifact/__tests__/upload-artifact.test.ts index 808f2c1d..958b27fd 100644 --- a/packages/artifact/__tests__/upload-artifact.test.ts +++ b/packages/artifact/__tests__/upload-artifact.test.ts @@ -47,12 +47,10 @@ describe('upload-artifact', () => { jest .spyOn(zip, 'createZipUploadStream') .mockReturnValue(Promise.resolve(new zip.ZipUploadStream(1))) - jest - .spyOn(util, 'getBackendIdsFromToken') - .mockReturnValue({ - workflowRunBackendId: '1234', - workflowJobRunBackendId: '5678' - }) + jest.spyOn(util, 'getBackendIdsFromToken').mockReturnValue({ + workflowRunBackendId: '1234', + workflowJobRunBackendId: '5678' + }) jest .spyOn(retention, 'getExpiration') .mockReturnValue(Timestamp.fromDate(mockDate)) @@ -64,15 +62,13 @@ describe('upload-artifact', () => { signedUploadUrl: 'https://signed-upload-url.com' }) ) - jest - .spyOn(blobUpload, 'uploadZipToBlobStorage') - .mockReturnValue( - Promise.resolve({ - isSuccess: true, - uploadSize: 1234, - md5Hash: 'test-md5-hash' - }) - ) + jest.spyOn(blobUpload, 'uploadZipToBlobStorage').mockReturnValue( + Promise.resolve({ + isSuccess: true, + uploadSize: 1234, + md5Hash: 'test-md5-hash' + }) + ) jest .spyOn(ArtifactServiceClientJSON.prototype, 'FinalizeArtifact') .mockReturnValue(Promise.resolve({ok: true, artifactId: '1'})) @@ -202,12 +198,10 @@ describe('upload-artifact', () => { jest .spyOn(zip, 'createZipUploadStream') .mockReturnValue(Promise.resolve(new zip.ZipUploadStream(1))) - jest - .spyOn(util, 'getBackendIdsFromToken') - .mockReturnValue({ - workflowRunBackendId: '1234', - workflowJobRunBackendId: '5678' - }) + jest.spyOn(util, 'getBackendIdsFromToken').mockReturnValue({ + workflowRunBackendId: '1234', + workflowJobRunBackendId: '5678' + }) jest .spyOn(retention, 'getExpiration') .mockReturnValue(Timestamp.fromDate(mockDate)) @@ -259,12 +253,10 @@ describe('upload-artifact', () => { jest .spyOn(zip, 'createZipUploadStream') .mockReturnValue(Promise.resolve(new zip.ZipUploadStream(1))) - jest - .spyOn(util, 'getBackendIdsFromToken') - .mockReturnValue({ - workflowRunBackendId: '1234', - workflowJobRunBackendId: '5678' - }) + jest.spyOn(util, 'getBackendIdsFromToken').mockReturnValue({ + workflowRunBackendId: '1234', + workflowJobRunBackendId: '5678' + }) jest .spyOn(retention, 'getExpiration') .mockReturnValue(Timestamp.fromDate(mockDate)) @@ -324,12 +316,10 @@ describe('upload-artifact', () => { jest .spyOn(zip, 'createZipUploadStream') .mockReturnValue(Promise.resolve(new zip.ZipUploadStream(1))) - jest - .spyOn(util, 'getBackendIdsFromToken') - .mockReturnValue({ - workflowRunBackendId: '1234', - workflowJobRunBackendId: '5678' - }) + jest.spyOn(util, 'getBackendIdsFromToken').mockReturnValue({ + workflowRunBackendId: '1234', + workflowJobRunBackendId: '5678' + }) jest .spyOn(retention, 'getExpiration') .mockReturnValue(Timestamp.fromDate(mockDate)) @@ -341,15 +331,13 @@ describe('upload-artifact', () => { signedUploadUrl: 'https://signed-upload-url.com' }) ) - jest - .spyOn(blobUpload, 'uploadZipToBlobStorage') - .mockReturnValue( - Promise.resolve({ - isSuccess: true, - uploadSize: 1234, - md5Hash: 'test-md5-hash' - }) - ) + jest.spyOn(blobUpload, 'uploadZipToBlobStorage').mockReturnValue( + Promise.resolve({ + isSuccess: true, + uploadSize: 1234, + md5Hash: 'test-md5-hash' + }) + ) jest .spyOn(ArtifactServiceClientJSON.prototype, 'FinalizeArtifact') .mockReturnValue(Promise.resolve({ok: false, artifactId: ''})) diff --git a/packages/artifact/src/internal/download/download-artifact.ts b/packages/artifact/src/internal/download/download-artifact.ts index 17699dec..d229065d 100644 --- a/packages/artifact/src/internal/download/download-artifact.ts +++ b/packages/artifact/src/internal/download/download-artifact.ts @@ -61,7 +61,9 @@ export async function downloadArtifact( } if (!(await exists(downloadPath))) { - core.debug(`Artifact destination folder does not exist, creating: ${downloadPath}`) + core.debug( + `Artifact destination folder does not exist, creating: ${downloadPath}` + ) await fs.mkdir(downloadPath, {recursive: true}) } else { core.debug(`Artifact destination folder already exists: ${downloadPath}`) diff --git a/packages/artifact/src/internal/find/list-artifacts.ts b/packages/artifact/src/internal/find/list-artifacts.ts index 7fd96838..2b357ac8 100644 --- a/packages/artifact/src/internal/find/list-artifacts.ts +++ b/packages/artifact/src/internal/find/list-artifacts.ts @@ -29,7 +29,9 @@ export async function listArtifacts( repositoryName: string, token: string ): Promise { - info(`Fetching artifact list for workflow run ${workflowRunId} in repository ${repositoryOwner}/${repositoryName}`) + info( + `Fetching artifact list for workflow run ${workflowRunId} in repository ${repositoryOwner}/${repositoryName}` + ) const artifacts: Artifact[] = [] const [retryOpts, requestOpts] = getRetryOptions(