orator-conversion 1.0.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.
- package/.dockerignore +15 -0
- package/.vscode/launch.json +47 -0
- package/Dockerfile +60 -0
- package/LICENSE +21 -0
- package/README.md +166 -0
- package/debug/Harness.js +48 -0
- package/debug/PostJPGToDebugServer.sh +44 -0
- package/debug/PostPDFToDebugServer.sh +54 -0
- package/debug/PostPNGToDebugServer.sh +44 -0
- package/docs/.nojekyll +0 -0
- package/docs/README.md +68 -0
- package/docs/_sidebar.md +16 -0
- package/docs/api-reference.md +146 -0
- package/docs/configuration.md +77 -0
- package/docs/cover.md +13 -0
- package/docs/endpoints/001-jpg-to-png.md +60 -0
- package/docs/endpoints/002-png-to-jpg.md +60 -0
- package/docs/endpoints/003-pdf-to-page-png.md +93 -0
- package/docs/endpoints/004-pdf-to-page-jpg.md +94 -0
- package/docs/endpoints/README.md +75 -0
- package/docs/getting-started.md +113 -0
- package/docs/index.html +39 -0
- package/package.json +55 -0
- package/source/Orator-File-Translation.js +504 -0
- package/source/endpoints/Endpoint-Image-JpgToPng.js +34 -0
- package/source/endpoints/Endpoint-Image-PngToJpg.js +34 -0
- package/source/endpoints/Endpoint-Pdf-PageToJpg-Sized.js +43 -0
- package/source/endpoints/Endpoint-Pdf-PageToJpg.js +36 -0
- package/source/endpoints/Endpoint-Pdf-PageToPng-Sized.js +43 -0
- package/source/endpoints/Endpoint-Pdf-PageToPng.js +36 -0
- package/test/Orator-File-Translation_basic_tests.js +371 -0
- package/test/Orator-File-Translation_tests.js +694 -0
|
@@ -0,0 +1,694 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for Orator File Translation - HTTP Integration
|
|
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 libHTTP = require('http');
|
|
13
|
+
|
|
14
|
+
const libFable = require('fable');
|
|
15
|
+
const libOrator = require('orator');
|
|
16
|
+
const libOratorServiceServerRestify = require('orator-serviceserver-restify');
|
|
17
|
+
const libOratorFileTranslation = require('../source/Orator-File-Translation.js');
|
|
18
|
+
|
|
19
|
+
const libSharp = require('sharp');
|
|
20
|
+
|
|
21
|
+
const defaultFableSettings = (
|
|
22
|
+
{
|
|
23
|
+
Product: 'OratorFileTranslation-IntegrationTests',
|
|
24
|
+
ProductVersion: '0.0.0',
|
|
25
|
+
APIServerPort: 0
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Helper that creates a Fable + Orator + Restify + FileTranslation harness,
|
|
30
|
+
* starts the service, then calls back with the harness and the actual port.
|
|
31
|
+
*
|
|
32
|
+
* @param {object} pFableSettings - Fable settings to merge with defaults.
|
|
33
|
+
* @param {object} pFileTranslationOptions - Options for the file translation instance.
|
|
34
|
+
* @param {Function} fCallback - Called with (pHarness) after the service starts.
|
|
35
|
+
*/
|
|
36
|
+
function createStartedHarness(pFableSettings, pFileTranslationOptions, fCallback)
|
|
37
|
+
{
|
|
38
|
+
let tmpFableSettings = Object.assign({}, defaultFableSettings, pFableSettings || {});
|
|
39
|
+
let tmpFable = new libFable(tmpFableSettings);
|
|
40
|
+
|
|
41
|
+
tmpFable.serviceManager.addServiceType('OratorServiceServer', libOratorServiceServerRestify);
|
|
42
|
+
tmpFable.serviceManager.addServiceType('Orator', libOrator);
|
|
43
|
+
tmpFable.serviceManager.addServiceType('OratorFileTranslation', libOratorFileTranslation);
|
|
44
|
+
|
|
45
|
+
let tmpRestifyServer = tmpFable.serviceManager.instantiateServiceProvider('OratorServiceServer', {});
|
|
46
|
+
let tmpOrator = tmpFable.serviceManager.instantiateServiceProvider('Orator', {});
|
|
47
|
+
let tmpFileTranslation = tmpFable.serviceManager.instantiateServiceProvider('OratorFileTranslation', pFileTranslationOptions || {});
|
|
48
|
+
|
|
49
|
+
let tmpResult = (
|
|
50
|
+
{
|
|
51
|
+
fable: tmpFable,
|
|
52
|
+
orator: tmpOrator,
|
|
53
|
+
restifyServer: tmpRestifyServer,
|
|
54
|
+
fileTranslation: tmpFileTranslation,
|
|
55
|
+
port: 0
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
tmpOrator.startService(
|
|
59
|
+
() =>
|
|
60
|
+
{
|
|
61
|
+
tmpFileTranslation.connectRoutes();
|
|
62
|
+
// Get the actual port from the restify server
|
|
63
|
+
tmpResult.port = tmpRestifyServer.server.address().port;
|
|
64
|
+
return fCallback(tmpResult);
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* POST binary data to an endpoint and collect the response.
|
|
70
|
+
*
|
|
71
|
+
* @param {number} pPort - The port to connect to.
|
|
72
|
+
* @param {string} pPath - The URL path.
|
|
73
|
+
* @param {Buffer} pBuffer - The binary body to send.
|
|
74
|
+
* @param {Function} fCallback - Called with (pError, pResponse, pBody).
|
|
75
|
+
*/
|
|
76
|
+
function postBinaryToEndpoint(pPort, pPath, pBuffer, fCallback)
|
|
77
|
+
{
|
|
78
|
+
let tmpOptions =
|
|
79
|
+
{
|
|
80
|
+
hostname: '127.0.0.1',
|
|
81
|
+
port: pPort,
|
|
82
|
+
path: pPath,
|
|
83
|
+
method: 'POST',
|
|
84
|
+
headers:
|
|
85
|
+
{
|
|
86
|
+
'Content-Type': 'application/octet-stream',
|
|
87
|
+
'Content-Length': pBuffer ? pBuffer.length : 0
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
let tmpRequest = libHTTP.request(tmpOptions,
|
|
92
|
+
(pResponse) =>
|
|
93
|
+
{
|
|
94
|
+
let tmpChunks = [];
|
|
95
|
+
pResponse.on('data', (pChunk) => { tmpChunks.push(pChunk); });
|
|
96
|
+
pResponse.on('end',
|
|
97
|
+
() =>
|
|
98
|
+
{
|
|
99
|
+
let tmpBody = Buffer.concat(tmpChunks);
|
|
100
|
+
return fCallback(null, pResponse, tmpBody);
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
tmpRequest.on('error', (pError) => { return fCallback(pError); });
|
|
105
|
+
|
|
106
|
+
if (pBuffer && pBuffer.length > 0)
|
|
107
|
+
{
|
|
108
|
+
tmpRequest.write(pBuffer);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
tmpRequest.end();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Generate a minimal JPEG test image buffer.
|
|
116
|
+
*/
|
|
117
|
+
function createTestJpegBuffer(fCallback)
|
|
118
|
+
{
|
|
119
|
+
libSharp(
|
|
120
|
+
{
|
|
121
|
+
create:
|
|
122
|
+
{
|
|
123
|
+
width: 2,
|
|
124
|
+
height: 2,
|
|
125
|
+
channels: 3,
|
|
126
|
+
background: { r: 255, g: 0, b: 0 }
|
|
127
|
+
}
|
|
128
|
+
}).jpeg().toBuffer().then(
|
|
129
|
+
(pBuffer) => { return fCallback(null, pBuffer); },
|
|
130
|
+
(pError) => { return fCallback(pError); });
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Generate a minimal PNG test image buffer.
|
|
135
|
+
*/
|
|
136
|
+
function createTestPngBuffer(fCallback)
|
|
137
|
+
{
|
|
138
|
+
libSharp(
|
|
139
|
+
{
|
|
140
|
+
create:
|
|
141
|
+
{
|
|
142
|
+
width: 2,
|
|
143
|
+
height: 2,
|
|
144
|
+
channels: 4,
|
|
145
|
+
background: { r: 0, g: 0, b: 255, alpha: 1 }
|
|
146
|
+
}
|
|
147
|
+
}).png().toBuffer().then(
|
|
148
|
+
(pBuffer) => { return fCallback(null, pBuffer); },
|
|
149
|
+
(pError) => { return fCallback(pError); });
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Generate a minimal single-page test PDF buffer.
|
|
154
|
+
* This is a hand-crafted minimal PDF that pdftk can process.
|
|
155
|
+
*/
|
|
156
|
+
function createTestPdfBuffer(fCallback)
|
|
157
|
+
{
|
|
158
|
+
let tmpPdf = '%PDF-1.4\n' +
|
|
159
|
+
'1 0 obj<</Type/Catalog/Pages 2 0 R>>endobj\n' +
|
|
160
|
+
'2 0 obj<</Type/Pages/Kids[3 0 R]/Count 1>>endobj\n' +
|
|
161
|
+
'3 0 obj<</Type/Page/Parent 2 0 R/MediaBox[0 0 72 72]/Contents 4 0 R>>endobj\n' +
|
|
162
|
+
'4 0 obj<</Length 44>>stream\n' +
|
|
163
|
+
'1 0 0 rg\n' +
|
|
164
|
+
'0 0 72 72 re\n' +
|
|
165
|
+
'f\n' +
|
|
166
|
+
'endstream\n' +
|
|
167
|
+
'endobj\n' +
|
|
168
|
+
'xref\n' +
|
|
169
|
+
'0 5\n' +
|
|
170
|
+
'0000000000 65535 f \n' +
|
|
171
|
+
'0000000009 00000 n \n' +
|
|
172
|
+
'0000000058 00000 n \n' +
|
|
173
|
+
'0000000115 00000 n \n' +
|
|
174
|
+
'0000000210 00000 n \n' +
|
|
175
|
+
'trailer<</Root 1 0 R/Size 5>>\n' +
|
|
176
|
+
'startxref\n' +
|
|
177
|
+
'304\n' +
|
|
178
|
+
'%%EOF';
|
|
179
|
+
|
|
180
|
+
return fCallback(null, Buffer.from(tmpPdf));
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
suite
|
|
184
|
+
(
|
|
185
|
+
'Orator File Translation HTTP Integration',
|
|
186
|
+
() =>
|
|
187
|
+
{
|
|
188
|
+
suite
|
|
189
|
+
(
|
|
190
|
+
'JPG to PNG Conversion',
|
|
191
|
+
() =>
|
|
192
|
+
{
|
|
193
|
+
test
|
|
194
|
+
(
|
|
195
|
+
'should convert a JPEG image to PNG format via versioned endpoint',
|
|
196
|
+
(fDone) =>
|
|
197
|
+
{
|
|
198
|
+
createTestJpegBuffer(
|
|
199
|
+
(pError, pJpegBuffer) =>
|
|
200
|
+
{
|
|
201
|
+
Expect(pError).to.equal(null);
|
|
202
|
+
Expect(pJpegBuffer).to.be.an.instanceOf(Buffer);
|
|
203
|
+
Expect(pJpegBuffer.length).to.be.greaterThan(0);
|
|
204
|
+
|
|
205
|
+
createStartedHarness(null, null,
|
|
206
|
+
(pHarness) =>
|
|
207
|
+
{
|
|
208
|
+
postBinaryToEndpoint(pHarness.port, '/conversion/1.0/image/jpg-to-png', pJpegBuffer,
|
|
209
|
+
(pPostError, pResponse, pBody) =>
|
|
210
|
+
{
|
|
211
|
+
Expect(pPostError).to.equal(null);
|
|
212
|
+
Expect(pResponse.statusCode).to.equal(200);
|
|
213
|
+
Expect(pResponse.headers['content-type']).to.equal('image/png');
|
|
214
|
+
Expect(pBody).to.be.an.instanceOf(Buffer);
|
|
215
|
+
Expect(pBody.length).to.be.greaterThan(0);
|
|
216
|
+
|
|
217
|
+
// Verify the output is actually a valid PNG by checking the magic bytes
|
|
218
|
+
// PNG files start with: 137 80 78 71 13 10 26 10
|
|
219
|
+
Expect(pBody[0]).to.equal(137);
|
|
220
|
+
Expect(pBody[1]).to.equal(80);
|
|
221
|
+
Expect(pBody[2]).to.equal(78);
|
|
222
|
+
Expect(pBody[3]).to.equal(71);
|
|
223
|
+
|
|
224
|
+
pHarness.orator.stopService(fDone);
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
suite
|
|
234
|
+
(
|
|
235
|
+
'PNG to JPG Conversion',
|
|
236
|
+
() =>
|
|
237
|
+
{
|
|
238
|
+
test
|
|
239
|
+
(
|
|
240
|
+
'should convert a PNG image to JPEG format via versioned endpoint',
|
|
241
|
+
(fDone) =>
|
|
242
|
+
{
|
|
243
|
+
createTestPngBuffer(
|
|
244
|
+
(pError, pPngBuffer) =>
|
|
245
|
+
{
|
|
246
|
+
Expect(pError).to.equal(null);
|
|
247
|
+
Expect(pPngBuffer).to.be.an.instanceOf(Buffer);
|
|
248
|
+
|
|
249
|
+
createStartedHarness(null, null,
|
|
250
|
+
(pHarness) =>
|
|
251
|
+
{
|
|
252
|
+
postBinaryToEndpoint(pHarness.port, '/conversion/1.0/image/png-to-jpg', pPngBuffer,
|
|
253
|
+
(pPostError, pResponse, pBody) =>
|
|
254
|
+
{
|
|
255
|
+
Expect(pPostError).to.equal(null);
|
|
256
|
+
Expect(pResponse.statusCode).to.equal(200);
|
|
257
|
+
Expect(pResponse.headers['content-type']).to.equal('image/jpeg');
|
|
258
|
+
Expect(pBody).to.be.an.instanceOf(Buffer);
|
|
259
|
+
Expect(pBody.length).to.be.greaterThan(0);
|
|
260
|
+
|
|
261
|
+
// Verify the output is actually a valid JPEG by checking the magic bytes
|
|
262
|
+
// JPEG files start with: 0xFF 0xD8 0xFF
|
|
263
|
+
Expect(pBody[0]).to.equal(0xFF);
|
|
264
|
+
Expect(pBody[1]).to.equal(0xD8);
|
|
265
|
+
Expect(pBody[2]).to.equal(0xFF);
|
|
266
|
+
|
|
267
|
+
pHarness.orator.stopService(fDone);
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
suite
|
|
277
|
+
(
|
|
278
|
+
'PDF Page Extraction',
|
|
279
|
+
() =>
|
|
280
|
+
{
|
|
281
|
+
test
|
|
282
|
+
(
|
|
283
|
+
'should extract page 1 from a PDF and return as PNG',
|
|
284
|
+
(fDone) =>
|
|
285
|
+
{
|
|
286
|
+
createTestPdfBuffer(
|
|
287
|
+
(pError, pPdfBuffer) =>
|
|
288
|
+
{
|
|
289
|
+
Expect(pError).to.equal(null);
|
|
290
|
+
Expect(pPdfBuffer).to.be.an.instanceOf(Buffer);
|
|
291
|
+
|
|
292
|
+
createStartedHarness(null, null,
|
|
293
|
+
(pHarness) =>
|
|
294
|
+
{
|
|
295
|
+
postBinaryToEndpoint(pHarness.port, '/conversion/1.0/pdf-to-page-png/1', pPdfBuffer,
|
|
296
|
+
(pPostError, pResponse, pBody) =>
|
|
297
|
+
{
|
|
298
|
+
Expect(pPostError).to.equal(null);
|
|
299
|
+
Expect(pResponse.statusCode).to.equal(200);
|
|
300
|
+
Expect(pResponse.headers['content-type']).to.equal('image/png');
|
|
301
|
+
Expect(pBody).to.be.an.instanceOf(Buffer);
|
|
302
|
+
Expect(pBody.length).to.be.greaterThan(0);
|
|
303
|
+
|
|
304
|
+
// Verify PNG magic bytes
|
|
305
|
+
Expect(pBody[0]).to.equal(137);
|
|
306
|
+
Expect(pBody[1]).to.equal(80);
|
|
307
|
+
Expect(pBody[2]).to.equal(78);
|
|
308
|
+
Expect(pBody[3]).to.equal(71);
|
|
309
|
+
|
|
310
|
+
pHarness.orator.stopService(fDone);
|
|
311
|
+
});
|
|
312
|
+
});
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
);
|
|
316
|
+
|
|
317
|
+
test
|
|
318
|
+
(
|
|
319
|
+
'should extract page 1 from a PDF and return as JPEG',
|
|
320
|
+
(fDone) =>
|
|
321
|
+
{
|
|
322
|
+
createTestPdfBuffer(
|
|
323
|
+
(pError, pPdfBuffer) =>
|
|
324
|
+
{
|
|
325
|
+
Expect(pError).to.equal(null);
|
|
326
|
+
|
|
327
|
+
createStartedHarness(null, null,
|
|
328
|
+
(pHarness) =>
|
|
329
|
+
{
|
|
330
|
+
postBinaryToEndpoint(pHarness.port, '/conversion/1.0/pdf-to-page-jpg/1', pPdfBuffer,
|
|
331
|
+
(pPostError, pResponse, pBody) =>
|
|
332
|
+
{
|
|
333
|
+
Expect(pPostError).to.equal(null);
|
|
334
|
+
Expect(pResponse.statusCode).to.equal(200);
|
|
335
|
+
Expect(pResponse.headers['content-type']).to.equal('image/jpeg');
|
|
336
|
+
Expect(pBody).to.be.an.instanceOf(Buffer);
|
|
337
|
+
Expect(pBody.length).to.be.greaterThan(0);
|
|
338
|
+
|
|
339
|
+
// Verify JPEG magic bytes
|
|
340
|
+
Expect(pBody[0]).to.equal(0xFF);
|
|
341
|
+
Expect(pBody[1]).to.equal(0xD8);
|
|
342
|
+
Expect(pBody[2]).to.equal(0xFF);
|
|
343
|
+
|
|
344
|
+
pHarness.orator.stopService(fDone);
|
|
345
|
+
});
|
|
346
|
+
});
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
);
|
|
350
|
+
|
|
351
|
+
test
|
|
352
|
+
(
|
|
353
|
+
'should return 500 when requesting an invalid page number from a PDF',
|
|
354
|
+
(fDone) =>
|
|
355
|
+
{
|
|
356
|
+
createTestPdfBuffer(
|
|
357
|
+
(pError, pPdfBuffer) =>
|
|
358
|
+
{
|
|
359
|
+
Expect(pError).to.equal(null);
|
|
360
|
+
|
|
361
|
+
createStartedHarness(null, null,
|
|
362
|
+
(pHarness) =>
|
|
363
|
+
{
|
|
364
|
+
// Request page 999 from a 1-page PDF
|
|
365
|
+
postBinaryToEndpoint(pHarness.port, '/conversion/1.0/pdf-to-page-png/999', pPdfBuffer,
|
|
366
|
+
(pPostError, pResponse, pBody) =>
|
|
367
|
+
{
|
|
368
|
+
Expect(pPostError).to.equal(null);
|
|
369
|
+
Expect(pResponse.statusCode).to.equal(500);
|
|
370
|
+
|
|
371
|
+
let tmpResponseData = JSON.parse(pBody.toString());
|
|
372
|
+
Expect(tmpResponseData).to.have.a.property('error');
|
|
373
|
+
Expect(tmpResponseData.error).to.include('Conversion failed');
|
|
374
|
+
|
|
375
|
+
pHarness.orator.stopService(fDone);
|
|
376
|
+
});
|
|
377
|
+
});
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
);
|
|
381
|
+
|
|
382
|
+
test
|
|
383
|
+
(
|
|
384
|
+
'should return 500 when posting invalid data to PDF endpoint',
|
|
385
|
+
(fDone) =>
|
|
386
|
+
{
|
|
387
|
+
createStartedHarness(null, null,
|
|
388
|
+
(pHarness) =>
|
|
389
|
+
{
|
|
390
|
+
let tmpInvalidData = Buffer.from('this is not a pdf');
|
|
391
|
+
|
|
392
|
+
postBinaryToEndpoint(pHarness.port, '/conversion/1.0/pdf-to-page-png/1', tmpInvalidData,
|
|
393
|
+
(pPostError, pResponse, pBody) =>
|
|
394
|
+
{
|
|
395
|
+
Expect(pPostError).to.equal(null);
|
|
396
|
+
Expect(pResponse.statusCode).to.equal(500);
|
|
397
|
+
|
|
398
|
+
let tmpResponseData = JSON.parse(pBody.toString());
|
|
399
|
+
Expect(tmpResponseData).to.have.a.property('error');
|
|
400
|
+
Expect(tmpResponseData.error).to.include('Conversion failed');
|
|
401
|
+
|
|
402
|
+
pHarness.orator.stopService(fDone);
|
|
403
|
+
});
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
);
|
|
409
|
+
|
|
410
|
+
suite
|
|
411
|
+
(
|
|
412
|
+
'Error Handling',
|
|
413
|
+
() =>
|
|
414
|
+
{
|
|
415
|
+
test
|
|
416
|
+
(
|
|
417
|
+
'should return 400 when no file data is provided',
|
|
418
|
+
(fDone) =>
|
|
419
|
+
{
|
|
420
|
+
createStartedHarness(null, null,
|
|
421
|
+
(pHarness) =>
|
|
422
|
+
{
|
|
423
|
+
postBinaryToEndpoint(pHarness.port, '/conversion/1.0/image/jpg-to-png', Buffer.alloc(0),
|
|
424
|
+
(pPostError, pResponse, pBody) =>
|
|
425
|
+
{
|
|
426
|
+
Expect(pPostError).to.equal(null);
|
|
427
|
+
Expect(pResponse.statusCode).to.equal(400);
|
|
428
|
+
|
|
429
|
+
let tmpResponseData = JSON.parse(pBody.toString());
|
|
430
|
+
Expect(tmpResponseData).to.have.a.property('error');
|
|
431
|
+
Expect(tmpResponseData.error).to.include('No file data');
|
|
432
|
+
|
|
433
|
+
pHarness.orator.stopService(fDone);
|
|
434
|
+
});
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
);
|
|
438
|
+
|
|
439
|
+
test
|
|
440
|
+
(
|
|
441
|
+
'should return 500 when invalid image data is provided',
|
|
442
|
+
(fDone) =>
|
|
443
|
+
{
|
|
444
|
+
createStartedHarness(null, null,
|
|
445
|
+
(pHarness) =>
|
|
446
|
+
{
|
|
447
|
+
let tmpInvalidData = Buffer.from('this is not an image file');
|
|
448
|
+
|
|
449
|
+
postBinaryToEndpoint(pHarness.port, '/conversion/1.0/image/jpg-to-png', tmpInvalidData,
|
|
450
|
+
(pPostError, pResponse, pBody) =>
|
|
451
|
+
{
|
|
452
|
+
Expect(pPostError).to.equal(null);
|
|
453
|
+
Expect(pResponse.statusCode).to.equal(500);
|
|
454
|
+
|
|
455
|
+
let tmpResponseData = JSON.parse(pBody.toString());
|
|
456
|
+
Expect(tmpResponseData).to.have.a.property('error');
|
|
457
|
+
Expect(tmpResponseData.error).to.include('Conversion failed');
|
|
458
|
+
|
|
459
|
+
pHarness.orator.stopService(fDone);
|
|
460
|
+
});
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
);
|
|
464
|
+
}
|
|
465
|
+
);
|
|
466
|
+
|
|
467
|
+
suite
|
|
468
|
+
(
|
|
469
|
+
'Custom Route Prefix and Version',
|
|
470
|
+
() =>
|
|
471
|
+
{
|
|
472
|
+
test
|
|
473
|
+
(
|
|
474
|
+
'should use a custom route prefix and version for conversion endpoints',
|
|
475
|
+
(fDone) =>
|
|
476
|
+
{
|
|
477
|
+
createTestJpegBuffer(
|
|
478
|
+
(pError, pJpegBuffer) =>
|
|
479
|
+
{
|
|
480
|
+
Expect(pError).to.equal(null);
|
|
481
|
+
|
|
482
|
+
createStartedHarness(null,
|
|
483
|
+
{
|
|
484
|
+
RoutePrefix: '/api/convert',
|
|
485
|
+
Version: '2.0'
|
|
486
|
+
},
|
|
487
|
+
(pHarness) =>
|
|
488
|
+
{
|
|
489
|
+
postBinaryToEndpoint(pHarness.port, '/api/convert/2.0/image/jpg-to-png', pJpegBuffer,
|
|
490
|
+
(pPostError, pResponse, pBody) =>
|
|
491
|
+
{
|
|
492
|
+
Expect(pPostError).to.equal(null);
|
|
493
|
+
Expect(pResponse.statusCode).to.equal(200);
|
|
494
|
+
Expect(pResponse.headers['content-type']).to.equal('image/png');
|
|
495
|
+
|
|
496
|
+
pHarness.orator.stopService(fDone);
|
|
497
|
+
});
|
|
498
|
+
});
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
);
|
|
502
|
+
}
|
|
503
|
+
);
|
|
504
|
+
|
|
505
|
+
suite
|
|
506
|
+
(
|
|
507
|
+
'PDF Page Extraction with LongSidePixels',
|
|
508
|
+
() =>
|
|
509
|
+
{
|
|
510
|
+
test
|
|
511
|
+
(
|
|
512
|
+
'should extract page 1 from a PDF as PNG resized to 100 pixels on the long side',
|
|
513
|
+
(fDone) =>
|
|
514
|
+
{
|
|
515
|
+
createTestPdfBuffer(
|
|
516
|
+
(pError, pPdfBuffer) =>
|
|
517
|
+
{
|
|
518
|
+
Expect(pError).to.equal(null);
|
|
519
|
+
Expect(pPdfBuffer).to.be.an.instanceOf(Buffer);
|
|
520
|
+
|
|
521
|
+
createStartedHarness(null, null,
|
|
522
|
+
(pHarness) =>
|
|
523
|
+
{
|
|
524
|
+
postBinaryToEndpoint(pHarness.port, '/conversion/1.0/pdf-to-page-png/1/100', pPdfBuffer,
|
|
525
|
+
(pPostError, pResponse, pBody) =>
|
|
526
|
+
{
|
|
527
|
+
Expect(pPostError).to.equal(null);
|
|
528
|
+
Expect(pResponse.statusCode).to.equal(200);
|
|
529
|
+
Expect(pResponse.headers['content-type']).to.equal('image/png');
|
|
530
|
+
Expect(pBody).to.be.an.instanceOf(Buffer);
|
|
531
|
+
Expect(pBody.length).to.be.greaterThan(0);
|
|
532
|
+
|
|
533
|
+
// Verify PNG magic bytes
|
|
534
|
+
Expect(pBody[0]).to.equal(137);
|
|
535
|
+
Expect(pBody[1]).to.equal(80);
|
|
536
|
+
Expect(pBody[2]).to.equal(78);
|
|
537
|
+
Expect(pBody[3]).to.equal(71);
|
|
538
|
+
|
|
539
|
+
// Verify the image dimensions using sharp metadata
|
|
540
|
+
libSharp(pBody).metadata().then(
|
|
541
|
+
(pMetadata) =>
|
|
542
|
+
{
|
|
543
|
+
let tmpLongSide = Math.max(pMetadata.width, pMetadata.height);
|
|
544
|
+
Expect(tmpLongSide).to.equal(100);
|
|
545
|
+
pHarness.orator.stopService(fDone);
|
|
546
|
+
}).catch(
|
|
547
|
+
(pMetaError) =>
|
|
548
|
+
{
|
|
549
|
+
pHarness.orator.stopService(() => { fDone(pMetaError); });
|
|
550
|
+
});
|
|
551
|
+
});
|
|
552
|
+
});
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
);
|
|
556
|
+
|
|
557
|
+
test
|
|
558
|
+
(
|
|
559
|
+
'should extract page 1 from a PDF as JPEG resized to 200 pixels on the long side',
|
|
560
|
+
(fDone) =>
|
|
561
|
+
{
|
|
562
|
+
createTestPdfBuffer(
|
|
563
|
+
(pError, pPdfBuffer) =>
|
|
564
|
+
{
|
|
565
|
+
Expect(pError).to.equal(null);
|
|
566
|
+
|
|
567
|
+
createStartedHarness(null, null,
|
|
568
|
+
(pHarness) =>
|
|
569
|
+
{
|
|
570
|
+
postBinaryToEndpoint(pHarness.port, '/conversion/1.0/pdf-to-page-jpg/1/200', pPdfBuffer,
|
|
571
|
+
(pPostError, pResponse, pBody) =>
|
|
572
|
+
{
|
|
573
|
+
Expect(pPostError).to.equal(null);
|
|
574
|
+
Expect(pResponse.statusCode).to.equal(200);
|
|
575
|
+
Expect(pResponse.headers['content-type']).to.equal('image/jpeg');
|
|
576
|
+
Expect(pBody).to.be.an.instanceOf(Buffer);
|
|
577
|
+
Expect(pBody.length).to.be.greaterThan(0);
|
|
578
|
+
|
|
579
|
+
// Verify JPEG magic bytes
|
|
580
|
+
Expect(pBody[0]).to.equal(0xFF);
|
|
581
|
+
Expect(pBody[1]).to.equal(0xD8);
|
|
582
|
+
Expect(pBody[2]).to.equal(0xFF);
|
|
583
|
+
|
|
584
|
+
// Verify the image dimensions using sharp metadata
|
|
585
|
+
libSharp(pBody).metadata().then(
|
|
586
|
+
(pMetadata) =>
|
|
587
|
+
{
|
|
588
|
+
let tmpLongSide = Math.max(pMetadata.width, pMetadata.height);
|
|
589
|
+
Expect(tmpLongSide).to.equal(200);
|
|
590
|
+
pHarness.orator.stopService(fDone);
|
|
591
|
+
}).catch(
|
|
592
|
+
(pMetaError) =>
|
|
593
|
+
{
|
|
594
|
+
pHarness.orator.stopService(() => { fDone(pMetaError); });
|
|
595
|
+
});
|
|
596
|
+
});
|
|
597
|
+
});
|
|
598
|
+
});
|
|
599
|
+
}
|
|
600
|
+
);
|
|
601
|
+
|
|
602
|
+
test
|
|
603
|
+
(
|
|
604
|
+
'should still work with the original page-only route (no resize)',
|
|
605
|
+
(fDone) =>
|
|
606
|
+
{
|
|
607
|
+
createTestPdfBuffer(
|
|
608
|
+
(pError, pPdfBuffer) =>
|
|
609
|
+
{
|
|
610
|
+
Expect(pError).to.equal(null);
|
|
611
|
+
|
|
612
|
+
createStartedHarness(null, null,
|
|
613
|
+
(pHarness) =>
|
|
614
|
+
{
|
|
615
|
+
postBinaryToEndpoint(pHarness.port, '/conversion/1.0/pdf-to-page-png/1', pPdfBuffer,
|
|
616
|
+
(pPostError, pResponse, pBody) =>
|
|
617
|
+
{
|
|
618
|
+
Expect(pPostError).to.equal(null);
|
|
619
|
+
Expect(pResponse.statusCode).to.equal(200);
|
|
620
|
+
Expect(pResponse.headers['content-type']).to.equal('image/png');
|
|
621
|
+
|
|
622
|
+
// Verify the original route still produces output at the default 150 DPI
|
|
623
|
+
libSharp(pBody).metadata().then(
|
|
624
|
+
(pMetadata) =>
|
|
625
|
+
{
|
|
626
|
+
// The test PDF is 72x72 points; at 150 DPI that's 150 pixels
|
|
627
|
+
Expect(pMetadata.width).to.equal(150);
|
|
628
|
+
Expect(pMetadata.height).to.equal(150);
|
|
629
|
+
pHarness.orator.stopService(fDone);
|
|
630
|
+
}).catch(
|
|
631
|
+
(pMetaError) =>
|
|
632
|
+
{
|
|
633
|
+
pHarness.orator.stopService(() => { fDone(pMetaError); });
|
|
634
|
+
});
|
|
635
|
+
});
|
|
636
|
+
});
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
);
|
|
640
|
+
}
|
|
641
|
+
);
|
|
642
|
+
|
|
643
|
+
suite
|
|
644
|
+
(
|
|
645
|
+
'Custom Converter',
|
|
646
|
+
() =>
|
|
647
|
+
{
|
|
648
|
+
test
|
|
649
|
+
(
|
|
650
|
+
'should support custom converters added via addConverter',
|
|
651
|
+
(fDone) =>
|
|
652
|
+
{
|
|
653
|
+
let tmpFable = new libFable(defaultFableSettings);
|
|
654
|
+
|
|
655
|
+
tmpFable.serviceManager.addServiceType('OratorServiceServer', libOratorServiceServerRestify);
|
|
656
|
+
tmpFable.serviceManager.addServiceType('Orator', libOrator);
|
|
657
|
+
tmpFable.serviceManager.addServiceType('OratorFileTranslation', libOratorFileTranslation);
|
|
658
|
+
|
|
659
|
+
let tmpRestifyServer = tmpFable.serviceManager.instantiateServiceProvider('OratorServiceServer', {});
|
|
660
|
+
let tmpOrator = tmpFable.serviceManager.instantiateServiceProvider('Orator', {});
|
|
661
|
+
let tmpFileTranslation = tmpFable.serviceManager.instantiateServiceProvider('OratorFileTranslation', {});
|
|
662
|
+
|
|
663
|
+
// Add a custom echo converter before connecting routes
|
|
664
|
+
tmpFileTranslation.addConverter('custom/echo',
|
|
665
|
+
(pInputBuffer, pRequest, fCallback) =>
|
|
666
|
+
{
|
|
667
|
+
return fCallback(null, pInputBuffer, 'application/octet-stream');
|
|
668
|
+
});
|
|
669
|
+
|
|
670
|
+
tmpOrator.startService(
|
|
671
|
+
() =>
|
|
672
|
+
{
|
|
673
|
+
tmpFileTranslation.connectRoutes();
|
|
674
|
+
let tmpPort = tmpRestifyServer.server.address().port;
|
|
675
|
+
|
|
676
|
+
let tmpTestData = Buffer.from('hello world');
|
|
677
|
+
|
|
678
|
+
postBinaryToEndpoint(tmpPort, '/conversion/1.0/custom/echo', tmpTestData,
|
|
679
|
+
(pPostError, pResponse, pBody) =>
|
|
680
|
+
{
|
|
681
|
+
Expect(pPostError).to.equal(null);
|
|
682
|
+
Expect(pResponse.statusCode).to.equal(200);
|
|
683
|
+
Expect(pResponse.headers['content-type']).to.equal('application/octet-stream');
|
|
684
|
+
Expect(pBody.toString()).to.equal('hello world');
|
|
685
|
+
|
|
686
|
+
tmpOrator.stopService(fDone);
|
|
687
|
+
});
|
|
688
|
+
});
|
|
689
|
+
}
|
|
690
|
+
);
|
|
691
|
+
}
|
|
692
|
+
);
|
|
693
|
+
}
|
|
694
|
+
);
|