mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-09-14 23:22:54 +00:00
* feat(setup): select coding agent integrations * style: format setup agent selection * fix(setup): validate explicit agent selection --------- Co-authored-by: Gergő Magyar <gergomagyar@icloud.com>
131 lines
5.3 KiB
TypeScript
131 lines
5.3 KiB
TypeScript
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
import fs from 'fs/promises';
|
|
import os from 'os';
|
|
import path from 'path';
|
|
|
|
const execFileMock = vi.fn((...args: any[]) => {
|
|
const callback = args.at(-1);
|
|
if (typeof callback === 'function') callback(null, '', '');
|
|
});
|
|
|
|
vi.mock('child_process', () => ({
|
|
execFile: execFileMock,
|
|
execFileSync: vi.fn(() => {
|
|
throw new Error('not found');
|
|
}),
|
|
}));
|
|
|
|
describe('setupCommand coding-agent selection', () => {
|
|
let tempHome: string;
|
|
let originalHome: string | undefined;
|
|
let originalUserProfile: string | undefined;
|
|
let originalExitCode: number | string | null | undefined;
|
|
|
|
beforeEach(async () => {
|
|
vi.resetModules();
|
|
vi.clearAllMocks();
|
|
originalHome = process.env.HOME;
|
|
originalUserProfile = process.env.USERPROFILE;
|
|
originalExitCode = process.exitCode;
|
|
tempHome = await fs.mkdtemp(path.join(os.tmpdir(), 'gn-setup-selection-'));
|
|
process.env.HOME = tempHome;
|
|
process.env.USERPROFILE = tempHome;
|
|
process.exitCode = undefined;
|
|
await Promise.all([
|
|
fs.mkdir(path.join(tempHome, '.cursor'), { recursive: true }),
|
|
fs.mkdir(path.join(tempHome, '.claude'), { recursive: true }),
|
|
fs.mkdir(path.join(tempHome, '.gemini', 'antigravity'), { recursive: true }),
|
|
fs.mkdir(path.join(tempHome, '.config', 'opencode'), { recursive: true }),
|
|
fs.mkdir(path.join(tempHome, '.codex'), { recursive: true }),
|
|
]);
|
|
vi.spyOn(console, 'log').mockImplementation(() => {});
|
|
});
|
|
|
|
afterEach(async () => {
|
|
vi.restoreAllMocks();
|
|
process.env.HOME = originalHome;
|
|
process.env.USERPROFILE = originalUserProfile;
|
|
process.exitCode = originalExitCode;
|
|
await fs.rm(tempHome, { recursive: true, force: true });
|
|
});
|
|
|
|
it('configures only the requested coding agent', async () => {
|
|
const { setupCommand } = await import('../../src/cli/setup.js');
|
|
await setupCommand({ codingAgent: ['opencode'] });
|
|
|
|
await expect(
|
|
fs.access(path.join(tempHome, '.config', 'opencode', 'opencode.json')),
|
|
).resolves.toBeUndefined();
|
|
await expect(fs.access(path.join(tempHome, '.cursor', 'mcp.json'))).rejects.toThrow();
|
|
await expect(fs.access(path.join(tempHome, '.claude.json'))).rejects.toThrow();
|
|
await expect(
|
|
fs.access(path.join(tempHome, '.gemini', 'antigravity', 'mcp_config.json')),
|
|
).rejects.toThrow();
|
|
await expect(fs.access(path.join(tempHome, '.codex', 'config.toml'))).rejects.toThrow();
|
|
});
|
|
|
|
it('accepts comma-separated and repeated selections without configuring others', async () => {
|
|
const { setupCommand } = await import('../../src/cli/setup.js');
|
|
await setupCommand({ codingAgent: ['cursor,opencode', 'cursor'] });
|
|
|
|
await expect(fs.access(path.join(tempHome, '.cursor', 'mcp.json'))).resolves.toBeUndefined();
|
|
await expect(
|
|
fs.access(path.join(tempHome, '.config', 'opencode', 'opencode.json')),
|
|
).resolves.toBeUndefined();
|
|
await expect(fs.access(path.join(tempHome, '.claude.json'))).rejects.toThrow();
|
|
});
|
|
|
|
it('rejects unknown values before writing configuration', async () => {
|
|
const stderr = vi.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
const { setupCommand } = await import('../../src/cli/setup.js');
|
|
await setupCommand({ codingAgent: ['opencode,unknown'] });
|
|
|
|
expect(process.exitCode).toBe(1);
|
|
expect(stderr).toHaveBeenCalledWith(
|
|
expect.stringContaining('Valid values: cursor, claude, antigravity, opencode, codex'),
|
|
);
|
|
await expect(
|
|
fs.access(path.join(tempHome, '.config', 'opencode', 'opencode.json')),
|
|
).rejects.toThrow();
|
|
});
|
|
|
|
it.each([
|
|
['an empty string', ''],
|
|
['an empty array', []],
|
|
])('rejects %s before writing configuration', async (_label, codingAgent) => {
|
|
const stderr = vi.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
const { setupCommand } = await import('../../src/cli/setup.js');
|
|
|
|
await setupCommand({ codingAgent });
|
|
|
|
expect(process.exitCode).toBe(1);
|
|
expect(stderr).toHaveBeenCalledWith(expect.stringContaining('No coding agents were provided.'));
|
|
await expect(fs.access(path.join(tempHome, '.cursor', 'mcp.json'))).rejects.toThrow();
|
|
});
|
|
|
|
it('fails clearly when an explicitly selected agent is not installed', async () => {
|
|
await fs.rm(path.join(tempHome, '.codex'), { recursive: true, force: true });
|
|
const stderr = vi.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
const { setupCommand } = await import('../../src/cli/setup.js');
|
|
|
|
await setupCommand({ codingAgent: ['codex'] });
|
|
|
|
expect(process.exitCode).toBe(1);
|
|
expect(stderr).toHaveBeenCalledWith(
|
|
'None of the explicitly selected coding agents were configured.\n',
|
|
);
|
|
expect(vi.mocked(console.log).mock.calls.flat().join('\n')).not.toContain('MCP is ready!');
|
|
});
|
|
|
|
it('preserves the no-flag default of configuring every detected agent', async () => {
|
|
const { setupCommand } = await import('../../src/cli/setup.js');
|
|
|
|
await setupCommand();
|
|
|
|
await expect(fs.access(path.join(tempHome, '.cursor', 'mcp.json'))).resolves.toBeUndefined();
|
|
await expect(fs.access(path.join(tempHome, '.claude.json'))).resolves.toBeUndefined();
|
|
await expect(
|
|
fs.access(path.join(tempHome, '.config', 'opencode', 'opencode.json')),
|
|
).resolves.toBeUndefined();
|
|
});
|
|
});
|