@aaronsb/google-workspace-mcp 2.7.0 → 3.0.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.
Files changed (142) hide show
  1. package/README.md +21 -10
  2. package/build/factory/generator.d.ts +0 -2
  3. package/build/factory/generator.js +32 -27
  4. package/build/factory/generator.js.map +1 -1
  5. package/build/factory/manifest/gmail.yaml +24 -2
  6. package/build/factory/registry.d.ts +2 -3
  7. package/build/factory/registry.js +3 -9
  8. package/build/factory/registry.js.map +1 -1
  9. package/build/index.js +27 -1
  10. package/build/index.js.map +1 -1
  11. package/build/node-floor.d.ts +16 -0
  12. package/build/node-floor.js +73 -0
  13. package/build/node-floor.js.map +1 -0
  14. package/build/services/gmail/patch.js +67 -3
  15. package/build/services/gmail/patch.js.map +1 -1
  16. package/build/version.d.ts +1 -1
  17. package/build/version.js +2 -2
  18. package/build/version.js.map +1 -1
  19. package/package.json +17 -10
  20. package/build/__helpers__/testSetup.d.ts +0 -1
  21. package/build/__helpers__/testSetup.js +0 -6
  22. package/build/__helpers__/testSetup.js.map +0 -1
  23. package/build/__tests__/accounts/credentials.test.d.ts +0 -1
  24. package/build/__tests__/accounts/credentials.test.js +0 -127
  25. package/build/__tests__/accounts/credentials.test.js.map +0 -1
  26. package/build/__tests__/accounts/oauth.test.d.ts +0 -1
  27. package/build/__tests__/accounts/oauth.test.js +0 -71
  28. package/build/__tests__/accounts/oauth.test.js.map +0 -1
  29. package/build/__tests__/accounts/registry.test.d.ts +0 -1
  30. package/build/__tests__/accounts/registry.test.js +0 -74
  31. package/build/__tests__/accounts/registry.test.js.map +0 -1
  32. package/build/__tests__/accounts/token-service.test.d.ts +0 -1
  33. package/build/__tests__/accounts/token-service.test.js +0 -126
  34. package/build/__tests__/accounts/token-service.test.js.map +0 -1
  35. package/build/__tests__/executor/errors.test.d.ts +0 -1
  36. package/build/__tests__/executor/errors.test.js +0 -42
  37. package/build/__tests__/executor/errors.test.js.map +0 -1
  38. package/build/__tests__/executor/gws.test.d.ts +0 -1
  39. package/build/__tests__/executor/gws.test.js +0 -187
  40. package/build/__tests__/executor/gws.test.js.map +0 -1
  41. package/build/__tests__/executor/paths.test.d.ts +0 -1
  42. package/build/__tests__/executor/paths.test.js +0 -60
  43. package/build/__tests__/executor/paths.test.js.map +0 -1
  44. package/build/__tests__/executor/resolve-binary.test.d.ts +0 -1
  45. package/build/__tests__/executor/resolve-binary.test.js +0 -58
  46. package/build/__tests__/executor/resolve-binary.test.js.map +0 -1
  47. package/build/__tests__/executor/workspace.test.d.ts +0 -1
  48. package/build/__tests__/executor/workspace.test.js +0 -143
  49. package/build/__tests__/executor/workspace.test.js.map +0 -1
  50. package/build/__tests__/factory/calendar-patch.test.d.ts +0 -5
  51. package/build/__tests__/factory/calendar-patch.test.js +0 -269
  52. package/build/__tests__/factory/calendar-patch.test.js.map +0 -1
  53. package/build/__tests__/factory/drive-patch.test.d.ts +0 -5
  54. package/build/__tests__/factory/drive-patch.test.js +0 -344
  55. package/build/__tests__/factory/drive-patch.test.js.map +0 -1
  56. package/build/__tests__/factory/generator.test.d.ts +0 -1
  57. package/build/__tests__/factory/generator.test.js +0 -240
  58. package/build/__tests__/factory/generator.test.js.map +0 -1
  59. package/build/__tests__/factory/patch-coverage.test.d.ts +0 -10
  60. package/build/__tests__/factory/patch-coverage.test.js +0 -182
  61. package/build/__tests__/factory/patch-coverage.test.js.map +0 -1
  62. package/build/__tests__/factory/safety.test.d.ts +0 -1
  63. package/build/__tests__/factory/safety.test.js +0 -107
  64. package/build/__tests__/factory/safety.test.js.map +0 -1
  65. package/build/__tests__/factory/sheets-patch.test.d.ts +0 -6
  66. package/build/__tests__/factory/sheets-patch.test.js +0 -498
  67. package/build/__tests__/factory/sheets-patch.test.js.map +0 -1
  68. package/build/__tests__/integration/executor.test.d.ts +0 -5
  69. package/build/__tests__/integration/executor.test.js +0 -38
  70. package/build/__tests__/integration/executor.test.js.map +0 -1
  71. package/build/__tests__/integration/handlers.test.d.ts +0 -6
  72. package/build/__tests__/integration/handlers.test.js +0 -95
  73. package/build/__tests__/integration/handlers.test.js.map +0 -1
  74. package/build/__tests__/integration/setup.d.ts +0 -19
  75. package/build/__tests__/integration/setup.js +0 -61
  76. package/build/__tests__/integration/setup.js.map +0 -1
  77. package/build/__tests__/server/formatting/html-sanitize.test.d.ts +0 -7
  78. package/build/__tests__/server/formatting/html-sanitize.test.js +0 -174
  79. package/build/__tests__/server/formatting/html-sanitize.test.js.map +0 -1
  80. package/build/__tests__/server/formatting/markdown.test.d.ts +0 -1
  81. package/build/__tests__/server/formatting/markdown.test.js +0 -259
  82. package/build/__tests__/server/formatting/markdown.test.js.map +0 -1
  83. package/build/__tests__/server/formatting/next-steps.test.d.ts +0 -1
  84. package/build/__tests__/server/formatting/next-steps.test.js +0 -42
  85. package/build/__tests__/server/formatting/next-steps.test.js.map +0 -1
  86. package/build/__tests__/server/handler.test.d.ts +0 -1
  87. package/build/__tests__/server/handler.test.js +0 -97
  88. package/build/__tests__/server/handler.test.js.map +0 -1
  89. package/build/__tests__/server/handlers/__mocks__/executor.d.ts +0 -173
  90. package/build/__tests__/server/handlers/__mocks__/executor.js +0 -135
  91. package/build/__tests__/server/handlers/__mocks__/executor.js.map +0 -1
  92. package/build/__tests__/server/handlers/accounts.test.d.ts +0 -1
  93. package/build/__tests__/server/handlers/accounts.test.js +0 -129
  94. package/build/__tests__/server/handlers/accounts.test.js.map +0 -1
  95. package/build/__tests__/server/handlers/calendar.test.d.ts +0 -1
  96. package/build/__tests__/server/handlers/calendar.test.js +0 -96
  97. package/build/__tests__/server/handlers/calendar.test.js.map +0 -1
  98. package/build/__tests__/server/handlers/drive.test.d.ts +0 -1
  99. package/build/__tests__/server/handlers/drive.test.js +0 -68
  100. package/build/__tests__/server/handlers/drive.test.js.map +0 -1
  101. package/build/__tests__/server/handlers/email.test.d.ts +0 -1
  102. package/build/__tests__/server/handlers/email.test.js +0 -99
  103. package/build/__tests__/server/handlers/email.test.js.map +0 -1
  104. package/build/__tests__/server/handlers/validate.test.d.ts +0 -1
  105. package/build/__tests__/server/handlers/validate.test.js +0 -88
  106. package/build/__tests__/server/handlers/validate.test.js.map +0 -1
  107. package/build/__tests__/server/queue.test.d.ts +0 -1
  108. package/build/__tests__/server/queue.test.js +0 -199
  109. package/build/__tests__/server/queue.test.js.map +0 -1
  110. package/build/__tests__/server/server.test.d.ts +0 -7
  111. package/build/__tests__/server/server.test.js +0 -135
  112. package/build/__tests__/server/server.test.js.map +0 -1
  113. package/build/__tests__/server/session/context.test.d.ts +0 -1
  114. package/build/__tests__/server/session/context.test.js +0 -101
  115. package/build/__tests__/server/session/context.test.js.map +0 -1
  116. package/build/__tests__/server/session/tracker.test.d.ts +0 -1
  117. package/build/__tests__/server/session/tracker.test.js +0 -146
  118. package/build/__tests__/server/session/tracker.test.js.map +0 -1
  119. package/build/__tests__/server/tools.test.d.ts +0 -1
  120. package/build/__tests__/server/tools.test.js +0 -91
  121. package/build/__tests__/server/tools.test.js.map +0 -1
  122. package/build/__tests__/services/gmail/mime.test.d.ts +0 -1
  123. package/build/__tests__/services/gmail/mime.test.js +0 -23
  124. package/build/__tests__/services/gmail/mime.test.js.map +0 -1
  125. package/build/__tests__/services/meet/patch.test.d.ts +0 -4
  126. package/build/__tests__/services/meet/patch.test.js +0 -374
  127. package/build/__tests__/services/meet/patch.test.js.map +0 -1
  128. package/build/server/scratchpad/__tests__/docs-sync-integration.test.d.ts +0 -11
  129. package/build/server/scratchpad/__tests__/docs-sync-integration.test.js +0 -187
  130. package/build/server/scratchpad/__tests__/docs-sync-integration.test.js.map +0 -1
  131. package/build/server/scratchpad/__tests__/docs-sync.test.d.ts +0 -16
  132. package/build/server/scratchpad/__tests__/docs-sync.test.js +0 -213
  133. package/build/server/scratchpad/__tests__/docs-sync.test.js.map +0 -1
  134. package/build/server/scratchpad/__tests__/json-path.test.d.ts +0 -4
  135. package/build/server/scratchpad/__tests__/json-path.test.js +0 -121
  136. package/build/server/scratchpad/__tests__/json-path.test.js.map +0 -1
  137. package/build/server/scratchpad/__tests__/manager.test.d.ts +0 -5
  138. package/build/server/scratchpad/__tests__/manager.test.js +0 -886
  139. package/build/server/scratchpad/__tests__/manager.test.js.map +0 -1
  140. package/build/server/scratchpad/__tests__/validate.test.d.ts +0 -4
  141. package/build/server/scratchpad/__tests__/validate.test.js +0 -112
  142. package/build/server/scratchpad/__tests__/validate.test.js.map +0 -1
@@ -1,344 +0,0 @@
1
- /**
2
- * Tests for the drive service patch — custom handlers for download/export
3
- * that ensure parent directories are created before writing files.
4
- */
5
- import * as fs from 'node:fs/promises';
6
- import * as path from 'node:path';
7
- import * as os from 'node:os';
8
- // Mock executor before importing patch
9
- jest.mock('../../executor/gws.js');
10
- import { execute } from '../../executor/gws.js';
11
- // Mock workspace module with a temp dir
12
- const tmpWorkspace = path.join(os.tmpdir(), `gws-test-${Date.now()}`);
13
- jest.mock('../../executor/workspace.js', () => ({
14
- ensureWorkspaceDir: jest.fn(async () => ({ path: tmpWorkspace, valid: true })),
15
- getWorkspaceDir: jest.fn(() => tmpWorkspace),
16
- resolveWorkspacePath: jest.fn((filename) => path.join(tmpWorkspace, filename)),
17
- verifyPathSafety: jest.fn(async () => { }),
18
- }));
19
- // Mock file-output to avoid reading actual files
20
- jest.mock('../../executor/file-output.js', () => ({
21
- isTextFile: jest.fn(() => true),
22
- isImageFile: jest.fn(() => false),
23
- formatFileOutput: jest.fn(() => '## Exported file\n'),
24
- buildImageBlock: jest.fn(() => null),
25
- buildImageBlockFromFile: jest.fn(() => null),
26
- }));
27
- import { drivePatch } from '../../services/drive/patch.js';
28
- const mockExecute = execute;
29
- describe('drivePatch custom handlers', () => {
30
- beforeEach(async () => {
31
- mockExecute.mockReset();
32
- // Start with a clean workspace — only the root dir exists
33
- await fs.rm(tmpWorkspace, { recursive: true, force: true });
34
- await fs.mkdir(tmpWorkspace, { recursive: true });
35
- });
36
- afterAll(async () => {
37
- await fs.rm(tmpWorkspace, { recursive: true, force: true });
38
- });
39
- describe('export', () => {
40
- it('creates parent directories before calling gws', async () => {
41
- const outputPath = path.join(tmpWorkspace, 'subdir', 'Report.txt');
42
- const { resolveWorkspacePath } = require('../../executor/workspace.js');
43
- resolveWorkspacePath.mockReturnValue(outputPath);
44
- // First call: get file metadata
45
- // Second call: export — simulate gws writing the file
46
- mockExecute
47
- .mockResolvedValueOnce({ success: true, data: { name: 'Report.gdoc' }, stderr: '' })
48
- .mockImplementationOnce(async () => {
49
- // gws would write the file here — parent dir must already exist
50
- await fs.writeFile(outputPath, 'exported content');
51
- return { success: true, data: {}, stderr: '' };
52
- });
53
- const handler = drivePatch.customHandlers.export;
54
- await handler({ fileId: 'abc123', mimeType: 'text/plain', outputPath: 'subdir/Report.txt' }, 'user@test.com');
55
- // The export call should have --output pointing to the nested path
56
- const exportCall = mockExecute.mock.calls[1][0];
57
- expect(exportCall).toContain('--output');
58
- expect(exportCall[exportCall.indexOf('--output') + 1]).toBe(outputPath);
59
- });
60
- it('handler mkdir is required — without it gws write would fail', async () => {
61
- const outputPath = path.join(tmpWorkspace, 'deep', 'nested', 'Doc.txt');
62
- const { resolveWorkspacePath } = require('../../executor/workspace.js');
63
- resolveWorkspacePath.mockReturnValue(outputPath);
64
- // Verify the parent directory does NOT exist before handler runs
65
- const parentBefore = await fs.stat(path.dirname(outputPath)).catch(() => null);
66
- expect(parentBefore).toBeNull();
67
- mockExecute
68
- .mockResolvedValueOnce({ success: true, data: { name: 'Doc' }, stderr: '' })
69
- .mockImplementationOnce(async () => {
70
- // Parent directory must exist at this point (handler created it)
71
- const stat = await fs.stat(path.dirname(outputPath));
72
- expect(stat.isDirectory()).toBe(true);
73
- await fs.writeFile(outputPath, 'content');
74
- return { success: true, data: {}, stderr: '' };
75
- });
76
- const handler = drivePatch.customHandlers.export;
77
- await handler({ fileId: 'abc123', mimeType: 'text/plain' }, 'user@test.com');
78
- });
79
- it('works with flat filename (no subdirectory)', async () => {
80
- const outputPath = path.join(tmpWorkspace, 'Doc.pdf');
81
- const { resolveWorkspacePath } = require('../../executor/workspace.js');
82
- resolveWorkspacePath.mockReturnValue(outputPath);
83
- mockExecute
84
- .mockResolvedValueOnce({ success: true, data: { name: 'Doc' }, stderr: '' })
85
- .mockImplementationOnce(async () => {
86
- await fs.writeFile(outputPath, 'pdf content');
87
- return { success: true, data: {}, stderr: '' };
88
- });
89
- const handler = drivePatch.customHandlers.export;
90
- const result = await handler({ fileId: 'abc123', mimeType: 'application/pdf' }, 'user@test.com');
91
- expect(result.text).toBeDefined();
92
- expect(mockExecute).toHaveBeenCalledTimes(2);
93
- });
94
- });
95
- describe('share', () => {
96
- it('sends type + role + emailAddress as JSON body, not via --params', async () => {
97
- mockExecute.mockResolvedValueOnce({
98
- success: true,
99
- data: { id: 'perm-1', emailAddress: 'bob@test.com', role: 'reader', type: 'user' },
100
- stderr: '',
101
- });
102
- const handler = drivePatch.customHandlers.share;
103
- await handler({ fileId: 'file-1', shareEmail: 'bob@test.com', role: 'reader' }, 'user@test.com');
104
- const args = mockExecute.mock.calls[0][0];
105
- expect(args.slice(0, 3)).toEqual(['drive', 'permissions', 'create']);
106
- expect(args).toContain('--json');
107
- const body = JSON.parse(args[args.indexOf('--json') + 1]);
108
- // type MUST be in the body — omitting it caused
109
- // "The permission type field is required." on every share call.
110
- expect(body.type).toBe('user');
111
- expect(body.role).toBe('reader');
112
- expect(body.emailAddress).toBe('bob@test.com');
113
- // --params should only carry query params, not the permission body.
114
- const queryParams = JSON.parse(args[args.indexOf('--params') + 1]);
115
- expect(queryParams.fileId).toBe('file-1');
116
- expect(queryParams.type).toBeUndefined();
117
- expect(queryParams.role).toBeUndefined();
118
- });
119
- it("defaults type to 'user' when not provided", async () => {
120
- mockExecute.mockResolvedValueOnce({ success: true, data: { id: 'perm-1' }, stderr: '' });
121
- const handler = drivePatch.customHandlers.share;
122
- await handler({ fileId: 'file-1', shareEmail: 'bob@test.com' }, 'user@test.com');
123
- const args = mockExecute.mock.calls[0][0];
124
- const body = JSON.parse(args[args.indexOf('--json') + 1]);
125
- expect(body.type).toBe('user');
126
- expect(body.role).toBe('reader'); // default from manifest flows through
127
- });
128
- it('rejects user/group share without shareEmail', async () => {
129
- const handler = drivePatch.customHandlers.share;
130
- await expect(handler({ fileId: 'file-1' }, 'user@test.com')).rejects.toThrow('shareEmail');
131
- await expect(handler({ fileId: 'file-1', type: 'group' }, 'user@test.com')).rejects.toThrow('shareEmail');
132
- });
133
- it("uses domain field when type is 'domain'", async () => {
134
- mockExecute.mockResolvedValueOnce({ success: true, data: { id: 'perm-1' }, stderr: '' });
135
- const handler = drivePatch.customHandlers.share;
136
- await handler({ fileId: 'file-1', type: 'domain', domain: 'acme.com', role: 'writer' }, 'user@test.com');
137
- const args = mockExecute.mock.calls[0][0];
138
- const body = JSON.parse(args[args.indexOf('--json') + 1]);
139
- expect(body.type).toBe('domain');
140
- expect(body.domain).toBe('acme.com');
141
- expect(body.emailAddress).toBeUndefined();
142
- });
143
- it("requires domain when type is 'domain'", async () => {
144
- const handler = drivePatch.customHandlers.share;
145
- await expect(handler({ fileId: 'file-1', type: 'domain' }, 'user@test.com')).rejects.toThrow('domain');
146
- });
147
- it("accepts type 'anyone' with no target", async () => {
148
- mockExecute.mockResolvedValueOnce({ success: true, data: { id: 'perm-1' }, stderr: '' });
149
- const handler = drivePatch.customHandlers.share;
150
- await handler({ fileId: 'file-1', type: 'anyone', role: 'reader' }, 'user@test.com');
151
- const args = mockExecute.mock.calls[0][0];
152
- const body = JSON.parse(args[args.indexOf('--json') + 1]);
153
- expect(body.type).toBe('anyone');
154
- expect(body.emailAddress).toBeUndefined();
155
- expect(body.domain).toBeUndefined();
156
- });
157
- it('suppresses email notifications by default for user/group shares', async () => {
158
- mockExecute.mockResolvedValueOnce({ success: true, data: { id: 'perm-1' }, stderr: '' });
159
- const handler = drivePatch.customHandlers.share;
160
- await handler({ fileId: 'file-1', shareEmail: 'bob@test.com' }, 'user@test.com');
161
- const args = mockExecute.mock.calls[0][0];
162
- const queryParams = JSON.parse(args[args.indexOf('--params') + 1]);
163
- expect(queryParams.sendNotificationEmail).toBe(false);
164
- });
165
- });
166
- describe('listPermissions', () => {
167
- it('hits permissions.list and requests the fields the API omits by default', async () => {
168
- mockExecute.mockResolvedValueOnce({
169
- success: true,
170
- data: {
171
- permissions: [
172
- { id: 'owner-perm', type: 'user', role: 'owner', emailAddress: 'me@test.com' },
173
- { id: 'reader-perm', type: 'user', role: 'reader', emailAddress: 'bob@test.com' },
174
- ],
175
- },
176
- stderr: '',
177
- });
178
- const handler = drivePatch.customHandlers.listPermissions;
179
- const result = await handler({ fileId: 'file-1' }, 'me@test.com');
180
- const args = mockExecute.mock.calls[0][0];
181
- expect(args.slice(0, 3)).toEqual(['drive', 'permissions', 'list']);
182
- const queryParams = JSON.parse(args[args.indexOf('--params') + 1]);
183
- expect(queryParams.fileId).toBe('file-1');
184
- // Without an explicit fields mask the Drive API returns only id/type — not
185
- // role or emailAddress — which made the listing useless.
186
- expect(queryParams.fields).toContain('role');
187
- expect(queryParams.fields).toContain('emailAddress');
188
- expect(result.text).toContain('Permissions on file-1 (2)');
189
- expect(result.text).toContain('owner-perm | owner | user | me@test.com');
190
- expect(result.text).toContain('reader-perm | reader | user | bob@test.com');
191
- expect(result.refs.count).toBe(2);
192
- // A list has no canonical "the" permission — no singular permissionId ref.
193
- expect(result.refs.permissionId).toBeUndefined();
194
- expect(result.refs.permissions).toEqual([
195
- { permissionId: 'owner-perm', type: 'user', role: 'owner', target: 'me@test.com' },
196
- { permissionId: 'reader-perm', type: 'user', role: 'reader', target: 'bob@test.com' },
197
- ]);
198
- });
199
- it('reports an empty permission list without claiming "No files found"', async () => {
200
- mockExecute.mockResolvedValueOnce({ success: true, data: { permissions: [] }, stderr: '' });
201
- const handler = drivePatch.customHandlers.listPermissions;
202
- const result = await handler({ fileId: 'file-2' }, 'me@test.com');
203
- expect(result.text).toContain('No sharing permissions on file file-2');
204
- expect(result.text).not.toContain('No files found');
205
- expect(result.refs.count).toBe(0);
206
- expect(result.refs.permissions).toEqual([]);
207
- });
208
- it('labels anyone-with-link, pending-owner, deleted, and domain/displayName targets', async () => {
209
- mockExecute.mockResolvedValueOnce({
210
- success: true,
211
- data: {
212
- permissions: [
213
- { id: 'anyone-perm', type: 'anyone', role: 'reader' },
214
- { id: 'pending-perm', type: 'user', role: 'writer', emailAddress: 'new@test.com', pendingOwner: true },
215
- { id: 'domain-perm', type: 'domain', role: 'reader', domain: 'acme.com' },
216
- { id: 'group-perm', type: 'group', role: 'commenter', displayName: 'Eng Team' },
217
- { id: 'gone-perm', type: 'user', role: 'reader', emailAddress: 'old@test.com', deleted: true },
218
- ],
219
- },
220
- stderr: '',
221
- });
222
- const handler = drivePatch.customHandlers.listPermissions;
223
- const result = await handler({ fileId: 'file-3' }, 'me@test.com');
224
- expect(result.text).toContain('anyone-perm | reader | anyone | anyone with the link');
225
- expect(result.text).toContain('[pending owner]');
226
- expect(result.text).toContain('domain-perm | reader | domain | acme.com');
227
- expect(result.text).toContain('group-perm | commenter | group | Eng Team');
228
- expect(result.text).toContain('gone-perm | reader | user | old@test.com [deleted account]');
229
- });
230
- });
231
- describe('copy', () => {
232
- it('sends name as the request body, not via --params, so the copy is renamed', async () => {
233
- mockExecute.mockResolvedValueOnce({
234
- success: true,
235
- data: { id: 'copy-1', name: 'My New Name', mimeType: 'application/vnd.google-apps.spreadsheet' },
236
- stderr: '',
237
- });
238
- const handler = drivePatch.customHandlers.copy;
239
- const result = await handler({ fileId: 'src-1', name: 'My New Name' }, 'user@test.com');
240
- const args = mockExecute.mock.calls[0][0];
241
- expect(args.slice(0, 3)).toEqual(['drive', 'files', 'copy']);
242
- const body = JSON.parse(args[args.indexOf('--json') + 1]);
243
- expect(body.name).toBe('My New Name');
244
- const queryParams = JSON.parse(args[args.indexOf('--params') + 1]);
245
- expect(queryParams.fileId).toBe('src-1');
246
- // name must NOT leak into the query — that's the bug being fixed.
247
- expect(queryParams.name).toBeUndefined();
248
- expect(result.text).toContain('File copied: **My New Name**');
249
- expect(result.text).toContain('**File ID:** copy-1');
250
- expect(result.refs.fileId).toBe('copy-1');
251
- expect(result.refs.sourceFileId).toBe('src-1');
252
- });
253
- it('forwards parentFolderId as body.parents', async () => {
254
- mockExecute.mockResolvedValueOnce({ success: true, data: { id: 'copy-2', name: 'Copy of X', parents: ['folder-9'] }, stderr: '' });
255
- const handler = drivePatch.customHandlers.copy;
256
- await handler({ fileId: 'src-2', parentFolderId: 'folder-9' }, 'user@test.com');
257
- const args = mockExecute.mock.calls[0][0];
258
- const body = JSON.parse(args[args.indexOf('--json') + 1]);
259
- expect(body.parents).toEqual(['folder-9']);
260
- });
261
- it('omits --json entirely when neither name nor parentFolderId is given', async () => {
262
- mockExecute.mockResolvedValueOnce({ success: true, data: { id: 'copy-3', name: 'Copy of X' }, stderr: '' });
263
- const handler = drivePatch.customHandlers.copy;
264
- await handler({ fileId: 'src-3' }, 'user@test.com');
265
- const args = mockExecute.mock.calls[0][0];
266
- expect(args).not.toContain('--json');
267
- });
268
- });
269
- describe('update', () => {
270
- it('renames via the request body and surfaces the new name', async () => {
271
- mockExecute.mockResolvedValueOnce({ success: true, data: { id: 'file-1', name: 'Renamed.pdf', parents: ['root'] }, stderr: '' });
272
- const handler = drivePatch.customHandlers.update;
273
- const result = await handler({ fileId: 'file-1', name: 'Renamed.pdf' }, 'user@test.com');
274
- const args = mockExecute.mock.calls[0][0];
275
- expect(args.slice(0, 3)).toEqual(['drive', 'files', 'update']);
276
- const body = JSON.parse(args[args.indexOf('--json') + 1]);
277
- expect(body.name).toBe('Renamed.pdf');
278
- const queryParams = JSON.parse(args[args.indexOf('--params') + 1]);
279
- expect(queryParams.name).toBeUndefined();
280
- expect(result.text).toContain('File updated: **Renamed.pdf**');
281
- expect(result.refs.name).toBe('Renamed.pdf');
282
- });
283
- it('moves between folders via addParents/removeParents query params (no body)', async () => {
284
- mockExecute.mockResolvedValueOnce({ success: true, data: { id: 'file-2', name: 'doc', parents: ['new-folder'] }, stderr: '' });
285
- const handler = drivePatch.customHandlers.update;
286
- await handler({ fileId: 'file-2', addParents: 'new-folder', removeParents: 'old-folder' }, 'user@test.com');
287
- const args = mockExecute.mock.calls[0][0];
288
- expect(args).not.toContain('--json'); // nothing to put in the body
289
- const queryParams = JSON.parse(args[args.indexOf('--params') + 1]);
290
- expect(queryParams.addParents).toBe('new-folder');
291
- expect(queryParams.removeParents).toBe('old-folder');
292
- });
293
- it('rename + move in one call populates both --json (name) and --params (parents)', async () => {
294
- mockExecute.mockResolvedValueOnce({ success: true, data: { id: 'file-4', name: 'Moved.pdf', parents: ['dest'] }, stderr: '' });
295
- const handler = drivePatch.customHandlers.update;
296
- const result = await handler({ fileId: 'file-4', name: 'Moved.pdf', addParents: 'dest', removeParents: 'src' }, 'user@test.com');
297
- const args = mockExecute.mock.calls[0][0];
298
- const body = JSON.parse(args[args.indexOf('--json') + 1]);
299
- expect(body.name).toBe('Moved.pdf');
300
- const queryParams = JSON.parse(args[args.indexOf('--params') + 1]);
301
- expect(queryParams.addParents).toBe('dest');
302
- expect(queryParams.removeParents).toBe('src');
303
- expect(result.text).toContain('File updated: **Moved.pdf**');
304
- expect(result.text).toContain('**Parents:** dest');
305
- expect(result.refs.parents).toEqual(['dest']);
306
- });
307
- it('omits refs.parents when the API response has none', async () => {
308
- mockExecute.mockResolvedValueOnce({ success: true, data: { id: 'file-5', name: 'R.pdf' }, stderr: '' });
309
- const handler = drivePatch.customHandlers.update;
310
- const result = await handler({ fileId: 'file-5', name: 'R.pdf' }, 'user@test.com');
311
- expect('parents' in result.refs).toBe(false);
312
- });
313
- it('rejects a no-op update (none of name/addParents/removeParents)', async () => {
314
- const handler = drivePatch.customHandlers.update;
315
- await expect(handler({ fileId: 'file-3' }, 'user@test.com')).rejects.toThrow(/name.*addParents.*removeParents/);
316
- expect(mockExecute).not.toHaveBeenCalled();
317
- });
318
- });
319
- describe('download', () => {
320
- it('creates parent directories before calling gws', async () => {
321
- const outputPath = path.join(tmpWorkspace, 'images', 'photo.png');
322
- const { resolveWorkspacePath } = require('../../executor/workspace.js');
323
- resolveWorkspacePath.mockReturnValue(outputPath);
324
- // Verify parent does NOT exist before handler runs
325
- const parentBefore = await fs.stat(path.dirname(outputPath)).catch(() => null);
326
- expect(parentBefore).toBeNull();
327
- mockExecute
328
- .mockResolvedValueOnce({ success: true, data: { name: 'photo.png', mimeType: 'image/png' }, stderr: '' })
329
- .mockImplementationOnce(async () => {
330
- // Parent directory must exist at this point
331
- const stat = await fs.stat(path.dirname(outputPath));
332
- expect(stat.isDirectory()).toBe(true);
333
- await fs.writeFile(outputPath, 'png data');
334
- return { success: true, data: {}, stderr: '' };
335
- });
336
- const handler = drivePatch.customHandlers.download;
337
- await handler({ fileId: 'img-1', outputPath: 'images/photo.png' }, 'user@test.com');
338
- const downloadCall = mockExecute.mock.calls[1][0];
339
- expect(downloadCall).toContain('--output');
340
- expect(downloadCall[downloadCall.indexOf('--output') + 1]).toBe(outputPath);
341
- });
342
- });
343
- });
344
- //# sourceMappingURL=drive-patch.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"drive-patch.test.js","sourceRoot":"","sources":["../../../src/__tests__/factory/drive-patch.test.ts"],"names":[],"mappings":"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"}
@@ -1 +0,0 @@
1
- export {};
@@ -1,240 +0,0 @@
1
- import { loadManifest, generateTools, generateSchema, generateHandler } from '../../factory/generator.js';
2
- import { patches } from '../../factory/patches.js';
3
- // Mock executor for handler tests
4
- jest.mock('../../executor/gws.js');
5
- import { execute } from '../../executor/gws.js';
6
- const mockExecute = execute;
7
- describe('loadManifest', () => {
8
- it('loads and parses the manifest YAML', () => {
9
- const manifest = loadManifest();
10
- expect(manifest.services).toBeDefined();
11
- expect(manifest.services.gmail).toBeDefined();
12
- expect(manifest.services.calendar).toBeDefined();
13
- expect(manifest.services.drive).toBeDefined();
14
- });
15
- it('has correct tool names', () => {
16
- const manifest = loadManifest();
17
- expect(manifest.services.gmail.tool_name).toBe('manage_email');
18
- expect(manifest.services.calendar.tool_name).toBe('manage_calendar');
19
- expect(manifest.services.drive.tool_name).toBe('manage_drive');
20
- });
21
- });
22
- describe('generateSchema', () => {
23
- const manifest = loadManifest();
24
- it('generates operation enum from manifest operations', () => {
25
- const schema = generateSchema(manifest.services.gmail);
26
- const props = schema.inputSchema.properties;
27
- // Core operations present (manifest may expand)
28
- expect(props.operation.enum).toContain('search');
29
- expect(props.operation.enum).toContain('read');
30
- expect(props.operation.enum).toContain('send');
31
- expect(props.operation.enum).toContain('reply');
32
- expect(props.operation.enum).toContain('triage');
33
- expect(props.operation.enum).toContain('forward');
34
- expect(props.operation.enum).toContain('trash');
35
- expect(props.operation.enum).toContain('labels');
36
- });
37
- it('includes email param when requires_email is true', () => {
38
- const schema = generateSchema(manifest.services.gmail);
39
- const required = schema.inputSchema.required;
40
- expect(required).toContain('email');
41
- });
42
- it('collects params from all operations', () => {
43
- const schema = generateSchema(manifest.services.gmail);
44
- const props = schema.inputSchema.properties;
45
- // From search
46
- expect(props.query).toBeDefined();
47
- expect(props.maxResults).toBeDefined();
48
- // From read
49
- expect(props.messageId).toBeDefined();
50
- // From send
51
- expect(props.to).toBeDefined();
52
- expect(props.subject).toBeDefined();
53
- expect(props.body).toBeDefined();
54
- });
55
- it('sets additionalProperties: false', () => {
56
- const schema = generateSchema(manifest.services.gmail);
57
- expect(schema.inputSchema.additionalProperties).toBe(false);
58
- });
59
- it('uses tool_name from service def', () => {
60
- const schema = generateSchema(manifest.services.drive);
61
- expect(schema.name).toBe('manage_drive');
62
- });
63
- });
64
- describe('generateTools', () => {
65
- it('produces one tool per manifest service', () => {
66
- const manifest = loadManifest();
67
- const tools = generateTools(manifest, patches);
68
- expect(tools.length).toBeGreaterThanOrEqual(6);
69
- const names = tools.map(t => t.schema.name);
70
- expect(names).toContain('manage_email');
71
- expect(names).toContain('manage_calendar');
72
- expect(names).toContain('manage_drive');
73
- expect(names).toContain('manage_sheets');
74
- expect(names).toContain('manage_tasks');
75
- expect(names).toContain('manage_meet');
76
- // manage_contacts excluded pending gws auth scope support
77
- });
78
- it('each tool has both schema and handler', () => {
79
- const manifest = loadManifest();
80
- const tools = generateTools(manifest, patches);
81
- for (const tool of tools) {
82
- expect(tool.schema).toHaveProperty('name');
83
- expect(tool.schema).toHaveProperty('inputSchema');
84
- expect(typeof tool.handler).toBe('function');
85
- }
86
- });
87
- });
88
- describe('generateHandler', () => {
89
- const manifest = loadManifest();
90
- beforeEach(() => {
91
- mockExecute.mockReset();
92
- });
93
- it('calls execute with correct args for resource-based operations', async () => {
94
- mockExecute.mockResolvedValue({ success: true, data: { files: [] }, stderr: '' });
95
- const handler = generateHandler(manifest.services.drive, patches.drive);
96
- await handler({ operation: 'search', email: 'u@t.com', query: 'budget' });
97
- expect(mockExecute).toHaveBeenCalledWith(expect.arrayContaining(['drive', 'files', 'list']), expect.objectContaining({ account: 'u@t.com' }));
98
- });
99
- it('calls execute with correct args for helper-based operations', async () => {
100
- mockExecute.mockResolvedValue({ success: true, data: { messages: [] }, stderr: '' });
101
- const handler = generateHandler(manifest.services.gmail, patches.gmail);
102
- await handler({ operation: 'triage', email: 'u@t.com' });
103
- expect(mockExecute).toHaveBeenCalledWith(['gmail', '+triage'], expect.objectContaining({ account: 'u@t.com' }));
104
- });
105
- it('uses patch formatList when available', async () => {
106
- mockExecute.mockResolvedValue({
107
- success: true,
108
- data: { messages: [{ id: 'msg-1', from: 'alice', subject: 'hi', date: '2024-01-01' }] },
109
- stderr: '',
110
- });
111
- const handler = generateHandler(manifest.services.gmail, patches.gmail);
112
- const result = await handler({ operation: 'triage', email: 'u@t.com' });
113
- // Gmail patch uses formatEmailList which produces pipe-delimited format
114
- expect(result.text).toContain('msg-1');
115
- expect(result.text).toContain('|');
116
- });
117
- it('delegates to customHandler when defined', async () => {
118
- // Gmail send is a custom handler
119
- mockExecute.mockResolvedValue({
120
- success: true,
121
- data: { id: 'sent-1', threadId: 'thread-1' },
122
- stderr: '',
123
- });
124
- const handler = generateHandler(manifest.services.gmail, patches.gmail);
125
- const result = await handler({
126
- operation: 'send',
127
- email: 'u@t.com',
128
- to: 'bob@t.com',
129
- subject: 'hello',
130
- body: 'hi bob',
131
- });
132
- expect(result.text).toContain('Email sent to bob@t.com');
133
- expect(result.refs).toHaveProperty('to', 'bob@t.com');
134
- });
135
- it('throws on unknown operation', async () => {
136
- const handler = generateHandler(manifest.services.gmail, patches.gmail);
137
- await expect(handler({ operation: 'nonexistent', email: 'u@t.com' })).rejects.toThrow('Unknown gmail operation: nonexistent');
138
- });
139
- it('applies afterExecute hook for gmail search hydration', async () => {
140
- // First call: messages.list returns IDs
141
- mockExecute.mockResolvedValueOnce({
142
- success: true,
143
- data: { messages: [{ id: 'msg-1' }, { id: 'msg-2' }] },
144
- stderr: '',
145
- });
146
- // Hydration calls for each message
147
- mockExecute.mockResolvedValueOnce({
148
- success: true,
149
- data: {
150
- id: 'msg-1', threadId: 't1', snippet: 'hello',
151
- payload: { headers: [
152
- { name: 'From', value: 'alice@t.com' },
153
- { name: 'Subject', value: 'Meeting' },
154
- { name: 'Date', value: '2024-01-15' },
155
- ] },
156
- },
157
- stderr: '',
158
- });
159
- mockExecute.mockResolvedValueOnce({
160
- success: true,
161
- data: {
162
- id: 'msg-2', threadId: 't2', snippet: 'world',
163
- payload: { headers: [
164
- { name: 'From', value: 'bob@t.com' },
165
- { name: 'Subject', value: 'Update' },
166
- { name: 'Date', value: '2024-01-16' },
167
- ] },
168
- },
169
- stderr: '',
170
- });
171
- const handler = generateHandler(manifest.services.gmail, patches.gmail);
172
- const result = await handler({ operation: 'search', email: 'u@t.com', query: 'test' });
173
- // Should have hydrated the messages
174
- expect(result.text).toContain('alice@t.com');
175
- expect(result.text).toContain('Meeting');
176
- expect(result.refs).toHaveProperty('count', 2);
177
- });
178
- });
179
- // ADR-303: the generator appends next-steps for custom handlers, so patches
180
- // no longer need to call nextSteps() inline. This is the architectural
181
- // guarantee that replaces the per-handler regression tests.
182
- describe('generateHandler — custom-handler next-steps wrapping', () => {
183
- const manifest = loadManifest();
184
- beforeEach(() => mockExecute.mockReset());
185
- it('appends next-steps to a custom handler response', async () => {
186
- // sheets.addSheet is a customHandler — its handler return has no footer,
187
- // but the factory should wrap with one from the next-steps registry.
188
- mockExecute.mockResolvedValueOnce({
189
- success: true,
190
- data: { replies: [{ addSheet: { properties: { sheetId: 42, title: 'T', gridProperties: {} } } }] },
191
- stderr: '',
192
- });
193
- const handler = generateHandler(manifest.services.sheets, patches.sheets);
194
- const result = await handler({
195
- operation: 'addSheet',
196
- email: 'u@t.com',
197
- spreadsheetId: 'sheet-123',
198
- title: 'T',
199
- });
200
- expect(result.text).toContain('Sheet added');
201
- expect(result.text).toContain('Next steps:');
202
- });
203
- it('resolves placeholder values from the input params on custom handlers', async () => {
204
- mockExecute.mockResolvedValueOnce({
205
- success: true,
206
- data: { replies: [{}] },
207
- stderr: '',
208
- });
209
- const handler = generateHandler(manifest.services.sheets, patches.sheets);
210
- const result = await handler({
211
- operation: 'renameSheet',
212
- email: 'u@t.com',
213
- spreadsheetId: 'sheet-xyz',
214
- sheetId: 0,
215
- title: 'Main',
216
- });
217
- // The sheets.renameSheet next-steps entry references <spreadsheetId> —
218
- // the generator's contextMap should have resolved it.
219
- expect(result.text).toContain('sheet-xyz');
220
- expect(result.text).not.toContain('<spreadsheetId>');
221
- });
222
- it('does not double-append next-steps (regression for ADR-303 migration)', async () => {
223
- mockExecute.mockResolvedValueOnce({
224
- success: true,
225
- data: { replies: [{ addSheet: { properties: { sheetId: 99, title: 'Q', gridProperties: {} } } }] },
226
- stderr: '',
227
- });
228
- const handler = generateHandler(manifest.services.sheets, patches.sheets);
229
- const result = await handler({
230
- operation: 'addSheet',
231
- email: 'u@t.com',
232
- spreadsheetId: 'sheet-123',
233
- title: 'Q',
234
- });
235
- // Exactly one footer marker in the response
236
- const matches = result.text.match(/---\n\*\*Next steps:\*\*/g) ?? [];
237
- expect(matches.length).toBe(1);
238
- });
239
- });
240
- //# sourceMappingURL=generator.test.js.map