google-drive-mock 1.1.5 → 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 +59 -10
  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 +65 -11
  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 +175 -74
  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 +60 -18
  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
@@ -7,7 +9,7 @@ import { DriveFile } from '../src/store';
7
9
 
8
10
  const randomString = () => Math.random().toString(36).substring(7);
9
11
 
10
- const createFileWithContent = async (name: string, content: string, config: TestConfig) => {
12
+ const createFileWithContent = async (name: string, content: string, config: TestConfig, parentId?: string) => {
11
13
  const res = await fetch(`${config.baseUrl}/upload/drive/v3/files?uploadType=media`, {
12
14
  method: 'POST',
13
15
  headers: {
@@ -24,7 +26,11 @@ const createFileWithContent = async (name: string, content: string, config: Test
24
26
  // actually, let's just use the patch to set name/metadata to ensure it's correct for the test.
25
27
 
26
28
  // Better: use multipart or just update after create.
27
- await fetch(`${config.baseUrl}/drive/v3/files/${file.id}`, {
29
+ const patchUrl = parentId
30
+ ? `${config.baseUrl}/drive/v3/files/${file.id}?addParents=${parentId}`
31
+ : `${config.baseUrl}/drive/v3/files/${file.id}`;
32
+
33
+ await fetch(patchUrl, {
28
34
  method: 'PATCH',
29
35
  headers: {
30
36
  'Authorization': `Bearer ${config.token}`,
@@ -52,61 +58,87 @@ describe('Iterate Changes Queries', () => {
52
58
  });
53
59
 
54
60
  it('should find files where last write time was greater than X, sorted by modifiedTime and id, with limit', async () => {
61
+ // Create a parent folder
62
+ const parentRes = await fetch(`${config.baseUrl}/drive/v3/files`, {
63
+ method: 'POST',
64
+ headers: { ...headers, 'Content-Type': 'application/json' },
65
+ body: JSON.stringify({
66
+ name: 'ParentFolder_GreaterThan_' + randomString(),
67
+ mimeType: 'application/vnd.google-apps.folder'
68
+ })
69
+ });
70
+ expect(parentRes.status).toBe(200);
71
+ const parentId = (await parentRes.json()).id;
72
+
55
73
  // Create 3 files with slight delays to ensure different modifiedTimes
56
- const file1 = await createFileWithContent('file1', randomString(), config);
74
+ const file1 = await createFileWithContent('file1', randomString(), config, parentId);
57
75
  await new Promise(r => setTimeout(r, 1100)); // Ensure > 1s diff for reliable sorting if seconds resolution
58
- const file2 = await createFileWithContent('file2', randomString(), config);
76
+ const file2 = await createFileWithContent('file2', randomString(), config, parentId);
59
77
  await new Promise(r => setTimeout(r, 1100));
60
- const file3 = await createFileWithContent('file3', randomString(), config);
78
+ const file3 = await createFileWithContent('file3', randomString(), config, parentId);
61
79
 
62
80
  // Use file1's modifiedTime as the baseline (X)
63
81
  const timeX = file1.modifiedTime;
64
82
 
65
- // Query: modifiedTime > X, orderBy modifiedTime asc, name asc (using name as proxy for ID stability in test if needed, but user asked for ID)
66
- // User asked for: Sorted by write data and id. with limit
67
- const q = `modifiedTime > '${timeX}' and trashed = false`;
83
+ // Query: modifiedTime > X, orderBy modifiedTime asc, name asc
84
+ const q = `modifiedTime > '${timeX}' and '${parentId}' in parents and trashed = false`;
68
85
  const orderBy = 'modifiedTime asc, name asc';
69
- 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);
70
104
 
71
105
  // First page
106
+ const pageSize = 1;
72
107
  const url1 = `${config.baseUrl}/drive/v3/files?q=${encodeURIComponent(q)}&orderBy=${encodeURIComponent(orderBy)}&pageSize=${pageSize}`;
73
108
  const res1 = await fetch(url1, { headers });
74
- if (res1.status !== 200) {
75
- const txt = await res1.text();
76
- console.error('Error 1:', txt);
77
- }
78
109
  expect(res1.status).toBe(200);
79
110
  const data1 = await res1.json();
80
111
 
81
112
  expect(data1.files.length).toBe(1);
82
113
  expect(data1.files[0].id).toBe(file2.id);
83
-
84
- // If we want to simulate iteration, we would use nextPageToken or just offset logic if we supported it,
85
- // but here we just test that the query works and LIMIT works.
86
-
87
- // Verify we can get the next one if we increase limit
88
- const url2 = `${config.baseUrl}/drive/v3/files?q=${encodeURIComponent(q)}&orderBy=${encodeURIComponent(orderBy)}&pageSize=2`;
89
- const res2 = await fetch(url2, { headers });
90
- const data2 = await res2.json();
91
- expect(data2.files.length).toBe(2);
92
- expect(data2.files[0].id).toBe(file2.id);
93
- expect(data2.files[1].id).toBe(file3.id);
94
114
  }, 60000);
95
115
 
96
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
+
97
129
  // Create 3 files effectively at the "same" time.
98
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).
99
131
  // However, Drive API might not allow arbitrary modifiedTime patching easily without setModifiedDate=true param or similar.
100
132
  // Actually, V3 supports modifying modifiedTime.
101
133
 
102
- const file1 = await createFileWithContent('file_B_middle', randomString(), config);
134
+ const file1 = await createFileWithContent('file_B_middle', randomString(), config, parentId);
103
135
  // Get the time from file1 to use as target
104
136
  const timeXRes = await fetch(`${config.baseUrl}/drive/v3/files/${file1.id}?fields=modifiedTime`, { headers });
105
137
  const timeX = (await timeXRes.json()).modifiedTime;
106
138
 
107
139
  // Create two more files
108
- const file2 = await createFileWithContent('file_A_first', randomString(), config);
109
- 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);
110
142
 
111
143
  // Patch file2 and file3 to have the SAME modifiedTime as file1
112
144
  // We need to wait a bit to ensure they would naturally have different times if we didn't patch,
@@ -117,16 +149,24 @@ describe('Iterate Changes Queries', () => {
117
149
  await fetch(`${config.baseUrl}/drive/v3/files/${file2.id}`, { method: 'PATCH', headers: { ...headers, 'Content-Type': 'application/json' }, body: patchBody });
118
150
  await fetch(`${config.baseUrl}/drive/v3/files/${file3.id}`, { method: 'PATCH', headers: { ...headers, 'Content-Type': 'application/json' }, body: patchBody });
119
151
 
120
- const q = `modifiedTime = '${timeX}' and trashed = false`;
152
+ const q = `modifiedTime = '${timeX}' and '${parentId}' in parents and trashed = false`;
121
153
  const orderBy = 'name asc';
122
154
  const pageSize = 10;
123
155
 
156
+ let data: any;
124
157
  const url = `${config.baseUrl}/drive/v3/files?q=${encodeURIComponent(q)}&orderBy=${encodeURIComponent(orderBy)}&pageSize=${pageSize}&fields=files(id,name,modifiedTime)`;
125
- const res = await fetch(url, { headers });
126
- expect(res.status).toBe(200);
127
- 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
+ }
128
169
 
129
- // Should find all 3 files
130
170
  const relevantFiles = data.files.filter((f: DriveFile) => [file1.id, file2.id, file3.id].includes(f.id));
131
171
  expect(relevantFiles.length).toBe(3);
132
172
 
@@ -190,10 +230,19 @@ describe('Iterate Changes Queries', () => {
190
230
  const q = `modifiedTime = '${timeX}' and '${parentId}' in parents and trashed = false`;
191
231
  const orderBy = 'name asc';
192
232
 
233
+ let data: any;
193
234
  const url = `${config.baseUrl}/drive/v3/files?q=${encodeURIComponent(q)}&orderBy=${encodeURIComponent(orderBy)}&fields=files(id,name,modifiedTime,parents)`;
194
- const res = await fetch(url, { headers });
195
- expect(res.status).toBe(200);
196
- 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
+ }
197
246
 
198
247
  // 4. Verify results
199
248
  // Should find file1, file2, file3
@@ -213,21 +262,33 @@ describe('Iterate Changes Queries', () => {
213
262
  }, 60000);
214
263
 
215
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
+
216
277
  // 1. Create file OLD (should be excluded)
217
- const fileOld = await createFileWithContent('file_A_Old', randomString(), config);
278
+ const fileOld = await createFileWithContent('file_A_Old', randomString(), config, parentId);
218
279
 
219
280
  // Wait to ensure distinct time
220
281
  await new Promise(r => setTimeout(r, 1500));
221
282
 
222
283
  // 2. Create ISO-time target files (Equal Time)
223
284
  // We create one, get its time, then create another and patch it to match.
224
- const fileMiddle1 = await createFileWithContent('file_B_Middle1', randomString(), config);
285
+ const fileMiddle1 = await createFileWithContent('file_B_Middle1', randomString(), config, parentId);
225
286
  // Get target time
226
287
  const timeXRes = await fetch(`${config.baseUrl}/drive/v3/files/${fileMiddle1.id}?fields=modifiedTime`, { headers });
227
288
  const timeX = (await timeXRes.json()).modifiedTime;
228
289
 
229
290
  // Create second middle file
230
- const fileMiddle2 = await createFileWithContent('file_B_Middle2', randomString(), config);
291
+ const fileMiddle2 = await createFileWithContent('file_B_Middle2', randomString(), config, parentId);
231
292
 
232
293
  // Patch fileMiddle2 to match fileMiddle1 time
233
294
  await new Promise(r => setTimeout(r, 1100)); // Wait before patching to ensure it would be different otherwise
@@ -238,20 +299,26 @@ describe('Iterate Changes Queries', () => {
238
299
  await new Promise(r => setTimeout(r, 1500));
239
300
 
240
301
  // 3. Create file New (should be included, greater match logic)
241
- const fileNew = await createFileWithContent('file_C_New', randomString(), config);
302
+ const fileNew = await createFileWithContent('file_C_New', randomString(), config, parentId);
242
303
 
243
304
  // 4. Query: modifiedTime >= timeX
244
305
  // Sort by modifiedTime asc, name asc
245
- const q = `modifiedTime >= '${timeX}' and trashed = false`;
306
+ const q = `modifiedTime >= '${timeX}' and '${parentId}' in parents and trashed = false`;
246
307
  const orderBy = 'modifiedTime asc, name asc';
247
308
 
309
+ let data: any;
248
310
  const url = `${config.baseUrl}/drive/v3/files?q=${encodeURIComponent(q)}&orderBy=${encodeURIComponent(orderBy)}&fields=files(id,name,modifiedTime)`;
249
- const res = await fetch(url, { headers });
250
- if (res.status !== 200) {
251
- 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));
252
321
  }
253
- expect(res.status).toBe(200);
254
- const data = await res.json();
255
322
 
256
323
  // 5. Verify results
257
324
  const resultIds = data.files.map((f: DriveFile) => f.id);
@@ -560,10 +627,19 @@ describe('Iterate Changes Queries', () => {
560
627
  const q = `modifiedTime > '${timeX}' and '${parentId}' in parents and trashed = false`;
561
628
  const orderBy = 'modifiedTime asc, name asc';
562
629
 
630
+ let data: any;
563
631
  const url = `${config.baseUrl}/drive/v3/files?q=${encodeURIComponent(q)}&orderBy=${encodeURIComponent(orderBy)}&fields=files(id,name,parents,modifiedTime)`;
564
- const res = await fetch(url, { headers });
565
- expect(res.status).toBe(200);
566
- 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
+ }
567
643
 
568
644
  const matchingFiles = data.files.filter((f: DriveFile) => f.id === file2.id);
569
645
  const nonMatchingFile1 = data.files.filter((f: DriveFile) => f.id === file1.id);
@@ -582,22 +658,39 @@ describe('Iterate Changes Queries', () => {
582
658
  }, 60000);
583
659
 
584
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
+
585
673
  // Create files
586
674
  const totalFiles = 6;
587
675
  const baseName = 'PaginatedFile_' + randomString();
588
676
  for (let i = 0; i < totalFiles; i++) {
589
- await createFileWithContent(`${baseName}_${i}`, `content_${i}`, config);
677
+ await createFileWithContent(`${baseName}_${i}`, `content_${i}`, config, parentId);
590
678
  // Small delay to ensure order if we sort by time, but we'll sort by name to be deterministic
591
679
  }
592
680
 
593
- const q = `name contains '${baseName}' and trashed = false`;
681
+ const q = `'${parentId}' in parents and name contains '${baseName}' and trashed = false`;
594
682
  const orderBy = 'name asc';
595
683
  const pageSize = 2;
596
684
  const collectedFiles: DriveFile[] = [];
597
685
  let pageToken: string | undefined;
686
+ let pageCount = 0;
598
687
 
599
688
  // Iterate pages until no token
600
689
  do {
690
+ pageCount++;
691
+ if (pageCount > 50) {
692
+ throw new Error('Too many pages retrieved (infinite loop safety limit)');
693
+ }
601
694
  const url: string = `${config.baseUrl}/drive/v3/files?q=${encodeURIComponent(q)}&orderBy=${encodeURIComponent(orderBy)}&pageSize=${pageSize}` + (pageToken ? `&pageToken=${pageToken}` : '');
602
695
  const res = await fetch(url, { headers });
603
696
 
@@ -779,17 +872,23 @@ describe('Iterate Changes Queries', () => {
779
872
  }
780
873
  }
781
874
 
782
- // Wait for potential eventual consistency/indexing
783
- console.log('Waiting 5s for indexing...');
784
- await new Promise(r => setTimeout(r, 5000));
785
-
786
- // Get one file to check time
787
- const listOne = await fetch(`${config.baseUrl}/drive/v3/files?q='${parentId}' in parents&pageSize=1&fields=files(modifiedTime)`, { headers });
788
- const oneData = await listOne.json();
789
- if (!oneData.files || oneData.files.length === 0) {
790
- 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!');
791
891
  }
792
- const refTime = oneData.files[0].modifiedTime;
793
892
  console.log('Reference File Time:', refTime);
794
893
 
795
894
  // Filter: modifiedTime > refTime - 1 hour (to safely include all)
@@ -861,21 +960,23 @@ describe('Iterate Changes Queries', () => {
861
960
  expect(res.status).toBe(200);
862
961
  }
863
962
 
864
- console.log('Waiting 5s for indexing (v2)...');
865
- await new Promise(r => setTimeout(r, 5000));
866
-
867
- // Get one file to check time (v2 uses modifiedDate)
868
- // v2 list: items
869
- const listOne = await fetch(`${config.baseUrl}/drive/v2/files?q='${parentId}' in parents&maxResults=1`, { headers });
870
- if (listOne.status !== 200) {
871
- 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));
872
976
  }
873
- const oneData = await listOne.json();
874
-
875
- if (!oneData.items || oneData.items.length === 0) {
876
- 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!');
877
979
  }
878
- const refTime = oneData.items[0].modifiedDate;
879
980
  console.log('Reference File Time v2:', refTime);
880
981
 
881
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