diff options
| author | Gal Zahavi <[email protected]> | 2025-07-28 17:56:52 -0700 |
|---|---|---|
| committer | GitHub <[email protected]> | 2025-07-29 00:56:52 +0000 |
| commit | 871e0dfab811192f67cd80bc270580ad784ffdc8 (patch) | |
| tree | bcb8de6af3a2e52a7d7597e5b28465c35b87f60a /packages/cli/src/utils/handleAutoUpdate.test.ts | |
| parent | 83c4dddb7ee7ba34d7dec09d00819972d2e1ff5f (diff) | |
feat: Add auto update functionality (#4686)
Diffstat (limited to 'packages/cli/src/utils/handleAutoUpdate.test.ts')
| -rw-r--r-- | packages/cli/src/utils/handleAutoUpdate.test.ts | 153 |
1 files changed, 153 insertions, 0 deletions
diff --git a/packages/cli/src/utils/handleAutoUpdate.test.ts b/packages/cli/src/utils/handleAutoUpdate.test.ts new file mode 100644 index 00000000..adaed932 --- /dev/null +++ b/packages/cli/src/utils/handleAutoUpdate.test.ts @@ -0,0 +1,153 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; +import { ChildProcess, spawn } from 'node:child_process'; +import { handleAutoUpdate } from './handleAutoUpdate.js'; +import { getInstallationInfo, PackageManager } from './installationInfo.js'; +import { updateEventEmitter } from './updateEventEmitter.js'; +import { UpdateObject } from '../ui/utils/updateCheck.js'; +import { LoadedSettings } from '../config/settings.js'; + +// Mock dependencies +vi.mock('node:child_process', async () => { + const actual = await vi.importActual('node:child_process'); + return { + ...actual, + spawn: vi.fn(), + }; +}); + +vi.mock('./installationInfo.js', async () => { + const actual = await vi.importActual('./installationInfo.js'); + return { + ...actual, + getInstallationInfo: vi.fn(), + }; +}); + +vi.mock('./updateEventEmitter.js', async () => { + const actual = await vi.importActual('./updateEventEmitter.js'); + return { + ...actual, + updateEventEmitter: { + ...actual.updateEventEmitter, + emit: vi.fn(), + }, + }; +}); + +const mockSpawn = vi.mocked(spawn); +const mockGetInstallationInfo = vi.mocked(getInstallationInfo); +const mockUpdateEventEmitter = vi.mocked(updateEventEmitter); + +describe('handleAutoUpdate', () => { + let mockUpdateInfo: UpdateObject; + let mockSettings: LoadedSettings; + let mockChildProcess: { + stderr: { on: ReturnType<typeof vi.fn> }; + stdout: { on: ReturnType<typeof vi.fn> }; + on: ReturnType<typeof vi.fn>; + unref: ReturnType<typeof vi.fn>; + }; + + beforeEach(() => { + mockUpdateInfo = { + update: { + latest: '2.0.0', + current: '1.0.0', + type: 'major', + name: '@google/gemini-cli', + }, + message: 'An update is available!', + }; + + mockSettings = { + merged: { + disableAutoUpdate: false, + }, + } as LoadedSettings; + + mockChildProcess = { + stdout: { on: vi.fn() }, + stderr: { on: vi.fn() }, + on: vi.fn(), + unref: vi.fn(), + }; + mockSpawn.mockReturnValue(mockChildProcess as unknown as ChildProcess); + }); + + afterEach(() => { + vi.clearAllMocks(); + }); + + it('should do nothing if update info is null', () => { + handleAutoUpdate(null, mockSettings, '/root'); + expect(mockGetInstallationInfo).not.toHaveBeenCalled(); + expect(mockUpdateEventEmitter.emit).not.toHaveBeenCalled(); + expect(mockSpawn).not.toHaveBeenCalled(); + }); + + it('should emit "update-received" but not update if auto-updates are disabled', () => { + mockSettings.merged.disableAutoUpdate = true; + mockGetInstallationInfo.mockReturnValue({ + updateCommand: 'npm i -g @google/gemini-cli@latest', + updateMessage: 'Please update manually.', + isGlobal: true, + packageManager: PackageManager.NPM, + }); + + handleAutoUpdate(mockUpdateInfo, mockSettings, '/root'); + + expect(mockUpdateEventEmitter.emit).toHaveBeenCalledTimes(1); + expect(mockUpdateEventEmitter.emit).toHaveBeenCalledWith( + 'update-received', + { + message: 'An update is available!\nPlease update manually.', + }, + ); + expect(mockSpawn).not.toHaveBeenCalled(); + }); + + it('should emit "update-received" but not update if no update command is found', () => { + mockGetInstallationInfo.mockReturnValue({ + updateCommand: undefined, + updateMessage: 'Cannot determine update command.', + isGlobal: false, + packageManager: PackageManager.NPM, + }); + + handleAutoUpdate(mockUpdateInfo, mockSettings, '/root'); + + expect(mockUpdateEventEmitter.emit).toHaveBeenCalledTimes(1); + expect(mockUpdateEventEmitter.emit).toHaveBeenCalledWith( + 'update-received', + { + message: 'An update is available!\nCannot determine update command.', + }, + ); + expect(mockSpawn).not.toHaveBeenCalled(); + }); + + it('should combine update messages correctly', () => { + mockGetInstallationInfo.mockReturnValue({ + updateCommand: undefined, // No command to prevent spawn + updateMessage: 'This is an additional message.', + isGlobal: false, + packageManager: PackageManager.NPM, + }); + + handleAutoUpdate(mockUpdateInfo, mockSettings, '/root'); + + expect(mockUpdateEventEmitter.emit).toHaveBeenCalledTimes(1); + expect(mockUpdateEventEmitter.emit).toHaveBeenCalledWith( + 'update-received', + { + message: 'An update is available!\nThis is an additional message.', + }, + ); + }); +}); |
