@joeytheman/gemini-mcp-tool 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +26 -0
- package/README.md +239 -0
- package/dist/__tests__/ask-gemini.test.d.ts +2 -0
- package/dist/__tests__/ask-gemini.test.d.ts.map +1 -0
- package/dist/__tests__/ask-gemini.test.js +165 -0
- package/dist/__tests__/ask-gemini.test.js.map +1 -0
- package/dist/__tests__/server.test.d.ts +2 -0
- package/dist/__tests__/server.test.d.ts.map +1 -0
- package/dist/__tests__/server.test.js +72 -0
- package/dist/__tests__/server.test.js.map +1 -0
- package/dist/__tests__/tools.test.d.ts +2 -0
- package/dist/__tests__/tools.test.d.ts.map +1 -0
- package/dist/__tests__/tools.test.js +131 -0
- package/dist/__tests__/tools.test.js.map +1 -0
- package/dist/__tests__/utils/changeModeChunker.test.d.ts +2 -0
- package/dist/__tests__/utils/changeModeChunker.test.d.ts.map +1 -0
- package/dist/__tests__/utils/changeModeChunker.test.js +137 -0
- package/dist/__tests__/utils/changeModeChunker.test.js.map +1 -0
- package/dist/__tests__/utils/changeModeParser.test.d.ts +2 -0
- package/dist/__tests__/utils/changeModeParser.test.d.ts.map +1 -0
- package/dist/__tests__/utils/changeModeParser.test.js +254 -0
- package/dist/__tests__/utils/changeModeParser.test.js.map +1 -0
- package/dist/__tests__/utils/changeModeTranslator.test.d.ts +2 -0
- package/dist/__tests__/utils/changeModeTranslator.test.d.ts.map +1 -0
- package/dist/__tests__/utils/changeModeTranslator.test.js +112 -0
- package/dist/__tests__/utils/changeModeTranslator.test.js.map +1 -0
- package/dist/__tests__/utils/chunkCache.test.d.ts +2 -0
- package/dist/__tests__/utils/chunkCache.test.d.ts.map +1 -0
- package/dist/__tests__/utils/chunkCache.test.js +140 -0
- package/dist/__tests__/utils/chunkCache.test.js.map +1 -0
- package/dist/__tests__/utils/commandExecutor.test.d.ts +2 -0
- package/dist/__tests__/utils/commandExecutor.test.d.ts.map +1 -0
- package/dist/__tests__/utils/commandExecutor.test.js +139 -0
- package/dist/__tests__/utils/commandExecutor.test.js.map +1 -0
- package/dist/__tests__/utils/geminiExecutor.test.d.ts +2 -0
- package/dist/__tests__/utils/geminiExecutor.test.d.ts.map +1 -0
- package/dist/__tests__/utils/geminiExecutor.test.js +337 -0
- package/dist/__tests__/utils/geminiExecutor.test.js.map +1 -0
- package/dist/__tests__/utils/responseCache.test.d.ts +2 -0
- package/dist/__tests__/utils/responseCache.test.d.ts.map +1 -0
- package/dist/__tests__/utils/responseCache.test.js +98 -0
- package/dist/__tests__/utils/responseCache.test.js.map +1 -0
- package/dist/__tests__/utils/test-helpers.d.ts +17 -0
- package/dist/__tests__/utils/test-helpers.d.ts.map +1 -0
- package/dist/__tests__/utils/test-helpers.js +50 -0
- package/dist/__tests__/utils/test-helpers.js.map +1 -0
- package/dist/constants.d.ts +96 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +89 -0
- package/dist/constants.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +186 -0
- package/dist/index.js.map +1 -0
- package/dist/tools/ask-gemini.tool.d.ts +3 -0
- package/dist/tools/ask-gemini.tool.d.ts.map +1 -0
- package/dist/tools/ask-gemini.tool.js +65 -0
- package/dist/tools/ask-gemini.tool.js.map +1 -0
- package/dist/tools/brainstorm.tool.d.ts +3 -0
- package/dist/tools/brainstorm.tool.d.ts.map +1 -0
- package/dist/tools/brainstorm.tool.js +147 -0
- package/dist/tools/brainstorm.tool.js.map +1 -0
- package/dist/tools/fetch-chunk.tool.d.ts +3 -0
- package/dist/tools/fetch-chunk.tool.d.ts.map +1 -0
- package/dist/tools/fetch-chunk.tool.js +67 -0
- package/dist/tools/fetch-chunk.tool.js.map +1 -0
- package/dist/tools/index.d.ts +2 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +10 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/registry.d.ts +43 -0
- package/dist/tools/registry.d.ts.map +1 -0
- package/dist/tools/registry.js +81 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/simple-tools.d.ts +4 -0
- package/dist/tools/simple-tools.d.ts.map +1 -0
- package/dist/tools/simple-tools.js +42 -0
- package/dist/tools/simple-tools.js.map +1 -0
- package/dist/tools/test-tool.example.d.ts +13 -0
- package/dist/tools/test-tool.example.d.ts.map +1 -0
- package/dist/tools/test-tool.example.js +32 -0
- package/dist/tools/test-tool.example.js.map +1 -0
- package/dist/tools/timeout-test.tool.d.ts +3 -0
- package/dist/tools/timeout-test.tool.d.ts.map +1 -0
- package/dist/tools/timeout-test.tool.js +36 -0
- package/dist/tools/timeout-test.tool.js.map +1 -0
- package/dist/utils/changeModeChunker.d.ts +11 -0
- package/dist/utils/changeModeChunker.d.ts.map +1 -0
- package/dist/utils/changeModeChunker.js +89 -0
- package/dist/utils/changeModeChunker.js.map +1 -0
- package/dist/utils/changeModeParser.d.ts +15 -0
- package/dist/utils/changeModeParser.d.ts.map +1 -0
- package/dist/utils/changeModeParser.js +67 -0
- package/dist/utils/changeModeParser.js.map +1 -0
- package/dist/utils/changeModeTranslator.d.ts +8 -0
- package/dist/utils/changeModeTranslator.d.ts.map +1 -0
- package/dist/utils/changeModeTranslator.js +70 -0
- package/dist/utils/changeModeTranslator.js.map +1 -0
- package/dist/utils/chunkCache.d.ts +22 -0
- package/dist/utils/chunkCache.d.ts.map +1 -0
- package/dist/utils/chunkCache.js +161 -0
- package/dist/utils/chunkCache.js.map +1 -0
- package/dist/utils/commandExecutor.d.ts +2 -0
- package/dist/utils/commandExecutor.d.ts.map +1 -0
- package/dist/utils/commandExecutor.js +79 -0
- package/dist/utils/commandExecutor.js.map +1 -0
- package/dist/utils/geminiExecutor.d.ts +17 -0
- package/dist/utils/geminiExecutor.d.ts.map +1 -0
- package/dist/utils/geminiExecutor.js +238 -0
- package/dist/utils/geminiExecutor.js.map +1 -0
- package/dist/utils/logger.d.ts +13 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +42 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/responseCache.d.ts +33 -0
- package/dist/utils/responseCache.d.ts.map +1 -0
- package/dist/utils/responseCache.js +87 -0
- package/dist/utils/responseCache.js.map +1 -0
- package/dist/utils/timeoutManager.d.ts +2 -0
- package/dist/utils/timeoutManager.d.ts.map +1 -0
- package/dist/utils/timeoutManager.js +2 -0
- package/dist/utils/timeoutManager.js.map +1 -0
- package/package.json +79 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { formatChangeModeResponse, summarizeChangeModeEdits } from '../../utils/changeModeTranslator.js';
|
|
3
|
+
function makeEdit(filename, oldCode = 'old', newCode = 'new') {
|
|
4
|
+
return {
|
|
5
|
+
filename,
|
|
6
|
+
oldStartLine: 1,
|
|
7
|
+
oldEndLine: 1,
|
|
8
|
+
oldCode,
|
|
9
|
+
newStartLine: 1,
|
|
10
|
+
newEndLine: 1,
|
|
11
|
+
newCode,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
describe('formatChangeModeResponse', () => {
|
|
15
|
+
it('should format single chunk without chunkInfo', () => {
|
|
16
|
+
const edits = [makeEdit('src/a.ts')];
|
|
17
|
+
const result = formatChangeModeResponse(edits);
|
|
18
|
+
expect(result).toContain('[CHANGEMODE OUTPUT');
|
|
19
|
+
expect(result).toContain('1 modification');
|
|
20
|
+
expect(result).toContain('### Edit 1: src/a.ts');
|
|
21
|
+
expect(result).toContain('old');
|
|
22
|
+
expect(result).toContain('new');
|
|
23
|
+
expect(result).not.toContain('Chunk');
|
|
24
|
+
expect(result).not.toContain('fetch-chunk');
|
|
25
|
+
});
|
|
26
|
+
it('should format multi-chunk with navigation footer', () => {
|
|
27
|
+
const edits = [makeEdit('src/a.ts'), makeEdit('src/b.ts')];
|
|
28
|
+
const chunkInfo = { current: 1, total: 3, cacheKey: 'abc12345' };
|
|
29
|
+
const result = formatChangeModeResponse(edits, chunkInfo);
|
|
30
|
+
expect(result).toContain('Chunk 1 of 3');
|
|
31
|
+
expect(result).toContain('2 complete edits');
|
|
32
|
+
expect(result).toContain('fetch-chunk cacheKey="abc12345" chunkIndex=2');
|
|
33
|
+
expect(result).toContain('2 more chunks');
|
|
34
|
+
});
|
|
35
|
+
it('should use correct pluralization for 1 edit', () => {
|
|
36
|
+
const edits = [makeEdit('src/a.ts')];
|
|
37
|
+
const result = formatChangeModeResponse(edits);
|
|
38
|
+
expect(result).toContain('1 modification');
|
|
39
|
+
expect(result).not.toContain('modifications');
|
|
40
|
+
});
|
|
41
|
+
it('should use correct pluralization for multiple edits', () => {
|
|
42
|
+
const edits = [makeEdit('src/a.ts'), makeEdit('src/b.ts'), makeEdit('src/c.ts')];
|
|
43
|
+
const result = formatChangeModeResponse(edits);
|
|
44
|
+
expect(result).toContain('3 modifications');
|
|
45
|
+
});
|
|
46
|
+
it('should use correct pluralization in multi-chunk header', () => {
|
|
47
|
+
const edits = [makeEdit('src/a.ts')];
|
|
48
|
+
const chunkInfo = { current: 1, total: 2, cacheKey: 'key123' };
|
|
49
|
+
const result = formatChangeModeResponse(edits, chunkInfo);
|
|
50
|
+
expect(result).toContain('1 complete edit ');
|
|
51
|
+
expect(result).not.toContain('1 complete edits');
|
|
52
|
+
});
|
|
53
|
+
it('should show "is 1 more chunk" when only 1 remaining', () => {
|
|
54
|
+
const edits = [makeEdit('src/a.ts')];
|
|
55
|
+
const chunkInfo = { current: 1, total: 2, cacheKey: 'key123' };
|
|
56
|
+
const result = formatChangeModeResponse(edits, chunkInfo);
|
|
57
|
+
expect(result).toContain('is 1 more chunk');
|
|
58
|
+
expect(result).not.toContain('is 1 more chunks');
|
|
59
|
+
});
|
|
60
|
+
it('should show "are N more chunks" for multiple remaining', () => {
|
|
61
|
+
const edits = [makeEdit('src/a.ts')];
|
|
62
|
+
const chunkInfo = { current: 1, total: 4, cacheKey: 'key123' };
|
|
63
|
+
const result = formatChangeModeResponse(edits, chunkInfo);
|
|
64
|
+
expect(result).toContain('are 3 more chunks');
|
|
65
|
+
});
|
|
66
|
+
it('should not include footer for last chunk', () => {
|
|
67
|
+
const edits = [makeEdit('src/a.ts')];
|
|
68
|
+
const chunkInfo = { current: 3, total: 3, cacheKey: 'key123' };
|
|
69
|
+
const result = formatChangeModeResponse(edits, chunkInfo);
|
|
70
|
+
expect(result).not.toContain('fetch-chunk');
|
|
71
|
+
expect(result).not.toContain('Next Step');
|
|
72
|
+
});
|
|
73
|
+
it('should include correct chunkIndex in fetch-chunk command', () => {
|
|
74
|
+
const edits = [makeEdit('src/a.ts')];
|
|
75
|
+
const chunkInfo = { current: 2, total: 5, cacheKey: 'mykey' };
|
|
76
|
+
const result = formatChangeModeResponse(edits, chunkInfo);
|
|
77
|
+
expect(result).toContain('fetch-chunk cacheKey="mykey" chunkIndex=3');
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
describe('summarizeChangeModeEdits', () => {
|
|
81
|
+
it('should show correct file grouping and edit counts', () => {
|
|
82
|
+
const edits = [
|
|
83
|
+
makeEdit('src/a.ts'),
|
|
84
|
+
makeEdit('src/a.ts'),
|
|
85
|
+
makeEdit('src/b.ts'),
|
|
86
|
+
];
|
|
87
|
+
const summary = summarizeChangeModeEdits(edits);
|
|
88
|
+
expect(summary).toContain('ChangeMode Summary:');
|
|
89
|
+
expect(summary).toContain('Total edits: 3');
|
|
90
|
+
expect(summary).toContain('Files affected: 2');
|
|
91
|
+
expect(summary).toContain('- src/a.ts: 2 edits');
|
|
92
|
+
expect(summary).toContain('- src/b.ts: 1 edit');
|
|
93
|
+
expect(summary).not.toContain('1 edits');
|
|
94
|
+
});
|
|
95
|
+
it('should add "across all chunks" text when isPartialView is true', () => {
|
|
96
|
+
const edits = [makeEdit('src/a.ts')];
|
|
97
|
+
const summary = summarizeChangeModeEdits(edits, true);
|
|
98
|
+
expect(summary).toContain('(across all chunks)');
|
|
99
|
+
expect(summary).toContain('Complete analysis across all chunks');
|
|
100
|
+
});
|
|
101
|
+
it('should not add "across all chunks" text when isPartialView is false', () => {
|
|
102
|
+
const edits = [makeEdit('src/a.ts')];
|
|
103
|
+
const summary = summarizeChangeModeEdits(edits, false);
|
|
104
|
+
expect(summary).not.toContain('across all chunks');
|
|
105
|
+
});
|
|
106
|
+
it('should not add "across all chunks" text when isPartialView is undefined', () => {
|
|
107
|
+
const edits = [makeEdit('src/a.ts')];
|
|
108
|
+
const summary = summarizeChangeModeEdits(edits);
|
|
109
|
+
expect(summary).not.toContain('across all chunks');
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
//# sourceMappingURL=changeModeTranslator.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"changeModeTranslator.test.js","sourceRoot":"","sources":["../../../src/__tests__/utils/changeModeTranslator.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chunkCache.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/utils/chunkCache.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
// Mock logger
|
|
4
|
+
vi.mock('../../utils/logger.js', () => ({
|
|
5
|
+
Logger: {
|
|
6
|
+
debug: vi.fn(),
|
|
7
|
+
warn: vi.fn(),
|
|
8
|
+
error: vi.fn(),
|
|
9
|
+
log: vi.fn(),
|
|
10
|
+
},
|
|
11
|
+
}));
|
|
12
|
+
// Track mock file store
|
|
13
|
+
const mockFileStore = new Map();
|
|
14
|
+
const mockFileStats = new Map();
|
|
15
|
+
vi.mock('fs', () => ({
|
|
16
|
+
existsSync: vi.fn((filePath) => {
|
|
17
|
+
return mockFileStore.has(filePath) || filePath.endsWith('gemini-mcp-chunks');
|
|
18
|
+
}),
|
|
19
|
+
mkdirSync: vi.fn(),
|
|
20
|
+
writeFileSync: vi.fn((filePath, content) => {
|
|
21
|
+
mockFileStore.set(filePath, content);
|
|
22
|
+
mockFileStats.set(filePath, { mtimeMs: Date.now() });
|
|
23
|
+
}),
|
|
24
|
+
readFileSync: vi.fn((filePath) => {
|
|
25
|
+
const content = mockFileStore.get(filePath);
|
|
26
|
+
if (!content)
|
|
27
|
+
throw new Error(`ENOENT: no such file: ${filePath}`);
|
|
28
|
+
return content;
|
|
29
|
+
}),
|
|
30
|
+
unlinkSync: vi.fn((filePath) => {
|
|
31
|
+
mockFileStore.delete(filePath);
|
|
32
|
+
mockFileStats.delete(filePath);
|
|
33
|
+
}),
|
|
34
|
+
readdirSync: vi.fn(() => {
|
|
35
|
+
const files = [];
|
|
36
|
+
for (const key of mockFileStore.keys()) {
|
|
37
|
+
const parts = key.split('/');
|
|
38
|
+
const filename = parts[parts.length - 1];
|
|
39
|
+
if (filename.endsWith('.json')) {
|
|
40
|
+
files.push(filename);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return files;
|
|
44
|
+
}),
|
|
45
|
+
statSync: vi.fn((filePath) => {
|
|
46
|
+
const stats = mockFileStats.get(filePath);
|
|
47
|
+
if (!stats)
|
|
48
|
+
throw new Error(`ENOENT: no such file: ${filePath}`);
|
|
49
|
+
return stats;
|
|
50
|
+
}),
|
|
51
|
+
}));
|
|
52
|
+
import { cacheChunks, getChunks, clearCache, getCacheStats } from '../../utils/chunkCache.js';
|
|
53
|
+
const sampleChunks = [
|
|
54
|
+
{
|
|
55
|
+
edits: [{
|
|
56
|
+
filename: 'test.ts',
|
|
57
|
+
oldStartLine: 1,
|
|
58
|
+
oldEndLine: 1,
|
|
59
|
+
oldCode: 'old',
|
|
60
|
+
newStartLine: 1,
|
|
61
|
+
newEndLine: 1,
|
|
62
|
+
newCode: 'new',
|
|
63
|
+
}],
|
|
64
|
+
chunkIndex: 1,
|
|
65
|
+
totalChunks: 1,
|
|
66
|
+
hasMore: false,
|
|
67
|
+
estimatedChars: 100,
|
|
68
|
+
},
|
|
69
|
+
];
|
|
70
|
+
describe('chunkCache', () => {
|
|
71
|
+
beforeEach(() => {
|
|
72
|
+
vi.clearAllMocks();
|
|
73
|
+
mockFileStore.clear();
|
|
74
|
+
mockFileStats.clear();
|
|
75
|
+
});
|
|
76
|
+
describe('cacheChunks', () => {
|
|
77
|
+
it('should return an 8-char key', () => {
|
|
78
|
+
const key = cacheChunks('test prompt', sampleChunks);
|
|
79
|
+
expect(key).toHaveLength(8);
|
|
80
|
+
expect(key).toMatch(/^[0-9a-f]{8}$/);
|
|
81
|
+
});
|
|
82
|
+
it('should produce deterministic keys for same prompt', () => {
|
|
83
|
+
const key1 = cacheChunks('same prompt', sampleChunks);
|
|
84
|
+
mockFileStore.clear();
|
|
85
|
+
const key2 = cacheChunks('same prompt', sampleChunks);
|
|
86
|
+
expect(key1).toBe(key2);
|
|
87
|
+
});
|
|
88
|
+
it('should write JSON file', () => {
|
|
89
|
+
cacheChunks('test', sampleChunks);
|
|
90
|
+
expect(fs.writeFileSync).toHaveBeenCalled();
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
describe('getChunks', () => {
|
|
94
|
+
it('should return cached data on hit', () => {
|
|
95
|
+
const key = cacheChunks('test', sampleChunks);
|
|
96
|
+
const result = getChunks(key);
|
|
97
|
+
expect(result).not.toBeNull();
|
|
98
|
+
expect(result).toHaveLength(1);
|
|
99
|
+
expect(result[0].edits[0].filename).toBe('test.ts');
|
|
100
|
+
});
|
|
101
|
+
it('should return null for expired data (>10 min)', () => {
|
|
102
|
+
const key = cacheChunks('test', sampleChunks);
|
|
103
|
+
// Simulate expired timestamp by modifying the stored data
|
|
104
|
+
for (const [path, content] of mockFileStore.entries()) {
|
|
105
|
+
if (path.includes(key)) {
|
|
106
|
+
const data = JSON.parse(content);
|
|
107
|
+
data.timestamp = Date.now() - 11 * 60 * 1000; // 11 minutes ago
|
|
108
|
+
mockFileStore.set(path, JSON.stringify(data));
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
const result = getChunks(key);
|
|
112
|
+
expect(result).toBeNull();
|
|
113
|
+
});
|
|
114
|
+
it('should return null for missing key', () => {
|
|
115
|
+
// Override existsSync for this test to return false for the specific file
|
|
116
|
+
vi.mocked(fs.existsSync).mockReturnValueOnce(false);
|
|
117
|
+
const result = getChunks('nonexistent');
|
|
118
|
+
expect(result).toBeNull();
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
describe('clearCache', () => {
|
|
122
|
+
it('should remove all JSON files', () => {
|
|
123
|
+
// Set up mock state so readdirSync returns known files
|
|
124
|
+
vi.mocked(fs.readdirSync).mockReturnValueOnce(['abc.json', 'def.json']);
|
|
125
|
+
clearCache();
|
|
126
|
+
expect(fs.unlinkSync).toHaveBeenCalledTimes(2);
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
describe('getCacheStats', () => {
|
|
130
|
+
it('should return correct stats', () => {
|
|
131
|
+
vi.mocked(fs.readdirSync).mockReturnValueOnce(['a.json', 'b.json', 'c.json']);
|
|
132
|
+
const stats = getCacheStats();
|
|
133
|
+
expect(stats.size).toBe(3);
|
|
134
|
+
expect(stats.ttl).toBe(10 * 60 * 1000);
|
|
135
|
+
expect(stats.maxSize).toBe(50);
|
|
136
|
+
expect(stats.cacheDir).toContain('gemini-mcp-chunks');
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
//# sourceMappingURL=chunkCache.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chunkCache.test.js","sourceRoot":"","sources":["../../../src/__tests__/utils/chunkCache.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commandExecutor.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/utils/commandExecutor.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import { EventEmitter } from 'node:events';
|
|
3
|
+
// Mock logger to suppress output
|
|
4
|
+
vi.mock('../../utils/logger.js', () => ({
|
|
5
|
+
Logger: {
|
|
6
|
+
debug: vi.fn(),
|
|
7
|
+
warn: vi.fn(),
|
|
8
|
+
error: vi.fn(),
|
|
9
|
+
log: vi.fn(),
|
|
10
|
+
commandExecution: vi.fn(),
|
|
11
|
+
commandComplete: vi.fn(),
|
|
12
|
+
},
|
|
13
|
+
}));
|
|
14
|
+
// Create a mock spawn that returns controllable process objects
|
|
15
|
+
const mockSpawn = vi.fn();
|
|
16
|
+
vi.mock('child_process', () => ({
|
|
17
|
+
spawn: (...args) => mockSpawn(...args),
|
|
18
|
+
}));
|
|
19
|
+
// Import after mocks are set up
|
|
20
|
+
import { executeCommand } from '../../utils/commandExecutor.js';
|
|
21
|
+
function createMockChildProcess() {
|
|
22
|
+
const proc = new EventEmitter();
|
|
23
|
+
proc.stdout = new EventEmitter();
|
|
24
|
+
proc.stderr = new EventEmitter();
|
|
25
|
+
return proc;
|
|
26
|
+
}
|
|
27
|
+
describe('executeCommand', () => {
|
|
28
|
+
beforeEach(() => {
|
|
29
|
+
vi.clearAllMocks();
|
|
30
|
+
});
|
|
31
|
+
it('should return trimmed stdout on success', async () => {
|
|
32
|
+
const mockProc = createMockChildProcess();
|
|
33
|
+
mockSpawn.mockReturnValue(mockProc);
|
|
34
|
+
const promise = executeCommand('echo', ['hello']);
|
|
35
|
+
// Simulate stdout data and successful close
|
|
36
|
+
mockProc.stdout.emit('data', ' hello world ');
|
|
37
|
+
mockProc.emit('close', 0);
|
|
38
|
+
const result = await promise;
|
|
39
|
+
expect(result).toBe('hello world');
|
|
40
|
+
});
|
|
41
|
+
it('should reject with error message on non-zero exit code', async () => {
|
|
42
|
+
const mockProc = createMockChildProcess();
|
|
43
|
+
mockSpawn.mockReturnValue(mockProc);
|
|
44
|
+
const promise = executeCommand('failing-cmd', []);
|
|
45
|
+
mockProc.stderr.emit('data', 'something went wrong');
|
|
46
|
+
mockProc.emit('close', 1);
|
|
47
|
+
await expect(promise).rejects.toThrow('Command failed with exit code 1: something went wrong');
|
|
48
|
+
});
|
|
49
|
+
it('should reject with descriptive error on process error event', async () => {
|
|
50
|
+
const mockProc = createMockChildProcess();
|
|
51
|
+
mockSpawn.mockReturnValue(mockProc);
|
|
52
|
+
const promise = executeCommand('nonexistent', []);
|
|
53
|
+
mockProc.emit('error', new Error('ENOENT'));
|
|
54
|
+
await expect(promise).rejects.toThrow('Failed to spawn command: ENOENT');
|
|
55
|
+
});
|
|
56
|
+
it('should call onProgress with each stdout chunk', async () => {
|
|
57
|
+
const mockProc = createMockChildProcess();
|
|
58
|
+
mockSpawn.mockReturnValue(mockProc);
|
|
59
|
+
const progressFn = vi.fn();
|
|
60
|
+
const promise = executeCommand('cmd', [], progressFn);
|
|
61
|
+
mockProc.stdout.emit('data', 'chunk1');
|
|
62
|
+
mockProc.stdout.emit('data', 'chunk2');
|
|
63
|
+
mockProc.emit('close', 0);
|
|
64
|
+
await promise;
|
|
65
|
+
expect(progressFn).toHaveBeenCalledTimes(2);
|
|
66
|
+
expect(progressFn).toHaveBeenCalledWith('chunk1');
|
|
67
|
+
expect(progressFn).toHaveBeenCalledWith('chunk2');
|
|
68
|
+
});
|
|
69
|
+
it('should handle Buffer data from stdout', async () => {
|
|
70
|
+
const mockProc = createMockChildProcess();
|
|
71
|
+
mockSpawn.mockReturnValue(mockProc);
|
|
72
|
+
const promise = executeCommand('cmd', []);
|
|
73
|
+
mockProc.stdout.emit('data', Buffer.from('buffer data'));
|
|
74
|
+
mockProc.emit('close', 0);
|
|
75
|
+
const result = await promise;
|
|
76
|
+
expect(result).toBe('buffer data');
|
|
77
|
+
});
|
|
78
|
+
it('should log RESOURCE_EXHAUSTED from stderr', async () => {
|
|
79
|
+
const { Logger } = await import('../../utils/logger.js');
|
|
80
|
+
const mockProc = createMockChildProcess();
|
|
81
|
+
mockSpawn.mockReturnValue(mockProc);
|
|
82
|
+
const promise = executeCommand('gemini', ['-p', 'test']);
|
|
83
|
+
mockProc.stderr.emit('data', 'RESOURCE_EXHAUSTED: Quota exceeded');
|
|
84
|
+
mockProc.emit('close', 1);
|
|
85
|
+
await expect(promise).rejects.toThrow();
|
|
86
|
+
expect(Logger.error).toHaveBeenCalled();
|
|
87
|
+
});
|
|
88
|
+
it('should reject with "Unknown error" when stderr is empty', async () => {
|
|
89
|
+
const mockProc = createMockChildProcess();
|
|
90
|
+
mockSpawn.mockReturnValue(mockProc);
|
|
91
|
+
const promise = executeCommand('cmd', []);
|
|
92
|
+
mockProc.emit('close', 1);
|
|
93
|
+
await expect(promise).rejects.toThrow('Command failed with exit code 1: Unknown error');
|
|
94
|
+
});
|
|
95
|
+
it('should only resolve once even if close fires after error', async () => {
|
|
96
|
+
const mockProc = createMockChildProcess();
|
|
97
|
+
mockSpawn.mockReturnValue(mockProc);
|
|
98
|
+
const promise = executeCommand('cmd', []);
|
|
99
|
+
mockProc.emit('error', new Error('process error'));
|
|
100
|
+
// Close event after error should be ignored
|
|
101
|
+
mockProc.emit('close', 0);
|
|
102
|
+
await expect(promise).rejects.toThrow('Failed to spawn command: process error');
|
|
103
|
+
});
|
|
104
|
+
it('should pass correct args to spawn', async () => {
|
|
105
|
+
const mockProc = createMockChildProcess();
|
|
106
|
+
mockSpawn.mockReturnValue(mockProc);
|
|
107
|
+
const promise = executeCommand('gemini', ['-m', 'pro', '-p', 'test']);
|
|
108
|
+
mockProc.emit('close', 0);
|
|
109
|
+
await promise;
|
|
110
|
+
expect(mockSpawn).toHaveBeenCalledWith('gemini', ['-m', 'pro', '-p', 'test'], {
|
|
111
|
+
env: process.env,
|
|
112
|
+
shell: false,
|
|
113
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
it('should pass cwd to spawn when provided', async () => {
|
|
117
|
+
const mockProc = createMockChildProcess();
|
|
118
|
+
mockSpawn.mockReturnValue(mockProc);
|
|
119
|
+
const promise = executeCommand('gemini', ['-p', 'test'], undefined, '/some/dir');
|
|
120
|
+
mockProc.emit('close', 0);
|
|
121
|
+
await promise;
|
|
122
|
+
expect(mockSpawn).toHaveBeenCalledWith('gemini', ['-p', 'test'], {
|
|
123
|
+
env: process.env,
|
|
124
|
+
shell: false,
|
|
125
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
126
|
+
cwd: '/some/dir',
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
it('should not include cwd in spawn options when undefined', async () => {
|
|
130
|
+
const mockProc = createMockChildProcess();
|
|
131
|
+
mockSpawn.mockReturnValue(mockProc);
|
|
132
|
+
const promise = executeCommand('gemini', ['-p', 'test']);
|
|
133
|
+
mockProc.emit('close', 0);
|
|
134
|
+
await promise;
|
|
135
|
+
const spawnOptions = mockSpawn.mock.calls[0][2];
|
|
136
|
+
expect(spawnOptions).not.toHaveProperty('cwd');
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
//# sourceMappingURL=commandExecutor.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commandExecutor.test.js","sourceRoot":"","sources":["../../../src/__tests__/utils/commandExecutor.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"geminiExecutor.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/utils/geminiExecutor.test.ts"],"names":[],"mappings":""}
|