winston-cloudwatch
Version:
Send logs to Amazon Cloudwatch using Winston.
301 lines (249 loc) • 8.51 kB
JavaScript
describe('index', function() {
var sinon = require('sinon'),
should = require('should'),
mockery = require('mockery');
var stubbedWinston = {
transports: {},
Transport: function() {}
};
var stubbedAWS = {
CloudWatchLogs: function(options) {
this.fakeOptions = options;
},
config: {
update: sinon.stub()
}
};
var stubbedCloudwatchIntegration = {
upload: sinon.spy(function(aws, groupName, streamName, logEvents, retention, options, cb) {
this.lastLoggedEvents = logEvents.splice(0, 20);
cb();
}),
clearSequenceToken: sinon.stub()
};
var clock = sinon.useFakeTimers();
var WinstonCloudWatch;
before(function() {
mockery.enable({
warnOnReplace: false
});
mockery.registerAllowable('util');
mockery.registerAllowable('lodash.find');
mockery.registerAllowable('lodash.assign');
mockery.registerAllowable('lodash.isempty');
mockery.registerAllowable('lodash.iserror');
mockery.registerAllowable('./lib/utils');
mockery.registerAllowable('@aws-sdk/client-cloudwatch-logs');
mockery.registerAllowable('aws-crt');
mockery.registerMock('winston', stubbedWinston);
mockery.registerMock('aws-sdk', stubbedAWS);
mockery.registerMock('./lib/cloudwatch-integration', stubbedCloudwatchIntegration);
mockery.registerAllowable('../index.js');
WinstonCloudWatch = require('../index.js');
});
after(function() {
mockery.deregisterAll();
mockery.disable();
clock.restore();
});
describe('construtor', function() {
it('allows cloudWatchLogs', function() {
var options = {
cloudWatchLogs: { fakeOptions: { region: 'us-west-2' }}
};
var transport = new WinstonCloudWatch(options);
transport.cloudwatchlogs.fakeOptions.region.should.equal('us-west-2');
});
it('allows awsOptions', function() {
var options = {
awsOptions: {
region: 'us-east-1'
}
};
var transport = new WinstonCloudWatch(options);
transport.cloudwatchlogs.config.region().then(region => {
region.should.equal('us-east-1');
})
});
it('merges awsOptions into existing ones', function() {
var options = {
region: 'eu-west-1',
awsOptions: {
region: 'us-east-1'
}
};
var transport = new WinstonCloudWatch(options);
return transport.cloudwatchlogs.config.region().then(region => {
region.should.equal('us-east-1')
})
});
});
describe('log', function() {
var transport;
beforeEach(function(done) {
transport = new WinstonCloudWatch({});
transport.log({ level: 'level' }, function() {
clock.tick(2000);
done();
});
});
it('does not upload if empty message', function(done) {
stubbedCloudwatchIntegration.upload.called.should.equal(false);
done();
});
it('flushes logs and exits in case of an exception', function(done) {
transport = new WinstonCloudWatch({});
transport.log({ message: 'uncaughtException: ' }, function() {
clock.tick(2000);
should.not.exist(transport.intervalId);
// if done is called it means submit(callback) has been called
done();
});
});
describe('as json', function() {
var transport;
var options = {
jsonMessage: true
};
before(function(done) {
transport = new WinstonCloudWatch(options);
transport.log({ level: 'level', message: 'message', something: 'else' },
function() {
clock.tick(2000);
done();
});
});
it('logs json', function() {
var message = stubbedCloudwatchIntegration.lastLoggedEvents[0].message;
var jsonMessage = JSON.parse(message);
jsonMessage.level.should.equal('level');
jsonMessage.message.should.equal('message');
jsonMessage.something.should.equal('else');
});
});
describe('as text', function() {
var transport;
describe('using the default formatter', function() {
var options = {};
before(function(done) {
transport = new WinstonCloudWatch(options);
transport.log({ level: 'level', message: 'message' }, done);
clock.tick(2000);
});
it('logs text', function() {
var message = stubbedCloudwatchIntegration.lastLoggedEvents[0].message;
message.should.equal('level - message');
});
});
describe('using a custom formatter', function() {
var options = {
messageFormatter: function(log) {
return log.level + ' ' + log.message + ' ' + log.something;
}
};
before(function(done) {
transport = new WinstonCloudWatch(options);
transport.log({ level: 'level', message: 'message', something: 'else' }, done);
clock.tick(2000);
});
it('logs text', function() {
var message = stubbedCloudwatchIntegration.lastLoggedEvents[0].message;
message.should.equal('level message else');
});
});
});
describe('info object and a callback as arguments', function() {
before(function(done) {
transport = new WinstonCloudWatch({});
transport.log({ level: 'level', message: 'message' }, function() {
clock.tick(2000);
done();
});
});
it('logs text', function() {
var message = stubbedCloudwatchIntegration.lastLoggedEvents[0].message;
message.should.equal('level - message');
});
});
describe('handles error', function() {
beforeEach(function() {
stubbedCloudwatchIntegration.upload = sinon.stub().yields('ERROR');
mockery.registerMock('./lib/cloudwatch-integration', stubbedCloudwatchIntegration);
sinon.stub(console, 'error');
});
afterEach(function() {
stubbedCloudwatchIntegration.upload = sinon.spy();
console.error.restore();
});
it('invoking errorHandler if provided', function() {
var errorHandlerSpy = sinon.spy();
var transport = new WinstonCloudWatch({
errorHandler: errorHandlerSpy
});
transport.log({ level: 'level', message: 'message' }, sinon.stub());
clock.tick(2000);
errorHandlerSpy.args[0][0].should.equal('ERROR');
});
it('console.error if errorHandler is not provided', function() {
var transport = new WinstonCloudWatch({});
transport.log({ level: 'level', message: 'message' }, sinon.stub());
clock.tick(2000);
console.error.args[0][0].should.equal('ERROR');
});
});
});
describe('ktxhbye', function() {
var transport;
beforeEach(function() {
sinon.stub(global, 'setInterval');
sinon.stub(global, 'clearInterval');
transport = new WinstonCloudWatch({});
sinon.stub(transport, 'submit').callsFake(function(cb){
this.logEvents.splice(0, 20);
cb();
});
});
afterEach(function() {
global.setInterval.restore();
global.clearInterval.restore();
transport.submit.restore();
});
it('clears the interval', function(done) {
transport.intervalId = 'fake';
transport.kthxbye(function() {
global.clearInterval.callCount.should.equal(1);
should.not.exist(transport.intervalId);
done();
});
});
it('submit the logs', function(done) {
transport.kthxbye(function() {
transport.submit.callCount.should.equal(1);
done();
});
});
it('should not send all messages if called while posting', function(done) {
for (var index = 0; index < 30; index++) {
transport.add({ message: 'message' + index });
}
transport.kthxbye(function() {
transport.logEvents.length.should.equal(0);
done();
});
clock.tick(1);
});
it('should exit if logs are not cleared by the timeout period', function(done) {
transport.add({ message: 'message' });
transport.submit.callsFake(function(cb){
clock.tick(500);
cb(); // callback is called but logEvents is not cleared
});
transport.kthxbye(function(error) {
error.should.be.Error();
transport.logEvents.length.should.equal(1);
done();
});
clock.tick(1);
});
});
});