mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-13 23:51:16 -07:00
Introduce GEMINI_CLI_HOME for strict test isolation (#15907)
This commit is contained in:
@@ -9,9 +9,8 @@ import { HybridTokenStorage } from '../mcp/token-storage/hybrid-token-storage.js
|
||||
import { OAUTH_FILE } from '../config/storage.js';
|
||||
import type { OAuthCredentials } from '../mcp/token-storage/types.js';
|
||||
import * as path from 'node:path';
|
||||
import * as os from 'node:os';
|
||||
import { promises as fs } from 'node:fs';
|
||||
import { GEMINI_DIR } from '../utils/paths.js';
|
||||
import { GEMINI_DIR, homedir } from '../utils/paths.js';
|
||||
import { coreEvents } from '../utils/events.js';
|
||||
|
||||
const KEYCHAIN_SERVICE_NAME = 'gemini-cli-oauth';
|
||||
@@ -91,7 +90,7 @@ export class OAuthCredentialStorage {
|
||||
await this.storage.deleteCredentials(MAIN_ACCOUNT_KEY);
|
||||
|
||||
// Also try to remove the old file if it exists
|
||||
const oldFilePath = path.join(os.homedir(), GEMINI_DIR, OAUTH_FILE);
|
||||
const oldFilePath = path.join(homedir(), GEMINI_DIR, OAUTH_FILE);
|
||||
await fs.rm(oldFilePath, { force: true }).catch(() => {});
|
||||
} catch (error: unknown) {
|
||||
coreEvents.emitFeedback(
|
||||
@@ -107,7 +106,7 @@ export class OAuthCredentialStorage {
|
||||
* Migrate credentials from old file-based storage to keychain
|
||||
*/
|
||||
private static async migrateFromFileStorage(): Promise<Credentials | null> {
|
||||
const oldFilePath = path.join(os.homedir(), GEMINI_DIR, OAUTH_FILE);
|
||||
const oldFilePath = path.join(homedir(), GEMINI_DIR, OAUTH_FILE);
|
||||
|
||||
let credsJson: string;
|
||||
try {
|
||||
|
||||
@@ -26,16 +26,24 @@ import { AuthType } from '../core/contentGenerator.js';
|
||||
import type { Config } from '../config/config.js';
|
||||
import readline from 'node:readline';
|
||||
import { FORCE_ENCRYPTED_FILE_ENV_VAR } from '../mcp/token-storage/index.js';
|
||||
import { GEMINI_DIR } from '../utils/paths.js';
|
||||
import { GEMINI_DIR, homedir as pathsHomedir } from '../utils/paths.js';
|
||||
import { debugLogger } from '../utils/debugLogger.js';
|
||||
import { writeToStdout } from '../utils/stdio.js';
|
||||
import { FatalCancellationError } from '../utils/errors.js';
|
||||
import process from 'node:process';
|
||||
|
||||
vi.mock('os', async (importOriginal) => {
|
||||
const os = await importOriginal<typeof import('os')>();
|
||||
vi.mock('node:os', async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import('node:os')>();
|
||||
return {
|
||||
...os,
|
||||
...actual,
|
||||
homedir: vi.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock('../utils/paths.js', async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import('../utils/paths.js')>();
|
||||
return {
|
||||
...actual,
|
||||
homedir: vi.fn(),
|
||||
};
|
||||
});
|
||||
@@ -89,6 +97,7 @@ describe('oauth2', () => {
|
||||
path.join(os.tmpdir(), 'gemini-cli-test-home-'),
|
||||
);
|
||||
vi.mocked(os.homedir).mockReturnValue(tempHomeDir);
|
||||
vi.mocked(pathsHomedir).mockReturnValue(tempHomeDir);
|
||||
});
|
||||
afterEach(() => {
|
||||
fs.rmSync(tempHomeDir, { recursive: true, force: true });
|
||||
@@ -1129,15 +1138,10 @@ describe('oauth2', () => {
|
||||
() => mockHttpServer as unknown as http.Server,
|
||||
);
|
||||
|
||||
// Mock process.on to immediately trigger SIGINT
|
||||
// Mock process.on to capture SIGINT handler
|
||||
const processOnSpy = vi
|
||||
.spyOn(process, 'on')
|
||||
.mockImplementation((event, listener: () => void) => {
|
||||
if (event === 'SIGINT') {
|
||||
listener();
|
||||
}
|
||||
return process;
|
||||
});
|
||||
.mockImplementation(() => process);
|
||||
|
||||
const processRemoveListenerSpy = vi.spyOn(process, 'removeListener');
|
||||
|
||||
@@ -1146,6 +1150,24 @@ describe('oauth2', () => {
|
||||
mockConfig,
|
||||
);
|
||||
|
||||
// Wait for the SIGINT handler to be registered
|
||||
let sigIntHandler: (() => void) | undefined;
|
||||
await vi.waitFor(() => {
|
||||
const sigintCall = processOnSpy.mock.calls.find(
|
||||
(call) => call[0] === 'SIGINT',
|
||||
);
|
||||
sigIntHandler = sigintCall?.[1] as (() => void) | undefined;
|
||||
if (!sigIntHandler)
|
||||
throw new Error('SIGINT handler not registered yet');
|
||||
});
|
||||
|
||||
expect(sigIntHandler).toBeDefined();
|
||||
|
||||
// Trigger SIGINT
|
||||
if (sigIntHandler) {
|
||||
sigIntHandler();
|
||||
}
|
||||
|
||||
await expect(clientPromise).rejects.toThrow(FatalCancellationError);
|
||||
expect(processRemoveListenerSpy).toHaveBeenCalledWith(
|
||||
'SIGINT',
|
||||
@@ -1180,17 +1202,10 @@ describe('oauth2', () => {
|
||||
() => mockHttpServer as unknown as http.Server,
|
||||
);
|
||||
|
||||
// Spy on process.stdin.on and immediately trigger Ctrl+C
|
||||
// Spy on process.stdin.on to capture data handler
|
||||
const stdinOnSpy = vi
|
||||
.spyOn(process.stdin, 'on')
|
||||
.mockImplementation(
|
||||
(event: string, listener: (data: Buffer) => void) => {
|
||||
if (event === 'data') {
|
||||
listener(Buffer.from([0x03]));
|
||||
}
|
||||
return process.stdin;
|
||||
},
|
||||
);
|
||||
.mockImplementation(() => process.stdin);
|
||||
|
||||
const stdinRemoveListenerSpy = vi.spyOn(
|
||||
process.stdin,
|
||||
@@ -1202,6 +1217,23 @@ describe('oauth2', () => {
|
||||
mockConfig,
|
||||
);
|
||||
|
||||
// Wait for the stdin handler to be registered
|
||||
let dataHandler: ((data: Buffer) => void) | undefined;
|
||||
await vi.waitFor(() => {
|
||||
const dataCall = stdinOnSpy.mock.calls.find(
|
||||
(call: [string, ...unknown[]]) => call[0] === 'data',
|
||||
);
|
||||
dataHandler = dataCall?.[1] as ((data: Buffer) => void) | undefined;
|
||||
if (!dataHandler) throw new Error('stdin handler not registered yet');
|
||||
});
|
||||
|
||||
expect(dataHandler).toBeDefined();
|
||||
|
||||
// Trigger Ctrl+C
|
||||
if (dataHandler) {
|
||||
dataHandler(Buffer.from([0x03]));
|
||||
}
|
||||
|
||||
await expect(clientPromise).rejects.toThrow(FatalCancellationError);
|
||||
expect(stdinRemoveListenerSpy).toHaveBeenCalledWith(
|
||||
'data',
|
||||
@@ -1302,7 +1334,8 @@ describe('oauth2', () => {
|
||||
tempHomeDir = fs.mkdtempSync(
|
||||
path.join(os.tmpdir(), 'gemini-cli-test-home-'),
|
||||
);
|
||||
(os.homedir as Mock).mockReturnValue(tempHomeDir);
|
||||
vi.mocked(os.homedir).mockReturnValue(tempHomeDir);
|
||||
vi.mocked(pathsHomedir).mockReturnValue(tempHomeDir);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
||||
@@ -336,7 +336,7 @@ async function initOauthClient(
|
||||
|
||||
// Note that SIGINT might not get raised on Ctrl+C in raw mode
|
||||
// so we also need to look for Ctrl+C directly in stdin.
|
||||
stdinHandler = (data) => {
|
||||
stdinHandler = (data: Buffer) => {
|
||||
if (data.includes(0x03)) {
|
||||
reject(
|
||||
new FatalCancellationError('Authentication cancelled by user.'),
|
||||
|
||||
Reference in New Issue
Block a user