@brookmind/ai-toolkit 1.2.0 → 1.2.2
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/README.md +8 -12
- package/dist/__tests__/constants.test.d.ts +2 -0
- package/dist/__tests__/constants.test.d.ts.map +1 -0
- package/dist/__tests__/constants.test.js +102 -0
- package/dist/__tests__/constants.test.js.map +1 -0
- package/dist/__tests__/index.test.js +6 -105
- package/dist/__tests__/index.test.js.map +1 -1
- package/dist/__tests__/integration/installer.test.d.ts +2 -0
- package/dist/__tests__/integration/installer.test.d.ts.map +1 -0
- package/dist/__tests__/integration/installer.test.js +450 -0
- package/dist/__tests__/integration/installer.test.js.map +1 -0
- package/dist/__tests__/services/installers.test.d.ts +2 -0
- package/dist/__tests__/services/installers.test.d.ts.map +1 -0
- package/dist/__tests__/services/installers.test.js +250 -0
- package/dist/__tests__/services/installers.test.js.map +1 -0
- package/dist/__tests__/services/opencode.test.d.ts +2 -0
- package/dist/__tests__/services/opencode.test.d.ts.map +1 -0
- package/dist/__tests__/services/opencode.test.js +120 -0
- package/dist/__tests__/services/opencode.test.js.map +1 -0
- package/dist/__tests__/ui/categorize.test.d.ts +2 -0
- package/dist/__tests__/ui/categorize.test.d.ts.map +1 -0
- package/dist/__tests__/ui/categorize.test.js +225 -0
- package/dist/__tests__/ui/categorize.test.js.map +1 -0
- package/dist/__tests__/ui/choices.test.d.ts +2 -0
- package/dist/__tests__/ui/choices.test.d.ts.map +1 -0
- package/dist/__tests__/ui/choices.test.js +223 -0
- package/dist/__tests__/ui/choices.test.js.map +1 -0
- package/dist/__tests__/ui/display.test.d.ts +2 -0
- package/dist/__tests__/ui/display.test.d.ts.map +1 -0
- package/dist/__tests__/ui/display.test.js +138 -0
- package/dist/__tests__/ui/display.test.js.map +1 -0
- package/dist/__tests__/utils/fs.test.d.ts +2 -0
- package/dist/__tests__/utils/fs.test.d.ts.map +1 -0
- package/dist/__tests__/utils/fs.test.js +301 -0
- package/dist/__tests__/utils/fs.test.js.map +1 -0
- package/dist/__tests__/utils/terminal.test.d.ts +2 -0
- package/dist/__tests__/utils/terminal.test.d.ts.map +1 -0
- package/dist/__tests__/utils/terminal.test.js +97 -0
- package/dist/__tests__/utils/terminal.test.js.map +1 -0
- package/dist/constants.d.ts +1 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +3 -0
- package/dist/constants.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +24 -29
- package/dist/index.js.map +1 -1
- package/dist/services/installers.d.ts +1 -1
- package/dist/services/installers.d.ts.map +1 -1
- package/dist/services/installers.js +21 -14
- package/dist/services/installers.js.map +1 -1
- package/dist/types.d.ts +15 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +28 -1
- package/dist/types.js.map +1 -1
- package/dist/ui/categorize.d.ts +1 -0
- package/dist/ui/categorize.d.ts.map +1 -1
- package/dist/ui/categorize.js +15 -3
- package/dist/ui/categorize.js.map +1 -1
- package/dist/ui/choices.d.ts +1 -0
- package/dist/ui/choices.d.ts.map +1 -1
- package/dist/ui/choices.js +14 -3
- package/dist/ui/choices.js.map +1 -1
- package/dist/ui/display.d.ts +1 -2
- package/dist/ui/display.d.ts.map +1 -1
- package/dist/ui/display.js +14 -17
- package/dist/ui/display.js.map +1 -1
- package/dist/utils/fs.d.ts +14 -0
- package/dist/utils/fs.d.ts.map +1 -1
- package/dist/utils/fs.js +72 -2
- package/dist/utils/fs.js.map +1 -1
- package/package.json +4 -2
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
2
|
+
vi.mock('fs/promises', async () => {
|
|
3
|
+
const actual = await vi.importActual('fs/promises');
|
|
4
|
+
return {
|
|
5
|
+
...actual,
|
|
6
|
+
cp: vi.fn(),
|
|
7
|
+
mkdir: vi.fn(),
|
|
8
|
+
readFile: vi.fn(),
|
|
9
|
+
writeFile: vi.fn(),
|
|
10
|
+
};
|
|
11
|
+
});
|
|
12
|
+
vi.mock('../../constants.js', () => {
|
|
13
|
+
const testDir = '/tmp/ai-toolkit-test';
|
|
14
|
+
return {
|
|
15
|
+
OPENCODE_DIR: `${testDir}/opencode`,
|
|
16
|
+
OPENCODE_CONFIG: `${testDir}/opencode/opencode.json`,
|
|
17
|
+
TOOLS_DIR: `${testDir}/tools`,
|
|
18
|
+
};
|
|
19
|
+
});
|
|
20
|
+
vi.mock('../../utils/fs.js', () => ({
|
|
21
|
+
exists: vi.fn(),
|
|
22
|
+
readOpencodeConfig: vi.fn(),
|
|
23
|
+
readMcpConfig: vi.fn(),
|
|
24
|
+
}));
|
|
25
|
+
describe('services/installers', () => {
|
|
26
|
+
let mockCp;
|
|
27
|
+
let mockMkdir;
|
|
28
|
+
let mockReadFile;
|
|
29
|
+
let mockWriteFile;
|
|
30
|
+
let mockExists;
|
|
31
|
+
let mockReadOpencodeConfig;
|
|
32
|
+
let mockReadMcpConfig;
|
|
33
|
+
beforeEach(async () => {
|
|
34
|
+
vi.resetAllMocks();
|
|
35
|
+
const fsPromises = await import('fs/promises');
|
|
36
|
+
mockCp = fsPromises.cp;
|
|
37
|
+
mockMkdir = fsPromises.mkdir;
|
|
38
|
+
mockReadFile = fsPromises.readFile;
|
|
39
|
+
mockWriteFile = fsPromises.writeFile;
|
|
40
|
+
const fsUtils = await import('../../utils/fs.js');
|
|
41
|
+
mockExists = fsUtils.exists;
|
|
42
|
+
mockReadOpencodeConfig = fsUtils.readOpencodeConfig;
|
|
43
|
+
mockReadMcpConfig = fsUtils.readMcpConfig;
|
|
44
|
+
mockMkdir.mockResolvedValue(undefined);
|
|
45
|
+
mockCp.mockResolvedValue(undefined);
|
|
46
|
+
mockWriteFile.mockResolvedValue(undefined);
|
|
47
|
+
mockReadFile.mockResolvedValue('{}');
|
|
48
|
+
mockExists.mockResolvedValue(false);
|
|
49
|
+
mockReadOpencodeConfig.mockResolvedValue({});
|
|
50
|
+
mockReadMcpConfig.mockResolvedValue({});
|
|
51
|
+
});
|
|
52
|
+
afterEach(() => {
|
|
53
|
+
vi.clearAllMocks();
|
|
54
|
+
});
|
|
55
|
+
describe('installAgents', () => {
|
|
56
|
+
it('should do nothing when agents array is empty', async () => {
|
|
57
|
+
const { installAgents } = await import('../../services/installers.js');
|
|
58
|
+
await installAgents([]);
|
|
59
|
+
expect(mockMkdir).not.toHaveBeenCalled();
|
|
60
|
+
expect(mockCp).not.toHaveBeenCalled();
|
|
61
|
+
});
|
|
62
|
+
it('should create directory and copy agent files', async () => {
|
|
63
|
+
const { installAgents } = await import('../../services/installers.js');
|
|
64
|
+
await installAgents(['test-agent']);
|
|
65
|
+
expect(mockMkdir).toHaveBeenCalledWith(expect.stringContaining('opencode/agent'), {
|
|
66
|
+
recursive: true,
|
|
67
|
+
});
|
|
68
|
+
expect(mockCp).toHaveBeenCalledWith(expect.stringContaining('tools/agents/test-agent.md'), expect.stringContaining('opencode/agent/test-agent.md'));
|
|
69
|
+
});
|
|
70
|
+
it('should install multiple agents', async () => {
|
|
71
|
+
const { installAgents } = await import('../../services/installers.js');
|
|
72
|
+
await installAgents(['agent1', 'agent2', 'agent3']);
|
|
73
|
+
expect(mockCp).toHaveBeenCalledTimes(3);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
describe('installSkills', () => {
|
|
77
|
+
it('should do nothing when skills array is empty', async () => {
|
|
78
|
+
const { installSkills } = await import('../../services/installers.js');
|
|
79
|
+
await installSkills([]);
|
|
80
|
+
expect(mockMkdir).not.toHaveBeenCalled();
|
|
81
|
+
expect(mockCp).not.toHaveBeenCalled();
|
|
82
|
+
});
|
|
83
|
+
it('should create directory and copy skill folder', async () => {
|
|
84
|
+
const { installSkills } = await import('../../services/installers.js');
|
|
85
|
+
await installSkills(['test-skill']);
|
|
86
|
+
expect(mockMkdir).toHaveBeenCalledWith(expect.stringContaining('opencode/skill'), {
|
|
87
|
+
recursive: true,
|
|
88
|
+
});
|
|
89
|
+
expect(mockCp).toHaveBeenCalledWith(expect.stringContaining('tools/skills/test-skill'), expect.stringContaining('opencode/skill/test-skill'), { recursive: true, force: true });
|
|
90
|
+
});
|
|
91
|
+
it('should install multiple skills', async () => {
|
|
92
|
+
const { installSkills } = await import('../../services/installers.js');
|
|
93
|
+
await installSkills(['skill1', 'skill2']);
|
|
94
|
+
expect(mockCp).toHaveBeenCalledTimes(2);
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
describe('installCommands', () => {
|
|
98
|
+
it('should do nothing when commands array is empty', async () => {
|
|
99
|
+
const { installCommands } = await import('../../services/installers.js');
|
|
100
|
+
await installCommands([]);
|
|
101
|
+
expect(mockMkdir).not.toHaveBeenCalled();
|
|
102
|
+
expect(mockCp).not.toHaveBeenCalled();
|
|
103
|
+
});
|
|
104
|
+
it('should create directory and copy command files', async () => {
|
|
105
|
+
const { installCommands } = await import('../../services/installers.js');
|
|
106
|
+
await installCommands(['test-cmd']);
|
|
107
|
+
expect(mockMkdir).toHaveBeenCalledWith(expect.stringContaining('opencode/command'), {
|
|
108
|
+
recursive: true,
|
|
109
|
+
});
|
|
110
|
+
expect(mockCp).toHaveBeenCalledWith(expect.stringContaining('tools/commands/test-cmd.md'), expect.stringContaining('opencode/command/test-cmd.md'));
|
|
111
|
+
});
|
|
112
|
+
it('should install multiple commands', async () => {
|
|
113
|
+
const { installCommands } = await import('../../services/installers.js');
|
|
114
|
+
await installCommands(['cmd1', 'cmd2']);
|
|
115
|
+
expect(mockCp).toHaveBeenCalledTimes(2);
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
describe('installMcps', () => {
|
|
119
|
+
it('should do nothing when mcps array is empty', async () => {
|
|
120
|
+
const { installMcps } = await import('../../services/installers.js');
|
|
121
|
+
await installMcps([]);
|
|
122
|
+
expect(mockWriteFile).not.toHaveBeenCalled();
|
|
123
|
+
});
|
|
124
|
+
it('should create mcp section if it does not exist', async () => {
|
|
125
|
+
const { installMcps } = await import('../../services/installers.js');
|
|
126
|
+
mockReadOpencodeConfig.mockResolvedValue({});
|
|
127
|
+
mockReadMcpConfig.mockResolvedValue({ mcpServers: { 'test-server': { command: 'test' } } });
|
|
128
|
+
await installMcps(['test-mcp']);
|
|
129
|
+
expect(mockWriteFile).toHaveBeenCalledWith(expect.stringContaining('opencode.json'), expect.stringContaining('"test-server"'));
|
|
130
|
+
});
|
|
131
|
+
it('should merge servers into existing mcp section', async () => {
|
|
132
|
+
const { installMcps } = await import('../../services/installers.js');
|
|
133
|
+
mockReadOpencodeConfig.mockResolvedValue({ mcp: { 'existing-server': {} } });
|
|
134
|
+
mockReadMcpConfig.mockResolvedValue({ mcpServers: { 'new-server': {} } });
|
|
135
|
+
await installMcps(['test-mcp']);
|
|
136
|
+
expect(mockWriteFile).toHaveBeenCalledWith(expect.stringContaining('opencode.json'), expect.stringMatching(/"existing-server".*"new-server"|"new-server".*"existing-server"/s));
|
|
137
|
+
});
|
|
138
|
+
it('should handle mcps without servers', async () => {
|
|
139
|
+
const { installMcps } = await import('../../services/installers.js');
|
|
140
|
+
mockReadOpencodeConfig.mockResolvedValue({});
|
|
141
|
+
mockReadMcpConfig.mockResolvedValue({});
|
|
142
|
+
await installMcps(['test-mcp']);
|
|
143
|
+
expect(mockWriteFile).toHaveBeenCalled();
|
|
144
|
+
});
|
|
145
|
+
it('should install multiple mcps', async () => {
|
|
146
|
+
const { installMcps } = await import('../../services/installers.js');
|
|
147
|
+
mockReadOpencodeConfig.mockResolvedValue({});
|
|
148
|
+
mockReadMcpConfig
|
|
149
|
+
.mockResolvedValueOnce({ mcpServers: { server1: {} } })
|
|
150
|
+
.mockResolvedValueOnce({ mcpServers: { server2: {} } });
|
|
151
|
+
await installMcps(['mcp1', 'mcp2']);
|
|
152
|
+
expect(mockWriteFile).toHaveBeenCalled();
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
describe('installThemes', () => {
|
|
156
|
+
it('should do nothing when themes array is empty', async () => {
|
|
157
|
+
const { installThemes } = await import('../../services/installers.js');
|
|
158
|
+
await installThemes([]);
|
|
159
|
+
expect(mockMkdir).not.toHaveBeenCalled();
|
|
160
|
+
expect(mockCp).not.toHaveBeenCalled();
|
|
161
|
+
});
|
|
162
|
+
it('should copy themes and update config', async () => {
|
|
163
|
+
const { installThemes } = await import('../../services/installers.js');
|
|
164
|
+
mockReadOpencodeConfig.mockResolvedValue({});
|
|
165
|
+
await installThemes(['claude-vivid']);
|
|
166
|
+
expect(mockMkdir).toHaveBeenCalledWith(expect.stringContaining('opencode/themes'), {
|
|
167
|
+
recursive: true,
|
|
168
|
+
});
|
|
169
|
+
expect(mockCp).toHaveBeenCalledWith(expect.stringContaining('themes/claude-vivid.json'), expect.stringContaining('opencode/themes/claude-vivid.json'));
|
|
170
|
+
expect(mockWriteFile).toHaveBeenCalledWith(expect.stringContaining('opencode.json'), expect.stringContaining('claude-vivid'));
|
|
171
|
+
});
|
|
172
|
+
it('should install multiple themes and set first as active', async () => {
|
|
173
|
+
const { installThemes } = await import('../../services/installers.js');
|
|
174
|
+
mockReadOpencodeConfig.mockResolvedValue({});
|
|
175
|
+
await installThemes(['theme1', 'theme2']);
|
|
176
|
+
expect(mockCp).toHaveBeenCalledTimes(2);
|
|
177
|
+
expect(mockWriteFile).toHaveBeenCalledWith(expect.stringContaining('opencode.json'), expect.stringContaining('theme1'));
|
|
178
|
+
});
|
|
179
|
+
it('should not update config when first theme is empty string', async () => {
|
|
180
|
+
const { installThemes } = await import('../../services/installers.js');
|
|
181
|
+
mockReadOpencodeConfig.mockResolvedValue({});
|
|
182
|
+
// Empty string is falsy, so firstTheme check will fail
|
|
183
|
+
await installThemes(['']);
|
|
184
|
+
expect(mockCp).toHaveBeenCalledTimes(1);
|
|
185
|
+
expect(mockWriteFile).not.toHaveBeenCalled();
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
describe('isThemeInstalled', () => {
|
|
189
|
+
it('should return true when theme exists', async () => {
|
|
190
|
+
const { isThemeInstalled } = await import('../../services/installers.js');
|
|
191
|
+
mockExists.mockResolvedValue(true);
|
|
192
|
+
const result = await isThemeInstalled('claude-vivid');
|
|
193
|
+
expect(result).toBe(true);
|
|
194
|
+
expect(mockExists).toHaveBeenCalledWith(expect.stringContaining('opencode/themes/claude-vivid.json'));
|
|
195
|
+
});
|
|
196
|
+
it('should return false when theme does not exist', async () => {
|
|
197
|
+
const { isThemeInstalled } = await import('../../services/installers.js');
|
|
198
|
+
mockExists.mockResolvedValue(false);
|
|
199
|
+
const result = await isThemeInstalled('nonexistent');
|
|
200
|
+
expect(result).toBe(false);
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
describe('getInstalledMcps', () => {
|
|
204
|
+
it('should return empty set when config does not exist', async () => {
|
|
205
|
+
const { getInstalledMcps } = await import('../../services/installers.js');
|
|
206
|
+
mockReadFile.mockRejectedValue(new Error('ENOENT'));
|
|
207
|
+
const result = await getInstalledMcps();
|
|
208
|
+
expect(result).toEqual(new Set());
|
|
209
|
+
});
|
|
210
|
+
it('should return empty set when config has no mcp section', async () => {
|
|
211
|
+
const { getInstalledMcps } = await import('../../services/installers.js');
|
|
212
|
+
mockReadFile.mockResolvedValue(JSON.stringify({ theme: 'test' }));
|
|
213
|
+
const result = await getInstalledMcps();
|
|
214
|
+
expect(result).toEqual(new Set());
|
|
215
|
+
});
|
|
216
|
+
it('should return server names from mcp section', async () => {
|
|
217
|
+
const { getInstalledMcps } = await import('../../services/installers.js');
|
|
218
|
+
mockReadFile.mockResolvedValue(JSON.stringify({
|
|
219
|
+
mcp: { server1: {}, server2: {} },
|
|
220
|
+
}));
|
|
221
|
+
const result = await getInstalledMcps();
|
|
222
|
+
expect(result).toEqual(new Set(['server1', 'server2']));
|
|
223
|
+
});
|
|
224
|
+
it('should return empty set on JSON parse error', async () => {
|
|
225
|
+
const { getInstalledMcps } = await import('../../services/installers.js');
|
|
226
|
+
mockReadFile.mockResolvedValue('invalid json');
|
|
227
|
+
const result = await getInstalledMcps();
|
|
228
|
+
expect(result).toEqual(new Set());
|
|
229
|
+
});
|
|
230
|
+
it('should handle empty mcp section', async () => {
|
|
231
|
+
const { getInstalledMcps } = await import('../../services/installers.js');
|
|
232
|
+
mockReadFile.mockResolvedValue(JSON.stringify({ mcp: {} }));
|
|
233
|
+
const result = await getInstalledMcps();
|
|
234
|
+
expect(result).toEqual(new Set());
|
|
235
|
+
});
|
|
236
|
+
it('should return empty set when config has invalid structure (mcp is not an object)', async () => {
|
|
237
|
+
const { getInstalledMcps } = await import('../../services/installers.js');
|
|
238
|
+
mockReadFile.mockResolvedValue(JSON.stringify({ mcp: 'not-an-object' }));
|
|
239
|
+
const result = await getInstalledMcps();
|
|
240
|
+
expect(result).toEqual(new Set());
|
|
241
|
+
});
|
|
242
|
+
it('should return empty set when config has invalid structure (theme is not a string)', async () => {
|
|
243
|
+
const { getInstalledMcps } = await import('../../services/installers.js');
|
|
244
|
+
mockReadFile.mockResolvedValue(JSON.stringify({ theme: 123, mcp: { server1: {} } }));
|
|
245
|
+
const result = await getInstalledMcps();
|
|
246
|
+
expect(result).toEqual(new Set());
|
|
247
|
+
});
|
|
248
|
+
});
|
|
249
|
+
});
|
|
250
|
+
//# sourceMappingURL=installers.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"installers.test.js","sourceRoot":"","sources":["../../../src/__tests__/services/installers.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"opencode.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/services/opencode.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
2
|
+
import { exec } from 'child_process';
|
|
3
|
+
import { isOpenCodeInstalled, installOpenCode } from '../../services/opencode.js';
|
|
4
|
+
vi.mock('child_process', () => ({
|
|
5
|
+
exec: vi.fn(),
|
|
6
|
+
}));
|
|
7
|
+
vi.mock('../../utils/fs.js', () => ({
|
|
8
|
+
exists: vi.fn(),
|
|
9
|
+
}));
|
|
10
|
+
vi.mock('ora', () => ({
|
|
11
|
+
default: vi.fn(() => ({
|
|
12
|
+
start: vi.fn().mockReturnThis(),
|
|
13
|
+
succeed: vi.fn().mockReturnThis(),
|
|
14
|
+
fail: vi.fn().mockReturnThis(),
|
|
15
|
+
})),
|
|
16
|
+
}));
|
|
17
|
+
describe('services/opencode', () => {
|
|
18
|
+
let mockExec;
|
|
19
|
+
let mockExists;
|
|
20
|
+
let mockOra;
|
|
21
|
+
let consoleLogSpy;
|
|
22
|
+
beforeEach(async () => {
|
|
23
|
+
vi.resetAllMocks();
|
|
24
|
+
mockExec = exec;
|
|
25
|
+
const fsModule = await import('../../utils/fs.js');
|
|
26
|
+
mockExists = fsModule.exists;
|
|
27
|
+
const oraModule = await import('ora');
|
|
28
|
+
mockOra = oraModule.default;
|
|
29
|
+
consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(() => undefined);
|
|
30
|
+
});
|
|
31
|
+
afterEach(() => {
|
|
32
|
+
consoleLogSpy.mockRestore();
|
|
33
|
+
});
|
|
34
|
+
describe('isOpenCodeInstalled', () => {
|
|
35
|
+
it('should return true when which opencode succeeds', async () => {
|
|
36
|
+
mockExec.mockImplementation((_cmd, callback) => {
|
|
37
|
+
if (callback) {
|
|
38
|
+
callback(null, { stdout: '/usr/local/bin/opencode', stderr: '' });
|
|
39
|
+
}
|
|
40
|
+
return undefined;
|
|
41
|
+
});
|
|
42
|
+
const result = await isOpenCodeInstalled();
|
|
43
|
+
expect(result).toBe(true);
|
|
44
|
+
});
|
|
45
|
+
it('should check directory when which fails', async () => {
|
|
46
|
+
mockExec.mockImplementation((_cmd, callback) => {
|
|
47
|
+
if (callback) {
|
|
48
|
+
callback(new Error('not found'), { stdout: '', stderr: '' });
|
|
49
|
+
}
|
|
50
|
+
return undefined;
|
|
51
|
+
});
|
|
52
|
+
mockExists.mockResolvedValue(true);
|
|
53
|
+
const result = await isOpenCodeInstalled();
|
|
54
|
+
expect(result).toBe(true);
|
|
55
|
+
expect(mockExists).toHaveBeenCalled();
|
|
56
|
+
});
|
|
57
|
+
it('should return false when both which and directory check fail', async () => {
|
|
58
|
+
mockExec.mockImplementation((_cmd, callback) => {
|
|
59
|
+
if (callback) {
|
|
60
|
+
callback(new Error('not found'), { stdout: '', stderr: '' });
|
|
61
|
+
}
|
|
62
|
+
return undefined;
|
|
63
|
+
});
|
|
64
|
+
mockExists.mockResolvedValue(false);
|
|
65
|
+
const result = await isOpenCodeInstalled();
|
|
66
|
+
expect(result).toBe(false);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
describe('installOpenCode', () => {
|
|
70
|
+
it('should install successfully and show spinner', async () => {
|
|
71
|
+
const spinnerMock = {
|
|
72
|
+
start: vi.fn().mockReturnThis(),
|
|
73
|
+
succeed: vi.fn().mockReturnThis(),
|
|
74
|
+
fail: vi.fn().mockReturnThis(),
|
|
75
|
+
};
|
|
76
|
+
mockOra.mockReturnValue(spinnerMock);
|
|
77
|
+
mockExec.mockImplementation((_cmd, _options, callback) => {
|
|
78
|
+
if (callback) {
|
|
79
|
+
callback(null, { stdout: 'success', stderr: '' });
|
|
80
|
+
}
|
|
81
|
+
return undefined;
|
|
82
|
+
});
|
|
83
|
+
await installOpenCode();
|
|
84
|
+
expect(spinnerMock.start).toHaveBeenCalled();
|
|
85
|
+
expect(spinnerMock.succeed).toHaveBeenCalledWith('OpenCode installed successfully!');
|
|
86
|
+
});
|
|
87
|
+
it('should fail and throw error on installation failure', async () => {
|
|
88
|
+
const spinnerMock = {
|
|
89
|
+
start: vi.fn().mockReturnThis(),
|
|
90
|
+
succeed: vi.fn().mockReturnThis(),
|
|
91
|
+
fail: vi.fn().mockReturnThis(),
|
|
92
|
+
};
|
|
93
|
+
mockOra.mockReturnValue(spinnerMock);
|
|
94
|
+
mockExec.mockImplementation((_cmd, _options, callback) => {
|
|
95
|
+
if (callback) {
|
|
96
|
+
callback(new Error('curl failed'), { stdout: '', stderr: '' });
|
|
97
|
+
}
|
|
98
|
+
return undefined;
|
|
99
|
+
});
|
|
100
|
+
await expect(installOpenCode()).rejects.toThrow('Failed to install OpenCode: curl failed');
|
|
101
|
+
expect(spinnerMock.fail).toHaveBeenCalledWith('Installation failed');
|
|
102
|
+
});
|
|
103
|
+
it('should handle non-Error exceptions', async () => {
|
|
104
|
+
const spinnerMock = {
|
|
105
|
+
start: vi.fn().mockReturnThis(),
|
|
106
|
+
succeed: vi.fn().mockReturnThis(),
|
|
107
|
+
fail: vi.fn().mockReturnThis(),
|
|
108
|
+
};
|
|
109
|
+
mockOra.mockReturnValue(spinnerMock);
|
|
110
|
+
mockExec.mockImplementation((_cmd, _options, callback) => {
|
|
111
|
+
if (callback) {
|
|
112
|
+
callback('string error', { stdout: '', stderr: '' });
|
|
113
|
+
}
|
|
114
|
+
return undefined;
|
|
115
|
+
});
|
|
116
|
+
await expect(installOpenCode()).rejects.toThrow('Failed to install OpenCode: string error');
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
//# sourceMappingURL=opencode.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"opencode.test.js","sourceRoot":"","sources":["../../../src/__tests__/services/opencode.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"categorize.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/ui/categorize.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
2
|
+
import { categorizeAgents, categorizeSkills, categorizeCommands, categorizeMcps, categorizeThemes, } from '../../ui/categorize.js';
|
|
3
|
+
vi.mock('../../utils/fs.js', () => ({
|
|
4
|
+
exists: vi.fn(),
|
|
5
|
+
readMcpConfig: vi.fn(),
|
|
6
|
+
}));
|
|
7
|
+
vi.mock('../../services/installers.js', () => ({
|
|
8
|
+
getInstalledMcps: vi.fn(),
|
|
9
|
+
isThemeInstalled: vi.fn(),
|
|
10
|
+
}));
|
|
11
|
+
vi.mock('../../constants.js', async (importOriginal) => {
|
|
12
|
+
const original = await importOriginal();
|
|
13
|
+
return {
|
|
14
|
+
...original,
|
|
15
|
+
MCP_DISPLAY_NAMES: {
|
|
16
|
+
'pg-aiguide': 'postgres-mcp',
|
|
17
|
+
},
|
|
18
|
+
OPENCODE_DIR: '/fake/opencode',
|
|
19
|
+
TOOLS_DIR: '/fake/tools',
|
|
20
|
+
};
|
|
21
|
+
});
|
|
22
|
+
describe('ui/categorize', () => {
|
|
23
|
+
let mockExists;
|
|
24
|
+
let mockReadMcpConfig;
|
|
25
|
+
let mockGetInstalledMcps;
|
|
26
|
+
let mockIsThemeInstalled;
|
|
27
|
+
beforeEach(async () => {
|
|
28
|
+
vi.resetAllMocks();
|
|
29
|
+
const fsModule = await import('../../utils/fs.js');
|
|
30
|
+
mockExists = fsModule.exists;
|
|
31
|
+
mockReadMcpConfig = fsModule.readMcpConfig;
|
|
32
|
+
const installersModule = await import('../../services/installers.js');
|
|
33
|
+
mockGetInstalledMcps = installersModule.getInstalledMcps;
|
|
34
|
+
mockIsThemeInstalled = installersModule.isThemeInstalled;
|
|
35
|
+
mockExists.mockResolvedValue(false);
|
|
36
|
+
mockReadMcpConfig.mockResolvedValue({});
|
|
37
|
+
mockGetInstalledMcps.mockResolvedValue(new Set());
|
|
38
|
+
mockIsThemeInstalled.mockResolvedValue(false);
|
|
39
|
+
});
|
|
40
|
+
afterEach(() => {
|
|
41
|
+
vi.clearAllMocks();
|
|
42
|
+
});
|
|
43
|
+
describe('categorizeAgents', () => {
|
|
44
|
+
it('should return empty arrays for empty agents', async () => {
|
|
45
|
+
const result = await categorizeAgents([]);
|
|
46
|
+
expect(result).toEqual({ new: [], update: [] });
|
|
47
|
+
});
|
|
48
|
+
it('should categorize all as new when none installed', async () => {
|
|
49
|
+
mockExists.mockResolvedValue(false);
|
|
50
|
+
const result = await categorizeAgents(['agent1', 'agent2']);
|
|
51
|
+
expect(result.new).toEqual(['agent1', 'agent2']);
|
|
52
|
+
expect(result.update).toEqual([]);
|
|
53
|
+
});
|
|
54
|
+
it('should categorize all as update when all installed', async () => {
|
|
55
|
+
mockExists.mockResolvedValue(true);
|
|
56
|
+
const result = await categorizeAgents(['agent1', 'agent2']);
|
|
57
|
+
expect(result.new).toEqual([]);
|
|
58
|
+
expect(result.update).toEqual(['agent1', 'agent2']);
|
|
59
|
+
});
|
|
60
|
+
it('should categorize mixed new and update', async () => {
|
|
61
|
+
mockExists.mockResolvedValueOnce(false).mockResolvedValueOnce(true);
|
|
62
|
+
const result = await categorizeAgents(['agent1', 'agent2']);
|
|
63
|
+
expect(result.new).toEqual(['agent1']);
|
|
64
|
+
expect(result.update).toEqual(['agent2']);
|
|
65
|
+
});
|
|
66
|
+
it('should check correct path for agents', async () => {
|
|
67
|
+
mockExists.mockResolvedValue(false);
|
|
68
|
+
await categorizeAgents(['test-agent']);
|
|
69
|
+
expect(mockExists).toHaveBeenCalledWith('/fake/opencode/agent/test-agent.md');
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
describe('categorizeSkills', () => {
|
|
73
|
+
it('should return empty arrays for empty skills', async () => {
|
|
74
|
+
const result = await categorizeSkills([]);
|
|
75
|
+
expect(result).toEqual({ new: [], update: [] });
|
|
76
|
+
});
|
|
77
|
+
it('should categorize all as new when none installed', async () => {
|
|
78
|
+
mockExists.mockResolvedValue(false);
|
|
79
|
+
const result = await categorizeSkills(['skill1', 'skill2']);
|
|
80
|
+
expect(result.new).toEqual(['skill1', 'skill2']);
|
|
81
|
+
expect(result.update).toEqual([]);
|
|
82
|
+
});
|
|
83
|
+
it('should categorize all as update when all installed', async () => {
|
|
84
|
+
mockExists.mockResolvedValue(true);
|
|
85
|
+
const result = await categorizeSkills(['skill1', 'skill2']);
|
|
86
|
+
expect(result.new).toEqual([]);
|
|
87
|
+
expect(result.update).toEqual(['skill1', 'skill2']);
|
|
88
|
+
});
|
|
89
|
+
it('should categorize mixed new and update', async () => {
|
|
90
|
+
mockExists.mockResolvedValueOnce(true).mockResolvedValueOnce(false);
|
|
91
|
+
const result = await categorizeSkills(['skill1', 'skill2']);
|
|
92
|
+
expect(result.new).toEqual(['skill2']);
|
|
93
|
+
expect(result.update).toEqual(['skill1']);
|
|
94
|
+
});
|
|
95
|
+
it('should check correct path for skills (directory, not .md)', async () => {
|
|
96
|
+
mockExists.mockResolvedValue(false);
|
|
97
|
+
await categorizeSkills(['test-skill']);
|
|
98
|
+
expect(mockExists).toHaveBeenCalledWith('/fake/opencode/skill/test-skill');
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
describe('categorizeCommands', () => {
|
|
102
|
+
it('should return empty arrays for empty commands', async () => {
|
|
103
|
+
const result = await categorizeCommands([]);
|
|
104
|
+
expect(result).toEqual({ new: [], update: [] });
|
|
105
|
+
});
|
|
106
|
+
it('should categorize all as new when none installed', async () => {
|
|
107
|
+
mockExists.mockResolvedValue(false);
|
|
108
|
+
const result = await categorizeCommands(['cmd1', 'cmd2']);
|
|
109
|
+
expect(result.new).toEqual(['cmd1', 'cmd2']);
|
|
110
|
+
expect(result.update).toEqual([]);
|
|
111
|
+
});
|
|
112
|
+
it('should categorize all as update when all installed', async () => {
|
|
113
|
+
mockExists.mockResolvedValue(true);
|
|
114
|
+
const result = await categorizeCommands(['cmd1', 'cmd2']);
|
|
115
|
+
expect(result.new).toEqual([]);
|
|
116
|
+
expect(result.update).toEqual(['cmd1', 'cmd2']);
|
|
117
|
+
});
|
|
118
|
+
it('should categorize mixed new and update', async () => {
|
|
119
|
+
mockExists
|
|
120
|
+
.mockResolvedValueOnce(false)
|
|
121
|
+
.mockResolvedValueOnce(true)
|
|
122
|
+
.mockResolvedValueOnce(false);
|
|
123
|
+
const result = await categorizeCommands(['cmd1', 'cmd2', 'cmd3']);
|
|
124
|
+
expect(result.new).toEqual(['cmd1', 'cmd3']);
|
|
125
|
+
expect(result.update).toEqual(['cmd2']);
|
|
126
|
+
});
|
|
127
|
+
it('should check correct path for commands', async () => {
|
|
128
|
+
mockExists.mockResolvedValue(false);
|
|
129
|
+
await categorizeCommands(['test-cmd']);
|
|
130
|
+
expect(mockExists).toHaveBeenCalledWith('/fake/opencode/command/test-cmd.md');
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
describe('categorizeMcps', () => {
|
|
134
|
+
it('should return empty arrays for empty mcps', async () => {
|
|
135
|
+
const result = await categorizeMcps([]);
|
|
136
|
+
expect(result).toEqual({ new: [], update: [] });
|
|
137
|
+
});
|
|
138
|
+
it('should categorize all as new when none installed', async () => {
|
|
139
|
+
mockGetInstalledMcps.mockResolvedValue(new Set());
|
|
140
|
+
mockReadMcpConfig.mockResolvedValue({
|
|
141
|
+
mcpServers: { server1: {} },
|
|
142
|
+
});
|
|
143
|
+
const result = await categorizeMcps(['mcp1', 'mcp2']);
|
|
144
|
+
expect(result.new).toHaveLength(2);
|
|
145
|
+
expect(result.update).toEqual([]);
|
|
146
|
+
});
|
|
147
|
+
it('should categorize all as update when all installed', async () => {
|
|
148
|
+
mockGetInstalledMcps.mockResolvedValue(new Set(['server1', 'server2']));
|
|
149
|
+
mockReadMcpConfig
|
|
150
|
+
.mockResolvedValueOnce({ mcpServers: { server1: {} } })
|
|
151
|
+
.mockResolvedValueOnce({ mcpServers: { server2: {} } });
|
|
152
|
+
const result = await categorizeMcps(['mcp1', 'mcp2']);
|
|
153
|
+
expect(result.new).toEqual([]);
|
|
154
|
+
expect(result.update).toHaveLength(2);
|
|
155
|
+
});
|
|
156
|
+
it('should categorize mixed new and update', async () => {
|
|
157
|
+
mockGetInstalledMcps.mockResolvedValue(new Set(['server1']));
|
|
158
|
+
mockReadMcpConfig
|
|
159
|
+
.mockResolvedValueOnce({ mcpServers: { server1: {} } })
|
|
160
|
+
.mockResolvedValueOnce({ mcpServers: { server2: {} } });
|
|
161
|
+
const result = await categorizeMcps(['mcp1', 'mcp2']);
|
|
162
|
+
expect(result.new).toHaveLength(1);
|
|
163
|
+
expect(result.update).toHaveLength(1);
|
|
164
|
+
});
|
|
165
|
+
it('should use display name from MCP_DISPLAY_NAMES', async () => {
|
|
166
|
+
mockGetInstalledMcps.mockResolvedValue(new Set());
|
|
167
|
+
mockReadMcpConfig.mockResolvedValue({
|
|
168
|
+
mcpServers: { 'pg-server': {} },
|
|
169
|
+
});
|
|
170
|
+
const result = await categorizeMcps(['pg-aiguide']);
|
|
171
|
+
expect(result.new).toEqual(['postgres-mcp']);
|
|
172
|
+
});
|
|
173
|
+
it('should use mcp name when not in MCP_DISPLAY_NAMES', async () => {
|
|
174
|
+
mockGetInstalledMcps.mockResolvedValue(new Set());
|
|
175
|
+
mockReadMcpConfig.mockResolvedValue({
|
|
176
|
+
mcpServers: { server: {} },
|
|
177
|
+
});
|
|
178
|
+
const result = await categorizeMcps(['custom-mcp']);
|
|
179
|
+
expect(result.new).toEqual(['custom-mcp']);
|
|
180
|
+
});
|
|
181
|
+
it('should handle mcps without servers', async () => {
|
|
182
|
+
mockGetInstalledMcps.mockResolvedValue(new Set(['existing']));
|
|
183
|
+
mockReadMcpConfig.mockResolvedValue({});
|
|
184
|
+
const result = await categorizeMcps(['no-servers']);
|
|
185
|
+
expect(result.new).toEqual(['no-servers']);
|
|
186
|
+
expect(result.update).toEqual([]);
|
|
187
|
+
});
|
|
188
|
+
it('should check server installation by server name', async () => {
|
|
189
|
+
mockGetInstalledMcps.mockResolvedValue(new Set(['exact-server-name']));
|
|
190
|
+
mockReadMcpConfig.mockResolvedValue({
|
|
191
|
+
mcpServers: { 'exact-server-name': { cmd: 'test' } },
|
|
192
|
+
});
|
|
193
|
+
const result = await categorizeMcps(['test-mcp']);
|
|
194
|
+
expect(result.update).toEqual(['test-mcp']);
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
describe('categorizeThemes', () => {
|
|
198
|
+
it('should return empty arrays for empty themes', async () => {
|
|
199
|
+
const result = await categorizeThemes([]);
|
|
200
|
+
expect(result).toEqual({ new: [], update: [] });
|
|
201
|
+
});
|
|
202
|
+
it('should categorize all as new when none installed', async () => {
|
|
203
|
+
mockIsThemeInstalled.mockResolvedValue(false);
|
|
204
|
+
const result = await categorizeThemes(['theme1', 'theme2']);
|
|
205
|
+
expect(result.new).toEqual(['theme1', 'theme2']);
|
|
206
|
+
expect(result.update).toEqual([]);
|
|
207
|
+
});
|
|
208
|
+
it('should categorize all as update when all installed', async () => {
|
|
209
|
+
mockIsThemeInstalled.mockResolvedValue(true);
|
|
210
|
+
const result = await categorizeThemes(['theme1', 'theme2']);
|
|
211
|
+
expect(result.new).toEqual([]);
|
|
212
|
+
expect(result.update).toEqual(['theme1', 'theme2']);
|
|
213
|
+
});
|
|
214
|
+
it('should categorize mixed themes correctly', async () => {
|
|
215
|
+
mockIsThemeInstalled
|
|
216
|
+
.mockResolvedValueOnce(false)
|
|
217
|
+
.mockResolvedValueOnce(true)
|
|
218
|
+
.mockResolvedValueOnce(false);
|
|
219
|
+
const result = await categorizeThemes(['theme1', 'theme2', 'theme3']);
|
|
220
|
+
expect(result.new).toEqual(['theme1', 'theme3']);
|
|
221
|
+
expect(result.update).toEqual(['theme2']);
|
|
222
|
+
});
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
//# sourceMappingURL=categorize.test.js.map
|