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.
- package/.claude-plugin/marketplace.json +38 -0
- package/.claude-plugin/plugin.json +25 -0
- package/.mcp.json +14 -0
- package/.mcpbignore +27 -0
- package/README.md +3 -0
- package/dist/index.js +2416 -665
- package/dist/lib.js +2533 -780
- package/manifest.json +273 -1
- package/package.json +21 -3
- package/server.json +35 -0
- package/src/lib.ts +2 -0
- package/src/server.ts +10 -1
- package/src/tools/calendar.ts +92 -0
- package/src/tools/classroom.ts +696 -0
- package/src/tools/contacts.ts +74 -0
- package/src/tools/drive.ts +17 -6
- package/src/tools/slides.ts +203 -0
- package/src/tools/utils.ts +9 -1
- package/tests/runner.test.ts +16 -0
- package/tests/server.test.ts +43 -0
- package/tests/tools/calendar.test.ts +138 -0
- package/tests/tools/classroom.test.ts +875 -0
- package/tests/tools/contacts.test.ts +100 -0
- package/tests/tools/drive.test.ts +49 -3
- package/tests/tools/slides.test.ts +441 -0
- package/tests/tools/utils.test.ts +65 -0
|
@@ -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('
|
|
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
|
+
});
|