gogcli-mcp 2.0.0 → 2.0.4

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.
@@ -120,3 +120,103 @@ describe('gog_contacts_run', () => {
120
120
  expect(result.content[0].text).toBe('Error: Run failed');
121
121
  });
122
122
  });
123
+
124
+ describe('gog_people_me', () => {
125
+ it('calls run with people me', async () => {
126
+ vi.mocked(runner.run).mockResolvedValue('{"resourceName":"people/me"}');
127
+ const handlers = setupHandlers();
128
+ await handlers.get('gog_people_me')!({});
129
+ expect(runner.run).toHaveBeenCalledWith(['people', 'me'], { account: undefined });
130
+ });
131
+
132
+ it('returns error text on failure', async () => {
133
+ vi.mocked(runner.run).mockRejectedValue(new Error('Me failed'));
134
+ const handlers = setupHandlers();
135
+ const result = await handlers.get('gog_people_me')!({});
136
+ expect(result.content[0].text).toBe('Error: Me failed');
137
+ });
138
+ });
139
+
140
+ describe('gog_people_get', () => {
141
+ it('calls run with userId', async () => {
142
+ vi.mocked(runner.run).mockResolvedValue('{}');
143
+ const handlers = setupHandlers();
144
+ await handlers.get('gog_people_get')!({ userId: 'people/c123' });
145
+ expect(runner.run).toHaveBeenCalledWith(['people', 'get', 'people/c123'], { account: undefined });
146
+ });
147
+ });
148
+
149
+ describe('gog_people_search', () => {
150
+ it('calls run with query', async () => {
151
+ vi.mocked(runner.run).mockResolvedValue('{}');
152
+ const handlers = setupHandlers();
153
+ await handlers.get('gog_people_search')!({ query: 'alice' });
154
+ expect(runner.run).toHaveBeenCalledWith(['people', 'search', 'alice'], { account: undefined });
155
+ });
156
+
157
+ it('passes pagination flags', async () => {
158
+ vi.mocked(runner.run).mockResolvedValue('{}');
159
+ const handlers = setupHandlers();
160
+ await handlers.get('gog_people_search')!({ query: 'alice', max: 100, page: 'tok', all: true });
161
+ expect(runner.run).toHaveBeenCalledWith(
162
+ ['people', 'search', 'alice', '--max=100', '--page=tok', '--all'],
163
+ { account: undefined },
164
+ );
165
+ });
166
+
167
+ it('omits --all when false', async () => {
168
+ vi.mocked(runner.run).mockResolvedValue('{}');
169
+ const handlers = setupHandlers();
170
+ await handlers.get('gog_people_search')!({ query: 'x', all: false });
171
+ expect(runner.run).toHaveBeenCalledWith(['people', 'search', 'x'], { account: undefined });
172
+ });
173
+ });
174
+
175
+ describe('gog_people_relations', () => {
176
+ it('calls run with no userId', async () => {
177
+ vi.mocked(runner.run).mockResolvedValue('{}');
178
+ const handlers = setupHandlers();
179
+ await handlers.get('gog_people_relations')!({});
180
+ expect(runner.run).toHaveBeenCalledWith(['people', 'relations'], { account: undefined });
181
+ });
182
+
183
+ it('passes userId and --type when provided', async () => {
184
+ vi.mocked(runner.run).mockResolvedValue('{}');
185
+ const handlers = setupHandlers();
186
+ await handlers.get('gog_people_relations')!({ userId: 'people/c123', type: 'manager' });
187
+ expect(runner.run).toHaveBeenCalledWith(
188
+ ['people', 'relations', 'people/c123', '--type=manager'],
189
+ { account: undefined },
190
+ );
191
+ });
192
+ });
193
+
194
+ describe('gog_people_raw', () => {
195
+ it('calls run with userId', async () => {
196
+ vi.mocked(runner.run).mockResolvedValue('{}');
197
+ const handlers = setupHandlers();
198
+ await handlers.get('gog_people_raw')!({ userId: 'people/c123' });
199
+ expect(runner.run).toHaveBeenCalledWith(['people', 'raw', 'people/c123'], { account: undefined });
200
+ });
201
+
202
+ it('passes --person-fields and --pretty when provided', async () => {
203
+ vi.mocked(runner.run).mockResolvedValue('{}');
204
+ const handlers = setupHandlers();
205
+ await handlers.get('gog_people_raw')!({
206
+ userId: 'people/c123',
207
+ personFields: 'names,emailAddresses',
208
+ pretty: true,
209
+ });
210
+ expect(runner.run).toHaveBeenCalledWith(
211
+ ['people', 'raw', 'people/c123', '--person-fields=names,emailAddresses', '--pretty'],
212
+ { account: undefined },
213
+ );
214
+ });
215
+
216
+ it('omits --pretty when false', async () => {
217
+ vi.mocked(runner.run).mockResolvedValue('{}');
218
+ const handlers = setupHandlers();
219
+ await handlers.get('gog_people_raw')!({ userId: 'people/c123', pretty: false });
220
+ expect(runner.run).toHaveBeenCalledWith(['people', 'raw', 'people/c123'], { account: undefined });
221
+ });
222
+ });
@@ -28,11 +28,40 @@ describe('gog_drive_ls', () => {
28
28
  expect(runner.run).toHaveBeenCalledWith(['drive', 'ls'], { account: undefined });
29
29
  });
30
30
 
31
- it('appends folderId when provided', async () => {
31
+ it('passes folderId as --parent flag', async () => {
32
32
  vi.mocked(runner.run).mockResolvedValue('{}');
33
33
  const handlers = setupHandlers();
34
34
  await handlers.get('gog_drive_ls')!({ folderId: 'folder1' });
35
- expect(runner.run).toHaveBeenCalledWith(['drive', 'ls', 'folder1'], { account: undefined });
35
+ expect(runner.run).toHaveBeenCalledWith(['drive', 'ls', '--parent=folder1'], { account: undefined });
36
+ });
37
+
38
+ it('supports max, page, query, and allDrives flags', async () => {
39
+ vi.mocked(runner.run).mockResolvedValue('{}');
40
+ const handlers = setupHandlers();
41
+ await handlers.get('gog_drive_ls')!({
42
+ folderId: 'folder1',
43
+ max: 50,
44
+ page: 'tok',
45
+ query: "name contains 'x'",
46
+ });
47
+ expect(runner.run).toHaveBeenCalledWith(
48
+ ['drive', 'ls', '--parent=folder1', '--max=50', '--page=tok', "--query=name contains 'x'"],
49
+ { account: undefined },
50
+ );
51
+ });
52
+
53
+ it('passes --no-all-drives when allDrives is false', async () => {
54
+ vi.mocked(runner.run).mockResolvedValue('{}');
55
+ const handlers = setupHandlers();
56
+ await handlers.get('gog_drive_ls')!({ allDrives: false });
57
+ expect(runner.run).toHaveBeenCalledWith(['drive', 'ls', '--no-all-drives'], { account: undefined });
58
+ });
59
+
60
+ it('omits all-drives flag when allDrives is true (default)', async () => {
61
+ vi.mocked(runner.run).mockResolvedValue('{}');
62
+ const handlers = setupHandlers();
63
+ await handlers.get('gog_drive_ls')!({ allDrives: true });
64
+ expect(runner.run).toHaveBeenCalledWith(['drive', 'ls'], { account: undefined });
36
65
  });
37
66
 
38
67
  it('returns error text on failure', async () => {
@@ -124,13 +153,30 @@ describe('gog_drive_move', () => {
124
153
  });
125
154
 
126
155
  describe('gog_drive_delete', () => {
127
- it('calls run with fileId', async () => {
156
+ it('calls run with fileId (trash by default)', async () => {
128
157
  vi.mocked(runner.run).mockResolvedValue('{}');
129
158
  const handlers = setupHandlers();
130
159
  await handlers.get('gog_drive_delete')!({ fileId: 'file1' });
131
160
  expect(runner.run).toHaveBeenCalledWith(['drive', 'delete', 'file1'], { account: undefined });
132
161
  });
133
162
 
163
+ it('appends --permanent when permanent=true', async () => {
164
+ vi.mocked(runner.run).mockResolvedValue('{}');
165
+ const handlers = setupHandlers();
166
+ await handlers.get('gog_drive_delete')!({ fileId: 'file1', permanent: true });
167
+ expect(runner.run).toHaveBeenCalledWith(
168
+ ['drive', 'delete', 'file1', '--permanent'],
169
+ { account: undefined },
170
+ );
171
+ });
172
+
173
+ it('omits --permanent when permanent=false', async () => {
174
+ vi.mocked(runner.run).mockResolvedValue('{}');
175
+ const handlers = setupHandlers();
176
+ await handlers.get('gog_drive_delete')!({ fileId: 'file1', permanent: false });
177
+ expect(runner.run).toHaveBeenCalledWith(['drive', 'delete', 'file1'], { account: undefined });
178
+ });
179
+
134
180
  it('returns error text on failure', async () => {
135
181
  vi.mocked(runner.run).mockRejectedValue(new Error('Delete failed'));
136
182
  const handlers = setupHandlers();
@@ -0,0 +1,441 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
+ import { registerSlidesTools } from '../../src/tools/slides.js';
4
+ import * as runner from '../../src/runner.js';
5
+
6
+ vi.mock('../../src/runner.js');
7
+
8
+ type ToolHandler = (args: Record<string, unknown>) => Promise<{ content: Array<{ type: string; text: string }> }>;
9
+
10
+ function setupHandlers(): Map<string, ToolHandler> {
11
+ const server = new McpServer({ name: 'test', version: '0.0.0' });
12
+ const handlers = new Map<string, ToolHandler>();
13
+ vi.spyOn(server, 'registerTool').mockImplementation((name, _config, cb) => {
14
+ handlers.set(name, cb as ToolHandler);
15
+ return undefined as never;
16
+ });
17
+ registerSlidesTools(server);
18
+ return handlers;
19
+ }
20
+
21
+ beforeEach(() => vi.clearAllMocks());
22
+
23
+ describe('gog_slides_export', () => {
24
+ it('calls run with presentationId', async () => {
25
+ vi.mocked(runner.run).mockResolvedValue('{}');
26
+ const handlers = setupHandlers();
27
+ await handlers.get('gog_slides_export')!({ presentationId: 'p1' });
28
+ expect(runner.run).toHaveBeenCalledWith(['slides', 'export', 'p1'], { account: undefined });
29
+ });
30
+
31
+ it('passes --out and --format when provided', async () => {
32
+ vi.mocked(runner.run).mockResolvedValue('{}');
33
+ const handlers = setupHandlers();
34
+ await handlers.get('gog_slides_export')!({ presentationId: 'p1', out: '/tmp/out.pdf', format: 'pdf' });
35
+ expect(runner.run).toHaveBeenCalledWith(
36
+ ['slides', 'export', 'p1', '--out=/tmp/out.pdf', '--format=pdf'],
37
+ { account: undefined },
38
+ );
39
+ });
40
+
41
+ it('returns error text on failure', async () => {
42
+ vi.mocked(runner.run).mockRejectedValue(new Error('Export failed'));
43
+ const handlers = setupHandlers();
44
+ const result = await handlers.get('gog_slides_export')!({ presentationId: 'bad' });
45
+ expect(result.content[0].text).toBe('Error: Export failed');
46
+ });
47
+ });
48
+
49
+ describe('gog_slides_info', () => {
50
+ it('calls run with presentationId', async () => {
51
+ vi.mocked(runner.run).mockResolvedValue('{}');
52
+ const handlers = setupHandlers();
53
+ await handlers.get('gog_slides_info')!({ presentationId: 'p1' });
54
+ expect(runner.run).toHaveBeenCalledWith(['slides', 'info', 'p1'], { account: undefined });
55
+ });
56
+
57
+ it('returns error text on failure', async () => {
58
+ vi.mocked(runner.run).mockRejectedValue(new Error('Info failed'));
59
+ const handlers = setupHandlers();
60
+ const result = await handlers.get('gog_slides_info')!({ presentationId: 'bad' });
61
+ expect(result.content[0].text).toBe('Error: Info failed');
62
+ });
63
+ });
64
+
65
+ describe('gog_slides_create', () => {
66
+ it('calls run with title only', async () => {
67
+ vi.mocked(runner.run).mockResolvedValue('{}');
68
+ const handlers = setupHandlers();
69
+ await handlers.get('gog_slides_create')!({ title: 'My Deck' });
70
+ expect(runner.run).toHaveBeenCalledWith(['slides', 'create', 'My Deck'], { account: undefined });
71
+ });
72
+
73
+ it('passes --parent and --template when provided', async () => {
74
+ vi.mocked(runner.run).mockResolvedValue('{}');
75
+ const handlers = setupHandlers();
76
+ await handlers.get('gog_slides_create')!({ title: 'My Deck', parent: 'folder1', template: 'tpl1' });
77
+ expect(runner.run).toHaveBeenCalledWith(
78
+ ['slides', 'create', 'My Deck', '--parent=folder1', '--template=tpl1'],
79
+ { account: undefined },
80
+ );
81
+ });
82
+
83
+ it('returns error text on failure', async () => {
84
+ vi.mocked(runner.run).mockRejectedValue(new Error('Create failed'));
85
+ const handlers = setupHandlers();
86
+ const result = await handlers.get('gog_slides_create')!({ title: 'x' });
87
+ expect(result.content[0].text).toBe('Error: Create failed');
88
+ });
89
+ });
90
+
91
+ describe('gog_slides_create_from_markdown', () => {
92
+ it('calls run with title only', async () => {
93
+ vi.mocked(runner.run).mockResolvedValue('{}');
94
+ const handlers = setupHandlers();
95
+ await handlers.get('gog_slides_create_from_markdown')!({ title: 'Deck' });
96
+ expect(runner.run).toHaveBeenCalledWith(
97
+ ['slides', 'create-from-markdown', 'Deck'],
98
+ { account: undefined },
99
+ );
100
+ });
101
+
102
+ it('passes all optional flags', async () => {
103
+ vi.mocked(runner.run).mockResolvedValue('{}');
104
+ const handlers = setupHandlers();
105
+ await handlers.get('gog_slides_create_from_markdown')!({
106
+ title: 'Deck',
107
+ content: '# Slide 1',
108
+ contentFile: '/tmp/deck.md',
109
+ parent: 'folder1',
110
+ debug: true,
111
+ });
112
+ expect(runner.run).toHaveBeenCalledWith(
113
+ [
114
+ 'slides', 'create-from-markdown', 'Deck',
115
+ '--content=# Slide 1',
116
+ '--content-file=/tmp/deck.md',
117
+ '--parent=folder1',
118
+ '--debug',
119
+ ],
120
+ { account: undefined },
121
+ );
122
+ });
123
+
124
+ it('omits --debug when false', async () => {
125
+ vi.mocked(runner.run).mockResolvedValue('{}');
126
+ const handlers = setupHandlers();
127
+ await handlers.get('gog_slides_create_from_markdown')!({ title: 'Deck', debug: false });
128
+ expect(runner.run).toHaveBeenCalledWith(
129
+ ['slides', 'create-from-markdown', 'Deck'],
130
+ { account: undefined },
131
+ );
132
+ });
133
+
134
+ it('returns error text on failure', async () => {
135
+ vi.mocked(runner.run).mockRejectedValue(new Error('Markdown failed'));
136
+ const handlers = setupHandlers();
137
+ const result = await handlers.get('gog_slides_create_from_markdown')!({ title: 'x' });
138
+ expect(result.content[0].text).toBe('Error: Markdown failed');
139
+ });
140
+ });
141
+
142
+ describe('gog_slides_create_from_template', () => {
143
+ it('calls run with templateId and title only', async () => {
144
+ vi.mocked(runner.run).mockResolvedValue('{}');
145
+ const handlers = setupHandlers();
146
+ await handlers.get('gog_slides_create_from_template')!({ templateId: 'tpl1', title: 'Deck' });
147
+ expect(runner.run).toHaveBeenCalledWith(
148
+ ['slides', 'create-from-template', 'tpl1', 'Deck'],
149
+ { account: undefined },
150
+ );
151
+ });
152
+
153
+ it('passes --replace for a single replacement entry', async () => {
154
+ vi.mocked(runner.run).mockResolvedValue('{}');
155
+ const handlers = setupHandlers();
156
+ await handlers.get('gog_slides_create_from_template')!({
157
+ templateId: 'tpl1',
158
+ title: 'Deck',
159
+ replacements: { name: 'Alice' },
160
+ });
161
+ expect(runner.run).toHaveBeenCalledWith(
162
+ ['slides', 'create-from-template', 'tpl1', 'Deck', '--replace=name=Alice'],
163
+ { account: undefined },
164
+ );
165
+ });
166
+
167
+ it('passes --replace for each entry in replacements', async () => {
168
+ vi.mocked(runner.run).mockResolvedValue('{}');
169
+ const handlers = setupHandlers();
170
+ await handlers.get('gog_slides_create_from_template')!({
171
+ templateId: 'tpl1',
172
+ title: 'Deck',
173
+ replacements: { name: 'Alice', company: 'Acme' },
174
+ });
175
+ const call = vi.mocked(runner.run).mock.calls[0]!;
176
+ expect(call[0]).toEqual(expect.arrayContaining(['--replace=name=Alice', '--replace=company=Acme']));
177
+ expect(call[1]).toEqual({ account: undefined });
178
+ });
179
+
180
+ it('passes --replacements, --parent, and --exact', async () => {
181
+ vi.mocked(runner.run).mockResolvedValue('{}');
182
+ const handlers = setupHandlers();
183
+ await handlers.get('gog_slides_create_from_template')!({
184
+ templateId: 'tpl1',
185
+ title: 'Deck',
186
+ replacementsFile: '/tmp/r.json',
187
+ parent: 'folder1',
188
+ exact: true,
189
+ });
190
+ expect(runner.run).toHaveBeenCalledWith(
191
+ [
192
+ 'slides', 'create-from-template', 'tpl1', 'Deck',
193
+ '--replacements=/tmp/r.json',
194
+ '--parent=folder1',
195
+ '--exact',
196
+ ],
197
+ { account: undefined },
198
+ );
199
+ });
200
+
201
+ it('omits --exact when false', async () => {
202
+ vi.mocked(runner.run).mockResolvedValue('{}');
203
+ const handlers = setupHandlers();
204
+ await handlers.get('gog_slides_create_from_template')!({ templateId: 'tpl1', title: 'Deck', exact: false });
205
+ expect(runner.run).toHaveBeenCalledWith(
206
+ ['slides', 'create-from-template', 'tpl1', 'Deck'],
207
+ { account: undefined },
208
+ );
209
+ });
210
+
211
+ it('returns error text on failure', async () => {
212
+ vi.mocked(runner.run).mockRejectedValue(new Error('Template failed'));
213
+ const handlers = setupHandlers();
214
+ const result = await handlers.get('gog_slides_create_from_template')!({ templateId: 't', title: 'x' });
215
+ expect(result.content[0].text).toBe('Error: Template failed');
216
+ });
217
+ });
218
+
219
+ describe('gog_slides_copy', () => {
220
+ it('calls run with presentationId and title', async () => {
221
+ vi.mocked(runner.run).mockResolvedValue('{}');
222
+ const handlers = setupHandlers();
223
+ await handlers.get('gog_slides_copy')!({ presentationId: 'p1', title: 'Copy' });
224
+ expect(runner.run).toHaveBeenCalledWith(
225
+ ['slides', 'copy', 'p1', 'Copy'],
226
+ { account: undefined },
227
+ );
228
+ });
229
+
230
+ it('passes --parent when provided', async () => {
231
+ vi.mocked(runner.run).mockResolvedValue('{}');
232
+ const handlers = setupHandlers();
233
+ await handlers.get('gog_slides_copy')!({ presentationId: 'p1', title: 'Copy', parent: 'folder1' });
234
+ expect(runner.run).toHaveBeenCalledWith(
235
+ ['slides', 'copy', 'p1', 'Copy', '--parent=folder1'],
236
+ { account: undefined },
237
+ );
238
+ });
239
+
240
+ it('returns error text on failure', async () => {
241
+ vi.mocked(runner.run).mockRejectedValue(new Error('Copy failed'));
242
+ const handlers = setupHandlers();
243
+ const result = await handlers.get('gog_slides_copy')!({ presentationId: 'p', title: 'x' });
244
+ expect(result.content[0].text).toBe('Error: Copy failed');
245
+ });
246
+ });
247
+
248
+ describe('gog_slides_add_slide', () => {
249
+ it('calls run with presentationId and image', async () => {
250
+ vi.mocked(runner.run).mockResolvedValue('{}');
251
+ const handlers = setupHandlers();
252
+ await handlers.get('gog_slides_add_slide')!({ presentationId: 'p1', image: '/tmp/img.png' });
253
+ expect(runner.run).toHaveBeenCalledWith(
254
+ ['slides', 'add-slide', 'p1', '/tmp/img.png'],
255
+ { account: undefined },
256
+ );
257
+ });
258
+
259
+ it('passes --notes, --notes-file, and --before', async () => {
260
+ vi.mocked(runner.run).mockResolvedValue('{}');
261
+ const handlers = setupHandlers();
262
+ await handlers.get('gog_slides_add_slide')!({
263
+ presentationId: 'p1',
264
+ image: '/tmp/img.png',
265
+ notes: 'Speaker note',
266
+ notesFile: '/tmp/notes.txt',
267
+ before: 'slide5',
268
+ });
269
+ expect(runner.run).toHaveBeenCalledWith(
270
+ [
271
+ 'slides', 'add-slide', 'p1', '/tmp/img.png',
272
+ '--notes=Speaker note',
273
+ '--notes-file=/tmp/notes.txt',
274
+ '--before=slide5',
275
+ ],
276
+ { account: undefined },
277
+ );
278
+ });
279
+
280
+ it('returns error text on failure', async () => {
281
+ vi.mocked(runner.run).mockRejectedValue(new Error('Add failed'));
282
+ const handlers = setupHandlers();
283
+ const result = await handlers.get('gog_slides_add_slide')!({ presentationId: 'p', image: 'i' });
284
+ expect(result.content[0].text).toBe('Error: Add failed');
285
+ });
286
+ });
287
+
288
+ describe('gog_slides_list_slides', () => {
289
+ it('calls run with presentationId', async () => {
290
+ vi.mocked(runner.run).mockResolvedValue('{}');
291
+ const handlers = setupHandlers();
292
+ await handlers.get('gog_slides_list_slides')!({ presentationId: 'p1' });
293
+ expect(runner.run).toHaveBeenCalledWith(['slides', 'list-slides', 'p1'], { account: undefined });
294
+ });
295
+
296
+ it('returns error text on failure', async () => {
297
+ vi.mocked(runner.run).mockRejectedValue(new Error('List failed'));
298
+ const handlers = setupHandlers();
299
+ const result = await handlers.get('gog_slides_list_slides')!({ presentationId: 'bad' });
300
+ expect(result.content[0].text).toBe('Error: List failed');
301
+ });
302
+ });
303
+
304
+ describe('gog_slides_delete_slide', () => {
305
+ it('calls run with presentationId and slideId', async () => {
306
+ vi.mocked(runner.run).mockResolvedValue('{}');
307
+ const handlers = setupHandlers();
308
+ await handlers.get('gog_slides_delete_slide')!({ presentationId: 'p1', slideId: 's1' });
309
+ expect(runner.run).toHaveBeenCalledWith(
310
+ ['slides', 'delete-slide', 'p1', 's1'],
311
+ { account: undefined },
312
+ );
313
+ });
314
+
315
+ it('returns error text on failure', async () => {
316
+ vi.mocked(runner.run).mockRejectedValue(new Error('Delete failed'));
317
+ const handlers = setupHandlers();
318
+ const result = await handlers.get('gog_slides_delete_slide')!({ presentationId: 'p', slideId: 's' });
319
+ expect(result.content[0].text).toBe('Error: Delete failed');
320
+ });
321
+ });
322
+
323
+ describe('gog_slides_read_slide', () => {
324
+ it('calls run with presentationId and slideId', async () => {
325
+ vi.mocked(runner.run).mockResolvedValue('{}');
326
+ const handlers = setupHandlers();
327
+ await handlers.get('gog_slides_read_slide')!({ presentationId: 'p1', slideId: 's1' });
328
+ expect(runner.run).toHaveBeenCalledWith(
329
+ ['slides', 'read-slide', 'p1', 's1'],
330
+ { account: undefined },
331
+ );
332
+ });
333
+
334
+ it('returns error text on failure', async () => {
335
+ vi.mocked(runner.run).mockRejectedValue(new Error('Read failed'));
336
+ const handlers = setupHandlers();
337
+ const result = await handlers.get('gog_slides_read_slide')!({ presentationId: 'p', slideId: 's' });
338
+ expect(result.content[0].text).toBe('Error: Read failed');
339
+ });
340
+ });
341
+
342
+ describe('gog_slides_update_notes', () => {
343
+ it('calls run with presentationId and slideId', async () => {
344
+ vi.mocked(runner.run).mockResolvedValue('{}');
345
+ const handlers = setupHandlers();
346
+ await handlers.get('gog_slides_update_notes')!({ presentationId: 'p1', slideId: 's1' });
347
+ expect(runner.run).toHaveBeenCalledWith(
348
+ ['slides', 'update-notes', 'p1', 's1'],
349
+ { account: undefined },
350
+ );
351
+ });
352
+
353
+ it('passes --notes and --notes-file when provided', async () => {
354
+ vi.mocked(runner.run).mockResolvedValue('{}');
355
+ const handlers = setupHandlers();
356
+ await handlers.get('gog_slides_update_notes')!({
357
+ presentationId: 'p1',
358
+ slideId: 's1',
359
+ notes: 'speak clearly',
360
+ notesFile: '/tmp/n.txt',
361
+ });
362
+ expect(runner.run).toHaveBeenCalledWith(
363
+ [
364
+ 'slides', 'update-notes', 'p1', 's1',
365
+ '--notes=speak clearly',
366
+ '--notes-file=/tmp/n.txt',
367
+ ],
368
+ { account: undefined },
369
+ );
370
+ });
371
+
372
+ it('returns error text on failure', async () => {
373
+ vi.mocked(runner.run).mockRejectedValue(new Error('Notes failed'));
374
+ const handlers = setupHandlers();
375
+ const result = await handlers.get('gog_slides_update_notes')!({ presentationId: 'p', slideId: 's' });
376
+ expect(result.content[0].text).toBe('Error: Notes failed');
377
+ });
378
+ });
379
+
380
+ describe('gog_slides_replace_slide', () => {
381
+ it('calls run with presentationId, slideId, and image', async () => {
382
+ vi.mocked(runner.run).mockResolvedValue('{}');
383
+ const handlers = setupHandlers();
384
+ await handlers.get('gog_slides_replace_slide')!({
385
+ presentationId: 'p1',
386
+ slideId: 's1',
387
+ image: '/tmp/img.png',
388
+ });
389
+ expect(runner.run).toHaveBeenCalledWith(
390
+ ['slides', 'replace-slide', 'p1', 's1', '/tmp/img.png'],
391
+ { account: undefined },
392
+ );
393
+ });
394
+
395
+ it('passes --notes and --notes-file when provided', async () => {
396
+ vi.mocked(runner.run).mockResolvedValue('{}');
397
+ const handlers = setupHandlers();
398
+ await handlers.get('gog_slides_replace_slide')!({
399
+ presentationId: 'p1',
400
+ slideId: 's1',
401
+ image: '/tmp/img.png',
402
+ notes: 'updated',
403
+ notesFile: '/tmp/n.txt',
404
+ });
405
+ expect(runner.run).toHaveBeenCalledWith(
406
+ [
407
+ 'slides', 'replace-slide', 'p1', 's1', '/tmp/img.png',
408
+ '--notes=updated',
409
+ '--notes-file=/tmp/n.txt',
410
+ ],
411
+ { account: undefined },
412
+ );
413
+ });
414
+
415
+ it('returns error text on failure', async () => {
416
+ vi.mocked(runner.run).mockRejectedValue(new Error('Replace failed'));
417
+ const handlers = setupHandlers();
418
+ const result = await handlers.get('gog_slides_replace_slide')!({
419
+ presentationId: 'p',
420
+ slideId: 's',
421
+ image: 'i',
422
+ });
423
+ expect(result.content[0].text).toBe('Error: Replace failed');
424
+ });
425
+ });
426
+
427
+ describe('gog_slides_run', () => {
428
+ it('passes subcommand and args to runner', async () => {
429
+ vi.mocked(runner.run).mockResolvedValue('{}');
430
+ const handlers = setupHandlers();
431
+ await handlers.get('gog_slides_run')!({ subcommand: 'info', args: ['p1'] });
432
+ expect(runner.run).toHaveBeenCalledWith(['slides', 'info', 'p1'], { account: undefined });
433
+ });
434
+
435
+ it('returns error text on failure', async () => {
436
+ vi.mocked(runner.run).mockRejectedValue(new Error('Run failed'));
437
+ const handlers = setupHandlers();
438
+ const result = await handlers.get('gog_slides_run')!({ subcommand: 'info', args: [] });
439
+ expect(result.content[0].text).toBe('Error: Run failed');
440
+ });
441
+ });