| |
| |
| |
| |
| |
|
|
| import { vi, describe, it, expect, beforeEach } from 'vitest'; |
| import { getVersion } from '../get-release-version.js'; |
| import { execSync } from 'node:child_process'; |
| import { readFileSync } from 'node:fs'; |
|
|
| vi.mock('node:child_process'); |
| vi.mock('node:fs'); |
|
|
| describe('getVersion', () => { |
| beforeEach(() => { |
| vi.resetAllMocks(); |
| vi.setSystemTime(new Date('2025-09-17T00:00:00.000Z')); |
| |
| vi.mocked(readFileSync).mockReturnValue( |
| JSON.stringify({ version: '0.8.0' }), |
| ); |
| }); |
|
|
| |
| const mockExecSync = (command) => { |
| |
| if (command.includes('npm view') && command.includes('--tag=latest')) |
| return '0.6.1'; |
| if (command.includes('npm view') && command.includes('--tag=preview')) |
| return '0.7.0-preview.1'; |
| if (command.includes('npm view') && command.includes('--tag=nightly')) |
| return '0.8.0-nightly.20250916.abcdef'; |
|
|
| |
| if (command.includes('npm view') && command.includes('versions --json')) |
| return JSON.stringify([ |
| '0.6.0', |
| '0.6.1', |
| '0.7.0-preview.0', |
| '0.7.0-preview.1', |
| '0.8.0-nightly.20250916.abcdef', |
| ]); |
|
|
| |
| if (command.includes('deprecated')) return ''; |
|
|
| |
| if (command.includes("git tag -l 'v[0-9].[0-9].[0-9]'")) return 'v0.6.1'; |
| if (command.includes("git tag -l 'v*-preview*'")) return 'v0.7.0-preview.1'; |
| if (command.includes("git tag -l 'v*-nightly*'")) |
| return 'v0.8.0-nightly.20250916.abcdef'; |
|
|
| |
| if (command.includes('git rev-parse --short HEAD')) return 'd3bf8a3d'; |
|
|
| |
| if ( |
| command.includes('npm view') && |
| command.includes('@google/gemini-cli@') |
| ) { |
| throw new Error('NPM version not found'); |
| } |
| if (command.includes('git tag -l')) return ''; |
| if (command.includes('gh release view')) { |
| throw new Error('GH release not found'); |
| } |
|
|
| return ''; |
| }; |
|
|
| describe('Happy Path - Version Calculation', () => { |
| it('should calculate the next stable version from the latest preview', () => { |
| vi.mocked(execSync).mockImplementation(mockExecSync); |
| const result = getVersion({ type: 'stable' }); |
| expect(result.releaseVersion).toBe('0.7.0'); |
| expect(result.npmTag).toBe('latest'); |
| expect(result.previousReleaseTag).toBe('v0.6.1'); |
| }); |
|
|
| it('should calculate the next preview version from the latest nightly', () => { |
| vi.mocked(execSync).mockImplementation(mockExecSync); |
| const result = getVersion({ |
| type: 'preview', |
| 'stable-base-version': '0.7.0', |
| }); |
| expect(result.releaseVersion).toBe('0.8.0-preview.0'); |
| expect(result.npmTag).toBe('preview'); |
| expect(result.previousReleaseTag).toBe('v0.7.0-preview.1'); |
| }); |
|
|
| it('should calculate the next nightly version from package.json', () => { |
| vi.mocked(execSync).mockImplementation(mockExecSync); |
| const result = getVersion({ type: 'nightly' }); |
| |
| expect(result.releaseVersion).toBe('0.8.0-nightly.20250917.gd3bf8a3d'); |
| expect(result.npmTag).toBe('nightly'); |
| expect(result.previousReleaseTag).toBe('v0.8.0-nightly.20250916.abcdef'); |
| }); |
|
|
| it('should calculate the next patch version for a stable release', () => { |
| vi.mocked(execSync).mockImplementation(mockExecSync); |
| const result = getVersion({ type: 'patch', 'patch-from': 'stable' }); |
| expect(result.releaseVersion).toBe('0.6.2'); |
| expect(result.npmTag).toBe('latest'); |
| expect(result.previousReleaseTag).toBe('v0.6.1'); |
| }); |
|
|
| it('should calculate the next patch version for a preview release', () => { |
| vi.mocked(execSync).mockImplementation(mockExecSync); |
| const result = getVersion({ type: 'patch', 'patch-from': 'preview' }); |
| expect(result.releaseVersion).toBe('0.7.0-preview.2'); |
| expect(result.npmTag).toBe('preview'); |
| expect(result.previousReleaseTag).toBe('v0.7.0-preview.1'); |
| }); |
| }); |
|
|
| describe('Advanced Scenarios', () => { |
| it('should ignore a deprecated version and use the next highest', () => { |
| const mockWithDeprecated = (command) => { |
| |
| if (command.includes('npm view') && command.includes('versions --json')) |
| return JSON.stringify([ |
| '0.8.0-nightly.20250916.abcdef', |
| '0.9.0-nightly.20250917.deprecated', |
| ]); |
| |
| if ( |
| command.includes( |
| 'npm view @google/gemini-cli@0.9.0-nightly.20250917.deprecated deprecated', |
| ) |
| ) |
| return 'This version is deprecated'; |
| |
| if (command.includes('npm view') && command.includes('--tag=nightly')) |
| return '0.8.0-nightly.20250916.abcdef'; |
|
|
| return mockExecSync(command); |
| }; |
| vi.mocked(execSync).mockImplementation(mockWithDeprecated); |
|
|
| const result = getVersion({ |
| type: 'preview', |
| 'stable-base-version': '0.7.0', |
| }); |
| |
| expect(result.releaseVersion).toBe('0.8.0-preview.0'); |
| }); |
|
|
| it('should auto-increment patch version if the calculated one already exists', () => { |
| const mockWithConflict = (command) => { |
| |
| if (command.includes("git tag -l 'v0.7.0'")) return 'v0.7.0'; |
| |
| if (command.includes("git tag -l 'v0.7.1'")) return ''; |
|
|
| return mockExecSync(command); |
| }; |
| vi.mocked(execSync).mockImplementation(mockWithConflict); |
|
|
| const result = getVersion({ type: 'stable' }); |
| |
| expect(result.releaseVersion).toBe('0.7.1'); |
| }); |
|
|
| it('should auto-increment preview number if the calculated one already exists', () => { |
| const mockWithConflict = (command) => { |
| |
| if ( |
| command.includes( |
| 'npm view @google/gemini-cli@0.8.0-preview.0 version', |
| ) |
| ) |
| return '0.8.0-preview.0'; |
| |
| if ( |
| command.includes( |
| 'npm view @google/gemini-cli@0.8.0-preview.1 version', |
| ) |
| ) |
| throw new Error('Not found'); |
|
|
| return mockExecSync(command); |
| }; |
| vi.mocked(execSync).mockImplementation(mockWithConflict); |
|
|
| const result = getVersion({ |
| type: 'preview', |
| 'stable-base-version': '0.7.0', |
| }); |
| |
| expect(result.releaseVersion).toBe('0.8.0-preview.1'); |
| }); |
|
|
| it('should preserve a git hash with a leading zero via the g prefix', () => { |
| const mockWithLeadingZeroHash = (command) => { |
| |
| if (command.includes('git rev-parse --short HEAD')) return '017972622'; |
| return mockExecSync(command); |
| }; |
| vi.mocked(execSync).mockImplementation(mockWithLeadingZeroHash); |
|
|
| const result = getVersion({ type: 'nightly' }); |
| |
| |
| expect(result.releaseVersion).toBe('0.8.0-nightly.20250917.g017972622'); |
| }); |
| }); |
| }); |
|
|