| 
									
										
										
										
											2017-02-06 20:40:28 +02:00
										 |  |  | // Imports
 | 
					
						
							|  |  |  | import * as fs from 'fs'; | 
					
						
							|  |  |  | import * as shell from 'shelljs'; | 
					
						
							|  |  |  | import {BuildCleaner} from '../../lib/clean-up/build-cleaner'; | 
					
						
							|  |  |  | import {GithubPullRequests} from '../../lib/common/github-pull-requests'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Tests
 | 
					
						
							|  |  |  | describe('BuildCleaner', () => { | 
					
						
							|  |  |  |   let cleaner: BuildCleaner; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   beforeEach(() => cleaner = new BuildCleaner('/foo/bar', 'baz/qux', '12345')); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   describe('constructor()', () => { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('should throw if \'buildsDir\' is empty', () => { | 
					
						
							| 
									
										
										
										
											2017-02-27 22:40:13 +02:00
										 |  |  |       expect(() => new BuildCleaner('', '/baz/qux', '12345')). | 
					
						
							|  |  |  |         toThrowError('Missing or empty required parameter \'buildsDir\'!'); | 
					
						
							| 
									
										
										
										
											2017-02-06 20:40:28 +02:00
										 |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-27 11:36:12 +02:00
										 |  |  |     it('should throw if \'repoSlug\' is empty', () => { | 
					
						
							| 
									
										
										
										
											2017-02-27 22:40:13 +02:00
										 |  |  |       expect(() => new BuildCleaner('/foo/bar', '', '12345')). | 
					
						
							|  |  |  |         toThrowError('Missing or empty required parameter \'repoSlug\'!'); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('should throw if \'githubToken\' is empty', () => { | 
					
						
							|  |  |  |       expect(() => new BuildCleaner('/foo/bar', 'baz/qux', '')). | 
					
						
							|  |  |  |         toThrowError('Missing or empty required parameter \'githubToken\'!'); | 
					
						
							| 
									
										
										
										
											2017-02-06 20:40:28 +02:00
										 |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   describe('cleanUp()', () => { | 
					
						
							|  |  |  |     let cleanerGetExistingBuildNumbersSpy: jasmine.Spy; | 
					
						
							|  |  |  |     let cleanerGetOpenPrNumbersSpy: jasmine.Spy; | 
					
						
							|  |  |  |     let cleanerRemoveUnnecessaryBuildsSpy: jasmine.Spy; | 
					
						
							|  |  |  |     let existingBuildsDeferred: {resolve: Function, reject: Function}; | 
					
						
							|  |  |  |     let openPrsDeferred: {resolve: Function, reject: Function}; | 
					
						
							|  |  |  |     let promise: Promise<void>; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     beforeEach(() => { | 
					
						
							|  |  |  |       cleanerGetExistingBuildNumbersSpy = spyOn(cleaner as any, 'getExistingBuildNumbers').and.callFake(() => { | 
					
						
							|  |  |  |         return new Promise((resolve, reject) => existingBuildsDeferred = {resolve, reject}); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |       cleanerGetOpenPrNumbersSpy = spyOn(cleaner as any, 'getOpenPrNumbers').and.callFake(() => { | 
					
						
							|  |  |  |         return new Promise((resolve, reject) => openPrsDeferred = {resolve, reject}); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |       cleanerRemoveUnnecessaryBuildsSpy = spyOn(cleaner as any, 'removeUnnecessaryBuilds'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       promise = cleaner.cleanUp(); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('should return a promise', () => { | 
					
						
							|  |  |  |       expect(promise).toEqual(jasmine.any(Promise)); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('should get the existing builds', () => { | 
					
						
							|  |  |  |       expect(cleanerGetExistingBuildNumbersSpy).toHaveBeenCalled(); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('should get the open PRs', () => { | 
					
						
							|  |  |  |       expect(cleanerGetOpenPrNumbersSpy).toHaveBeenCalled(); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('should reject if \'getExistingBuildNumbers()\' rejects', done => { | 
					
						
							|  |  |  |       promise.catch(err => { | 
					
						
							|  |  |  |         expect(err).toBe('Test'); | 
					
						
							|  |  |  |         done(); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       existingBuildsDeferred.reject('Test'); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('should reject if \'getOpenPrNumbers()\' rejects', done => { | 
					
						
							|  |  |  |       promise.catch(err => { | 
					
						
							|  |  |  |         expect(err).toBe('Test'); | 
					
						
							|  |  |  |         done(); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       openPrsDeferred.reject('Test'); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('should reject if \'removeUnnecessaryBuilds()\' rejects', done => { | 
					
						
							|  |  |  |       promise.catch(err => { | 
					
						
							|  |  |  |         expect(err).toBe('Test'); | 
					
						
							|  |  |  |         done(); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       cleanerRemoveUnnecessaryBuildsSpy.and.returnValue(Promise.reject('Test')); | 
					
						
							|  |  |  |       existingBuildsDeferred.resolve(); | 
					
						
							|  |  |  |       openPrsDeferred.resolve(); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('should pass existing builds and open PRs to \'removeUnnecessaryBuilds()\'', done => { | 
					
						
							|  |  |  |       promise.then(() => { | 
					
						
							|  |  |  |         expect(cleanerRemoveUnnecessaryBuildsSpy).toHaveBeenCalledWith('foo', 'bar'); | 
					
						
							|  |  |  |         done(); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       existingBuildsDeferred.resolve('foo'); | 
					
						
							|  |  |  |       openPrsDeferred.resolve('bar'); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('should resolve with the value returned by \'removeUnnecessaryBuilds()\'', done => { | 
					
						
							|  |  |  |       promise.then(result => { | 
					
						
							|  |  |  |         expect(result).toBe('Test'); | 
					
						
							|  |  |  |         done(); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       cleanerRemoveUnnecessaryBuildsSpy.and.returnValue(Promise.resolve('Test')); | 
					
						
							|  |  |  |       existingBuildsDeferred.resolve(); | 
					
						
							|  |  |  |       openPrsDeferred.resolve(); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Protected methods
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   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 as any).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 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: Function, reject: Function}; | 
					
						
							|  |  |  |     let promise: Promise<number[]>; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     beforeEach(() => { | 
					
						
							|  |  |  |       spyOn(GithubPullRequests.prototype, 'fetchAll').and.callFake(() => { | 
					
						
							|  |  |  |         return new Promise((resolve, reject) => prDeferred = {resolve, reject}); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       promise = (cleaner as any).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}]); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   describe('removeDir()', () => { | 
					
						
							|  |  |  |     let shellChmodSpy: jasmine.Spy; | 
					
						
							|  |  |  |     let shellRmSpy: jasmine.Spy; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     beforeEach(() => { | 
					
						
							|  |  |  |       shellChmodSpy = spyOn(shell, 'chmod'); | 
					
						
							|  |  |  |       shellRmSpy = spyOn(shell, 'rm'); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('should remove the specified directory and its content', () => { | 
					
						
							|  |  |  |       (cleaner as any).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 as any).removeDir('/foo/bar'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       expect(shellRmSpy).toHaveBeenCalled(); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('should catch errors and log them', () => { | 
					
						
							|  |  |  |       const consoleErrorSpy = spyOn(console, 'error'); | 
					
						
							|  |  |  |       shellRmSpy.and.callFake(() => { throw 'Test'; }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       (cleaner as any).removeDir('/foo/bar'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       expect(consoleErrorSpy).toHaveBeenCalled(); | 
					
						
							|  |  |  |       expect(consoleErrorSpy.calls.argsFor(0)[0]).toContain('Unable to remove \'/foo/bar\''); | 
					
						
							|  |  |  |       expect(consoleErrorSpy.calls.argsFor(0)[1]).toBe('Test'); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   describe('removeUnnecessaryBuilds()', () => { | 
					
						
							|  |  |  |     let consoleLogSpy: jasmine.Spy; | 
					
						
							|  |  |  |     let cleanerRemoveDirSpy: jasmine.Spy; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     beforeEach(() => { | 
					
						
							|  |  |  |       consoleLogSpy = spyOn(console, 'log'); | 
					
						
							|  |  |  |       cleanerRemoveDirSpy = spyOn(cleaner as any, 'removeDir'); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('should log the number of existing builds, open PRs and builds to be removed', () => { | 
					
						
							|  |  |  |       (cleaner as any).removeUnnecessaryBuilds([1, 2, 3], [3, 4, 5, 6]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       expect(console.log).toHaveBeenCalledWith('Existing builds: 3'); | 
					
						
							|  |  |  |       expect(console.log).toHaveBeenCalledWith('Open pull requests: 4'); | 
					
						
							|  |  |  |       expect(console.log).toHaveBeenCalledWith('Removing 2 build(s): 1, 2'); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('should construct full paths to directories (by prepending \'buildsDir\')', () => { | 
					
						
							|  |  |  |       (cleaner as any).removeUnnecessaryBuilds([1, 2, 3], []); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       expect(cleanerRemoveDirSpy).toHaveBeenCalledWith('/foo/bar/1'); | 
					
						
							|  |  |  |       expect(cleanerRemoveDirSpy).toHaveBeenCalledWith('/foo/bar/2'); | 
					
						
							|  |  |  |       expect(cleanerRemoveDirSpy).toHaveBeenCalledWith('/foo/bar/3'); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('should remove the builds that do not correspond to open PRs', () => { | 
					
						
							|  |  |  |       (cleaner as any).removeUnnecessaryBuilds([1, 2, 3, 4], [2, 4]); | 
					
						
							|  |  |  |       expect(cleanerRemoveDirSpy).toHaveBeenCalledTimes(2); | 
					
						
							|  |  |  |       expect(cleanerRemoveDirSpy).toHaveBeenCalledWith('/foo/bar/1'); | 
					
						
							|  |  |  |       expect(cleanerRemoveDirSpy).toHaveBeenCalledWith('/foo/bar/3'); | 
					
						
							|  |  |  |       cleanerRemoveDirSpy.calls.reset(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       (cleaner as any).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(4); | 
					
						
							|  |  |  |       expect(cleanerRemoveDirSpy).toHaveBeenCalledWith('/foo/bar/1'); | 
					
						
							|  |  |  |       expect(cleanerRemoveDirSpy).toHaveBeenCalledWith('/foo/bar/2'); | 
					
						
							|  |  |  |       expect(cleanerRemoveDirSpy).toHaveBeenCalledWith('/foo/bar/3'); | 
					
						
							|  |  |  |       expect(cleanerRemoveDirSpy).toHaveBeenCalledWith('/foo/bar/4'); | 
					
						
							|  |  |  |       cleanerRemoveDirSpy.calls.reset(); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | }); |