orator 5.0.0 → 5.0.2
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 +58 -81
- package/docs/.nojekyll +0 -0
- package/docs/README.md +142 -0
- package/docs/_sidebar.md +21 -0
- package/docs/architecture.md +92 -0
- package/docs/configuration.md +94 -0
- package/docs/cover.md +11 -0
- package/docs/getting-started.md +133 -0
- package/docs/http-proxy.md +45 -0
- package/docs/index.html +51 -0
- package/docs/ipc-server.md +123 -0
- package/docs/lifecycle-hooks.md +95 -0
- package/docs/restify-server.md +82 -0
- package/docs/service-servers.md +87 -0
- package/docs/static-files.md +75 -0
- package/package.json +7 -7
- package/source/Orator.js +46 -3
- package/test/Orator_complex_routes_tests.js +949 -0
- package/test/Orator_static_serving_tests.js +1322 -0
- package/test/static_content/about.html +1 -0
- package/test/static_content/data.json +1 -0
- package/test/static_content/index.html +1 -0
- package/test/static_content/style.css +1 -0
- package/test/static_content/subsite/index.html +1 -0
|
@@ -0,0 +1,1322 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for Orator Static File Serving
|
|
3
|
+
*
|
|
4
|
+
* @license MIT
|
|
5
|
+
*
|
|
6
|
+
* @author Steven Velozo <steven@velozo.com>
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const libOrator = require('../source/Orator.js');
|
|
10
|
+
|
|
11
|
+
const Chai = require("chai");
|
|
12
|
+
const Expect = Chai.expect;
|
|
13
|
+
const Assert = Chai.assert;
|
|
14
|
+
|
|
15
|
+
const libFable = require('fable');
|
|
16
|
+
const libPath = require('path');
|
|
17
|
+
const libHTTP = require('http');
|
|
18
|
+
|
|
19
|
+
const defaultFableSettings = (
|
|
20
|
+
{
|
|
21
|
+
Product:'Orator-StaticServingTests',
|
|
22
|
+
ProductVersion: '0.0.0',
|
|
23
|
+
APIServerPort: 0
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const _StaticContentPath = libPath.normalize(__dirname + '/static_content/');
|
|
27
|
+
|
|
28
|
+
// Port counter for test HTTP servers to avoid collisions
|
|
29
|
+
let _NextTestPort = 19100;
|
|
30
|
+
function getNextTestPort()
|
|
31
|
+
{
|
|
32
|
+
return _NextTestPort++;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Create a lightweight HTTP server that routes GET requests through Orator's IPC router.
|
|
37
|
+
* This allows testing static file serving (which requires a real HTTP response stream)
|
|
38
|
+
* without needing the orator-serviceserver-restify dependency.
|
|
39
|
+
*
|
|
40
|
+
* @param {Object} pOrator - The Orator instance with routes already registered.
|
|
41
|
+
* @param {number} pPort - The port to listen on.
|
|
42
|
+
* @param {Function} fCallback - Called with (server, port) when the server is ready.
|
|
43
|
+
*/
|
|
44
|
+
function createTestHTTPServer(pOrator, pPort, fCallback)
|
|
45
|
+
{
|
|
46
|
+
let tmpServer = libHTTP.createServer(
|
|
47
|
+
(pRequest, pResponse) =>
|
|
48
|
+
{
|
|
49
|
+
let tmpHandler = pOrator.serviceServer.router.find(pRequest.method, pRequest.url);
|
|
50
|
+
if (tmpHandler)
|
|
51
|
+
{
|
|
52
|
+
pRequest.params = tmpHandler.params || {};
|
|
53
|
+
pRequest.searchParams = tmpHandler.searchParams || {};
|
|
54
|
+
tmpHandler.handler(pRequest, pResponse, null);
|
|
55
|
+
}
|
|
56
|
+
else
|
|
57
|
+
{
|
|
58
|
+
pResponse.writeHead(404);
|
|
59
|
+
pResponse.end('Not found');
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
tmpServer.listen(pPort,
|
|
64
|
+
() =>
|
|
65
|
+
{
|
|
66
|
+
return fCallback(tmpServer, tmpServer.address().port);
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Make an HTTP GET request and collect the response.
|
|
72
|
+
*
|
|
73
|
+
* @param {number} pPort - Port to connect to.
|
|
74
|
+
* @param {string} pPath - URL path to request.
|
|
75
|
+
* @param {Function} fCallback - Called with (error, statusCode, headers, body).
|
|
76
|
+
* @param {Object} [pHeaders] - Optional extra headers for the request.
|
|
77
|
+
*/
|
|
78
|
+
function makeRequest(pPort, pPath, fCallback, pHeaders)
|
|
79
|
+
{
|
|
80
|
+
let tmpOptions = (
|
|
81
|
+
{
|
|
82
|
+
hostname: 'localhost',
|
|
83
|
+
port: pPort,
|
|
84
|
+
path: pPath,
|
|
85
|
+
method: 'GET',
|
|
86
|
+
headers: Object.assign({}, pHeaders || {})
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
let tmpRequest = libHTTP.request(tmpOptions,
|
|
90
|
+
(pResponse) =>
|
|
91
|
+
{
|
|
92
|
+
let tmpData = '';
|
|
93
|
+
pResponse.on('data',
|
|
94
|
+
(pChunk) =>
|
|
95
|
+
{
|
|
96
|
+
tmpData += pChunk;
|
|
97
|
+
});
|
|
98
|
+
pResponse.on('end',
|
|
99
|
+
() =>
|
|
100
|
+
{
|
|
101
|
+
return fCallback(null, pResponse.statusCode, pResponse.headers, tmpData);
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
tmpRequest.on('error',
|
|
106
|
+
(pError) =>
|
|
107
|
+
{
|
|
108
|
+
return fCallback(pError);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
tmpRequest.end();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
suite
|
|
115
|
+
(
|
|
116
|
+
'Orator',
|
|
117
|
+
() =>
|
|
118
|
+
{
|
|
119
|
+
suite
|
|
120
|
+
(
|
|
121
|
+
'Static File Serving - Parameter Validation',
|
|
122
|
+
() =>
|
|
123
|
+
{
|
|
124
|
+
test
|
|
125
|
+
(
|
|
126
|
+
'addStaticRoute should return false when no file path is provided',
|
|
127
|
+
(fDone) =>
|
|
128
|
+
{
|
|
129
|
+
let tmpFable = new libFable(defaultFableSettings);
|
|
130
|
+
tmpFable.serviceManager.addServiceType('Orator', libOrator);
|
|
131
|
+
let tmpOrator = tmpFable.serviceManager.instantiateServiceProvider('Orator', {});
|
|
132
|
+
tmpOrator.initialize(
|
|
133
|
+
() =>
|
|
134
|
+
{
|
|
135
|
+
let tmpResult = tmpOrator.addStaticRoute();
|
|
136
|
+
Expect(tmpResult).to.equal(false);
|
|
137
|
+
tmpOrator.log.info('addStaticRoute correctly rejected missing file path');
|
|
138
|
+
return fDone();
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
test
|
|
144
|
+
(
|
|
145
|
+
'addStaticRoute should reject non-string file paths',
|
|
146
|
+
(fDone) =>
|
|
147
|
+
{
|
|
148
|
+
let tmpFable = new libFable(defaultFableSettings);
|
|
149
|
+
tmpFable.serviceManager.addServiceType('Orator', libOrator);
|
|
150
|
+
let tmpOrator = tmpFable.serviceManager.instantiateServiceProvider('Orator', {});
|
|
151
|
+
tmpOrator.initialize(
|
|
152
|
+
() =>
|
|
153
|
+
{
|
|
154
|
+
Expect(tmpOrator.addStaticRoute(42)).to.equal(false);
|
|
155
|
+
Expect(tmpOrator.addStaticRoute(null)).to.equal(false);
|
|
156
|
+
Expect(tmpOrator.addStaticRoute({})).to.equal(false);
|
|
157
|
+
Expect(tmpOrator.addStaticRoute(true)).to.equal(false);
|
|
158
|
+
Expect(tmpOrator.addStaticRoute(undefined)).to.equal(false);
|
|
159
|
+
Expect(tmpOrator.addStaticRoute([])).to.equal(false);
|
|
160
|
+
tmpOrator.log.info('addStaticRoute rejected all non-string file paths');
|
|
161
|
+
return fDone();
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
test
|
|
167
|
+
(
|
|
168
|
+
'addStaticRoute should return true with a valid file path',
|
|
169
|
+
(fDone) =>
|
|
170
|
+
{
|
|
171
|
+
let tmpFable = new libFable(defaultFableSettings);
|
|
172
|
+
tmpFable.serviceManager.addServiceType('Orator', libOrator);
|
|
173
|
+
let tmpOrator = tmpFable.serviceManager.instantiateServiceProvider('Orator', {});
|
|
174
|
+
tmpOrator.initialize(
|
|
175
|
+
() =>
|
|
176
|
+
{
|
|
177
|
+
let tmpResult = tmpOrator.addStaticRoute(_StaticContentPath);
|
|
178
|
+
Expect(tmpResult).to.equal(true);
|
|
179
|
+
tmpOrator.log.info(`addStaticRoute mapped [${_StaticContentPath}] successfully`);
|
|
180
|
+
return fDone();
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
test
|
|
186
|
+
(
|
|
187
|
+
'addStaticRoute should accept all optional parameters',
|
|
188
|
+
(fDone) =>
|
|
189
|
+
{
|
|
190
|
+
let tmpFable = new libFable(defaultFableSettings);
|
|
191
|
+
tmpFable.serviceManager.addServiceType('Orator', libOrator);
|
|
192
|
+
let tmpOrator = tmpFable.serviceManager.instantiateServiceProvider('Orator', {});
|
|
193
|
+
tmpOrator.initialize(
|
|
194
|
+
() =>
|
|
195
|
+
{
|
|
196
|
+
let tmpResult = tmpOrator.addStaticRoute(_StaticContentPath, 'about.html', '/content/*', '/content/', {maxAge: '1d'});
|
|
197
|
+
Expect(tmpResult).to.equal(true);
|
|
198
|
+
tmpOrator.log.info('addStaticRoute accepted all optional parameters');
|
|
199
|
+
return fDone();
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
test
|
|
205
|
+
(
|
|
206
|
+
'addStaticRoute should use default route /* when no route is specified',
|
|
207
|
+
(fDone) =>
|
|
208
|
+
{
|
|
209
|
+
let tmpFable = new libFable(defaultFableSettings);
|
|
210
|
+
tmpFable.serviceManager.addServiceType('Orator', libOrator);
|
|
211
|
+
let tmpOrator = tmpFable.serviceManager.instantiateServiceProvider('Orator', {});
|
|
212
|
+
tmpOrator.startService();
|
|
213
|
+
|
|
214
|
+
let tmpResult = tmpOrator.addStaticRoute(_StaticContentPath);
|
|
215
|
+
Expect(tmpResult).to.equal(true);
|
|
216
|
+
|
|
217
|
+
// Verify the wildcard route is registered by checking the router
|
|
218
|
+
let tmpHandler = tmpOrator.serviceServer.router.find('GET', '/anything');
|
|
219
|
+
Expect(tmpHandler).to.not.equal(null);
|
|
220
|
+
Expect(tmpHandler.handler).to.be.a('function');
|
|
221
|
+
tmpOrator.log.info('Default wildcard route /* was registered');
|
|
222
|
+
return fDone();
|
|
223
|
+
}
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
test
|
|
227
|
+
(
|
|
228
|
+
'addStaticRoute should register a GET route on the service server',
|
|
229
|
+
(fDone) =>
|
|
230
|
+
{
|
|
231
|
+
let tmpFable = new libFable(defaultFableSettings);
|
|
232
|
+
tmpFable.serviceManager.addServiceType('Orator', libOrator);
|
|
233
|
+
let tmpOrator = tmpFable.serviceManager.instantiateServiceProvider('Orator', {});
|
|
234
|
+
tmpOrator.startService();
|
|
235
|
+
|
|
236
|
+
let tmpResult = tmpOrator.addStaticRoute(_StaticContentPath, 'index.html', '/static/*', '/static/');
|
|
237
|
+
Expect(tmpResult).to.equal(true);
|
|
238
|
+
|
|
239
|
+
// The route should match paths under /static/
|
|
240
|
+
let tmpHandler = tmpOrator.serviceServer.router.find('GET', '/static/test.html');
|
|
241
|
+
Expect(tmpHandler).to.not.equal(null);
|
|
242
|
+
Expect(tmpHandler.handler).to.be.a('function');
|
|
243
|
+
|
|
244
|
+
tmpOrator.log.info('Custom route /static/* was registered');
|
|
245
|
+
return fDone();
|
|
246
|
+
}
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
);
|
|
250
|
+
|
|
251
|
+
suite
|
|
252
|
+
(
|
|
253
|
+
'Static File Serving - MIME Type Detection',
|
|
254
|
+
() =>
|
|
255
|
+
{
|
|
256
|
+
test
|
|
257
|
+
(
|
|
258
|
+
'setMimeHeader should set Content-Type for HTML files',
|
|
259
|
+
(fDone) =>
|
|
260
|
+
{
|
|
261
|
+
let tmpFable = new libFable(defaultFableSettings);
|
|
262
|
+
tmpFable.serviceManager.addServiceType('Orator', libOrator);
|
|
263
|
+
let tmpOrator = tmpFable.serviceManager.instantiateServiceProvider('Orator', {});
|
|
264
|
+
tmpOrator.initialize(
|
|
265
|
+
() =>
|
|
266
|
+
{
|
|
267
|
+
let tmpCapturedHeaders = {};
|
|
268
|
+
let tmpMockResponse = { setHeader: function(pName, pValue) { tmpCapturedHeaders[pName] = pValue; } };
|
|
269
|
+
|
|
270
|
+
tmpOrator.setMimeHeader('index.html', tmpMockResponse);
|
|
271
|
+
Expect(tmpCapturedHeaders['Content-Type']).to.equal('text/html');
|
|
272
|
+
|
|
273
|
+
tmpOrator.setMimeHeader('/path/to/page.html', tmpMockResponse);
|
|
274
|
+
Expect(tmpCapturedHeaders['Content-Type']).to.equal('text/html');
|
|
275
|
+
|
|
276
|
+
tmpOrator.log.info('HTML MIME type correctly detected');
|
|
277
|
+
return fDone();
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
);
|
|
281
|
+
|
|
282
|
+
test
|
|
283
|
+
(
|
|
284
|
+
'setMimeHeader should set Content-Type for CSS files',
|
|
285
|
+
(fDone) =>
|
|
286
|
+
{
|
|
287
|
+
let tmpFable = new libFable(defaultFableSettings);
|
|
288
|
+
tmpFable.serviceManager.addServiceType('Orator', libOrator);
|
|
289
|
+
let tmpOrator = tmpFable.serviceManager.instantiateServiceProvider('Orator', {});
|
|
290
|
+
tmpOrator.initialize(
|
|
291
|
+
() =>
|
|
292
|
+
{
|
|
293
|
+
let tmpCapturedHeaders = {};
|
|
294
|
+
let tmpMockResponse = { setHeader: function(pName, pValue) { tmpCapturedHeaders[pName] = pValue; } };
|
|
295
|
+
|
|
296
|
+
tmpOrator.setMimeHeader('style.css', tmpMockResponse);
|
|
297
|
+
Expect(tmpCapturedHeaders['Content-Type']).to.equal('text/css');
|
|
298
|
+
|
|
299
|
+
tmpOrator.log.info('CSS MIME type correctly detected');
|
|
300
|
+
return fDone();
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
);
|
|
304
|
+
|
|
305
|
+
test
|
|
306
|
+
(
|
|
307
|
+
'setMimeHeader should set Content-Type for JSON files',
|
|
308
|
+
(fDone) =>
|
|
309
|
+
{
|
|
310
|
+
let tmpFable = new libFable(defaultFableSettings);
|
|
311
|
+
tmpFable.serviceManager.addServiceType('Orator', libOrator);
|
|
312
|
+
let tmpOrator = tmpFable.serviceManager.instantiateServiceProvider('Orator', {});
|
|
313
|
+
tmpOrator.initialize(
|
|
314
|
+
() =>
|
|
315
|
+
{
|
|
316
|
+
let tmpCapturedHeaders = {};
|
|
317
|
+
let tmpMockResponse = { setHeader: function(pName, pValue) { tmpCapturedHeaders[pName] = pValue; } };
|
|
318
|
+
|
|
319
|
+
tmpOrator.setMimeHeader('data.json', tmpMockResponse);
|
|
320
|
+
Expect(tmpCapturedHeaders['Content-Type']).to.equal('application/json');
|
|
321
|
+
|
|
322
|
+
tmpOrator.log.info('JSON MIME type correctly detected');
|
|
323
|
+
return fDone();
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
);
|
|
327
|
+
|
|
328
|
+
test
|
|
329
|
+
(
|
|
330
|
+
'setMimeHeader should set Content-Type for JavaScript files',
|
|
331
|
+
(fDone) =>
|
|
332
|
+
{
|
|
333
|
+
let tmpFable = new libFable(defaultFableSettings);
|
|
334
|
+
tmpFable.serviceManager.addServiceType('Orator', libOrator);
|
|
335
|
+
let tmpOrator = tmpFable.serviceManager.instantiateServiceProvider('Orator', {});
|
|
336
|
+
tmpOrator.initialize(
|
|
337
|
+
() =>
|
|
338
|
+
{
|
|
339
|
+
let tmpCapturedHeaders = {};
|
|
340
|
+
let tmpMockResponse = { setHeader: function(pName, pValue) { tmpCapturedHeaders[pName] = pValue; } };
|
|
341
|
+
|
|
342
|
+
tmpOrator.setMimeHeader('app.js', tmpMockResponse);
|
|
343
|
+
Expect(tmpCapturedHeaders['Content-Type']).to.equal('application/javascript');
|
|
344
|
+
|
|
345
|
+
tmpOrator.log.info('JavaScript MIME type correctly detected');
|
|
346
|
+
return fDone();
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
);
|
|
350
|
+
|
|
351
|
+
test
|
|
352
|
+
(
|
|
353
|
+
'setMimeHeader should set Content-Type for image files',
|
|
354
|
+
(fDone) =>
|
|
355
|
+
{
|
|
356
|
+
let tmpFable = new libFable(defaultFableSettings);
|
|
357
|
+
tmpFable.serviceManager.addServiceType('Orator', libOrator);
|
|
358
|
+
let tmpOrator = tmpFable.serviceManager.instantiateServiceProvider('Orator', {});
|
|
359
|
+
tmpOrator.initialize(
|
|
360
|
+
() =>
|
|
361
|
+
{
|
|
362
|
+
let tmpCapturedHeaders = {};
|
|
363
|
+
let tmpMockResponse = { setHeader: function(pName, pValue) { tmpCapturedHeaders[pName] = pValue; } };
|
|
364
|
+
|
|
365
|
+
tmpOrator.setMimeHeader('photo.png', tmpMockResponse);
|
|
366
|
+
Expect(tmpCapturedHeaders['Content-Type']).to.equal('image/png');
|
|
367
|
+
|
|
368
|
+
tmpOrator.setMimeHeader('logo.jpg', tmpMockResponse);
|
|
369
|
+
Expect(tmpCapturedHeaders['Content-Type']).to.equal('image/jpeg');
|
|
370
|
+
|
|
371
|
+
tmpOrator.setMimeHeader('icon.gif', tmpMockResponse);
|
|
372
|
+
Expect(tmpCapturedHeaders['Content-Type']).to.equal('image/gif');
|
|
373
|
+
|
|
374
|
+
tmpOrator.setMimeHeader('vector.svg', tmpMockResponse);
|
|
375
|
+
Expect(tmpCapturedHeaders['Content-Type']).to.equal('image/svg+xml');
|
|
376
|
+
|
|
377
|
+
tmpOrator.log.info('Image MIME types correctly detected');
|
|
378
|
+
return fDone();
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
);
|
|
382
|
+
|
|
383
|
+
test
|
|
384
|
+
(
|
|
385
|
+
'setMimeHeader should set Content-Type for common web font and document types',
|
|
386
|
+
(fDone) =>
|
|
387
|
+
{
|
|
388
|
+
let tmpFable = new libFable(defaultFableSettings);
|
|
389
|
+
tmpFable.serviceManager.addServiceType('Orator', libOrator);
|
|
390
|
+
let tmpOrator = tmpFable.serviceManager.instantiateServiceProvider('Orator', {});
|
|
391
|
+
tmpOrator.initialize(
|
|
392
|
+
() =>
|
|
393
|
+
{
|
|
394
|
+
let tmpCapturedHeaders = {};
|
|
395
|
+
let tmpMockResponse = { setHeader: function(pName, pValue) { tmpCapturedHeaders[pName] = pValue; } };
|
|
396
|
+
|
|
397
|
+
tmpOrator.setMimeHeader('document.pdf', tmpMockResponse);
|
|
398
|
+
Expect(tmpCapturedHeaders['Content-Type']).to.equal('application/pdf');
|
|
399
|
+
|
|
400
|
+
tmpOrator.setMimeHeader('archive.zip', tmpMockResponse);
|
|
401
|
+
Expect(tmpCapturedHeaders['Content-Type']).to.equal('application/zip');
|
|
402
|
+
|
|
403
|
+
tmpOrator.setMimeHeader('data.xml', tmpMockResponse);
|
|
404
|
+
Expect(tmpCapturedHeaders['Content-Type']).to.equal('application/xml');
|
|
405
|
+
|
|
406
|
+
tmpOrator.setMimeHeader('readme.txt', tmpMockResponse);
|
|
407
|
+
Expect(tmpCapturedHeaders['Content-Type']).to.equal('text/plain');
|
|
408
|
+
|
|
409
|
+
tmpOrator.log.info('Document and archive MIME types correctly detected');
|
|
410
|
+
return fDone();
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
);
|
|
414
|
+
|
|
415
|
+
test
|
|
416
|
+
(
|
|
417
|
+
'setMimeHeader should fall back to application/octet-stream for unknown extensions',
|
|
418
|
+
(fDone) =>
|
|
419
|
+
{
|
|
420
|
+
let tmpFable = new libFable(defaultFableSettings);
|
|
421
|
+
tmpFable.serviceManager.addServiceType('Orator', libOrator);
|
|
422
|
+
let tmpOrator = tmpFable.serviceManager.instantiateServiceProvider('Orator', {});
|
|
423
|
+
tmpOrator.initialize(
|
|
424
|
+
() =>
|
|
425
|
+
{
|
|
426
|
+
let tmpCapturedHeaders = {};
|
|
427
|
+
let tmpMockResponse = { setHeader: function(pName, pValue) { tmpCapturedHeaders[pName] = pValue; } };
|
|
428
|
+
|
|
429
|
+
tmpOrator.setMimeHeader('mystery.xyz123', tmpMockResponse);
|
|
430
|
+
Expect(tmpCapturedHeaders['Content-Type']).to.equal('application/octet-stream');
|
|
431
|
+
|
|
432
|
+
tmpOrator.setMimeHeader('noextension', tmpMockResponse);
|
|
433
|
+
Expect(tmpCapturedHeaders['Content-Type']).to.equal('application/octet-stream');
|
|
434
|
+
|
|
435
|
+
tmpOrator.log.info('Unknown MIME types fall back to application/octet-stream');
|
|
436
|
+
return fDone();
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
);
|
|
440
|
+
|
|
441
|
+
test
|
|
442
|
+
(
|
|
443
|
+
'setMimeHeader should handle file paths with directories',
|
|
444
|
+
(fDone) =>
|
|
445
|
+
{
|
|
446
|
+
let tmpFable = new libFable(defaultFableSettings);
|
|
447
|
+
tmpFable.serviceManager.addServiceType('Orator', libOrator);
|
|
448
|
+
let tmpOrator = tmpFable.serviceManager.instantiateServiceProvider('Orator', {});
|
|
449
|
+
tmpOrator.initialize(
|
|
450
|
+
() =>
|
|
451
|
+
{
|
|
452
|
+
let tmpCapturedHeaders = {};
|
|
453
|
+
let tmpMockResponse = { setHeader: function(pName, pValue) { tmpCapturedHeaders[pName] = pValue; } };
|
|
454
|
+
|
|
455
|
+
tmpOrator.setMimeHeader('/assets/css/main.css', tmpMockResponse);
|
|
456
|
+
Expect(tmpCapturedHeaders['Content-Type']).to.equal('text/css');
|
|
457
|
+
|
|
458
|
+
tmpOrator.setMimeHeader('/deep/nested/path/to/image.png', tmpMockResponse);
|
|
459
|
+
Expect(tmpCapturedHeaders['Content-Type']).to.equal('image/png');
|
|
460
|
+
|
|
461
|
+
tmpOrator.log.info('MIME type detected correctly from paths with directories');
|
|
462
|
+
return fDone();
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
);
|
|
466
|
+
}
|
|
467
|
+
);
|
|
468
|
+
|
|
469
|
+
suite
|
|
470
|
+
(
|
|
471
|
+
'Static File Serving - Serving HTML Files',
|
|
472
|
+
() =>
|
|
473
|
+
{
|
|
474
|
+
test
|
|
475
|
+
(
|
|
476
|
+
'should serve index.html with correct content and Content-Type',
|
|
477
|
+
(fDone) =>
|
|
478
|
+
{
|
|
479
|
+
let tmpFable = new libFable(defaultFableSettings);
|
|
480
|
+
tmpFable.serviceManager.addServiceType('Orator', libOrator);
|
|
481
|
+
let tmpOrator = tmpFable.serviceManager.instantiateServiceProvider('Orator', {});
|
|
482
|
+
tmpOrator.startService();
|
|
483
|
+
tmpOrator.addStaticRoute(_StaticContentPath);
|
|
484
|
+
|
|
485
|
+
let tmpPort = getNextTestPort();
|
|
486
|
+
createTestHTTPServer(tmpOrator, tmpPort,
|
|
487
|
+
(pServer, pActualPort) =>
|
|
488
|
+
{
|
|
489
|
+
makeRequest(pActualPort, '/index.html',
|
|
490
|
+
(pError, pStatusCode, pHeaders, pBody) =>
|
|
491
|
+
{
|
|
492
|
+
Expect(pError).to.equal(null);
|
|
493
|
+
Expect(pStatusCode).to.equal(200);
|
|
494
|
+
Expect(pHeaders['content-type']).to.contain('text/html');
|
|
495
|
+
Expect(pBody).to.contain('Test Index');
|
|
496
|
+
Expect(pBody).to.contain('Welcome to the test server');
|
|
497
|
+
tmpOrator.log.info(`Served index.html: status=${pStatusCode} content-type=${pHeaders['content-type']}`);
|
|
498
|
+
pServer.close();
|
|
499
|
+
return fDone();
|
|
500
|
+
});
|
|
501
|
+
});
|
|
502
|
+
}
|
|
503
|
+
);
|
|
504
|
+
|
|
505
|
+
test
|
|
506
|
+
(
|
|
507
|
+
'should serve about.html with correct content',
|
|
508
|
+
(fDone) =>
|
|
509
|
+
{
|
|
510
|
+
let tmpFable = new libFable(defaultFableSettings);
|
|
511
|
+
tmpFable.serviceManager.addServiceType('Orator', libOrator);
|
|
512
|
+
let tmpOrator = tmpFable.serviceManager.instantiateServiceProvider('Orator', {});
|
|
513
|
+
tmpOrator.startService();
|
|
514
|
+
tmpOrator.addStaticRoute(_StaticContentPath);
|
|
515
|
+
|
|
516
|
+
let tmpPort = getNextTestPort();
|
|
517
|
+
createTestHTTPServer(tmpOrator, tmpPort,
|
|
518
|
+
(pServer, pActualPort) =>
|
|
519
|
+
{
|
|
520
|
+
makeRequest(pActualPort, '/about.html',
|
|
521
|
+
(pError, pStatusCode, pHeaders, pBody) =>
|
|
522
|
+
{
|
|
523
|
+
Expect(pError).to.equal(null);
|
|
524
|
+
Expect(pStatusCode).to.equal(200);
|
|
525
|
+
Expect(pHeaders['content-type']).to.contain('text/html');
|
|
526
|
+
Expect(pBody).to.contain('About');
|
|
527
|
+
Expect(pBody).to.contain('About page content');
|
|
528
|
+
tmpOrator.log.info(`Served about.html: status=${pStatusCode}`);
|
|
529
|
+
pServer.close();
|
|
530
|
+
return fDone();
|
|
531
|
+
});
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
);
|
|
535
|
+
|
|
536
|
+
test
|
|
537
|
+
(
|
|
538
|
+
'should serve default file (index.html) when requesting the root path',
|
|
539
|
+
(fDone) =>
|
|
540
|
+
{
|
|
541
|
+
let tmpFable = new libFable(defaultFableSettings);
|
|
542
|
+
tmpFable.serviceManager.addServiceType('Orator', libOrator);
|
|
543
|
+
let tmpOrator = tmpFable.serviceManager.instantiateServiceProvider('Orator', {});
|
|
544
|
+
tmpOrator.startService();
|
|
545
|
+
tmpOrator.addStaticRoute(_StaticContentPath);
|
|
546
|
+
|
|
547
|
+
let tmpPort = getNextTestPort();
|
|
548
|
+
createTestHTTPServer(tmpOrator, tmpPort,
|
|
549
|
+
(pServer, pActualPort) =>
|
|
550
|
+
{
|
|
551
|
+
makeRequest(pActualPort, '/',
|
|
552
|
+
(pError, pStatusCode, pHeaders, pBody) =>
|
|
553
|
+
{
|
|
554
|
+
Expect(pError).to.equal(null);
|
|
555
|
+
Expect(pStatusCode).to.equal(200);
|
|
556
|
+
// The Content-Type should be text/html even when requesting '/' (no extension)
|
|
557
|
+
Expect(pHeaders['content-type']).to.contain('text/html');
|
|
558
|
+
Expect(pBody).to.contain('Test Index');
|
|
559
|
+
tmpOrator.log.info(`Root path served default index.html: status=${pStatusCode} content-type=${pHeaders['content-type']}`);
|
|
560
|
+
pServer.close();
|
|
561
|
+
return fDone();
|
|
562
|
+
});
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
);
|
|
566
|
+
|
|
567
|
+
test
|
|
568
|
+
(
|
|
569
|
+
'should serve a custom default file when specified',
|
|
570
|
+
(fDone) =>
|
|
571
|
+
{
|
|
572
|
+
let tmpFable = new libFable(defaultFableSettings);
|
|
573
|
+
tmpFable.serviceManager.addServiceType('Orator', libOrator);
|
|
574
|
+
let tmpOrator = tmpFable.serviceManager.instantiateServiceProvider('Orator', {});
|
|
575
|
+
tmpOrator.startService();
|
|
576
|
+
// Set about.html as the default file
|
|
577
|
+
tmpOrator.addStaticRoute(_StaticContentPath, 'about.html');
|
|
578
|
+
|
|
579
|
+
let tmpPort = getNextTestPort();
|
|
580
|
+
createTestHTTPServer(tmpOrator, tmpPort,
|
|
581
|
+
(pServer, pActualPort) =>
|
|
582
|
+
{
|
|
583
|
+
makeRequest(pActualPort, '/',
|
|
584
|
+
(pError, pStatusCode, pHeaders, pBody) =>
|
|
585
|
+
{
|
|
586
|
+
Expect(pError).to.equal(null);
|
|
587
|
+
Expect(pStatusCode).to.equal(200);
|
|
588
|
+
// Even with a custom default file, Content-Type should be text/html
|
|
589
|
+
Expect(pHeaders['content-type']).to.contain('text/html');
|
|
590
|
+
Expect(pBody).to.contain('About page content');
|
|
591
|
+
tmpOrator.log.info(`Custom default file about.html served: status=${pStatusCode} content-type=${pHeaders['content-type']}`);
|
|
592
|
+
pServer.close();
|
|
593
|
+
return fDone();
|
|
594
|
+
});
|
|
595
|
+
});
|
|
596
|
+
}
|
|
597
|
+
);
|
|
598
|
+
}
|
|
599
|
+
);
|
|
600
|
+
|
|
601
|
+
suite
|
|
602
|
+
(
|
|
603
|
+
'Static File Serving - Directory MIME Type Resolution',
|
|
604
|
+
() =>
|
|
605
|
+
{
|
|
606
|
+
test
|
|
607
|
+
(
|
|
608
|
+
'should set text/html Content-Type for directory paths with html default file',
|
|
609
|
+
(fDone) =>
|
|
610
|
+
{
|
|
611
|
+
let tmpFable = new libFable(defaultFableSettings);
|
|
612
|
+
tmpFable.serviceManager.addServiceType('Orator', libOrator);
|
|
613
|
+
let tmpOrator = tmpFable.serviceManager.instantiateServiceProvider('Orator', {});
|
|
614
|
+
tmpOrator.startService();
|
|
615
|
+
tmpOrator.addStaticRoute(_StaticContentPath, 'index.html', '/site/*', '/site/');
|
|
616
|
+
|
|
617
|
+
let tmpPort = getNextTestPort();
|
|
618
|
+
createTestHTTPServer(tmpOrator, tmpPort,
|
|
619
|
+
(pServer, pActualPort) =>
|
|
620
|
+
{
|
|
621
|
+
makeRequest(pActualPort, '/site/',
|
|
622
|
+
(pError, pStatusCode, pHeaders, pBody) =>
|
|
623
|
+
{
|
|
624
|
+
Expect(pError).to.equal(null);
|
|
625
|
+
Expect(pStatusCode).to.equal(200);
|
|
626
|
+
// Directory path should resolve MIME from default file, not the bare '/'
|
|
627
|
+
Expect(pHeaders['content-type']).to.contain('text/html');
|
|
628
|
+
Expect(pBody).to.contain('Test Index');
|
|
629
|
+
tmpOrator.log.info(`Directory path MIME resolved to text/html from default file`);
|
|
630
|
+
pServer.close();
|
|
631
|
+
return fDone();
|
|
632
|
+
});
|
|
633
|
+
});
|
|
634
|
+
}
|
|
635
|
+
);
|
|
636
|
+
|
|
637
|
+
test
|
|
638
|
+
(
|
|
639
|
+
'should set correct Content-Type for extensionless paths based on default file',
|
|
640
|
+
(fDone) =>
|
|
641
|
+
{
|
|
642
|
+
let tmpFable = new libFable(defaultFableSettings);
|
|
643
|
+
tmpFable.serviceManager.addServiceType('Orator', libOrator);
|
|
644
|
+
let tmpOrator = tmpFable.serviceManager.instantiateServiceProvider('Orator', {});
|
|
645
|
+
tmpOrator.initialize(
|
|
646
|
+
() =>
|
|
647
|
+
{
|
|
648
|
+
// Test the MIME detection logic directly with a mock response
|
|
649
|
+
let tmpCapturedHeaders = {};
|
|
650
|
+
let tmpMockResponse = { setHeader: function(pName, pValue) { tmpCapturedHeaders[pName] = pValue; } };
|
|
651
|
+
|
|
652
|
+
// A URL like '/' has no extension, so should fall back to default
|
|
653
|
+
// This simulates what happens in addStaticRoute after the fix
|
|
654
|
+
let tmpUrl = '/';
|
|
655
|
+
let tmpMimeTarget = tmpUrl;
|
|
656
|
+
if (tmpMimeTarget.endsWith('/') || tmpMimeTarget.indexOf('.') < 0)
|
|
657
|
+
{
|
|
658
|
+
tmpMimeTarget = 'index.html';
|
|
659
|
+
}
|
|
660
|
+
tmpOrator.setMimeHeader(tmpMimeTarget, tmpMockResponse);
|
|
661
|
+
Expect(tmpCapturedHeaders['Content-Type']).to.equal('text/html');
|
|
662
|
+
|
|
663
|
+
// A URL like '/somepath' (no extension, no slash) should also fall back
|
|
664
|
+
tmpUrl = '/somepath';
|
|
665
|
+
tmpMimeTarget = tmpUrl;
|
|
666
|
+
if (tmpMimeTarget.endsWith('/') || tmpMimeTarget.indexOf('.') < 0)
|
|
667
|
+
{
|
|
668
|
+
tmpMimeTarget = 'index.html';
|
|
669
|
+
}
|
|
670
|
+
tmpOrator.setMimeHeader(tmpMimeTarget, tmpMockResponse);
|
|
671
|
+
Expect(tmpCapturedHeaders['Content-Type']).to.equal('text/html');
|
|
672
|
+
|
|
673
|
+
// A URL with an extension should use its own extension
|
|
674
|
+
tmpUrl = '/style.css';
|
|
675
|
+
tmpMimeTarget = tmpUrl;
|
|
676
|
+
if (tmpMimeTarget.endsWith('/') || tmpMimeTarget.indexOf('.') < 0)
|
|
677
|
+
{
|
|
678
|
+
tmpMimeTarget = 'index.html';
|
|
679
|
+
}
|
|
680
|
+
tmpOrator.setMimeHeader(tmpMimeTarget, tmpMockResponse);
|
|
681
|
+
Expect(tmpCapturedHeaders['Content-Type']).to.equal('text/css');
|
|
682
|
+
|
|
683
|
+
tmpOrator.log.info('Directory and extensionless MIME detection verified');
|
|
684
|
+
return fDone();
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
);
|
|
688
|
+
}
|
|
689
|
+
);
|
|
690
|
+
|
|
691
|
+
suite
|
|
692
|
+
(
|
|
693
|
+
'Static File Serving - FilePersistence Auto-Instantiation',
|
|
694
|
+
() =>
|
|
695
|
+
{
|
|
696
|
+
test
|
|
697
|
+
(
|
|
698
|
+
'Orator constructor should auto-instantiate FilePersistence if not present',
|
|
699
|
+
(fDone) =>
|
|
700
|
+
{
|
|
701
|
+
let tmpFable = new libFable(defaultFableSettings);
|
|
702
|
+
// Verify FilePersistence is NOT yet on fable before Orator construction
|
|
703
|
+
Expect(tmpFable.FilePersistence).to.equal(undefined);
|
|
704
|
+
|
|
705
|
+
tmpFable.serviceManager.addServiceType('Orator', libOrator);
|
|
706
|
+
let tmpOrator = tmpFable.serviceManager.instantiateServiceProvider('Orator', {});
|
|
707
|
+
|
|
708
|
+
// After constructing Orator, FilePersistence should now be available
|
|
709
|
+
Expect(tmpFable.FilePersistence).to.be.an('object');
|
|
710
|
+
Expect(tmpFable.FilePersistence.libFS).to.be.an('object');
|
|
711
|
+
Expect(tmpFable.FilePersistence.libFS.existsSync).to.be.a('function');
|
|
712
|
+
tmpOrator.log.info('FilePersistence auto-instantiated by Orator constructor');
|
|
713
|
+
return fDone();
|
|
714
|
+
}
|
|
715
|
+
);
|
|
716
|
+
|
|
717
|
+
test
|
|
718
|
+
(
|
|
719
|
+
'Orator constructor should not re-instantiate FilePersistence if already present',
|
|
720
|
+
(fDone) =>
|
|
721
|
+
{
|
|
722
|
+
let tmpFable = new libFable(defaultFableSettings);
|
|
723
|
+
// Pre-instantiate FilePersistence
|
|
724
|
+
let tmpOriginal = tmpFable.serviceManager.instantiateServiceProvider('FilePersistence');
|
|
725
|
+
Expect(tmpFable.FilePersistence).to.equal(tmpOriginal);
|
|
726
|
+
|
|
727
|
+
tmpFable.serviceManager.addServiceType('Orator', libOrator);
|
|
728
|
+
let tmpOrator = tmpFable.serviceManager.instantiateServiceProvider('Orator', {});
|
|
729
|
+
|
|
730
|
+
// Should still be the same instance, not a new one
|
|
731
|
+
Expect(tmpFable.FilePersistence).to.equal(tmpOriginal);
|
|
732
|
+
tmpOrator.log.info('FilePersistence preserved when already present');
|
|
733
|
+
return fDone();
|
|
734
|
+
}
|
|
735
|
+
);
|
|
736
|
+
}
|
|
737
|
+
);
|
|
738
|
+
|
|
739
|
+
suite
|
|
740
|
+
(
|
|
741
|
+
'Static File Serving - CSS and JSON Files',
|
|
742
|
+
() =>
|
|
743
|
+
{
|
|
744
|
+
test
|
|
745
|
+
(
|
|
746
|
+
'should serve CSS files with correct Content-Type',
|
|
747
|
+
(fDone) =>
|
|
748
|
+
{
|
|
749
|
+
let tmpFable = new libFable(defaultFableSettings);
|
|
750
|
+
tmpFable.serviceManager.addServiceType('Orator', libOrator);
|
|
751
|
+
let tmpOrator = tmpFable.serviceManager.instantiateServiceProvider('Orator', {});
|
|
752
|
+
tmpOrator.startService();
|
|
753
|
+
tmpOrator.addStaticRoute(_StaticContentPath);
|
|
754
|
+
|
|
755
|
+
let tmpPort = getNextTestPort();
|
|
756
|
+
createTestHTTPServer(tmpOrator, tmpPort,
|
|
757
|
+
(pServer, pActualPort) =>
|
|
758
|
+
{
|
|
759
|
+
makeRequest(pActualPort, '/style.css',
|
|
760
|
+
(pError, pStatusCode, pHeaders, pBody) =>
|
|
761
|
+
{
|
|
762
|
+
Expect(pError).to.equal(null);
|
|
763
|
+
Expect(pStatusCode).to.equal(200);
|
|
764
|
+
Expect(pHeaders['content-type']).to.contain('text/css');
|
|
765
|
+
Expect(pBody).to.contain('font-family');
|
|
766
|
+
Expect(pBody).to.contain('sans-serif');
|
|
767
|
+
tmpOrator.log.info(`Served style.css: content-type=${pHeaders['content-type']}`);
|
|
768
|
+
pServer.close();
|
|
769
|
+
return fDone();
|
|
770
|
+
});
|
|
771
|
+
});
|
|
772
|
+
}
|
|
773
|
+
);
|
|
774
|
+
|
|
775
|
+
test
|
|
776
|
+
(
|
|
777
|
+
'should serve JSON files with correct Content-Type and parseable content',
|
|
778
|
+
(fDone) =>
|
|
779
|
+
{
|
|
780
|
+
let tmpFable = new libFable(defaultFableSettings);
|
|
781
|
+
tmpFable.serviceManager.addServiceType('Orator', libOrator);
|
|
782
|
+
let tmpOrator = tmpFable.serviceManager.instantiateServiceProvider('Orator', {});
|
|
783
|
+
tmpOrator.startService();
|
|
784
|
+
tmpOrator.addStaticRoute(_StaticContentPath);
|
|
785
|
+
|
|
786
|
+
let tmpPort = getNextTestPort();
|
|
787
|
+
createTestHTTPServer(tmpOrator, tmpPort,
|
|
788
|
+
(pServer, pActualPort) =>
|
|
789
|
+
{
|
|
790
|
+
makeRequest(pActualPort, '/data.json',
|
|
791
|
+
(pError, pStatusCode, pHeaders, pBody) =>
|
|
792
|
+
{
|
|
793
|
+
Expect(pError).to.equal(null);
|
|
794
|
+
Expect(pStatusCode).to.equal(200);
|
|
795
|
+
Expect(pHeaders['content-type']).to.contain('application/json');
|
|
796
|
+
let tmpParsed = JSON.parse(pBody);
|
|
797
|
+
Expect(tmpParsed).to.have.a.property('TestKey');
|
|
798
|
+
Expect(tmpParsed.TestKey).to.equal('TestValue');
|
|
799
|
+
Expect(tmpParsed.Numbers).to.be.an('array');
|
|
800
|
+
Expect(tmpParsed.Numbers).to.have.lengthOf(3);
|
|
801
|
+
tmpOrator.log.info(`Served data.json: parsed successfully with TestKey=${tmpParsed.TestKey}`);
|
|
802
|
+
pServer.close();
|
|
803
|
+
return fDone();
|
|
804
|
+
});
|
|
805
|
+
});
|
|
806
|
+
}
|
|
807
|
+
);
|
|
808
|
+
}
|
|
809
|
+
);
|
|
810
|
+
|
|
811
|
+
suite
|
|
812
|
+
(
|
|
813
|
+
'Static File Serving - Route Stripping',
|
|
814
|
+
() =>
|
|
815
|
+
{
|
|
816
|
+
test
|
|
817
|
+
(
|
|
818
|
+
'should strip route prefix from URL before serving files',
|
|
819
|
+
(fDone) =>
|
|
820
|
+
{
|
|
821
|
+
let tmpFable = new libFable(defaultFableSettings);
|
|
822
|
+
tmpFable.serviceManager.addServiceType('Orator', libOrator);
|
|
823
|
+
let tmpOrator = tmpFable.serviceManager.instantiateServiceProvider('Orator', {});
|
|
824
|
+
tmpOrator.startService();
|
|
825
|
+
// Serve static content at /assets/* and strip /assets/ from the path
|
|
826
|
+
tmpOrator.addStaticRoute(_StaticContentPath, 'index.html', '/assets/*', '/assets/');
|
|
827
|
+
|
|
828
|
+
let tmpPort = getNextTestPort();
|
|
829
|
+
createTestHTTPServer(tmpOrator, tmpPort,
|
|
830
|
+
(pServer, pActualPort) =>
|
|
831
|
+
{
|
|
832
|
+
makeRequest(pActualPort, '/assets/style.css',
|
|
833
|
+
(pError, pStatusCode, pHeaders, pBody) =>
|
|
834
|
+
{
|
|
835
|
+
Expect(pError).to.equal(null);
|
|
836
|
+
Expect(pStatusCode).to.equal(200);
|
|
837
|
+
Expect(pHeaders['content-type']).to.contain('text/css');
|
|
838
|
+
Expect(pBody).to.contain('font-family');
|
|
839
|
+
tmpOrator.log.info(`Route stripping: /assets/style.css served correctly with content-type=${pHeaders['content-type']}`);
|
|
840
|
+
pServer.close();
|
|
841
|
+
return fDone();
|
|
842
|
+
});
|
|
843
|
+
});
|
|
844
|
+
}
|
|
845
|
+
);
|
|
846
|
+
|
|
847
|
+
test
|
|
848
|
+
(
|
|
849
|
+
'should serve the default file at the stripped route root',
|
|
850
|
+
(fDone) =>
|
|
851
|
+
{
|
|
852
|
+
let tmpFable = new libFable(defaultFableSettings);
|
|
853
|
+
tmpFable.serviceManager.addServiceType('Orator', libOrator);
|
|
854
|
+
let tmpOrator = tmpFable.serviceManager.instantiateServiceProvider('Orator', {});
|
|
855
|
+
tmpOrator.startService();
|
|
856
|
+
tmpOrator.addStaticRoute(_StaticContentPath, 'index.html', '/docs/*', '/docs/');
|
|
857
|
+
|
|
858
|
+
let tmpPort = getNextTestPort();
|
|
859
|
+
createTestHTTPServer(tmpOrator, tmpPort,
|
|
860
|
+
(pServer, pActualPort) =>
|
|
861
|
+
{
|
|
862
|
+
makeRequest(pActualPort, '/docs/',
|
|
863
|
+
(pError, pStatusCode, pHeaders, pBody) =>
|
|
864
|
+
{
|
|
865
|
+
Expect(pError).to.equal(null);
|
|
866
|
+
Expect(pStatusCode).to.equal(200);
|
|
867
|
+
Expect(pBody).to.contain('Test Index');
|
|
868
|
+
tmpOrator.log.info(`Route root /docs/ served default index.html`);
|
|
869
|
+
pServer.close();
|
|
870
|
+
return fDone();
|
|
871
|
+
});
|
|
872
|
+
});
|
|
873
|
+
}
|
|
874
|
+
);
|
|
875
|
+
|
|
876
|
+
test
|
|
877
|
+
(
|
|
878
|
+
'should serve JSON through a stripped route',
|
|
879
|
+
(fDone) =>
|
|
880
|
+
{
|
|
881
|
+
let tmpFable = new libFable(defaultFableSettings);
|
|
882
|
+
tmpFable.serviceManager.addServiceType('Orator', libOrator);
|
|
883
|
+
let tmpOrator = tmpFable.serviceManager.instantiateServiceProvider('Orator', {});
|
|
884
|
+
tmpOrator.startService();
|
|
885
|
+
tmpOrator.addStaticRoute(_StaticContentPath, 'index.html', '/api/static/*', '/api/static/');
|
|
886
|
+
|
|
887
|
+
let tmpPort = getNextTestPort();
|
|
888
|
+
createTestHTTPServer(tmpOrator, tmpPort,
|
|
889
|
+
(pServer, pActualPort) =>
|
|
890
|
+
{
|
|
891
|
+
makeRequest(pActualPort, '/api/static/data.json',
|
|
892
|
+
(pError, pStatusCode, pHeaders, pBody) =>
|
|
893
|
+
{
|
|
894
|
+
Expect(pError).to.equal(null);
|
|
895
|
+
Expect(pStatusCode).to.equal(200);
|
|
896
|
+
Expect(pHeaders['content-type']).to.contain('application/json');
|
|
897
|
+
let tmpParsed = JSON.parse(pBody);
|
|
898
|
+
Expect(tmpParsed.TestKey).to.equal('TestValue');
|
|
899
|
+
tmpOrator.log.info(`Served data.json through stripped route /api/static/data.json`);
|
|
900
|
+
pServer.close();
|
|
901
|
+
return fDone();
|
|
902
|
+
});
|
|
903
|
+
});
|
|
904
|
+
}
|
|
905
|
+
);
|
|
906
|
+
}
|
|
907
|
+
);
|
|
908
|
+
|
|
909
|
+
suite
|
|
910
|
+
(
|
|
911
|
+
'Static File Serving - Query String Handling',
|
|
912
|
+
() =>
|
|
913
|
+
{
|
|
914
|
+
test
|
|
915
|
+
(
|
|
916
|
+
'should strip query strings from URLs before serving files',
|
|
917
|
+
(fDone) =>
|
|
918
|
+
{
|
|
919
|
+
let tmpFable = new libFable(defaultFableSettings);
|
|
920
|
+
tmpFable.serviceManager.addServiceType('Orator', libOrator);
|
|
921
|
+
let tmpOrator = tmpFable.serviceManager.instantiateServiceProvider('Orator', {});
|
|
922
|
+
tmpOrator.startService();
|
|
923
|
+
tmpOrator.addStaticRoute(_StaticContentPath);
|
|
924
|
+
|
|
925
|
+
let tmpPort = getNextTestPort();
|
|
926
|
+
createTestHTTPServer(tmpOrator, tmpPort,
|
|
927
|
+
(pServer, pActualPort) =>
|
|
928
|
+
{
|
|
929
|
+
makeRequest(pActualPort, '/style.css?v=1.0.0&bust=true',
|
|
930
|
+
(pError, pStatusCode, pHeaders, pBody) =>
|
|
931
|
+
{
|
|
932
|
+
Expect(pError).to.equal(null);
|
|
933
|
+
Expect(pStatusCode).to.equal(200);
|
|
934
|
+
Expect(pHeaders['content-type']).to.contain('text/css');
|
|
935
|
+
Expect(pBody).to.contain('font-family');
|
|
936
|
+
tmpOrator.log.info('Query string was stripped and file served correctly');
|
|
937
|
+
pServer.close();
|
|
938
|
+
return fDone();
|
|
939
|
+
});
|
|
940
|
+
});
|
|
941
|
+
}
|
|
942
|
+
);
|
|
943
|
+
}
|
|
944
|
+
);
|
|
945
|
+
|
|
946
|
+
suite
|
|
947
|
+
(
|
|
948
|
+
'Static File Serving - Missing Files',
|
|
949
|
+
() =>
|
|
950
|
+
{
|
|
951
|
+
test
|
|
952
|
+
(
|
|
953
|
+
'should return 404 for a file that does not exist',
|
|
954
|
+
(fDone) =>
|
|
955
|
+
{
|
|
956
|
+
let tmpFable = new libFable(defaultFableSettings);
|
|
957
|
+
tmpFable.serviceManager.addServiceType('Orator', libOrator);
|
|
958
|
+
let tmpOrator = tmpFable.serviceManager.instantiateServiceProvider('Orator', {});
|
|
959
|
+
tmpOrator.startService();
|
|
960
|
+
tmpOrator.addStaticRoute(_StaticContentPath);
|
|
961
|
+
|
|
962
|
+
let tmpPort = getNextTestPort();
|
|
963
|
+
createTestHTTPServer(tmpOrator, tmpPort,
|
|
964
|
+
(pServer, pActualPort) =>
|
|
965
|
+
{
|
|
966
|
+
makeRequest(pActualPort, '/nonexistent.html',
|
|
967
|
+
(pError, pStatusCode, pHeaders, pBody) =>
|
|
968
|
+
{
|
|
969
|
+
Expect(pError).to.equal(null);
|
|
970
|
+
Expect(pStatusCode).to.equal(404);
|
|
971
|
+
tmpOrator.log.info(`Missing file correctly returned 404`);
|
|
972
|
+
pServer.close();
|
|
973
|
+
return fDone();
|
|
974
|
+
});
|
|
975
|
+
});
|
|
976
|
+
}
|
|
977
|
+
);
|
|
978
|
+
|
|
979
|
+
test
|
|
980
|
+
(
|
|
981
|
+
'should return 404 for a path traversal attempt',
|
|
982
|
+
(fDone) =>
|
|
983
|
+
{
|
|
984
|
+
let tmpFable = new libFable(defaultFableSettings);
|
|
985
|
+
tmpFable.serviceManager.addServiceType('Orator', libOrator);
|
|
986
|
+
let tmpOrator = tmpFable.serviceManager.instantiateServiceProvider('Orator', {});
|
|
987
|
+
tmpOrator.startService();
|
|
988
|
+
tmpOrator.addStaticRoute(_StaticContentPath, 'index.html', '/safe/*', '/safe/');
|
|
989
|
+
|
|
990
|
+
let tmpPort = getNextTestPort();
|
|
991
|
+
createTestHTTPServer(tmpOrator, tmpPort,
|
|
992
|
+
(pServer, pActualPort) =>
|
|
993
|
+
{
|
|
994
|
+
makeRequest(pActualPort, '/safe/../../../etc/passwd',
|
|
995
|
+
(pError, pStatusCode, pHeaders, pBody) =>
|
|
996
|
+
{
|
|
997
|
+
Expect(pError).to.equal(null);
|
|
998
|
+
// serve-static should prevent path traversal
|
|
999
|
+
Expect(pStatusCode).to.be.oneOf([400, 403, 404]);
|
|
1000
|
+
tmpOrator.log.info(`Path traversal attempt correctly blocked with status ${pStatusCode}`);
|
|
1001
|
+
pServer.close();
|
|
1002
|
+
return fDone();
|
|
1003
|
+
});
|
|
1004
|
+
});
|
|
1005
|
+
}
|
|
1006
|
+
);
|
|
1007
|
+
}
|
|
1008
|
+
);
|
|
1009
|
+
|
|
1010
|
+
suite
|
|
1011
|
+
(
|
|
1012
|
+
'Static File Serving - Response Headers',
|
|
1013
|
+
() =>
|
|
1014
|
+
{
|
|
1015
|
+
test
|
|
1016
|
+
(
|
|
1017
|
+
'should include standard caching headers in responses',
|
|
1018
|
+
(fDone) =>
|
|
1019
|
+
{
|
|
1020
|
+
let tmpFable = new libFable(defaultFableSettings);
|
|
1021
|
+
tmpFable.serviceManager.addServiceType('Orator', libOrator);
|
|
1022
|
+
let tmpOrator = tmpFable.serviceManager.instantiateServiceProvider('Orator', {});
|
|
1023
|
+
tmpOrator.startService();
|
|
1024
|
+
tmpOrator.addStaticRoute(_StaticContentPath);
|
|
1025
|
+
|
|
1026
|
+
let tmpPort = getNextTestPort();
|
|
1027
|
+
createTestHTTPServer(tmpOrator, tmpPort,
|
|
1028
|
+
(pServer, pActualPort) =>
|
|
1029
|
+
{
|
|
1030
|
+
makeRequest(pActualPort, '/index.html',
|
|
1031
|
+
(pError, pStatusCode, pHeaders, pBody) =>
|
|
1032
|
+
{
|
|
1033
|
+
Expect(pError).to.equal(null);
|
|
1034
|
+
Expect(pStatusCode).to.equal(200);
|
|
1035
|
+
// serve-static sets these headers by default
|
|
1036
|
+
Expect(pHeaders).to.have.a.property('etag');
|
|
1037
|
+
Expect(pHeaders).to.have.a.property('last-modified');
|
|
1038
|
+
Expect(pHeaders).to.have.a.property('content-length');
|
|
1039
|
+
Expect(pHeaders).to.have.a.property('accept-ranges');
|
|
1040
|
+
tmpOrator.log.info(`Standard headers present: etag=${pHeaders['etag']} last-modified=${pHeaders['last-modified']}`);
|
|
1041
|
+
pServer.close();
|
|
1042
|
+
return fDone();
|
|
1043
|
+
});
|
|
1044
|
+
});
|
|
1045
|
+
}
|
|
1046
|
+
);
|
|
1047
|
+
|
|
1048
|
+
test
|
|
1049
|
+
(
|
|
1050
|
+
'should pass custom serve-static params through to the library',
|
|
1051
|
+
(fDone) =>
|
|
1052
|
+
{
|
|
1053
|
+
let tmpFable = new libFable(defaultFableSettings);
|
|
1054
|
+
tmpFable.serviceManager.addServiceType('Orator', libOrator);
|
|
1055
|
+
let tmpOrator = tmpFable.serviceManager.instantiateServiceProvider('Orator', {});
|
|
1056
|
+
tmpOrator.startService();
|
|
1057
|
+
// Pass maxAge as a custom parameter to serve-static
|
|
1058
|
+
tmpOrator.addStaticRoute(_StaticContentPath, 'index.html', '/*', '/', {maxAge: 86400000});
|
|
1059
|
+
|
|
1060
|
+
let tmpPort = getNextTestPort();
|
|
1061
|
+
createTestHTTPServer(tmpOrator, tmpPort,
|
|
1062
|
+
(pServer, pActualPort) =>
|
|
1063
|
+
{
|
|
1064
|
+
makeRequest(pActualPort, '/index.html',
|
|
1065
|
+
(pError, pStatusCode, pHeaders, pBody) =>
|
|
1066
|
+
{
|
|
1067
|
+
Expect(pError).to.equal(null);
|
|
1068
|
+
Expect(pStatusCode).to.equal(200);
|
|
1069
|
+
// maxAge should be reflected in the Cache-Control header
|
|
1070
|
+
Expect(pHeaders['cache-control']).to.contain('max-age=86400');
|
|
1071
|
+
tmpOrator.log.info(`Custom params applied: cache-control=${pHeaders['cache-control']}`);
|
|
1072
|
+
pServer.close();
|
|
1073
|
+
return fDone();
|
|
1074
|
+
});
|
|
1075
|
+
});
|
|
1076
|
+
}
|
|
1077
|
+
);
|
|
1078
|
+
}
|
|
1079
|
+
);
|
|
1080
|
+
|
|
1081
|
+
suite
|
|
1082
|
+
(
|
|
1083
|
+
'Static File Serving - Subdomain Magic Subfolder Routing',
|
|
1084
|
+
() =>
|
|
1085
|
+
{
|
|
1086
|
+
test
|
|
1087
|
+
(
|
|
1088
|
+
'should serve from a subfolder when the hostname matches an existing subfolder',
|
|
1089
|
+
(fDone) =>
|
|
1090
|
+
{
|
|
1091
|
+
let tmpFable = new libFable(defaultFableSettings);
|
|
1092
|
+
// FilePersistence is now auto-instantiated by Orator's constructor
|
|
1093
|
+
tmpFable.serviceManager.addServiceType('Orator', libOrator);
|
|
1094
|
+
let tmpOrator = tmpFable.serviceManager.instantiateServiceProvider('Orator', {});
|
|
1095
|
+
tmpOrator.startService();
|
|
1096
|
+
tmpOrator.addStaticRoute(_StaticContentPath);
|
|
1097
|
+
|
|
1098
|
+
let tmpPort = getNextTestPort();
|
|
1099
|
+
createTestHTTPServer(tmpOrator, tmpPort,
|
|
1100
|
+
(pServer, pActualPort) =>
|
|
1101
|
+
{
|
|
1102
|
+
// Request with host 'subsite.example.com' -- the 'subsite' prefix matches the subfolder
|
|
1103
|
+
makeRequest(pActualPort, '/',
|
|
1104
|
+
(pError, pStatusCode, pHeaders, pBody) =>
|
|
1105
|
+
{
|
|
1106
|
+
Expect(pError).to.equal(null);
|
|
1107
|
+
Expect(pStatusCode).to.equal(200);
|
|
1108
|
+
// Should serve from static_content/subsite/index.html
|
|
1109
|
+
Expect(pBody).to.contain('Subsite');
|
|
1110
|
+
Expect(pBody).to.contain('Subsite index page');
|
|
1111
|
+
tmpOrator.log.info(`Subdomain magic routing: host=subsite.example.com served subsite content`);
|
|
1112
|
+
pServer.close();
|
|
1113
|
+
return fDone();
|
|
1114
|
+
},
|
|
1115
|
+
{ 'Host': 'subsite.example.com' });
|
|
1116
|
+
});
|
|
1117
|
+
}
|
|
1118
|
+
);
|
|
1119
|
+
|
|
1120
|
+
test
|
|
1121
|
+
(
|
|
1122
|
+
'should serve from the root folder when the hostname does not match any subfolder',
|
|
1123
|
+
(fDone) =>
|
|
1124
|
+
{
|
|
1125
|
+
let tmpFable = new libFable(defaultFableSettings);
|
|
1126
|
+
// FilePersistence is now auto-instantiated by Orator's constructor
|
|
1127
|
+
tmpFable.serviceManager.addServiceType('Orator', libOrator);
|
|
1128
|
+
let tmpOrator = tmpFable.serviceManager.instantiateServiceProvider('Orator', {});
|
|
1129
|
+
tmpOrator.startService();
|
|
1130
|
+
tmpOrator.addStaticRoute(_StaticContentPath);
|
|
1131
|
+
|
|
1132
|
+
let tmpPort = getNextTestPort();
|
|
1133
|
+
createTestHTTPServer(tmpOrator, tmpPort,
|
|
1134
|
+
(pServer, pActualPort) =>
|
|
1135
|
+
{
|
|
1136
|
+
// Request with host 'nonexistent.example.com' -- no matching subfolder
|
|
1137
|
+
makeRequest(pActualPort, '/',
|
|
1138
|
+
(pError, pStatusCode, pHeaders, pBody) =>
|
|
1139
|
+
{
|
|
1140
|
+
Expect(pError).to.equal(null);
|
|
1141
|
+
Expect(pStatusCode).to.equal(200);
|
|
1142
|
+
// Should serve from static_content/index.html (root)
|
|
1143
|
+
Expect(pBody).to.contain('Test Index');
|
|
1144
|
+
tmpOrator.log.info(`Non-matching subdomain served root content correctly`);
|
|
1145
|
+
pServer.close();
|
|
1146
|
+
return fDone();
|
|
1147
|
+
},
|
|
1148
|
+
{ 'Host': 'nonexistent.example.com' });
|
|
1149
|
+
});
|
|
1150
|
+
}
|
|
1151
|
+
);
|
|
1152
|
+
|
|
1153
|
+
test
|
|
1154
|
+
(
|
|
1155
|
+
'should serve from the root folder when hostname has only one segment (no dots)',
|
|
1156
|
+
(fDone) =>
|
|
1157
|
+
{
|
|
1158
|
+
let tmpFable = new libFable(defaultFableSettings);
|
|
1159
|
+
tmpFable.serviceManager.addServiceType('Orator', libOrator);
|
|
1160
|
+
let tmpOrator = tmpFable.serviceManager.instantiateServiceProvider('Orator', {});
|
|
1161
|
+
tmpOrator.startService();
|
|
1162
|
+
tmpOrator.addStaticRoute(_StaticContentPath);
|
|
1163
|
+
|
|
1164
|
+
let tmpPort = getNextTestPort();
|
|
1165
|
+
createTestHTTPServer(tmpOrator, tmpPort,
|
|
1166
|
+
(pServer, pActualPort) =>
|
|
1167
|
+
{
|
|
1168
|
+
// 'localhost' has no dots, so no subdomain magic should apply
|
|
1169
|
+
makeRequest(pActualPort, '/',
|
|
1170
|
+
(pError, pStatusCode, pHeaders, pBody) =>
|
|
1171
|
+
{
|
|
1172
|
+
Expect(pError).to.equal(null);
|
|
1173
|
+
Expect(pStatusCode).to.equal(200);
|
|
1174
|
+
Expect(pBody).to.contain('Test Index');
|
|
1175
|
+
tmpOrator.log.info(`Single-segment hostname served root content`);
|
|
1176
|
+
pServer.close();
|
|
1177
|
+
return fDone();
|
|
1178
|
+
},
|
|
1179
|
+
{ 'Host': 'localhost' });
|
|
1180
|
+
});
|
|
1181
|
+
}
|
|
1182
|
+
);
|
|
1183
|
+
}
|
|
1184
|
+
);
|
|
1185
|
+
|
|
1186
|
+
suite
|
|
1187
|
+
(
|
|
1188
|
+
'Static File Serving - Subsite Direct Access',
|
|
1189
|
+
() =>
|
|
1190
|
+
{
|
|
1191
|
+
test
|
|
1192
|
+
(
|
|
1193
|
+
'should serve files from a subdirectory path directly',
|
|
1194
|
+
(fDone) =>
|
|
1195
|
+
{
|
|
1196
|
+
let tmpFable = new libFable(defaultFableSettings);
|
|
1197
|
+
tmpFable.serviceManager.addServiceType('Orator', libOrator);
|
|
1198
|
+
let tmpOrator = tmpFable.serviceManager.instantiateServiceProvider('Orator', {});
|
|
1199
|
+
tmpOrator.startService();
|
|
1200
|
+
tmpOrator.addStaticRoute(_StaticContentPath);
|
|
1201
|
+
|
|
1202
|
+
let tmpPort = getNextTestPort();
|
|
1203
|
+
createTestHTTPServer(tmpOrator, tmpPort,
|
|
1204
|
+
(pServer, pActualPort) =>
|
|
1205
|
+
{
|
|
1206
|
+
makeRequest(pActualPort, '/subsite/index.html',
|
|
1207
|
+
(pError, pStatusCode, pHeaders, pBody) =>
|
|
1208
|
+
{
|
|
1209
|
+
Expect(pError).to.equal(null);
|
|
1210
|
+
Expect(pStatusCode).to.equal(200);
|
|
1211
|
+
Expect(pHeaders['content-type']).to.contain('text/html');
|
|
1212
|
+
Expect(pBody).to.contain('Subsite');
|
|
1213
|
+
tmpOrator.log.info(`Subsite direct path access: /subsite/index.html served correctly`);
|
|
1214
|
+
pServer.close();
|
|
1215
|
+
return fDone();
|
|
1216
|
+
});
|
|
1217
|
+
});
|
|
1218
|
+
}
|
|
1219
|
+
);
|
|
1220
|
+
}
|
|
1221
|
+
);
|
|
1222
|
+
|
|
1223
|
+
suite
|
|
1224
|
+
(
|
|
1225
|
+
'Static File Serving - Multiple Static Routes',
|
|
1226
|
+
() =>
|
|
1227
|
+
{
|
|
1228
|
+
test
|
|
1229
|
+
(
|
|
1230
|
+
'should be able to register multiple static routes on different paths',
|
|
1231
|
+
(fDone) =>
|
|
1232
|
+
{
|
|
1233
|
+
let tmpFable = new libFable(defaultFableSettings);
|
|
1234
|
+
tmpFable.serviceManager.addServiceType('Orator', libOrator);
|
|
1235
|
+
let tmpOrator = tmpFable.serviceManager.instantiateServiceProvider('Orator', {});
|
|
1236
|
+
tmpOrator.startService();
|
|
1237
|
+
|
|
1238
|
+
// Register the subsite as a separate static route
|
|
1239
|
+
let tmpSubsitePath = libPath.normalize(__dirname + '/static_content/subsite/');
|
|
1240
|
+
let tmpResult1 = tmpOrator.addStaticRoute(_StaticContentPath, 'index.html', '/main/*', '/main/');
|
|
1241
|
+
let tmpResult2 = tmpOrator.addStaticRoute(tmpSubsitePath, 'index.html', '/sub/*', '/sub/');
|
|
1242
|
+
|
|
1243
|
+
Expect(tmpResult1).to.equal(true);
|
|
1244
|
+
Expect(tmpResult2).to.equal(true);
|
|
1245
|
+
|
|
1246
|
+
let tmpPort = getNextTestPort();
|
|
1247
|
+
createTestHTTPServer(tmpOrator, tmpPort,
|
|
1248
|
+
(pServer, pActualPort) =>
|
|
1249
|
+
{
|
|
1250
|
+
tmpFable.Utility.waterfall([
|
|
1251
|
+
(fStageComplete) =>
|
|
1252
|
+
{
|
|
1253
|
+
makeRequest(pActualPort, '/main/data.json',
|
|
1254
|
+
(pError, pStatusCode, pHeaders, pBody) =>
|
|
1255
|
+
{
|
|
1256
|
+
Expect(pStatusCode).to.equal(200);
|
|
1257
|
+
let tmpParsed = JSON.parse(pBody);
|
|
1258
|
+
Expect(tmpParsed.TestKey).to.equal('TestValue');
|
|
1259
|
+
tmpOrator.log.info('Route /main/ served data.json from main content');
|
|
1260
|
+
return fStageComplete();
|
|
1261
|
+
});
|
|
1262
|
+
},
|
|
1263
|
+
(fStageComplete) =>
|
|
1264
|
+
{
|
|
1265
|
+
makeRequest(pActualPort, '/sub/',
|
|
1266
|
+
(pError, pStatusCode, pHeaders, pBody) =>
|
|
1267
|
+
{
|
|
1268
|
+
Expect(pStatusCode).to.equal(200);
|
|
1269
|
+
Expect(pBody).to.contain('Subsite');
|
|
1270
|
+
tmpOrator.log.info('Route /sub/ served index.html from subsite content');
|
|
1271
|
+
return fStageComplete();
|
|
1272
|
+
});
|
|
1273
|
+
}
|
|
1274
|
+
],
|
|
1275
|
+
(pError) =>
|
|
1276
|
+
{
|
|
1277
|
+
pServer.close();
|
|
1278
|
+
return fDone();
|
|
1279
|
+
});
|
|
1280
|
+
});
|
|
1281
|
+
}
|
|
1282
|
+
);
|
|
1283
|
+
}
|
|
1284
|
+
);
|
|
1285
|
+
|
|
1286
|
+
suite
|
|
1287
|
+
(
|
|
1288
|
+
'Static File Serving - oldLibMime Compatibility Flag',
|
|
1289
|
+
() =>
|
|
1290
|
+
{
|
|
1291
|
+
test
|
|
1292
|
+
(
|
|
1293
|
+
'should correctly detect the mime library version',
|
|
1294
|
+
(fDone) =>
|
|
1295
|
+
{
|
|
1296
|
+
let tmpFable = new libFable(defaultFableSettings);
|
|
1297
|
+
tmpFable.serviceManager.addServiceType('Orator', libOrator);
|
|
1298
|
+
let tmpOrator = tmpFable.serviceManager.instantiateServiceProvider('Orator', {});
|
|
1299
|
+
tmpOrator.initialize(
|
|
1300
|
+
() =>
|
|
1301
|
+
{
|
|
1302
|
+
// The oldLibMime flag should be a boolean
|
|
1303
|
+
Expect(tmpOrator.oldLibMime).to.be.a('boolean');
|
|
1304
|
+
// With the current version of the mime library, it should use the lookup function
|
|
1305
|
+
let libMime = require('mime');
|
|
1306
|
+
if ('lookup' in libMime)
|
|
1307
|
+
{
|
|
1308
|
+
Expect(tmpOrator.oldLibMime).to.equal(true);
|
|
1309
|
+
}
|
|
1310
|
+
else
|
|
1311
|
+
{
|
|
1312
|
+
Expect(tmpOrator.oldLibMime).to.equal(false);
|
|
1313
|
+
}
|
|
1314
|
+
tmpOrator.log.info(`oldLibMime flag is ${tmpOrator.oldLibMime}`);
|
|
1315
|
+
return fDone();
|
|
1316
|
+
});
|
|
1317
|
+
}
|
|
1318
|
+
);
|
|
1319
|
+
}
|
|
1320
|
+
);
|
|
1321
|
+
}
|
|
1322
|
+
);
|