Ralph Khreish 37af0f1912
feat: add codebase context capabilities to gemini-cli (#1163)
* feat: add support for claude code context

- code context for:
  - add-task
  - update-subtask
  - update-task
  - update

* feat: fix CI and format + refactor

* chore: format

* chore: fix test

* feat: add gemini-cli support for codebase context

* feat: add google cli integration and fix tests

* chore: apply requested coderabbit changes

* chore: bump gemini cli package
2025-08-28 22:44:52 +02:00

123 lines
2.8 KiB
JavaScript

import { jest } from '@jest/globals';
jest.unstable_mockModule('fs', () => {
const mockFs = {
existsSync: jest.fn(() => true),
writeFileSync: jest.fn(),
readFileSync: jest.fn(),
unlinkSync: jest.fn()
};
return { default: mockFs, ...mockFs };
});
jest.unstable_mockModule('../../../../../scripts/modules/utils.js', () => ({
readJSON: jest.fn(),
writeJSON: jest.fn(),
log: jest.fn(),
isSilentMode: jest.fn(() => false),
findProjectRoot: jest.fn(() => '/project'),
flattenTasksWithSubtasks: jest.fn(() => []),
truncate: jest.fn((t) => t),
isEmpty: jest.fn(() => false),
resolveEnvVariable: jest.fn(),
findTaskById: jest.fn(),
getCurrentTag: jest.fn(() => 'master')
}));
jest.unstable_mockModule('../../../../../scripts/modules/ui.js', () => ({
getStatusWithColor: jest.fn((s) => s),
startLoadingIndicator: jest.fn(() => ({ stop: jest.fn() })),
stopLoadingIndicator: jest.fn(),
displayAiUsageSummary: jest.fn()
}));
jest.unstable_mockModule(
'../../../../../scripts/modules/task-manager/generate-task-files.js',
() => ({
default: jest.fn().mockResolvedValue()
})
);
jest.unstable_mockModule(
'../../../../../scripts/modules/ai-services-unified.js',
() => ({
generateTextService: jest
.fn()
.mockResolvedValue({ mainResult: { content: '{}' }, telemetryData: {} })
})
);
jest.unstable_mockModule(
'../../../../../scripts/modules/config-manager.js',
() => ({
getDebugFlag: jest.fn(() => false),
isApiKeySet: jest.fn(() => true),
hasCodebaseAnalysis: jest.fn(() => false)
})
);
const { readJSON, log } = await import(
'../../../../../scripts/modules/utils.js'
);
const { default: updateTaskById } = await import(
'../../../../../scripts/modules/task-manager/update-task-by-id.js'
);
describe('updateTaskById validation', () => {
beforeEach(() => {
jest.clearAllMocks();
jest.spyOn(process, 'exit').mockImplementation(() => {
throw new Error('process.exit called');
});
});
test('throws error if prompt is empty', async () => {
await expect(
updateTaskById(
'tasks/tasks.json',
1,
'',
false,
{ tag: 'master' },
'json'
)
).rejects.toThrow('Prompt cannot be empty');
});
test('throws error if task file missing', async () => {
const fs = await import('fs');
fs.existsSync.mockReturnValue(false);
await expect(
updateTaskById(
'tasks/tasks.json',
1,
'prompt',
false,
{
tag: 'master'
},
'json'
)
).rejects.toThrow('Tasks file not found');
});
test('throws error when task ID not found', async () => {
const fs = await import('fs');
fs.existsSync.mockReturnValue(true);
readJSON.mockReturnValue({ tag: 'master', tasks: [] });
await expect(
updateTaskById(
'tasks/tasks.json',
42,
'prompt',
false,
{
tag: 'master'
},
'json'
)
).rejects.toThrow('Task with ID 42 not found');
expect(log).toHaveBeenCalled();
});
});