Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions integration-tests/resume_repro.responses
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"Session started."}],"role":"model"},"finishReason":"STOP","index":0}]}]}
42 changes: 42 additions & 0 deletions integration-tests/resume_repro.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/

import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { TestRig } from './test-helper.js';
import * as path from 'node:path';
import { fileURLToPath } from 'node:url';

const __dirname = path.dirname(fileURLToPath(import.meta.url));

describe('resume-repro', () => {
let rig: TestRig;

beforeEach(() => {
rig = new TestRig();
});

afterEach(async () => await rig.cleanup());

it('should be able to resume a session without "Storage must be initialized before use"', async () => {
const responsesPath = path.join(__dirname, 'resume_repro.responses');
await rig.setup('should be able to resume a session', {
fakeResponsesPath: responsesPath,
});

// 1. First run to create a session
await rig.run({
args: 'hello',
});

// 2. Second run with --resume latest
// This should NOT fail with "Storage must be initialized before use"
const result = await rig.run({
args: ['--resume', 'latest', 'continue'],
});

expect(result).toContain('Session started');
});
});
6 changes: 6 additions & 0 deletions packages/cli/src/gemini.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,12 @@ export async function main() {
projectHooks: settings.workspace.settings.hooks,
});
loadConfigHandle?.end();

// Initialize storage immediately after loading config to ensure that
// storage-related operations (like listing or resuming sessions) have
// access to the project identifier.
await config.storage.initialize();

adminControlsListner.setConfig(config);

if (config.isInteractive() && config.storage && config.getDebugMode()) {
Expand Down
2 changes: 2 additions & 0 deletions packages/cli/src/gemini_cleanup.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ vi.mock('./config/config.js', () => ({
getSandbox: vi.fn(() => false),
getQuestion: vi.fn(() => ''),
isInteractive: () => false,
storage: { initialize: vi.fn().mockResolvedValue(undefined) },
} as unknown as Config),
parseArguments: vi.fn().mockResolvedValue({}),
isDebugMode: vi.fn(() => false),
Expand Down Expand Up @@ -195,6 +196,7 @@ describe('gemini.tsx main function cleanup', () => {
getEnableHooks: vi.fn(() => false),
getHookSystem: () => undefined,
initialize: vi.fn(),
storage: { initialize: vi.fn().mockResolvedValue(undefined) },
getContentGeneratorConfig: vi.fn(),
getMcpServers: () => ({}),
getMcpClientManager: vi.fn(),
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/test-utils/mockConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export const createMockConfig = (overrides: Partial<Config> = {}): Config =>
setTerminalBackground: vi.fn(),
storage: {
getProjectTempDir: vi.fn().mockReturnValue('/tmp/gemini-test'),
initialize: vi.fn().mockResolvedValue(undefined),
},
getDebugMode: vi.fn(() => false),
getProjectRoot: vi.fn(() => '/'),
Expand Down
Loading