gogcli-mcp 2.25.0 → 2.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,284 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+ import { registerChatTools } from '../../src/tools/chat.js';
3
+ import * as runner from '../../src/runner.js';
4
+ import { createTestHarness } from '@chrischall/mcp-utils/test';
5
+
6
+ vi.mock('../../src/runner.js');
7
+
8
+ const setupHandlers = () => createTestHarness(registerChatTools);
9
+
10
+ beforeEach(() => vi.clearAllMocks());
11
+
12
+ describe('gog_chat_spaces_list', () => {
13
+ it('lists spaces', async () => {
14
+ vi.mocked(runner.run).mockResolvedValue('{"spaces":[]}');
15
+ const harness = await setupHandlers();
16
+ await harness.callTool('gog_chat_spaces_list', {});
17
+ expect(runner.run).toHaveBeenCalledWith(['chat', 'spaces', 'list'], { account: undefined });
18
+ });
19
+
20
+ it('passes pagination flags', async () => {
21
+ vi.mocked(runner.run).mockResolvedValue('{"spaces":[]}');
22
+ const harness = await setupHandlers();
23
+ await harness.callTool('gog_chat_spaces_list', { max: 20, pageToken: 'tok', all: true });
24
+ expect(runner.run).toHaveBeenCalledWith(
25
+ ['chat', 'spaces', 'list', '--max=20', '--page=tok', '--all'],
26
+ { account: undefined },
27
+ );
28
+ });
29
+
30
+ it('returns error text on failure', async () => {
31
+ vi.mocked(runner.run).mockRejectedValue(new Error('Spaces failed'));
32
+ const harness = await setupHandlers();
33
+ const result = await harness.callTool('gog_chat_spaces_list', {});
34
+ expect(result.content[0].text).toBe('Error: Spaces failed');
35
+ });
36
+
37
+ // The constraint is the ACCOUNT, not the token, so it has to be in the
38
+ // description a model reads — a source comment would not reach it. Every chat
39
+ // tool carries the note, because any of them can be the first one called.
40
+ it('warns in EVERY description that Chat is Workspace-only', async () => {
41
+ const { McpServer } = await import('@modelcontextprotocol/sdk/server/mcp.js');
42
+ const server = new McpServer({ name: 'test', version: '0.0.0' });
43
+ const configs = new Map<string, { description?: string }>();
44
+ vi.spyOn(server, 'registerTool').mockImplementation((name, config) => {
45
+ configs.set(name, config as { description?: string });
46
+ return undefined as never;
47
+ });
48
+ registerChatTools(server);
49
+ expect(configs.size).toBe(12);
50
+ for (const [name, config] of configs) {
51
+ expect(config.description, name).toMatch(/consumer accounts|Workspace/i);
52
+ }
53
+ });
54
+ });
55
+
56
+ describe('gog_chat_spaces_find', () => {
57
+ it('searches by display name', async () => {
58
+ vi.mocked(runner.run).mockResolvedValue('{}');
59
+ const harness = await setupHandlers();
60
+ await harness.callTool('gog_chat_spaces_find', { displayName: 'Team' });
61
+ expect(runner.run).toHaveBeenCalledWith(['chat', 'spaces', 'find', 'Team'], { account: undefined });
62
+ });
63
+
64
+ it('passes --exact and --max', async () => {
65
+ vi.mocked(runner.run).mockResolvedValue('{}');
66
+ const harness = await setupHandlers();
67
+ await harness.callTool('gog_chat_spaces_find', { displayName: 'Team', exact: true, max: 5 });
68
+ expect(runner.run).toHaveBeenCalledWith(
69
+ ['chat', 'spaces', 'find', 'Team', '--exact', '--max=5'],
70
+ { account: undefined },
71
+ );
72
+ });
73
+ });
74
+
75
+ describe('gog_chat_spaces_create', () => {
76
+ it('creates a space with members', async () => {
77
+ vi.mocked(runner.run).mockResolvedValue('{}');
78
+ const harness = await setupHandlers();
79
+ await harness.callTool('gog_chat_spaces_create', { displayName: 'Launch', members: ['a@b.com', 'c@d.com'] });
80
+ expect(runner.run).toHaveBeenCalledWith(
81
+ ['chat', 'spaces', 'create', 'Launch', '--member=a@b.com', '--member=c@d.com'],
82
+ { account: undefined },
83
+ );
84
+ });
85
+
86
+ it('creates an empty space', async () => {
87
+ vi.mocked(runner.run).mockResolvedValue('{}');
88
+ const harness = await setupHandlers();
89
+ await harness.callTool('gog_chat_spaces_create', { displayName: 'Solo' });
90
+ expect(runner.run).toHaveBeenCalledWith(['chat', 'spaces', 'create', 'Solo'], { account: undefined });
91
+ });
92
+ });
93
+
94
+ describe('gog_chat_threads_list', () => {
95
+ it('lists threads in a space', async () => {
96
+ vi.mocked(runner.run).mockResolvedValue('{}');
97
+ const harness = await setupHandlers();
98
+ await harness.callTool('gog_chat_threads_list', { space: 'spaces/AAA', max: 10 });
99
+ expect(runner.run).toHaveBeenCalledWith(
100
+ ['chat', 'threads', 'list', 'spaces/AAA', '--max=10'],
101
+ { account: undefined },
102
+ );
103
+ });
104
+ });
105
+
106
+ describe('gog_chat_messages_list', () => {
107
+ it('lists messages in a space', async () => {
108
+ vi.mocked(runner.run).mockResolvedValue('{}');
109
+ const harness = await setupHandlers();
110
+ await harness.callTool('gog_chat_messages_list', { space: 'spaces/AAA' });
111
+ expect(runner.run).toHaveBeenCalledWith(['chat', 'messages', 'list', 'spaces/AAA'], { account: undefined });
112
+ });
113
+
114
+ it('filters by thread, unread and order', async () => {
115
+ vi.mocked(runner.run).mockResolvedValue('{}');
116
+ const harness = await setupHandlers();
117
+ await harness.callTool('gog_chat_messages_list', {
118
+ space: 'spaces/AAA', thread: 'spaces/AAA/threads/T', unread: true, order: 'createTime desc', max: 50,
119
+ });
120
+ expect(runner.run).toHaveBeenCalledWith(
121
+ ['chat', 'messages', 'list', 'spaces/AAA', '--thread=spaces/AAA/threads/T', '--unread',
122
+ '--order=createTime desc', '--max=50'],
123
+ { account: undefined },
124
+ );
125
+ });
126
+
127
+ it('rejects a sort order Chat does not accept', async () => {
128
+ const harness = await setupHandlers();
129
+ const result = await harness.callTool('gog_chat_messages_list', { space: 'spaces/AAA', order: 'newest' });
130
+ expect(result.isError).toBe(true);
131
+ expect(runner.run).not.toHaveBeenCalled();
132
+ });
133
+ });
134
+
135
+ describe('gog_chat_messages_send', () => {
136
+ it('sends text to a space', async () => {
137
+ vi.mocked(runner.run).mockResolvedValue('{}');
138
+ const harness = await setupHandlers();
139
+ await harness.callTool('gog_chat_messages_send', { space: 'spaces/AAA', text: 'hi' });
140
+ expect(runner.run).toHaveBeenCalledWith(
141
+ ['chat', 'messages', 'send', 'spaces/AAA', '--text=hi'],
142
+ { account: undefined },
143
+ );
144
+ });
145
+
146
+ it('replies in a thread and attaches a server-side path', async () => {
147
+ vi.mocked(runner.run).mockResolvedValue('{}');
148
+ const harness = await setupHandlers();
149
+ await harness.callTool('gog_chat_messages_send', {
150
+ space: 'spaces/AAA', text: 'see this', thread: 'spaces/AAA/threads/T', attach: ['/tmp/a.png'],
151
+ });
152
+ expect(runner.run).toHaveBeenCalledWith(
153
+ ['chat', 'messages', 'send', 'spaces/AAA', '--text=see this', '--thread=spaces/AAA/threads/T', '--attach=/tmp/a.png'],
154
+ { account: undefined },
155
+ );
156
+ });
157
+
158
+ it('carries caller-side bytes as a file arg', async () => {
159
+ vi.mocked(runner.run).mockResolvedValue('{}');
160
+ const harness = await setupHandlers();
161
+ await harness.callTool('gog_chat_messages_send', {
162
+ space: 'spaces/AAA',
163
+ text: 'chart',
164
+ attachInline: [{ filename: 'chart.png', contentBase64: Buffer.from('png').toString('base64') }],
165
+ });
166
+ const args = vi.mocked(runner.run).mock.calls[0][0];
167
+ expect(args).toContainEqual(expect.objectContaining({
168
+ kind: 'file', flag: 'attach', filename: 'chart.png', encoding: 'base64',
169
+ }));
170
+ });
171
+
172
+ it('sends an attachment with no text at all', async () => {
173
+ vi.mocked(runner.run).mockResolvedValue('{}');
174
+ const harness = await setupHandlers();
175
+ await harness.callTool('gog_chat_messages_send', { space: 'spaces/AAA', attach: ['/tmp/a.png'] });
176
+ expect(runner.run).toHaveBeenCalledWith(
177
+ ['chat', 'messages', 'send', 'spaces/AAA', '--attach=/tmp/a.png'],
178
+ { account: undefined },
179
+ );
180
+ });
181
+
182
+ it('refuses a message with neither text nor an attachment', async () => {
183
+ const harness = await setupHandlers();
184
+ const result = await harness.callTool('gog_chat_messages_send', { space: 'spaces/AAA' });
185
+ expect(result.isError).toBe(true);
186
+ expect(runner.run).not.toHaveBeenCalled();
187
+ });
188
+ });
189
+
190
+ describe('gog_chat_dm_send', () => {
191
+ it('sends a direct message by email', async () => {
192
+ vi.mocked(runner.run).mockResolvedValue('{}');
193
+ const harness = await setupHandlers();
194
+ await harness.callTool('gog_chat_dm_send', { email: 'a@b.com', text: 'hi' });
195
+ expect(runner.run).toHaveBeenCalledWith(
196
+ ['chat', 'dm', 'send', 'a@b.com', '--text=hi'],
197
+ { account: undefined },
198
+ );
199
+ });
200
+
201
+ it('replies in an existing DM thread', async () => {
202
+ vi.mocked(runner.run).mockResolvedValue('{}');
203
+ const harness = await setupHandlers();
204
+ await harness.callTool('gog_chat_dm_send', { email: 'a@b.com', text: 'hi', thread: 'spaces/D/threads/T' });
205
+ expect(runner.run).toHaveBeenCalledWith(
206
+ ['chat', 'dm', 'send', 'a@b.com', '--text=hi', '--thread=spaces/D/threads/T'],
207
+ { account: undefined },
208
+ );
209
+ });
210
+ });
211
+
212
+ describe('gog_chat_dm_space', () => {
213
+ it('resolves the DM space for an address', async () => {
214
+ vi.mocked(runner.run).mockResolvedValue('{}');
215
+ const harness = await setupHandlers();
216
+ await harness.callTool('gog_chat_dm_space', { email: 'a@b.com' });
217
+ expect(runner.run).toHaveBeenCalledWith(['chat', 'dm', 'space', 'a@b.com'], { account: undefined });
218
+ });
219
+ });
220
+
221
+ describe('gog_chat_reactions_list', () => {
222
+ it('lists reactions, qualifying a bare message ID with its space', async () => {
223
+ vi.mocked(runner.run).mockResolvedValue('{}');
224
+ const harness = await setupHandlers();
225
+ await harness.callTool('gog_chat_reactions_list', { message: 'MSG', space: 'spaces/AAA', max: 10 });
226
+ expect(runner.run).toHaveBeenCalledWith(
227
+ ['chat', 'messages', 'reactions', 'list', 'MSG', '--space=spaces/AAA', '--max=10'],
228
+ { account: undefined },
229
+ );
230
+ });
231
+
232
+ it('omits --space when the message is already fully qualified', async () => {
233
+ vi.mocked(runner.run).mockResolvedValue('{}');
234
+ const harness = await setupHandlers();
235
+ await harness.callTool('gog_chat_reactions_list', { message: 'spaces/AAA/messages/M' });
236
+ expect(runner.run).toHaveBeenCalledWith(
237
+ ['chat', 'messages', 'reactions', 'list', 'spaces/AAA/messages/M'],
238
+ { account: undefined },
239
+ );
240
+ });
241
+ });
242
+
243
+ describe('gog_chat_reactions_create', () => {
244
+ it('adds an emoji reaction', async () => {
245
+ vi.mocked(runner.run).mockResolvedValue('{}');
246
+ const harness = await setupHandlers();
247
+ await harness.callTool('gog_chat_reactions_create', { message: 'spaces/AAA/messages/M', emoji: '👍' });
248
+ expect(runner.run).toHaveBeenCalledWith(
249
+ ['chat', 'messages', 'reactions', 'create', 'spaces/AAA/messages/M', '👍'],
250
+ { account: undefined },
251
+ );
252
+ });
253
+
254
+ it('passes --space for a bare message ID', async () => {
255
+ vi.mocked(runner.run).mockResolvedValue('{}');
256
+ const harness = await setupHandlers();
257
+ await harness.callTool('gog_chat_reactions_create', { message: 'M', emoji: '👍', space: 'spaces/AAA' });
258
+ expect(runner.run).toHaveBeenCalledWith(
259
+ ['chat', 'messages', 'reactions', 'create', 'M', '👍', '--space=spaces/AAA'],
260
+ { account: undefined },
261
+ );
262
+ });
263
+ });
264
+
265
+ describe('gog_chat_reactions_delete', () => {
266
+ it('deletes a reaction by resource name', async () => {
267
+ vi.mocked(runner.run).mockResolvedValue('{}');
268
+ const harness = await setupHandlers();
269
+ await harness.callTool('gog_chat_reactions_delete', { reaction: 'spaces/AAA/messages/M/reactions/R' });
270
+ expect(runner.run).toHaveBeenCalledWith(
271
+ ['chat', 'messages', 'reactions', 'delete', 'spaces/AAA/messages/M/reactions/R'],
272
+ { account: undefined },
273
+ );
274
+ });
275
+ });
276
+
277
+ describe('gog_chat_run', () => {
278
+ it('passes the subcommand and args through', async () => {
279
+ vi.mocked(runner.run).mockResolvedValue('{}');
280
+ const harness = await setupHandlers();
281
+ await harness.callTool('gog_chat_run', { subcommand: 'spaces', args: ['list'] });
282
+ expect(runner.run).toHaveBeenCalledWith(['chat', 'spaces', 'list'], { account: undefined });
283
+ });
284
+ });