Skip to content

Commit a943d57

Browse files
Merge pull request #10 from allenhutchison/test/improve-coverage
test: add comprehensive test coverage for MCP tools and config
2 parents f8efff7 + 94b43d5 commit a943d57

File tree

2 files changed

+726
-0
lines changed

2 files changed

+726
-0
lines changed

src/config/WorkspaceConfig.test.ts

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,4 +81,198 @@ describe('WorkspaceConfigManager', () => {
8181
expect.stringContaining('"new-store": "stores/123"')
8282
);
8383
});
84+
85+
it('should return default config and warn when file is corrupt', () => {
86+
const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
87+
mockExistsSync.mockReturnValue(true);
88+
mockReadFileSync.mockReturnValue('{ invalid json }');
89+
90+
const config = WorkspaceConfigManager.load();
91+
92+
expect(config).toEqual({ researchIds: [], fileSearchStores: {}, uploadOperations: {} });
93+
expect(warnSpy).toHaveBeenCalledWith(
94+
expect.stringContaining('Failed to load workspace config'),
95+
expect.anything()
96+
);
97+
warnSpy.mockRestore();
98+
});
99+
100+
it('should return default config and warn when schema validation fails', () => {
101+
const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
102+
mockExistsSync.mockReturnValue(true);
103+
// Valid JSON but invalid schema: researchIds should be an array, not a string
104+
mockReadFileSync.mockReturnValue(JSON.stringify({ researchIds: 'not-an-array' }));
105+
106+
const config = WorkspaceConfigManager.load();
107+
108+
expect(config).toEqual({ researchIds: [], fileSearchStores: {}, uploadOperations: {} });
109+
expect(warnSpy).toHaveBeenCalledWith(
110+
expect.stringContaining('Failed to load workspace config'),
111+
expect.anything()
112+
);
113+
warnSpy.mockRestore();
114+
});
115+
116+
it('should not add duplicate research ID', () => {
117+
mockExistsSync.mockReturnValue(true);
118+
mockReadFileSync.mockReturnValue(JSON.stringify({ researchIds: ['existing-id'], fileSearchStores: {}, uploadOperations: {} }));
119+
120+
WorkspaceConfigManager.addResearchId('existing-id');
121+
122+
// Should not call save since ID already exists
123+
expect(mockWriteFileSync).not.toHaveBeenCalled();
124+
});
125+
126+
it('should get upload operation by id', () => {
127+
const mockOperation = {
128+
id: 'op-123',
129+
status: 'completed',
130+
path: '/test/path',
131+
storeName: 'store-1',
132+
smartSync: false,
133+
totalFiles: 10,
134+
completedFiles: 10,
135+
skippedFiles: 0,
136+
failedFiles: 0,
137+
failedFilesList: [],
138+
startedAt: '2024-01-01T00:00:00Z',
139+
};
140+
mockExistsSync.mockReturnValue(true);
141+
mockReadFileSync.mockReturnValue(JSON.stringify({
142+
researchIds: [],
143+
fileSearchStores: {},
144+
uploadOperations: { 'op-123': mockOperation }
145+
}));
146+
147+
const operation = WorkspaceConfigManager.getUploadOperation('op-123');
148+
149+
expect(operation).toEqual(mockOperation);
150+
});
151+
152+
it('should return undefined for non-existent upload operation', () => {
153+
mockExistsSync.mockReturnValue(true);
154+
mockReadFileSync.mockReturnValue(JSON.stringify({ researchIds: [], fileSearchStores: {}, uploadOperations: {} }));
155+
156+
const operation = WorkspaceConfigManager.getUploadOperation('non-existent');
157+
158+
expect(operation).toBeUndefined();
159+
});
160+
161+
it('should set upload operation', () => {
162+
const mockOperation = {
163+
id: 'op-456',
164+
status: 'in_progress' as const,
165+
path: '/new/path',
166+
storeName: 'store-2',
167+
smartSync: true,
168+
totalFiles: 5,
169+
completedFiles: 2,
170+
skippedFiles: 1,
171+
failedFiles: 0,
172+
failedFilesList: [],
173+
startedAt: '2024-01-01T00:00:00Z',
174+
};
175+
mockExistsSync.mockReturnValue(true);
176+
mockReadFileSync.mockReturnValue(JSON.stringify({ researchIds: [], fileSearchStores: {}, uploadOperations: {} }));
177+
178+
WorkspaceConfigManager.setUploadOperation('op-456', mockOperation);
179+
180+
expect(mockWriteFileSync).toHaveBeenCalledWith(
181+
mockConfigPath,
182+
expect.stringContaining('"op-456"')
183+
);
184+
});
185+
186+
it('should get all upload operations', () => {
187+
const mockOperations = {
188+
'op-1': { id: 'op-1', status: 'completed', path: '/path1', storeName: 'store', smartSync: false, totalFiles: 1, completedFiles: 1, skippedFiles: 0, failedFiles: 0, failedFilesList: [], startedAt: '2024-01-01' },
189+
'op-2': { id: 'op-2', status: 'pending', path: '/path2', storeName: 'store', smartSync: true, totalFiles: 0, completedFiles: 0, skippedFiles: 0, failedFiles: 0, failedFilesList: [], startedAt: '2024-01-02' },
190+
};
191+
mockExistsSync.mockReturnValue(true);
192+
mockReadFileSync.mockReturnValue(JSON.stringify({
193+
researchIds: [],
194+
fileSearchStores: {},
195+
uploadOperations: mockOperations
196+
}));
197+
198+
const operations = WorkspaceConfigManager.getAllUploadOperations();
199+
200+
expect(operations).toEqual(mockOperations);
201+
});
202+
});
203+
204+
// Dynamic import WorkspaceOperationStorage
205+
const { WorkspaceOperationStorage } = await import('./WorkspaceConfig');
206+
207+
describe('WorkspaceOperationStorage', () => {
208+
beforeEach(() => {
209+
jest.clearAllMocks();
210+
});
211+
212+
it('should get operation via WorkspaceConfigManager', () => {
213+
const mockOperation = {
214+
id: 'op-789',
215+
status: 'completed',
216+
path: '/test',
217+
storeName: 'store',
218+
smartSync: false,
219+
totalFiles: 1,
220+
completedFiles: 1,
221+
skippedFiles: 0,
222+
failedFiles: 0,
223+
failedFilesList: [],
224+
startedAt: '2024-01-01',
225+
};
226+
mockExistsSync.mockReturnValue(true);
227+
mockReadFileSync.mockReturnValue(JSON.stringify({
228+
researchIds: [],
229+
fileSearchStores: {},
230+
uploadOperations: { 'op-789': mockOperation }
231+
}));
232+
233+
const storage = new WorkspaceOperationStorage();
234+
const operation = storage.get('op-789');
235+
236+
expect(operation).toEqual(mockOperation);
237+
});
238+
239+
it('should set operation via WorkspaceConfigManager', () => {
240+
const mockOperation = {
241+
id: 'op-new',
242+
status: 'pending' as const,
243+
path: '/new',
244+
storeName: 'store',
245+
smartSync: false,
246+
totalFiles: 0,
247+
completedFiles: 0,
248+
skippedFiles: 0,
249+
failedFiles: 0,
250+
failedFilesList: [],
251+
startedAt: '2024-01-01',
252+
};
253+
mockExistsSync.mockReturnValue(true);
254+
mockReadFileSync.mockReturnValue(JSON.stringify({ researchIds: [], fileSearchStores: {}, uploadOperations: {} }));
255+
256+
const storage = new WorkspaceOperationStorage();
257+
storage.set('op-new', mockOperation);
258+
259+
expect(mockWriteFileSync).toHaveBeenCalled();
260+
});
261+
262+
it('should get all operations via WorkspaceConfigManager', () => {
263+
const mockOperations = {
264+
'op-a': { id: 'op-a', status: 'completed', path: '/a', storeName: 'store', smartSync: false, totalFiles: 1, completedFiles: 1, skippedFiles: 0, failedFiles: 0, failedFilesList: [], startedAt: '2024-01-01' },
265+
};
266+
mockExistsSync.mockReturnValue(true);
267+
mockReadFileSync.mockReturnValue(JSON.stringify({
268+
researchIds: [],
269+
fileSearchStores: {},
270+
uploadOperations: mockOperations
271+
}));
272+
273+
const storage = new WorkspaceOperationStorage();
274+
const operations = storage.getAll();
275+
276+
expect(operations).toEqual(mockOperations);
277+
});
84278
});

0 commit comments

Comments
 (0)