google-drive-mock 1.1.6 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/AGENTS.md +4 -1
  2. package/CLAUDE.md +17 -0
  3. package/dist/index.js +2 -1
  4. package/dist/mappers.d.ts +5 -0
  5. package/dist/mappers.js +15 -0
  6. package/dist/routes/v2.js +16 -8
  7. package/dist/routes/v3.js +30 -9
  8. package/dist/store.js +2 -2
  9. package/package.json +4 -4
  10. package/scripts/check-token.ts +107 -38
  11. package/scripts/run-loop.sh +18 -0
  12. package/src/index.ts +2 -1
  13. package/src/mappers.ts +15 -0
  14. package/src/routes/v2.ts +16 -8
  15. package/src/routes/v3.ts +35 -10
  16. package/src/store.ts +2 -2
  17. package/test/advanced_changes.test.ts +76 -29
  18. package/test/advanced_ordering.test.ts +2 -1
  19. package/test/basics.test.ts +34 -58
  20. package/test/batch_and_query.test.ts +28 -62
  21. package/test/batch_insert_download.test.ts +2 -1
  22. package/test/check_empty.test.ts +60 -0
  23. package/test/complex_query.test.ts +2 -1
  24. package/test/concurrent_fetch.test.ts +2 -1
  25. package/test/config.ts +164 -7
  26. package/test/dates_and_sorting.test.ts +2 -1
  27. package/test/etag.test.ts +8 -4
  28. package/test/features.test.ts +2 -1
  29. package/test/folder_query.test.ts +2 -1
  30. package/test/folder_search.test.ts +2 -1
  31. package/test/iterate_changes.test.ts +153 -68
  32. package/test/latency.test.ts +2 -1
  33. package/test/mime_types.test.ts +2 -1
  34. package/test/missing_fields.test.ts +2 -1
  35. package/test/multipart_behavior.test.ts +2 -1
  36. package/test/parallel_update.test.ts +2 -1
  37. package/test/parity_media_download.test.ts +2 -1
  38. package/test/routines.test.ts +15 -12
  39. package/test/upload.test.ts +2 -1
  40. package/test/url_parameters.test.ts +2 -1
  41. package/test/v2_basics.test.ts +22 -13
  42. package/test/v2_content.test.ts +2 -1
  43. package/test/v2_missing_ops.test.ts +75 -75
  44. package/test/v2_routes.test.ts +31 -21
  45. package/test/v2_upload.test.ts +17 -9
  46. package/test/v3_parity.test.ts +56 -20
  47. package/test_etag_headers.ts +92 -0
  48. package/vitest.config.ts +7 -1
package/src/routes/v3.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import express, { Request, Response } from 'express';
2
2
  import { driveStore } from '../store';
3
- import { applyFields } from '../mappers';
3
+ import { applyFields, toV3File } from '../mappers';
4
4
 
5
5
  export const createV3Router = () => {
6
6
  const app = express.Router();
@@ -202,7 +202,7 @@ export const createV3Router = () => {
202
202
  }
203
203
 
204
204
  const totalFiles = files.length;
205
- const resultFiles = files.slice(skip, skip + pageSize);
205
+ const resultFiles = files.slice(skip, skip + pageSize).map(toV3File);
206
206
 
207
207
  let nextPageToken: string | undefined;
208
208
  if (skip + pageSize < totalFiles) {
@@ -260,11 +260,20 @@ export const createV3Router = () => {
260
260
  }
261
261
 
262
262
  const result = driveStore.getChanges(pageToken);
263
+ const mappedChanges = result.changes.map(c => {
264
+ if (c.file) {
265
+ return {
266
+ ...c,
267
+ file: toV3File(c.file)
268
+ };
269
+ }
270
+ return c;
271
+ });
263
272
  res.json({
264
273
  kind: "drive#changeList",
265
274
  newStartPageToken: result.newStartPageToken,
266
275
  nextPageToken: result.nextPageToken,
267
- changes: result.changes
276
+ changes: mappedChanges
268
277
  });
269
278
  });
270
279
 
@@ -292,7 +301,7 @@ export const createV3Router = () => {
292
301
  parents: [],
293
302
  content: typeof content === 'string' || Buffer.isBuffer(content) ? content : JSON.stringify(content)
294
303
  });
295
- res.status(200).json(newFile);
304
+ res.status(200).json(toV3File(newFile));
296
305
  return;
297
306
  }
298
307
 
@@ -367,7 +376,7 @@ export const createV3Router = () => {
367
376
  content: content
368
377
  });
369
378
 
370
- res.status(200).json(newFile);
379
+ res.status(200).json(toV3File(newFile));
371
380
  });
372
381
 
373
382
  // Upload Files: Update (PATCH)
@@ -384,6 +393,17 @@ export const createV3Router = () => {
384
393
  return;
385
394
  }
386
395
 
396
+ const ifMatchHeader = req.headers['if-match'];
397
+ const ifMatch = Array.isArray(ifMatchHeader) ? ifMatchHeader[0] : ifMatchHeader;
398
+ if (ifMatch && ifMatch !== '*') {
399
+ const cleanIfMatch = ifMatch.replace(/^"|"$/g, '');
400
+ const cleanEtag = existingFile.etag.replace(/^"|"$/g, '');
401
+ if (cleanIfMatch !== cleanEtag) {
402
+ res.status(412).json({ error: { code: 412, message: "Precondition Failed" } });
403
+ return;
404
+ }
405
+ }
406
+
387
407
  const uploadType = req.query.uploadType as string;
388
408
 
389
409
  if (uploadType === 'media') {
@@ -396,7 +416,7 @@ export const createV3Router = () => {
396
416
  content: typeof content === 'string' || Buffer.isBuffer(content) ? content : JSON.stringify(content),
397
417
  modifiedTime: new Date().toISOString()
398
418
  });
399
- res.status(200).json(updatedFile!);
419
+ res.status(200).json(toV3File(updatedFile!));
400
420
  return;
401
421
  }
402
422
 
@@ -470,7 +490,7 @@ export const createV3Router = () => {
470
490
  modifiedTime: new Date().toISOString()
471
491
  });
472
492
 
473
- res.status(200).json(updatedFile);
493
+ res.status(200).json(toV3File(updatedFile!));
474
494
  return;
475
495
  }
476
496
 
@@ -489,7 +509,7 @@ export const createV3Router = () => {
489
509
  parents: body.parents || []
490
510
  });
491
511
 
492
- res.status(200).json(newFile);
512
+ res.status(200).json(toV3File(newFile));
493
513
  });
494
514
 
495
515
  // Files: Get
@@ -542,7 +562,12 @@ export const createV3Router = () => {
542
562
  return;
543
563
  }
544
564
 
545
- res.json(file);
565
+ const v3File = toV3File(file);
566
+ if (fields) {
567
+ res.json(applyFields(v3File, fields));
568
+ return;
569
+ }
570
+ res.json(v3File);
546
571
  });
547
572
 
548
573
  // Files: Update
@@ -591,7 +616,7 @@ export const createV3Router = () => {
591
616
  }
592
617
  }
593
618
 
594
- res.json(updatedFile);
619
+ res.json(toV3File(updatedFile));
595
620
  });
596
621
 
597
622
  // Files: Delete
package/src/store.ts CHANGED
@@ -95,7 +95,7 @@ export class DriveStore {
95
95
  ...file,
96
96
  id,
97
97
  version: 1, // Initialize version
98
- etag: "1", // Initialize etag
98
+ etag: '"1"', // Initialize etag
99
99
  // Ensure calculated stats override provided ones
100
100
  size: stats.size,
101
101
  md5Checksum: stats.md5Checksum
@@ -123,7 +123,7 @@ export class DriveStore {
123
123
  ...updates,
124
124
  ...statsUpdates,
125
125
  version: newVersion,
126
- etag: String(newVersion),
126
+ etag: `"${newVersion}"`,
127
127
  modifiedTime: updates.modifiedTime || new Date().toISOString()
128
128
  };
129
129
  this.files.set(id, updatedFile);
@@ -1,4 +1,5 @@
1
- import { describe, it, expect, beforeAll, vi, afterEach } from 'vitest';
1
+ import { describe, expect, beforeAll, vi, afterEach } from 'vitest';
2
+ import { it } from './config';;
2
3
  import { getTestConfig, TestConfig } from './config';
3
4
  import { DriveFile, DriveChange } from '../src/store';
4
5
 
@@ -29,10 +30,6 @@ describe('Advanced Drive Features (Part 1)', () => {
29
30
  return { status: res.status, body: text, headers: res.headers };
30
31
  }
31
32
  };
32
-
33
- if (!config.testFolderId) {
34
- // ensure folder
35
- }
36
33
  });
37
34
 
38
35
  // Rate Limit Mitigation for Real API
@@ -40,7 +37,7 @@ describe('Advanced Drive Features (Part 1)', () => {
40
37
  await new Promise(r => setTimeout(r, 1000));
41
38
  });
42
39
 
43
- it('should support changes feed (startPageToken and listing changes)', async () => {
40
+ it('should support changes feed: file creation change', async () => {
44
41
  // 1. Get Start Page Token
45
42
  const tokenRes = await req('GET', '/drive/v3/changes/startPageToken?supportsAllDrives=true');
46
43
  expect(tokenRes.status).toBe(200);
@@ -48,7 +45,7 @@ describe('Advanced Drive Features (Part 1)', () => {
48
45
  expect(startToken).toBeDefined();
49
46
 
50
47
  // 2. Make a change (Create file)
51
- const fileName = `ChangeTest-${Date.now()}`;
48
+ const fileName = `ChangeTest-Create-${Date.now()}`;
52
49
  const createRes = await req('POST', '/drive/v3/files', {
53
50
  name: fileName,
54
51
  parents: [config.testFolderId]
@@ -56,23 +53,30 @@ describe('Advanced Drive Features (Part 1)', () => {
56
53
  expect(createRes.status).toBe(200);
57
54
  const fileId = (createRes.body as DriveFile).id;
58
55
 
59
- // 3. List Changes
60
- const changesRes = await req('GET', `/drive/v3/changes?pageToken=${startToken}&supportsAllDrives=true&includeItemsFromAllDrives=true&fields=changes(fileId,removed,file(name))`);
61
- expect(changesRes.status).toBe(200);
62
-
56
+ // 3. List Changes (poll for creation)
63
57
  let found: DriveChange | undefined;
64
58
  const maxRetries = 20;
65
- const retryDelay = 400;
59
+ const retryDelay = 300;
66
60
 
67
61
  for (let i = 0; i < maxRetries; i++) {
68
- const changesRes = await req('GET', `/drive/v3/changes?pageToken=${startToken}&supportsAllDrives=true&includeItemsFromAllDrives=true&fields=changes(fileId,removed,file(name))`);
69
- expect(changesRes.status).toBe(200);
70
- const changes = (changesRes.body as { changes: DriveChange[] }).changes;
71
- found = changes.find((c: DriveChange) => c.fileId === fileId);
62
+ let currentToken: string | undefined = startToken;
63
+ let foundInPage = false;
64
+ while (currentToken) {
65
+ const changesRes = await req('GET', `/drive/v3/changes?pageToken=${currentToken}&supportsAllDrives=true&includeItemsFromAllDrives=true&fields=changes(fileId,removed,file(name)),nextPageToken`);
66
+ expect(changesRes.status).toBe(200);
67
+ const body = changesRes.body as { changes: DriveChange[]; nextPageToken?: string };
68
+ const changes = body.changes || [];
69
+ found = changes.find((c: DriveChange) => c.fileId === fileId);
70
+
71
+ if (found) {
72
+ foundInPage = true;
73
+ break;
74
+ }
75
+ currentToken = body.nextPageToken;
76
+ }
72
77
 
73
- if (found) break;
78
+ if (foundInPage) break;
74
79
 
75
- // Wait before retry
76
80
  if (i < maxRetries - 1) {
77
81
  await new Promise(r => setTimeout(r, retryDelay));
78
82
  }
@@ -84,18 +88,61 @@ describe('Advanced Drive Features (Part 1)', () => {
84
88
  expect(found.file?.name).toBe(fileName);
85
89
  }
86
90
 
87
- // 4. Delete file (Change)
91
+ // Clean up without waiting
92
+ await req('DELETE', `/drive/v3/files/${fileId}`);
93
+ }, 10000);
94
+
95
+ it('should support changes feed: file deletion change', async () => {
96
+ // 1. Get Start Page Token
97
+ const tokenRes = await req('GET', '/drive/v3/changes/startPageToken?supportsAllDrives=true');
98
+ expect(tokenRes.status).toBe(200);
99
+ const startToken = (tokenRes.body as { startPageToken: string }).startPageToken;
100
+ expect(startToken).toBeDefined();
101
+
102
+ // 2. Create file
103
+ const createRes = await req('POST', '/drive/v3/files', {
104
+ name: `ChangeTest-DeletePrep-${Date.now()}`,
105
+ parents: [config.testFolderId]
106
+ });
107
+ expect(createRes.status).toBe(200);
108
+ const fileId = (createRes.body as DriveFile).id;
109
+
110
+ // 3. Delete the file
88
111
  await req('DELETE', `/drive/v3/files/${fileId}`);
89
- const changesRes2 = await req('GET', `/drive/v3/changes?pageToken=${startToken}&supportsAllDrives=true&includeItemsFromAllDrives=true`);
90
- const changes2 = (changesRes2.body as { changes: DriveChange[] }).changes;
91
- const deletion = changes2.find((c: DriveChange) => c.fileId === fileId && c.removed === true);
92
-
93
- // On Real API, deletion might propagate slowly or token behavior slightly diff.
94
- // We'll relax the test or expect it to work.
95
- // For strict parity 'Tests should behave exactly equal', we should expect definition.
96
- // If it fails on Real, we fix the test to wait/retry.
97
- // For now, let's keep expectation or if we want to be safe, verify if 'deletion' exists is enough.
98
- if (deletion) expect(deletion.removed).toBe(true);
112
+
113
+ // 4. Poll changes feed for deletion
114
+ let deletion: DriveChange | undefined;
115
+ const maxRetries = 20;
116
+ const retryDelay = 300;
117
+
118
+ for (let i = 0; i < maxRetries; i++) {
119
+ let currentToken: string | undefined = startToken;
120
+ let foundInPage = false;
121
+ while (currentToken) {
122
+ const changesRes2 = await req('GET', `/drive/v3/changes?pageToken=${currentToken}&supportsAllDrives=true&includeItemsFromAllDrives=true&fields=changes(fileId,removed),nextPageToken`);
123
+ expect(changesRes2.status).toBe(200);
124
+ const body = changesRes2.body as { changes: DriveChange[]; nextPageToken?: string };
125
+ const changes2 = body.changes || [];
126
+ deletion = changes2.find((c: DriveChange) => c.fileId === fileId && c.removed === true);
127
+
128
+ if (deletion) {
129
+ foundInPage = true;
130
+ break;
131
+ }
132
+ currentToken = body.nextPageToken;
133
+ }
134
+
135
+ if (foundInPage) break;
136
+
137
+ if (i < maxRetries - 1) {
138
+ await new Promise(r => setTimeout(r, retryDelay));
139
+ }
140
+ }
141
+
142
+ expect(deletion).toBeDefined();
143
+ if (deletion) {
144
+ expect(deletion.removed).toBe(true);
145
+ }
99
146
  }, 10000);
100
147
 
101
148
  it('should support advanced query operators (contains, in parents)', async () => {
@@ -1,4 +1,5 @@
1
- import { describe, it, expect, beforeAll, vi, afterEach } from 'vitest';
1
+ import { describe, expect, beforeAll, vi, afterEach } from 'vitest';
2
+ import { it } from './config';;
2
3
  import { getTestConfig, TestConfig } from './config';
3
4
  import { DriveFile } from '../src/store';
4
5
 
@@ -1,4 +1,5 @@
1
- import { describe, it, expect, beforeAll, afterAll } from 'vitest';
1
+ import { describe, expect, beforeAll, afterAll } from 'vitest';
2
+ import { it } from './config';;
2
3
  import { getTestConfig, TestConfig } from './config';
3
4
  import { Server } from 'http';
4
5
 
@@ -75,12 +76,11 @@ describe('Google Drive Mock API', () => {
75
76
  });
76
77
 
77
78
  describe('Files API', () => {
78
- let createdFileId: string;
79
+ const fileName = 'Test File ' + Math.random().toString(36).substring(7);
79
80
 
80
- // 1. Create File
81
- it('POST /drive/v3/files - should create a file (Happy Path)', async () => {
81
+ it('should support full file lifecycle: create, get, update, delete', async () => {
82
82
  const newFile = {
83
- name: 'Test File',
83
+ name: fileName,
84
84
  mimeType: 'text/plain',
85
85
  parents: [config.testFolderId]
86
86
  };
@@ -89,8 +89,27 @@ describe('Google Drive Mock API', () => {
89
89
  expect(response.status).toBe(200);
90
90
  expect(response.body.name).toBe(newFile.name);
91
91
  expect(response.body.id).toBeDefined();
92
- // Verify parent? (Real API doesn't always return parents by default unless requested)
93
- createdFileId = response.body.id;
92
+ const createdFileId = response.body.id;
93
+
94
+ // 2. Get File
95
+ const responseGet = await req('GET', `/drive/v3/files/${createdFileId}`);
96
+ expect(responseGet.status).toBe(200);
97
+ expect(responseGet.body.id).toBe(createdFileId);
98
+ expect(responseGet.body.name).toBe(fileName);
99
+
100
+ // 3. Update File
101
+ const updatedName = 'Updated Name ' + Math.random().toString(36).substring(7);
102
+ const responsePatch = await req('PATCH', `/drive/v3/files/${createdFileId}`, { name: updatedName });
103
+ expect(responsePatch.status).toBe(200);
104
+ expect(responsePatch.body.name).toBe(updatedName);
105
+
106
+ // 4. Delete File
107
+ const responseDelete = await req('DELETE', `/drive/v3/files/${createdFileId}`);
108
+ expect(responseDelete.status).toBe(204);
109
+
110
+ // 5. Verify Deletion
111
+ const responseCheck = await req('GET', `/drive/v3/files/${createdFileId}`);
112
+ expect(responseCheck.status).toBe(404);
94
113
  });
95
114
 
96
115
  it('POST /drive/v3/files - should allow file creation without name (defaults to Untitled?)', async () => {
@@ -104,69 +123,26 @@ describe('Google Drive Mock API', () => {
104
123
  // Optionally check name if we want to be strict about "Untitled".
105
124
  // For now, status 200 is the key parity requirement.
106
125
  });
107
-
108
- // 2. Get File
109
- it('GET /drive/v3/files/:id - should get file', async () => {
110
- // Need to verify createdFileId exists (if previous test failed, this might fail or throw)
111
- if (!createdFileId) return; // Skip
112
-
113
- const response = await req('GET', `/drive/v3/files/${createdFileId}`);
114
-
115
- expect(response.status).toBe(200);
116
- expect(response.body.id).toBe(createdFileId);
117
- expect(response.body.name).toBe('Test File');
118
- });
119
-
120
- // 3. Update File
121
- it('PATCH /drive/v3/files/:id - should update file', async () => {
122
- if (!createdFileId) return;
123
-
124
- const response = await req('PATCH', `/drive/v3/files/${createdFileId}`, { name: 'Updated Name' });
125
-
126
- expect(response.status).toBe(200);
127
- expect(response.body.name).toBe('Updated Name');
128
- });
129
-
130
- // 4. Delete File
131
- it('DELETE /drive/v3/files/:id - should delete file', async () => {
132
- if (!createdFileId) return;
133
- const response = await req('DELETE', `/drive/v3/files/${createdFileId}`);
134
- expect(response.status).toBe(204);
135
- });
136
-
137
- // 5. Verify Deletion
138
- it('GET /drive/v3/files/:id - should return 404 after delete', async () => {
139
- if (!createdFileId) return;
140
- const response = await req('GET', `/drive/v3/files/${createdFileId}`);
141
- expect(response.status).toBe(404);
142
- });
143
126
  });
144
127
 
145
128
  describe('Folders API', () => {
146
- let folderId: string;
147
-
148
- it('should create a new folder', async () => {
129
+ it('should support full folder lifecycle: create, delete, check 404', async () => {
130
+ const folderName = 'Test Folder ' + Math.random().toString(36).substring(7);
149
131
  const folder = {
150
- name: 'Test Folder',
132
+ name: folderName,
151
133
  mimeType: 'application/vnd.google-apps.folder',
152
134
  parents: [config.testFolderId]
153
135
  };
154
136
  const res = await req('POST', '/drive/v3/files', folder);
155
137
  expect(res.status).toBe(200);
156
138
  expect(res.body.mimeType).toBe('application/vnd.google-apps.folder');
157
- folderId = res.body.id;
158
- });
139
+ const folderId = res.body.id;
159
140
 
160
- it('should delete the folder', async () => {
161
- if (!folderId) return;
162
- const res = await req('DELETE', `/drive/v3/files/${folderId}`);
163
- expect(res.status).toBe(204);
164
- });
141
+ const resDelete = await req('DELETE', `/drive/v3/files/${folderId}`);
142
+ expect(resDelete.status).toBe(204);
165
143
 
166
- it('should return 404 after folder deletion', async () => {
167
- if (!folderId) return;
168
- const res = await req('GET', `/drive/v3/files/${folderId}`);
169
- expect(res.status).toBe(404);
144
+ const resCheck = await req('GET', `/drive/v3/files/${folderId}`);
145
+ expect(resCheck.status).toBe(404);
170
146
  });
171
147
  });
172
148
 
@@ -1,28 +1,24 @@
1
-
2
- import { describe, it, expect, beforeAll, afterAll } from 'vitest';
1
+ import { describe, expect, beforeAll, afterAll } from 'vitest';
2
+ import { it } from './config';
3
3
  import { getTestConfig, TestConfig } from './config';
4
4
 
5
5
  describe('Batch and Complex Query Operations', () => {
6
6
  let config: TestConfig;
7
- let createdFileIds: string[] = [];
8
7
 
9
8
  beforeAll(async () => {
10
9
  config = await getTestConfig();
11
10
  });
12
11
 
13
12
  afterAll(async () => {
14
- // Cleanup if needed
15
13
  if (config) config.stop();
16
14
  });
17
15
 
18
- it('should perform bulk insert using batch API', async () => {
16
+ it('should perform bulk insert, find, and update operations using batch and complex queries', async () => {
19
17
  const docs = [
20
18
  { id: 'bulk1', content: '{"foo":1}' },
21
19
  { id: 'bulk2', content: '{"bar":2}' }
22
20
  ];
23
21
  const boundary = "batch_" + Math.random().toString(16).slice(2);
24
-
25
- // Ensure we have a valid folder ID. Using root or a test folder from config.
26
22
  const targetFolderId = config.testFolderId;
27
23
 
28
24
  const parts = docs.map((doc, i) => {
@@ -44,7 +40,6 @@ describe('Batch and Complex Query Operations', () => {
44
40
  });
45
41
 
46
42
  const batchBody = parts.join("") + `--${boundary}--`;
47
-
48
43
  const url = config.baseUrl + "/batch/drive/v3";
49
44
  console.log('Sending batch insert request to:', url);
50
45
 
@@ -64,28 +59,18 @@ describe('Batch and Complex Query Operations', () => {
64
59
 
65
60
  const text = await res.text();
66
61
  console.log('Batch Insert Response:', text);
67
-
68
- // Verify operations succeeded
69
62
  expect(text).toContain('HTTP/1.1 200 OK');
70
63
 
71
- // Extract IDs for later use in update tests
72
- // This is a bit hacky parsing but sufficient for test
73
- // Responses are JSON inside multipart
74
- // We can list files to get IDs reliably
64
+ // Extract IDs for later use
75
65
  const listRes = await fetch(config.baseUrl + `/drive/v3/files?q='${targetFolderId}'+in+parents`, {
76
66
  headers: { Authorization: `Bearer ${config.token}` }
77
67
  });
78
68
  const listData = await listRes.json();
79
69
  const files = listData.files.filter((f: { name: string; id: string }) => f.name === 'bulk1.json' || f.name === 'bulk2.json');
80
70
  expect(files.length).toBe(2);
81
- createdFileIds = files.map((f: { id: string }) => f.id);
82
- });
83
71
 
84
- it('should perform bulk find using complex query', async () => {
85
- // Ensure the files exist from previous test
86
- expect(createdFileIds.length).toBe(2);
72
+ // 2. Perform bulk find using complex query
87
73
  const docIds = ['bulk1', 'bulk2'];
88
-
89
74
  const fileNames = docIds.map(id => id + '.json');
90
75
  let q = fileNames
91
76
  .map(name => `name = '${name.replace("'", "\\'")}'`)
@@ -101,58 +86,42 @@ describe('Batch and Complex Query Operations', () => {
101
86
  includeItemsFromAllDrives: "true",
102
87
  supportsAllDrives: "true",
103
88
  });
104
- const url = config.baseUrl + '/drive/v3/files?' + params.toString();
105
- const res = await fetch(url, {
89
+ const findUrl = config.baseUrl + '/drive/v3/files?' + params.toString();
90
+ const findRes = await fetch(findUrl, {
106
91
  method: "GET",
107
92
  headers: {
108
93
  Authorization: `Bearer ${config.token}`,
109
94
  },
110
95
  });
111
96
 
112
- expect(res.status).toBe(200);
113
- const data = await res.json();
97
+ expect(findRes.status).toBe(200);
98
+ const findData = await findRes.json();
114
99
 
115
- // Should find both files
116
- expect(data.files).toBeDefined();
117
- // Depending on query parsing logic, it might find one or both or none if logic is broken
118
- // The expectation is that this query works "like" finding specific files in a folder
119
- const foundNames = data.files.map((f: { name: string }) => f.name);
100
+ expect(findData.files).toBeDefined();
101
+ const foundNames = findData.files.map((f: { name: string }) => f.name);
120
102
  expect(foundNames).toContain('bulk1.json');
121
103
  expect(foundNames).toContain('bulk2.json');
122
- expect(data.files.length).toBeGreaterThanOrEqual(2);
123
- });
124
-
125
- it('should perform bulk update using batch API', async () => {
126
- expect(createdFileIds.length).toBe(2);
104
+ expect(findData.files.length).toBeGreaterThanOrEqual(2);
127
105
 
106
+ // 3. Perform bulk update using batch API
128
107
  interface DocUpdate {
129
108
  id: string;
130
109
  newName: string;
131
110
  }
132
- const docs: DocUpdate[] = [
111
+ const docUpdates: DocUpdate[] = [
133
112
  { id: 'bulk1', newName: 'bulk1_updated' },
134
113
  { id: 'bulk2', newName: 'bulk2_updated' }
135
114
  ];
136
115
 
137
- // Map doc ID to file ID (assuming order or searching)
138
- // For simplicity, we'll fetch IDs again or use stored ones knowing names match
139
- // Let's assume createdFileIds corresponds to 'bulk1.json' and 'bulk2.json' somehow
140
- // But better to use exact mapping.
141
-
142
- // Re-fetch to be sure of mapping
143
- const listRes = await fetch(config.baseUrl + `/drive/v3/files?q='${config.testFolderId}'+in+parents`, {
144
- headers: { Authorization: `Bearer ${config.token}` }
145
- });
146
- const listData = await listRes.json();
147
116
  const fileIdByDocId: Record<string, string> = {};
148
- for (const f of listData.files) {
117
+ for (const f of findData.files) {
149
118
  if (f.name === 'bulk1.json') fileIdByDocId['bulk1'] = f.id;
150
119
  if (f.name === 'bulk2.json') fileIdByDocId['bulk2'] = f.id;
151
120
  }
152
121
 
153
- const boundary = "batch_" + Math.random().toString(16).slice(2);
122
+ const updateBoundary = "batch_" + Math.random().toString(16).slice(2);
154
123
 
155
- const parts = docs.map((doc, i) => {
124
+ const updateParts = docUpdates.map((doc, i) => {
156
125
  const id = doc.id;
157
126
  const fileId = fileIdByDocId[id];
158
127
  if (!fileId) throw new Error(`File ID not found for ${id}`);
@@ -160,11 +129,10 @@ describe('Batch and Complex Query Operations', () => {
160
129
  const body = JSON.stringify({
161
130
  name: doc.newName + '.json',
162
131
  mimeType: "application/json",
163
- // parents: [config.testFolderId], // Optional in update usually
164
132
  });
165
133
 
166
134
  return (
167
- `--${boundary}\r\n` +
135
+ `--${updateBoundary}\r\n` +
168
136
  `Content-Type: application/http\r\n` +
169
137
  `Content-ID: <item-${i}>\r\n\r\n` +
170
138
  `PATCH /drive/v3/files/${encodeURIComponent(fileId)}?supportsAllDrives=true&fields=id,name,mimeType,parents HTTP/1.1\r\n` +
@@ -173,25 +141,23 @@ describe('Batch and Complex Query Operations', () => {
173
141
  );
174
142
  });
175
143
 
176
- const batchBody = parts.join("") + `--${boundary}--`;
144
+ const updateBatchBody = updateParts.join("") + `--${updateBoundary}--`;
145
+ const updateUrl = config.baseUrl + "/batch/drive/v3";
146
+ console.log('Sending batch update request to:', updateUrl);
177
147
 
178
- const url = config.baseUrl + "/batch/drive/v3";
179
- console.log('Sending batch update request to:', url);
180
-
181
- const res = await fetch(url, {
148
+ const updateRes = await fetch(updateUrl, {
182
149
  method: "POST",
183
150
  headers: {
184
151
  Authorization: `Bearer ${config.token}`,
185
- "Content-Type": `multipart/mixed; boundary=${boundary}`,
152
+ "Content-Type": `multipart/mixed; boundary=${updateBoundary}`,
186
153
  },
187
- body: batchBody,
154
+ body: updateBatchBody,
188
155
  });
189
156
 
190
- expect(res.status).toBe(200);
191
- const text = await res.text();
192
- console.log('Batch Update Response:', text);
193
-
194
- expect(text).toContain('HTTP/1.1 200 OK');
157
+ expect(updateRes.status).toBe(200);
158
+ const updateText = await updateRes.text();
159
+ console.log('Batch Update Response:', updateText);
160
+ expect(updateText).toContain('HTTP/1.1 200 OK');
195
161
 
196
162
  // Verify updates
197
163
  const verifyRes = await fetch(config.baseUrl + `/drive/v3/files?q='${config.testFolderId}'+in+parents`, {
@@ -1,5 +1,6 @@
1
1
 
2
- import { describe, it, expect, beforeAll, afterAll } from 'vitest';
2
+ import { describe, expect, beforeAll, afterAll } from 'vitest';
3
+ import { it } from './config';;
3
4
  import { getTestConfig, TestConfig } from './config';
4
5
 
5
6
  describe('Batch Insert and Download Test', () => {