orator-conversion 1.0.2 → 1.0.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/debug/Harness.js +86 -1
- package/package.json +8 -6
- package/source/Conversion-Core.js +939 -0
- package/source/Orator-Conversion-BeaconProvider.js +852 -0
- package/source/Orator-File-Translation.js +154 -0
- package/source/endpoints/Endpoint-Image-Convert.js +33 -0
- package/source/endpoints/Endpoint-Image-Resize.js +49 -0
- package/source/endpoints/Endpoint-Image-Rotate.js +34 -0
- package/test/Conversion-Core_tests.js +442 -0
- package/test/Orator-File-Translation_basic_tests.js +10 -4
- package/test/Orator-File-Translation_tests.js +148 -0
|
@@ -0,0 +1,442 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for Conversion-Core — Image, Video, Audio operations
|
|
3
|
+
*
|
|
4
|
+
* @license MIT
|
|
5
|
+
*
|
|
6
|
+
* @author Steven Velozo <steven@velozo.com>
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const Chai = require("chai");
|
|
10
|
+
const Expect = Chai.expect;
|
|
11
|
+
|
|
12
|
+
const libSharp = require('sharp');
|
|
13
|
+
|
|
14
|
+
const libConversionCore = require('../source/Conversion-Core.js');
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Generate a minimal JPEG test image buffer (10x20 red).
|
|
18
|
+
*/
|
|
19
|
+
function createTestJpegBuffer(fCallback)
|
|
20
|
+
{
|
|
21
|
+
libSharp(
|
|
22
|
+
{
|
|
23
|
+
create:
|
|
24
|
+
{
|
|
25
|
+
width: 10,
|
|
26
|
+
height: 20,
|
|
27
|
+
channels: 3,
|
|
28
|
+
background: { r: 255, g: 0, b: 0 }
|
|
29
|
+
}
|
|
30
|
+
}).jpeg().toBuffer().then(
|
|
31
|
+
(pBuffer) => { return fCallback(null, pBuffer); },
|
|
32
|
+
(pError) => { return fCallback(pError); });
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Generate a minimal PNG test image buffer.
|
|
37
|
+
*/
|
|
38
|
+
function createTestPngBuffer(fCallback)
|
|
39
|
+
{
|
|
40
|
+
libSharp(
|
|
41
|
+
{
|
|
42
|
+
create:
|
|
43
|
+
{
|
|
44
|
+
width: 10,
|
|
45
|
+
height: 20,
|
|
46
|
+
channels: 4,
|
|
47
|
+
background: { r: 0, g: 0, b: 255, alpha: 1 }
|
|
48
|
+
}
|
|
49
|
+
}).png().toBuffer().then(
|
|
50
|
+
(pBuffer) => { return fCallback(null, pBuffer); },
|
|
51
|
+
(pError) => { return fCallback(pError); });
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
suite
|
|
55
|
+
(
|
|
56
|
+
'Conversion Core',
|
|
57
|
+
() =>
|
|
58
|
+
{
|
|
59
|
+
suite
|
|
60
|
+
(
|
|
61
|
+
'Image Rotate',
|
|
62
|
+
() =>
|
|
63
|
+
{
|
|
64
|
+
test
|
|
65
|
+
(
|
|
66
|
+
'should rotate an image 90 degrees and swap dimensions',
|
|
67
|
+
(fDone) =>
|
|
68
|
+
{
|
|
69
|
+
createTestJpegBuffer(
|
|
70
|
+
(pError, pJpegBuffer) =>
|
|
71
|
+
{
|
|
72
|
+
Expect(pError).to.equal(null);
|
|
73
|
+
|
|
74
|
+
let tmpCore = new libConversionCore();
|
|
75
|
+
|
|
76
|
+
tmpCore.imageRotate(pJpegBuffer,
|
|
77
|
+
{ Angle: 90 },
|
|
78
|
+
(pRotateError, pOutputBuffer, pContentType) =>
|
|
79
|
+
{
|
|
80
|
+
Expect(pRotateError).to.equal(null);
|
|
81
|
+
Expect(pOutputBuffer).to.be.an.instanceOf(Buffer);
|
|
82
|
+
Expect(pOutputBuffer.length).to.be.greaterThan(0);
|
|
83
|
+
Expect(pContentType).to.equal('image/jpeg');
|
|
84
|
+
|
|
85
|
+
// Verify dimensions are swapped (10x20 → 20x10)
|
|
86
|
+
libSharp(pOutputBuffer).metadata().then(
|
|
87
|
+
(pMetadata) =>
|
|
88
|
+
{
|
|
89
|
+
Expect(pMetadata.width).to.equal(20);
|
|
90
|
+
Expect(pMetadata.height).to.equal(10);
|
|
91
|
+
return fDone();
|
|
92
|
+
}).catch(
|
|
93
|
+
(pMetaError) => { return fDone(pMetaError); });
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
test
|
|
100
|
+
(
|
|
101
|
+
'should flip an image without error',
|
|
102
|
+
(fDone) =>
|
|
103
|
+
{
|
|
104
|
+
createTestJpegBuffer(
|
|
105
|
+
(pError, pJpegBuffer) =>
|
|
106
|
+
{
|
|
107
|
+
Expect(pError).to.equal(null);
|
|
108
|
+
|
|
109
|
+
let tmpCore = new libConversionCore();
|
|
110
|
+
|
|
111
|
+
tmpCore.imageRotate(pJpegBuffer,
|
|
112
|
+
{ Flip: true },
|
|
113
|
+
(pRotateError, pOutputBuffer, pContentType) =>
|
|
114
|
+
{
|
|
115
|
+
Expect(pRotateError).to.equal(null);
|
|
116
|
+
Expect(pOutputBuffer).to.be.an.instanceOf(Buffer);
|
|
117
|
+
Expect(pOutputBuffer.length).to.be.greaterThan(0);
|
|
118
|
+
return fDone();
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
test
|
|
125
|
+
(
|
|
126
|
+
'should flop an image without error',
|
|
127
|
+
(fDone) =>
|
|
128
|
+
{
|
|
129
|
+
createTestJpegBuffer(
|
|
130
|
+
(pError, pJpegBuffer) =>
|
|
131
|
+
{
|
|
132
|
+
Expect(pError).to.equal(null);
|
|
133
|
+
|
|
134
|
+
let tmpCore = new libConversionCore();
|
|
135
|
+
|
|
136
|
+
tmpCore.imageRotate(pJpegBuffer,
|
|
137
|
+
{ Flop: true },
|
|
138
|
+
(pRotateError, pOutputBuffer, pContentType) =>
|
|
139
|
+
{
|
|
140
|
+
Expect(pRotateError).to.equal(null);
|
|
141
|
+
Expect(pOutputBuffer).to.be.an.instanceOf(Buffer);
|
|
142
|
+
Expect(pOutputBuffer.length).to.be.greaterThan(0);
|
|
143
|
+
return fDone();
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
suite
|
|
152
|
+
(
|
|
153
|
+
'Image Convert',
|
|
154
|
+
() =>
|
|
155
|
+
{
|
|
156
|
+
test
|
|
157
|
+
(
|
|
158
|
+
'should convert JPEG to PNG',
|
|
159
|
+
(fDone) =>
|
|
160
|
+
{
|
|
161
|
+
createTestJpegBuffer(
|
|
162
|
+
(pError, pJpegBuffer) =>
|
|
163
|
+
{
|
|
164
|
+
Expect(pError).to.equal(null);
|
|
165
|
+
|
|
166
|
+
let tmpCore = new libConversionCore();
|
|
167
|
+
|
|
168
|
+
tmpCore.imageConvert(pJpegBuffer,
|
|
169
|
+
{ Format: 'png' },
|
|
170
|
+
(pConvertError, pOutputBuffer, pContentType) =>
|
|
171
|
+
{
|
|
172
|
+
Expect(pConvertError).to.equal(null);
|
|
173
|
+
Expect(pOutputBuffer).to.be.an.instanceOf(Buffer);
|
|
174
|
+
Expect(pContentType).to.equal('image/png');
|
|
175
|
+
|
|
176
|
+
// Verify PNG magic bytes
|
|
177
|
+
Expect(pOutputBuffer[0]).to.equal(137);
|
|
178
|
+
Expect(pOutputBuffer[1]).to.equal(80);
|
|
179
|
+
Expect(pOutputBuffer[2]).to.equal(78);
|
|
180
|
+
Expect(pOutputBuffer[3]).to.equal(71);
|
|
181
|
+
|
|
182
|
+
return fDone();
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
test
|
|
189
|
+
(
|
|
190
|
+
'should convert PNG to WebP',
|
|
191
|
+
(fDone) =>
|
|
192
|
+
{
|
|
193
|
+
createTestPngBuffer(
|
|
194
|
+
(pError, pPngBuffer) =>
|
|
195
|
+
{
|
|
196
|
+
Expect(pError).to.equal(null);
|
|
197
|
+
|
|
198
|
+
let tmpCore = new libConversionCore();
|
|
199
|
+
|
|
200
|
+
tmpCore.imageConvert(pPngBuffer,
|
|
201
|
+
{ Format: 'webp' },
|
|
202
|
+
(pConvertError, pOutputBuffer, pContentType) =>
|
|
203
|
+
{
|
|
204
|
+
Expect(pConvertError).to.equal(null);
|
|
205
|
+
Expect(pOutputBuffer).to.be.an.instanceOf(Buffer);
|
|
206
|
+
Expect(pContentType).to.equal('image/webp');
|
|
207
|
+
Expect(pOutputBuffer.length).to.be.greaterThan(0);
|
|
208
|
+
|
|
209
|
+
return fDone();
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
test
|
|
216
|
+
(
|
|
217
|
+
'should convert PNG to JPEG with quality option',
|
|
218
|
+
(fDone) =>
|
|
219
|
+
{
|
|
220
|
+
createTestPngBuffer(
|
|
221
|
+
(pError, pPngBuffer) =>
|
|
222
|
+
{
|
|
223
|
+
Expect(pError).to.equal(null);
|
|
224
|
+
|
|
225
|
+
let tmpCore = new libConversionCore();
|
|
226
|
+
|
|
227
|
+
tmpCore.imageConvert(pPngBuffer,
|
|
228
|
+
{ Format: 'jpeg', Quality: 50 },
|
|
229
|
+
(pConvertError, pOutputBuffer, pContentType) =>
|
|
230
|
+
{
|
|
231
|
+
Expect(pConvertError).to.equal(null);
|
|
232
|
+
Expect(pOutputBuffer).to.be.an.instanceOf(Buffer);
|
|
233
|
+
Expect(pContentType).to.equal('image/jpeg');
|
|
234
|
+
|
|
235
|
+
// Verify JPEG magic bytes
|
|
236
|
+
Expect(pOutputBuffer[0]).to.equal(0xFF);
|
|
237
|
+
Expect(pOutputBuffer[1]).to.equal(0xD8);
|
|
238
|
+
Expect(pOutputBuffer[2]).to.equal(0xFF);
|
|
239
|
+
|
|
240
|
+
return fDone();
|
|
241
|
+
});
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
);
|
|
247
|
+
|
|
248
|
+
suite
|
|
249
|
+
(
|
|
250
|
+
'Enhanced Image Resize',
|
|
251
|
+
() =>
|
|
252
|
+
{
|
|
253
|
+
test
|
|
254
|
+
(
|
|
255
|
+
'should resize with contain fit mode',
|
|
256
|
+
(fDone) =>
|
|
257
|
+
{
|
|
258
|
+
createTestJpegBuffer(
|
|
259
|
+
(pError, pJpegBuffer) =>
|
|
260
|
+
{
|
|
261
|
+
Expect(pError).to.equal(null);
|
|
262
|
+
|
|
263
|
+
let tmpCore = new libConversionCore();
|
|
264
|
+
|
|
265
|
+
// 10x20 image, resize to fit within 5x5 with "inside" mode
|
|
266
|
+
tmpCore.imageResize(pJpegBuffer,
|
|
267
|
+
{ Width: 5, Height: 5, Fit: 'inside' },
|
|
268
|
+
(pResizeError, pOutputBuffer, pContentType) =>
|
|
269
|
+
{
|
|
270
|
+
Expect(pResizeError).to.equal(null);
|
|
271
|
+
Expect(pOutputBuffer).to.be.an.instanceOf(Buffer);
|
|
272
|
+
|
|
273
|
+
libSharp(pOutputBuffer).metadata().then(
|
|
274
|
+
(pMetadata) =>
|
|
275
|
+
{
|
|
276
|
+
// With 'inside' fit, the image should fit within 5x5
|
|
277
|
+
// The original is 10x20 (portrait), so height is dominant
|
|
278
|
+
// Scaled to fit: width=2 or 3, height=5
|
|
279
|
+
Expect(pMetadata.width).to.be.at.most(5);
|
|
280
|
+
Expect(pMetadata.height).to.be.at.most(5);
|
|
281
|
+
return fDone();
|
|
282
|
+
}).catch(
|
|
283
|
+
(pMetaError) => { return fDone(pMetaError); });
|
|
284
|
+
});
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
test
|
|
290
|
+
(
|
|
291
|
+
'should support avif output format',
|
|
292
|
+
(fDone) =>
|
|
293
|
+
{
|
|
294
|
+
createTestJpegBuffer(
|
|
295
|
+
(pError, pJpegBuffer) =>
|
|
296
|
+
{
|
|
297
|
+
Expect(pError).to.equal(null);
|
|
298
|
+
|
|
299
|
+
let tmpCore = new libConversionCore();
|
|
300
|
+
|
|
301
|
+
tmpCore.imageResize(pJpegBuffer,
|
|
302
|
+
{ Width: 5, Format: 'avif' },
|
|
303
|
+
(pResizeError, pOutputBuffer, pContentType) =>
|
|
304
|
+
{
|
|
305
|
+
Expect(pResizeError).to.equal(null);
|
|
306
|
+
Expect(pOutputBuffer).to.be.an.instanceOf(Buffer);
|
|
307
|
+
Expect(pContentType).to.equal('image/avif');
|
|
308
|
+
Expect(pOutputBuffer.length).to.be.greaterThan(0);
|
|
309
|
+
|
|
310
|
+
return fDone();
|
|
311
|
+
});
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
);
|
|
317
|
+
|
|
318
|
+
suite
|
|
319
|
+
(
|
|
320
|
+
'Tool Availability Checks',
|
|
321
|
+
() =>
|
|
322
|
+
{
|
|
323
|
+
test
|
|
324
|
+
(
|
|
325
|
+
'checkFfmpeg should return a result',
|
|
326
|
+
(fDone) =>
|
|
327
|
+
{
|
|
328
|
+
let tmpCore = new libConversionCore();
|
|
329
|
+
|
|
330
|
+
tmpCore.checkFfmpeg(
|
|
331
|
+
(pError, pAvailable) =>
|
|
332
|
+
{
|
|
333
|
+
// We don't require ffmpeg to be installed;
|
|
334
|
+
// just verify the check completes without throwing
|
|
335
|
+
Expect(typeof pAvailable).to.equal('boolean');
|
|
336
|
+
return fDone();
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
);
|
|
340
|
+
|
|
341
|
+
test
|
|
342
|
+
(
|
|
343
|
+
'checkFfprobe should return a result',
|
|
344
|
+
(fDone) =>
|
|
345
|
+
{
|
|
346
|
+
let tmpCore = new libConversionCore();
|
|
347
|
+
|
|
348
|
+
tmpCore.checkFfprobe(
|
|
349
|
+
(pError, pAvailable) =>
|
|
350
|
+
{
|
|
351
|
+
Expect(typeof pAvailable).to.equal('boolean');
|
|
352
|
+
return fDone();
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
);
|
|
356
|
+
|
|
357
|
+
test
|
|
358
|
+
(
|
|
359
|
+
'checkFfmpeg with invalid path should return not available',
|
|
360
|
+
(fDone) =>
|
|
361
|
+
{
|
|
362
|
+
let tmpCore = new libConversionCore({ FfmpegPath: '/nonexistent/ffmpeg' });
|
|
363
|
+
|
|
364
|
+
tmpCore.checkFfmpeg(
|
|
365
|
+
(pError, pAvailable) =>
|
|
366
|
+
{
|
|
367
|
+
Expect(pError).to.not.equal(null);
|
|
368
|
+
Expect(pAvailable).to.equal(false);
|
|
369
|
+
return fDone();
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
);
|
|
373
|
+
|
|
374
|
+
test
|
|
375
|
+
(
|
|
376
|
+
'checkFfprobe with invalid path should return not available',
|
|
377
|
+
(fDone) =>
|
|
378
|
+
{
|
|
379
|
+
let tmpCore = new libConversionCore({ FfprobePath: '/nonexistent/ffprobe' });
|
|
380
|
+
|
|
381
|
+
tmpCore.checkFfprobe(
|
|
382
|
+
(pError, pAvailable) =>
|
|
383
|
+
{
|
|
384
|
+
Expect(pError).to.not.equal(null);
|
|
385
|
+
Expect(pAvailable).to.equal(false);
|
|
386
|
+
return fDone();
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
);
|
|
390
|
+
}
|
|
391
|
+
);
|
|
392
|
+
|
|
393
|
+
suite
|
|
394
|
+
(
|
|
395
|
+
'Media Probe',
|
|
396
|
+
() =>
|
|
397
|
+
{
|
|
398
|
+
test
|
|
399
|
+
(
|
|
400
|
+
'should return an error when no file path is provided',
|
|
401
|
+
(fDone) =>
|
|
402
|
+
{
|
|
403
|
+
let tmpCore = new libConversionCore();
|
|
404
|
+
|
|
405
|
+
tmpCore.mediaProbe(null,
|
|
406
|
+
(pError, pMetadata) =>
|
|
407
|
+
{
|
|
408
|
+
Expect(pError).to.not.equal(null);
|
|
409
|
+
Expect(pError.message).to.include('No file path');
|
|
410
|
+
return fDone();
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
);
|
|
414
|
+
}
|
|
415
|
+
);
|
|
416
|
+
|
|
417
|
+
suite
|
|
418
|
+
(
|
|
419
|
+
'Audio Extract Segment',
|
|
420
|
+
() =>
|
|
421
|
+
{
|
|
422
|
+
test
|
|
423
|
+
(
|
|
424
|
+
'should require a Duration parameter',
|
|
425
|
+
(fDone) =>
|
|
426
|
+
{
|
|
427
|
+
let tmpCore = new libConversionCore();
|
|
428
|
+
|
|
429
|
+
tmpCore.audioExtractSegment('/tmp/input.mp3', '/tmp/output.mp3',
|
|
430
|
+
{ Start: '0' },
|
|
431
|
+
(pError, pResultPath) =>
|
|
432
|
+
{
|
|
433
|
+
Expect(pError).to.not.equal(null);
|
|
434
|
+
Expect(pError.message).to.include('Duration is required');
|
|
435
|
+
return fDone();
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
);
|
|
439
|
+
}
|
|
440
|
+
);
|
|
441
|
+
}
|
|
442
|
+
);
|
|
@@ -79,6 +79,12 @@ suite
|
|
|
79
79
|
Expect(tmpFileTranslation.converters['image/jpg-to-png']).to.be.a('function');
|
|
80
80
|
Expect(tmpFileTranslation.converters).to.have.a.property('image/png-to-jpg');
|
|
81
81
|
Expect(tmpFileTranslation.converters['image/png-to-jpg']).to.be.a('function');
|
|
82
|
+
Expect(tmpFileTranslation.converters).to.have.a.property('image/resize');
|
|
83
|
+
Expect(tmpFileTranslation.converters['image/resize']).to.be.a('function');
|
|
84
|
+
Expect(tmpFileTranslation.converters).to.have.a.property('image/rotate/:Angle');
|
|
85
|
+
Expect(tmpFileTranslation.converters['image/rotate/:Angle']).to.be.a('function');
|
|
86
|
+
Expect(tmpFileTranslation.converters).to.have.a.property('image/convert/:Format');
|
|
87
|
+
Expect(tmpFileTranslation.converters['image/convert/:Format']).to.be.a('function');
|
|
82
88
|
Expect(tmpFileTranslation.converters).to.have.a.property('pdf-to-page-png/:Page');
|
|
83
89
|
Expect(tmpFileTranslation.converters['pdf-to-page-png/:Page']).to.be.a('function');
|
|
84
90
|
Expect(tmpFileTranslation.converters).to.have.a.property('pdf-to-page-jpg/:Page');
|
|
@@ -270,13 +276,13 @@ suite
|
|
|
270
276
|
let tmpOrator = tmpFable.serviceManager.instantiateServiceProvider('Orator', {});
|
|
271
277
|
let tmpFileTranslation = tmpFable.serviceManager.instantiateServiceProvider('OratorFileTranslation', {});
|
|
272
278
|
|
|
273
|
-
// Should have
|
|
274
|
-
Expect(Object.keys(tmpFileTranslation.converters).length).to.equal(
|
|
279
|
+
// Should have 9 default converters (jpg-to-png, png-to-jpg, resize, rotate, convert, pdf-to-page-png, pdf-to-page-jpg, pdf-to-page-png sized, pdf-to-page-jpg sized)
|
|
280
|
+
Expect(Object.keys(tmpFileTranslation.converters).length).to.equal(9);
|
|
275
281
|
|
|
276
282
|
tmpFileTranslation.addConverter('document/txt-to-html', (pInput, pReq, fCb) => { fCb(null, pInput, 'text/html'); });
|
|
277
283
|
|
|
278
|
-
// Should now have
|
|
279
|
-
Expect(Object.keys(tmpFileTranslation.converters).length).to.equal(
|
|
284
|
+
// Should now have 10
|
|
285
|
+
Expect(Object.keys(tmpFileTranslation.converters).length).to.equal(10);
|
|
280
286
|
|
|
281
287
|
return fDone();
|
|
282
288
|
}
|
|
@@ -640,6 +640,154 @@ suite
|
|
|
640
640
|
}
|
|
641
641
|
);
|
|
642
642
|
|
|
643
|
+
suite
|
|
644
|
+
(
|
|
645
|
+
'Image Resize Endpoint',
|
|
646
|
+
() =>
|
|
647
|
+
{
|
|
648
|
+
test
|
|
649
|
+
(
|
|
650
|
+
'should resize an image via the resize endpoint',
|
|
651
|
+
(fDone) =>
|
|
652
|
+
{
|
|
653
|
+
createTestJpegBuffer(
|
|
654
|
+
(pError, pJpegBuffer) =>
|
|
655
|
+
{
|
|
656
|
+
Expect(pError).to.equal(null);
|
|
657
|
+
|
|
658
|
+
createStartedHarness(null, null,
|
|
659
|
+
(pHarness) =>
|
|
660
|
+
{
|
|
661
|
+
postBinaryToEndpoint(pHarness.port, '/conversion/1.0/image/resize?Width=5&Height=5', pJpegBuffer,
|
|
662
|
+
(pPostError, pResponse, pBody) =>
|
|
663
|
+
{
|
|
664
|
+
Expect(pPostError).to.equal(null);
|
|
665
|
+
Expect(pResponse.statusCode).to.equal(200);
|
|
666
|
+
Expect(pBody).to.be.an.instanceOf(Buffer);
|
|
667
|
+
Expect(pBody.length).to.be.greaterThan(0);
|
|
668
|
+
|
|
669
|
+
libSharp(pBody).metadata().then(
|
|
670
|
+
(pMetadata) =>
|
|
671
|
+
{
|
|
672
|
+
Expect(pMetadata.width).to.equal(5);
|
|
673
|
+
Expect(pMetadata.height).to.equal(5);
|
|
674
|
+
pHarness.orator.stopService(fDone);
|
|
675
|
+
}).catch(
|
|
676
|
+
(pMetaError) =>
|
|
677
|
+
{
|
|
678
|
+
pHarness.orator.stopService(() => { fDone(pMetaError); });
|
|
679
|
+
});
|
|
680
|
+
});
|
|
681
|
+
});
|
|
682
|
+
});
|
|
683
|
+
}
|
|
684
|
+
);
|
|
685
|
+
}
|
|
686
|
+
);
|
|
687
|
+
|
|
688
|
+
suite
|
|
689
|
+
(
|
|
690
|
+
'Image Rotate Endpoint',
|
|
691
|
+
() =>
|
|
692
|
+
{
|
|
693
|
+
test
|
|
694
|
+
(
|
|
695
|
+
'should rotate an image 90 degrees via the rotate endpoint',
|
|
696
|
+
(fDone) =>
|
|
697
|
+
{
|
|
698
|
+
createTestJpegBuffer(
|
|
699
|
+
(pError, pJpegBuffer) =>
|
|
700
|
+
{
|
|
701
|
+
Expect(pError).to.equal(null);
|
|
702
|
+
|
|
703
|
+
createStartedHarness(null, null,
|
|
704
|
+
(pHarness) =>
|
|
705
|
+
{
|
|
706
|
+
postBinaryToEndpoint(pHarness.port, '/conversion/1.0/image/rotate/90', pJpegBuffer,
|
|
707
|
+
(pPostError, pResponse, pBody) =>
|
|
708
|
+
{
|
|
709
|
+
Expect(pPostError).to.equal(null);
|
|
710
|
+
Expect(pResponse.statusCode).to.equal(200);
|
|
711
|
+
Expect(pBody).to.be.an.instanceOf(Buffer);
|
|
712
|
+
Expect(pBody.length).to.be.greaterThan(0);
|
|
713
|
+
|
|
714
|
+
pHarness.orator.stopService(fDone);
|
|
715
|
+
});
|
|
716
|
+
});
|
|
717
|
+
});
|
|
718
|
+
}
|
|
719
|
+
);
|
|
720
|
+
}
|
|
721
|
+
);
|
|
722
|
+
|
|
723
|
+
suite
|
|
724
|
+
(
|
|
725
|
+
'Image Convert Endpoint',
|
|
726
|
+
() =>
|
|
727
|
+
{
|
|
728
|
+
test
|
|
729
|
+
(
|
|
730
|
+
'should convert a JPEG to PNG via the convert endpoint',
|
|
731
|
+
(fDone) =>
|
|
732
|
+
{
|
|
733
|
+
createTestJpegBuffer(
|
|
734
|
+
(pError, pJpegBuffer) =>
|
|
735
|
+
{
|
|
736
|
+
Expect(pError).to.equal(null);
|
|
737
|
+
|
|
738
|
+
createStartedHarness(null, null,
|
|
739
|
+
(pHarness) =>
|
|
740
|
+
{
|
|
741
|
+
postBinaryToEndpoint(pHarness.port, '/conversion/1.0/image/convert/png', pJpegBuffer,
|
|
742
|
+
(pPostError, pResponse, pBody) =>
|
|
743
|
+
{
|
|
744
|
+
Expect(pPostError).to.equal(null);
|
|
745
|
+
Expect(pResponse.statusCode).to.equal(200);
|
|
746
|
+
Expect(pResponse.headers['content-type']).to.equal('image/png');
|
|
747
|
+
|
|
748
|
+
// Verify PNG magic bytes
|
|
749
|
+
Expect(pBody[0]).to.equal(137);
|
|
750
|
+
Expect(pBody[1]).to.equal(80);
|
|
751
|
+
Expect(pBody[2]).to.equal(78);
|
|
752
|
+
Expect(pBody[3]).to.equal(71);
|
|
753
|
+
|
|
754
|
+
pHarness.orator.stopService(fDone);
|
|
755
|
+
});
|
|
756
|
+
});
|
|
757
|
+
});
|
|
758
|
+
}
|
|
759
|
+
);
|
|
760
|
+
|
|
761
|
+
test
|
|
762
|
+
(
|
|
763
|
+
'should convert a JPEG to WebP via the convert endpoint',
|
|
764
|
+
(fDone) =>
|
|
765
|
+
{
|
|
766
|
+
createTestJpegBuffer(
|
|
767
|
+
(pError, pJpegBuffer) =>
|
|
768
|
+
{
|
|
769
|
+
Expect(pError).to.equal(null);
|
|
770
|
+
|
|
771
|
+
createStartedHarness(null, null,
|
|
772
|
+
(pHarness) =>
|
|
773
|
+
{
|
|
774
|
+
postBinaryToEndpoint(pHarness.port, '/conversion/1.0/image/convert/webp', pJpegBuffer,
|
|
775
|
+
(pPostError, pResponse, pBody) =>
|
|
776
|
+
{
|
|
777
|
+
Expect(pPostError).to.equal(null);
|
|
778
|
+
Expect(pResponse.statusCode).to.equal(200);
|
|
779
|
+
Expect(pResponse.headers['content-type']).to.equal('image/webp');
|
|
780
|
+
Expect(pBody.length).to.be.greaterThan(0);
|
|
781
|
+
|
|
782
|
+
pHarness.orator.stopService(fDone);
|
|
783
|
+
});
|
|
784
|
+
});
|
|
785
|
+
});
|
|
786
|
+
}
|
|
787
|
+
);
|
|
788
|
+
}
|
|
789
|
+
);
|
|
790
|
+
|
|
643
791
|
suite
|
|
644
792
|
(
|
|
645
793
|
'Custom Converter',
|