@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.
@@ -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
- if (originalExistsSync(testBaseDir)) {
109
- node_fs_1.default.rmSync(testBaseDir, { recursive: true, force: true });
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
- if (originalExistsSync(testBaseDir)) {
115
- node_fs_1.default.rmSync(testBaseDir, { recursive: true, force: true });
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.mockResolvedValue({
256
- ok: true
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.8",
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.14",
37
+ "@quatrain/core": "^1.2.17",
38
38
  "@quatrain/queue": "^1.2.3",
39
- "@quatrain/storage": "^1.2.9",
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",
@@ -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
- if (originalExistsSync(testBaseDir)) {
80
- fs.rmSync(testBaseDir, { recursive: true, force: true })
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
- if (originalExistsSync(testBaseDir)) {
87
- fs.rmSync(testBaseDir, { recursive: true, force: true })
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).mockResolvedValue({
259
- ok: true
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
  })