@aaronsb/google-workspace-mcp 2.0.0-alpha.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.
Files changed (184) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +193 -0
  3. package/build/__helpers__/testSetup.d.ts +1 -0
  4. package/build/__helpers__/testSetup.js +6 -0
  5. package/build/__helpers__/testSetup.js.map +1 -0
  6. package/build/__tests__/accounts/credentials.test.d.ts +1 -0
  7. package/build/__tests__/accounts/credentials.test.js +129 -0
  8. package/build/__tests__/accounts/credentials.test.js.map +1 -0
  9. package/build/__tests__/accounts/registry.test.d.ts +1 -0
  10. package/build/__tests__/accounts/registry.test.js +74 -0
  11. package/build/__tests__/accounts/registry.test.js.map +1 -0
  12. package/build/__tests__/executor/errors.test.d.ts +1 -0
  13. package/build/__tests__/executor/errors.test.js +42 -0
  14. package/build/__tests__/executor/errors.test.js.map +1 -0
  15. package/build/__tests__/executor/gws.test.d.ts +1 -0
  16. package/build/__tests__/executor/gws.test.js +178 -0
  17. package/build/__tests__/executor/gws.test.js.map +1 -0
  18. package/build/__tests__/executor/paths.test.d.ts +1 -0
  19. package/build/__tests__/executor/paths.test.js +60 -0
  20. package/build/__tests__/executor/paths.test.js.map +1 -0
  21. package/build/__tests__/executor/workspace.test.d.ts +1 -0
  22. package/build/__tests__/executor/workspace.test.js +117 -0
  23. package/build/__tests__/executor/workspace.test.js.map +1 -0
  24. package/build/__tests__/factory/generator.test.d.ts +1 -0
  25. package/build/__tests__/factory/generator.test.js +178 -0
  26. package/build/__tests__/factory/generator.test.js.map +1 -0
  27. package/build/__tests__/factory/patch-coverage.test.d.ts +10 -0
  28. package/build/__tests__/factory/patch-coverage.test.js +148 -0
  29. package/build/__tests__/factory/patch-coverage.test.js.map +1 -0
  30. package/build/__tests__/factory/safety.test.d.ts +1 -0
  31. package/build/__tests__/factory/safety.test.js +107 -0
  32. package/build/__tests__/factory/safety.test.js.map +1 -0
  33. package/build/__tests__/integration/executor.test.d.ts +5 -0
  34. package/build/__tests__/integration/executor.test.js +46 -0
  35. package/build/__tests__/integration/executor.test.js.map +1 -0
  36. package/build/__tests__/integration/handlers.test.d.ts +6 -0
  37. package/build/__tests__/integration/handlers.test.js +95 -0
  38. package/build/__tests__/integration/handlers.test.js.map +1 -0
  39. package/build/__tests__/integration/setup.d.ts +19 -0
  40. package/build/__tests__/integration/setup.js +61 -0
  41. package/build/__tests__/integration/setup.js.map +1 -0
  42. package/build/__tests__/server/formatting/markdown.test.d.ts +1 -0
  43. package/build/__tests__/server/formatting/markdown.test.js +149 -0
  44. package/build/__tests__/server/formatting/markdown.test.js.map +1 -0
  45. package/build/__tests__/server/formatting/next-steps.test.d.ts +1 -0
  46. package/build/__tests__/server/formatting/next-steps.test.js +42 -0
  47. package/build/__tests__/server/formatting/next-steps.test.js.map +1 -0
  48. package/build/__tests__/server/handler.test.d.ts +1 -0
  49. package/build/__tests__/server/handler.test.js +97 -0
  50. package/build/__tests__/server/handler.test.js.map +1 -0
  51. package/build/__tests__/server/handlers/__mocks__/executor.d.ts +147 -0
  52. package/build/__tests__/server/handlers/__mocks__/executor.js +114 -0
  53. package/build/__tests__/server/handlers/__mocks__/executor.js.map +1 -0
  54. package/build/__tests__/server/handlers/accounts.test.d.ts +1 -0
  55. package/build/__tests__/server/handlers/accounts.test.js +127 -0
  56. package/build/__tests__/server/handlers/accounts.test.js.map +1 -0
  57. package/build/__tests__/server/handlers/calendar.test.d.ts +1 -0
  58. package/build/__tests__/server/handlers/calendar.test.js +95 -0
  59. package/build/__tests__/server/handlers/calendar.test.js.map +1 -0
  60. package/build/__tests__/server/handlers/drive.test.d.ts +1 -0
  61. package/build/__tests__/server/handlers/drive.test.js +81 -0
  62. package/build/__tests__/server/handlers/drive.test.js.map +1 -0
  63. package/build/__tests__/server/handlers/email.test.d.ts +1 -0
  64. package/build/__tests__/server/handlers/email.test.js +99 -0
  65. package/build/__tests__/server/handlers/email.test.js.map +1 -0
  66. package/build/__tests__/server/handlers/validate.test.d.ts +1 -0
  67. package/build/__tests__/server/handlers/validate.test.js +88 -0
  68. package/build/__tests__/server/handlers/validate.test.js.map +1 -0
  69. package/build/__tests__/server/queue.test.d.ts +1 -0
  70. package/build/__tests__/server/queue.test.js +194 -0
  71. package/build/__tests__/server/queue.test.js.map +1 -0
  72. package/build/__tests__/server/server.test.d.ts +7 -0
  73. package/build/__tests__/server/server.test.js +135 -0
  74. package/build/__tests__/server/server.test.js.map +1 -0
  75. package/build/__tests__/server/tools.test.d.ts +1 -0
  76. package/build/__tests__/server/tools.test.js +91 -0
  77. package/build/__tests__/server/tools.test.js.map +1 -0
  78. package/build/accounts/auth.d.ts +24 -0
  79. package/build/accounts/auth.js +118 -0
  80. package/build/accounts/auth.js.map +1 -0
  81. package/build/accounts/credentials.d.ts +11 -0
  82. package/build/accounts/credentials.js +52 -0
  83. package/build/accounts/credentials.js.map +1 -0
  84. package/build/accounts/index.d.ts +6 -0
  85. package/build/accounts/index.js +4 -0
  86. package/build/accounts/index.js.map +1 -0
  87. package/build/accounts/registry.d.ts +11 -0
  88. package/build/accounts/registry.js +62 -0
  89. package/build/accounts/registry.js.map +1 -0
  90. package/build/executor/errors.d.ts +15 -0
  91. package/build/executor/errors.js +37 -0
  92. package/build/executor/errors.js.map +1 -0
  93. package/build/executor/file-output.d.ts +23 -0
  94. package/build/executor/file-output.js +67 -0
  95. package/build/executor/file-output.js.map +1 -0
  96. package/build/executor/gws.d.ts +23 -0
  97. package/build/executor/gws.js +144 -0
  98. package/build/executor/gws.js.map +1 -0
  99. package/build/executor/index.d.ts +4 -0
  100. package/build/executor/index.js +4 -0
  101. package/build/executor/index.js.map +1 -0
  102. package/build/executor/paths.d.ts +6 -0
  103. package/build/executor/paths.js +25 -0
  104. package/build/executor/paths.js.map +1 -0
  105. package/build/executor/workspace.d.ts +38 -0
  106. package/build/executor/workspace.js +146 -0
  107. package/build/executor/workspace.js.map +1 -0
  108. package/build/factory/defaults.d.ts +8 -0
  109. package/build/factory/defaults.js +101 -0
  110. package/build/factory/defaults.js.map +1 -0
  111. package/build/factory/generator.d.ts +23 -0
  112. package/build/factory/generator.js +253 -0
  113. package/build/factory/generator.js.map +1 -0
  114. package/build/factory/manifest.yaml +852 -0
  115. package/build/factory/patches.d.ts +6 -0
  116. package/build/factory/patches.js +13 -0
  117. package/build/factory/patches.js.map +1 -0
  118. package/build/factory/registry.d.ts +12 -0
  119. package/build/factory/registry.js +18 -0
  120. package/build/factory/registry.js.map +1 -0
  121. package/build/factory/safety.d.ts +68 -0
  122. package/build/factory/safety.js +157 -0
  123. package/build/factory/safety.js.map +1 -0
  124. package/build/factory/types.d.ts +102 -0
  125. package/build/factory/types.js +6 -0
  126. package/build/factory/types.js.map +1 -0
  127. package/build/factory/yaml.d.ts +2 -0
  128. package/build/factory/yaml.js +3 -0
  129. package/build/factory/yaml.js.map +1 -0
  130. package/build/index.d.ts +2 -0
  131. package/build/index.js +14 -0
  132. package/build/index.js.map +1 -0
  133. package/build/server/formatting/markdown.d.ts +21 -0
  134. package/build/server/formatting/markdown.js +324 -0
  135. package/build/server/formatting/markdown.js.map +1 -0
  136. package/build/server/formatting/next-steps.d.ts +9 -0
  137. package/build/server/formatting/next-steps.js +123 -0
  138. package/build/server/formatting/next-steps.js.map +1 -0
  139. package/build/server/handler.d.ts +3 -0
  140. package/build/server/handler.js +24 -0
  141. package/build/server/handler.js.map +1 -0
  142. package/build/server/handlers/accounts.d.ts +2 -0
  143. package/build/server/handlers/accounts.js +181 -0
  144. package/build/server/handlers/accounts.js.map +1 -0
  145. package/build/server/handlers/calendar.d.ts +2 -0
  146. package/build/server/handlers/calendar.js +93 -0
  147. package/build/server/handlers/calendar.js.map +1 -0
  148. package/build/server/handlers/drive.d.ts +2 -0
  149. package/build/server/handlers/drive.js +74 -0
  150. package/build/server/handlers/drive.js.map +1 -0
  151. package/build/server/handlers/email.d.ts +2 -0
  152. package/build/server/handlers/email.js +115 -0
  153. package/build/server/handlers/email.js.map +1 -0
  154. package/build/server/handlers/validate.d.ts +3 -0
  155. package/build/server/handlers/validate.js +22 -0
  156. package/build/server/handlers/validate.js.map +1 -0
  157. package/build/server/handlers/workspace.d.ts +9 -0
  158. package/build/server/handlers/workspace.js +110 -0
  159. package/build/server/handlers/workspace.js.map +1 -0
  160. package/build/server/index.d.ts +4 -0
  161. package/build/server/index.js +4 -0
  162. package/build/server/index.js.map +1 -0
  163. package/build/server/queue.d.ts +11 -0
  164. package/build/server/queue.js +141 -0
  165. package/build/server/queue.js.map +1 -0
  166. package/build/server/server.d.ts +3 -0
  167. package/build/server/server.js +214 -0
  168. package/build/server/server.js.map +1 -0
  169. package/build/server/tools.d.ts +13 -0
  170. package/build/server/tools.js +99 -0
  171. package/build/server/tools.js.map +1 -0
  172. package/build/services/calendar/patch.d.ts +11 -0
  173. package/build/services/calendar/patch.js +116 -0
  174. package/build/services/calendar/patch.js.map +1 -0
  175. package/build/services/drive/patch.d.ts +10 -0
  176. package/build/services/drive/patch.js +131 -0
  177. package/build/services/drive/patch.js.map +1 -0
  178. package/build/services/gmail/attachments.d.ts +19 -0
  179. package/build/services/gmail/attachments.js +90 -0
  180. package/build/services/gmail/attachments.js.map +1 -0
  181. package/build/services/gmail/patch.d.ts +10 -0
  182. package/build/services/gmail/patch.js +226 -0
  183. package/build/services/gmail/patch.js.map +1 -0
  184. package/package.json +34 -0
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Patch coverage report — tracks which operations have custom formatting,
3
+ * custom handlers, or hooks vs falling through to defaults.
4
+ *
5
+ * This isn't a pass/fail gate — it's a visibility tool. When the manifest
6
+ * expands, this test shows which new operations are using defaults and
7
+ * might benefit from a patch. When patches change, it catches regressions
8
+ * where an operation silently loses its custom formatter.
9
+ */
10
+ import { loadManifest } from '../../factory/generator.js';
11
+ import { patches } from '../../factory/patches.js';
12
+ // Mock executor — patches that call execute() in custom handlers need this
13
+ jest.mock('../../executor/gws.js');
14
+ const manifest = loadManifest();
15
+ /** Check if a format hook dispatches to a custom formatter for a given operation. */
16
+ function probeFormatHook(hook, operation) {
17
+ if (!hook)
18
+ return false;
19
+ // Call the formatter with empty data and see if it produces something
20
+ // different from what the default would — we check by calling with a
21
+ // known operation name and seeing if the switch statement catches it
22
+ try {
23
+ const ctx = { operation, params: {}, account: 'test@test.com' };
24
+ const result = hook({}, ctx);
25
+ // If it returns without throwing, the hook handles this operation
26
+ return result !== undefined;
27
+ }
28
+ catch {
29
+ return false;
30
+ }
31
+ }
32
+ function buildCoverageMap() {
33
+ const entries = [];
34
+ for (const [serviceName, serviceDef] of Object.entries(manifest.services)) {
35
+ const patch = patches[serviceName];
36
+ for (const [opName, opDef] of Object.entries(serviceDef.operations)) {
37
+ entries.push({
38
+ service: serviceName,
39
+ operation: opName,
40
+ opType: opDef.type,
41
+ hasCustomHandler: Boolean(patch?.customHandlers?.[opName]),
42
+ hasBeforeExecute: Boolean(patch?.beforeExecute?.[opName]),
43
+ hasAfterExecute: Boolean(patch?.afterExecute?.[opName]),
44
+ usesCustomFormat: (() => {
45
+ if (patch?.customHandlers?.[opName])
46
+ return true; // custom handlers do their own formatting
47
+ switch (opDef.type) {
48
+ case 'list': return probeFormatHook(patch?.formatList, opName);
49
+ case 'detail': return probeFormatHook(patch?.formatDetail, opName);
50
+ case 'action': return probeFormatHook(patch?.formatAction, opName);
51
+ default: return false;
52
+ }
53
+ })(),
54
+ });
55
+ }
56
+ }
57
+ return entries;
58
+ }
59
+ describe('patch coverage', () => {
60
+ const coverage = buildCoverageMap();
61
+ it('reports coverage for all manifest operations', () => {
62
+ // This is the visibility test — it prints coverage so you can see gaps
63
+ const total = coverage.length;
64
+ const customFormatted = coverage.filter(e => e.usesCustomFormat).length;
65
+ const defaultFormatted = total - customFormatted;
66
+ const withHooks = coverage.filter(e => e.hasBeforeExecute || e.hasAfterExecute).length;
67
+ console.log(`\n Patch Coverage: ${customFormatted}/${total} operations have custom formatting`);
68
+ console.log(` Default formatting: ${defaultFormatted} operations`);
69
+ console.log(` Lifecycle hooks: ${withHooks} operations\n`);
70
+ // Log per-service breakdown
71
+ for (const serviceName of Object.keys(manifest.services)) {
72
+ const serviceOps = coverage.filter(e => e.service === serviceName);
73
+ const custom = serviceOps.filter(e => e.usesCustomFormat);
74
+ const defaults = serviceOps.filter(e => !e.usesCustomFormat);
75
+ console.log(` ${serviceName}: ${custom.length}/${serviceOps.length} custom`);
76
+ if (defaults.length > 0) {
77
+ console.log(` defaults: ${defaults.map(e => e.operation).join(', ')}`);
78
+ }
79
+ }
80
+ expect(total).toBeGreaterThan(0);
81
+ });
82
+ // Snapshot test — catches when operations silently lose their patches
83
+ it('gmail core operations have custom formatting', () => {
84
+ const gmailCustom = coverage
85
+ .filter(e => e.service === 'gmail' && e.usesCustomFormat)
86
+ .map(e => e.operation)
87
+ .sort();
88
+ expect(gmailCustom).toContain('search');
89
+ expect(gmailCustom).toContain('read');
90
+ expect(gmailCustom).toContain('triage');
91
+ expect(gmailCustom).toContain('labels');
92
+ expect(gmailCustom).toContain('threads');
93
+ expect(gmailCustom).toContain('send');
94
+ expect(gmailCustom).toContain('reply');
95
+ });
96
+ it('calendar core operations have custom formatting', () => {
97
+ const calCustom = coverage
98
+ .filter(e => e.service === 'calendar' && e.usesCustomFormat)
99
+ .map(e => e.operation)
100
+ .sort();
101
+ expect(calCustom).toContain('list');
102
+ expect(calCustom).toContain('get');
103
+ expect(calCustom).toContain('calendars');
104
+ expect(calCustom).toContain('agenda');
105
+ expect(calCustom).toContain('create');
106
+ expect(calCustom).toContain('delete');
107
+ });
108
+ it('drive core operations have custom formatting', () => {
109
+ const driveCustom = coverage
110
+ .filter(e => e.service === 'drive' && e.usesCustomFormat)
111
+ .map(e => e.operation)
112
+ .sort();
113
+ expect(driveCustom).toContain('search');
114
+ expect(driveCustom).toContain('get');
115
+ expect(driveCustom).toContain('upload');
116
+ expect(driveCustom).toContain('download');
117
+ });
118
+ it('gmail search has afterExecute hydration hook', () => {
119
+ const searchEntry = coverage.find(e => e.service === 'gmail' && e.operation === 'search');
120
+ expect(searchEntry?.hasAfterExecute).toBe(true);
121
+ });
122
+ it('calendar list has beforeExecute hook for timeMin default', () => {
123
+ const listEntry = coverage.find(e => e.service === 'calendar' && e.operation === 'list');
124
+ expect(listEntry?.hasBeforeExecute).toBe(true);
125
+ });
126
+ it('all operations have a patch or use defaults (no orphans)', () => {
127
+ // Every operation should either have custom formatting OR fall through
128
+ // to the default formatter. This catches operations that might error
129
+ // because neither path handles them.
130
+ for (const entry of coverage) {
131
+ const hasPath = entry.usesCustomFormat || !entry.usesCustomFormat; // always true, but...
132
+ // The real check: if there IS a patch for this service, the format
133
+ // hooks should handle all operation types without throwing
134
+ const patch = patches[entry.service];
135
+ if (patch) {
136
+ const ctx = { operation: entry.operation, params: {}, account: 'test@test.com' };
137
+ if (entry.opType === 'list' && patch.formatList) {
138
+ expect(() => patch.formatList({}, ctx)).not.toThrow();
139
+ }
140
+ if (entry.opType === 'detail' && patch.formatDetail) {
141
+ expect(() => patch.formatDetail({}, ctx)).not.toThrow();
142
+ }
143
+ }
144
+ expect(hasPath).toBe(true);
145
+ }
146
+ });
147
+ });
148
+ //# sourceMappingURL=patch-coverage.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"patch-coverage.test.js","sourceRoot":"","sources":["../../../src/__tests__/factory/patch-coverage.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,107 @@
1
+ import { configurePolicies, evaluatePolicies, draftOnlyEmail, noDelete, readOnly, auditLog, } from '../../factory/safety.js';
2
+ function ctx(operation) {
3
+ return { operation, params: {}, account: 'test@test.com' };
4
+ }
5
+ describe('draftOnlyEmail', () => {
6
+ beforeAll(() => configurePolicies([draftOnlyEmail]));
7
+ afterAll(() => configurePolicies([]));
8
+ it('blocks send', () => {
9
+ const result = evaluatePolicies([], ctx('send'), 'gmail');
10
+ expect(result.action).toBe('block');
11
+ expect(result.reason).toContain('draft-only');
12
+ });
13
+ it('blocks reply', () => {
14
+ expect(evaluatePolicies([], ctx('reply'), 'gmail').action).toBe('block');
15
+ });
16
+ it('blocks replyAll', () => {
17
+ expect(evaluatePolicies([], ctx('replyAll'), 'gmail').action).toBe('block');
18
+ });
19
+ it('blocks forward', () => {
20
+ expect(evaluatePolicies([], ctx('forward'), 'gmail').action).toBe('block');
21
+ });
22
+ it('allows search', () => {
23
+ expect(evaluatePolicies([], ctx('search'), 'gmail').action).toBe('allow');
24
+ });
25
+ it('allows triage', () => {
26
+ expect(evaluatePolicies([], ctx('triage'), 'gmail').action).toBe('allow');
27
+ });
28
+ it('allows read', () => {
29
+ expect(evaluatePolicies([], ctx('read'), 'gmail').action).toBe('allow');
30
+ });
31
+ it('does not apply to other services', () => {
32
+ expect(evaluatePolicies([], ctx('delete'), 'drive').action).toBe('allow');
33
+ });
34
+ });
35
+ describe('noDelete', () => {
36
+ beforeAll(() => configurePolicies([noDelete]));
37
+ afterAll(() => configurePolicies([]));
38
+ it('blocks drive delete', () => {
39
+ const result = evaluatePolicies([], ctx('delete'), 'drive');
40
+ expect(result.action).toBe('block');
41
+ expect(result.reason).toContain('no-delete');
42
+ });
43
+ it('blocks calendar delete', () => {
44
+ expect(evaluatePolicies([], ctx('delete'), 'calendar').action).toBe('block');
45
+ });
46
+ it('blocks task delete', () => {
47
+ expect(evaluatePolicies([], ctx('delete'), 'tasks').action).toBe('block');
48
+ });
49
+ it('blocks deleteTaskList', () => {
50
+ expect(evaluatePolicies([], ctx('deleteTaskList'), 'tasks').action).toBe('block');
51
+ });
52
+ it('allows gmail trash (reversible)', () => {
53
+ expect(evaluatePolicies([], ctx('trash'), 'gmail').action).toBe('allow');
54
+ });
55
+ it('allows search', () => {
56
+ expect(evaluatePolicies([], ctx('search'), 'drive').action).toBe('allow');
57
+ });
58
+ });
59
+ describe('readOnly', () => {
60
+ beforeAll(() => configurePolicies([readOnly]));
61
+ afterAll(() => configurePolicies([]));
62
+ it('allows search', () => {
63
+ expect(evaluatePolicies([], ctx('search'), 'gmail').action).toBe('allow');
64
+ });
65
+ it('allows list', () => {
66
+ expect(evaluatePolicies([], ctx('list'), 'calendar').action).toBe('allow');
67
+ });
68
+ it('allows triage', () => {
69
+ expect(evaluatePolicies([], ctx('triage'), 'gmail').action).toBe('allow');
70
+ });
71
+ it('blocks send', () => {
72
+ expect(evaluatePolicies([], ctx('send'), 'gmail').action).toBe('block');
73
+ });
74
+ it('blocks create', () => {
75
+ expect(evaluatePolicies([], ctx('create'), 'calendar').action).toBe('block');
76
+ });
77
+ it('blocks delete', () => {
78
+ expect(evaluatePolicies([], ctx('delete'), 'drive').action).toBe('block');
79
+ });
80
+ it('blocks upload', () => {
81
+ expect(evaluatePolicies([], ctx('upload'), 'drive').action).toBe('block');
82
+ });
83
+ });
84
+ describe('policy composition', () => {
85
+ beforeAll(() => configurePolicies([auditLog, draftOnlyEmail, noDelete]));
86
+ afterAll(() => configurePolicies([]));
87
+ it('audit allows but draftOnly blocks send', () => {
88
+ const result = evaluatePolicies([], ctx('send'), 'gmail');
89
+ expect(result.action).toBe('block');
90
+ });
91
+ it('audit allows and noDelete blocks drive delete', () => {
92
+ const result = evaluatePolicies([], ctx('delete'), 'drive');
93
+ expect(result.action).toBe('block');
94
+ });
95
+ it('all allow search', () => {
96
+ const result = evaluatePolicies([], ctx('search'), 'gmail');
97
+ expect(result.action).toBe('allow');
98
+ });
99
+ });
100
+ describe('no policies configured', () => {
101
+ beforeAll(() => configurePolicies([]));
102
+ it('allows everything', () => {
103
+ expect(evaluatePolicies([], ctx('send'), 'gmail').action).toBe('allow');
104
+ expect(evaluatePolicies([], ctx('delete'), 'drive').action).toBe('allow');
105
+ });
106
+ });
107
+ //# sourceMappingURL=safety.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"safety.test.js","sourceRoot":"","sources":["../../../src/__tests__/factory/safety.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Integration tests for the executor layer.
3
+ * Runs real gws commands against the first configured account.
4
+ */
5
+ export {};
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Integration tests for the executor layer.
3
+ * Runs real gws commands against the first configured account.
4
+ */
5
+ import { execute, gwsVersion } from '../../executor/gws.js';
6
+ import { GwsError } from '../../executor/errors.js';
7
+ import { getTestAccount } from './setup.js';
8
+ const account = getTestAccount();
9
+ const describeIf = account ? describe : describe.skip;
10
+ describeIf('executor (integration)', () => {
11
+ it('gwsVersion returns a version string', async () => {
12
+ const version = await gwsVersion();
13
+ expect(version).toMatch(/gws\s+\d+\.\d+/);
14
+ }, 10_000);
15
+ it('executes gmail messages list with account credentials', async () => {
16
+ const result = await execute(['gmail', 'users', 'messages', 'list', '--params', JSON.stringify({ userId: 'me', maxResults: 1 })], { account: account.email });
17
+ expect(result.success).toBe(true);
18
+ expect(result.data).toHaveProperty('messages');
19
+ const messages = result.data.messages;
20
+ expect(Array.isArray(messages)).toBe(true);
21
+ expect(messages.length).toBeGreaterThan(0);
22
+ expect(messages[0]).toHaveProperty('id');
23
+ }, 15_000);
24
+ it('executes calendar list', async () => {
25
+ const result = await execute(['calendar', 'calendarList', 'list'], { account: account.email });
26
+ expect(result.success).toBe(true);
27
+ expect(result.data).toBeDefined();
28
+ }, 15_000);
29
+ it('rejects with GwsError for invalid API call', async () => {
30
+ await expect(execute(['gmail', 'users', 'messages', 'get', '--params', JSON.stringify({ userId: 'me', id: 'nonexistent-message-id-xxxxx' })], { account: account.email })).rejects.toThrow(GwsError);
31
+ }, 15_000);
32
+ it('maps exit codes correctly on auth error', async () => {
33
+ // Use a bogus credential file to trigger auth error
34
+ try {
35
+ await execute(['gmail', 'users', 'messages', 'list', '--params', JSON.stringify({ userId: 'me', maxResults: 1 })], { account: 'nonexistent-account@bogus.invalid' });
36
+ fail('Should have thrown');
37
+ }
38
+ catch (err) {
39
+ expect(err).toBeInstanceOf(GwsError);
40
+ // Could be AuthError (2) or InternalError (5) depending on gws behavior
41
+ // with a missing credential file — either way it should be a GwsError
42
+ expect(err.exitCode).toBeGreaterThan(0);
43
+ }
44
+ }, 15_000);
45
+ });
46
+ //# sourceMappingURL=executor.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"executor.test.js","sourceRoot":"","sources":["../../../src/__tests__/integration/executor.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Integration tests for handler layer.
3
+ * Verifies that handlers produce correctly shaped markdown output
4
+ * when talking to real Google APIs through gws.
5
+ */
6
+ export {};
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Integration tests for handler layer.
3
+ * Verifies that handlers produce correctly shaped markdown output
4
+ * when talking to real Google APIs through gws.
5
+ */
6
+ // Mock registry — avoids import.meta.url in Jest
7
+ jest.mock('../../factory/registry.js', () => {
8
+ const { loadManifest, generateTools } = jest.requireActual('../../factory/generator.js');
9
+ const { patches } = jest.requireActual('../../factory/patches.js');
10
+ const manifest = loadManifest();
11
+ return { manifest, generatedTools: generateTools(manifest, patches) };
12
+ });
13
+ import { handleEmail } from '../../server/handlers/email.js';
14
+ import { handleCalendar } from '../../server/handlers/calendar.js';
15
+ import { handleDrive } from '../../server/handlers/drive.js';
16
+ import { handleAccounts } from '../../server/handlers/accounts.js';
17
+ import { getTestAccount } from './setup.js';
18
+ const account = getTestAccount();
19
+ const describeIf = account ? describe : describe.skip;
20
+ describeIf('handlers (integration)', () => {
21
+ describe('manage_accounts', () => {
22
+ it('list returns markdown with accounts and next-steps', async () => {
23
+ const result = await handleAccounts({ operation: 'list' });
24
+ expect(result.text).toContain('## Accounts');
25
+ expect(result.text).toContain('[x]'); // at least one account with credentials
26
+ expect(result.text).toContain('**Next steps:**');
27
+ expect(result.refs.count).toBeGreaterThan(0);
28
+ expect(result.refs.accounts).toBeDefined();
29
+ }, 10_000);
30
+ });
31
+ describe('manage_email', () => {
32
+ it('search returns markdown email list', async () => {
33
+ const result = await handleEmail({
34
+ operation: 'search',
35
+ email: account.email,
36
+ query: 'in:inbox',
37
+ maxResults: 3,
38
+ });
39
+ expect(typeof result.text).toBe('string');
40
+ expect(result.text.length).toBeGreaterThan(0);
41
+ expect(result.text).toContain('**Next steps:**');
42
+ expect(result.refs.count).toBeGreaterThanOrEqual(0);
43
+ }, 15_000);
44
+ it('read returns markdown email detail', async () => {
45
+ // First get a message ID
46
+ const list = await handleEmail({
47
+ operation: 'search',
48
+ email: account.email,
49
+ query: 'in:inbox',
50
+ maxResults: 1,
51
+ });
52
+ if (list.refs.count === 0) {
53
+ console.warn('No emails in inbox — skipping read test');
54
+ return;
55
+ }
56
+ const messageId = list.refs.messageId;
57
+ const result = await handleEmail({
58
+ operation: 'read',
59
+ email: account.email,
60
+ messageId,
61
+ });
62
+ expect(result.text).toContain('**From:**');
63
+ expect(result.text).toContain('**Next steps:**');
64
+ expect(result.refs.from).toBeDefined();
65
+ expect(result.refs.subject).toBeDefined();
66
+ }, 20_000);
67
+ });
68
+ describe('manage_calendar', () => {
69
+ it('list returns markdown events', async () => {
70
+ const result = await handleCalendar({
71
+ operation: 'list',
72
+ email: account.email,
73
+ maxResults: 5,
74
+ });
75
+ expect(typeof result.text).toBe('string');
76
+ expect(result.text.length).toBeGreaterThan(0);
77
+ expect(result.text).toContain('**Next steps:**');
78
+ expect(result.refs.count).toBeGreaterThanOrEqual(0);
79
+ }, 15_000);
80
+ });
81
+ describe('manage_drive', () => {
82
+ it('search returns markdown file list', async () => {
83
+ const result = await handleDrive({
84
+ operation: 'search',
85
+ email: account.email,
86
+ maxResults: 3,
87
+ });
88
+ expect(typeof result.text).toBe('string');
89
+ expect(result.text.length).toBeGreaterThan(0);
90
+ expect(result.text).toContain('**Next steps:**');
91
+ expect(result.refs.count).toBeGreaterThanOrEqual(0);
92
+ }, 15_000);
93
+ });
94
+ });
95
+ //# sourceMappingURL=handlers.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handlers.test.js","sourceRoot":"","sources":["../../../src/__tests__/integration/handlers.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Integration test setup — resolves test account from:
3
+ * 1. TEST_ACCOUNT env var (explicit override)
4
+ * 2. First account in accounts.json (default)
5
+ *
6
+ * Skips the entire suite if no accounts or credentials are configured,
7
+ * so CI without credentials just reports "skipped" rather than failing.
8
+ */
9
+ interface TestAccount {
10
+ email: string;
11
+ credentialFile: string;
12
+ }
13
+ export declare function getTestAccount(): TestAccount | null;
14
+ /**
15
+ * Call at the top of an integration describe block.
16
+ * Returns the test account or skips the suite.
17
+ */
18
+ export declare function requireTestAccount(): TestAccount;
19
+ export {};
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Integration test setup — resolves test account from:
3
+ * 1. TEST_ACCOUNT env var (explicit override)
4
+ * 2. First account in accounts.json (default)
5
+ *
6
+ * Skips the entire suite if no accounts or credentials are configured,
7
+ * so CI without credentials just reports "skipped" rather than failing.
8
+ */
9
+ import * as fs from 'node:fs';
10
+ import { accountsFilePath, credentialPath } from '../../executor/paths.js';
11
+ let cachedAccount;
12
+ export function getTestAccount() {
13
+ if (cachedAccount !== undefined)
14
+ return cachedAccount;
15
+ try {
16
+ // Prefer explicit override
17
+ const override = process.env.TEST_ACCOUNT;
18
+ if (override) {
19
+ const credFile = credentialPath(override);
20
+ if (!fs.existsSync(credFile)) {
21
+ console.warn(`TEST_ACCOUNT=${override} but no credential file at ${credFile}`);
22
+ cachedAccount = null;
23
+ return null;
24
+ }
25
+ cachedAccount = { email: override, credentialFile: credFile };
26
+ return cachedAccount;
27
+ }
28
+ // Fall back to first account in registry
29
+ const raw = fs.readFileSync(accountsFilePath(), 'utf-8');
30
+ const { accounts } = JSON.parse(raw);
31
+ if (!accounts?.length) {
32
+ cachedAccount = null;
33
+ return null;
34
+ }
35
+ const email = accounts[0].email;
36
+ const credFile = credentialPath(email);
37
+ if (!fs.existsSync(credFile)) {
38
+ cachedAccount = null;
39
+ return null;
40
+ }
41
+ cachedAccount = { email, credentialFile: credFile };
42
+ return cachedAccount;
43
+ }
44
+ catch {
45
+ cachedAccount = null;
46
+ return null;
47
+ }
48
+ }
49
+ /**
50
+ * Call at the top of an integration describe block.
51
+ * Returns the test account or skips the suite.
52
+ */
53
+ export function requireTestAccount() {
54
+ const account = getTestAccount();
55
+ if (!account) {
56
+ // Jest doesn't have suite-level skip, so we use a conditional describe pattern
57
+ throw new Error('No test account available — skipping integration tests');
58
+ }
59
+ return account;
60
+ }
61
+ //# sourceMappingURL=setup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup.js","sourceRoot":"","sources":["../../../src/__tests__/integration/setup.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAO3E,IAAI,aAA6C,CAAC;AAElD,MAAM,UAAU,cAAc;IAC5B,IAAI,aAAa,KAAK,SAAS;QAAE,OAAO,aAAa,CAAC;IAEtD,IAAI,CAAC;QACH,2BAA2B;QAC3B,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;QAC1C,IAAI,QAAQ,EAAE,CAAC;YACb,MAAM,QAAQ,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAC;YAC1C,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC7B,OAAO,CAAC,IAAI,CAAC,gBAAgB,QAAQ,8BAA8B,QAAQ,EAAE,CAAC,CAAC;gBAC/E,aAAa,GAAG,IAAI,CAAC;gBACrB,OAAO,IAAI,CAAC;YACd,CAAC;YACD,aAAa,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,QAAQ,EAAE,CAAC;YAC9D,OAAO,aAAa,CAAC;QACvB,CAAC;QAED,yCAAyC;QACzC,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,gBAAgB,EAAE,EAAE,OAAO,CAAC,CAAC;QACzD,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACrC,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAC;YACtB,aAAa,GAAG,IAAI,CAAC;YACrB,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,KAAK,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;QAChC,MAAM,QAAQ,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;QAEvC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC7B,aAAa,GAAG,IAAI,CAAC;YACrB,OAAO,IAAI,CAAC;QACd,CAAC;QAED,aAAa,GAAG,EAAE,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,CAAC;QACpD,OAAO,aAAa,CAAC;IACvB,CAAC;IAAC,MAAM,CAAC;QACP,aAAa,GAAG,IAAI,CAAC;QACrB,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,kBAAkB;IAChC,MAAM,OAAO,GAAG,cAAc,EAAE,CAAC;IACjC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,+EAA+E;QAC/E,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAC;IAC5E,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}