328 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			TypeScript
		
	
	
		
		
			
		
	
	
			328 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			TypeScript
		
	
	
|  | import * as core from '@actions/core'; | ||
|  | import NotImplementedException from './error/not-implemented-exception'; | ||
|  | import System from './system'; | ||
|  | import Versioning from './versioning'; | ||
|  | 
 | ||
|  | afterEach(() => { | ||
|  |   jest.restoreAllMocks(); | ||
|  | }); | ||
|  | 
 | ||
|  | describe('Versioning', () => { | ||
|  |   describe('strategies', () => { | ||
|  |     it('returns an object', () => { | ||
|  |       expect(typeof Versioning.strategies).toStrictEqual('object'); | ||
|  |     }); | ||
|  | 
 | ||
|  |     it('has items', () => { | ||
|  |       expect(Object.values(Versioning.strategies).length).toBeGreaterThan(2); | ||
|  |     }); | ||
|  | 
 | ||
|  |     it('has an opt out option', () => { | ||
|  |       expect(Versioning.strategies).toHaveProperty('None'); | ||
|  |     }); | ||
|  | 
 | ||
|  |     it('has the semantic option', () => { | ||
|  |       expect(Versioning.strategies).toHaveProperty('Semantic'); | ||
|  |     }); | ||
|  | 
 | ||
|  |     it('has a strategy for tags', () => { | ||
|  |       expect(Versioning.strategies).toHaveProperty('Tag'); | ||
|  |     }); | ||
|  | 
 | ||
|  |     it('has an option that allows custom input', () => { | ||
|  |       expect(Versioning.strategies).toHaveProperty('Custom'); | ||
|  |     }); | ||
|  |   }); | ||
|  | 
 | ||
|  |   describe('branch', () => { | ||
|  |     it('returns headRef when set', () => { | ||
|  |       const headReference = jest.spyOn(Versioning, 'headRef', 'get').mockReturnValue('feature-branch-1'); | ||
|  | 
 | ||
|  |       expect(Versioning.branch).toStrictEqual('feature-branch-1'); | ||
|  |       expect(headReference).toHaveBeenCalledTimes(1); | ||
|  |     }); | ||
|  | 
 | ||
|  |     it('returns part of Ref when set', () => { | ||
|  |       jest.spyOn(Versioning, 'headRef', 'get').mockImplementation(); | ||
|  |       const reference = jest.spyOn(Versioning, 'ref', 'get').mockReturnValue('refs/heads/feature-branch-2'); | ||
|  | 
 | ||
|  |       expect(Versioning.branch).toStrictEqual('feature-branch-2'); | ||
|  |       expect(reference).toHaveBeenCalledTimes(2); | ||
|  |     }); | ||
|  | 
 | ||
|  |     it('prefers headRef over ref when set', () => { | ||
|  |       const headReference = jest.spyOn(Versioning, 'headRef', 'get').mockReturnValue('feature-branch-1'); | ||
|  |       const reference = jest.spyOn(Versioning, 'ref', 'get').mockReturnValue('refs/heads/feature-2'); | ||
|  | 
 | ||
|  |       expect(Versioning.branch).toStrictEqual('feature-branch-1'); | ||
|  |       expect(headReference).toHaveBeenCalledTimes(1); | ||
|  |       expect(reference).toHaveBeenCalledTimes(0); | ||
|  |     }); | ||
|  | 
 | ||
|  |     it('returns undefined when headRef and ref are not set', () => { | ||
|  |       const headReference = jest.spyOn(Versioning, 'headRef', 'get').mockImplementation(); | ||
|  |       const reference = jest.spyOn(Versioning, 'ref', 'get').mockImplementation(); | ||
|  | 
 | ||
|  |       expect(Versioning.branch).not.toBeDefined(); | ||
|  | 
 | ||
|  |       expect(headReference).toHaveBeenCalledTimes(1); | ||
|  |       expect(reference).toHaveBeenCalledTimes(1); | ||
|  |     }); | ||
|  |   }); | ||
|  | 
 | ||
|  |   describe('headRef', () => { | ||
|  |     it('does not throw', () => { | ||
|  |       expect(() => Versioning.headRef).not.toThrow(); | ||
|  |     }); | ||
|  |   }); | ||
|  | 
 | ||
|  |   describe('ref', () => { | ||
|  |     it('does not throw', () => { | ||
|  |       expect(() => Versioning.ref).not.toThrow(); | ||
|  |     }); | ||
|  |   }); | ||
|  | 
 | ||
|  |   describe('isDirtyAllowed', () => { | ||
|  |     it('does not throw', () => { | ||
|  |       expect(() => Versioning.isDirtyAllowed).not.toThrow(); | ||
|  |     }); | ||
|  | 
 | ||
|  |     it('returns false by default', () => { | ||
|  |       expect(Versioning.isDirtyAllowed).toStrictEqual(false); | ||
|  |     }); | ||
|  |   }); | ||
|  | 
 | ||
|  |   describe('logging git diff', () => { | ||
|  |     it('calls git diff', async () => { | ||
|  |       // allowDirtyBuild: true
 | ||
|  |       jest.spyOn(core, 'getInput').mockReturnValue('true'); | ||
|  |       jest.spyOn(Versioning, 'isShallow').mockResolvedValue(true); | ||
|  |       jest.spyOn(Versioning, 'isDirty').mockResolvedValue(false); | ||
|  |       jest.spyOn(Versioning, 'fetch').mockImplementation(); | ||
|  |       jest.spyOn(Versioning, 'hasAnyVersionTags').mockResolvedValue(true); | ||
|  |       jest | ||
|  |         .spyOn(Versioning, 'parseSemanticVersion') | ||
|  |         .mockResolvedValue({ match: '', tag: 'mocktag', commits: 'abcdef', hash: '75822BCAF' }); | ||
|  |       const logDiffSpy = jest.spyOn(Versioning, 'logDiff'); | ||
|  |       const gitSpy = jest.spyOn(System, 'run').mockImplementation(); | ||
|  | 
 | ||
|  |       await Versioning.generateSemanticVersion(); | ||
|  | 
 | ||
|  |       expect(logDiffSpy).toHaveBeenCalledTimes(1); | ||
|  |       expect(gitSpy).toHaveBeenCalledTimes(1); | ||
|  |       // Todo - this no longer works since typescript
 | ||
|  |       // const issuedCommand = System.run.mock.calls[0][2].input.toString();
 | ||
|  |       // expect(issuedCommand.indexOf('diff')).toBeGreaterThan(-1);
 | ||
|  |     }); | ||
|  |   }); | ||
|  | 
 | ||
|  |   describe('descriptionRegex', () => { | ||
|  |     it('is a valid regex', () => { | ||
|  |       expect(Versioning.descriptionRegex1).toBeInstanceOf(RegExp); | ||
|  |     }); | ||
|  | 
 | ||
|  |     test.each(['v1.1-1-g12345678', 'v0.1-2-g12345678', 'v0.0-500-gA9B6C3D0-dirty'])( | ||
|  |       'is happy with valid %s', | ||
|  |       (description) => { | ||
|  |         expect(Versioning.descriptionRegex1.test(description)).toBeTruthy(); | ||
|  |       }, | ||
|  |     ); | ||
|  | 
 | ||
|  |     test.each(['v0', 'v0.1', 'v0.1.2', 'v0.1-2', 'v0.1-2-g'])('does not like %s', (description) => { | ||
|  |       expect(Versioning.descriptionRegex1.test(description)).toBeFalsy(); | ||
|  |       // Also never expect without the v to work for any of these cases.
 | ||
|  |       expect(Versioning.descriptionRegex1.test(description?.slice(1))).toBeFalsy(); | ||
|  |     }); | ||
|  |   }); | ||
|  | 
 | ||
|  |   describe('determineVersion', () => { | ||
|  |     test.each(['somethingRandom'])('throws for invalid strategy %s', async (strategy) => { | ||
|  |       await expect(Versioning.determineVersion(strategy, '')).rejects.toThrowErrorMatchingSnapshot(); | ||
|  |     }); | ||
|  | 
 | ||
|  |     describe('opt out strategy', () => { | ||
|  |       it("returns 'none'", async () => { | ||
|  |         await expect(Versioning.determineVersion('None', 'v1.0')).resolves.toMatchInlineSnapshot(`"none"`); | ||
|  |       }); | ||
|  |     }); | ||
|  | 
 | ||
|  |     describe('custom strategy', () => { | ||
|  |       test.each(['v0.1', '1', 'CamelCase', 'dashed-version'])( | ||
|  |         'returns the inputVersion for %s', | ||
|  |         async (inputVersion) => { | ||
|  |           await expect(Versioning.determineVersion('Custom', inputVersion)).resolves.toStrictEqual(inputVersion); | ||
|  |         }, | ||
|  |       ); | ||
|  |     }); | ||
|  | 
 | ||
|  |     describe('semantic strategy', () => { | ||
|  |       it('refers to generateSemanticVersion', async () => { | ||
|  |         const generateSemanticVersion = jest.spyOn(Versioning, 'generateSemanticVersion').mockResolvedValue('1.3.37'); | ||
|  | 
 | ||
|  |         await expect(Versioning.determineVersion('Semantic', '')).resolves.toStrictEqual('1.3.37'); | ||
|  |         expect(generateSemanticVersion).toHaveBeenCalledTimes(1); | ||
|  |       }); | ||
|  |     }); | ||
|  | 
 | ||
|  |     describe('tag strategy', () => { | ||
|  |       it('refers to generateTagVersion', async () => { | ||
|  |         const generateTagVersion = jest.spyOn(Versioning, 'generateTagVersion').mockResolvedValue('0.1'); | ||
|  | 
 | ||
|  |         await expect(Versioning.determineVersion('Tag', '')).resolves.toStrictEqual('0.1'); | ||
|  |         expect(generateTagVersion).toHaveBeenCalledTimes(1); | ||
|  |       }); | ||
|  |     }); | ||
|  | 
 | ||
|  |     describe('not implemented strategy', () => { | ||
|  |       it('throws a not implemented exception', async () => { | ||
|  |         const strategy = 'Test'; | ||
|  |         // @ts-ignore
 | ||
|  |         jest.spyOn(Versioning, 'strategies', 'get').mockReturnValue({ [strategy]: strategy }); | ||
|  |         await expect(Versioning.determineVersion(strategy, '')).rejects.toThrowError(NotImplementedException); | ||
|  |       }); | ||
|  |     }); | ||
|  |   }); | ||
|  | 
 | ||
|  |   describe('generateTagVersion', () => { | ||
|  |     it('removes the v', async () => { | ||
|  |       jest.spyOn(Versioning, 'getTag').mockResolvedValue('v1.3.37'); | ||
|  |       await expect(Versioning.generateTagVersion()).resolves.toStrictEqual('1.3.37'); | ||
|  |     }); | ||
|  |   }); | ||
|  | 
 | ||
|  |   describe('parseSemanticVersion', () => { | ||
|  |     it('returns the named parts', async () => { | ||
|  |       jest.spyOn(Versioning, 'getVersionDescription').mockResolvedValue('v0.1-2-g12345678'); | ||
|  | 
 | ||
|  |       await expect(Versioning.parseSemanticVersion()).resolves.toMatchObject({ | ||
|  |         tag: '0.1', | ||
|  |         commits: '2', | ||
|  |         hash: '12345678', | ||
|  |       }); | ||
|  |     }); | ||
|  | 
 | ||
|  |     it('throws when no match could be made', async () => { | ||
|  |       jest.spyOn(Versioning, 'getVersionDescription').mockResolvedValue('no-match-can-be-made'); | ||
|  | 
 | ||
|  |       await expect(Versioning.parseSemanticVersion()).toMatchObject({}); | ||
|  |     }); | ||
|  |   }); | ||
|  | 
 | ||
|  |   describe('getVersionDescription', () => { | ||
|  |     it('returns the commands output', async () => { | ||
|  |       const runOutput = 'someValue'; | ||
|  |       jest.spyOn(System, 'run').mockResolvedValue(runOutput); | ||
|  |       await expect(Versioning.getVersionDescription()).resolves.toStrictEqual(runOutput); | ||
|  |     }); | ||
|  |   }); | ||
|  | 
 | ||
|  |   describe('isShallow', () => { | ||
|  |     it('returns true when the repo is shallow', async () => { | ||
|  |       const runOutput = 'true\n'; | ||
|  |       jest.spyOn(System, 'run').mockResolvedValue(runOutput); | ||
|  |       await expect(Versioning.isShallow()).resolves.toStrictEqual(true); | ||
|  |     }); | ||
|  | 
 | ||
|  |     it('returns false when the repo is not shallow', async () => { | ||
|  |       const runOutput = 'false\n'; | ||
|  |       jest.spyOn(System, 'run').mockResolvedValue(runOutput); | ||
|  |       await expect(Versioning.isShallow()).resolves.toStrictEqual(false); | ||
|  |     }); | ||
|  |   }); | ||
|  | 
 | ||
|  |   describe('fetch', () => { | ||
|  |     it('awaits the command', async () => { | ||
|  |       jest.spyOn(core, 'warning').mockImplementation(() => {}); | ||
|  |       jest.spyOn(System, 'run').mockImplementation(); | ||
|  |       await expect(Versioning.fetch()).resolves.not.toThrow(); | ||
|  |     }); | ||
|  | 
 | ||
|  |     it('falls back to the second strategy when the first fails', async () => { | ||
|  |       jest.spyOn(core, 'warning').mockImplementation(() => {}); | ||
|  |       const gitFetch = jest.spyOn(System, 'run').mockImplementation(); | ||
|  | 
 | ||
|  |       await expect(Versioning.fetch()).resolves.not.toThrow(); | ||
|  |       expect(gitFetch).toHaveBeenCalledTimes(1); | ||
|  |     }); | ||
|  |   }); | ||
|  | 
 | ||
|  |   describe('generateSemanticVersion', () => { | ||
|  |     it('returns a proper version from description', async () => { | ||
|  |       jest.spyOn(System, 'run').mockImplementation(); | ||
|  |       jest.spyOn(core, 'info').mockImplementation(() => {}); | ||
|  |       jest.spyOn(Versioning, 'isDirty').mockResolvedValue(false); | ||
|  |       jest.spyOn(Versioning, 'hasAnyVersionTags').mockResolvedValue(true); | ||
|  |       jest.spyOn(Versioning, 'getTotalNumberOfCommits').mockResolvedValue(2); | ||
|  |       jest.spyOn(Versioning, 'parseSemanticVersion').mockResolvedValue({ | ||
|  |         match: '0.1-2-g1b345678', | ||
|  |         tag: '0.1', | ||
|  |         commits: '2', | ||
|  |         hash: '1b345678', | ||
|  |       }); | ||
|  | 
 | ||
|  |       await expect(Versioning.generateSemanticVersion()).resolves.toStrictEqual('0.1.2'); | ||
|  |     }); | ||
|  | 
 | ||
|  |     it('throws when dirty', async () => { | ||
|  |       jest.spyOn(System, 'run').mockImplementation(); | ||
|  |       jest.spyOn(core, 'info').mockImplementation(() => {}); | ||
|  |       jest.spyOn(Versioning, 'isDirty').mockResolvedValue(true); | ||
|  |       await expect(Versioning.generateSemanticVersion()).rejects.toThrowError(); | ||
|  |     }); | ||
|  | 
 | ||
|  |     it('falls back to commits only, when no tags are present', async () => { | ||
|  |       const commits = Math.round(Math.random() * 10); | ||
|  |       jest.spyOn(System, 'run').mockImplementation(); | ||
|  |       jest.spyOn(core, 'info').mockImplementation(() => {}); | ||
|  |       jest.spyOn(Versioning, 'isDirty').mockResolvedValue(false); | ||
|  |       jest.spyOn(Versioning, 'hasAnyVersionTags').mockResolvedValue(false); | ||
|  |       jest.spyOn(Versioning, 'getTotalNumberOfCommits').mockResolvedValue(commits); | ||
|  | 
 | ||
|  |       await expect(Versioning.generateSemanticVersion()).resolves.toStrictEqual(`0.0.${commits}`); | ||
|  |     }); | ||
|  |   }); | ||
|  | 
 | ||
|  |   describe('isDirty', () => { | ||
|  |     it('returns true when there are files listed', async () => { | ||
|  |       const runOutput = 'file.ext\nfile2.ext'; | ||
|  |       jest.spyOn(System, 'run').mockResolvedValue(runOutput); | ||
|  |       await expect(Versioning.isDirty()).resolves.toStrictEqual(true); | ||
|  |     }); | ||
|  | 
 | ||
|  |     it('returns false when there is no output', async () => { | ||
|  |       const runOutput = ''; | ||
|  |       jest.spyOn(System, 'run').mockResolvedValue(runOutput); | ||
|  |       await expect(Versioning.isDirty()).resolves.toStrictEqual(false); | ||
|  |     }); | ||
|  |   }); | ||
|  | 
 | ||
|  |   describe('getTag', () => { | ||
|  |     it('returns the commands output', async () => { | ||
|  |       const runOutput = 'v1.0'; | ||
|  |       jest.spyOn(System, 'run').mockResolvedValue(runOutput); | ||
|  |       await expect(Versioning.getTag()).resolves.toStrictEqual(runOutput); | ||
|  |     }); | ||
|  |   }); | ||
|  | 
 | ||
|  |   describe('hasAnyVersionTags', () => { | ||
|  |     it('returns false when the command returns 0', async () => { | ||
|  |       const runOutput = '0'; | ||
|  |       jest.spyOn(System, 'run').mockResolvedValue(runOutput); | ||
|  |       await expect(Versioning.hasAnyVersionTags()).resolves.toStrictEqual(false); | ||
|  |     }); | ||
|  | 
 | ||
|  |     it('returns true when the command returns >= 0', async () => { | ||
|  |       const runOutput = '9'; | ||
|  |       jest.spyOn(System, 'run').mockResolvedValue(runOutput); | ||
|  |       await expect(Versioning.hasAnyVersionTags()).resolves.toStrictEqual(true); | ||
|  |     }); | ||
|  |   }); | ||
|  | 
 | ||
|  |   describe('getTotalNumberOfCommits', () => { | ||
|  |     it('returns a number from the command', async () => { | ||
|  |       jest.spyOn(System, 'run').mockResolvedValue('9'); | ||
|  |       await expect(Versioning.getTotalNumberOfCommits()).resolves.toStrictEqual(9); | ||
|  |     }); | ||
|  |   }); | ||
|  | }); |