google-drive-mock 1.1.2 → 1.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -5,7 +5,8 @@
5
5
 
6
6
  <p style="text-align: center;">
7
7
  Mock-Server that simulates being google-drive.<br />
8
- Used for testing the <a href="https://rxdb.info/" target="_blank">RxDB Google-Drive-Sync</a>.<br />
8
+ Used for testing the <a href="https://rxdb.info/replication-google-drive.html" target="_blank">RxDB Google-Drive-Sync</a>.<br />
9
+ Sister project to <a href="https://github.com/pubkey/microsoft-onedrive-mock" target="_blank">microsoft-onedrive-mock</a>.<br />
9
10
  Mostly Vibe-Coded.<br />
10
11
  </p>
11
12
 
package/dist/index.js CHANGED
@@ -52,8 +52,17 @@ const createApp = (config = {}) => {
52
52
  }
53
53
  next();
54
54
  }));
55
- app.use(express_1.default.json());
56
- app.use(express_1.default.text({ type: ['multipart/mixed', 'multipart/related', 'text/*', 'application/xml'] }));
55
+ app.use(express_1.default.json({
56
+ verify: (req, res, buf) => {
57
+ req.rawBody = buf;
58
+ }
59
+ }));
60
+ app.use(express_1.default.text({
61
+ type: ['multipart/mixed', 'multipart/related', 'text/*', 'application/xml'],
62
+ verify: (req, res, buf) => {
63
+ req.rawBody = buf;
64
+ }
65
+ }));
57
66
  // Batch Route
58
67
  app.post('/batch', batch_1.handleBatchRequest);
59
68
  app.post('/batch/drive/v3', batch_1.handleBatchRequest);
package/dist/routes/v3.js CHANGED
@@ -248,16 +248,18 @@ const createV3Router = () => {
248
248
  return;
249
249
  }
250
250
  if (uploadType === 'media') {
251
- const rawBody = req.body;
251
+ // Use rawBody if available to preserve exact content (whitespace, etc.)
252
+ const content = req.rawBody !== undefined ? req.rawBody : req.body;
252
253
  // Handle edge case where express.json() parses empty body as {}
253
- if (req.headers['content-length'] === '0' && JSON.stringify(rawBody) === '{}') {
254
+ // If rawBody is used, this check might need adjustment or be irrelevant if rawBody is buffer
255
+ if (req.headers['content-length'] === '0' && JSON.stringify(req.body) === '{}' && !req.rawBody) {
254
256
  // Empty body
255
257
  }
256
258
  const newFile = store_1.driveStore.createFile({
257
259
  name: "Untitled",
258
260
  mimeType: req.headers['content-type'] || "application/octet-stream",
259
261
  parents: [],
260
- content: typeof rawBody === 'string' ? rawBody : JSON.stringify(rawBody) // Handle body if parsed
262
+ content: typeof content === 'string' || Buffer.isBuffer(content) ? content : JSON.stringify(content)
261
263
  });
262
264
  res.status(200).json(newFile);
263
265
  return;
@@ -339,8 +341,10 @@ const createV3Router = () => {
339
341
  if (uploadType === 'media') {
340
342
  const rawBody = req.body;
341
343
  // V3 update content via media upload
344
+ // Use rawBody if available
345
+ const content = req.rawBody !== undefined ? req.rawBody : rawBody;
342
346
  const updatedFile = store_1.driveStore.updateFile(fileId, {
343
- content: rawBody,
347
+ content: typeof content === 'string' || Buffer.isBuffer(content) ? content : JSON.stringify(content),
344
348
  modifiedTime: new Date().toISOString()
345
349
  });
346
350
  res.status(200).json(updatedFile);
@@ -441,7 +445,10 @@ const createV3Router = () => {
441
445
  res.send("");
442
446
  return;
443
447
  }
444
- if (typeof file.content === 'object') {
448
+ if (Buffer.isBuffer(file.content)) {
449
+ res.send(file.content);
450
+ }
451
+ else if (typeof file.content === 'object') {
445
452
  res.json(file.content);
446
453
  }
447
454
  else {
package/dist/store.js CHANGED
@@ -45,6 +45,9 @@ class DriveStore {
45
45
  if (typeof content === 'string') {
46
46
  buffer = Buffer.from(content);
47
47
  }
48
+ else if (Buffer.isBuffer(content)) {
49
+ buffer = content;
50
+ }
48
51
  else if (content === undefined || content === null) {
49
52
  buffer = Buffer.from('');
50
53
  }
package/dist/types.d.ts CHANGED
@@ -3,3 +3,10 @@ export interface AppConfig {
3
3
  apiEndpoint?: string;
4
4
  serverLagAfter?: number;
5
5
  }
6
+ declare global {
7
+ namespace Express {
8
+ interface Request {
9
+ rawBody?: Buffer;
10
+ }
11
+ }
12
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "google-drive-mock",
3
- "version": "1.1.2",
3
+ "version": "1.1.4",
4
4
  "description": "Mock-Server that simulates being google-drive. Used for testing.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/src/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- import express from 'express';
1
+ import express, { Request } from 'express';
2
2
  import cors from 'cors';
3
3
  import { driveStore } from './store';
4
4
  import { handleBatchRequest } from './batch';
@@ -44,8 +44,17 @@ const createApp = (config: AppConfig = {}) => {
44
44
  next();
45
45
  });
46
46
 
47
- app.use(express.json());
48
- app.use(express.text({ type: ['multipart/mixed', 'multipart/related', 'text/*', 'application/xml'] }));
47
+ app.use(express.json({
48
+ verify: (req: Request, res, buf) => {
49
+ req.rawBody = buf;
50
+ }
51
+ }));
52
+ app.use(express.text({
53
+ type: ['multipart/mixed', 'multipart/related', 'text/*', 'application/xml'],
54
+ verify: (req: Request, res, buf) => {
55
+ req.rawBody = buf;
56
+ }
57
+ }));
49
58
 
50
59
  // Batch Route
51
60
  app.post('/batch', handleBatchRequest);
package/src/routes/v3.ts CHANGED
@@ -260,9 +260,12 @@ export const createV3Router = () => {
260
260
  }
261
261
 
262
262
  if (uploadType === 'media') {
263
- const rawBody = req.body;
263
+ // Use rawBody if available to preserve exact content (whitespace, etc.)
264
+ const content = req.rawBody !== undefined ? req.rawBody : req.body;
265
+
264
266
  // Handle edge case where express.json() parses empty body as {}
265
- if (req.headers['content-length'] === '0' && JSON.stringify(rawBody) === '{}') {
267
+ // If rawBody is used, this check might need adjustment or be irrelevant if rawBody is buffer
268
+ if (req.headers['content-length'] === '0' && JSON.stringify(req.body) === '{}' && !req.rawBody) {
266
269
  // Empty body
267
270
  }
268
271
 
@@ -270,7 +273,7 @@ export const createV3Router = () => {
270
273
  name: "Untitled",
271
274
  mimeType: req.headers['content-type'] || "application/octet-stream",
272
275
  parents: [],
273
- content: typeof rawBody === 'string' ? rawBody : JSON.stringify(rawBody) // Handle body if parsed
276
+ content: typeof content === 'string' || Buffer.isBuffer(content) ? content : JSON.stringify(content)
274
277
  });
275
278
  res.status(200).json(newFile);
276
279
  return;
@@ -369,8 +372,11 @@ export const createV3Router = () => {
369
372
  if (uploadType === 'media') {
370
373
  const rawBody = req.body;
371
374
  // V3 update content via media upload
375
+ // Use rawBody if available
376
+ const content = req.rawBody !== undefined ? req.rawBody : rawBody;
377
+
372
378
  const updatedFile = driveStore.updateFile(fileId, {
373
- content: rawBody,
379
+ content: typeof content === 'string' || Buffer.isBuffer(content) ? content : JSON.stringify(content),
374
380
  modifiedTime: new Date().toISOString()
375
381
  });
376
382
  res.status(200).json(updatedFile!);
@@ -497,7 +503,9 @@ export const createV3Router = () => {
497
503
  res.send("");
498
504
  return;
499
505
  }
500
- if (typeof file.content === 'object') {
506
+ if (Buffer.isBuffer(file.content)) {
507
+ res.send(file.content);
508
+ } else if (typeof file.content === 'object') {
501
509
  res.json(file.content);
502
510
  } else {
503
511
  res.send(file.content);
package/src/store.ts CHANGED
@@ -54,6 +54,8 @@ export class DriveStore {
54
54
  let buffer: Buffer;
55
55
  if (typeof content === 'string') {
56
56
  buffer = Buffer.from(content);
57
+ } else if (Buffer.isBuffer(content)) {
58
+ buffer = content;
57
59
  } else if (content === undefined || content === null) {
58
60
  buffer = Buffer.from('');
59
61
  } else {
package/src/types.ts CHANGED
@@ -3,3 +3,12 @@ export interface AppConfig {
3
3
  apiEndpoint?: string;
4
4
  serverLagAfter?: number;
5
5
  }
6
+
7
+ declare global {
8
+ // eslint-disable-next-line @typescript-eslint/no-namespace
9
+ namespace Express {
10
+ interface Request {
11
+ rawBody?: Buffer;
12
+ }
13
+ }
14
+ }
@@ -0,0 +1,100 @@
1
+
2
+ import { describe, it, expect, beforeAll, afterAll } from 'vitest';
3
+ import { getTestConfig, TestConfig } from './config';
4
+ import { Server } from 'http';
5
+
6
+ async function makeRequest(
7
+ target: Server | string,
8
+ method: string,
9
+ path: string,
10
+ headers: Record<string, string>,
11
+ body?: unknown
12
+ ) {
13
+ if (typeof target === 'string') {
14
+ const url = `${target}${path}`;
15
+ const fetchOptions: RequestInit = {
16
+ method: method,
17
+ headers: headers
18
+ };
19
+ if (body) {
20
+ if (typeof body === 'string') {
21
+ fetchOptions.body = body;
22
+ } else {
23
+ fetchOptions.body = JSON.stringify(body);
24
+ if (!headers['Content-Type']) {
25
+ headers['Content-Type'] = 'application/json';
26
+ }
27
+ }
28
+ }
29
+
30
+ const res = await fetch(url, fetchOptions);
31
+
32
+ // Capture headers as an array of [key, value] to preserve order if possible (though Headers object iteration order is not guaranteed to match wire order in all environments, it's worth checking)
33
+ const headerList: [string, string][] = [];
34
+ res.headers.forEach((val, key) => headerList.push([key, val]));
35
+
36
+ const resText = await res.text();
37
+
38
+ return { status: res.status, headers: headerList, body: resText, rawHeaders: res.headers };
39
+ } else {
40
+ const addr = target.address();
41
+ const port = typeof addr === 'object' && addr ? addr.port : 0;
42
+ const baseUrl = `http://localhost:${port}`;
43
+ return makeRequest(baseUrl, method, path, headers, body);
44
+ }
45
+ }
46
+
47
+ describe('Parity: Media Download Order', () => {
48
+ let config: TestConfig;
49
+
50
+ beforeAll(async () => {
51
+ config = await getTestConfig();
52
+ });
53
+
54
+ afterAll(() => {
55
+ if (config) config.stop();
56
+ });
57
+
58
+ async function req(method: string, path: string, body?: unknown, customHeaders: Record<string, string> = {}) {
59
+ const headers = {
60
+ 'Authorization': `Bearer ${config.token}`,
61
+ ...customHeaders
62
+ };
63
+ return makeRequest(config.target, method, path, headers, body);
64
+ }
65
+
66
+
67
+
68
+ it('should return data in the same order and format', async () => {
69
+ // 1. Create a file with specific content (Pretty printed JSON)
70
+ const fileContent = '{\n "b": 1,\n "a": 2\n}';
71
+ const createRes = await req('POST', '/drive/v3/files', {
72
+ name: 'Order Test File',
73
+ mimeType: 'application/json'
74
+ });
75
+ expect(createRes.status).toBe(200);
76
+ const fileId = JSON.parse(createRes.body).id;
77
+
78
+ // Upload content
79
+ const updateRes = await req('PATCH', `/upload/drive/v3/files/${fileId}?uploadType=media`, fileContent, {
80
+ 'Content-Type': 'application/json'
81
+ });
82
+ expect(updateRes.status).toBe(200);
83
+
84
+ // 2. Download with alt=media
85
+ const downloadUrl = `/drive/v3/files/${encodeURIComponent(fileId)}?alt=media&supportsAllDrives=true`;
86
+ const res = await req('GET', downloadUrl);
87
+
88
+ console.log('--- Response Headers ---');
89
+ console.log(JSON.stringify(res.headers, null, 2));
90
+ console.log('--- Response Body ---');
91
+ console.log(res.body);
92
+
93
+ expect(res.status).toBe(200);
94
+ expect(res.body).toBe(fileContent);
95
+
96
+ // Clean up
97
+ await req('DELETE', `/drive/v3/files/${fileId}`);
98
+ });
99
+
100
+ });