Includes the following fixes: - Fix cron entry format for clean-up script. Crontabs in `/etc` should not have a user field. No idea why it used to work before, but it started giving errors recently: `/bin/sh: root: not found`. - Set required env variable in clean-up script. (Broken in cc6f36a9d.) This was producing the following error: `ERROR: Missing required environment variable 'AIO_CIRCLE_CI_TOKEN'!` - Use the correct path for downloads to be removed. (Broken in cc6f36a9d.) PR Close #25671
		
			
				
	
	
		
			502 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			502 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| // Imports
 | |
| import * as fs from 'fs';
 | |
| import {normalize} from 'path';
 | |
| import * as shell from 'shelljs';
 | |
| import {BuildCleaner} from '../../lib/clean-up/build-cleaner';
 | |
| import {HIDDEN_DIR_PREFIX} from '../../lib/common/constants';
 | |
| import {GithubPullRequests} from '../../lib/common/github-pull-requests';
 | |
| import {Logger} from '../../lib/common/utils';
 | |
| 
 | |
| const EXISTING_BUILDS = [10, 20, 30, 40];
 | |
| const EXISTING_DOWNLOADS = [
 | |
|   '10-ABCDEF0-build.zip',
 | |
|   '10-1234567-build.zip',
 | |
|   '20-ABCDEF0-build.zip',
 | |
|   '20-1234567-build.zip',
 | |
| ];
 | |
| const OPEN_PRS = [10, 40];
 | |
| const ANY_DATE = jasmine.any(String);
 | |
| 
 | |
| // Tests
 | |
| describe('BuildCleaner', () => {
 | |
|   let loggerErrorSpy: jasmine.Spy;
 | |
|   let loggerLogSpy: jasmine.Spy;
 | |
|   let cleaner: BuildCleaner;
 | |
| 
 | |
|   beforeEach(() => {
 | |
|     loggerErrorSpy = spyOn(Logger.prototype, 'error');
 | |
|     loggerLogSpy = spyOn(Logger.prototype, 'log');
 | |
|     cleaner = new BuildCleaner('/foo/bar', 'baz', 'qux', '12345', '/downloads', 'build.zip');
 | |
|   });
 | |
| 
 | |
|   describe('constructor()', () => {
 | |
| 
 | |
|     it('should throw if \'buildsDir\' is empty', () => {
 | |
|       expect(() => new BuildCleaner('', 'baz', 'qux', '12345', 'downloads', 'build.zip')).
 | |
|         toThrowError('Missing or empty required parameter \'buildsDir\'!');
 | |
|     });
 | |
| 
 | |
| 
 | |
|     it('should throw if \'githubOrg\' is empty', () => {
 | |
|       expect(() => new BuildCleaner('/foo/bar', '', 'qux', '12345', 'downloads', 'build.zip')).
 | |
|         toThrowError('Missing or empty required parameter \'githubOrg\'!');
 | |
|     });
 | |
| 
 | |
| 
 | |
|     it('should throw if \'githubRepo\' is empty', () => {
 | |
|       expect(() => new BuildCleaner('/foo/bar', 'baz', '', '12345', 'downloads', 'build.zip')).
 | |
|         toThrowError('Missing or empty required parameter \'githubRepo\'!');
 | |
|     });
 | |
| 
 | |
| 
 | |
|     it('should throw if \'githubToken\' is empty', () => {
 | |
|       expect(() => new BuildCleaner('/foo/bar', 'baz', 'qux', '', 'downloads', 'build.zip')).
 | |
|         toThrowError('Missing or empty required parameter \'githubToken\'!');
 | |
|     });
 | |
| 
 | |
| 
 | |
|     it('should throw if \'downloadsDir\' is empty', () => {
 | |
|       expect(() => new BuildCleaner('/foo/bar', 'baz', 'qux', '12345', '', 'build.zip')).
 | |
|         toThrowError('Missing or empty required parameter \'downloadsDir\'!');
 | |
|     });
 | |
| 
 | |
| 
 | |
|     it('should throw if \'artifactPath\' is empty', () => {
 | |
|       expect(() => new BuildCleaner('/foo/bar', 'baz', 'qux', '12345', 'downloads', '')).
 | |
|         toThrowError('Missing or empty required parameter \'artifactPath\'!');
 | |
|     });
 | |
| 
 | |
|   });
 | |
| 
 | |
| 
 | |
|   describe('cleanUp()', () => {
 | |
|     let cleanerGetExistingBuildNumbersSpy: jasmine.Spy;
 | |
|     let cleanerGetOpenPrNumbersSpy: jasmine.Spy;
 | |
|     let cleanerGetExistingDownloadsSpy: jasmine.Spy;
 | |
|     let cleanerRemoveUnnecessaryBuildsSpy: jasmine.Spy;
 | |
|     let cleanerRemoveUnnecessaryDownloadsSpy: jasmine.Spy;
 | |
| 
 | |
|     beforeEach(() => {
 | |
|       cleanerGetExistingBuildNumbersSpy = spyOn(cleaner, 'getExistingBuildNumbers')
 | |
|         .and.callFake(() => Promise.resolve(EXISTING_BUILDS));
 | |
|       cleanerGetOpenPrNumbersSpy = spyOn(cleaner, 'getOpenPrNumbers')
 | |
|         .and.callFake(() => Promise.resolve(OPEN_PRS));
 | |
|       cleanerGetExistingDownloadsSpy = spyOn(cleaner, 'getExistingDownloads')
 | |
|         .and.callFake(() => Promise.resolve(EXISTING_DOWNLOADS));
 | |
| 
 | |
|       cleanerRemoveUnnecessaryBuildsSpy = spyOn(cleaner, 'removeUnnecessaryBuilds');
 | |
|       cleanerRemoveUnnecessaryDownloadsSpy = spyOn(cleaner, 'removeUnnecessaryDownloads');
 | |
| 
 | |
|     });
 | |
| 
 | |
| 
 | |
|     it('should return a promise', async () => {
 | |
|       const promise = cleaner.cleanUp();
 | |
|       expect(promise).toEqual(jasmine.any(Promise));
 | |
| 
 | |
|       // Do not complete the test and release the spies synchronously, to avoid running the actual implementations.
 | |
|       await promise;
 | |
|     });
 | |
| 
 | |
| 
 | |
|     it('should get the open PRs', async () => {
 | |
|       await cleaner.cleanUp();
 | |
|       expect(cleanerGetOpenPrNumbersSpy).toHaveBeenCalled();
 | |
|     });
 | |
| 
 | |
| 
 | |
|     it('should get the existing builds', async () => {
 | |
|       await cleaner.cleanUp();
 | |
|       expect(cleanerGetExistingBuildNumbersSpy).toHaveBeenCalled();
 | |
|     });
 | |
| 
 | |
| 
 | |
|     it('should get the existing downloads', async () => {
 | |
|       await cleaner.cleanUp();
 | |
|       expect(cleanerGetExistingDownloadsSpy).toHaveBeenCalled();
 | |
|     });
 | |
| 
 | |
| 
 | |
|     it('should pass existing builds and open PRs to \'removeUnnecessaryBuilds()\'', async () => {
 | |
|       await cleaner.cleanUp();
 | |
|       expect(cleanerRemoveUnnecessaryBuildsSpy).toHaveBeenCalledWith(EXISTING_BUILDS, OPEN_PRS);
 | |
|     });
 | |
| 
 | |
| 
 | |
|     it('should pass existing downloads and open PRs to \'removeUnnecessaryDownloads()\'', async () => {
 | |
|       await cleaner.cleanUp();
 | |
|       expect(cleanerRemoveUnnecessaryDownloadsSpy).toHaveBeenCalledWith(EXISTING_DOWNLOADS, OPEN_PRS);
 | |
|     });
 | |
| 
 | |
| 
 | |
|     it('should reject if \'getOpenPrNumbers()\' rejects', async () => {
 | |
|       try {
 | |
|         cleanerGetOpenPrNumbersSpy.and.callFake(() => Promise.reject('Test'));
 | |
|         await cleaner.cleanUp();
 | |
|       } catch (err) {
 | |
|         expect(err).toBe('Test');
 | |
|       }
 | |
|     });
 | |
| 
 | |
| 
 | |
|     it('should reject if \'getExistingBuildNumbers()\' rejects', async () => {
 | |
|       try {
 | |
|         cleanerGetExistingBuildNumbersSpy.and.callFake(() => Promise.reject('Test'));
 | |
|         await cleaner.cleanUp();
 | |
|       } catch (err) {
 | |
|         expect(err).toBe('Test');
 | |
|       }
 | |
|     });
 | |
| 
 | |
| 
 | |
|     it('should reject if \'getExistingDownloads()\' rejects', async () => {
 | |
|       try {
 | |
|         cleanerGetExistingDownloadsSpy.and.callFake(() => Promise.reject('Test'));
 | |
|         await cleaner.cleanUp();
 | |
|       } catch (err) {
 | |
|         expect(err).toBe('Test');
 | |
|       }
 | |
|     });
 | |
| 
 | |
| 
 | |
|     it('should reject if \'removeUnnecessaryBuilds()\' rejects', async () => {
 | |
|       try {
 | |
|         cleanerRemoveUnnecessaryBuildsSpy.and.callFake(() => Promise.reject('Test'));
 | |
|         await cleaner.cleanUp();
 | |
|       } catch (err) {
 | |
|         expect(err).toBe('Test');
 | |
|       }
 | |
|     });
 | |
| 
 | |
| 
 | |
|     it('should reject if \'removeUnnecessaryDownloads()\' rejects', async () => {
 | |
|       try {
 | |
|         cleanerRemoveUnnecessaryDownloadsSpy.and.callFake(() => Promise.reject('Test'));
 | |
|         await cleaner.cleanUp();
 | |
|       } catch (err) {
 | |
|         expect(err).toBe('Test');
 | |
|       }
 | |
|     });
 | |
| 
 | |
|   });
 | |
| 
 | |
| 
 | |
|   describe('getExistingBuildNumbers()', () => {
 | |
|     let fsReaddirSpy: jasmine.Spy;
 | |
|     let readdirCb: (err: any, files?: string[]) => void;
 | |
|     let promise: Promise<number[]>;
 | |
| 
 | |
|     beforeEach(() => {
 | |
|       fsReaddirSpy = spyOn(fs, 'readdir').and.callFake((_: string, cb: typeof readdirCb) => readdirCb = cb);
 | |
|       promise = cleaner.getExistingBuildNumbers();
 | |
|     });
 | |
| 
 | |
| 
 | |
|     it('should return a promise', () => {
 | |
|       expect(promise).toEqual(jasmine.any(Promise));
 | |
|     });
 | |
| 
 | |
| 
 | |
|     it('should get the contents of the builds directory', () => {
 | |
|       expect(fsReaddirSpy).toHaveBeenCalled();
 | |
|       expect(fsReaddirSpy.calls.argsFor(0)[0]).toBe('/foo/bar');
 | |
|     });
 | |
| 
 | |
| 
 | |
|     it('should reject if an error occurs while getting the files', done => {
 | |
|       promise.catch(err => {
 | |
|         expect(err).toBe('Test');
 | |
|         done();
 | |
|       });
 | |
| 
 | |
|       readdirCb('Test');
 | |
|     });
 | |
| 
 | |
| 
 | |
|     it('should resolve with the returned files (as numbers)', done => {
 | |
|       promise.then(result => {
 | |
|         expect(result).toEqual([12, 34, 56]);
 | |
|         done();
 | |
|       });
 | |
| 
 | |
|       readdirCb(null, ['12', '34', '56']);
 | |
|     });
 | |
| 
 | |
| 
 | |
|     it('should remove `HIDDEN_DIR_PREFIX` from the filenames', done => {
 | |
|       promise.then(result => {
 | |
|         expect(result).toEqual([12, 34, 56]);
 | |
|         done();
 | |
|       });
 | |
| 
 | |
|       readdirCb(null, [`${HIDDEN_DIR_PREFIX}12`, '34', `${HIDDEN_DIR_PREFIX}56`]);
 | |
|     });
 | |
| 
 | |
| 
 | |
|     it('should ignore files with non-numeric (or zero) names', done => {
 | |
|       promise.then(result => {
 | |
|         expect(result).toEqual([12, 34, 56]);
 | |
|         done();
 | |
|       });
 | |
| 
 | |
|       readdirCb(null, ['12', 'foo', '34', 'bar', '56', '000']);
 | |
|     });
 | |
| 
 | |
|   });
 | |
| 
 | |
| 
 | |
|   describe('getOpenPrNumbers()', () => {
 | |
|     let prDeferred: {resolve: (v: any) => void, reject: (v: any) => void};
 | |
|     let promise: Promise<number[]>;
 | |
| 
 | |
|     beforeEach(() => {
 | |
|       spyOn(GithubPullRequests.prototype, 'fetchAll').and.callFake(() => {
 | |
|         return new Promise((resolve, reject) => prDeferred = {resolve, reject});
 | |
|       });
 | |
| 
 | |
|       promise = cleaner.getOpenPrNumbers();
 | |
|     });
 | |
| 
 | |
| 
 | |
|     it('should return a promise', () => {
 | |
|       expect(promise).toEqual(jasmine.any(Promise));
 | |
|     });
 | |
| 
 | |
| 
 | |
|     it('should fetch open PRs via \'GithubPullRequests\'', () => {
 | |
|       expect(GithubPullRequests.prototype.fetchAll).toHaveBeenCalledWith('open');
 | |
|     });
 | |
| 
 | |
| 
 | |
|     it('should reject if an error occurs while fetching PRs', done => {
 | |
|       promise.catch(err => {
 | |
|         expect(err).toBe('Test');
 | |
|         done();
 | |
|       });
 | |
| 
 | |
|       prDeferred.reject('Test');
 | |
|     });
 | |
| 
 | |
| 
 | |
|     it('should resolve with the numbers of the fetched PRs', done => {
 | |
|       promise.then(prNumbers => {
 | |
|         expect(prNumbers).toEqual([1, 2, 3]);
 | |
|         done();
 | |
|       });
 | |
| 
 | |
|       prDeferred.resolve([{id: 0, number: 1}, {id: 1, number: 2}, {id: 2, number: 3}]);
 | |
|     });
 | |
| 
 | |
| 
 | |
|     it('should log the number of open PRs', () => {
 | |
|       promise.then(prNumbers => {
 | |
|         expect(loggerLogSpy).toHaveBeenCalledWith(
 | |
|           ANY_DATE, 'BuildCleaner:        ', `Open pull requests: ${prNumbers}`);
 | |
|       });
 | |
|     });
 | |
| 
 | |
|   });
 | |
| 
 | |
| 
 | |
|   describe('getExistingDownloads()', () => {
 | |
|     let fsReaddirSpy: jasmine.Spy;
 | |
|     let readdirCb: (err: any, files?: string[]) => void;
 | |
|     let promise: Promise<string[]>;
 | |
| 
 | |
|     beforeEach(() => {
 | |
|       fsReaddirSpy = spyOn(fs, 'readdir').and.callFake((_: string, cb: typeof readdirCb) => readdirCb = cb);
 | |
|       promise = cleaner.getExistingDownloads();
 | |
|     });
 | |
| 
 | |
| 
 | |
|     it('should return a promise', () => {
 | |
|       expect(promise).toEqual(jasmine.any(Promise));
 | |
|     });
 | |
| 
 | |
| 
 | |
|     it('should get the contents of the downloads directory', () => {
 | |
|       expect(fsReaddirSpy).toHaveBeenCalled();
 | |
|       expect(fsReaddirSpy.calls.argsFor(0)[0]).toBe('/downloads');
 | |
|     });
 | |
| 
 | |
| 
 | |
|     it('should reject if an error occurs while getting the files', done => {
 | |
|       promise.catch(err => {
 | |
|         expect(err).toBe('Test');
 | |
|         done();
 | |
|       });
 | |
| 
 | |
|       readdirCb('Test');
 | |
|     });
 | |
| 
 | |
| 
 | |
|     it('should resolve with the returned file names', done => {
 | |
|       promise.then(result => {
 | |
|         expect(result).toEqual(EXISTING_DOWNLOADS);
 | |
|         done();
 | |
|       });
 | |
| 
 | |
|       readdirCb(null, EXISTING_DOWNLOADS);
 | |
|     });
 | |
| 
 | |
| 
 | |
|     it('should ignore files that do not match the artifactPath', done => {
 | |
|       promise.then(result => {
 | |
|         expect(result).toEqual(['10-ABCDEF-build.zip', '30-FFFFFFF-build.zip']);
 | |
|         done();
 | |
|       });
 | |
| 
 | |
|       readdirCb(null, ['10-ABCDEF-build.zip', '20-AAAAAAA-otherfile.zip', '30-FFFFFFF-build.zip']);
 | |
|     });
 | |
| 
 | |
|   });
 | |
| 
 | |
| 
 | |
|   describe('removeDir()', () => {
 | |
|     let shellChmodSpy: jasmine.Spy;
 | |
|     let shellRmSpy: jasmine.Spy;
 | |
|     let shellTestSpy: jasmine.Spy;
 | |
| 
 | |
|     beforeEach(() => {
 | |
|       shellChmodSpy = spyOn(shell, 'chmod');
 | |
|       shellRmSpy = spyOn(shell, 'rm');
 | |
|       shellTestSpy = spyOn(shell, 'test').and.returnValue(true);
 | |
|     });
 | |
| 
 | |
| 
 | |
|     it('should test if the directory exists (and return if is does not)', () => {
 | |
|       shellTestSpy.and.returnValue(false);
 | |
|       cleaner.removeDir('/foo/bar');
 | |
| 
 | |
|       expect(shellTestSpy).toHaveBeenCalledWith('-d', '/foo/bar');
 | |
|       expect(shellChmodSpy).not.toHaveBeenCalled();
 | |
|       expect(shellRmSpy).not.toHaveBeenCalled();
 | |
|     });
 | |
| 
 | |
| 
 | |
|     it('should remove the specified directory and its content', () => {
 | |
|       cleaner.removeDir('/foo/bar');
 | |
|       expect(shellRmSpy).toHaveBeenCalledWith('-rf', '/foo/bar');
 | |
|     });
 | |
| 
 | |
| 
 | |
|     it('should make the directory and its content writable before removing', () => {
 | |
|       shellRmSpy.and.callFake(() => expect(shellChmodSpy).toHaveBeenCalledWith('-R', 'a+w', '/foo/bar'));
 | |
|       cleaner.removeDir('/foo/bar');
 | |
| 
 | |
|       expect(shellRmSpy).toHaveBeenCalled();
 | |
|     });
 | |
| 
 | |
| 
 | |
|     it('should catch errors and log them', () => {
 | |
|       shellRmSpy.and.callFake(() => {
 | |
|         // tslint:disable-next-line: no-string-throw
 | |
|         throw 'Test';
 | |
|       });
 | |
| 
 | |
|       cleaner.removeDir('/foo/bar');
 | |
| 
 | |
|       expect(loggerErrorSpy).toHaveBeenCalledWith('ERROR: Unable to remove \'/foo/bar\' due to:', 'Test');
 | |
|     });
 | |
| 
 | |
|   });
 | |
| 
 | |
| 
 | |
|   describe('removeUnnecessaryBuilds()', () => {
 | |
|     let cleanerRemoveDirSpy: jasmine.Spy;
 | |
| 
 | |
|     beforeEach(() => {
 | |
|       cleanerRemoveDirSpy = spyOn(cleaner, 'removeDir');
 | |
|     });
 | |
| 
 | |
| 
 | |
|     it('should log the number of existing builds and builds to be removed', () => {
 | |
|       cleaner.removeUnnecessaryBuilds([1, 2, 3], [3, 4, 5, 6]);
 | |
| 
 | |
|       expect(loggerLogSpy).toHaveBeenCalledWith('Existing builds: 3');
 | |
|       expect(loggerLogSpy).toHaveBeenCalledWith('Removing 2 build(s): 1, 2');
 | |
|     });
 | |
| 
 | |
| 
 | |
|     it('should construct full paths to directories (by prepending \'buildsDir\')', () => {
 | |
|       cleaner.removeUnnecessaryBuilds([1, 2, 3], []);
 | |
| 
 | |
|       expect(cleanerRemoveDirSpy).toHaveBeenCalledWith(normalize('/foo/bar/1'));
 | |
|       expect(cleanerRemoveDirSpy).toHaveBeenCalledWith(normalize('/foo/bar/2'));
 | |
|       expect(cleanerRemoveDirSpy).toHaveBeenCalledWith(normalize('/foo/bar/3'));
 | |
|     });
 | |
| 
 | |
| 
 | |
|     it('should try removing hidden directories as well', () => {
 | |
|       cleaner.removeUnnecessaryBuilds([1, 2, 3], []);
 | |
| 
 | |
|       expect(cleanerRemoveDirSpy).toHaveBeenCalledWith(normalize(`/foo/bar/${HIDDEN_DIR_PREFIX}1`));
 | |
|       expect(cleanerRemoveDirSpy).toHaveBeenCalledWith(normalize(`/foo/bar/${HIDDEN_DIR_PREFIX}2`));
 | |
|       expect(cleanerRemoveDirSpy).toHaveBeenCalledWith(normalize(`/foo/bar/${HIDDEN_DIR_PREFIX}3`));
 | |
|     });
 | |
| 
 | |
| 
 | |
|     it('should remove the builds that do not correspond to open PRs', () => {
 | |
|       cleaner.removeUnnecessaryBuilds([1, 2, 3, 4], [2, 4]);
 | |
|       expect(cleanerRemoveDirSpy).toHaveBeenCalledTimes(4);
 | |
|       expect(cleanerRemoveDirSpy).toHaveBeenCalledWith(normalize('/foo/bar/1'));
 | |
|       expect(cleanerRemoveDirSpy).toHaveBeenCalledWith(normalize('/foo/bar/3'));
 | |
|       expect(cleanerRemoveDirSpy).toHaveBeenCalledWith(normalize(`/foo/bar/${HIDDEN_DIR_PREFIX}1`));
 | |
|       expect(cleanerRemoveDirSpy).toHaveBeenCalledWith(normalize(`/foo/bar/${HIDDEN_DIR_PREFIX}3`));
 | |
|       cleanerRemoveDirSpy.calls.reset();
 | |
| 
 | |
|       cleaner.removeUnnecessaryBuilds([1, 2, 3, 4], [1, 2, 3, 4]);
 | |
|       expect(cleanerRemoveDirSpy).toHaveBeenCalledTimes(0);
 | |
|       cleanerRemoveDirSpy.calls.reset();
 | |
| 
 | |
|       (cleaner as any).removeUnnecessaryBuilds([1, 2, 3, 4], []);
 | |
|       expect(cleanerRemoveDirSpy).toHaveBeenCalledTimes(8);
 | |
|       expect(cleanerRemoveDirSpy).toHaveBeenCalledWith(normalize('/foo/bar/1'));
 | |
|       expect(cleanerRemoveDirSpy).toHaveBeenCalledWith(normalize('/foo/bar/2'));
 | |
|       expect(cleanerRemoveDirSpy).toHaveBeenCalledWith(normalize('/foo/bar/3'));
 | |
|       expect(cleanerRemoveDirSpy).toHaveBeenCalledWith(normalize('/foo/bar/4'));
 | |
|       expect(cleanerRemoveDirSpy).toHaveBeenCalledWith(normalize(`/foo/bar/${HIDDEN_DIR_PREFIX}1`));
 | |
|       expect(cleanerRemoveDirSpy).toHaveBeenCalledWith(normalize(`/foo/bar/${HIDDEN_DIR_PREFIX}2`));
 | |
|       expect(cleanerRemoveDirSpy).toHaveBeenCalledWith(normalize(`/foo/bar/${HIDDEN_DIR_PREFIX}3`));
 | |
|       expect(cleanerRemoveDirSpy).toHaveBeenCalledWith(normalize(`/foo/bar/${HIDDEN_DIR_PREFIX}4`));
 | |
|       cleanerRemoveDirSpy.calls.reset();
 | |
|     });
 | |
| 
 | |
|   });
 | |
| 
 | |
| 
 | |
|   describe('removeUnnecessaryDownloads()', () => {
 | |
|     let shellRmSpy: jasmine.Spy;
 | |
| 
 | |
|     beforeEach(() => {
 | |
|       shellRmSpy = spyOn(shell, 'rm');
 | |
|     });
 | |
| 
 | |
| 
 | |
|     it('should log the number of existing downloads and downloads to be removed', () => {
 | |
|       cleaner.removeUnnecessaryDownloads(EXISTING_DOWNLOADS, OPEN_PRS);
 | |
| 
 | |
|       expect(loggerLogSpy).toHaveBeenCalledWith('Existing downloads: 4');
 | |
|       expect(loggerLogSpy).toHaveBeenCalledWith('Removing 2 download(s): 20-ABCDEF0-build.zip, 20-1234567-build.zip');
 | |
|     });
 | |
| 
 | |
| 
 | |
|     it('should construct full paths to directories (by prepending \'downloadsDir\')', () => {
 | |
|       cleaner.removeUnnecessaryDownloads(['dl-1', 'dl-2', 'dl-3'], []);
 | |
| 
 | |
|       expect(shellRmSpy).toHaveBeenCalledWith(normalize('/downloads/dl-1'));
 | |
|       expect(shellRmSpy).toHaveBeenCalledWith(normalize('/downloads/dl-2'));
 | |
|       expect(shellRmSpy).toHaveBeenCalledWith(normalize('/downloads/dl-3'));
 | |
|     });
 | |
| 
 | |
| 
 | |
|     it('should remove the downloads that do not correspond to open PRs', () => {
 | |
|       cleaner.removeUnnecessaryDownloads(EXISTING_DOWNLOADS, OPEN_PRS);
 | |
|       expect(shellRmSpy).toHaveBeenCalledTimes(2);
 | |
|       expect(shellRmSpy).toHaveBeenCalledWith(normalize('/downloads/20-ABCDEF0-build.zip'));
 | |
|       expect(shellRmSpy).toHaveBeenCalledWith(normalize('/downloads/20-1234567-build.zip'));
 | |
|     });
 | |
| 
 | |
|   });
 | |
| });
 |