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
@@ -1,4 +1,6 @@
1
- import { describe, it, expect, beforeAll } from 'vitest';
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import { describe, expect, beforeAll } from 'vitest';
3
+ import { it } from './config';;
2
4
  import {
3
5
  getTestConfig,
4
6
  TestConfig
@@ -78,51 +80,65 @@ describe('Iterate Changes Queries', () => {
78
80
  // Use file1's modifiedTime as the baseline (X)
79
81
  const timeX = file1.modifiedTime;
80
82
 
81
- // Query: modifiedTime > X, orderBy modifiedTime asc, name asc (using name as proxy for ID stability in test if needed, but user asked for ID)
82
- // User asked for: Sorted by write data and id. with limit
83
+ // Query: modifiedTime > X, orderBy modifiedTime asc, name asc
83
84
  const q = `modifiedTime > '${timeX}' and '${parentId}' in parents and trashed = false`;
84
85
  const orderBy = 'modifiedTime asc, name asc';
85
- const pageSize = 1;
86
+
87
+ // Poll until the search index is fully updated and returns both expected files
88
+ let data2: any;
89
+ const url2 = `${config.baseUrl}/drive/v3/files?q=${encodeURIComponent(q)}&orderBy=${encodeURIComponent(orderBy)}&pageSize=2`;
90
+ for (let attempt = 1; attempt <= 15; attempt++) {
91
+ const res2 = await fetch(url2, { headers });
92
+ expect(res2.status).toBe(200);
93
+ data2 = await res2.json();
94
+ const ids = (data2.files || []).map((f: any) => f.id);
95
+ if (ids.includes(file2.id) && ids.includes(file3.id)) {
96
+ break;
97
+ }
98
+ await new Promise(r => setTimeout(r, 2000));
99
+ }
100
+
101
+ expect(data2.files.length).toBe(2);
102
+ expect(data2.files[0].id).toBe(file2.id);
103
+ expect(data2.files[1].id).toBe(file3.id);
86
104
 
87
105
  // First page
106
+ const pageSize = 1;
88
107
  const url1 = `${config.baseUrl}/drive/v3/files?q=${encodeURIComponent(q)}&orderBy=${encodeURIComponent(orderBy)}&pageSize=${pageSize}`;
89
108
  const res1 = await fetch(url1, { headers });
90
- if (res1.status !== 200) {
91
- const txt = await res1.text();
92
- console.error('Error 1:', txt);
93
- }
94
109
  expect(res1.status).toBe(200);
95
110
  const data1 = await res1.json();
96
111
 
97
112
  expect(data1.files.length).toBe(1);
98
113
  expect(data1.files[0].id).toBe(file2.id);
99
-
100
- // If we want to simulate iteration, we would use nextPageToken or just offset logic if we supported it,
101
- // but here we just test that the query works and LIMIT works.
102
-
103
- // Verify we can get the next one if we increase limit
104
- const url2 = `${config.baseUrl}/drive/v3/files?q=${encodeURIComponent(q)}&orderBy=${encodeURIComponent(orderBy)}&pageSize=2`;
105
- const res2 = await fetch(url2, { headers });
106
- const data2 = await res2.json();
107
- expect(data2.files.length).toBe(2);
108
- expect(data2.files[0].id).toBe(file2.id);
109
- expect(data2.files[1].id).toBe(file3.id);
110
114
  }, 60000);
111
115
 
112
116
  it('should find all files where write time was equal to X, sorted by name, with limit', async () => {
117
+ // Create a parent folder
118
+ const parentRes = await fetch(`${config.baseUrl}/drive/v3/files`, {
119
+ method: 'POST',
120
+ headers: { ...headers, 'Content-Type': 'application/json' },
121
+ body: JSON.stringify({
122
+ name: 'ParentFolder_EqualTime_' + randomString(),
123
+ mimeType: 'application/vnd.google-apps.folder'
124
+ })
125
+ });
126
+ expect(parentRes.status).toBe(200);
127
+ const parentId = (await parentRes.json()).id;
128
+
113
129
  // Create 3 files effectively at the "same" time.
114
130
  // To do this reliably on Real API, we create one, get its time, and then PATCH the others to have that same time (if possible).
115
131
  // However, Drive API might not allow arbitrary modifiedTime patching easily without setModifiedDate=true param or similar.
116
132
  // Actually, V3 supports modifying modifiedTime.
117
133
 
118
- const file1 = await createFileWithContent('file_B_middle', randomString(), config);
134
+ const file1 = await createFileWithContent('file_B_middle', randomString(), config, parentId);
119
135
  // Get the time from file1 to use as target
120
136
  const timeXRes = await fetch(`${config.baseUrl}/drive/v3/files/${file1.id}?fields=modifiedTime`, { headers });
121
137
  const timeX = (await timeXRes.json()).modifiedTime;
122
138
 
123
139
  // Create two more files
124
- const file2 = await createFileWithContent('file_A_first', randomString(), config);
125
- const file3 = await createFileWithContent('file_C_last', randomString(), config);
140
+ const file2 = await createFileWithContent('file_A_first', randomString(), config, parentId);
141
+ const file3 = await createFileWithContent('file_C_last', randomString(), config, parentId);
126
142
 
127
143
  // Patch file2 and file3 to have the SAME modifiedTime as file1
128
144
  // We need to wait a bit to ensure they would naturally have different times if we didn't patch,
@@ -133,16 +149,24 @@ describe('Iterate Changes Queries', () => {
133
149
  await fetch(`${config.baseUrl}/drive/v3/files/${file2.id}`, { method: 'PATCH', headers: { ...headers, 'Content-Type': 'application/json' }, body: patchBody });
134
150
  await fetch(`${config.baseUrl}/drive/v3/files/${file3.id}`, { method: 'PATCH', headers: { ...headers, 'Content-Type': 'application/json' }, body: patchBody });
135
151
 
136
- const q = `modifiedTime = '${timeX}' and trashed = false`;
152
+ const q = `modifiedTime = '${timeX}' and '${parentId}' in parents and trashed = false`;
137
153
  const orderBy = 'name asc';
138
154
  const pageSize = 10;
139
155
 
156
+ let data: any;
140
157
  const url = `${config.baseUrl}/drive/v3/files?q=${encodeURIComponent(q)}&orderBy=${encodeURIComponent(orderBy)}&pageSize=${pageSize}&fields=files(id,name,modifiedTime)`;
141
- const res = await fetch(url, { headers });
142
- expect(res.status).toBe(200);
143
- const data = await res.json();
158
+
159
+ for (let attempt = 1; attempt <= 15; attempt++) {
160
+ const res = await fetch(url, { headers });
161
+ expect(res.status).toBe(200);
162
+ data = await res.json();
163
+ const relevantFiles = (data.files || []).filter((f: DriveFile) => [file1.id, file2.id, file3.id].includes(f.id));
164
+ if (relevantFiles.length === 3) {
165
+ break;
166
+ }
167
+ await new Promise(r => setTimeout(r, 2000));
168
+ }
144
169
 
145
- // Should find all 3 files
146
170
  const relevantFiles = data.files.filter((f: DriveFile) => [file1.id, file2.id, file3.id].includes(f.id));
147
171
  expect(relevantFiles.length).toBe(3);
148
172
 
@@ -206,10 +230,19 @@ describe('Iterate Changes Queries', () => {
206
230
  const q = `modifiedTime = '${timeX}' and '${parentId}' in parents and trashed = false`;
207
231
  const orderBy = 'name asc';
208
232
 
233
+ let data: any;
209
234
  const url = `${config.baseUrl}/drive/v3/files?q=${encodeURIComponent(q)}&orderBy=${encodeURIComponent(orderBy)}&fields=files(id,name,modifiedTime,parents)`;
210
- const res = await fetch(url, { headers });
211
- expect(res.status).toBe(200);
212
- const data = await res.json();
235
+
236
+ for (let attempt = 1; attempt <= 15; attempt++) {
237
+ const res = await fetch(url, { headers });
238
+ expect(res.status).toBe(200);
239
+ data = await res.json();
240
+ const ids = (data.files || []).map((f: DriveFile) => f.id);
241
+ if (ids.includes(file1.id) && ids.includes(file2.id) && ids.includes(file3.id)) {
242
+ break;
243
+ }
244
+ await new Promise(r => setTimeout(r, 2000));
245
+ }
213
246
 
214
247
  // 4. Verify results
215
248
  // Should find file1, file2, file3
@@ -229,21 +262,33 @@ describe('Iterate Changes Queries', () => {
229
262
  }, 60000);
230
263
 
231
264
  it('should find files where write time >= X, sorted by modifiedTime and name', async () => {
265
+ // Create a parent folder
266
+ const parentRes = await fetch(`${config.baseUrl}/drive/v3/files`, {
267
+ method: 'POST',
268
+ headers: { ...headers, 'Content-Type': 'application/json' },
269
+ body: JSON.stringify({
270
+ name: 'ParentFolder_GreaterOrEqual_' + randomString(),
271
+ mimeType: 'application/vnd.google-apps.folder'
272
+ })
273
+ });
274
+ expect(parentRes.status).toBe(200);
275
+ const parentId = (await parentRes.json()).id;
276
+
232
277
  // 1. Create file OLD (should be excluded)
233
- const fileOld = await createFileWithContent('file_A_Old', randomString(), config);
278
+ const fileOld = await createFileWithContent('file_A_Old', randomString(), config, parentId);
234
279
 
235
280
  // Wait to ensure distinct time
236
281
  await new Promise(r => setTimeout(r, 1500));
237
282
 
238
283
  // 2. Create ISO-time target files (Equal Time)
239
284
  // We create one, get its time, then create another and patch it to match.
240
- const fileMiddle1 = await createFileWithContent('file_B_Middle1', randomString(), config);
285
+ const fileMiddle1 = await createFileWithContent('file_B_Middle1', randomString(), config, parentId);
241
286
  // Get target time
242
287
  const timeXRes = await fetch(`${config.baseUrl}/drive/v3/files/${fileMiddle1.id}?fields=modifiedTime`, { headers });
243
288
  const timeX = (await timeXRes.json()).modifiedTime;
244
289
 
245
290
  // Create second middle file
246
- const fileMiddle2 = await createFileWithContent('file_B_Middle2', randomString(), config);
291
+ const fileMiddle2 = await createFileWithContent('file_B_Middle2', randomString(), config, parentId);
247
292
 
248
293
  // Patch fileMiddle2 to match fileMiddle1 time
249
294
  await new Promise(r => setTimeout(r, 1100)); // Wait before patching to ensure it would be different otherwise
@@ -254,20 +299,26 @@ describe('Iterate Changes Queries', () => {
254
299
  await new Promise(r => setTimeout(r, 1500));
255
300
 
256
301
  // 3. Create file New (should be included, greater match logic)
257
- const fileNew = await createFileWithContent('file_C_New', randomString(), config);
302
+ const fileNew = await createFileWithContent('file_C_New', randomString(), config, parentId);
258
303
 
259
304
  // 4. Query: modifiedTime >= timeX
260
305
  // Sort by modifiedTime asc, name asc
261
- const q = `modifiedTime >= '${timeX}' and trashed = false`;
306
+ const q = `modifiedTime >= '${timeX}' and '${parentId}' in parents and trashed = false`;
262
307
  const orderBy = 'modifiedTime asc, name asc';
263
308
 
309
+ let data: any;
264
310
  const url = `${config.baseUrl}/drive/v3/files?q=${encodeURIComponent(q)}&orderBy=${encodeURIComponent(orderBy)}&fields=files(id,name,modifiedTime)`;
265
- const res = await fetch(url, { headers });
266
- if (res.status !== 200) {
267
- console.error('Error response:', await res.text());
311
+
312
+ for (let attempt = 1; attempt <= 15; attempt++) {
313
+ const res = await fetch(url, { headers });
314
+ expect(res.status).toBe(200);
315
+ data = await res.json();
316
+ const relevantFiles = (data.files || []).filter((f: DriveFile) => [fileMiddle1.id, fileMiddle2.id, fileNew.id].includes(f.id));
317
+ if (relevantFiles.length === 3) {
318
+ break;
319
+ }
320
+ await new Promise(r => setTimeout(r, 2000));
268
321
  }
269
- expect(res.status).toBe(200);
270
- const data = await res.json();
271
322
 
272
323
  // 5. Verify results
273
324
  const resultIds = data.files.map((f: DriveFile) => f.id);
@@ -576,10 +627,19 @@ describe('Iterate Changes Queries', () => {
576
627
  const q = `modifiedTime > '${timeX}' and '${parentId}' in parents and trashed = false`;
577
628
  const orderBy = 'modifiedTime asc, name asc';
578
629
 
630
+ let data: any;
579
631
  const url = `${config.baseUrl}/drive/v3/files?q=${encodeURIComponent(q)}&orderBy=${encodeURIComponent(orderBy)}&fields=files(id,name,parents,modifiedTime)`;
580
- const res = await fetch(url, { headers });
581
- expect(res.status).toBe(200);
582
- const data = await res.json();
632
+
633
+ for (let attempt = 1; attempt <= 15; attempt++) {
634
+ const res = await fetch(url, { headers });
635
+ expect(res.status).toBe(200);
636
+ data = await res.json();
637
+ const matchingFiles = (data.files || []).filter((f: DriveFile) => f.id === file2.id);
638
+ if (matchingFiles.length === 1) {
639
+ break;
640
+ }
641
+ await new Promise(r => setTimeout(r, 2000));
642
+ }
583
643
 
584
644
  const matchingFiles = data.files.filter((f: DriveFile) => f.id === file2.id);
585
645
  const nonMatchingFile1 = data.files.filter((f: DriveFile) => f.id === file1.id);
@@ -598,22 +658,39 @@ describe('Iterate Changes Queries', () => {
598
658
  }, 60000);
599
659
 
600
660
  it('should paginate through files using nextPageToken', async () => {
661
+ // Create a parent folder
662
+ const parentRes = await fetch(`${config.baseUrl}/drive/v3/files`, {
663
+ method: 'POST',
664
+ headers: { ...headers, 'Content-Type': 'application/json' },
665
+ body: JSON.stringify({
666
+ name: 'ParentFolder_PaginationToken_' + randomString(),
667
+ mimeType: 'application/vnd.google-apps.folder'
668
+ })
669
+ });
670
+ expect(parentRes.status).toBe(200);
671
+ const parentId = (await parentRes.json()).id;
672
+
601
673
  // Create files
602
674
  const totalFiles = 6;
603
675
  const baseName = 'PaginatedFile_' + randomString();
604
676
  for (let i = 0; i < totalFiles; i++) {
605
- await createFileWithContent(`${baseName}_${i}`, `content_${i}`, config);
677
+ await createFileWithContent(`${baseName}_${i}`, `content_${i}`, config, parentId);
606
678
  // Small delay to ensure order if we sort by time, but we'll sort by name to be deterministic
607
679
  }
608
680
 
609
- const q = `name contains '${baseName}' and trashed = false`;
681
+ const q = `'${parentId}' in parents and name contains '${baseName}' and trashed = false`;
610
682
  const orderBy = 'name asc';
611
683
  const pageSize = 2;
612
684
  const collectedFiles: DriveFile[] = [];
613
685
  let pageToken: string | undefined;
686
+ let pageCount = 0;
614
687
 
615
688
  // Iterate pages until no token
616
689
  do {
690
+ pageCount++;
691
+ if (pageCount > 50) {
692
+ throw new Error('Too many pages retrieved (infinite loop safety limit)');
693
+ }
617
694
  const url: string = `${config.baseUrl}/drive/v3/files?q=${encodeURIComponent(q)}&orderBy=${encodeURIComponent(orderBy)}&pageSize=${pageSize}` + (pageToken ? `&pageToken=${pageToken}` : '');
618
695
  const res = await fetch(url, { headers });
619
696
 
@@ -795,17 +872,23 @@ describe('Iterate Changes Queries', () => {
795
872
  }
796
873
  }
797
874
 
798
- // Wait for potential eventual consistency/indexing
799
- console.log('Waiting 5s for indexing...');
800
- await new Promise(r => setTimeout(r, 5000));
801
-
802
- // Get one file to check time
803
- const listOne = await fetch(`${config.baseUrl}/drive/v3/files?q='${parentId}' in parents&pageSize=1&fields=files(modifiedTime)`, { headers });
804
- const oneData = await listOne.json();
805
- if (!oneData.files || oneData.files.length === 0) {
806
- throw new Error('No files found in parent check!');
875
+ // Wait for indexing with retries
876
+ let refTime: string | undefined;
877
+ console.log('Waiting for indexing...');
878
+ for (let attempt = 0; attempt < 15; attempt++) {
879
+ const listOne = await fetch(`${config.baseUrl}/drive/v3/files?q='${parentId}' in parents&pageSize=${totalFiles}&fields=files(modifiedTime)`, { headers });
880
+ if (listOne.status === 200) {
881
+ const oneData = await listOne.json();
882
+ if (oneData.files && oneData.files.length === totalFiles) {
883
+ refTime = oneData.files[0].modifiedTime;
884
+ break;
885
+ }
886
+ }
887
+ await new Promise(r => setTimeout(r, 2000));
888
+ }
889
+ if (!refTime) {
890
+ throw new Error('All files not indexed in parent check!');
807
891
  }
808
- const refTime = oneData.files[0].modifiedTime;
809
892
  console.log('Reference File Time:', refTime);
810
893
 
811
894
  // Filter: modifiedTime > refTime - 1 hour (to safely include all)
@@ -877,21 +960,23 @@ describe('Iterate Changes Queries', () => {
877
960
  expect(res.status).toBe(200);
878
961
  }
879
962
 
880
- console.log('Waiting 5s for indexing (v2)...');
881
- await new Promise(r => setTimeout(r, 5000));
882
-
883
- // Get one file to check time (v2 uses modifiedDate)
884
- // v2 list: items
885
- const listOne = await fetch(`${config.baseUrl}/drive/v2/files?q='${parentId}' in parents&maxResults=1`, { headers });
886
- if (listOne.status !== 200) {
887
- console.error('v2 check failed:', await listOne.text());
963
+ // Wait for indexing with retries (v2)
964
+ let refTime: string | undefined;
965
+ console.log('Waiting for indexing (v2)...');
966
+ for (let attempt = 0; attempt < 15; attempt++) {
967
+ const listOne = await fetch(`${config.baseUrl}/drive/v2/files?q='${parentId}' in parents&maxResults=${totalFiles}`, { headers });
968
+ if (listOne.status === 200) {
969
+ const oneData = await listOne.json();
970
+ if (oneData.items && oneData.items.length === totalFiles) {
971
+ refTime = oneData.items[0].modifiedDate;
972
+ break;
973
+ }
974
+ }
975
+ await new Promise(r => setTimeout(r, 2000));
888
976
  }
889
- const oneData = await listOne.json();
890
-
891
- if (!oneData.items || oneData.items.length === 0) {
892
- throw new Error('No files found in parent check v2!');
977
+ if (!refTime) {
978
+ throw new Error('All files not indexed in parent check v2!');
893
979
  }
894
- const refTime = oneData.items[0].modifiedDate;
895
980
  console.log('Reference File Time v2:', refTime);
896
981
 
897
982
  // Filter: modifiedDate > refTime - 1 hour
@@ -1,4 +1,5 @@
1
- import { describe, it, expect, afterAll, beforeAll } from 'vitest';
1
+ import { describe, expect, afterAll, beforeAll } from 'vitest';
2
+ import { it } from './config';;
2
3
  import { getTestConfig, TestConfig } from './config';
3
4
 
4
5
  // Helper (Shared)
@@ -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
 
4
5
  describe('MIME Type Handling', () => {
@@ -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
 
4
5
  describe('Missing Fields Support (Size & MD5)', () => {
@@ -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('Multipart Upload Behavior (Conflicts, Overwrites, Replacements)', () => {
@@ -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('Parallel Content Update Test', () => {
@@ -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
  import { Server } from 'http';
5
6
 
@@ -1,5 +1,6 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
- import { describe, it, expect, afterAll, beforeAll, vi } from 'vitest';
2
+ import { describe, expect, afterAll, beforeAll, vi } from 'vitest';
3
+ import { it } from './config';;
3
4
  import { waitUntil } from 'async-test-util';
4
5
  import { getTestConfig, TestConfig } from './config';
5
6
 
@@ -68,8 +69,9 @@ describe('Complex Routines', () => {
68
69
 
69
70
  it('Lifecycle: Create -> Update -> Read -> Delete', async () => {
70
71
  // 1. Create
72
+ const title = 'Lifecycle File ' + Math.random().toString(36).substring(7);
71
73
  const newFile = {
72
- name: 'Lifecycle File',
74
+ name: title,
73
75
  mimeType: 'text/plain',
74
76
  parents: [config.testFolderId]
75
77
  };
@@ -78,14 +80,15 @@ describe('Complex Routines', () => {
78
80
  const fileId = createRes.body.id;
79
81
 
80
82
  // 2. Update
81
- const updateRes = await req('PATCH', `/drive/v3/files/${fileId}`, { name: 'Lifecycle Updated' });
83
+ const updatedTitle = 'Lifecycle Updated ' + Math.random().toString(36).substring(7);
84
+ const updateRes = await req('PATCH', `/drive/v3/files/${fileId}`, { name: updatedTitle });
82
85
  expect(updateRes.status).toBe(200);
83
- expect(updateRes.body.name).toBe('Lifecycle Updated');
86
+ expect(updateRes.body.name).toBe(updatedTitle);
84
87
 
85
88
  // 3. Read
86
89
  const readRes = await req('GET', `/drive/v3/files/${fileId}`);
87
90
  expect(readRes.status).toBe(200);
88
- expect(readRes.body.name).toBe('Lifecycle Updated');
91
+ expect(readRes.body.name).toBe(updatedTitle);
89
92
 
90
93
  // 4. Delete
91
94
  const deleteRes = await req('DELETE', `/drive/v3/files/${fileId}`);
@@ -97,7 +100,7 @@ describe('Complex Routines', () => {
97
100
  });
98
101
 
99
102
  it('Transaction Simulation: Lock -> Wait -> Release', async () => {
100
- const LOCK_FILE = 'transactions-lock-' + Date.now() + '.txt';
103
+ const LOCK_FILE = 'transactions-lock-' + Math.random().toString(36).substring(7) + '.txt';
101
104
 
102
105
  // Client A: Acquire Lock
103
106
  const createLock = await req('POST', '/drive/v3/files', {
@@ -113,7 +116,7 @@ describe('Complex Routines', () => {
113
116
  await Promise.all([
114
117
  // Client B
115
118
  waitUntil(async () => {
116
- const check = await req('GET', '/drive/v3/files', null); // removed query q for simplicity logic match
119
+ const check = await req('GET', `/drive/v3/files?q=${encodeURIComponent(`name = '${LOCK_FILE}' and trashed = false`)}`, null);
117
120
  // Actually to filter we can iterate body.files
118
121
 
119
122
  const files = check.body.files || [];
@@ -166,11 +169,11 @@ describe('Complex Routines', () => {
166
169
  });
167
170
 
168
171
  it('Routine: Concurrent Create (Duplicates Allowed)', async () => {
169
- const UNIQUE_FILE = 'unique.txt';
172
+ const UNIQUE_FILE = 'unique-' + Math.random().toString(36).substring(7) + '.txt';
170
173
 
171
174
  // 1. Clean
172
- const check1 = await req('GET', '/drive/v3/files');
173
- const existingFiles = check1.body.files.filter((f: any) => f.name === UNIQUE_FILE);
175
+ const check1 = await req('GET', `/drive/v3/files?q=${encodeURIComponent(`name = '${UNIQUE_FILE}' and trashed = false`)}`);
176
+ const existingFiles = (check1.body.files || []).filter((f: any) => f.name === UNIQUE_FILE);
174
177
  for (const file of existingFiles) {
175
178
  await req('DELETE', `/drive/v3/files/${file.id}`);
176
179
  }
@@ -196,8 +199,8 @@ describe('Complex Routines', () => {
196
199
  expect(statuses).toEqual([200, 200]);
197
200
 
198
201
  // Verify duplicates exist
199
- const check3 = await req('GET', '/drive/v3/files');
200
- const files = check3.body.files.filter((f: any) => f.name === UNIQUE_FILE);
202
+ const check3 = await req('GET', `/drive/v3/files?q=${encodeURIComponent(`name = '${UNIQUE_FILE}' and trashed = false`)}`);
203
+ const files = (check3.body.files || []).filter((f: any) => f.name === UNIQUE_FILE);
201
204
  expect(files.length).toBe(2);
202
205
  });
203
206
  });
@@ -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
 
4
5
  describe('Multipart Upload Feature', () => {
@@ -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('URL Parameters Test', () => {
@@ -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
 
@@ -63,12 +64,13 @@ describe('Google Drive API V2 Basics', () => {
63
64
  }
64
65
 
65
66
  it('should create a file with V2 fields (title vs name)', async () => {
67
+ const title = 'V2 Created File ' + Math.random().toString(36).substring(7);
66
68
  const createRes = await req('POST', '/drive/v2/files', {
67
- title: 'V2 Created File',
69
+ title,
68
70
  mimeType: 'text/plain'
69
71
  });
70
72
  expect(createRes.status).toBe(200);
71
- expect(createRes.body.title).toBe('V2 Created File');
73
+ expect(createRes.body.title).toBe(title);
72
74
  expect(createRes.body.kind).toBe('drive#file');
73
75
  expect(createRes.body.id).toBeTruthy();
74
76
 
@@ -77,38 +79,43 @@ describe('Google Drive API V2 Basics', () => {
77
79
  // Verify with GET
78
80
  const getRes = await req('GET', `/drive/v2/files/${fileId}`);
79
81
  expect(getRes.status).toBe(200);
80
- expect(getRes.body.title).toBe('V2 Created File');
82
+ expect(getRes.body.title).toBe(title);
81
83
  });
82
84
 
83
85
  it('should update a file using PUT', async () => {
84
86
  // Create
85
- const createRes = await req('POST', '/drive/v2/files', { title: 'To Update' });
87
+ const title = 'To Update ' + Math.random().toString(36).substring(7);
88
+ const updatedTitle = 'Updated via PUT ' + Math.random().toString(36).substring(7);
89
+ const createRes = await req('POST', '/drive/v2/files', { title });
86
90
  const fileId = createRes.body.id;
87
91
 
88
92
  // Update
89
93
  const updateRes = await req('PUT', `/drive/v2/files/${fileId}`, {
90
- title: 'Updated via PUT'
94
+ title: updatedTitle
91
95
  });
92
96
  expect(updateRes.status).toBe(200);
93
- expect(updateRes.body.title).toBe('Updated via PUT');
97
+ expect(updateRes.body.title).toBe(updatedTitle);
94
98
  });
95
99
 
96
100
  it('should patch a file using PATCH', async () => {
97
101
  // Create
98
- const createRes = await req('POST', '/drive/v2/files', { title: 'To Patch' });
102
+ const title = 'To Patch ' + Math.random().toString(36).substring(7);
103
+ const patchedTitle = 'Patched Title ' + Math.random().toString(36).substring(7);
104
+ const createRes = await req('POST', '/drive/v2/files', { title });
99
105
  const fileId = createRes.body.id;
100
106
 
101
107
  // Patch
102
108
  const patchRes = await req('PATCH', `/drive/v2/files/${fileId}`, {
103
- title: 'Patched Title'
109
+ title: patchedTitle
104
110
  });
105
111
  expect(patchRes.status).toBe(200);
106
- expect(patchRes.body.title).toBe('Patched Title');
112
+ expect(patchRes.body.title).toBe(patchedTitle);
107
113
  });
108
114
 
109
115
  it('should delete a file', async () => {
110
116
  // Create
111
- const createRes = await req('POST', '/drive/v2/files', { title: 'To Delete' });
117
+ const title = 'To Delete ' + Math.random().toString(36).substring(7);
118
+ const createRes = await req('POST', '/drive/v2/files', { title });
112
119
  const fileId = createRes.body.id;
113
120
 
114
121
  // Delete
@@ -121,14 +128,16 @@ describe('Google Drive API V2 Basics', () => {
121
128
  });
122
129
 
123
130
  it('should handle V2 parent references correctly', async () => {
131
+ const parentTitle = 'V2 Parent Folder ' + Math.random().toString(36).substring(7);
132
+ const childTitle = 'V2 Child File ' + Math.random().toString(36).substring(7);
124
133
  const parentRes = await req('POST', '/drive/v2/files', {
125
- title: 'V2 Parent Folder',
134
+ title: parentTitle,
126
135
  mimeType: 'application/vnd.google-apps.folder'
127
136
  });
128
137
  const parentId = parentRes.body.id;
129
138
 
130
139
  const childRes = await req('POST', '/drive/v2/files', {
131
- title: 'V2 Child File',
140
+ title: childTitle,
132
141
  parents: [{ id: parentId }]
133
142
  });
134
143
 
@@ -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
 
4
5
  describe('V2 Content Updates', () => {