gogcli-mcp-sheets 2.8.0 → 2.18.1
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 +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/dist/index.js +19992 -19655
- package/manifest.json +18 -2
- package/package.json +3 -2
- package/server.json +2 -2
- package/src/index.ts +7 -9
- package/src/tools/sheets-extra.ts +58 -17
- package/tests/tools/sheets-extra.test.ts +506 -412
- package/tsconfig.json +1 -1
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
2
|
import { registerExtraSheetsTools } from '../../src/tools/sheets-extra.js';
|
|
3
3
|
import * as lib from '../../../gogcli-mcp/src/lib.js';
|
|
4
|
-
import {
|
|
4
|
+
import { createTestHarness } from '@chrischall/mcp-utils/test';
|
|
5
|
+
import { rawTextResult } from '@chrischall/mcp-utils';
|
|
5
6
|
|
|
6
7
|
vi.mock('../../../gogcli-mcp/src/lib.js', async (importOriginal) => {
|
|
7
8
|
const actual = await importOriginal<typeof lib>();
|
|
@@ -13,23 +14,23 @@ vi.mock('../../../gogcli-mcp/src/lib.js', async (importOriginal) => {
|
|
|
13
14
|
};
|
|
14
15
|
});
|
|
15
16
|
|
|
16
|
-
const setupHandlers = () =>
|
|
17
|
+
const setupHandlers = () => createTestHarness(registerExtraSheetsTools);
|
|
17
18
|
|
|
18
19
|
beforeEach(() => vi.clearAllMocks());
|
|
19
20
|
|
|
20
21
|
// 1. add-tab
|
|
21
22
|
describe('gog_sheets_add_tab', () => {
|
|
22
23
|
it('calls runOrDiagnose with correct args', async () => {
|
|
23
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
24
|
-
const
|
|
25
|
-
await
|
|
24
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
25
|
+
const harness = await setupHandlers();
|
|
26
|
+
await harness.callTool('gog_sheets_add_tab', { spreadsheetId: 'sid', tabName: 'NewTab' });
|
|
26
27
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'add-tab', 'sid', 'NewTab'], { account: undefined });
|
|
27
28
|
});
|
|
28
29
|
|
|
29
30
|
it('forwards account', async () => {
|
|
30
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
31
|
-
const
|
|
32
|
-
await
|
|
31
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
32
|
+
const harness = await setupHandlers();
|
|
33
|
+
await harness.callTool('gog_sheets_add_tab', { spreadsheetId: 'sid', tabName: 'T', account: 'a@b.com' });
|
|
33
34
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'add-tab', 'sid', 'T'], { account: 'a@b.com' });
|
|
34
35
|
});
|
|
35
36
|
});
|
|
@@ -37,19 +38,19 @@ describe('gog_sheets_add_tab', () => {
|
|
|
37
38
|
// 2. delete-tab
|
|
38
39
|
describe('gog_sheets_delete_tab', () => {
|
|
39
40
|
it('calls runOrDiagnose with correct args', async () => {
|
|
40
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
41
|
-
const
|
|
42
|
-
await
|
|
43
|
-
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'delete-tab', 'sid', 'OldTab'], { account: undefined });
|
|
41
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
42
|
+
const harness = await setupHandlers();
|
|
43
|
+
await harness.callTool('gog_sheets_delete_tab', { spreadsheetId: 'sid', tabName: 'OldTab' });
|
|
44
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'delete-tab', 'sid', 'OldTab', '--force'], { account: undefined });
|
|
44
45
|
});
|
|
45
46
|
});
|
|
46
47
|
|
|
47
48
|
// 3. rename-tab
|
|
48
49
|
describe('gog_sheets_rename_tab', () => {
|
|
49
50
|
it('calls runOrDiagnose with correct args', async () => {
|
|
50
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
51
|
-
const
|
|
52
|
-
await
|
|
51
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
52
|
+
const harness = await setupHandlers();
|
|
53
|
+
await harness.callTool('gog_sheets_rename_tab', { spreadsheetId: 'sid', oldName: 'Old', newName: 'New' });
|
|
53
54
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'rename-tab', 'sid', 'Old', 'New'], { account: undefined });
|
|
54
55
|
});
|
|
55
56
|
});
|
|
@@ -57,16 +58,16 @@ describe('gog_sheets_rename_tab', () => {
|
|
|
57
58
|
// 4. copy
|
|
58
59
|
describe('gog_sheets_copy', () => {
|
|
59
60
|
it('calls runOrDiagnose with correct args', async () => {
|
|
60
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
61
|
-
const
|
|
62
|
-
await
|
|
61
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
62
|
+
const harness = await setupHandlers();
|
|
63
|
+
await harness.callTool('gog_sheets_copy', { spreadsheetId: 'sid', title: 'Copy' });
|
|
63
64
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'copy', 'sid', 'Copy'], { account: undefined });
|
|
64
65
|
});
|
|
65
66
|
|
|
66
67
|
it('includes --parent when provided', async () => {
|
|
67
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
68
|
-
const
|
|
69
|
-
await
|
|
68
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
69
|
+
const harness = await setupHandlers();
|
|
70
|
+
await harness.callTool('gog_sheets_copy', { spreadsheetId: 'sid', title: 'Copy', parent: 'folderId' });
|
|
70
71
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'copy', 'sid', 'Copy', '--parent=folderId'], { account: undefined });
|
|
71
72
|
});
|
|
72
73
|
});
|
|
@@ -74,40 +75,47 @@ describe('gog_sheets_copy', () => {
|
|
|
74
75
|
// 5. export
|
|
75
76
|
describe('gog_sheets_export', () => {
|
|
76
77
|
it('calls runOrDiagnose with correct args', async () => {
|
|
77
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
78
|
-
const
|
|
79
|
-
await
|
|
78
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
79
|
+
const harness = await setupHandlers();
|
|
80
|
+
await harness.callTool('gog_sheets_export', { spreadsheetId: 'sid' });
|
|
80
81
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'export', 'sid'], { account: undefined });
|
|
81
82
|
});
|
|
82
83
|
|
|
83
84
|
it('includes --format and --out when provided', async () => {
|
|
84
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
85
|
-
const
|
|
86
|
-
await
|
|
85
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
86
|
+
const harness = await setupHandlers();
|
|
87
|
+
await harness.callTool('gog_sheets_export', { spreadsheetId: 'sid', format: 'pdf', out: '/tmp/out.pdf' });
|
|
87
88
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'export', 'sid', '--format=pdf', '--out=/tmp/out.pdf'], { account: undefined });
|
|
88
89
|
});
|
|
90
|
+
|
|
91
|
+
it('includes --overwrite when requested', async () => {
|
|
92
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
93
|
+
const harness = await setupHandlers();
|
|
94
|
+
await harness.callTool('gog_sheets_export', { spreadsheetId: 'sid', out: '/tmp/out.csv', overwrite: true });
|
|
95
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'export', 'sid', '--out=/tmp/out.csv', '--overwrite'], { account: undefined });
|
|
96
|
+
});
|
|
89
97
|
});
|
|
90
98
|
|
|
91
99
|
// 6. freeze
|
|
92
100
|
describe('gog_sheets_freeze', () => {
|
|
93
101
|
it('calls runOrDiagnose with no optional flags', async () => {
|
|
94
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
95
|
-
const
|
|
96
|
-
await
|
|
102
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
103
|
+
const harness = await setupHandlers();
|
|
104
|
+
await harness.callTool('gog_sheets_freeze', { spreadsheetId: 'sid' });
|
|
97
105
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'freeze', 'sid'], { account: undefined });
|
|
98
106
|
});
|
|
99
107
|
|
|
100
108
|
it('includes --rows, --cols, --sheet when provided', async () => {
|
|
101
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
102
|
-
const
|
|
103
|
-
await
|
|
109
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
110
|
+
const harness = await setupHandlers();
|
|
111
|
+
await harness.callTool('gog_sheets_freeze', { spreadsheetId: 'sid', rows: 1, cols: 2, sheet: 'Data' });
|
|
104
112
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'freeze', 'sid', '--rows=1', '--cols=2', '--sheet=Data'], { account: undefined });
|
|
105
113
|
});
|
|
106
114
|
|
|
107
115
|
it('includes --rows=0 when rows is 0', async () => {
|
|
108
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
109
|
-
const
|
|
110
|
-
await
|
|
116
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
117
|
+
const harness = await setupHandlers();
|
|
118
|
+
await harness.callTool('gog_sheets_freeze', { spreadsheetId: 'sid', rows: 0 });
|
|
111
119
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'freeze', 'sid', '--rows=0'], { account: undefined });
|
|
112
120
|
});
|
|
113
121
|
});
|
|
@@ -118,42 +126,42 @@ describe('gog_sheets_insert', () => {
|
|
|
118
126
|
// gog's positional <start> is 1-based and, without --after, it inserts *before* it,
|
|
119
127
|
// so API start_index = positional - 1. To land at our 0-based start we send start+1.
|
|
120
128
|
// start=5 → positional 6 → API start_index 5.
|
|
121
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
122
|
-
const
|
|
123
|
-
await
|
|
129
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
130
|
+
const harness = await setupHandlers();
|
|
131
|
+
await harness.callTool('gog_sheets_insert', { spreadsheetId: 'sid', sheet: 'Sheet1', dimension: 'ROWS', start: 5 });
|
|
124
132
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'insert', 'sid', 'Sheet1', 'ROWS', '6'], { account: undefined });
|
|
125
133
|
});
|
|
126
134
|
|
|
127
135
|
it('bug: start=1, after:false inserts before 0-based index 1 (new row 2), not at the top', async () => {
|
|
128
136
|
// Regression for the reported "range.startIndex must be set" / wrong-placement bug.
|
|
129
137
|
// start=1 → positional 2 → gog API start_index 1 (insert before the 2nd row).
|
|
130
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
131
|
-
const
|
|
132
|
-
await
|
|
138
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
139
|
+
const harness = await setupHandlers();
|
|
140
|
+
await harness.callTool('gog_sheets_insert', { spreadsheetId: 'sid', sheet: 'S1', dimension: 'ROWS', start: 1, count: 1 });
|
|
133
141
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'insert', 'sid', 'S1', 'ROWS', '2', '--count=1'], { account: undefined });
|
|
134
142
|
});
|
|
135
143
|
|
|
136
144
|
it('shifts start by +1 when after:true so the new dimension lands AFTER start', async () => {
|
|
137
145
|
// Issue #42: with after:true, start=0 means "insert after index 0" — i.e. land at index 1.
|
|
138
146
|
// The MCP must compensate for the CLI's 1-based interpretation by passing start+1.
|
|
139
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
140
|
-
const
|
|
141
|
-
await
|
|
147
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
148
|
+
const harness = await setupHandlers();
|
|
149
|
+
await harness.callTool('gog_sheets_insert', { spreadsheetId: 'sid', sheet: 'S1', dimension: 'COLUMNS', start: 0, count: 3, after: true });
|
|
142
150
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'insert', 'sid', 'S1', 'COLUMNS', '1', '--count=3', '--after'], { account: undefined });
|
|
143
151
|
});
|
|
144
152
|
|
|
145
153
|
it('issue #42 acceptance: start=28, after:true leaves column 28 untouched (insertion lands at 29)', async () => {
|
|
146
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
147
|
-
const
|
|
148
|
-
await
|
|
154
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
155
|
+
const harness = await setupHandlers();
|
|
156
|
+
await harness.callTool('gog_sheets_insert', { spreadsheetId: 'sid', sheet: 'S1', dimension: 'COLUMNS', start: 28, after: true, count: 1 });
|
|
149
157
|
// CLI is 1-based; with --after CLI uses startIndex = c.Start, so passing 29 → API startIndex=29.
|
|
150
158
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'insert', 'sid', 'S1', 'COLUMNS', '29', '--count=1', '--after'], { account: undefined });
|
|
151
159
|
});
|
|
152
160
|
|
|
153
161
|
it('includes --count=0 when count is 0', async () => {
|
|
154
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
155
|
-
const
|
|
156
|
-
await
|
|
162
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
163
|
+
const harness = await setupHandlers();
|
|
164
|
+
await harness.callTool('gog_sheets_insert', { spreadsheetId: 'sid', sheet: 'S1', dimension: 'ROWS', start: 0, count: 0 });
|
|
157
165
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'insert', 'sid', 'S1', 'ROWS', '1', '--count=0'], { account: undefined });
|
|
158
166
|
});
|
|
159
167
|
|
|
@@ -161,46 +169,46 @@ describe('gog_sheets_insert', () => {
|
|
|
161
169
|
// start=0 → positional 1 → gog API start_index 0. Sending positional 0 would make gog
|
|
162
170
|
// reject the call outright ("start must be >= 1"), so the +1 shift is what makes a
|
|
163
171
|
// top-of-sheet insert reachable at all.
|
|
164
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
165
|
-
const
|
|
166
|
-
await
|
|
172
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
173
|
+
const harness = await setupHandlers();
|
|
174
|
+
await harness.callTool('gog_sheets_insert', { spreadsheetId: 'sid', sheet: 'S1', dimension: 'ROWS', start: 0, after: false });
|
|
167
175
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'insert', 'sid', 'S1', 'ROWS', '1'], { account: undefined });
|
|
168
176
|
});
|
|
169
177
|
|
|
170
178
|
it('passes --inherit-from-before=true when set', async () => {
|
|
171
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
172
|
-
const
|
|
173
|
-
await
|
|
179
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
180
|
+
const harness = await setupHandlers();
|
|
181
|
+
await harness.callTool('gog_sheets_insert', { spreadsheetId: 'sid', sheet: 'S1', dimension: 'ROWS', start: 5, after: true, inheritFromBefore: true });
|
|
174
182
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'insert', 'sid', 'S1', 'ROWS', '6', '--after', '--inherit-from-before=true'], { account: undefined });
|
|
175
183
|
});
|
|
176
184
|
|
|
177
185
|
it('passes --inherit-from-before=false to inherit from the following neighbor', async () => {
|
|
178
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
179
|
-
const
|
|
180
|
-
await
|
|
186
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
187
|
+
const harness = await setupHandlers();
|
|
188
|
+
await harness.callTool('gog_sheets_insert', { spreadsheetId: 'sid', sheet: 'S1', dimension: 'ROWS', start: 5, after: true, inheritFromBefore: false });
|
|
181
189
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'insert', 'sid', 'S1', 'ROWS', '6', '--after', '--inherit-from-before=false'], { account: undefined });
|
|
182
190
|
});
|
|
183
191
|
});
|
|
184
192
|
|
|
185
193
|
describe('gog_sheets_copy_paste', () => {
|
|
186
194
|
it('calls runOrDiagnose with required source/dest', async () => {
|
|
187
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
188
|
-
const
|
|
189
|
-
await
|
|
195
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
196
|
+
const harness = await setupHandlers();
|
|
197
|
+
await harness.callTool('gog_sheets_copy_paste', { spreadsheetId: 'sid', source: 'Sheet1!A2:H71', dest: 'Sheet1!A2:H120' });
|
|
190
198
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'copy-paste', 'sid', 'Sheet1!A2:H71', 'Sheet1!A2:H120'], { account: undefined });
|
|
191
199
|
});
|
|
192
200
|
|
|
193
201
|
it('appends --type and --transpose when provided', async () => {
|
|
194
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
195
|
-
const
|
|
196
|
-
await
|
|
202
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
203
|
+
const harness = await setupHandlers();
|
|
204
|
+
await harness.callTool('gog_sheets_copy_paste', { spreadsheetId: 'sid', source: 'A1:B2', dest: 'D1:E2', type: 'FORMULA', transpose: true });
|
|
197
205
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'copy-paste', 'sid', 'A1:B2', 'D1:E2', '--type=FORMULA', '--transpose'], { account: undefined });
|
|
198
206
|
});
|
|
199
207
|
|
|
200
208
|
it('forwards account', async () => {
|
|
201
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
202
|
-
const
|
|
203
|
-
await
|
|
209
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
210
|
+
const harness = await setupHandlers();
|
|
211
|
+
await harness.callTool('gog_sheets_copy_paste', { spreadsheetId: 'sid', source: 'A1', dest: 'B1', account: 'a@b.com' });
|
|
204
212
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'copy-paste', 'sid', 'A1', 'B1'], { account: 'a@b.com' });
|
|
205
213
|
});
|
|
206
214
|
});
|
|
@@ -208,16 +216,16 @@ describe('gog_sheets_copy_paste', () => {
|
|
|
208
216
|
// 8. merge
|
|
209
217
|
describe('gog_sheets_merge', () => {
|
|
210
218
|
it('calls runOrDiagnose with correct args', async () => {
|
|
211
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
212
|
-
const
|
|
213
|
-
await
|
|
219
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
220
|
+
const harness = await setupHandlers();
|
|
221
|
+
await harness.callTool('gog_sheets_merge', { spreadsheetId: 'sid', range: 'A1:C3' });
|
|
214
222
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'merge', 'sid', 'A1:C3'], { account: undefined });
|
|
215
223
|
});
|
|
216
224
|
|
|
217
225
|
it('includes --type when provided', async () => {
|
|
218
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
219
|
-
const
|
|
220
|
-
await
|
|
226
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
227
|
+
const harness = await setupHandlers();
|
|
228
|
+
await harness.callTool('gog_sheets_merge', { spreadsheetId: 'sid', range: 'A1:C3', type: 'MERGE_COLUMNS' });
|
|
221
229
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'merge', 'sid', 'A1:C3', '--type=MERGE_COLUMNS'], { account: undefined });
|
|
222
230
|
});
|
|
223
231
|
});
|
|
@@ -225,9 +233,9 @@ describe('gog_sheets_merge', () => {
|
|
|
225
233
|
// 9. unmerge
|
|
226
234
|
describe('gog_sheets_unmerge', () => {
|
|
227
235
|
it('calls runOrDiagnose with correct args', async () => {
|
|
228
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
229
|
-
const
|
|
230
|
-
await
|
|
236
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
237
|
+
const harness = await setupHandlers();
|
|
238
|
+
await harness.callTool('gog_sheets_unmerge', { spreadsheetId: 'sid', range: 'A1:C3' });
|
|
231
239
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'unmerge', 'sid', 'A1:C3'], { account: undefined });
|
|
232
240
|
});
|
|
233
241
|
});
|
|
@@ -235,10 +243,10 @@ describe('gog_sheets_unmerge', () => {
|
|
|
235
243
|
// 10. format
|
|
236
244
|
describe('gog_sheets_format', () => {
|
|
237
245
|
it('passes raw formatJson + formatFields through unchanged (escape hatch)', async () => {
|
|
238
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
239
|
-
const
|
|
246
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
247
|
+
const harness = await setupHandlers();
|
|
240
248
|
const fj = '{"textFormat":{"bold":true}}';
|
|
241
|
-
await
|
|
249
|
+
await harness.callTool('gog_sheets_format', { spreadsheetId: 'sid', range: 'A1:B2', formatJson: fj, formatFields: 'textFormat.bold' });
|
|
242
250
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
243
251
|
['sheets', 'format', 'sid', 'A1:B2', `--format-json=${fj}`, '--format-fields=textFormat.bold'],
|
|
244
252
|
{ account: undefined },
|
|
@@ -246,9 +254,9 @@ describe('gog_sheets_format', () => {
|
|
|
246
254
|
});
|
|
247
255
|
|
|
248
256
|
it('passes raw formatJson without formatFields', async () => {
|
|
249
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
250
|
-
const
|
|
251
|
-
await
|
|
257
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
258
|
+
const harness = await setupHandlers();
|
|
259
|
+
await harness.callTool('gog_sheets_format', { spreadsheetId: 'sid', range: 'A1', formatJson: '{}' });
|
|
252
260
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
253
261
|
['sheets', 'format', 'sid', 'A1', '--format-json={}'],
|
|
254
262
|
{ account: undefined },
|
|
@@ -256,9 +264,9 @@ describe('gog_sheets_format', () => {
|
|
|
256
264
|
});
|
|
257
265
|
|
|
258
266
|
it('composes named flags into CellFormat JSON + auto-computed fields', async () => {
|
|
259
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
260
|
-
const
|
|
261
|
-
await
|
|
267
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
268
|
+
const harness = await setupHandlers();
|
|
269
|
+
await harness.callTool('gog_sheets_format', {
|
|
262
270
|
spreadsheetId: 'sid', range: 'A1:C3',
|
|
263
271
|
bold: true,
|
|
264
272
|
backgroundColor: '#FFF5D9',
|
|
@@ -288,9 +296,9 @@ describe('gog_sheets_format', () => {
|
|
|
288
296
|
});
|
|
289
297
|
|
|
290
298
|
it('composes all named flags including textColor + alignment', async () => {
|
|
291
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
292
|
-
const
|
|
293
|
-
await
|
|
299
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
300
|
+
const harness = await setupHandlers();
|
|
301
|
+
await harness.callTool('gog_sheets_format', {
|
|
294
302
|
spreadsheetId: 'sid', range: 'A1',
|
|
295
303
|
italic: true,
|
|
296
304
|
underline: true,
|
|
@@ -315,33 +323,34 @@ describe('gog_sheets_format', () => {
|
|
|
315
323
|
});
|
|
316
324
|
|
|
317
325
|
it('rejects bad hex in textColor', async () => {
|
|
318
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
319
|
-
const
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
).
|
|
326
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
327
|
+
const harness = await setupHandlers();
|
|
328
|
+
// Thrown handler errors surface through the MCP RPC path as error results.
|
|
329
|
+
const result = await harness.callTool('gog_sheets_format', { spreadsheetId: 'sid', range: 'A1', textColor: 'not-a-hex' });
|
|
330
|
+
expect(result.isError).toBe(true);
|
|
331
|
+
expect(result.content[0].text).toMatch(/Invalid textColor/);
|
|
323
332
|
});
|
|
324
333
|
|
|
325
334
|
it('rejects bad hex in backgroundColor', async () => {
|
|
326
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
327
|
-
const
|
|
328
|
-
await
|
|
329
|
-
|
|
330
|
-
).
|
|
335
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
336
|
+
const harness = await setupHandlers();
|
|
337
|
+
const result = await harness.callTool('gog_sheets_format', { spreadsheetId: 'sid', range: 'A1', backgroundColor: 'orange' });
|
|
338
|
+
expect(result.isError).toBe(true);
|
|
339
|
+
expect(result.content[0].text).toMatch(/Invalid backgroundColor/);
|
|
331
340
|
});
|
|
332
341
|
|
|
333
342
|
it('rejects no-op calls with neither flags nor formatJson', async () => {
|
|
334
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
335
|
-
const
|
|
336
|
-
await
|
|
337
|
-
|
|
338
|
-
).
|
|
343
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
344
|
+
const harness = await setupHandlers();
|
|
345
|
+
const result = await harness.callTool('gog_sheets_format', { spreadsheetId: 'sid', range: 'A1' });
|
|
346
|
+
expect(result.isError).toBe(true);
|
|
347
|
+
expect(result.content[0].text).toMatch(/requires at least one named flag/);
|
|
339
348
|
});
|
|
340
349
|
|
|
341
350
|
it('honors caller-provided formatFields when also using named flags', async () => {
|
|
342
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
343
|
-
const
|
|
344
|
-
await
|
|
351
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
352
|
+
const harness = await setupHandlers();
|
|
353
|
+
await harness.callTool('gog_sheets_format', {
|
|
345
354
|
spreadsheetId: 'sid', range: 'A1',
|
|
346
355
|
bold: true,
|
|
347
356
|
formatFields: 'textFormat.bold,textFormat.italic',
|
|
@@ -353,9 +362,9 @@ describe('gog_sheets_format', () => {
|
|
|
353
362
|
|
|
354
363
|
describe('gog_sheets_list_tabs', () => {
|
|
355
364
|
it('uses metadata with a --select projection', async () => {
|
|
356
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
357
|
-
const
|
|
358
|
-
await
|
|
365
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
366
|
+
const harness = await setupHandlers();
|
|
367
|
+
await harness.callTool('gog_sheets_list_tabs', { spreadsheetId: 'sid' });
|
|
359
368
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
360
369
|
['sheets', 'metadata', 'sid', '--select=sheets.properties.sheetId,sheets.properties.title,sheets.properties.index,sheets.properties.gridProperties'],
|
|
361
370
|
{ account: undefined },
|
|
@@ -366,30 +375,30 @@ describe('gog_sheets_list_tabs', () => {
|
|
|
366
375
|
// 11. number-format
|
|
367
376
|
describe('gog_sheets_number_format', () => {
|
|
368
377
|
it('calls runOrDiagnose with correct args', async () => {
|
|
369
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
370
|
-
const
|
|
371
|
-
await
|
|
378
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
379
|
+
const harness = await setupHandlers();
|
|
380
|
+
await harness.callTool('gog_sheets_number_format', { spreadsheetId: 'sid', range: 'A1:A10' });
|
|
372
381
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'number-format', 'sid', 'A1:A10'], { account: undefined });
|
|
373
382
|
});
|
|
374
383
|
|
|
375
384
|
it('includes --type and --pattern when provided', async () => {
|
|
376
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
377
|
-
const
|
|
378
|
-
await
|
|
385
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
386
|
+
const harness = await setupHandlers();
|
|
387
|
+
await harness.callTool('gog_sheets_number_format', { spreadsheetId: 'sid', range: 'A1', type: 'CURRENCY', pattern: '#,##0.00' });
|
|
379
388
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'number-format', 'sid', 'A1', '--type=CURRENCY', '--pattern=#,##0.00'], { account: undefined });
|
|
380
389
|
});
|
|
381
390
|
|
|
382
391
|
// Issue #43: warn when DATE/DATE_TIME format applied to small integers (silent 1899/1900 render).
|
|
383
392
|
describe('DATE/DATE_TIME small-integer warning', () => {
|
|
384
393
|
it('emits a warning when DATE applied to a range of small integers', async () => {
|
|
385
|
-
const
|
|
394
|
+
const harness = await setupHandlers();
|
|
386
395
|
vi.mocked(lib.runOrDiagnose).mockImplementation(async (args: string[]) => {
|
|
387
396
|
if (args[1] === 'get') {
|
|
388
|
-
return
|
|
397
|
+
return rawTextResult(JSON.stringify({ range: 'Sheet1!A1:A3', values: [[1], [2], [3]] }));
|
|
389
398
|
}
|
|
390
|
-
return
|
|
399
|
+
return rawTextResult('{"ok":true}');
|
|
391
400
|
});
|
|
392
|
-
const result = await
|
|
401
|
+
const result = await harness.callTool('gog_sheets_number_format', { spreadsheetId: 'sid', range: 'Sheet1!A1:A3', type: 'DATE' });
|
|
393
402
|
// Peek call goes out first.
|
|
394
403
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
395
404
|
['sheets', 'get', 'sid', 'Sheet1!A1:A3', '--render=UNFORMATTED_VALUE'],
|
|
@@ -407,21 +416,21 @@ describe('gog_sheets_number_format', () => {
|
|
|
407
416
|
});
|
|
408
417
|
|
|
409
418
|
it('also warns for DATE_TIME', async () => {
|
|
410
|
-
const
|
|
419
|
+
const harness = await setupHandlers();
|
|
411
420
|
vi.mocked(lib.runOrDiagnose).mockImplementation(async (args: string[]) => {
|
|
412
421
|
if (args[1] === 'get') {
|
|
413
|
-
return
|
|
422
|
+
return rawTextResult(JSON.stringify({ range: 'Sheet1!A1:A2', values: [[5], [7]] }));
|
|
414
423
|
}
|
|
415
|
-
return
|
|
424
|
+
return rawTextResult('{"ok":true}');
|
|
416
425
|
});
|
|
417
|
-
const result = await
|
|
426
|
+
const result = await harness.callTool('gog_sheets_number_format', { spreadsheetId: 'sid', range: 'Sheet1!A1:A2', type: 'DATE_TIME' });
|
|
418
427
|
expect(result.content[0].text).toMatch(/warning/i);
|
|
419
428
|
});
|
|
420
429
|
|
|
421
430
|
it('does not peek or warn when force:true', async () => {
|
|
422
|
-
const
|
|
423
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
424
|
-
const result = await
|
|
431
|
+
const harness = await setupHandlers();
|
|
432
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{"ok":true}'));
|
|
433
|
+
const result = await harness.callTool('gog_sheets_number_format', { spreadsheetId: 'sid', range: 'Sheet1!A1:A3', type: 'DATE', force: true });
|
|
425
434
|
// Only the format call should fire — no peek.
|
|
426
435
|
expect(lib.runOrDiagnose).toHaveBeenCalledTimes(1);
|
|
427
436
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
@@ -432,22 +441,22 @@ describe('gog_sheets_number_format', () => {
|
|
|
432
441
|
});
|
|
433
442
|
|
|
434
443
|
it('peeks and warns when force:false is passed explicitly (round-trips the no-force path)', async () => {
|
|
435
|
-
const
|
|
444
|
+
const harness = await setupHandlers();
|
|
436
445
|
vi.mocked(lib.runOrDiagnose).mockImplementation(async (args: string[]) => {
|
|
437
446
|
if (args[1] === 'get') {
|
|
438
|
-
return
|
|
447
|
+
return rawTextResult(JSON.stringify({ range: 'A1:A3', values: [[1], [2], [3]] }));
|
|
439
448
|
}
|
|
440
|
-
return
|
|
449
|
+
return rawTextResult('{"ok":true}');
|
|
441
450
|
});
|
|
442
|
-
const result = await
|
|
451
|
+
const result = await harness.callTool('gog_sheets_number_format', { spreadsheetId: 'sid', range: 'A1:A3', type: 'DATE', force: false });
|
|
443
452
|
expect(lib.runOrDiagnose).toHaveBeenCalledTimes(2);
|
|
444
453
|
expect(result.content[0].text).toMatch(/warning/i);
|
|
445
454
|
});
|
|
446
455
|
|
|
447
456
|
it('does not peek when type is not DATE/DATE_TIME', async () => {
|
|
448
|
-
const
|
|
449
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
450
|
-
await
|
|
457
|
+
const harness = await setupHandlers();
|
|
458
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{"ok":true}'));
|
|
459
|
+
await harness.callTool('gog_sheets_number_format', { spreadsheetId: 'sid', range: 'A1:A3', type: 'CURRENCY' });
|
|
451
460
|
expect(lib.runOrDiagnose).toHaveBeenCalledTimes(1);
|
|
452
461
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
453
462
|
['sheets', 'number-format', 'sid', 'A1:A3', '--type=CURRENCY'],
|
|
@@ -456,112 +465,112 @@ describe('gog_sheets_number_format', () => {
|
|
|
456
465
|
});
|
|
457
466
|
|
|
458
467
|
it('does not warn when values include a large integer (>=10000)', async () => {
|
|
459
|
-
const
|
|
468
|
+
const harness = await setupHandlers();
|
|
460
469
|
vi.mocked(lib.runOrDiagnose).mockImplementation(async (args: string[]) => {
|
|
461
470
|
if (args[1] === 'get') {
|
|
462
471
|
// 45000 is a real day-serial (year ~2023) — legitimate date value.
|
|
463
|
-
return
|
|
472
|
+
return rawTextResult(JSON.stringify({ range: 'A1:A2', values: [[1], [45000]] }));
|
|
464
473
|
}
|
|
465
|
-
return
|
|
474
|
+
return rawTextResult('{"ok":true}');
|
|
466
475
|
});
|
|
467
|
-
const result = await
|
|
476
|
+
const result = await harness.callTool('gog_sheets_number_format', { spreadsheetId: 'sid', range: 'A1:A2', type: 'DATE' });
|
|
468
477
|
expect(result.content[0].text).not.toMatch(/warning/i);
|
|
469
478
|
});
|
|
470
479
|
|
|
471
480
|
it('does not warn when values include a negative integer (deltas/error codes, not day-serials)', async () => {
|
|
472
|
-
const
|
|
481
|
+
const harness = await setupHandlers();
|
|
473
482
|
vi.mocked(lib.runOrDiagnose).mockImplementation(async (args: string[]) => {
|
|
474
483
|
if (args[1] === 'get') {
|
|
475
|
-
return
|
|
484
|
+
return rawTextResult(JSON.stringify({ range: 'A1:A3', values: [[1], [-5], [3]] }));
|
|
476
485
|
}
|
|
477
|
-
return
|
|
486
|
+
return rawTextResult('{"ok":true}');
|
|
478
487
|
});
|
|
479
|
-
const result = await
|
|
488
|
+
const result = await harness.callTool('gog_sheets_number_format', { spreadsheetId: 'sid', range: 'A1:A3', type: 'DATE' });
|
|
480
489
|
expect(result.content[0].text).not.toMatch(/warning/i);
|
|
481
490
|
});
|
|
482
491
|
|
|
483
492
|
it('does not warn when values include non-integer numbers', async () => {
|
|
484
|
-
const
|
|
493
|
+
const harness = await setupHandlers();
|
|
485
494
|
vi.mocked(lib.runOrDiagnose).mockImplementation(async (args: string[]) => {
|
|
486
495
|
if (args[1] === 'get') {
|
|
487
|
-
return
|
|
496
|
+
return rawTextResult(JSON.stringify({ range: 'A1:A2', values: [[1], [2.5]] }));
|
|
488
497
|
}
|
|
489
|
-
return
|
|
498
|
+
return rawTextResult('{"ok":true}');
|
|
490
499
|
});
|
|
491
|
-
const result = await
|
|
500
|
+
const result = await harness.callTool('gog_sheets_number_format', { spreadsheetId: 'sid', range: 'A1:A2', type: 'DATE' });
|
|
492
501
|
expect(result.content[0].text).not.toMatch(/warning/i);
|
|
493
502
|
});
|
|
494
503
|
|
|
495
504
|
it('does not warn when range has no values (missing values key)', async () => {
|
|
496
|
-
const
|
|
505
|
+
const harness = await setupHandlers();
|
|
497
506
|
vi.mocked(lib.runOrDiagnose).mockImplementation(async (args: string[]) => {
|
|
498
507
|
if (args[1] === 'get') {
|
|
499
|
-
return
|
|
508
|
+
return rawTextResult(JSON.stringify({ range: 'A1:A3' }));
|
|
500
509
|
}
|
|
501
|
-
return
|
|
510
|
+
return rawTextResult('{"ok":true}');
|
|
502
511
|
});
|
|
503
|
-
const result = await
|
|
512
|
+
const result = await harness.callTool('gog_sheets_number_format', { spreadsheetId: 'sid', range: 'A1:A3', type: 'DATE' });
|
|
504
513
|
expect(result.content[0].text).not.toMatch(/warning/i);
|
|
505
514
|
});
|
|
506
515
|
|
|
507
516
|
it('does not warn when range has empty values array', async () => {
|
|
508
|
-
const
|
|
517
|
+
const harness = await setupHandlers();
|
|
509
518
|
vi.mocked(lib.runOrDiagnose).mockImplementation(async (args: string[]) => {
|
|
510
519
|
if (args[1] === 'get') {
|
|
511
|
-
return
|
|
520
|
+
return rawTextResult(JSON.stringify({ range: 'A1:A3', values: [] }));
|
|
512
521
|
}
|
|
513
|
-
return
|
|
522
|
+
return rawTextResult('{"ok":true}');
|
|
514
523
|
});
|
|
515
|
-
const result = await
|
|
524
|
+
const result = await harness.callTool('gog_sheets_number_format', { spreadsheetId: 'sid', range: 'A1:A3', type: 'DATE' });
|
|
516
525
|
expect(result.content[0].text).not.toMatch(/warning/i);
|
|
517
526
|
});
|
|
518
527
|
|
|
519
528
|
it('does not warn when values are strings', async () => {
|
|
520
|
-
const
|
|
529
|
+
const harness = await setupHandlers();
|
|
521
530
|
vi.mocked(lib.runOrDiagnose).mockImplementation(async (args: string[]) => {
|
|
522
531
|
if (args[1] === 'get') {
|
|
523
|
-
return
|
|
532
|
+
return rawTextResult(JSON.stringify({ range: 'A1:A2', values: [['hello'], ['world']] }));
|
|
524
533
|
}
|
|
525
|
-
return
|
|
534
|
+
return rawTextResult('{"ok":true}');
|
|
526
535
|
});
|
|
527
|
-
const result = await
|
|
536
|
+
const result = await harness.callTool('gog_sheets_number_format', { spreadsheetId: 'sid', range: 'A1:A2', type: 'DATE' });
|
|
528
537
|
expect(result.content[0].text).not.toMatch(/warning/i);
|
|
529
538
|
});
|
|
530
539
|
|
|
531
540
|
it('ignores null and empty cells when deciding to warn', async () => {
|
|
532
|
-
const
|
|
541
|
+
const harness = await setupHandlers();
|
|
533
542
|
vi.mocked(lib.runOrDiagnose).mockImplementation(async (args: string[]) => {
|
|
534
543
|
if (args[1] === 'get') {
|
|
535
544
|
// Real-world: nulls/empty strings interspersed with the ordinal ints.
|
|
536
|
-
return
|
|
545
|
+
return rawTextResult(JSON.stringify({ range: 'A1:A4', values: [[1], [null], [''], [3]] }));
|
|
537
546
|
}
|
|
538
|
-
return
|
|
547
|
+
return rawTextResult('{"ok":true}');
|
|
539
548
|
});
|
|
540
|
-
const result = await
|
|
549
|
+
const result = await harness.callTool('gog_sheets_number_format', { spreadsheetId: 'sid', range: 'A1:A4', type: 'DATE' });
|
|
541
550
|
expect(result.content[0].text).toMatch(/warning/i);
|
|
542
551
|
});
|
|
543
552
|
|
|
544
553
|
it('does not warn when every cell is null or empty (no small integers seen)', async () => {
|
|
545
|
-
const
|
|
554
|
+
const harness = await setupHandlers();
|
|
546
555
|
vi.mocked(lib.runOrDiagnose).mockImplementation(async (args: string[]) => {
|
|
547
556
|
if (args[1] === 'get') {
|
|
548
|
-
return
|
|
557
|
+
return rawTextResult(JSON.stringify({ range: 'A1:A3', values: [[null], [''], [null]] }));
|
|
549
558
|
}
|
|
550
|
-
return
|
|
559
|
+
return rawTextResult('{"ok":true}');
|
|
551
560
|
});
|
|
552
|
-
const result = await
|
|
561
|
+
const result = await harness.callTool('gog_sheets_number_format', { spreadsheetId: 'sid', range: 'A1:A3', type: 'DATE' });
|
|
553
562
|
expect(result.content[0].text).not.toMatch(/warning/i);
|
|
554
563
|
});
|
|
555
564
|
|
|
556
565
|
it('proceeds with format when peek fails (does not block)', async () => {
|
|
557
|
-
const
|
|
566
|
+
const harness = await setupHandlers();
|
|
558
567
|
vi.mocked(lib.runOrDiagnose).mockImplementation(async (args: string[]) => {
|
|
559
568
|
if (args[1] === 'get') {
|
|
560
|
-
return
|
|
569
|
+
return rawTextResult('Error: something went wrong');
|
|
561
570
|
}
|
|
562
|
-
return
|
|
571
|
+
return rawTextResult('{"ok":true}');
|
|
563
572
|
});
|
|
564
|
-
const result = await
|
|
573
|
+
const result = await harness.callTool('gog_sheets_number_format', { spreadsheetId: 'sid', range: 'A1:A3', type: 'DATE' });
|
|
565
574
|
// Format call should still happen.
|
|
566
575
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
567
576
|
['sheets', 'number-format', 'sid', 'A1:A3', '--type=DATE'],
|
|
@@ -572,14 +581,14 @@ describe('gog_sheets_number_format', () => {
|
|
|
572
581
|
});
|
|
573
582
|
|
|
574
583
|
it('forwards account on both peek and format calls', async () => {
|
|
575
|
-
const
|
|
584
|
+
const harness = await setupHandlers();
|
|
576
585
|
vi.mocked(lib.runOrDiagnose).mockImplementation(async (args: string[]) => {
|
|
577
586
|
if (args[1] === 'get') {
|
|
578
|
-
return
|
|
587
|
+
return rawTextResult(JSON.stringify({ range: 'A1', values: [[1]] }));
|
|
579
588
|
}
|
|
580
|
-
return
|
|
589
|
+
return rawTextResult('{}');
|
|
581
590
|
});
|
|
582
|
-
await
|
|
591
|
+
await harness.callTool('gog_sheets_number_format', { spreadsheetId: 'sid', range: 'A1', type: 'DATE', account: 'a@b.com' });
|
|
583
592
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
584
593
|
['sheets', 'get', 'sid', 'A1', '--render=UNFORMATTED_VALUE'],
|
|
585
594
|
{ account: 'a@b.com' },
|
|
@@ -595,23 +604,23 @@ describe('gog_sheets_number_format', () => {
|
|
|
595
604
|
// 12. read-format
|
|
596
605
|
describe('gog_sheets_read_format', () => {
|
|
597
606
|
it('calls runOrDiagnose with correct args', async () => {
|
|
598
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
599
|
-
const
|
|
600
|
-
await
|
|
607
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
608
|
+
const harness = await setupHandlers();
|
|
609
|
+
await harness.callTool('gog_sheets_read_format', { spreadsheetId: 'sid', range: 'A1:B2' });
|
|
601
610
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'read-format', 'sid', 'A1:B2'], { account: undefined });
|
|
602
611
|
});
|
|
603
612
|
|
|
604
613
|
it('includes --effective when true', async () => {
|
|
605
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
606
|
-
const
|
|
607
|
-
await
|
|
614
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
615
|
+
const harness = await setupHandlers();
|
|
616
|
+
await harness.callTool('gog_sheets_read_format', { spreadsheetId: 'sid', range: 'A1', effective: true });
|
|
608
617
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'read-format', 'sid', 'A1', '--effective'], { account: undefined });
|
|
609
618
|
});
|
|
610
619
|
|
|
611
620
|
it('omits --effective when false', async () => {
|
|
612
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
613
|
-
const
|
|
614
|
-
await
|
|
621
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
622
|
+
const harness = await setupHandlers();
|
|
623
|
+
await harness.callTool('gog_sheets_read_format', { spreadsheetId: 'sid', range: 'A1', effective: false });
|
|
615
624
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'read-format', 'sid', 'A1'], { account: undefined });
|
|
616
625
|
});
|
|
617
626
|
});
|
|
@@ -619,23 +628,23 @@ describe('gog_sheets_read_format', () => {
|
|
|
619
628
|
// 13. resize-columns
|
|
620
629
|
describe('gog_sheets_resize_columns', () => {
|
|
621
630
|
it('calls runOrDiagnose with correct args', async () => {
|
|
622
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
623
|
-
const
|
|
624
|
-
await
|
|
631
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
632
|
+
const harness = await setupHandlers();
|
|
633
|
+
await harness.callTool('gog_sheets_resize_columns', { spreadsheetId: 'sid', columns: 'A:C' });
|
|
625
634
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'resize-columns', 'sid', 'A:C'], { account: undefined });
|
|
626
635
|
});
|
|
627
636
|
|
|
628
637
|
it('includes --width and --auto when provided', async () => {
|
|
629
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
630
|
-
const
|
|
631
|
-
await
|
|
638
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
639
|
+
const harness = await setupHandlers();
|
|
640
|
+
await harness.callTool('gog_sheets_resize_columns', { spreadsheetId: 'sid', columns: 'A:C', width: 200, auto: true });
|
|
632
641
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'resize-columns', 'sid', 'A:C', '--width=200', '--auto'], { account: undefined });
|
|
633
642
|
});
|
|
634
643
|
|
|
635
644
|
it('includes --width=0 when width is 0', async () => {
|
|
636
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
637
|
-
const
|
|
638
|
-
await
|
|
645
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
646
|
+
const harness = await setupHandlers();
|
|
647
|
+
await harness.callTool('gog_sheets_resize_columns', { spreadsheetId: 'sid', columns: 'A:A', width: 0 });
|
|
639
648
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'resize-columns', 'sid', 'A:A', '--width=0'], { account: undefined });
|
|
640
649
|
});
|
|
641
650
|
});
|
|
@@ -643,23 +652,23 @@ describe('gog_sheets_resize_columns', () => {
|
|
|
643
652
|
// 14. resize-rows
|
|
644
653
|
describe('gog_sheets_resize_rows', () => {
|
|
645
654
|
it('calls runOrDiagnose with correct args', async () => {
|
|
646
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
647
|
-
const
|
|
648
|
-
await
|
|
655
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
656
|
+
const harness = await setupHandlers();
|
|
657
|
+
await harness.callTool('gog_sheets_resize_rows', { spreadsheetId: 'sid', rows: '1:10' });
|
|
649
658
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'resize-rows', 'sid', '1:10'], { account: undefined });
|
|
650
659
|
});
|
|
651
660
|
|
|
652
661
|
it('includes --height and --auto when provided', async () => {
|
|
653
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
654
|
-
const
|
|
655
|
-
await
|
|
662
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
663
|
+
const harness = await setupHandlers();
|
|
664
|
+
await harness.callTool('gog_sheets_resize_rows', { spreadsheetId: 'sid', rows: '1:5', height: 40, auto: true });
|
|
656
665
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'resize-rows', 'sid', '1:5', '--height=40', '--auto'], { account: undefined });
|
|
657
666
|
});
|
|
658
667
|
|
|
659
668
|
it('includes --height=0 when height is 0', async () => {
|
|
660
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
661
|
-
const
|
|
662
|
-
await
|
|
669
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
670
|
+
const harness = await setupHandlers();
|
|
671
|
+
await harness.callTool('gog_sheets_resize_rows', { spreadsheetId: 'sid', rows: '1:1', height: 0 });
|
|
663
672
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'resize-rows', 'sid', '1:1', '--height=0'], { account: undefined });
|
|
664
673
|
});
|
|
665
674
|
});
|
|
@@ -667,9 +676,9 @@ describe('gog_sheets_resize_rows', () => {
|
|
|
667
676
|
// 15. notes
|
|
668
677
|
describe('gog_sheets_notes', () => {
|
|
669
678
|
it('calls runOrDiagnose with correct args', async () => {
|
|
670
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
671
|
-
const
|
|
672
|
-
await
|
|
679
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
680
|
+
const harness = await setupHandlers();
|
|
681
|
+
await harness.callTool('gog_sheets_notes', { spreadsheetId: 'sid', range: 'A1:B5' });
|
|
673
682
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'notes', 'sid', 'A1:B5'], { account: undefined });
|
|
674
683
|
});
|
|
675
684
|
});
|
|
@@ -677,26 +686,38 @@ describe('gog_sheets_notes', () => {
|
|
|
677
686
|
// 16. update-note
|
|
678
687
|
describe('gog_sheets_update_note', () => {
|
|
679
688
|
it('calls runOrDiagnose with correct args', async () => {
|
|
680
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
681
|
-
const
|
|
682
|
-
await
|
|
689
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
690
|
+
const harness = await setupHandlers();
|
|
691
|
+
await harness.callTool('gog_sheets_update_note', { spreadsheetId: 'sid', range: 'A1', note: 'Hello' });
|
|
683
692
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'update-note', 'sid', 'A1', '--note=Hello'], { account: undefined });
|
|
684
693
|
});
|
|
685
694
|
|
|
686
695
|
it('passes empty string to clear note', async () => {
|
|
687
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
688
|
-
const
|
|
689
|
-
await
|
|
696
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
697
|
+
const harness = await setupHandlers();
|
|
698
|
+
await harness.callTool('gog_sheets_update_note', { spreadsheetId: 'sid', range: 'A1', note: '' });
|
|
690
699
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'update-note', 'sid', 'A1', '--note='], { account: undefined });
|
|
691
700
|
});
|
|
701
|
+
|
|
702
|
+
it('spills a large note to a --note-file payload arg', async () => {
|
|
703
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
704
|
+
const harness = await setupHandlers();
|
|
705
|
+
const big = 'n'.repeat(lib.PAYLOAD_INLINE_MAX + 1);
|
|
706
|
+
await harness.callTool('gog_sheets_update_note', { spreadsheetId: 'sid', range: 'A1', note: big });
|
|
707
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
708
|
+
['sheets', 'update-note', 'sid', 'A1',
|
|
709
|
+
{ kind: 'file', flag: 'note-file', contents: big, ext: undefined }],
|
|
710
|
+
{ account: undefined },
|
|
711
|
+
);
|
|
712
|
+
});
|
|
692
713
|
});
|
|
693
714
|
|
|
694
715
|
// 17. links
|
|
695
716
|
describe('gog_sheets_links', () => {
|
|
696
717
|
it('calls runOrDiagnose with correct args', async () => {
|
|
697
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
698
|
-
const
|
|
699
|
-
await
|
|
718
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
719
|
+
const harness = await setupHandlers();
|
|
720
|
+
await harness.callTool('gog_sheets_links', { spreadsheetId: 'sid', range: 'A1:Z100' });
|
|
700
721
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'links', 'sid', 'A1:Z100'], { account: undefined });
|
|
701
722
|
});
|
|
702
723
|
});
|
|
@@ -704,9 +725,9 @@ describe('gog_sheets_links', () => {
|
|
|
704
725
|
// gog 0.23.0
|
|
705
726
|
describe('gog_sheets_links_set', () => {
|
|
706
727
|
it('sets a single-cell hyperlink with cell/url/text positionals', async () => {
|
|
707
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
708
|
-
const
|
|
709
|
-
await
|
|
728
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
729
|
+
const harness = await setupHandlers();
|
|
730
|
+
await harness.callTool('gog_sheets_links_set', { spreadsheetId: 'sid', cell: 'A1', url: 'https://x.com', text: 'Link' });
|
|
710
731
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
711
732
|
['sheets', 'links', 'set', 'sid', 'A1', 'https://x.com', 'Link'],
|
|
712
733
|
{ account: undefined },
|
|
@@ -714,9 +735,9 @@ describe('gog_sheets_links_set', () => {
|
|
|
714
735
|
});
|
|
715
736
|
|
|
716
737
|
it('sets a multi-link cell with --runs-json', async () => {
|
|
717
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
718
|
-
const
|
|
719
|
-
await
|
|
738
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
739
|
+
const harness = await setupHandlers();
|
|
740
|
+
await harness.callTool('gog_sheets_links_set', { spreadsheetId: 'sid', cell: 'B2', runsJson: '[{"text":"A","uri":"https://a"}]' });
|
|
720
741
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
721
742
|
['sheets', 'links', 'set', 'sid', 'B2', '--runs-json=[{"text":"A","uri":"https://a"}]'],
|
|
722
743
|
{ account: undefined },
|
|
@@ -724,9 +745,9 @@ describe('gog_sheets_links_set', () => {
|
|
|
724
745
|
});
|
|
725
746
|
|
|
726
747
|
it('sets batch hyperlinks with --cells-json', async () => {
|
|
727
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
728
|
-
const
|
|
729
|
-
await
|
|
748
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
749
|
+
const harness = await setupHandlers();
|
|
750
|
+
await harness.callTool('gog_sheets_links_set', { spreadsheetId: 'sid', cellsJson: '[{"cell":"A1","url":"https://a"}]' });
|
|
730
751
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
731
752
|
['sheets', 'links', 'set', 'sid', '--cells-json=[{"cell":"A1","url":"https://a"}]'],
|
|
732
753
|
{ account: undefined },
|
|
@@ -737,9 +758,9 @@ describe('gog_sheets_links_set', () => {
|
|
|
737
758
|
// 18. named-ranges list
|
|
738
759
|
describe('gog_sheets_named_ranges_list', () => {
|
|
739
760
|
it('calls runOrDiagnose with correct args', async () => {
|
|
740
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
741
|
-
const
|
|
742
|
-
await
|
|
761
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
762
|
+
const harness = await setupHandlers();
|
|
763
|
+
await harness.callTool('gog_sheets_named_ranges_list', { spreadsheetId: 'sid' });
|
|
743
764
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'named-ranges', 'list', 'sid'], { account: undefined });
|
|
744
765
|
});
|
|
745
766
|
});
|
|
@@ -747,9 +768,9 @@ describe('gog_sheets_named_ranges_list', () => {
|
|
|
747
768
|
// 19. named-ranges get
|
|
748
769
|
describe('gog_sheets_named_ranges_get', () => {
|
|
749
770
|
it('calls runOrDiagnose with correct args', async () => {
|
|
750
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
751
|
-
const
|
|
752
|
-
await
|
|
771
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
772
|
+
const harness = await setupHandlers();
|
|
773
|
+
await harness.callTool('gog_sheets_named_ranges_get', { spreadsheetId: 'sid', nameOrId: 'MyRange' });
|
|
753
774
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'named-ranges', 'get', 'sid', 'MyRange'], { account: undefined });
|
|
754
775
|
});
|
|
755
776
|
});
|
|
@@ -757,9 +778,9 @@ describe('gog_sheets_named_ranges_get', () => {
|
|
|
757
778
|
// 20. named-ranges add
|
|
758
779
|
describe('gog_sheets_named_ranges_add', () => {
|
|
759
780
|
it('calls runOrDiagnose with correct args', async () => {
|
|
760
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
761
|
-
const
|
|
762
|
-
await
|
|
781
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
782
|
+
const harness = await setupHandlers();
|
|
783
|
+
await harness.callTool('gog_sheets_named_ranges_add', { spreadsheetId: 'sid', name: 'Totals', range: 'Sheet1!A1:B10' });
|
|
763
784
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'named-ranges', 'add', 'sid', 'Totals', 'Sheet1!A1:B10'], { account: undefined });
|
|
764
785
|
});
|
|
765
786
|
});
|
|
@@ -767,16 +788,16 @@ describe('gog_sheets_named_ranges_add', () => {
|
|
|
767
788
|
// 21. named-ranges update
|
|
768
789
|
describe('gog_sheets_named_ranges_update', () => {
|
|
769
790
|
it('calls runOrDiagnose with no optional flags', async () => {
|
|
770
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
771
|
-
const
|
|
772
|
-
await
|
|
791
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
792
|
+
const harness = await setupHandlers();
|
|
793
|
+
await harness.callTool('gog_sheets_named_ranges_update', { spreadsheetId: 'sid', nameOrId: 'MyRange' });
|
|
773
794
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'named-ranges', 'update', 'sid', 'MyRange'], { account: undefined });
|
|
774
795
|
});
|
|
775
796
|
|
|
776
797
|
it('includes --name and --range when provided', async () => {
|
|
777
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
778
|
-
const
|
|
779
|
-
await
|
|
798
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
799
|
+
const harness = await setupHandlers();
|
|
800
|
+
await harness.callTool('gog_sheets_named_ranges_update', { spreadsheetId: 'sid', nameOrId: 'MyRange', name: 'NewName', range: 'Sheet1!C1:D5' });
|
|
780
801
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'named-ranges', 'update', 'sid', 'MyRange', '--name=NewName', '--range=Sheet1!C1:D5'], { account: undefined });
|
|
781
802
|
});
|
|
782
803
|
});
|
|
@@ -784,9 +805,9 @@ describe('gog_sheets_named_ranges_update', () => {
|
|
|
784
805
|
// 22. named-ranges delete
|
|
785
806
|
describe('gog_sheets_named_ranges_delete', () => {
|
|
786
807
|
it('calls runOrDiagnose with correct args', async () => {
|
|
787
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
788
|
-
const
|
|
789
|
-
await
|
|
808
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
809
|
+
const harness = await setupHandlers();
|
|
810
|
+
await harness.callTool('gog_sheets_named_ranges_delete', { spreadsheetId: 'sid', nameOrId: 'OldRange' });
|
|
790
811
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'named-ranges', 'delete', 'sid', 'OldRange'], { account: undefined });
|
|
791
812
|
});
|
|
792
813
|
});
|
|
@@ -794,10 +815,10 @@ describe('gog_sheets_named_ranges_delete', () => {
|
|
|
794
815
|
// 23. batch-update (gog 0.18.0)
|
|
795
816
|
describe('gog_sheets_batch_update', () => {
|
|
796
817
|
it('passes --data-json and required spreadsheetId', async () => {
|
|
797
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
798
|
-
const
|
|
818
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
819
|
+
const harness = await setupHandlers();
|
|
799
820
|
const data = '[{"range":"Sheet1!A1:B1","values":[["a","b"]]}]';
|
|
800
|
-
await
|
|
821
|
+
await harness.callTool('gog_sheets_batch_update', { spreadsheetId: 'sid', dataJson: data });
|
|
801
822
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
802
823
|
['sheets', 'batch-update', 'sid', `--data-json=${data}`],
|
|
803
824
|
{ account: undefined },
|
|
@@ -805,9 +826,9 @@ describe('gog_sheets_batch_update', () => {
|
|
|
805
826
|
});
|
|
806
827
|
|
|
807
828
|
it('passes optional --input, --include-values-in-response, --response-render, --response-date-time-render', async () => {
|
|
808
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
809
|
-
const
|
|
810
|
-
await
|
|
829
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
830
|
+
const harness = await setupHandlers();
|
|
831
|
+
await harness.callTool('gog_sheets_batch_update', {
|
|
811
832
|
spreadsheetId: 'sid',
|
|
812
833
|
dataJson: '[]',
|
|
813
834
|
input: 'RAW',
|
|
@@ -830,9 +851,9 @@ describe('gog_sheets_batch_update', () => {
|
|
|
830
851
|
});
|
|
831
852
|
|
|
832
853
|
it('omits --include-values-in-response when false', async () => {
|
|
833
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
834
|
-
const
|
|
835
|
-
await
|
|
854
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
855
|
+
const harness = await setupHandlers();
|
|
856
|
+
await harness.callTool('gog_sheets_batch_update', {
|
|
836
857
|
spreadsheetId: 'sid', dataJson: '[]', includeValuesInResponse: false,
|
|
837
858
|
});
|
|
838
859
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
@@ -845,9 +866,9 @@ describe('gog_sheets_batch_update', () => {
|
|
|
845
866
|
// 24. reorder-tab (gog 0.18.0)
|
|
846
867
|
describe('gog_sheets_reorder_tab', () => {
|
|
847
868
|
it('passes --tab and --to', async () => {
|
|
848
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
849
|
-
const
|
|
850
|
-
await
|
|
869
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
870
|
+
const harness = await setupHandlers();
|
|
871
|
+
await harness.callTool('gog_sheets_reorder_tab', { spreadsheetId: 'sid', tab: 'Data', to: 2 });
|
|
851
872
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
852
873
|
['sheets', 'reorder-tab', 'sid', '--tab=Data', '--to=2'],
|
|
853
874
|
{ account: undefined },
|
|
@@ -855,9 +876,9 @@ describe('gog_sheets_reorder_tab', () => {
|
|
|
855
876
|
});
|
|
856
877
|
|
|
857
878
|
it('sends --to=0 explicitly to reach the leftmost position', async () => {
|
|
858
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
859
|
-
const
|
|
860
|
-
await
|
|
879
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
880
|
+
const harness = await setupHandlers();
|
|
881
|
+
await harness.callTool('gog_sheets_reorder_tab', { spreadsheetId: 'sid', tab: '123', to: 0 });
|
|
861
882
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
862
883
|
['sheets', 'reorder-tab', 'sid', '--tab=123', '--to=0'],
|
|
863
884
|
{ account: undefined },
|
|
@@ -865,9 +886,9 @@ describe('gog_sheets_reorder_tab', () => {
|
|
|
865
886
|
});
|
|
866
887
|
|
|
867
888
|
it('forwards account', async () => {
|
|
868
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
869
|
-
const
|
|
870
|
-
await
|
|
889
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
890
|
+
const harness = await setupHandlers();
|
|
891
|
+
await harness.callTool('gog_sheets_reorder_tab', { spreadsheetId: 'sid', tab: 'Data', to: 1, account: 'a@b.com' });
|
|
871
892
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
872
893
|
['sheets', 'reorder-tab', 'sid', '--tab=Data', '--to=1'],
|
|
873
894
|
{ account: 'a@b.com' },
|
|
@@ -878,16 +899,16 @@ describe('gog_sheets_reorder_tab', () => {
|
|
|
878
899
|
// 25. chart list (gog 0.19.0)
|
|
879
900
|
describe('gog_sheets_chart_list', () => {
|
|
880
901
|
it('calls runOrDiagnose with correct args', async () => {
|
|
881
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
882
|
-
const
|
|
883
|
-
await
|
|
902
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
903
|
+
const harness = await setupHandlers();
|
|
904
|
+
await harness.callTool('gog_sheets_chart_list', { spreadsheetId: 'sid' });
|
|
884
905
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'chart', 'list', 'sid'], { account: undefined });
|
|
885
906
|
});
|
|
886
907
|
|
|
887
908
|
it('forwards account', async () => {
|
|
888
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
889
|
-
const
|
|
890
|
-
await
|
|
909
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
910
|
+
const harness = await setupHandlers();
|
|
911
|
+
await harness.callTool('gog_sheets_chart_list', { spreadsheetId: 'sid', account: 'a@b.com' });
|
|
891
912
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'chart', 'list', 'sid'], { account: 'a@b.com' });
|
|
892
913
|
});
|
|
893
914
|
});
|
|
@@ -895,9 +916,9 @@ describe('gog_sheets_chart_list', () => {
|
|
|
895
916
|
// 26. chart get
|
|
896
917
|
describe('gog_sheets_chart_get', () => {
|
|
897
918
|
it('calls runOrDiagnose with correct args', async () => {
|
|
898
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
899
|
-
const
|
|
900
|
-
await
|
|
919
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
920
|
+
const harness = await setupHandlers();
|
|
921
|
+
await harness.callTool('gog_sheets_chart_get', { spreadsheetId: 'sid', chartId: '12345' });
|
|
901
922
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'chart', 'get', 'sid', '12345'], { account: undefined });
|
|
902
923
|
});
|
|
903
924
|
});
|
|
@@ -905,10 +926,10 @@ describe('gog_sheets_chart_get', () => {
|
|
|
905
926
|
// 27. chart create
|
|
906
927
|
describe('gog_sheets_chart_create', () => {
|
|
907
928
|
it('calls runOrDiagnose with only --spec-json', async () => {
|
|
908
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
909
|
-
const
|
|
929
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
930
|
+
const harness = await setupHandlers();
|
|
910
931
|
const spec = '{"basicChart":{"chartType":"COLUMN"}}';
|
|
911
|
-
await
|
|
932
|
+
await harness.callTool('gog_sheets_chart_create', { spreadsheetId: 'sid', specJson: spec });
|
|
912
933
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
913
934
|
['sheets', 'chart', 'create', 'sid', `--spec-json=${spec}`],
|
|
914
935
|
{ account: undefined },
|
|
@@ -916,9 +937,9 @@ describe('gog_sheets_chart_create', () => {
|
|
|
916
937
|
});
|
|
917
938
|
|
|
918
939
|
it('includes --sheet, --anchor, --width, --height when provided', async () => {
|
|
919
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
920
|
-
const
|
|
921
|
-
await
|
|
940
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
941
|
+
const harness = await setupHandlers();
|
|
942
|
+
await harness.callTool('gog_sheets_chart_create', {
|
|
922
943
|
spreadsheetId: 'sid', specJson: '{}', sheet: 'Data', anchor: 'E10', width: 800, height: 400,
|
|
923
944
|
});
|
|
924
945
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
@@ -928,9 +949,9 @@ describe('gog_sheets_chart_create', () => {
|
|
|
928
949
|
});
|
|
929
950
|
|
|
930
951
|
it('includes --width=0 and --height=0 when zero', async () => {
|
|
931
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
932
|
-
const
|
|
933
|
-
await
|
|
952
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
953
|
+
const harness = await setupHandlers();
|
|
954
|
+
await harness.callTool('gog_sheets_chart_create', { spreadsheetId: 'sid', specJson: '{}', width: 0, height: 0 });
|
|
934
955
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
935
956
|
['sheets', 'chart', 'create', 'sid', '--spec-json={}', '--width=0', '--height=0'],
|
|
936
957
|
{ account: undefined },
|
|
@@ -941,9 +962,9 @@ describe('gog_sheets_chart_create', () => {
|
|
|
941
962
|
// 28. chart update
|
|
942
963
|
describe('gog_sheets_chart_update', () => {
|
|
943
964
|
it('calls runOrDiagnose with chartId and --spec-json', async () => {
|
|
944
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
945
|
-
const
|
|
946
|
-
await
|
|
965
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
966
|
+
const harness = await setupHandlers();
|
|
967
|
+
await harness.callTool('gog_sheets_chart_update', { spreadsheetId: 'sid', chartId: '99', specJson: '{}' });
|
|
947
968
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
948
969
|
['sheets', 'chart', 'update', 'sid', '99', '--spec-json={}'],
|
|
949
970
|
{ account: undefined },
|
|
@@ -954,19 +975,19 @@ describe('gog_sheets_chart_update', () => {
|
|
|
954
975
|
// 29. chart delete
|
|
955
976
|
describe('gog_sheets_chart_delete', () => {
|
|
956
977
|
it('calls runOrDiagnose with correct args', async () => {
|
|
957
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
958
|
-
const
|
|
959
|
-
await
|
|
960
|
-
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'chart', 'delete', 'sid', '7'], { account: undefined });
|
|
978
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
979
|
+
const harness = await setupHandlers();
|
|
980
|
+
await harness.callTool('gog_sheets_chart_delete', { spreadsheetId: 'sid', chartId: '7' });
|
|
981
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'chart', 'delete', 'sid', '7', '--force'], { account: undefined });
|
|
961
982
|
});
|
|
962
983
|
});
|
|
963
984
|
|
|
964
985
|
// 30. table list
|
|
965
986
|
describe('gog_sheets_table_list', () => {
|
|
966
987
|
it('calls runOrDiagnose with correct args', async () => {
|
|
967
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
968
|
-
const
|
|
969
|
-
await
|
|
988
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
989
|
+
const harness = await setupHandlers();
|
|
990
|
+
await harness.callTool('gog_sheets_table_list', { spreadsheetId: 'sid' });
|
|
970
991
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'table', 'list', 'sid'], { account: undefined });
|
|
971
992
|
});
|
|
972
993
|
});
|
|
@@ -974,9 +995,9 @@ describe('gog_sheets_table_list', () => {
|
|
|
974
995
|
// 31. table get
|
|
975
996
|
describe('gog_sheets_table_get', () => {
|
|
976
997
|
it('calls runOrDiagnose with correct args', async () => {
|
|
977
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
978
|
-
const
|
|
979
|
-
await
|
|
998
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
999
|
+
const harness = await setupHandlers();
|
|
1000
|
+
await harness.callTool('gog_sheets_table_get', { spreadsheetId: 'sid', tableId: 't1' });
|
|
980
1001
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'table', 'get', 'sid', 't1'], { account: undefined });
|
|
981
1002
|
});
|
|
982
1003
|
});
|
|
@@ -984,10 +1005,10 @@ describe('gog_sheets_table_get', () => {
|
|
|
984
1005
|
// 32. table create
|
|
985
1006
|
describe('gog_sheets_table_create', () => {
|
|
986
1007
|
it('calls runOrDiagnose with range, --name, --columns-json', async () => {
|
|
987
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
988
|
-
const
|
|
1008
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
1009
|
+
const harness = await setupHandlers();
|
|
989
1010
|
const cols = '[{"columnName":"Name","columnType":"TEXT"}]';
|
|
990
|
-
await
|
|
1011
|
+
await harness.callTool('gog_sheets_table_create', { spreadsheetId: 'sid', range: 'Sheet1!A1:B10', name: 'People', columnsJson: cols });
|
|
991
1012
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
992
1013
|
['sheets', 'table', 'create', 'sid', 'Sheet1!A1:B10', '--name=People', `--columns-json=${cols}`],
|
|
993
1014
|
{ account: undefined },
|
|
@@ -998,9 +1019,9 @@ describe('gog_sheets_table_create', () => {
|
|
|
998
1019
|
// 33. table append
|
|
999
1020
|
describe('gog_sheets_table_append', () => {
|
|
1000
1021
|
it('calls runOrDiagnose with --values-json (no --input)', async () => {
|
|
1001
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
1002
|
-
const
|
|
1003
|
-
await
|
|
1022
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
1023
|
+
const harness = await setupHandlers();
|
|
1024
|
+
await harness.callTool('gog_sheets_table_append', { spreadsheetId: 'sid', tableId: 't1', valuesJson: '[["a",1]]' });
|
|
1004
1025
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
1005
1026
|
['sheets', 'table', 'append', 'sid', 't1', '--values-json=[["a",1]]'],
|
|
1006
1027
|
{ account: undefined },
|
|
@@ -1008,9 +1029,9 @@ describe('gog_sheets_table_append', () => {
|
|
|
1008
1029
|
});
|
|
1009
1030
|
|
|
1010
1031
|
it('includes --input when provided', async () => {
|
|
1011
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
1012
|
-
const
|
|
1013
|
-
await
|
|
1032
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
1033
|
+
const harness = await setupHandlers();
|
|
1034
|
+
await harness.callTool('gog_sheets_table_append', { spreadsheetId: 'sid', tableId: 't1', valuesJson: '[]', input: 'RAW' });
|
|
1014
1035
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
1015
1036
|
['sheets', 'table', 'append', 'sid', 't1', '--values-json=[]', '--input=RAW'],
|
|
1016
1037
|
{ account: undefined },
|
|
@@ -1021,19 +1042,19 @@ describe('gog_sheets_table_append', () => {
|
|
|
1021
1042
|
// 34. table clear
|
|
1022
1043
|
describe('gog_sheets_table_clear', () => {
|
|
1023
1044
|
it('calls runOrDiagnose with correct args', async () => {
|
|
1024
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
1025
|
-
const
|
|
1026
|
-
await
|
|
1027
|
-
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'table', 'clear', 'sid', 't1'], { account: undefined });
|
|
1045
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
1046
|
+
const harness = await setupHandlers();
|
|
1047
|
+
await harness.callTool('gog_sheets_table_clear', { spreadsheetId: 'sid', tableId: 't1' });
|
|
1048
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'table', 'clear', 'sid', 't1', '--force'], { account: undefined });
|
|
1028
1049
|
});
|
|
1029
1050
|
});
|
|
1030
1051
|
|
|
1031
1052
|
// 35. table delete
|
|
1032
1053
|
describe('gog_sheets_table_delete', () => {
|
|
1033
1054
|
it('keep_data=false deletes table (and data) directly with --discard-data --force', async () => {
|
|
1034
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
1035
|
-
const
|
|
1036
|
-
await
|
|
1055
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
1056
|
+
const harness = await setupHandlers();
|
|
1057
|
+
await harness.callTool('gog_sheets_table_delete', { spreadsheetId: 'sid', tableId: 't1', keep_data: false });
|
|
1037
1058
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'table', 'delete', 'sid', 't1', '--discard-data', '--force'], { account: undefined });
|
|
1038
1059
|
expect(lib.run).not.toHaveBeenCalled();
|
|
1039
1060
|
});
|
|
@@ -1044,8 +1065,8 @@ describe('gog_sheets_table_delete', () => {
|
|
|
1044
1065
|
.mockResolvedValueOnce('{"values":[["Name","City","N"],["A","NYC","=1+1"]]}') // get --render=FORMULA
|
|
1045
1066
|
.mockResolvedValueOnce('{"deleted":{"tableId":"t1"}}') // table delete
|
|
1046
1067
|
.mockResolvedValueOnce('{"updatedRows":2}'); // update restore
|
|
1047
|
-
const
|
|
1048
|
-
const res = await
|
|
1068
|
+
const harness = await setupHandlers();
|
|
1069
|
+
const res = await harness.callTool('gog_sheets_table_delete', { spreadsheetId: 'sid', tableId: 't1', account: 'a@b.com' });
|
|
1049
1070
|
expect(lib.run).toHaveBeenNthCalledWith(1, ['sheets', 'table', 'get', 'sid', 't1'], { account: 'a@b.com' });
|
|
1050
1071
|
expect(lib.run).toHaveBeenNthCalledWith(2, ['sheets', 'get', 'sid', 'Sheet1!A1:C2', '--render=FORMULA'], { account: 'a@b.com' });
|
|
1051
1072
|
expect(lib.run).toHaveBeenNthCalledWith(3, ['sheets', 'table', 'delete', 'sid', 't1', '--discard-data', '--force'], { account: 'a@b.com' });
|
|
@@ -1059,26 +1080,26 @@ describe('gog_sheets_table_delete', () => {
|
|
|
1059
1080
|
.mockResolvedValueOnce('{"table":{"a1":"Sheet1!A1:C1"}}') // table get
|
|
1060
1081
|
.mockResolvedValueOnce('{}') // get --render=FORMULA, no values key
|
|
1061
1082
|
.mockResolvedValueOnce('{"deleted":{}}'); // table delete
|
|
1062
|
-
const
|
|
1063
|
-
const res = await
|
|
1083
|
+
const harness = await setupHandlers();
|
|
1084
|
+
const res = await harness.callTool('gog_sheets_table_delete', { spreadsheetId: 'sid', tableId: 't1' });
|
|
1064
1085
|
expect(lib.run).toHaveBeenCalledTimes(3); // no update call
|
|
1065
1086
|
expect(res.content[0].text).toContain('0 row');
|
|
1066
1087
|
});
|
|
1067
1088
|
|
|
1068
1089
|
it('aborts without deleting when the table has no a1 range', async () => {
|
|
1069
1090
|
vi.mocked(lib.run).mockResolvedValueOnce('{"name":"Repro"}'); // table get, no a1
|
|
1070
|
-
vi.mocked(lib.diagnose).mockResolvedValue(
|
|
1071
|
-
const
|
|
1072
|
-
const res = await
|
|
1091
|
+
vi.mocked(lib.diagnose).mockResolvedValue(rawTextResult('diagnosed'));
|
|
1092
|
+
const harness = await setupHandlers();
|
|
1093
|
+
const res = await harness.callTool('gog_sheets_table_delete', { spreadsheetId: 'sid', tableId: 't1' });
|
|
1073
1094
|
expect(lib.run).toHaveBeenCalledTimes(1); // only the read, no delete
|
|
1074
1095
|
expect(res.content[0].text).toBe('diagnosed');
|
|
1075
1096
|
});
|
|
1076
1097
|
|
|
1077
1098
|
it('aborts without deleting when the pre-delete read fails', async () => {
|
|
1078
1099
|
vi.mocked(lib.run).mockRejectedValueOnce(new Error('read boom')); // table get throws
|
|
1079
|
-
vi.mocked(lib.diagnose).mockResolvedValue(
|
|
1080
|
-
const
|
|
1081
|
-
const res = await
|
|
1100
|
+
vi.mocked(lib.diagnose).mockResolvedValue(rawTextResult('diagnosed read'));
|
|
1101
|
+
const harness = await setupHandlers();
|
|
1102
|
+
const res = await harness.callTool('gog_sheets_table_delete', { spreadsheetId: 'sid', tableId: 't1' });
|
|
1082
1103
|
expect(lib.run).toHaveBeenCalledTimes(1);
|
|
1083
1104
|
expect(res.content[0].text).toBe('diagnosed read');
|
|
1084
1105
|
});
|
|
@@ -1088,9 +1109,9 @@ describe('gog_sheets_table_delete', () => {
|
|
|
1088
1109
|
.mockResolvedValueOnce('{"table":{"a1":"Sheet1!A1:C2"}}')
|
|
1089
1110
|
.mockResolvedValueOnce('{"values":[["A"]]}')
|
|
1090
1111
|
.mockRejectedValueOnce(new Error('delete boom')); // table delete throws
|
|
1091
|
-
vi.mocked(lib.diagnose).mockResolvedValue(
|
|
1092
|
-
const
|
|
1093
|
-
const res = await
|
|
1112
|
+
vi.mocked(lib.diagnose).mockResolvedValue(rawTextResult('diagnosed delete'));
|
|
1113
|
+
const harness = await setupHandlers();
|
|
1114
|
+
const res = await harness.callTool('gog_sheets_table_delete', { spreadsheetId: 'sid', tableId: 't1' });
|
|
1094
1115
|
expect(res.content[0].text).toBe('diagnosed delete');
|
|
1095
1116
|
});
|
|
1096
1117
|
|
|
@@ -1100,8 +1121,8 @@ describe('gog_sheets_table_delete', () => {
|
|
|
1100
1121
|
.mockResolvedValueOnce('{"values":[["keepme"]]}')
|
|
1101
1122
|
.mockResolvedValueOnce('{"deleted":{}}')
|
|
1102
1123
|
.mockRejectedValueOnce(new Error('restore boom')); // update throws
|
|
1103
|
-
const
|
|
1104
|
-
const res = await
|
|
1124
|
+
const harness = await setupHandlers();
|
|
1125
|
+
const res = await harness.callTool('gog_sheets_table_delete', { spreadsheetId: 'sid', tableId: 't1' });
|
|
1105
1126
|
const text = res.content[0].text;
|
|
1106
1127
|
expect(text).toContain('restoring its data FAILED');
|
|
1107
1128
|
expect(text).toContain('keepme'); // the data is handed back for manual recovery
|
|
@@ -1112,16 +1133,16 @@ describe('gog_sheets_table_delete', () => {
|
|
|
1112
1133
|
// 36. banding list
|
|
1113
1134
|
describe('gog_sheets_banding_list', () => {
|
|
1114
1135
|
it('calls runOrDiagnose with no --sheet', async () => {
|
|
1115
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
1116
|
-
const
|
|
1117
|
-
await
|
|
1136
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
1137
|
+
const harness = await setupHandlers();
|
|
1138
|
+
await harness.callTool('gog_sheets_banding_list', { spreadsheetId: 'sid' });
|
|
1118
1139
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'banding', 'list', 'sid'], { account: undefined });
|
|
1119
1140
|
});
|
|
1120
1141
|
|
|
1121
1142
|
it('includes --sheet when provided', async () => {
|
|
1122
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
1123
|
-
const
|
|
1124
|
-
await
|
|
1143
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
1144
|
+
const harness = await setupHandlers();
|
|
1145
|
+
await harness.callTool('gog_sheets_banding_list', { spreadsheetId: 'sid', sheet: 'Data' });
|
|
1125
1146
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'banding', 'list', 'sid', '--sheet=Data'], { account: undefined });
|
|
1126
1147
|
});
|
|
1127
1148
|
});
|
|
@@ -1129,16 +1150,16 @@ describe('gog_sheets_banding_list', () => {
|
|
|
1129
1150
|
// 37. banding set
|
|
1130
1151
|
describe('gog_sheets_banding_set', () => {
|
|
1131
1152
|
it('calls runOrDiagnose with range only', async () => {
|
|
1132
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
1133
|
-
const
|
|
1134
|
-
await
|
|
1153
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
1154
|
+
const harness = await setupHandlers();
|
|
1155
|
+
await harness.callTool('gog_sheets_banding_set', { spreadsheetId: 'sid', range: 'Sheet1!A1:D20' });
|
|
1135
1156
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'banding', 'set', 'sid', 'Sheet1!A1:D20'], { account: undefined });
|
|
1136
1157
|
});
|
|
1137
1158
|
|
|
1138
1159
|
it('includes --row-properties-json and --column-properties-json when provided', async () => {
|
|
1139
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
1140
|
-
const
|
|
1141
|
-
await
|
|
1160
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
1161
|
+
const harness = await setupHandlers();
|
|
1162
|
+
await harness.callTool('gog_sheets_banding_set', {
|
|
1142
1163
|
spreadsheetId: 'sid', range: 'A1:D20',
|
|
1143
1164
|
rowPropertiesJson: '{"firstBandColor":{}}',
|
|
1144
1165
|
columnPropertiesJson: '{"secondBandColor":{}}',
|
|
@@ -1153,47 +1174,47 @@ describe('gog_sheets_banding_set', () => {
|
|
|
1153
1174
|
// 38. banding clear
|
|
1154
1175
|
describe('gog_sheets_banding_clear', () => {
|
|
1155
1176
|
it('calls runOrDiagnose with no optional flags', async () => {
|
|
1156
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
1157
|
-
const
|
|
1158
|
-
await
|
|
1159
|
-
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'banding', 'clear', 'sid'], { account: undefined });
|
|
1177
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
1178
|
+
const harness = await setupHandlers();
|
|
1179
|
+
await harness.callTool('gog_sheets_banding_clear', { spreadsheetId: 'sid' });
|
|
1180
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'banding', 'clear', 'sid', '--force'], { account: undefined });
|
|
1160
1181
|
});
|
|
1161
1182
|
|
|
1162
1183
|
it('includes --id when provided (including 0)', async () => {
|
|
1163
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
1164
|
-
const
|
|
1165
|
-
await
|
|
1166
|
-
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'banding', 'clear', 'sid', '--id=0'], { account: undefined });
|
|
1184
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
1185
|
+
const harness = await setupHandlers();
|
|
1186
|
+
await harness.callTool('gog_sheets_banding_clear', { spreadsheetId: 'sid', id: 0 });
|
|
1187
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'banding', 'clear', 'sid', '--id=0', '--force'], { account: undefined });
|
|
1167
1188
|
});
|
|
1168
1189
|
|
|
1169
1190
|
it('includes --all and --sheet when clearing a whole sheet', async () => {
|
|
1170
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
1171
|
-
const
|
|
1172
|
-
await
|
|
1173
|
-
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'banding', 'clear', 'sid', '--all', '--sheet=Data'], { account: undefined });
|
|
1191
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
1192
|
+
const harness = await setupHandlers();
|
|
1193
|
+
await harness.callTool('gog_sheets_banding_clear', { spreadsheetId: 'sid', all: true, sheet: 'Data' });
|
|
1194
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'banding', 'clear', 'sid', '--all', '--sheet=Data', '--force'], { account: undefined });
|
|
1174
1195
|
});
|
|
1175
1196
|
|
|
1176
1197
|
it('omits --all when false', async () => {
|
|
1177
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
1178
|
-
const
|
|
1179
|
-
await
|
|
1180
|
-
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'banding', 'clear', 'sid', '--id=5'], { account: undefined });
|
|
1198
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
1199
|
+
const harness = await setupHandlers();
|
|
1200
|
+
await harness.callTool('gog_sheets_banding_clear', { spreadsheetId: 'sid', all: false, id: 5 });
|
|
1201
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'banding', 'clear', 'sid', '--id=5', '--force'], { account: undefined });
|
|
1181
1202
|
});
|
|
1182
1203
|
});
|
|
1183
1204
|
|
|
1184
1205
|
// 39. conditional-format list
|
|
1185
1206
|
describe('gog_sheets_conditional_format_list', () => {
|
|
1186
1207
|
it('calls runOrDiagnose with no --sheet', async () => {
|
|
1187
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
1188
|
-
const
|
|
1189
|
-
await
|
|
1208
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
1209
|
+
const harness = await setupHandlers();
|
|
1210
|
+
await harness.callTool('gog_sheets_conditional_format_list', { spreadsheetId: 'sid' });
|
|
1190
1211
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'conditional-format', 'list', 'sid'], { account: undefined });
|
|
1191
1212
|
});
|
|
1192
1213
|
|
|
1193
1214
|
it('includes --sheet when provided', async () => {
|
|
1194
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
1195
|
-
const
|
|
1196
|
-
await
|
|
1215
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
1216
|
+
const harness = await setupHandlers();
|
|
1217
|
+
await harness.callTool('gog_sheets_conditional_format_list', { spreadsheetId: 'sid', sheet: 'Data' });
|
|
1197
1218
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'conditional-format', 'list', 'sid', '--sheet=Data'], { account: undefined });
|
|
1198
1219
|
});
|
|
1199
1220
|
});
|
|
@@ -1201,9 +1222,9 @@ describe('gog_sheets_conditional_format_list', () => {
|
|
|
1201
1222
|
// 40. conditional-format add
|
|
1202
1223
|
describe('gog_sheets_conditional_format_add', () => {
|
|
1203
1224
|
it('calls runOrDiagnose with type and --format-json (no expr/fields)', async () => {
|
|
1204
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
1205
|
-
const
|
|
1206
|
-
await
|
|
1225
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
1226
|
+
const harness = await setupHandlers();
|
|
1227
|
+
await harness.callTool('gog_sheets_conditional_format_add', {
|
|
1207
1228
|
spreadsheetId: 'sid', range: 'A1:A100', type: 'not-blank', formatJson: '{"backgroundColor":{}}',
|
|
1208
1229
|
});
|
|
1209
1230
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
@@ -1213,9 +1234,9 @@ describe('gog_sheets_conditional_format_add', () => {
|
|
|
1213
1234
|
});
|
|
1214
1235
|
|
|
1215
1236
|
it('includes --expr and --format-fields when provided', async () => {
|
|
1216
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
1217
|
-
const
|
|
1218
|
-
await
|
|
1237
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
1238
|
+
const harness = await setupHandlers();
|
|
1239
|
+
await harness.callTool('gog_sheets_conditional_format_add', {
|
|
1219
1240
|
spreadsheetId: 'sid', range: 'B1:B50', type: 'number-gt', expr: '100', formatJson: '{}', formatFields: 'backgroundColor,textFormat.bold',
|
|
1220
1241
|
});
|
|
1221
1242
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
@@ -1225,9 +1246,9 @@ describe('gog_sheets_conditional_format_add', () => {
|
|
|
1225
1246
|
});
|
|
1226
1247
|
|
|
1227
1248
|
it('includes --expr when it is an empty string', async () => {
|
|
1228
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
1229
|
-
const
|
|
1230
|
-
await
|
|
1249
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
1250
|
+
const harness = await setupHandlers();
|
|
1251
|
+
await harness.callTool('gog_sheets_conditional_format_add', {
|
|
1231
1252
|
spreadsheetId: 'sid', range: 'A1', type: 'text-eq', expr: '', formatJson: '{}',
|
|
1232
1253
|
});
|
|
1233
1254
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
@@ -1235,52 +1256,97 @@ describe('gog_sheets_conditional_format_add', () => {
|
|
|
1235
1256
|
{ account: undefined },
|
|
1236
1257
|
);
|
|
1237
1258
|
});
|
|
1259
|
+
|
|
1260
|
+
it('passes --gradient-rule-json for gradient rules (no type/format-json)', async () => {
|
|
1261
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
1262
|
+
const harness = await setupHandlers();
|
|
1263
|
+
await harness.callTool('gog_sheets_conditional_format_add', {
|
|
1264
|
+
spreadsheetId: 'sid', range: 'Sheet1!A1:A100', gradientRuleJson: '{"minpoint":{},"maxpoint":{}}',
|
|
1265
|
+
});
|
|
1266
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
1267
|
+
['sheets', 'conditional-format', 'add', 'sid', 'Sheet1!A1:A100', '--gradient-rule-json={"minpoint":{},"maxpoint":{}}'],
|
|
1268
|
+
{ account: undefined },
|
|
1269
|
+
);
|
|
1270
|
+
});
|
|
1271
|
+
});
|
|
1272
|
+
|
|
1273
|
+
// 40b. filter set
|
|
1274
|
+
describe('gog_sheets_filter_set', () => {
|
|
1275
|
+
it('calls runOrDiagnose without --force by default', async () => {
|
|
1276
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
1277
|
+
const harness = await setupHandlers();
|
|
1278
|
+
await harness.callTool('gog_sheets_filter_set', { spreadsheetId: 'sid', range: 'Sheet1!A1:C10' });
|
|
1279
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
1280
|
+
['sheets', 'filter', 'set', 'sid', 'Sheet1!A1:C10'],
|
|
1281
|
+
{ account: undefined },
|
|
1282
|
+
);
|
|
1283
|
+
});
|
|
1284
|
+
|
|
1285
|
+
it('appends --force when replace is true', async () => {
|
|
1286
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
1287
|
+
const harness = await setupHandlers();
|
|
1288
|
+
await harness.callTool('gog_sheets_filter_set', { spreadsheetId: 'sid', range: 'Sheet1!A1:C10', replace: true });
|
|
1289
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
1290
|
+
['sheets', 'filter', 'set', 'sid', 'Sheet1!A1:C10', '--force'],
|
|
1291
|
+
{ account: undefined },
|
|
1292
|
+
);
|
|
1293
|
+
});
|
|
1294
|
+
|
|
1295
|
+
it('forwards account', async () => {
|
|
1296
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
1297
|
+
const harness = await setupHandlers();
|
|
1298
|
+
await harness.callTool('gog_sheets_filter_set', { spreadsheetId: 'sid', range: 'Named', account: 'x@y.com' });
|
|
1299
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
1300
|
+
['sheets', 'filter', 'set', 'sid', 'Named'],
|
|
1301
|
+
{ account: 'x@y.com' },
|
|
1302
|
+
);
|
|
1303
|
+
});
|
|
1238
1304
|
});
|
|
1239
1305
|
|
|
1240
1306
|
// 41. conditional-format clear
|
|
1241
1307
|
describe('gog_sheets_conditional_format_clear', () => {
|
|
1242
1308
|
it('calls runOrDiagnose with --sheet only', async () => {
|
|
1243
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
1244
|
-
const
|
|
1245
|
-
await
|
|
1246
|
-
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'conditional-format', 'clear', 'sid', '--sheet=Data'], { account: undefined });
|
|
1309
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
1310
|
+
const harness = await setupHandlers();
|
|
1311
|
+
await harness.callTool('gog_sheets_conditional_format_clear', { spreadsheetId: 'sid', sheet: 'Data' });
|
|
1312
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'conditional-format', 'clear', 'sid', '--sheet=Data', '--force'], { account: undefined });
|
|
1247
1313
|
});
|
|
1248
1314
|
|
|
1249
1315
|
it('includes --index when provided (including 0)', async () => {
|
|
1250
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
1251
|
-
const
|
|
1252
|
-
await
|
|
1253
|
-
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'conditional-format', 'clear', 'sid', '--sheet=Data', '--index=0'], { account: undefined });
|
|
1316
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
1317
|
+
const harness = await setupHandlers();
|
|
1318
|
+
await harness.callTool('gog_sheets_conditional_format_clear', { spreadsheetId: 'sid', sheet: 'Data', index: 0 });
|
|
1319
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'conditional-format', 'clear', 'sid', '--sheet=Data', '--index=0', '--force'], { account: undefined });
|
|
1254
1320
|
});
|
|
1255
1321
|
|
|
1256
1322
|
it('includes --all when true', async () => {
|
|
1257
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
1258
|
-
const
|
|
1259
|
-
await
|
|
1260
|
-
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'conditional-format', 'clear', 'sid', '--sheet=Data', '--all'], { account: undefined });
|
|
1323
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
1324
|
+
const harness = await setupHandlers();
|
|
1325
|
+
await harness.callTool('gog_sheets_conditional_format_clear', { spreadsheetId: 'sid', sheet: 'Data', all: true });
|
|
1326
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'conditional-format', 'clear', 'sid', '--sheet=Data', '--all', '--force'], { account: undefined });
|
|
1261
1327
|
});
|
|
1262
1328
|
|
|
1263
1329
|
it('omits --all when false', async () => {
|
|
1264
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
1265
|
-
const
|
|
1266
|
-
await
|
|
1267
|
-
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'conditional-format', 'clear', 'sid', '--sheet=Data', '--index=2'], { account: undefined });
|
|
1330
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
1331
|
+
const harness = await setupHandlers();
|
|
1332
|
+
await harness.callTool('gog_sheets_conditional_format_clear', { spreadsheetId: 'sid', sheet: 'Data', all: false, index: 2 });
|
|
1333
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'conditional-format', 'clear', 'sid', '--sheet=Data', '--index=2', '--force'], { account: undefined });
|
|
1268
1334
|
});
|
|
1269
1335
|
});
|
|
1270
1336
|
|
|
1271
1337
|
// 49. snapshot (backup before destructive edits)
|
|
1272
1338
|
describe('gog_sheets_snapshot', () => {
|
|
1273
1339
|
it('copies the spreadsheet to a named backup', async () => {
|
|
1274
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
1275
|
-
const
|
|
1276
|
-
await
|
|
1340
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
1341
|
+
const harness = await setupHandlers();
|
|
1342
|
+
await harness.callTool('gog_sheets_snapshot', { spreadsheetId: 'sid', name: 'Backup A' });
|
|
1277
1343
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['drive', 'copy', 'sid', 'Backup A'], { account: undefined });
|
|
1278
1344
|
});
|
|
1279
1345
|
|
|
1280
1346
|
it('includes --parent and forwards account', async () => {
|
|
1281
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
1282
|
-
const
|
|
1283
|
-
await
|
|
1347
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
1348
|
+
const harness = await setupHandlers();
|
|
1349
|
+
await harness.callTool('gog_sheets_snapshot', { spreadsheetId: 'sid', name: 'Backup A', parent: 'folder1', account: 'a@b.com' });
|
|
1284
1350
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['drive', 'copy', 'sid', 'Backup A', '--parent=folder1'], { account: 'a@b.com' });
|
|
1285
1351
|
});
|
|
1286
1352
|
});
|
|
@@ -1288,9 +1354,9 @@ describe('gog_sheets_snapshot', () => {
|
|
|
1288
1354
|
// gog 0.24.0
|
|
1289
1355
|
describe('gog_sheets_validation_get', () => {
|
|
1290
1356
|
it('reads validation rules for a range', async () => {
|
|
1291
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
1292
|
-
const
|
|
1293
|
-
await
|
|
1357
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
1358
|
+
const harness = await setupHandlers();
|
|
1359
|
+
await harness.callTool('gog_sheets_validation_get', { spreadsheetId: 'sid', range: 'Sheet1!A1:A10' });
|
|
1294
1360
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
1295
1361
|
['sheets', 'validation', 'get', 'sid', 'Sheet1!A1:A10'],
|
|
1296
1362
|
{ account: undefined },
|
|
@@ -1300,9 +1366,9 @@ describe('gog_sheets_validation_get', () => {
|
|
|
1300
1366
|
|
|
1301
1367
|
describe('gog_sheets_validation_set', () => {
|
|
1302
1368
|
it('sets a dropdown rule with repeated values', async () => {
|
|
1303
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
1304
|
-
const
|
|
1305
|
-
await
|
|
1369
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
1370
|
+
const harness = await setupHandlers();
|
|
1371
|
+
await harness.callTool('gog_sheets_validation_set', {
|
|
1306
1372
|
spreadsheetId: 'sid', range: 'A1:A10', type: 'ONE_OF_LIST', values: ['Red', 'Green'],
|
|
1307
1373
|
});
|
|
1308
1374
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
@@ -1312,9 +1378,9 @@ describe('gog_sheets_validation_set', () => {
|
|
|
1312
1378
|
});
|
|
1313
1379
|
|
|
1314
1380
|
it('passes strict, input message, custom UI and filtered-rows flags', async () => {
|
|
1315
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
1316
|
-
const
|
|
1317
|
-
await
|
|
1381
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
1382
|
+
const harness = await setupHandlers();
|
|
1383
|
+
await harness.callTool('gog_sheets_validation_set', {
|
|
1318
1384
|
spreadsheetId: 'sid', range: 'A1', type: 'NUMBER_BETWEEN', values: ['1', '10'],
|
|
1319
1385
|
strict: true, inputMessage: 'Pick 1-10', showCustomUi: true, filteredRowsIncluded: true,
|
|
1320
1386
|
});
|
|
@@ -1328,9 +1394,9 @@ describe('gog_sheets_validation_set', () => {
|
|
|
1328
1394
|
|
|
1329
1395
|
describe('gog_sheets_validation_clear', () => {
|
|
1330
1396
|
it('clears rules from a range', async () => {
|
|
1331
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
1332
|
-
const
|
|
1333
|
-
await
|
|
1397
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
1398
|
+
const harness = await setupHandlers();
|
|
1399
|
+
await harness.callTool('gog_sheets_validation_clear', { spreadsheetId: 'sid', range: 'A1:A10', filteredRowsIncluded: true });
|
|
1334
1400
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
1335
1401
|
['sheets', 'validation', 'clear', 'sid', 'A1:A10', '--filtered-rows-included'],
|
|
1336
1402
|
{ account: undefined },
|
|
@@ -1340,25 +1406,25 @@ describe('gog_sheets_validation_clear', () => {
|
|
|
1340
1406
|
|
|
1341
1407
|
describe('gog_sheets_delete_dimension', () => {
|
|
1342
1408
|
it('deletes a row span from a sheet target', async () => {
|
|
1343
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
1344
|
-
const
|
|
1345
|
-
await
|
|
1409
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
1410
|
+
const harness = await setupHandlers();
|
|
1411
|
+
await harness.callTool('gog_sheets_delete_dimension', {
|
|
1346
1412
|
spreadsheetId: 'sid', rangeOrSheet: 'Sheet1', dimension: 'ROWS', start: 5, end: 7,
|
|
1347
1413
|
});
|
|
1348
1414
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
1349
|
-
['sheets', 'delete-dimension', 'sid', 'Sheet1', '--dimension=ROWS', '--start=5', '--end=7'],
|
|
1415
|
+
['sheets', 'delete-dimension', 'sid', 'Sheet1', '--dimension=ROWS', '--start=5', '--end=7', '--force'],
|
|
1350
1416
|
{ account: undefined },
|
|
1351
1417
|
);
|
|
1352
1418
|
});
|
|
1353
1419
|
|
|
1354
1420
|
it('deletes columns by A1 range without start/end', async () => {
|
|
1355
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
1356
|
-
const
|
|
1357
|
-
await
|
|
1421
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
1422
|
+
const harness = await setupHandlers();
|
|
1423
|
+
await harness.callTool('gog_sheets_delete_dimension', {
|
|
1358
1424
|
spreadsheetId: 'sid', rangeOrSheet: 'Sheet1!C:D', dimension: 'COLUMNS', account: 'a@b.com',
|
|
1359
1425
|
});
|
|
1360
1426
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
1361
|
-
['sheets', 'delete-dimension', 'sid', 'Sheet1!C:D', '--dimension=COLUMNS'],
|
|
1427
|
+
['sheets', 'delete-dimension', 'sid', 'Sheet1!C:D', '--dimension=COLUMNS', '--force'],
|
|
1362
1428
|
{ account: 'a@b.com' },
|
|
1363
1429
|
);
|
|
1364
1430
|
});
|
|
@@ -1367,9 +1433,9 @@ describe('gog_sheets_delete_dimension', () => {
|
|
|
1367
1433
|
// Falsy-branch coverage for the validation tools (no optional flags)
|
|
1368
1434
|
describe('validation tools bare calls', () => {
|
|
1369
1435
|
it('gog_sheets_validation_set without values (e.g. BOOLEAN checkbox)', async () => {
|
|
1370
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
1371
|
-
const
|
|
1372
|
-
await
|
|
1436
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
1437
|
+
const harness = await setupHandlers();
|
|
1438
|
+
await harness.callTool('gog_sheets_validation_set', { spreadsheetId: 'sid', range: 'A1', type: 'BOOLEAN' });
|
|
1373
1439
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
1374
1440
|
['sheets', 'validation', 'set', 'sid', 'A1', '--type=BOOLEAN'],
|
|
1375
1441
|
{ account: undefined },
|
|
@@ -1377,12 +1443,40 @@ describe('validation tools bare calls', () => {
|
|
|
1377
1443
|
});
|
|
1378
1444
|
|
|
1379
1445
|
it('gog_sheets_validation_clear without filteredRowsIncluded', async () => {
|
|
1380
|
-
vi.mocked(lib.runOrDiagnose).mockResolvedValue(
|
|
1381
|
-
const
|
|
1382
|
-
await
|
|
1446
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
|
|
1447
|
+
const harness = await setupHandlers();
|
|
1448
|
+
await harness.callTool('gog_sheets_validation_clear', { spreadsheetId: 'sid', range: 'A1' });
|
|
1383
1449
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
1384
1450
|
['sheets', 'validation', 'clear', 'sid', 'A1'],
|
|
1385
1451
|
{ account: undefined },
|
|
1386
1452
|
);
|
|
1387
1453
|
});
|
|
1388
1454
|
});
|
|
1455
|
+
|
|
1456
|
+
// resultText degradations: a non-text tool result (never produced by
|
|
1457
|
+
// runOrDiagnose today, but allowed by the MCP result shape) degrades
|
|
1458
|
+
// gracefully instead of crashing the post-processing.
|
|
1459
|
+
describe('non-text result passthrough', () => {
|
|
1460
|
+
it('gog_sheets_number_format skips the warning when the peek result is non-text', async () => {
|
|
1461
|
+
const harness = await setupHandlers();
|
|
1462
|
+
vi.mocked(lib.runOrDiagnose).mockImplementation(async (args: string[]) => {
|
|
1463
|
+
if (args[1] === 'get') return { content: [] };
|
|
1464
|
+
return rawTextResult('{"ok":true}');
|
|
1465
|
+
});
|
|
1466
|
+
const result = await harness.callTool('gog_sheets_number_format', { spreadsheetId: 'sid', range: 'A1:A3', type: 'DATE' });
|
|
1467
|
+
expect(result.content[0].text).toBe('{"ok":true}');
|
|
1468
|
+
});
|
|
1469
|
+
|
|
1470
|
+
it('gog_sheets_number_format merges the warning onto a non-text format result', async () => {
|
|
1471
|
+
const harness = await setupHandlers();
|
|
1472
|
+
vi.mocked(lib.runOrDiagnose).mockImplementation(async (args: string[]) => {
|
|
1473
|
+
if (args[1] === 'get') {
|
|
1474
|
+
return rawTextResult(JSON.stringify({ range: 'A1:A3', values: [[1], [2], [3]] }));
|
|
1475
|
+
}
|
|
1476
|
+
return { content: [] };
|
|
1477
|
+
});
|
|
1478
|
+
const result = await harness.callTool('gog_sheets_number_format', { spreadsheetId: 'sid', range: 'A1:A3', type: 'DATE' });
|
|
1479
|
+
expect(result.content[0].type).toBe('text');
|
|
1480
|
+
expect(result.content[0].text).toMatch(/warning/i);
|
|
1481
|
+
});
|
|
1482
|
+
});
|