mirror of
https://github.com/di-sukharev/opencommit.git
synced 2026-01-13 15:47:58 -05:00
* fix(commit.ts): improve user confirmation handling by exiting on cancel actions to prevent unintended behavior refactor(commit.ts): streamline conditional checks for user confirmations to enhance code readability and maintainability * refactor(commit.ts): rename spinner variables for clarity and consistency in commit message generation process fix(commit.ts): ensure proper stopping of spinners in case of errors during commit message generation and committing process * refactor(config.ts): extract default configuration to a constant for better maintainability and readability refactor(config.ts): improve initGlobalConfig function to accept a configPath parameter for flexibility feat(config.ts): enhance getConfig function to support separate paths for global and environment configurations test(config.test.ts): update tests to reflect changes in config handling and ensure proper functionality style(utils.ts): clean up code formatting for consistency and readability style(tsconfig.json): adjust formatting in tsconfig.json for better clarity and maintainability * fix(utils.ts): add existsSync check before removing temp directory to prevent errors if directory does not exist (#401) --------- Co-authored-by: Takanori Matsumoto <matscube@gmail.com>
296 lines
9.5 KiB
TypeScript
296 lines
9.5 KiB
TypeScript
import { existsSync, readFileSync, rmSync } from 'fs';
|
|
import {
|
|
DEFAULT_CONFIG,
|
|
getConfig,
|
|
setConfig
|
|
} from '../../src/commands/config';
|
|
import { prepareFile } from './utils';
|
|
import { dirname } from 'path';
|
|
|
|
describe('config', () => {
|
|
const originalEnv = { ...process.env };
|
|
let globalConfigFile: { filePath: string; cleanup: () => Promise<void> };
|
|
let envConfigFile: { filePath: string; cleanup: () => Promise<void> };
|
|
|
|
function resetEnv(env: NodeJS.ProcessEnv) {
|
|
Object.keys(process.env).forEach((key) => {
|
|
if (!(key in env)) {
|
|
delete process.env[key];
|
|
} else {
|
|
process.env[key] = env[key];
|
|
}
|
|
});
|
|
}
|
|
|
|
beforeEach(async () => {
|
|
resetEnv(originalEnv);
|
|
if (globalConfigFile) await globalConfigFile.cleanup();
|
|
if (envConfigFile) await envConfigFile.cleanup();
|
|
});
|
|
|
|
afterEach(async () => {
|
|
if (globalConfigFile) await globalConfigFile.cleanup();
|
|
if (envConfigFile) await envConfigFile.cleanup();
|
|
});
|
|
|
|
afterAll(() => {
|
|
resetEnv(originalEnv);
|
|
});
|
|
|
|
const generateConfig = async (
|
|
fileName: string,
|
|
content: Record<string, string>
|
|
) => {
|
|
const fileContent = Object.entries(content)
|
|
.map(([key, value]) => `${key}="${value}"`)
|
|
.join('\n');
|
|
return await prepareFile(fileName, fileContent);
|
|
};
|
|
|
|
describe('getConfig', () => {
|
|
it('should prioritize local .env over global .opencommit config', async () => {
|
|
globalConfigFile = await generateConfig('.opencommit', {
|
|
OCO_OPENAI_API_KEY: 'global-key',
|
|
OCO_MODEL: 'gpt-3.5-turbo',
|
|
OCO_LANGUAGE: 'en'
|
|
});
|
|
|
|
envConfigFile = await generateConfig('.env', {
|
|
OCO_OPENAI_API_KEY: 'local-key',
|
|
OCO_ANTHROPIC_API_KEY: 'local-anthropic-key',
|
|
OCO_LANGUAGE: 'fr'
|
|
});
|
|
|
|
const config = getConfig({
|
|
globalPath: globalConfigFile.filePath,
|
|
envPath: envConfigFile.filePath
|
|
});
|
|
|
|
expect(config).not.toEqual(null);
|
|
expect(config.OCO_OPENAI_API_KEY).toEqual('local-key');
|
|
expect(config.OCO_MODEL).toEqual('gpt-3.5-turbo');
|
|
expect(config.OCO_LANGUAGE).toEqual('fr');
|
|
expect(config.OCO_ANTHROPIC_API_KEY).toEqual('local-anthropic-key');
|
|
});
|
|
|
|
it('should fallback to global config when local config is not set', async () => {
|
|
globalConfigFile = await generateConfig('.opencommit', {
|
|
OCO_OPENAI_API_KEY: 'global-key',
|
|
OCO_MODEL: 'gpt-4',
|
|
OCO_LANGUAGE: 'de',
|
|
OCO_DESCRIPTION: 'true'
|
|
});
|
|
|
|
envConfigFile = await generateConfig('.env', {
|
|
OCO_ANTHROPIC_API_KEY: 'local-anthropic-key'
|
|
});
|
|
|
|
const config = getConfig({
|
|
globalPath: globalConfigFile.filePath,
|
|
envPath: envConfigFile.filePath
|
|
});
|
|
|
|
expect(config).not.toEqual(null);
|
|
expect(config.OCO_OPENAI_API_KEY).toEqual('global-key');
|
|
expect(config.OCO_ANTHROPIC_API_KEY).toEqual('local-anthropic-key');
|
|
expect(config.OCO_MODEL).toEqual('gpt-4');
|
|
expect(config.OCO_LANGUAGE).toEqual('de');
|
|
expect(config.OCO_DESCRIPTION).toEqual(true);
|
|
});
|
|
|
|
it('should handle boolean and numeric values correctly', async () => {
|
|
globalConfigFile = await generateConfig('.opencommit', {
|
|
OCO_TOKENS_MAX_INPUT: '4096',
|
|
OCO_TOKENS_MAX_OUTPUT: '500',
|
|
OCO_GITPUSH: 'true'
|
|
});
|
|
|
|
envConfigFile = await generateConfig('.env', {
|
|
OCO_TOKENS_MAX_INPUT: '8192',
|
|
OCO_ONE_LINE_COMMIT: 'false'
|
|
});
|
|
|
|
const config = getConfig({
|
|
globalPath: globalConfigFile.filePath,
|
|
envPath: envConfigFile.filePath
|
|
});
|
|
|
|
expect(config).not.toEqual(null);
|
|
expect(config.OCO_TOKENS_MAX_INPUT).toEqual(8192);
|
|
expect(config.OCO_TOKENS_MAX_OUTPUT).toEqual(500);
|
|
expect(config.OCO_GITPUSH).toEqual(true);
|
|
expect(config.OCO_ONE_LINE_COMMIT).toEqual(false);
|
|
});
|
|
|
|
it('should handle empty local config correctly', async () => {
|
|
globalConfigFile = await generateConfig('.opencommit', {
|
|
OCO_OPENAI_API_KEY: 'global-key',
|
|
OCO_MODEL: 'gpt-4',
|
|
OCO_LANGUAGE: 'es'
|
|
});
|
|
|
|
envConfigFile = await generateConfig('.env', {});
|
|
|
|
const config = getConfig({
|
|
globalPath: globalConfigFile.filePath,
|
|
envPath: envConfigFile.filePath
|
|
});
|
|
|
|
expect(config).not.toEqual(null);
|
|
expect(config.OCO_OPENAI_API_KEY).toEqual('global-key');
|
|
expect(config.OCO_MODEL).toEqual('gpt-4');
|
|
expect(config.OCO_LANGUAGE).toEqual('es');
|
|
});
|
|
|
|
it('should override global config with null values in local .env', async () => {
|
|
globalConfigFile = await generateConfig('.opencommit', {
|
|
OCO_OPENAI_API_KEY: 'global-key',
|
|
OCO_MODEL: 'gpt-4',
|
|
OCO_LANGUAGE: 'es'
|
|
});
|
|
|
|
envConfigFile = await generateConfig('.env', {
|
|
OCO_OPENAI_API_KEY: 'null'
|
|
});
|
|
|
|
const config = getConfig({
|
|
globalPath: globalConfigFile.filePath,
|
|
envPath: envConfigFile.filePath
|
|
});
|
|
|
|
expect(config).not.toEqual(null);
|
|
expect(config.OCO_OPENAI_API_KEY).toEqual(null);
|
|
});
|
|
|
|
it('should handle empty global config', async () => {
|
|
globalConfigFile = await generateConfig('.opencommit', {});
|
|
envConfigFile = await generateConfig('.env', {});
|
|
|
|
const config = getConfig({
|
|
globalPath: globalConfigFile.filePath,
|
|
envPath: envConfigFile.filePath
|
|
});
|
|
|
|
expect(config).not.toEqual(null);
|
|
expect(config.OCO_OPENAI_API_KEY).toEqual(undefined);
|
|
});
|
|
});
|
|
|
|
describe('setConfig', () => {
|
|
beforeEach(async () => {
|
|
// we create and delete the file to have the parent directory, but not the file, to test the creation of the file
|
|
globalConfigFile = await generateConfig('.opencommit', {});
|
|
rmSync(globalConfigFile.filePath);
|
|
});
|
|
|
|
it('should create .opencommit file with DEFAULT CONFIG if it does not exist on first setConfig run', async () => {
|
|
const isGlobalConfigFileExist = existsSync(globalConfigFile.filePath);
|
|
expect(isGlobalConfigFileExist).toBe(false);
|
|
|
|
await setConfig(
|
|
[['OCO_OPENAI_API_KEY', 'persisted-key_1']],
|
|
globalConfigFile.filePath
|
|
);
|
|
|
|
const fileContent = readFileSync(globalConfigFile.filePath, 'utf8');
|
|
expect(fileContent).toContain('OCO_OPENAI_API_KEY=persisted-key_1');
|
|
Object.entries(DEFAULT_CONFIG).forEach(([key, value]) => {
|
|
expect(fileContent).toContain(`${key}=${value}`);
|
|
});
|
|
});
|
|
|
|
it('should set new config values', async () => {
|
|
globalConfigFile = await generateConfig('.opencommit', {});
|
|
await setConfig(
|
|
[
|
|
['OCO_OPENAI_API_KEY', 'new-key'],
|
|
['OCO_MODEL', 'gpt-4']
|
|
],
|
|
globalConfigFile.filePath
|
|
);
|
|
|
|
const config = getConfig({ globalPath: globalConfigFile.filePath });
|
|
expect(config.OCO_OPENAI_API_KEY).toEqual('new-key');
|
|
expect(config.OCO_MODEL).toEqual('gpt-4');
|
|
});
|
|
|
|
it('should update existing config values', async () => {
|
|
globalConfigFile = await generateConfig('.opencommit', {
|
|
OCO_OPENAI_API_KEY: 'initial-key'
|
|
});
|
|
await setConfig(
|
|
[['OCO_OPENAI_API_KEY', 'updated-key']],
|
|
globalConfigFile.filePath
|
|
);
|
|
|
|
const config = getConfig({ globalPath: globalConfigFile.filePath });
|
|
expect(config.OCO_OPENAI_API_KEY).toEqual('updated-key');
|
|
});
|
|
|
|
it('should handle boolean and numeric values correctly', async () => {
|
|
globalConfigFile = await generateConfig('.opencommit', {});
|
|
await setConfig(
|
|
[
|
|
['OCO_TOKENS_MAX_INPUT', '8192'],
|
|
['OCO_DESCRIPTION', 'true'],
|
|
['OCO_ONE_LINE_COMMIT', 'false']
|
|
],
|
|
globalConfigFile.filePath
|
|
);
|
|
|
|
const config = getConfig({ globalPath: globalConfigFile.filePath });
|
|
expect(config.OCO_TOKENS_MAX_INPUT).toEqual(8192);
|
|
expect(config.OCO_DESCRIPTION).toEqual(true);
|
|
expect(config.OCO_ONE_LINE_COMMIT).toEqual(false);
|
|
});
|
|
|
|
it('should throw an error for unsupported config keys', async () => {
|
|
globalConfigFile = await generateConfig('.opencommit', {});
|
|
|
|
try {
|
|
await setConfig(
|
|
[['UNSUPPORTED_KEY', 'value']],
|
|
globalConfigFile.filePath
|
|
);
|
|
throw new Error('NEVER_REACHED');
|
|
} catch (error) {
|
|
expect(error.message).toContain(
|
|
'Unsupported config key: UNSUPPORTED_KEY'
|
|
);
|
|
expect(error.message).not.toContain('NEVER_REACHED');
|
|
}
|
|
});
|
|
|
|
it('should persist changes to the config file', async () => {
|
|
const isGlobalConfigFileExist = existsSync(globalConfigFile.filePath);
|
|
expect(isGlobalConfigFileExist).toBe(false);
|
|
|
|
await setConfig(
|
|
[['OCO_OPENAI_API_KEY', 'persisted-key']],
|
|
globalConfigFile.filePath
|
|
);
|
|
|
|
const fileContent = readFileSync(globalConfigFile.filePath, 'utf8');
|
|
expect(fileContent).toContain('OCO_OPENAI_API_KEY=persisted-key');
|
|
});
|
|
|
|
it('should set multiple configs in a row and keep the changes', async () => {
|
|
const isGlobalConfigFileExist = existsSync(globalConfigFile.filePath);
|
|
expect(isGlobalConfigFileExist).toBe(false);
|
|
|
|
await setConfig(
|
|
[['OCO_OPENAI_API_KEY', 'persisted-key']],
|
|
globalConfigFile.filePath
|
|
);
|
|
|
|
const fileContent1 = readFileSync(globalConfigFile.filePath, 'utf8');
|
|
expect(fileContent1).toContain('OCO_OPENAI_API_KEY=persisted-key');
|
|
|
|
await setConfig([['OCO_MODEL', 'gpt-4']], globalConfigFile.filePath);
|
|
|
|
const fileContent2 = readFileSync(globalConfigFile.filePath, 'utf8');
|
|
expect(fileContent2).toContain('OCO_MODEL=gpt-4');
|
|
});
|
|
});
|
|
});
|