mirror of https://github.com/actions/toolkit
463 lines
14 KiB
TypeScript
463 lines
14 KiB
TypeScript
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, 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,
|
|
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()
|
|
})
|
|
|
|
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({
|
|
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).resolves.toEqual({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 reject 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).rejects.toThrowError(FilesNotFoundError)
|
|
})
|
|
|
|
it('should reject 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)))
|
|
|
|
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()
|
|
})
|
|
|
|
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).rejects.toThrow()
|
|
})
|
|
|
|
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.reject(new Error('boom')))
|
|
|
|
// 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.toThrow()
|
|
})
|
|
|
|
it('should reject 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({
|
|
uploadSize: 1234,
|
|
sha256Hash: 'test-sha256-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).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)
|
|
})
|
|
})
|
|
})
|