mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-21 01:07:46 +00:00
Refac: Centralize storage file management (#4078)
Co-authored-by: Taylor Mullen <ntaylormullen@google.com>
This commit is contained in:
@@ -40,9 +40,11 @@ describe('ChatRecordingService', () => {
|
||||
mockConfig = {
|
||||
getSessionId: vi.fn().mockReturnValue('test-session-id'),
|
||||
getProjectRoot: vi.fn().mockReturnValue('/test/project/root'),
|
||||
getProjectTempDir: vi
|
||||
.fn()
|
||||
.mockReturnValue('/test/project/root/.gemini/tmp'),
|
||||
storage: {
|
||||
getProjectTempDir: vi
|
||||
.fn()
|
||||
.mockReturnValue('/test/project/root/.gemini/tmp'),
|
||||
},
|
||||
getModel: vi.fn().mockReturnValue('gemini-pro'),
|
||||
getDebugMode: vi.fn().mockReturnValue(false),
|
||||
} as unknown as Config;
|
||||
|
||||
@@ -136,7 +136,10 @@ export class ChatRecordingService {
|
||||
this.cachedLastConvData = null;
|
||||
} else {
|
||||
// Create new session
|
||||
const chatsDir = path.join(this.config.getProjectTempDir(), 'chats');
|
||||
const chatsDir = path.join(
|
||||
this.config.storage.getProjectTempDir(),
|
||||
'chats',
|
||||
);
|
||||
fs.mkdirSync(chatsDir, { recursive: true });
|
||||
|
||||
const timestamp = new Date()
|
||||
@@ -422,7 +425,10 @@ export class ChatRecordingService {
|
||||
*/
|
||||
deleteSession(sessionId: string): void {
|
||||
try {
|
||||
const chatsDir = path.join(this.config.getProjectTempDir(), 'chats');
|
||||
const chatsDir = path.join(
|
||||
this.config.storage.getProjectTempDir(),
|
||||
'chats',
|
||||
);
|
||||
const sessionPath = path.join(chatsDir, `${sessionId}.json`);
|
||||
fs.unlinkSync(sessionPath);
|
||||
} catch (error) {
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||
import { GitService } from './gitService.js';
|
||||
import { Storage } from '../config/storage.js';
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs/promises';
|
||||
import * as os from 'os';
|
||||
@@ -55,6 +56,7 @@ describe('GitService', () => {
|
||||
let projectRoot: string;
|
||||
let homedir: string;
|
||||
let hash: string;
|
||||
let storage: Storage;
|
||||
|
||||
beforeEach(async () => {
|
||||
testRootDir = await fs.mkdtemp(path.join(os.tmpdir(), 'git-service-test-'));
|
||||
@@ -100,6 +102,7 @@ describe('GitService', () => {
|
||||
hoistedMockCommit.mockResolvedValue({
|
||||
commit: 'initial',
|
||||
});
|
||||
storage = new Storage(projectRoot);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
@@ -109,13 +112,13 @@ describe('GitService', () => {
|
||||
|
||||
describe('constructor', () => {
|
||||
it('should successfully create an instance', () => {
|
||||
expect(() => new GitService(projectRoot)).not.toThrow();
|
||||
expect(() => new GitService(projectRoot, storage)).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('verifyGitAvailability', () => {
|
||||
it('should resolve true if git --version command succeeds', async () => {
|
||||
const service = new GitService(projectRoot);
|
||||
const service = new GitService(projectRoot, storage);
|
||||
await expect(service.verifyGitAvailability()).resolves.toBe(true);
|
||||
});
|
||||
|
||||
@@ -124,7 +127,7 @@ describe('GitService', () => {
|
||||
callback(new Error('git not found'));
|
||||
return {} as ChildProcess;
|
||||
});
|
||||
const service = new GitService(projectRoot);
|
||||
const service = new GitService(projectRoot, storage);
|
||||
await expect(service.verifyGitAvailability()).resolves.toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -135,14 +138,14 @@ describe('GitService', () => {
|
||||
callback(new Error('git not found'));
|
||||
return {} as ChildProcess;
|
||||
});
|
||||
const service = new GitService(projectRoot);
|
||||
const service = new GitService(projectRoot, storage);
|
||||
await expect(service.initialize()).rejects.toThrow(
|
||||
'Checkpointing is enabled, but Git is not installed. Please install Git or disable checkpointing to continue.',
|
||||
);
|
||||
});
|
||||
|
||||
it('should call setupShadowGitRepository if Git is available', async () => {
|
||||
const service = new GitService(projectRoot);
|
||||
const service = new GitService(projectRoot, storage);
|
||||
const setupSpy = vi
|
||||
.spyOn(service, 'setupShadowGitRepository')
|
||||
.mockResolvedValue(undefined);
|
||||
@@ -162,14 +165,14 @@ describe('GitService', () => {
|
||||
});
|
||||
|
||||
it('should create history and repository directories', async () => {
|
||||
const service = new GitService(projectRoot);
|
||||
const service = new GitService(projectRoot, storage);
|
||||
await service.setupShadowGitRepository();
|
||||
const stats = await fs.stat(repoDir);
|
||||
expect(stats.isDirectory()).toBe(true);
|
||||
});
|
||||
|
||||
it('should create a .gitconfig file with the correct content', async () => {
|
||||
const service = new GitService(projectRoot);
|
||||
const service = new GitService(projectRoot, storage);
|
||||
await service.setupShadowGitRepository();
|
||||
|
||||
const expectedConfigContent =
|
||||
@@ -180,7 +183,7 @@ describe('GitService', () => {
|
||||
|
||||
it('should initialize git repo in historyDir if not already initialized', async () => {
|
||||
hoistedMockCheckIsRepo.mockResolvedValue(false);
|
||||
const service = new GitService(projectRoot);
|
||||
const service = new GitService(projectRoot, storage);
|
||||
await service.setupShadowGitRepository();
|
||||
expect(hoistedMockSimpleGit).toHaveBeenCalledWith(repoDir);
|
||||
expect(hoistedMockInit).toHaveBeenCalled();
|
||||
@@ -188,7 +191,7 @@ describe('GitService', () => {
|
||||
|
||||
it('should not initialize git repo if already initialized', async () => {
|
||||
hoistedMockCheckIsRepo.mockResolvedValue(true);
|
||||
const service = new GitService(projectRoot);
|
||||
const service = new GitService(projectRoot, storage);
|
||||
await service.setupShadowGitRepository();
|
||||
expect(hoistedMockInit).not.toHaveBeenCalled();
|
||||
});
|
||||
@@ -198,7 +201,7 @@ describe('GitService', () => {
|
||||
const visibleGitIgnorePath = path.join(projectRoot, '.gitignore');
|
||||
await fs.writeFile(visibleGitIgnorePath, gitignoreContent);
|
||||
|
||||
const service = new GitService(projectRoot);
|
||||
const service = new GitService(projectRoot, storage);
|
||||
await service.setupShadowGitRepository();
|
||||
|
||||
const hiddenGitIgnorePath = path.join(repoDir, '.gitignore');
|
||||
@@ -207,7 +210,7 @@ describe('GitService', () => {
|
||||
});
|
||||
|
||||
it('should not create a .gitignore in shadow repo if project .gitignore does not exist', async () => {
|
||||
const service = new GitService(projectRoot);
|
||||
const service = new GitService(projectRoot, storage);
|
||||
await service.setupShadowGitRepository();
|
||||
|
||||
const hiddenGitIgnorePath = path.join(repoDir, '.gitignore');
|
||||
@@ -221,7 +224,7 @@ describe('GitService', () => {
|
||||
// Create a directory instead of a file to cause a read error
|
||||
await fs.mkdir(visibleGitIgnorePath);
|
||||
|
||||
const service = new GitService(projectRoot);
|
||||
const service = new GitService(projectRoot, storage);
|
||||
// EISDIR is the expected error code on Unix-like systems
|
||||
await expect(service.setupShadowGitRepository()).rejects.toThrow(
|
||||
/EISDIR: illegal operation on a directory, read|EBUSY: resource busy or locked, read/,
|
||||
@@ -230,7 +233,7 @@ describe('GitService', () => {
|
||||
|
||||
it('should make an initial commit if no commits exist in history repo', async () => {
|
||||
hoistedMockCheckIsRepo.mockResolvedValue(false);
|
||||
const service = new GitService(projectRoot);
|
||||
const service = new GitService(projectRoot, storage);
|
||||
await service.setupShadowGitRepository();
|
||||
expect(hoistedMockCommit).toHaveBeenCalledWith('Initial commit', {
|
||||
'--allow-empty': null,
|
||||
@@ -239,7 +242,7 @@ describe('GitService', () => {
|
||||
|
||||
it('should not make an initial commit if commits already exist', async () => {
|
||||
hoistedMockCheckIsRepo.mockResolvedValue(true);
|
||||
const service = new GitService(projectRoot);
|
||||
const service = new GitService(projectRoot, storage);
|
||||
await service.setupShadowGitRepository();
|
||||
expect(hoistedMockCommit).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -6,22 +6,22 @@
|
||||
|
||||
import * as fs from 'fs/promises';
|
||||
import * as path from 'path';
|
||||
import * as os from 'os';
|
||||
import { isNodeError } from '../utils/errors.js';
|
||||
import { exec } from 'node:child_process';
|
||||
import { simpleGit, SimpleGit, CheckRepoActions } from 'simple-git';
|
||||
import { getProjectHash, GEMINI_DIR } from '../utils/paths.js';
|
||||
import { Storage } from '../config/storage.js';
|
||||
|
||||
export class GitService {
|
||||
private projectRoot: string;
|
||||
private storage: Storage;
|
||||
|
||||
constructor(projectRoot: string) {
|
||||
constructor(projectRoot: string, storage: Storage) {
|
||||
this.projectRoot = path.resolve(projectRoot);
|
||||
this.storage = storage;
|
||||
}
|
||||
|
||||
private getHistoryDir(): string {
|
||||
const hash = getProjectHash(this.projectRoot);
|
||||
return path.join(os.homedir(), GEMINI_DIR, 'history', hash);
|
||||
return this.storage.getHistoryDir();
|
||||
}
|
||||
|
||||
async initialize(): Promise<void> {
|
||||
|
||||
Reference in New Issue
Block a user