From 6e1d7543c4603a0803b99457aee11bbd8331d801 Mon Sep 17 00:00:00 2001 From: Vallie Joseph Date: Mon, 8 Apr 2024 17:21:24 +0000 Subject: [PATCH] updating tests --- .../__tests__/upload-artifact.test.ts | 112 +++++++++++++++++- packages/artifact/src/internal/upload/zip.ts | 35 +++--- 2 files changed, 128 insertions(+), 19 deletions(-) diff --git a/packages/artifact/__tests__/upload-artifact.test.ts b/packages/artifact/__tests__/upload-artifact.test.ts index b0dca5c8..b03b4aab 100644 --- a/packages/artifact/__tests__/upload-artifact.test.ts +++ b/packages/artifact/__tests__/upload-artifact.test.ts @@ -7,11 +7,21 @@ import {Timestamp, ArtifactServiceClientJSON} from '../src/generated' import * as blobUpload from '../src/internal/upload/blob-upload' import {uploadArtifact} from '../src/internal/upload/upload-artifact' import {noopLogs} from './common' -import {FilesNotFoundError} from '../src/internal/shared/errors' +import { + FilesNotFoundError, + InvalidResponseError +} from '../src/internal/shared/errors' +class NodeJSError extends Error { + code: string + constructor(message?: string, code?: string) { + super(message) // Pass the message to the Error constructor + this.code = code || '' + } +} describe('upload-artifact', () => { beforeEach(() => { - noopLogs() + // noopLogs() }) afterEach(() => { @@ -351,4 +361,102 @@ describe('upload-artifact', () => { expect(uploadResp).rejects.toThrow() }) + + describe('should respond with non-successful callback on different zipstream lifecycle methods', () => { + beforeEach(() => { + noopLogs() + }) + + afterEach(() => { + jest.restoreAllMocks() + }) + + it('should handle ENOENT error', async () => { + 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' + } + ]) + + const mockZipStream = { + entry: jest.fn((source, data, callback) => { + const err = (new NodeJSError( + "ENOENT: no such file or directory, open '/home/user/files/plz-upload/file1.txt'" + ).code = 'ENOENT') + callback(null, err) + }), + pipe: jest.fn(), + on: jest.fn(), + finalize: jest.fn() + } + + jest.mock('zip-stream', () => { + return { + default: jest.fn().mockImplementation(() => mockZipStream) + } + }) + + jest + .spyOn(zip, 'createZipUploadStream') + .mockReturnValue( + Promise.reject( + new NodeJSError( + "ENOENT: no such file or directory, open '/home/user/files/plz-upload/file1.txt'" + ) + ) + ) + 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({ + uploadSize: 1234, + sha256Hash: 'test-sha256-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).rejects.toThrowError(InvalidResponseError) + }) + }) }) diff --git a/packages/artifact/src/internal/upload/zip.ts b/packages/artifact/src/internal/upload/zip.ts index 3f717694..bdae11b6 100644 --- a/packages/artifact/src/internal/upload/zip.ts +++ b/packages/artifact/src/internal/upload/zip.ts @@ -43,8 +43,8 @@ export async function createZipUploadStream( const zipUploadStream = new ZipUploadStream(bufferSize) zip.pipe(zipUploadStream) // register callbacks for various events during the zip lifecycle - zip.on('error', zipErrorCallback) zip.on('warning', zipWarningCallback) + zip.on('error', zipErrorCallback) zip.on('finish', zipFinishCallback) zip.on('end', zipEndCallback) @@ -75,23 +75,24 @@ export async function createZipUploadStream( } } - async.eachSeries(uploadSpecification, addFileToZip, (error: unknown) => { - if (error) { - core.error('Failed to add a file to the zip:') - core.info(error.toString()) // Convert error to string - return - } - zip.finalize() // Finalize the archive once all files have been added + return new Promise((resolve, reject) => { + async.eachSeries(uploadSpecification, addFileToZip, (error: unknown) => { + if (error) { + core.error('Failed to add a file to the zip:') + core.info(error.toString()) // Convert error to string + reject(error) + return + } + zip.finalize() // Finalize the archive once all files have been added + core.debug( + `Zip write high watermark value ${zipUploadStream.writableHighWaterMark}` + ) + core.debug( + `Zip read high watermark value ${zipUploadStream.readableHighWaterMark}` + ) + resolve(zipUploadStream) + }) }) - - core.debug( - `Zip write high watermark value ${zipUploadStream.writableHighWaterMark}` - ) - core.debug( - `Zip read high watermark value ${zipUploadStream.readableHighWaterMark}` - ) - - return zipUploadStream } // eslint-disable-next-line @typescript-eslint/no-explicit-any