|
| 1 | +'use strict'; |
| 2 | + |
| 3 | +const test = require('tap').test; |
| 4 | +const sandbox = require('sandboxed-module'); |
| 5 | + |
| 6 | +function makeFakeNet() { |
| 7 | + return { |
| 8 | + logEvents: [], |
| 9 | + data: [], |
| 10 | + cbs: {}, |
| 11 | + createConnectionCalled: 0, |
| 12 | + fakeAppender: function (logEvent) { |
| 13 | + this.logEvents.push(logEvent); |
| 14 | + }, |
| 15 | + createConnection: function (port, host) { |
| 16 | + const fakeNet = this; |
| 17 | + this.port = port; |
| 18 | + this.host = host; |
| 19 | + this.createConnectionCalled += 1; |
| 20 | + return { |
| 21 | + on: function (evt, cb) { |
| 22 | + fakeNet.cbs[evt] = cb; |
| 23 | + }, |
| 24 | + write: function (data, encoding) { |
| 25 | + fakeNet.data.push(data); |
| 26 | + fakeNet.encoding = encoding; |
| 27 | + }, |
| 28 | + end: function () { |
| 29 | + fakeNet.closeCalled = true; |
| 30 | + } |
| 31 | + }; |
| 32 | + }, |
| 33 | + createServer: function (cb) { |
| 34 | + const fakeNet = this; |
| 35 | + cb({ |
| 36 | + remoteAddress: '1.2.3.4', |
| 37 | + remotePort: '1234', |
| 38 | + setEncoding: function (encoding) { |
| 39 | + fakeNet.encoding = encoding; |
| 40 | + }, |
| 41 | + on: function (event, cb2) { |
| 42 | + fakeNet.cbs[event] = cb2; |
| 43 | + } |
| 44 | + }); |
| 45 | + |
| 46 | + return { |
| 47 | + listen: function (port, host) { |
| 48 | + fakeNet.port = port; |
| 49 | + fakeNet.host = host; |
| 50 | + } |
| 51 | + }; |
| 52 | + } |
| 53 | + }; |
| 54 | +} |
| 55 | + |
| 56 | +test('Multiprocess Appender', (batch) => { |
| 57 | + batch.test('worker', (t) => { |
| 58 | + const fakeNet = makeFakeNet(); |
| 59 | + |
| 60 | + const appender = sandbox.require( |
| 61 | + '../../lib/appenders/multiprocess', |
| 62 | + { |
| 63 | + requires: { |
| 64 | + net: fakeNet |
| 65 | + } |
| 66 | + } |
| 67 | + ).appender({ mode: 'worker', loggerPort: 1234, loggerHost: 'pants' }); |
| 68 | + |
| 69 | + // don't need a proper log event for the worker tests |
| 70 | + appender('before connect'); |
| 71 | + fakeNet.cbs.connect(); |
| 72 | + appender('after connect'); |
| 73 | + fakeNet.cbs.close(true); |
| 74 | + appender('after error, before connect'); |
| 75 | + fakeNet.cbs.connect(); |
| 76 | + appender('after error, after connect'); |
| 77 | + appender(new Error('Error test')); |
| 78 | + |
| 79 | + const net = fakeNet; |
| 80 | + t.test('should open a socket to the loggerPort and loggerHost', (assert) => { |
| 81 | + assert.equal(net.port, 1234); |
| 82 | + assert.equal(net.host, 'pants'); |
| 83 | + assert.end(); |
| 84 | + }); |
| 85 | + |
| 86 | + t.test('should buffer messages written before socket is connected', (assert) => { |
| 87 | + assert.equal(net.data[0], JSON.stringify('before connect')); |
| 88 | + assert.end(); |
| 89 | + }); |
| 90 | + |
| 91 | + t.test('should write log messages to socket as json strings with a terminator string', (assert) => { |
| 92 | + assert.equal(net.data[0], JSON.stringify('before connect')); |
| 93 | + assert.equal(net.data[1], '__LOG4JS__'); |
| 94 | + assert.equal(net.data[2], JSON.stringify('after connect')); |
| 95 | + assert.equal(net.data[3], '__LOG4JS__'); |
| 96 | + assert.equal(net.encoding, 'utf8'); |
| 97 | + assert.end(); |
| 98 | + }); |
| 99 | + |
| 100 | + t.test('should attempt to re-open the socket on error', (assert) => { |
| 101 | + assert.equal(net.data[4], JSON.stringify('after error, before connect')); |
| 102 | + assert.equal(net.data[5], '__LOG4JS__'); |
| 103 | + assert.equal(net.data[6], JSON.stringify('after error, after connect')); |
| 104 | + assert.equal(net.data[7], '__LOG4JS__'); |
| 105 | + assert.equal(net.createConnectionCalled, 2); |
| 106 | + assert.end(); |
| 107 | + }); |
| 108 | + |
| 109 | + t.test('should serialize an Error correctly', (assert) => { |
| 110 | + assert.ok( |
| 111 | + JSON.parse(net.data[8]).stack, |
| 112 | + `Expected:\n\n${net.data[8]}\n\n to have a 'stack' property` |
| 113 | + ); |
| 114 | + const actual = JSON.parse(net.data[8]).stack; |
| 115 | + assert.match(actual, /^Error: Error test/); |
| 116 | + assert.end(); |
| 117 | + }); |
| 118 | + t.end(); |
| 119 | + }); |
| 120 | + |
| 121 | + batch.test('worker with timeout', (t) => { |
| 122 | + const fakeNet = makeFakeNet(); |
| 123 | + |
| 124 | + const appender = sandbox.require( |
| 125 | + '../../lib/appenders/multiprocess', |
| 126 | + { |
| 127 | + requires: { |
| 128 | + net: fakeNet |
| 129 | + } |
| 130 | + } |
| 131 | + ).appender({ mode: 'worker' }); |
| 132 | + |
| 133 | + // don't need a proper log event for the worker tests |
| 134 | + appender('before connect'); |
| 135 | + fakeNet.cbs.connect(); |
| 136 | + appender('after connect'); |
| 137 | + fakeNet.cbs.timeout(); |
| 138 | + appender('after timeout, before close'); |
| 139 | + fakeNet.cbs.close(); |
| 140 | + appender('after close, before connect'); |
| 141 | + fakeNet.cbs.connect(); |
| 142 | + appender('after close, after connect'); |
| 143 | + |
| 144 | + const net = fakeNet; |
| 145 | + |
| 146 | + t.test('should attempt to re-open the socket', (assert) => { |
| 147 | + // skipping the __LOG4JS__ separators |
| 148 | + assert.equal(net.data[0], JSON.stringify('before connect')); |
| 149 | + assert.equal(net.data[2], JSON.stringify('after connect')); |
| 150 | + assert.equal(net.data[4], JSON.stringify('after timeout, before close')); |
| 151 | + assert.equal(net.data[6], JSON.stringify('after close, before connect')); |
| 152 | + assert.equal(net.data[8], JSON.stringify('after close, after connect')); |
| 153 | + assert.equal(net.createConnectionCalled, 2); |
| 154 | + assert.end(); |
| 155 | + }); |
| 156 | + t.end(); |
| 157 | + }); |
| 158 | + |
| 159 | + batch.test('worker defaults', (t) => { |
| 160 | + const fakeNet = makeFakeNet(); |
| 161 | + |
| 162 | + sandbox.require( |
| 163 | + '../../lib/appenders/multiprocess', |
| 164 | + { |
| 165 | + requires: { |
| 166 | + net: fakeNet |
| 167 | + } |
| 168 | + } |
| 169 | + ).appender({ mode: 'worker' }); |
| 170 | + |
| 171 | + t.test('should open a socket to localhost:5000', (assert) => { |
| 172 | + assert.equal(fakeNet.port, 5000); |
| 173 | + assert.equal(fakeNet.host, 'localhost'); |
| 174 | + assert.end(); |
| 175 | + }); |
| 176 | + t.end(); |
| 177 | + }); |
| 178 | + |
| 179 | + batch.test('master', (t) => { |
| 180 | + const fakeNet = makeFakeNet(); |
| 181 | + |
| 182 | + const appender = sandbox.require( |
| 183 | + '../../lib/appenders/multiprocess', |
| 184 | + { |
| 185 | + requires: { |
| 186 | + net: fakeNet |
| 187 | + } |
| 188 | + } |
| 189 | + ).appender({ |
| 190 | + mode: 'master', |
| 191 | + loggerHost: 'server', |
| 192 | + loggerPort: 1234, |
| 193 | + actualAppender: fakeNet.fakeAppender.bind(fakeNet) |
| 194 | + }); |
| 195 | + |
| 196 | + appender('this should be sent to the actual appender directly'); |
| 197 | + |
| 198 | + const net = fakeNet; |
| 199 | + |
| 200 | + t.test('should listen for log messages on loggerPort and loggerHost', (assert) => { |
| 201 | + assert.equal(net.port, 1234); |
| 202 | + assert.equal(net.host, 'server'); |
| 203 | + assert.end(); |
| 204 | + }); |
| 205 | + |
| 206 | + t.test('should return the underlying appender', (assert) => { |
| 207 | + assert.equal(net.logEvents[0], 'this should be sent to the actual appender directly'); |
| 208 | + assert.end(); |
| 209 | + }); |
| 210 | + |
| 211 | + t.test('when a client connects', (assert) => { |
| 212 | + const logString = `${JSON.stringify( |
| 213 | + { |
| 214 | + level: { level: 10000, levelStr: 'DEBUG' }, |
| 215 | + data: ['some debug'] |
| 216 | + } |
| 217 | + )}__LOG4JS__`; |
| 218 | + |
| 219 | + net.cbs.data( |
| 220 | + `${JSON.stringify( |
| 221 | + { |
| 222 | + level: { level: 40000, levelStr: 'ERROR' }, |
| 223 | + data: ['an error message'] |
| 224 | + } |
| 225 | + )}__LOG4JS__` |
| 226 | + ); |
| 227 | + net.cbs.data(logString.substring(0, 10)); |
| 228 | + net.cbs.data(logString.substring(10)); |
| 229 | + net.cbs.data(logString + logString + logString); |
| 230 | + net.cbs.end( |
| 231 | + `${JSON.stringify( |
| 232 | + { |
| 233 | + level: { level: 50000, levelStr: 'FATAL' }, |
| 234 | + data: ["that's all folks"] |
| 235 | + } |
| 236 | + )}__LOG4JS__` |
| 237 | + ); |
| 238 | + net.cbs.data('bad message__LOG4JS__'); |
| 239 | + |
| 240 | + // should parse log messages into log events and send to appender |
| 241 | + assert.equal(net.logEvents[1].level.toString(), 'ERROR'); |
| 242 | + assert.equal(net.logEvents[1].data[0], 'an error message'); |
| 243 | + assert.equal(net.logEvents[1].remoteAddress, '1.2.3.4'); |
| 244 | + assert.equal(net.logEvents[1].remotePort, '1234'); |
| 245 | + |
| 246 | + // should parse log messages split into multiple chunks' |
| 247 | + assert.equal(net.logEvents[2].level.toString(), 'DEBUG'); |
| 248 | + assert.equal(net.logEvents[2].data[0], 'some debug'); |
| 249 | + assert.equal(net.logEvents[2].remoteAddress, '1.2.3.4'); |
| 250 | + assert.equal(net.logEvents[2].remotePort, '1234'); |
| 251 | + |
| 252 | + // should parse multiple log messages in a single chunk' |
| 253 | + assert.equal(net.logEvents[3].data[0], 'some debug'); |
| 254 | + assert.equal(net.logEvents[4].data[0], 'some debug'); |
| 255 | + assert.equal(net.logEvents[5].data[0], 'some debug'); |
| 256 | + |
| 257 | + // should handle log messages sent as part of end event' |
| 258 | + assert.equal(net.logEvents[6].data[0], "that's all folks"); |
| 259 | + |
| 260 | + // should handle unparseable log messages |
| 261 | + assert.equal(net.logEvents[7].level.toString(), 'ERROR'); |
| 262 | + assert.equal(net.logEvents[7].categoryName, 'log4js'); |
| 263 | + assert.equal(net.logEvents[7].data[0], 'Unable to parse log:'); |
| 264 | + assert.equal(net.logEvents[7].data[1], 'bad message'); |
| 265 | + |
| 266 | + assert.end(); |
| 267 | + }); |
| 268 | + t.end(); |
| 269 | + }); |
| 270 | + |
| 271 | + batch.test('master defaults', (t) => { |
| 272 | + const fake
10000
Net = makeFakeNet(); |
| 273 | + |
| 274 | + sandbox.require( |
| 275 | + '../../lib/appenders/multiprocess', |
| 276 | + { |
| 277 | + requires: { |
| 278 | + net: fakeNet |
| 279 | + } |
| 280 | + } |
| 281 | + ).appender({ mode: 'master' }); |
| 282 | + |
| 283 | + t.test('should listen for log messages on localhost:5000', (assert) => { |
| 284 | + assert.equal(fakeNet.port, 5000); |
| 285 | + assert.equal(fakeNet.host, 'localhost'); |
| 286 | + assert.end(); |
| 287 | + }); |
| 288 | + t.end(); |
| 289 | + }); |
| 290 | + |
| 291 | + batch.test('configure', (t) => { |
| 292 | + const results = {}; |
| 293 | + const fakeNet = makeFakeNet(); |
| 294 | + |
| 295 | + sandbox.require( |
| 296 | + '../../lib/appenders/multiprocess', |
| 297 | + { |
| 298 | + requires: { |
| 299 | + net: fakeNet, |
| 300 | + '../log4js': { |
| 301 | + loadAppender: function (app) { |
| 302 | + results.appenderLoaded = app; |
| 303 | + }, |
| 304 | + appenderMakers: { |
| 305 | + madeupappender: function (config, options) { |
| 306 | + results.config = config; |
| 307 | + results.options = options; |
| 308 | + } |
| 309 | + } |
| 310 | + } |
| 311 | + } |
| 312 | + } |
| 313 | + ).configure( |
| 314 | + { |
| 315 | + mode: 'master', |
| 316 | + appender: { |
| 317 | + type: 'madeupappender', |
| 318 | + cheese: 'gouda' |
| 319 | + } |
| 320 | + }, |
| 321 | + { crackers: 'jacobs' } |
| 322 | + ); |
| 323 | + |
| 324 | + t.equal(results.appenderLoaded, 'madeupappender', 'should load underlying appender for master'); |
| 325 | + t.equal(results.config.cheese, 'gouda', 'should pass config to underlying appender'); |
| 326 | + t.equal(results.options.crackers, 'jacobs', 'should pass options to underlying appender'); |
| 327 | + t.end(); |
| 328 | + }); |
| 329 | + |
| 330 | + batch.end(); |
| 331 | +}); |
0 commit comments