194 lines
7.8 KiB
TypeScript
194 lines
7.8 KiB
TypeScript
import * as fs from 'fs';
|
|
import * as nock from 'nock';
|
|
import {resolve as resolvePath} from 'path';
|
|
import {BuildInfo, CircleCiApi} from '../../lib/common/circle-ci-api';
|
|
import {Logger} from '../../lib/common/utils';
|
|
import {BuildRetriever} from '../../lib/preview-server/build-retriever';
|
|
|
|
describe('BuildRetriever', () => {
|
|
const MAX_DOWNLOAD_SIZE = 10000;
|
|
const DOWNLOAD_DIR = resolvePath('/DOWNLOAD/DIR');
|
|
const BASE_URL = 'http://test.com';
|
|
const ARTIFACT_PATH = '/some/path/build.zip';
|
|
|
|
let api: CircleCiApi;
|
|
let BUILD_INFO: BuildInfo;
|
|
let WRITEFILE_RESULT: any;
|
|
let writeFileSpy: jasmine.Spy;
|
|
let EXISTS_RESULT: boolean;
|
|
let existsSpy: jasmine.Spy;
|
|
let getBuildArtifactUrlSpy: jasmine.Spy;
|
|
|
|
beforeEach(() => {
|
|
BUILD_INFO = {
|
|
branch: 'pull/777',
|
|
build_num: 12345,
|
|
failed: false,
|
|
has_artifacts: true,
|
|
outcome: 'success',
|
|
reponame: 'REPO',
|
|
username: 'ORG',
|
|
vcs_revision: 'COMMIT',
|
|
};
|
|
|
|
api = new CircleCiApi('ORG', 'REPO', 'TOKEN');
|
|
spyOn(api, 'getBuildInfo').and.callFake(() => Promise.resolve(BUILD_INFO));
|
|
getBuildArtifactUrlSpy = spyOn(api, 'getBuildArtifactUrl')
|
|
.and.callFake(() => Promise.resolve(BASE_URL + ARTIFACT_PATH));
|
|
|
|
WRITEFILE_RESULT = undefined;
|
|
writeFileSpy = spyOn(fs, 'writeFile').and.callFake(
|
|
(_path: string, _buffer: Buffer, callback: (err?: any) => {}) => callback(WRITEFILE_RESULT),
|
|
);
|
|
|
|
EXISTS_RESULT = false;
|
|
existsSpy = spyOn(fs, 'exists').and.callFake(
|
|
(_path: string, callback: (exists: boolean) => {}) => callback(EXISTS_RESULT),
|
|
);
|
|
});
|
|
|
|
describe('constructor', () => {
|
|
it('should fail if the "downloadSizeLimit" is invalid', () => {
|
|
expect(() => new BuildRetriever(api, NaN, DOWNLOAD_DIR))
|
|
.toThrowError(`Invalid parameter "downloadSizeLimit" should be a number greater than 0.`);
|
|
expect(() => new BuildRetriever(api, 0, DOWNLOAD_DIR))
|
|
.toThrowError(`Invalid parameter "downloadSizeLimit" should be a number greater than 0.`);
|
|
expect(() => new BuildRetriever(api, -1, DOWNLOAD_DIR))
|
|
.toThrowError(`Invalid parameter "downloadSizeLimit" should be a number greater than 0.`);
|
|
});
|
|
it('should fail if the "downloadDir" is missing', () => {
|
|
expect(() => new BuildRetriever(api, MAX_DOWNLOAD_SIZE, ''))
|
|
.toThrowError(`Missing or empty required parameter 'downloadDir'!`);
|
|
});
|
|
});
|
|
|
|
|
|
describe('getGithubInfo', () => {
|
|
it('should request the info from CircleCI', async () => {
|
|
const retriever = new BuildRetriever(api, MAX_DOWNLOAD_SIZE, DOWNLOAD_DIR);
|
|
const info = await retriever.getGithubInfo(12345);
|
|
expect(api.getBuildInfo).toHaveBeenCalledWith(12345);
|
|
expect(info).toEqual({org: 'ORG', pr: 777, repo: 'REPO', sha: 'COMMIT', success: true});
|
|
});
|
|
|
|
it('should error if it is not possible to extract the PR number from the branch', async () => {
|
|
const retriever = new BuildRetriever(api, MAX_DOWNLOAD_SIZE, DOWNLOAD_DIR);
|
|
try {
|
|
BUILD_INFO.branch = 'master';
|
|
await retriever.getGithubInfo(12345);
|
|
throw new Error('Exception Expected');
|
|
} catch (error) {
|
|
expect(error.message).toEqual('No PR found in branch field: master');
|
|
}
|
|
});
|
|
});
|
|
|
|
|
|
describe('downloadBuildArtifact', () => {
|
|
const ARTIFACT_CONTENTS = 'ARTIFACT CONTENTS';
|
|
let retriever: BuildRetriever;
|
|
|
|
beforeEach(() => {
|
|
spyOn(Logger.prototype, 'warn');
|
|
retriever = new BuildRetriever(api, MAX_DOWNLOAD_SIZE, DOWNLOAD_DIR);
|
|
});
|
|
|
|
it('should get the artifact URL from the CircleCI API', async () => {
|
|
const artifactRequest = nock(BASE_URL).get(ARTIFACT_PATH).reply(200, ARTIFACT_CONTENTS);
|
|
await retriever.downloadBuildArtifact(12345, 777, 'COMMIT', ARTIFACT_PATH);
|
|
expect(api.getBuildArtifactUrl).toHaveBeenCalledWith(12345, ARTIFACT_PATH);
|
|
artifactRequest.done();
|
|
});
|
|
|
|
it('should download the artifact from its URL', async () => {
|
|
const artifactRequest = nock(BASE_URL).get(ARTIFACT_PATH).reply(200, ARTIFACT_CONTENTS);
|
|
await retriever.downloadBuildArtifact(12345, 777, 'COMMIT', ARTIFACT_PATH);
|
|
// The following line proves that the artifact URL fetch occurred.
|
|
artifactRequest.done();
|
|
});
|
|
|
|
it('should fail if the artifact is too large', async () => {
|
|
const artifactRequest = nock(BASE_URL).get(ARTIFACT_PATH).reply(200, ARTIFACT_CONTENTS);
|
|
retriever = new BuildRetriever(api, 10, DOWNLOAD_DIR);
|
|
try {
|
|
await retriever.downloadBuildArtifact(12345, 777, 'COMMIT', ARTIFACT_PATH);
|
|
throw new Error('Exception Expected');
|
|
} catch (error) {
|
|
expect(error.status).toEqual(413);
|
|
}
|
|
artifactRequest.done();
|
|
});
|
|
|
|
it('should not download the artifact if it already exists', async () => {
|
|
const artifactRequestInterceptor = nock(BASE_URL).get(ARTIFACT_PATH);
|
|
const artifactRequest = artifactRequestInterceptor.reply(200, ARTIFACT_CONTENTS);
|
|
EXISTS_RESULT = true;
|
|
await retriever.downloadBuildArtifact(12345, 777, 'COMMIT', ARTIFACT_PATH);
|
|
expect(existsSpy).toHaveBeenCalled();
|
|
expect(getBuildArtifactUrlSpy).not.toHaveBeenCalled();
|
|
expect(artifactRequest.isDone()).toEqual(false);
|
|
nock.removeInterceptor(artifactRequestInterceptor);
|
|
});
|
|
|
|
it('should write the artifact file to disk', async () => {
|
|
const artifactRequest = nock(BASE_URL).get(ARTIFACT_PATH).reply(200, ARTIFACT_CONTENTS);
|
|
const downloadPath = resolvePath(`${DOWNLOAD_DIR}/777-COMMIT-build.zip`);
|
|
|
|
await retriever.downloadBuildArtifact(12345, 777, 'COMMIT', ARTIFACT_PATH);
|
|
expect(writeFileSpy).toHaveBeenCalledWith(downloadPath, jasmine.any(Buffer), jasmine.any(Function));
|
|
|
|
const buffer: Buffer = writeFileSpy.calls.mostRecent().args[1];
|
|
expect(buffer.toString()).toEqual(ARTIFACT_CONTENTS);
|
|
|
|
artifactRequest.done();
|
|
});
|
|
|
|
it('should fail if the CircleCI API fails', async () => {
|
|
try {
|
|
getBuildArtifactUrlSpy.and.callFake(() => Promise.reject('getBuildArtifactUrl failed'));
|
|
await retriever.downloadBuildArtifact(12345, 777, 'COMMIT', ARTIFACT_PATH);
|
|
throw new Error('Exception Expected');
|
|
} catch (error) {
|
|
expect(error.message).toEqual('CircleCI artifact download failed (getBuildArtifactUrl failed)');
|
|
}
|
|
});
|
|
|
|
it('should fail if the URL fetch errors', async () => {
|
|
// create a new handler that errors
|
|
const artifactRequest = nock(BASE_URL).get(ARTIFACT_PATH).replyWithError('Artifact Request Failed');
|
|
try {
|
|
await retriever.downloadBuildArtifact(12345, 777, 'COMMIT', ARTIFACT_PATH);
|
|
throw new Error('Exception Expected');
|
|
} catch (error) {
|
|
expect(error.message).toEqual('CircleCI artifact download failed ' +
|
|
'(request to http://test.com/some/path/build.zip failed, reason: Artifact Request Failed)');
|
|
}
|
|
artifactRequest.done();
|
|
});
|
|
|
|
it('should fail if the URL fetch 404s', async () => {
|
|
// create a new handler that errors
|
|
const artifactRequest = nock(BASE_URL).get(ARTIFACT_PATH).reply(404, 'No such artifact');
|
|
try {
|
|
await retriever.downloadBuildArtifact(12345, 777, 'COMMIT', ARTIFACT_PATH);
|
|
throw new Error('Exception Expected');
|
|
} catch (error) {
|
|
expect(error.message).toEqual('CircleCI artifact download failed (Error 404 - Not Found)');
|
|
}
|
|
artifactRequest.done();
|
|
});
|
|
|
|
it('should fail if file write fails', async () => {
|
|
const artifactRequest = nock(BASE_URL).get(ARTIFACT_PATH).reply(200, ARTIFACT_CONTENTS);
|
|
try {
|
|
WRITEFILE_RESULT = 'Test Error';
|
|
await retriever.downloadBuildArtifact(12345, 777, 'COMMIT', ARTIFACT_PATH);
|
|
throw new Error('Exception Expected');
|
|
} catch (error) {
|
|
expect(error.message).toEqual('CircleCI artifact download failed (Test Error)');
|
|
}
|
|
artifactRequest.done();
|
|
});
|
|
});
|
|
});
|