@quatrain/worker 1.2.8 → 1.2.10
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/dist/FileSystem.test.js +70 -10
- package/package.json +3 -3
- package/src/FileSystem.test.ts +80 -12
package/dist/FileSystem.test.js
CHANGED
|
@@ -44,7 +44,14 @@ const node_fetch_native_1 = __importDefault(require("node-fetch-native"));
|
|
|
44
44
|
const ffmpeg = __importStar(require("fluent-ffmpeg"));
|
|
45
45
|
const node_stream_1 = require("node:stream");
|
|
46
46
|
jest.mock('axios');
|
|
47
|
-
jest.mock('node-fetch-native')
|
|
47
|
+
jest.mock('node-fetch-native', () => {
|
|
48
|
+
const fn = jest.fn();
|
|
49
|
+
return {
|
|
50
|
+
__esModule: true,
|
|
51
|
+
default: fn,
|
|
52
|
+
fetch: fn,
|
|
53
|
+
};
|
|
54
|
+
});
|
|
48
55
|
jest.mock('fluent-ffmpeg', () => ({
|
|
49
56
|
ffprobe: jest.fn()
|
|
50
57
|
}));
|
|
@@ -88,32 +95,38 @@ describe('FileSystem Utilities (Worker)', () => {
|
|
|
88
95
|
return node_stream_1.Readable.from(Buffer.alloc(35 * 1024));
|
|
89
96
|
});
|
|
90
97
|
jest.spyOn(node_fs_1.default, 'existsSync').mockImplementation((p) => {
|
|
91
|
-
if (typeof p === 'string' && (p.endsWith('test.txt') || p.endsWith('small.mp4') || p.endsWith('large.mp4') || p.endsWith('large-fail.mp4'))) {
|
|
98
|
+
if (typeof p === 'string' && (p.endsWith('test.txt') || p.endsWith('small.mp4') || p.endsWith('large.mp4') || p.endsWith('large-fail.mp4') || p.endsWith('small-fail.mp4') || p.endsWith('large-sync-fail.mp4'))) {
|
|
92
99
|
return true;
|
|
93
100
|
}
|
|
94
101
|
return originalExistsSync(p);
|
|
95
102
|
});
|
|
96
103
|
jest.spyOn(node_fs_1.default, 'statSync').mockImplementation((p) => {
|
|
97
104
|
if (typeof p === 'string') {
|
|
98
|
-
if (p.endsWith('small.mp4')) {
|
|
105
|
+
if (p.endsWith('small.mp4') || p.endsWith('small-fail.mp4')) {
|
|
99
106
|
return { size: 13 };
|
|
100
107
|
}
|
|
101
|
-
if (p.endsWith('large.mp4') || p.endsWith('large-fail.mp4')) {
|
|
108
|
+
if (p.endsWith('large.mp4') || p.endsWith('large-fail.mp4') || p.endsWith('large-sync-fail.mp4')) {
|
|
102
109
|
return { size: 35 * 1024 };
|
|
103
110
|
}
|
|
104
111
|
}
|
|
105
112
|
return (jest.requireActual('node:fs').statSync)(p);
|
|
106
113
|
});
|
|
107
114
|
// Setup base temp folder if needed
|
|
108
|
-
|
|
109
|
-
|
|
115
|
+
try {
|
|
116
|
+
if (originalExistsSync(testBaseDir)) {
|
|
117
|
+
node_fs_1.default.rmSync(testBaseDir, { recursive: true, force: true });
|
|
118
|
+
}
|
|
110
119
|
}
|
|
120
|
+
catch { }
|
|
111
121
|
});
|
|
112
122
|
afterEach(() => {
|
|
113
123
|
// Clean up base temp folder
|
|
114
|
-
|
|
115
|
-
|
|
124
|
+
try {
|
|
125
|
+
if (originalExistsSync(testBaseDir)) {
|
|
126
|
+
node_fs_1.default.rmSync(testBaseDir, { recursive: true, force: true });
|
|
127
|
+
}
|
|
116
128
|
}
|
|
129
|
+
catch { }
|
|
117
130
|
jest.clearAllMocks();
|
|
118
131
|
jest.restoreAllMocks();
|
|
119
132
|
});
|
|
@@ -252,8 +265,18 @@ describe('FileSystem Utilities (Worker)', () => {
|
|
|
252
265
|
ffmpeg.ffprobe.mockImplementation((file, callback) => {
|
|
253
266
|
callback(null, mockMetadata);
|
|
254
267
|
});
|
|
255
|
-
node_fetch_native_1.default.
|
|
256
|
-
|
|
268
|
+
node_fetch_native_1.default.mockImplementation((url, options) => {
|
|
269
|
+
return new Promise((resolve) => {
|
|
270
|
+
if (options.body && typeof options.body.on === 'function') {
|
|
271
|
+
options.body.on('data', () => { });
|
|
272
|
+
options.body.on('end', () => {
|
|
273
|
+
resolve({ ok: true });
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
else {
|
|
277
|
+
resolve({ ok: true });
|
|
278
|
+
}
|
|
279
|
+
});
|
|
257
280
|
});
|
|
258
281
|
const meta = {
|
|
259
282
|
bucket: 'mock-bucket',
|
|
@@ -281,5 +304,42 @@ describe('FileSystem Utilities (Worker)', () => {
|
|
|
281
304
|
};
|
|
282
305
|
await expect(FileSystem_1.FileSystem.uploadFile(testFile, meta)).rejects.toThrow('Connection abort');
|
|
283
306
|
});
|
|
307
|
+
it('should reject small uploads if HTTP request fails', async () => {
|
|
308
|
+
const testFile = node_path_1.default.join(testBaseDir, 'small-fail.mp4');
|
|
309
|
+
const mockMetadata = {
|
|
310
|
+
streams: [{ width: 320, height: 240, duration: '1', bit_rate: '100', nb_frames: '10' }]
|
|
311
|
+
};
|
|
312
|
+
ffmpeg.ffprobe.mockImplementation((file, callback) => {
|
|
313
|
+
callback(null, mockMetadata);
|
|
314
|
+
});
|
|
315
|
+
node_fetch_native_1.default.mockRejectedValue(new Error('Upload failed'));
|
|
316
|
+
// Stub readFileSync for small content read
|
|
317
|
+
jest.spyOn(node_fs_1.default, 'readFileSync').mockReturnValue(Buffer.from('small content'));
|
|
318
|
+
const meta = {
|
|
319
|
+
bucket: 'mock-bucket',
|
|
320
|
+
ref: 'remote/small-fail.mp4',
|
|
321
|
+
uploadUrl: 'https://upload.com/small-fail.mp4'
|
|
322
|
+
};
|
|
323
|
+
await expect(FileSystem_1.FileSystem.uploadFile(testFile, meta)).rejects.toThrow('Upload failed');
|
|
324
|
+
});
|
|
325
|
+
it('should handle synchronous errors inside stream initialization', async () => {
|
|
326
|
+
const testFile = node_path_1.default.join(testBaseDir, 'large-sync-fail.mp4');
|
|
327
|
+
const mockMetadata = {
|
|
328
|
+
streams: [{ width: 1280, height: 720, duration: '5', bit_rate: '2000', nb_frames: '150' }]
|
|
329
|
+
};
|
|
330
|
+
ffmpeg.ffprobe.mockImplementation((file, callback) => {
|
|
331
|
+
callback(null, mockMetadata);
|
|
332
|
+
});
|
|
333
|
+
// Make createReadStream throw a synchronous error
|
|
334
|
+
jest.spyOn(node_fs_1.default, 'createReadStream').mockImplementationOnce(() => {
|
|
335
|
+
throw new Error('Sync stream error');
|
|
336
|
+
});
|
|
337
|
+
const meta = {
|
|
338
|
+
bucket: 'mock-bucket',
|
|
339
|
+
ref: 'remote/large-sync-fail.mp4',
|
|
340
|
+
uploadUrl: 'https://upload.com/large-sync-fail.mp4'
|
|
341
|
+
};
|
|
342
|
+
await expect(FileSystem_1.FileSystem.uploadFile(testFile, meta)).rejects.toThrow('Sync stream error');
|
|
343
|
+
});
|
|
284
344
|
});
|
|
285
345
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quatrain/worker",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.10",
|
|
4
4
|
"description": "Container Worker helpers",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -34,9 +34,9 @@
|
|
|
34
34
|
"typescript": "^5.1.5"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@quatrain/core": "^1.2.
|
|
37
|
+
"@quatrain/core": "^1.2.17",
|
|
38
38
|
"@quatrain/queue": "^1.2.3",
|
|
39
|
-
"@quatrain/storage": "^1.2.
|
|
39
|
+
"@quatrain/storage": "^1.2.13",
|
|
40
40
|
"axios": "^1.7.7",
|
|
41
41
|
"fluent-ffmpeg": "^2.1.2",
|
|
42
42
|
"fs-extra": "^11.2.0",
|
package/src/FileSystem.test.ts
CHANGED
|
@@ -7,7 +7,14 @@ import * as ffmpeg from 'fluent-ffmpeg'
|
|
|
7
7
|
import { PassThrough, Readable } from 'node:stream'
|
|
8
8
|
|
|
9
9
|
jest.mock('axios')
|
|
10
|
-
jest.mock('node-fetch-native')
|
|
10
|
+
jest.mock('node-fetch-native', () => {
|
|
11
|
+
const fn = jest.fn()
|
|
12
|
+
return {
|
|
13
|
+
__esModule: true,
|
|
14
|
+
default: fn,
|
|
15
|
+
fetch: fn,
|
|
16
|
+
}
|
|
17
|
+
})
|
|
11
18
|
jest.mock('fluent-ffmpeg', () => ({
|
|
12
19
|
ffprobe: jest.fn()
|
|
13
20
|
}))
|
|
@@ -57,7 +64,7 @@ describe('FileSystem Utilities (Worker)', () => {
|
|
|
57
64
|
})
|
|
58
65
|
|
|
59
66
|
jest.spyOn(fs, 'existsSync').mockImplementation((p) => {
|
|
60
|
-
if (typeof p === 'string' && (p.endsWith('test.txt') || p.endsWith('small.mp4') || p.endsWith('large.mp4') || p.endsWith('large-fail.mp4'))) {
|
|
67
|
+
if (typeof p === 'string' && (p.endsWith('test.txt') || p.endsWith('small.mp4') || p.endsWith('large.mp4') || p.endsWith('large-fail.mp4') || p.endsWith('small-fail.mp4') || p.endsWith('large-sync-fail.mp4'))) {
|
|
61
68
|
return true
|
|
62
69
|
}
|
|
63
70
|
return originalExistsSync(p)
|
|
@@ -65,10 +72,10 @@ describe('FileSystem Utilities (Worker)', () => {
|
|
|
65
72
|
|
|
66
73
|
jest.spyOn(fs, 'statSync').mockImplementation((p) => {
|
|
67
74
|
if (typeof p === 'string') {
|
|
68
|
-
if (p.endsWith('small.mp4')) {
|
|
75
|
+
if (p.endsWith('small.mp4') || p.endsWith('small-fail.mp4')) {
|
|
69
76
|
return { size: 13 } as any
|
|
70
77
|
}
|
|
71
|
-
if (p.endsWith('large.mp4') || p.endsWith('large-fail.mp4')) {
|
|
78
|
+
if (p.endsWith('large.mp4') || p.endsWith('large-fail.mp4') || p.endsWith('large-sync-fail.mp4')) {
|
|
72
79
|
return { size: 35 * 1024 } as any
|
|
73
80
|
}
|
|
74
81
|
}
|
|
@@ -76,16 +83,20 @@ describe('FileSystem Utilities (Worker)', () => {
|
|
|
76
83
|
})
|
|
77
84
|
|
|
78
85
|
// Setup base temp folder if needed
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
86
|
+
try {
|
|
87
|
+
if (originalExistsSync(testBaseDir)) {
|
|
88
|
+
fs.rmSync(testBaseDir, { recursive: true, force: true })
|
|
89
|
+
}
|
|
90
|
+
} catch {}
|
|
82
91
|
})
|
|
83
92
|
|
|
84
93
|
afterEach(() => {
|
|
85
94
|
// Clean up base temp folder
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
95
|
+
try {
|
|
96
|
+
if (originalExistsSync(testBaseDir)) {
|
|
97
|
+
fs.rmSync(testBaseDir, { recursive: true, force: true })
|
|
98
|
+
}
|
|
99
|
+
} catch {}
|
|
89
100
|
jest.clearAllMocks()
|
|
90
101
|
jest.restoreAllMocks()
|
|
91
102
|
})
|
|
@@ -255,8 +266,17 @@ describe('FileSystem Utilities (Worker)', () => {
|
|
|
255
266
|
callback(null, mockMetadata)
|
|
256
267
|
});
|
|
257
268
|
|
|
258
|
-
(fetch as any).
|
|
259
|
-
|
|
269
|
+
(fetch as any).mockImplementation((url: string, options: any) => {
|
|
270
|
+
return new Promise((resolve) => {
|
|
271
|
+
if (options.body && typeof options.body.on === 'function') {
|
|
272
|
+
options.body.on('data', () => {})
|
|
273
|
+
options.body.on('end', () => {
|
|
274
|
+
resolve({ ok: true })
|
|
275
|
+
})
|
|
276
|
+
} else {
|
|
277
|
+
resolve({ ok: true })
|
|
278
|
+
}
|
|
279
|
+
})
|
|
260
280
|
})
|
|
261
281
|
|
|
262
282
|
const meta = {
|
|
@@ -291,5 +311,53 @@ describe('FileSystem Utilities (Worker)', () => {
|
|
|
291
311
|
|
|
292
312
|
await expect(FileSystem.uploadFile(testFile, meta)).rejects.toThrow('Connection abort')
|
|
293
313
|
})
|
|
314
|
+
|
|
315
|
+
it('should reject small uploads if HTTP request fails', async () => {
|
|
316
|
+
const testFile = path.join(testBaseDir, 'small-fail.mp4')
|
|
317
|
+
|
|
318
|
+
const mockMetadata = {
|
|
319
|
+
streams: [{ width: 320, height: 240, duration: '1', bit_rate: '100', nb_frames: '10' }]
|
|
320
|
+
};
|
|
321
|
+
(ffmpeg.ffprobe as any).mockImplementation((file: string, callback: any) => {
|
|
322
|
+
callback(null, mockMetadata)
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
(fetch as any).mockRejectedValue(new Error('Upload failed'))
|
|
326
|
+
|
|
327
|
+
// Stub readFileSync for small content read
|
|
328
|
+
jest.spyOn(fs, 'readFileSync').mockReturnValue(Buffer.from('small content'))
|
|
329
|
+
|
|
330
|
+
const meta = {
|
|
331
|
+
bucket: 'mock-bucket',
|
|
332
|
+
ref: 'remote/small-fail.mp4',
|
|
333
|
+
uploadUrl: 'https://upload.com/small-fail.mp4'
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
await expect(FileSystem.uploadFile(testFile, meta)).rejects.toThrow('Upload failed')
|
|
337
|
+
})
|
|
338
|
+
|
|
339
|
+
it('should handle synchronous errors inside stream initialization', async () => {
|
|
340
|
+
const testFile = path.join(testBaseDir, 'large-sync-fail.mp4')
|
|
341
|
+
|
|
342
|
+
const mockMetadata = {
|
|
343
|
+
streams: [{ width: 1280, height: 720, duration: '5', bit_rate: '2000', nb_frames: '150' }]
|
|
344
|
+
};
|
|
345
|
+
(ffmpeg.ffprobe as any).mockImplementation((file: string, callback: any) => {
|
|
346
|
+
callback(null, mockMetadata)
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
// Make createReadStream throw a synchronous error
|
|
350
|
+
jest.spyOn(fs, 'createReadStream').mockImplementationOnce(() => {
|
|
351
|
+
throw new Error('Sync stream error')
|
|
352
|
+
})
|
|
353
|
+
|
|
354
|
+
const meta = {
|
|
355
|
+
bucket: 'mock-bucket',
|
|
356
|
+
ref: 'remote/large-sync-fail.mp4',
|
|
357
|
+
uploadUrl: 'https://upload.com/large-sync-fail.mp4'
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
await expect(FileSystem.uploadFile(testFile, meta)).rejects.toThrow('Sync stream error')
|
|
361
|
+
})
|
|
294
362
|
})
|
|
295
363
|
})
|