Rebuild.
Testing / prettier (push) Failing after 1m59s
Testing / unit-test (20.x) (push) Failing after 3m31s
Testing / e2e-test (20.x) (push) Failing after 4m39s

This commit is contained in:
2026-01-01 14:23:49 -05:00
parent ebbaff0628
commit d3e130a8e8
15 changed files with 1603 additions and 930 deletions
+134
View File
@@ -0,0 +1,134 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const path_1 = __importDefault(require("path"));
require("cli-testing-library/extend-expect");
const child_process_1 = require("child_process");
const utils_1 = require("./utils");
const util_1 = require("util");
const cli_testing_library_1 = require("cli-testing-library");
const path_2 = require("path");
const fs_1 = require("fs");
const fsExec = (0, util_1.promisify)(child_process_1.exec);
const fsRemove = (0, util_1.promisify)(fs_1.rm);
/**
* git remote -v
*
* [no remotes]
*/
const prepareNoRemoteGitRepository = async () => {
const tempDir = await (0, utils_1.prepareTempDir)();
await fsExec('git init test', { cwd: tempDir });
const gitDir = path_1.default.resolve(tempDir, 'test');
const cleanup = async () => {
return fsRemove(tempDir, { recursive: true });
};
return {
gitDir,
cleanup
};
};
/**
* git remote -v
*
* origin /tmp/remote.git (fetch)
* origin /tmp/remote.git (push)
*/
const prepareOneRemoteGitRepository = async () => {
const tempDir = await (0, utils_1.prepareTempDir)();
await fsExec('git init --bare remote.git', { cwd: tempDir });
await fsExec('git clone remote.git test', { cwd: tempDir });
const gitDir = path_1.default.resolve(tempDir, 'test');
const cleanup = async () => {
return fsRemove(tempDir, { recursive: true });
};
return {
gitDir,
cleanup
};
};
/**
* git remote -v
*
* origin /tmp/remote.git (fetch)
* origin /tmp/remote.git (push)
* other ../remote2.git (fetch)
* other ../remote2.git (push)
*/
const prepareTwoRemotesGitRepository = async () => {
const tempDir = await (0, utils_1.prepareTempDir)();
await fsExec('git init --bare remote.git', { cwd: tempDir });
await fsExec('git init --bare other.git', { cwd: tempDir });
await fsExec('git clone remote.git test', { cwd: tempDir });
const gitDir = path_1.default.resolve(tempDir, 'test');
await fsExec('git remote add other ../other.git', { cwd: gitDir });
const cleanup = async () => {
return fsRemove(tempDir, { recursive: true });
};
return {
gitDir,
cleanup
};
};
describe('cli flow to push git branch', () => {
it('do nothing when OCO_GITPUSH is set to false', async () => {
const { gitDir, cleanup } = await prepareNoRemoteGitRepository();
await (0, cli_testing_library_1.render)('echo', [`'console.log("Hello World");' > index.ts`], {
cwd: gitDir
});
await (0, cli_testing_library_1.render)('git', ['add index.ts'], { cwd: gitDir });
const { queryByText, findByText, userEvent } = await (0, cli_testing_library_1.render)(`OCO_AI_PROVIDER='test' OCO_GITPUSH='false' node`, [(0, path_2.resolve)('./out/cli.cjs')], { cwd: gitDir });
expect(await findByText('Confirm the commit message?')).toBeInTheConsole();
userEvent.keyboard('[Enter]');
expect(await queryByText('Choose a remote to push to')).not.toBeInTheConsole();
expect(await queryByText('Do you want to run `git push`?')).not.toBeInTheConsole();
expect(await queryByText('Successfully pushed all commits to origin')).not.toBeInTheConsole();
expect(await queryByText('Command failed with exit code 1')).not.toBeInTheConsole();
await cleanup();
});
it('push and cause error when there is no remote', async () => {
const { gitDir, cleanup } = await prepareNoRemoteGitRepository();
await (0, cli_testing_library_1.render)('echo', [`'console.log("Hello World");' > index.ts`], {
cwd: gitDir
});
await (0, cli_testing_library_1.render)('git', ['add index.ts'], { cwd: gitDir });
const { queryByText, findByText, userEvent } = await (0, cli_testing_library_1.render)(`OCO_AI_PROVIDER='test' OCO_GITPUSH='true' node`, [(0, path_2.resolve)('./out/cli.cjs')], { cwd: gitDir });
expect(await findByText('Confirm the commit message?')).toBeInTheConsole();
userEvent.keyboard('[Enter]');
expect(await queryByText('Choose a remote to push to')).not.toBeInTheConsole();
expect(await queryByText('Do you want to run `git push`?')).not.toBeInTheConsole();
expect(await queryByText('Successfully pushed all commits to origin')).not.toBeInTheConsole();
expect(await findByText('Command failed with exit code 1')).toBeInTheConsole();
await cleanup();
});
it('push when one remote is set', async () => {
const { gitDir, cleanup } = await prepareOneRemoteGitRepository();
await (0, cli_testing_library_1.render)('echo', [`'console.log("Hello World");' > index.ts`], {
cwd: gitDir
});
await (0, cli_testing_library_1.render)('git', ['add index.ts'], { cwd: gitDir });
const { findByText, userEvent } = await (0, cli_testing_library_1.render)(`OCO_AI_PROVIDER='test' OCO_GITPUSH='true' node`, [(0, path_2.resolve)('./out/cli.cjs')], { cwd: gitDir });
expect(await findByText('Confirm the commit message?')).toBeInTheConsole();
userEvent.keyboard('[Enter]');
expect(await findByText('Do you want to run `git push`?')).toBeInTheConsole();
userEvent.keyboard('[Enter]');
expect(await findByText('Successfully pushed all commits to origin')).toBeInTheConsole();
await cleanup();
});
it('push when two remotes are set', async () => {
const { gitDir, cleanup } = await prepareTwoRemotesGitRepository();
await (0, cli_testing_library_1.render)('echo', [`'console.log("Hello World");' > index.ts`], {
cwd: gitDir
});
await (0, cli_testing_library_1.render)('git', ['add index.ts'], { cwd: gitDir });
const { findByText, userEvent } = await (0, cli_testing_library_1.render)(`OCO_AI_PROVIDER='test' OCO_GITPUSH='true' node`, [(0, path_2.resolve)('./out/cli.cjs')], { cwd: gitDir });
expect(await findByText('Confirm the commit message?')).toBeInTheConsole();
userEvent.keyboard('[Enter]');
expect(await findByText('Choose a remote to push to')).toBeInTheConsole();
userEvent.keyboard('[Enter]');
expect(await findByText('Successfully pushed all commits to origin')).toBeInTheConsole();
await cleanup();
});
});
+12
View File
@@ -0,0 +1,12 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const path_1 = require("path");
const cli_testing_library_1 = require("cli-testing-library");
require("cli-testing-library/extend-expect");
const utils_1 = require("./utils");
it('cli flow when there are no changes', async () => {
const { gitDir, cleanup } = await (0, utils_1.prepareEnvironment)();
const { findByText } = await (0, cli_testing_library_1.render)(`OCO_AI_PROVIDER='test' node`, [(0, path_1.resolve)('./out/cli.cjs')], { cwd: gitDir });
expect(await findByText('No changes detected')).toBeInTheConsole();
await cleanup();
});
+40
View File
@@ -0,0 +1,40 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const path_1 = require("path");
const cli_testing_library_1 = require("cli-testing-library");
require("cli-testing-library/extend-expect");
const utils_1 = require("./utils");
it('cli flow to generate commit message for 1 new file (staged)', async () => {
const { gitDir, cleanup } = await (0, utils_1.prepareEnvironment)();
await (0, cli_testing_library_1.render)('echo', [`'console.log("Hello World");' > index.ts`], { cwd: gitDir });
await (0, cli_testing_library_1.render)('git', ['add index.ts'], { cwd: gitDir });
const { queryByText, findByText, userEvent } = await (0, cli_testing_library_1.render)(`OCO_AI_PROVIDER='test' OCO_GITPUSH='true' node`, [(0, path_1.resolve)('./out/cli.cjs')], { cwd: gitDir });
expect(await queryByText('No files are staged')).not.toBeInTheConsole();
expect(await queryByText('Do you want to stage all files and generate commit message?')).not.toBeInTheConsole();
expect(await findByText('Generating the commit message')).toBeInTheConsole();
expect(await findByText('Confirm the commit message?')).toBeInTheConsole();
userEvent.keyboard('[Enter]');
expect(await findByText('Do you want to run `git push`?')).toBeInTheConsole();
userEvent.keyboard('[Enter]');
expect(await findByText('Successfully pushed all commits to origin')).toBeInTheConsole();
await cleanup();
});
it('cli flow to generate commit message for 1 changed file (not staged)', async () => {
const { gitDir, cleanup } = await (0, utils_1.prepareEnvironment)();
await (0, cli_testing_library_1.render)('echo', [`'console.log("Hello World");' > index.ts`], { cwd: gitDir });
await (0, cli_testing_library_1.render)('git', ['add index.ts'], { cwd: gitDir });
await (0, cli_testing_library_1.render)('git', [`commit -m 'add new file'`], { cwd: gitDir });
await (0, cli_testing_library_1.render)('echo', [`'console.log("Good night World");' >> index.ts`], { cwd: gitDir });
const { findByText, userEvent } = await (0, cli_testing_library_1.render)(`OCO_AI_PROVIDER='test' OCO_GITPUSH='true' node`, [(0, path_1.resolve)('./out/cli.cjs')], { cwd: gitDir });
expect(await findByText('No files are staged')).toBeInTheConsole();
expect(await findByText('Do you want to stage all files and generate commit message?')).toBeInTheConsole();
userEvent.keyboard('[Enter]');
expect(await findByText('Generating the commit message')).toBeInTheConsole();
expect(await findByText('Confirm the commit message?')).toBeInTheConsole();
userEvent.keyboard('[Enter]');
expect(await findByText('Successfully committed')).toBeInTheConsole();
expect(await findByText('Do you want to run `git push`?')).toBeInTheConsole();
userEvent.keyboard('[Enter]');
expect(await findByText('Successfully pushed all commits to origin')).toBeInTheConsole();
await cleanup();
});
+142
View File
@@ -0,0 +1,142 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const path_1 = require("path");
const cli_testing_library_1 = require("cli-testing-library");
require("cli-testing-library/extend-expect");
const utils_1 = require("../utils");
const path_2 = __importDefault(require("path"));
function getAbsolutePath(relativePath) {
// Use process.cwd() which should be the project root during test execution
return path_2.default.resolve(process.cwd(), 'test/e2e/prompt-module', relativePath);
}
async function setupCommitlint(dir, ver) {
let packagePath, packageJsonPath, configPath;
switch (ver) {
case 9:
packagePath = getAbsolutePath('./data/commitlint_9/node_modules');
packageJsonPath = getAbsolutePath('./data/commitlint_9/package.json');
configPath = getAbsolutePath('./data/commitlint_9/commitlint.config.js');
break;
case 18:
packagePath = getAbsolutePath('./data/commitlint_18/node_modules');
packageJsonPath = getAbsolutePath('./data/commitlint_18/package.json');
configPath = getAbsolutePath('./data/commitlint_18/commitlint.config.js');
break;
case 19:
packagePath = getAbsolutePath('./data/commitlint_19/node_modules');
packageJsonPath = getAbsolutePath('./data/commitlint_19/package.json');
configPath = getAbsolutePath('./data/commitlint_19/commitlint.config.js');
break;
}
await (0, cli_testing_library_1.render)('cp', ['-r', packagePath, '.'], { cwd: dir });
await (0, cli_testing_library_1.render)('cp', [packageJsonPath, '.'], { cwd: dir });
await (0, cli_testing_library_1.render)('cp', [configPath, '.'], { cwd: dir });
await (0, utils_1.wait)(3000); // Avoid flakiness by waiting
}
describe('cli flow to run "oco commitlint force"', () => {
it('on commitlint@9 using CJS', async () => {
const { gitDir, cleanup } = await (0, utils_1.prepareEnvironment)();
await setupCommitlint(gitDir, 9);
const npmList = await (0, cli_testing_library_1.render)('npm', ['list', '@commitlint/load'], {
cwd: gitDir
});
expect(await npmList.findByText('@commitlint/load@9')).toBeInTheConsole();
const { findByText } = await (0, cli_testing_library_1.render)(`
OCO_TEST_MOCK_TYPE='prompt-module-commitlint-config' \
OCO_PROMPT_MODULE='@commitlint' \
OCO_AI_PROVIDER='test' OCO_GITPUSH='true' \
node ${(0, path_1.resolve)('./out/cli.cjs')} commitlint force \
`, [], { cwd: gitDir });
expect(await findByText('opencommit — configure @commitlint')).toBeInTheConsole();
expect(await findByText('Read @commitlint configuration')).toBeInTheConsole();
expect(await findByText('Generating consistency with given @commitlint rules')).toBeInTheConsole();
expect(await findByText('Done - please review contents of')).toBeInTheConsole();
await cleanup();
});
it('on commitlint@18 using CJS', async () => {
const { gitDir, cleanup } = await (0, utils_1.prepareEnvironment)();
await setupCommitlint(gitDir, 18);
const npmList = await (0, cli_testing_library_1.render)('npm', ['list', '@commitlint/load'], {
cwd: gitDir
});
expect(await npmList.findByText('@commitlint/load@18')).toBeInTheConsole();
const { findByText } = await (0, cli_testing_library_1.render)(`
OCO_TEST_MOCK_TYPE='prompt-module-commitlint-config' \
OCO_PROMPT_MODULE='@commitlint' \
OCO_AI_PROVIDER='test' OCO_GITPUSH='true' \
node ${(0, path_1.resolve)('./out/cli.cjs')} commitlint force \
`, [], { cwd: gitDir });
expect(await findByText('opencommit — configure @commitlint')).toBeInTheConsole();
expect(await findByText('Read @commitlint configuration')).toBeInTheConsole();
expect(await findByText('Generating consistency with given @commitlint rules')).toBeInTheConsole();
expect(await findByText('Done - please review contents of')).toBeInTheConsole();
await cleanup();
});
it('on commitlint@19 using ESM', async () => {
const { gitDir, cleanup } = await (0, utils_1.prepareEnvironment)();
await setupCommitlint(gitDir, 19);
const npmList = await (0, cli_testing_library_1.render)('npm', ['list', '@commitlint/load'], {
cwd: gitDir
});
expect(await npmList.findByText('@commitlint/load@19')).toBeInTheConsole();
const { findByText } = await (0, cli_testing_library_1.render)(`
OCO_TEST_MOCK_TYPE='prompt-module-commitlint-config' \
OCO_PROMPT_MODULE='@commitlint' \
OCO_AI_PROVIDER='test' OCO_GITPUSH='true' \
node ${(0, path_1.resolve)('./out/cli.cjs')} commitlint force \
`, [], { cwd: gitDir });
expect(await findByText('opencommit — configure @commitlint')).toBeInTheConsole();
expect(await findByText('Read @commitlint configuration')).toBeInTheConsole();
expect(await findByText('Generating consistency with given @commitlint rules')).toBeInTheConsole();
expect(await findByText('Done - please review contents of')).toBeInTheConsole();
await cleanup();
});
});
describe('cli flow to generate commit message using @commitlint prompt-module', () => {
it('on commitlint@19 using ESM', async () => {
const { gitDir, cleanup } = await (0, utils_1.prepareEnvironment)();
// Setup commitlint@19
await setupCommitlint(gitDir, 19);
const npmList = await (0, cli_testing_library_1.render)('npm', ['list', '@commitlint/load'], {
cwd: gitDir
});
expect(await npmList.findByText('@commitlint/load@19')).toBeInTheConsole();
// Run `oco commitlint force`
const commitlintForce = await (0, cli_testing_library_1.render)(`
OCO_TEST_MOCK_TYPE='prompt-module-commitlint-config' \
OCO_PROMPT_MODULE='@commitlint' \
OCO_AI_PROVIDER='test' OCO_GITPUSH='true' \
node ${(0, path_1.resolve)('./out/cli.cjs')} commitlint force \
`, [], { cwd: gitDir });
expect(await commitlintForce.findByText('Done - please review contents of')).toBeInTheConsole();
// Run `oco commitlint get`
const commitlintGet = await (0, cli_testing_library_1.render)(`
OCO_TEST_MOCK_TYPE='prompt-module-commitlint-config' \
OCO_PROMPT_MODULE='@commitlint' \
OCO_AI_PROVIDER='test' OCO_GITPUSH='true' \
node ${(0, path_1.resolve)('./out/cli.cjs')} commitlint get \
`, [], { cwd: gitDir });
expect(await commitlintGet.findByText('consistency')).toBeInTheConsole();
// Run 'oco' using .opencommit-commitlint
await (0, cli_testing_library_1.render)('echo', [`'console.log("Hello World");' > index.ts`], {
cwd: gitDir
});
await (0, cli_testing_library_1.render)('git', ['add index.ts'], { cwd: gitDir });
const oco = await (0, cli_testing_library_1.render)(`
OCO_TEST_MOCK_TYPE='commit-message' \
OCO_PROMPT_MODULE='@commitlint' \
OCO_AI_PROVIDER='test' OCO_GITPUSH='true' \
node ${(0, path_1.resolve)('./out/cli.cjs')} \
`, [], { cwd: gitDir });
expect(await oco.findByText('Generating the commit message')).toBeInTheConsole();
expect(await oco.findByText('Confirm the commit message?')).toBeInTheConsole();
oco.userEvent.keyboard('[Enter]');
expect(await oco.findByText('Do you want to run `git push`?')).toBeInTheConsole();
oco.userEvent.keyboard('[Enter]');
expect(await oco.findByText('Successfully pushed all commits to origin')).toBeInTheConsole();
await cleanup();
});
});
+39
View File
@@ -0,0 +1,39 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.wait = exports.prepareTempDir = exports.prepareEnvironment = void 0;
const path_1 = __importDefault(require("path"));
const fs_1 = require("fs");
const util_1 = require("util");
const os_1 = require("os");
const child_process_1 = require("child_process");
const fsMakeTempDir = (0, util_1.promisify)(fs_1.mkdtemp);
const fsExec = (0, util_1.promisify)(child_process_1.exec);
const fsRemove = (0, util_1.promisify)(fs_1.rm);
/**
* Prepare the environment for the test
* Create a temporary git repository in the temp directory
*/
const prepareEnvironment = async () => {
const tempDir = await (0, exports.prepareTempDir)();
// Create a remote git repository int the temp directory. This is necessary to execute the `git push` command
await fsExec('git init --bare remote.git', { cwd: tempDir });
await fsExec('git clone remote.git test', { cwd: tempDir });
const gitDir = path_1.default.resolve(tempDir, 'test');
const cleanup = async () => {
return fsRemove(tempDir, { recursive: true });
};
return {
gitDir,
cleanup,
};
};
exports.prepareEnvironment = prepareEnvironment;
const prepareTempDir = async () => {
return await fsMakeTempDir(path_1.default.join((0, os_1.tmpdir)(), 'opencommit-test-'));
};
exports.prepareTempDir = prepareTempDir;
const wait = (ms) => new Promise(resolve => setTimeout(resolve, ms));
exports.wait = wait;
+11
View File
@@ -0,0 +1,11 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const globals_1 = require("@jest/globals");
require("cli-testing-library/extend-expect");
const cli_testing_library_1 = require("cli-testing-library");
// Make Jest available globally
global.jest = globals_1.jest;
/**
* Adjusted the wait time for waitFor/findByText to 2000ms, because the default 1000ms makes the test results flaky
*/
(0, cli_testing_library_1.configure)({ asyncUtilTimeout: 2000 });
+248
View File
@@ -0,0 +1,248 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const fs_1 = require("fs");
const config_1 = require("../../src/commands/config");
const utils_1 = require("./utils");
describe('config', () => {
const originalEnv = { ...process.env };
let globalConfigFile;
let envConfigFile;
function resetEnv(env) {
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, content) => {
const fileContent = Object.entries(content)
.map(([key, value]) => `${key}="${value}"`)
.join('\n');
return await (0, utils_1.prepareFile)(fileName, fileContent);
};
describe('getConfig', () => {
it('should prioritize local .env over global .opencommit config', async () => {
globalConfigFile = await generateConfig('.opencommit', {
OCO_API_KEY: 'global-key',
OCO_MODEL: 'gpt-3.5-turbo',
OCO_LANGUAGE: 'en'
});
envConfigFile = await generateConfig('.env', {
OCO_API_KEY: 'local-key',
OCO_LANGUAGE: 'fr'
});
const config = (0, config_1.getConfig)({
globalPath: globalConfigFile.filePath,
envPath: envConfigFile.filePath
});
expect(config).not.toEqual(null);
expect(config.OCO_API_KEY).toEqual('local-key');
expect(config.OCO_MODEL).toEqual('gpt-3.5-turbo');
expect(config.OCO_LANGUAGE).toEqual('fr');
});
it('should fallback to global config when local config is not set', async () => {
globalConfigFile = await generateConfig('.opencommit', {
OCO_API_KEY: 'global-key',
OCO_MODEL: 'gpt-4',
OCO_LANGUAGE: 'de',
OCO_DESCRIPTION: 'true'
});
envConfigFile = await generateConfig('.env', {
OCO_API_URL: 'local-api-url'
});
const config = (0, config_1.getConfig)({
globalPath: globalConfigFile.filePath,
envPath: envConfigFile.filePath
});
expect(config).not.toEqual(null);
expect(config.OCO_API_KEY).toEqual('global-key');
expect(config.OCO_API_URL).toEqual('local-api-url');
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',
OCO_OMIT_SCOPE: 'true'
});
const config = (0, config_1.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);
expect(config.OCO_OMIT_SCOPE).toEqual(true);
});
it('should handle custom HTTP headers correctly', async () => {
globalConfigFile = await generateConfig('.opencommit', {
OCO_API_CUSTOM_HEADERS: '{"X-Global-Header": "global-value"}'
});
envConfigFile = await generateConfig('.env', {
OCO_API_CUSTOM_HEADERS: '{"Authorization": "Bearer token123", "X-Custom-Header": "test-value"}'
});
const config = (0, config_1.getConfig)({
globalPath: globalConfigFile.filePath,
envPath: envConfigFile.filePath
});
expect(config).not.toEqual(null);
expect(config.OCO_API_CUSTOM_HEADERS).toEqual({ "Authorization": "Bearer token123", "X-Custom-Header": "test-value" });
// No need to parse JSON again since it's already an object
const parsedHeaders = config.OCO_API_CUSTOM_HEADERS;
expect(parsedHeaders).toHaveProperty('Authorization', 'Bearer token123');
expect(parsedHeaders).toHaveProperty('X-Custom-Header', 'test-value');
expect(parsedHeaders).not.toHaveProperty('X-Global-Header');
});
it('should handle empty local config correctly', async () => {
globalConfigFile = await generateConfig('.opencommit', {
OCO_API_KEY: 'global-key',
OCO_MODEL: 'gpt-4',
OCO_LANGUAGE: 'es'
});
envConfigFile = await generateConfig('.env', {});
const config = (0, config_1.getConfig)({
globalPath: globalConfigFile.filePath,
envPath: envConfigFile.filePath
});
expect(config).not.toEqual(null);
expect(config.OCO_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_API_KEY: 'global-key',
OCO_MODEL: 'gpt-4',
OCO_LANGUAGE: 'es'
});
envConfigFile = await generateConfig('.env', {
OCO_API_KEY: 'null'
});
const config = (0, config_1.getConfig)({
globalPath: globalConfigFile.filePath,
envPath: envConfigFile.filePath
});
expect(config).not.toEqual(null);
expect(config.OCO_API_KEY).toEqual(null);
});
it('should handle empty global config', async () => {
globalConfigFile = await generateConfig('.opencommit', {});
envConfigFile = await generateConfig('.env', {});
const config = (0, config_1.getConfig)({
globalPath: globalConfigFile.filePath,
envPath: envConfigFile.filePath
});
expect(config).not.toEqual(null);
expect(config.OCO_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', {});
(0, fs_1.rmSync)(globalConfigFile.filePath);
});
it('should create .opencommit file with DEFAULT CONFIG if it does not exist on first setConfig run', async () => {
const isGlobalConfigFileExist = (0, fs_1.existsSync)(globalConfigFile.filePath);
expect(isGlobalConfigFileExist).toBe(false);
await (0, config_1.setConfig)([[config_1.CONFIG_KEYS.OCO_API_KEY, 'persisted-key_1']], globalConfigFile.filePath);
const fileContent = (0, fs_1.readFileSync)(globalConfigFile.filePath, 'utf8');
expect(fileContent).toContain('OCO_API_KEY=persisted-key_1');
Object.entries(config_1.DEFAULT_CONFIG).forEach(([key, value]) => {
expect(fileContent).toContain(`${key}=${value}`);
});
});
it('should set new config values', async () => {
globalConfigFile = await generateConfig('.opencommit', {});
await (0, config_1.setConfig)([
[config_1.CONFIG_KEYS.OCO_API_KEY, 'new-key'],
[config_1.CONFIG_KEYS.OCO_MODEL, 'gpt-4']
], globalConfigFile.filePath);
const config = (0, config_1.getConfig)({
globalPath: globalConfigFile.filePath
});
expect(config.OCO_API_KEY).toEqual('new-key');
expect(config.OCO_MODEL).toEqual('gpt-4');
});
it('should update existing config values', async () => {
globalConfigFile = await generateConfig('.opencommit', {
OCO_API_KEY: 'initial-key'
});
await (0, config_1.setConfig)([[config_1.CONFIG_KEYS.OCO_API_KEY, 'updated-key']], globalConfigFile.filePath);
const config = (0, config_1.getConfig)({
globalPath: globalConfigFile.filePath
});
expect(config.OCO_API_KEY).toEqual('updated-key');
});
it('should handle boolean and numeric values correctly', async () => {
globalConfigFile = await generateConfig('.opencommit', {});
await (0, config_1.setConfig)([
[config_1.CONFIG_KEYS.OCO_TOKENS_MAX_INPUT, '8192'],
[config_1.CONFIG_KEYS.OCO_DESCRIPTION, 'true'],
[config_1.CONFIG_KEYS.OCO_ONE_LINE_COMMIT, 'false']
], globalConfigFile.filePath);
const config = (0, config_1.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 (0, config_1.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 = (0, fs_1.existsSync)(globalConfigFile.filePath);
expect(isGlobalConfigFileExist).toBe(false);
await (0, config_1.setConfig)([[config_1.CONFIG_KEYS.OCO_API_KEY, 'persisted-key']], globalConfigFile.filePath);
const fileContent = (0, fs_1.readFileSync)(globalConfigFile.filePath, 'utf8');
expect(fileContent).toContain('OCO_API_KEY=persisted-key');
});
it('should set multiple configs in a row and keep the changes', async () => {
const isGlobalConfigFileExist = (0, fs_1.existsSync)(globalConfigFile.filePath);
expect(isGlobalConfigFileExist).toBe(false);
await (0, config_1.setConfig)([[config_1.CONFIG_KEYS.OCO_API_KEY, 'persisted-key']], globalConfigFile.filePath);
const fileContent1 = (0, fs_1.readFileSync)(globalConfigFile.filePath, 'utf8');
expect(fileContent1).toContain('OCO_API_KEY=persisted-key');
await (0, config_1.setConfig)([[config_1.CONFIG_KEYS.OCO_MODEL, 'gpt-4']], globalConfigFile.filePath);
const fileContent2 = (0, fs_1.readFileSync)(globalConfigFile.filePath, 'utf8');
expect(fileContent2).toContain('OCO_MODEL=gpt-4');
});
});
});
+69
View File
@@ -0,0 +1,69 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const gemini_1 = require("../../src/engine/gemini");
const generative_ai_1 = require("@google/generative-ai");
const config_1 = require("../../src/commands/config");
describe('Gemini', () => {
let gemini;
let mockConfig;
let mockGoogleGenerativeAi;
let mockGenerativeModel;
let mockExit;
const noop = (...args) => { };
const mockGemini = () => {
mockConfig = (0, config_1.getConfig)();
gemini = new gemini_1.GeminiEngine({
apiKey: mockConfig.OCO_API_KEY,
model: mockConfig.OCO_MODEL
});
};
const oldEnv = process.env;
beforeEach(() => {
jest.resetModules();
process.env = { ...oldEnv };
jest.mock('@google/generative-ai');
jest.mock('../src/commands/config');
jest.mock('@clack/prompts', () => ({
intro: jest.fn(),
outro: jest.fn()
}));
mockExit = jest.spyOn(process, 'exit').mockImplementation();
mockConfig = (0, config_1.getConfig)();
mockConfig.OCO_AI_PROVIDER = config_1.OCO_AI_PROVIDER_ENUM.GEMINI;
mockConfig.OCO_API_KEY = 'mock-api-key';
mockConfig.OCO_MODEL = 'gemini-1.5-flash';
mockGoogleGenerativeAi = new generative_ai_1.GoogleGenerativeAI(mockConfig.OCO_API_KEY);
mockGenerativeModel = mockGoogleGenerativeAi.getGenerativeModel({
model: mockConfig.OCO_MODEL
});
});
afterEach(() => {
gemini = undefined;
});
afterAll(() => {
mockExit.mockRestore();
process.env = oldEnv;
});
it.skip('should exit process if OCO_GEMINI_API_KEY is not set and command is not config', () => {
process.env.OCO_GEMINI_API_KEY = undefined;
process.env.OCO_AI_PROVIDER = 'gemini';
mockGemini();
expect(mockExit).toHaveBeenCalledWith(1);
});
it('should generate commit message', async () => {
const mockGenerateContent = jest
.fn()
.mockResolvedValue({ response: { text: () => 'generated content' } });
mockGenerativeModel.generateContent = mockGenerateContent;
mockGemini();
const messages = [
{ role: 'system', content: 'system message' },
{ role: 'assistant', content: 'assistant message' }
];
jest
.spyOn(gemini, 'generateCommitMessage')
.mockImplementation(async () => 'generated content');
const result = await gemini.generateCommitMessage(messages);
expect(result).toEqual('generated content');
});
});
+50
View File
@@ -0,0 +1,50 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const removeContentTags_1 = require("../../src/utils/removeContentTags");
describe('removeContentTags', () => {
it('should remove content wrapped in specified tags', () => {
const content = 'This is <think>something to hide</think> visible content';
const result = (0, removeContentTags_1.removeContentTags)(content, 'think');
expect(result).toBe('This is visible content');
});
it('should handle multiple tag occurrences', () => {
const content = '<think>hidden</think> visible <think>also hidden</think> text';
const result = (0, removeContentTags_1.removeContentTags)(content, 'think');
expect(result).toBe('visible text');
});
it('should handle multiline content within tags', () => {
const content = 'Start <think>hidden\nover multiple\nlines</think> End';
const result = (0, removeContentTags_1.removeContentTags)(content, 'think');
expect(result).toBe('Start End');
});
it('should return content as is when tag is not found', () => {
const content = 'Content without any tags';
const result = (0, removeContentTags_1.removeContentTags)(content, 'think');
expect(result).toBe('Content without any tags');
});
it('should work with different tag names', () => {
const content = 'This is <custom>something to hide</custom> visible content';
const result = (0, removeContentTags_1.removeContentTags)(content, 'custom');
expect(result).toBe('This is visible content');
});
it('should handle null content', () => {
const content = null;
const result = (0, removeContentTags_1.removeContentTags)(content, 'think');
expect(result).toBe(null);
});
it('should handle undefined content', () => {
const content = undefined;
const result = (0, removeContentTags_1.removeContentTags)(content, 'think');
expect(result).toBe(undefined);
});
it('should trim the result', () => {
const content = ' <think>hidden</think> visible ';
const result = (0, removeContentTags_1.removeContentTags)(content, 'think');
expect(result).toBe('visible');
});
it('should handle nested tags correctly', () => {
const content = 'Outside <think>Inside <think>Nested</think></think> End';
const result = (0, removeContentTags_1.removeContentTags)(content, 'think');
expect(result).toBe('Outside End');
});
});
+31
View File
@@ -0,0 +1,31 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.prepareFile = void 0;
const fs_1 = require("fs");
const os_1 = require("os");
const path_1 = __importDefault(require("path"));
const util_1 = require("util");
const fsMakeTempDir = (0, util_1.promisify)(fs_1.mkdtemp);
const fsRemove = (0, util_1.promisify)(fs_1.rm);
const fsWriteFile = (0, util_1.promisify)(fs_1.writeFile);
/**
* Prepare tmp file for the test
*/
async function prepareFile(fileName, content) {
const tempDir = await fsMakeTempDir(path_1.default.join((0, os_1.tmpdir)(), 'opencommit-test-'));
const filePath = path_1.default.resolve(tempDir, fileName);
await fsWriteFile(filePath, content);
const cleanup = async () => {
if ((0, fs_1.existsSync)(tempDir)) {
await fsRemove(tempDir, { recursive: true });
}
};
return {
filePath,
cleanup
};
}
exports.prepareFile = prepareFile;