quantum-cli-core
Version:
Quantum CLI Core - Multi-LLM Collaboration System
266 lines • 11.5 kB
JavaScript
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { Config } from './config.js';
import * as path from 'path';
import { setQuantumMdFilename } from '../tools/memoryTool.js';
const mockSetQuantumMdFilename = vi.mocked(setQuantumMdFilename);
import { DEFAULT_TELEMETRY_TARGET, DEFAULT_OTLP_ENDPOINT, } from '../telemetry/index.js';
import { loadServerHierarchicalMemory } from '../utils/memoryDiscovery.js';
// Mock dependencies that might be called during Config construction or createServerConfig
vi.mock('../tools/tool-registry', () => {
const ToolRegistryMock = vi.fn();
ToolRegistryMock.prototype.registerTool = vi.fn();
ToolRegistryMock.prototype.discoverTools = vi.fn();
ToolRegistryMock.prototype.getAllTools = vi.fn(() => []); // Mock methods if needed
ToolRegistryMock.prototype.getTool = vi.fn();
ToolRegistryMock.prototype.getFunctionDeclarations = vi.fn(() => []);
return { ToolRegistry: ToolRegistryMock };
});
vi.mock('../utils/memoryDiscovery.js', () => ({
loadServerHierarchicalMemory: vi.fn(),
}));
// Mock individual tools if their constructors are complex or have side effects
vi.mock('../tools/ls');
vi.mock('../tools/read-file');
vi.mock('../tools/grep');
vi.mock('../tools/glob');
vi.mock('../tools/edit');
vi.mock('../tools/shell');
vi.mock('../tools/write-file');
vi.mock('../tools/web-fetch');
vi.mock('../tools/read-many-files');
vi.mock('../tools/memoryTool', () => ({
MemoryTool: vi.fn(),
setQuantumMdFilename: vi.fn(),
getCurrentGeminiMdFilename: vi.fn(() => 'QUANTUM.md'), // Mock the original filename
DEFAULT_CONTEXT_FILENAME: 'QUANTUM.md',
GEMINI_CONFIG_DIR: '.quantum',
QUANTUM_CONFIG_DIR: '.quantum',
}));
vi.mock('../core/contentGenerator.js', async (importOriginal) => {
const actual = await importOriginal();
return {
...actual,
createContentGeneratorConfig: vi.fn(),
};
});
vi.mock('../core/client.js', () => ({
GeminiClient: vi.fn().mockImplementation(() => ({
// Mock any methods on GeminiClient that might be used.
})),
}));
vi.mock('../telemetry/index.js', async (importOriginal) => {
const actual = await importOriginal();
return {
...actual,
initializeTelemetry: vi.fn(),
};
});
describe('Server Config (config.ts)', () => {
const MODEL = 'gemini-pro';
const SANDBOX = {
command: 'docker',
image: 'gemini-cli-sandbox',
};
const TARGET_DIR = '/path/to/target';
const DEBUG_MODE = false;
const QUESTION = 'test question';
const FULL_CONTEXT = false;
const USER_MEMORY = 'Test User Memory';
const TELEMETRY_SETTINGS = { enabled: false };
const EMBEDDING_MODEL = 'gemini-embedding';
const SESSION_ID = 'test-session-id';
const baseParams = {
cwd: '/tmp',
embeddingModel: EMBEDDING_MODEL,
sandbox: SANDBOX,
targetDir: TARGET_DIR,
debugMode: DEBUG_MODE,
question: QUESTION,
fullContext: FULL_CONTEXT,
userMemory: USER_MEMORY,
telemetry: TELEMETRY_SETTINGS,
sessionId: SESSION_ID,
model: MODEL,
quantumMdFileCount: 0,
};
beforeEach(() => {
// Reset mocks if necessary
vi.clearAllMocks();
});
// i can't get vi mocking to import in core. only in cli. can't fix it now.
// describe('refreshAuth', () => {
// it('should refresh auth and update config', async () => {
// const config = new Config(baseParams);
// const newModel = 'gemini-ultra';
// const authType = AuthType.USE_GEMINI;
// const mockContentConfig = {
// model: newModel,
// apiKey: 'test-key',
// };
// (createContentGeneratorConfig as vi.Mock).mockResolvedValue(
// mockContentConfig,
// );
// await config.refreshAuth(authType);
// expect(createContentGeneratorConfig).toHaveBeenCalledWith(
// newModel,
// authType,
// );
// expect(config.getContentGeneratorConfig()).toEqual(mockContentConfig);
// expect(GeminiClient).toHaveBeenCalledWith(config);
// });
// });
it('Config constructor should store userMemory correctly', () => {
const config = new Config(baseParams);
expect(config.getUserMemory()).toBe(USER_MEMORY);
// Verify other getters if needed
expect(config.getTargetDir()).toBe(path.resolve(TARGET_DIR)); // Check resolved path
});
it('Config constructor should default userMemory to empty string if not provided', () => {
const paramsWithoutMemory = { ...baseParams };
delete paramsWithoutMemory.userMemory;
const config = new Config(paramsWithoutMemory);
expect(config.getUserMemory()).toBe('');
});
it('Config constructor should call setQuantumMdFilename with contextFileName if provided', () => {
const contextFileName = 'CUSTOM_AGENTS.md';
const paramsWithContextFile = {
...baseParams,
contextFileName,
};
new Config(paramsWithContextFile);
expect(mockSetQuantumMdFilename).toHaveBeenCalledWith(contextFileName);
});
it('Config constructor should not call setQuantumMdFilename if contextFileName is not provided', () => {
new Config(baseParams); // baseParams does not have contextFileName
expect(mockSetQuantumMdFilename).not.toHaveBeenCalled();
});
it('should set default file filtering settings when not provided', () => {
const config = new Config(baseParams);
expect(config.getFileFilteringRespectGitIgnore()).toBe(true);
});
it('should set custom file filtering settings when provided', () => {
const paramsWithFileFiltering = {
...baseParams,
fileFiltering: {
respectGitIgnore: false,
},
};
const config = new Config(paramsWithFileFiltering);
expect(config.getFileFilteringRespectGitIgnore()).toBe(false);
});
it('Config constructor should set telemetry to true when provided as true', () => {
const paramsWithTelemetry = {
...baseParams,
telemetry: { enabled: true },
};
const config = new Config(paramsWithTelemetry);
expect(config.getTelemetryEnabled()).toBe(true);
});
it('Config constructor should set telemetry to false when provided as false', () => {
const paramsWithTelemetry = {
...baseParams,
telemetry: { enabled: false },
};
const config = new Config(paramsWithTelemetry);
expect(config.getTelemetryEnabled()).toBe(false);
});
it('Config constructor should default telemetry to default value if not provided', () => {
const paramsWithoutTelemetry = { ...baseParams };
delete paramsWithoutTelemetry.telemetry;
const config = new Config(paramsWithoutTelemetry);
expect(config.getTelemetryEnabled()).toBe(TELEMETRY_SETTINGS.enabled);
});
it('should have a getFileService method that returns FileDiscoveryService', () => {
const config = new Config(baseParams);
const fileService = config.getFileService();
expect(fileService).toBeDefined();
});
describe('Telemetry Settings', () => {
it('should return default telemetry target if not provided', () => {
const params = {
...baseParams,
telemetry: { enabled: true },
};
const config = new Config(params);
expect(config.getTelemetryTarget()).toBe(DEFAULT_TELEMETRY_TARGET);
});
it('should return provided OTLP endpoint', () => {
const endpoint = 'http://custom.otel.collector:4317';
const params = {
...baseParams,
telemetry: { enabled: true, otlpEndpoint: endpoint },
};
const config = new Config(params);
expect(config.getTelemetryOtlpEndpoint()).toBe(endpoint);
});
it('should return default OTLP endpoint if not provided', () => {
const params = {
...baseParams,
telemetry: { enabled: true },
};
const config = new Config(params);
expect(config.getTelemetryOtlpEndpoint()).toBe(DEFAULT_OTLP_ENDPOINT);
});
it('should return provided logPrompts setting', () => {
const params = {
...baseParams,
telemetry: { enabled: true, logPrompts: false },
};
const config = new Config(params);
expect(config.getTelemetryLogPromptsEnabled()).toBe(false);
});
it('should return default logPrompts setting (true) if not provided', () => {
const params = {
...baseParams,
telemetry: { enabled: true },
};
const config = new Config(params);
expect(config.getTelemetryLogPromptsEnabled()).toBe(true);
});
it('should return default logPrompts setting (true) if telemetry object is not provided', () => {
const paramsWithoutTelemetry = { ...baseParams };
delete paramsWithoutTelemetry.telemetry;
const config = new Config(paramsWithoutTelemetry);
expect(config.getTelemetryLogPromptsEnabled()).toBe(true);
});
it('should return default telemetry target if telemetry object is not provided', () => {
const paramsWithoutTelemetry = { ...baseParams };
delete paramsWithoutTelemetry.telemetry;
const config = new Config(paramsWithoutTelemetry);
expect(config.getTelemetryTarget()).toBe(DEFAULT_TELEMETRY_TARGET);
});
it('should return default OTLP endpoint if telemetry object is not provided', () => {
const paramsWithoutTelemetry = { ...baseParams };
delete paramsWithoutTelemetry.telemetry;
const config = new Config(paramsWithoutTelemetry);
expect(config.getTelemetryOtlpEndpoint()).toBe(DEFAULT_OTLP_ENDPOINT);
});
});
describe('refreshMemory', () => {
it('should update memory and file count on successful refresh', async () => {
const config = new Config(baseParams);
const mockMemoryData = {
memoryContent: 'new memory content',
fileCount: 5,
};
loadServerHierarchicalMemory.mockResolvedValue(mockMemoryData);
const result = await config.refreshMemory();
expect(loadServerHierarchicalMemory).toHaveBeenCalledWith(config.getWorkingDir(), config.getDebugMode(), config.getFileService(), config.getExtensionContextFilePaths());
expect(config.getUserMemory()).toBe(mockMemoryData.memoryContent);
expect(config.getQuantumMdFileCount()).toBe(mockMemoryData.fileCount);
expect(result).toEqual(mockMemoryData);
});
it('should propagate errors from loadServerHierarchicalMemory', async () => {
const config = new Config(baseParams);
const testError = new Error('Failed to load memory');
loadServerHierarchicalMemory.mockRejectedValue(testError);
await expect(config.refreshMemory()).rejects.toThrow(testError);
});
});
});
//# sourceMappingURL=config.test.js.map