UNPKG

winston-cloudwatch

Version:
556 lines (476 loc) 17.6 kB
describe('cloudwatch-integration', function() { var lib = require('../lib/cloudwatch-integration'), { CloudWatchLogs } = require('@aws-sdk/client-cloudwatch-logs'), sinon = require('sinon'), should = require('should'); describe('upload', function() { var aws = {}; beforeEach(function() { aws.putLogEvents = sinon.stub().yields(); aws.putRetentionPolicy = sinon.stub().returns(); sinon.stub(lib, 'getToken').yields(null, 'token'); sinon.stub(lib, 'submitWithAnotherToken').yields(); sinon.stub(console, 'error'); }); afterEach(function() { lib.getToken.restore(); lib.submitWithAnotherToken.restore(); console.error.restore(); lib._nextToken = {}; }); it('ignores upload calls if putLogEvents already in progress', function(done) { const events = [{ message : "test message", timestamp : new Date().toISOString()}]; aws.putLogEvents.onFirstCall().returns(); // Don't call call back to simulate ongoing request. aws.putLogEvents.onSecondCall().yields(); lib.upload(aws, 'group', 'stream', events, 0, { ensureGroupPresent: true }, function(){}); lib.upload(aws, 'group', 'stream', events, 0, { ensureGroupPresent: true }, function() { // The second upload call should get ignored aws.putLogEvents.calledOnce.should.equal(true); delete lib._postingEvents['stream']; // reset done() }); }); it('ignores upload calls if getToken already in progress', function(done) { const events = [{ message : "test message", timestamp : new Date().toISOString()}]; lib.getToken.onFirstCall().returns(); // Don't call call back to simulate ongoing token request. lib.getToken.onSecondCall().yields(null, 'token'); lib.upload(aws, 'group', 'stream', events, 0, { ensureGroupPresent: true }, function(){}); lib.upload(aws, 'group', 'stream', events, 0, { ensureGroupPresent: true }, function() { // The second upload call should get ignored lib.getToken.calledOnce.should.equal(true); delete lib._postingEvents['stream']; // reset done() }); }); it('not ignores upload calls if getToken already in progress for another stream', function(done) { const events = [{ message : "test message", timestamp : new Date().toISOString()}]; lib.getToken.onFirstCall().returns(); // Don't call call back to simulate ongoing token request. lib.getToken.onSecondCall().yields(null, 'token'); lib.upload(aws, 'group', 'stream1', events, 0, { ensureGroupPresent: true }, function(){ }); lib.upload(aws, 'group', 'stream2', events, 0, { ensureGroupPresent: true }, function(){ done() }); lib.getToken.calledTwice.should.equal(true); delete lib._postingEvents['stream1']; // reset delete lib._postingEvents['stream2']; // reset }); it('truncates very large messages and alerts the error handler', function(done) { var BIG_MSG_LEN = 300000; const events = [{ message : new Array(BIG_MSG_LEN).join('A'), timestamp : new Date().toISOString()}]; var errCalled = false; lib.upload(aws, 'group', 'stream', events, 0, { ensureGroupPresent: true }, function(err) { if(err) { errCalled = true; return; } errCalled.should.equal(true); aws.putLogEvents.calledOnce.should.equal(true); aws.putLogEvents.args[0][0].logEvents[0].message.length.should.be.lessThan(BIG_MSG_LEN); // Truncated done() }); }); it('batches messages so as not to exceed CW limits', function(done) { var BIG_MSG_LEN = 250000; // under single limit but a few of these will exceed the batch limit var bigMessage = new Array(BIG_MSG_LEN).join(' '); const events = [ { message : bigMessage, timestamp : new Date().toISOString()}, { message : bigMessage, timestamp : new Date().toISOString()}, { message : bigMessage, timestamp : new Date().toISOString()}, { message : bigMessage, timestamp : new Date().toISOString()}, { message : bigMessage, timestamp : new Date().toISOString()} ]; lib.upload(aws, 'group', 'stream', events, 0, { ensureGroupPresent: true }, function(err) { aws.putLogEvents.calledOnce.should.equal(true); aws.putLogEvents.args[0][0].logEvents.length.should.equal(3); // First Batch // Now, finish. lib.upload(aws, 'group', 'stream', events, 0, { ensureGroupPresent: true }, function(err) { aws.putLogEvents.args[1][0].logEvents.length.should.equal(2); // Second Batch done() }); }); }); it('puts log events', function(done) { lib.upload(aws, 'group', 'stream', Array(20), 0, { ensureGroupPresent: true }, function() { aws.putLogEvents.calledOnce.should.equal(true); aws.putLogEvents.args[0][0].logGroupName.should.equal('group'); aws.putLogEvents.args[0][0].logStreamName.should.equal('stream'); aws.putLogEvents.args[0][0].logEvents.length.should.equal(20); aws.putLogEvents.args[0][0].sequenceToken.should.equal('token'); done(); }); }); it('adds token to the payload only if it exists', function(done) { lib.getToken.yields(null); lib.upload(aws, 'group', 'stream', Array(20), { ensureGroupPresent: true }, 0, function() { aws.putLogEvents.calledOnce.should.equal(true); aws.putLogEvents.args[0][0].logGroupName.should.equal('group'); aws.putLogEvents.args[0][0].logStreamName.should.equal('stream'); aws.putLogEvents.args[0][0].logEvents.length.should.equal(20); should.not.exist(aws.putLogEvents.args[0][0].sequenceToken); done(); }); }); it('does not put if events are empty', function(done) { lib.upload(aws, 'group', 'stream', [], 0, { ensureGroupPresent: true }, function() { aws.putLogEvents.called.should.equal(false); done(); }); }); it('errors if getting the token errors', function(done) { lib.getToken.yields('err'); lib.upload(aws, 'group', 'stream', Array(20), 0, { ensureGroupPresent: true }, function(err) { err.should.equal('err'); done(); }); }); it('errors if putting log events errors', function(done) { aws.putLogEvents.yields('err'); lib.upload(aws, 'group', 'stream', Array(20), 0, { ensureGroupPresent: true }, function(err) { err.should.equal('err'); done(); }); }); it('gets another token if InvalidSequenceTokenException', function(done) { aws.putLogEvents.yields({ name: 'InvalidSequenceTokenException' }); lib.upload(aws, 'group', 'stream', Array(20), 0, { ensureGroupPresent: true }, function(err) { lib.submitWithAnotherToken.calledOnce.should.equal(true); done(); }); }); it('gets another token if ResourceNotFoundException', function(done) { aws.putLogEvents.yields({ name: 'InvalidSequenceTokenException' }); lib.upload(aws, 'group', 'stream', Array(20), 0, { ensureGroupPresent: true }, function(err) { lib.submitWithAnotherToken.calledOnce.should.equal(true); done(); }); }); it('nextToken is saved when available', function(done) { var nextSequenceToken = 'abc123'; aws.putLogEvents.yields(null, { nextSequenceToken: nextSequenceToken }); lib.upload(aws, 'group', 'stream', Array(20), 0, { ensureGroupPresent: true }, function() { sinon.assert.match(lib._nextToken, { 'group:stream': nextSequenceToken }); done(); }); }); }); describe('putRetentionPolicy', function() { var aws = {}; beforeEach(function() { aws.putRetentionPolicy = sinon.stub().returns(); }); it('only logs retention policy if given > 0', function() { lib.putRetentionPolicy(aws, 'group', 1); aws.putRetentionPolicy.calledOnce.should.equal(true); }); it('doesnt logs retention policy if given = 0', function() { lib.putRetentionPolicy(aws, 'group', 0); aws.putRetentionPolicy.calledOnce.should.equal(false); }); }); describe('getToken', function() { var aws; beforeEach(function() { sinon.stub(lib, 'ensureGroupPresent').yields(); sinon.stub(lib, 'getStream').yields(); }); afterEach(function() { lib.ensureGroupPresent.restore(); lib.getStream.restore(); }); it('ensures group and stream are present if no nextToken for group/stream', function(done) { lib.getToken(aws, 'group', 'stream', 0, { ensureGroupPresent: true }, function() { lib.ensureGroupPresent.calledOnce.should.equal(true); lib.getStream.calledOnce.should.equal(true); done(); }); }); it('yields token when group and stream are present', function(done) { lib.ensureGroupPresent.yields(null, true); lib.getStream.yields(null, { uploadSequenceToken: 'token' }); lib.getToken(aws, 'group', 'stream', 0, { ensureGroupPresent: true }, function(err, token) { should.not.exist(err); token.should.equal('token'); done(); }); }); it('errors when ensuring group errors', function(done) { lib.ensureGroupPresent.yields('err'); lib.getToken(aws, 'group', 'stream', 0, { ensureGroupPresent: true }, function(err) { err.should.equal('err'); done(); }); }); it('errors when ensuring stream errors', function(done) { lib.getStream.yields('err'); lib.getToken(aws, 'group', 'stream', 0, { ensureGroupPresent: true }, function(err) { err.should.equal('err'); done(); }); }); it('does not ensure group and stream are present if nextToken for group/stream', function(done) { lib._nextToken = { 'group:stream': 'test123' }; lib.getToken(aws, 'group', 'stream', 0, { ensureGroupPresent: true }, function() { lib.ensureGroupPresent.notCalled.should.equal(true); lib.getStream.notCalled.should.equal(true); done(); }); }); }); describe('ensureGroupPresent', function() { var aws; beforeEach(function() { aws = { describeLogStreams: function(params, cb) { cb(null, {}); } }; lib.putRetentionPolicy = sinon.stub(); }); it('makes sure that a group is present', function(done) { lib.ensureGroupPresent(aws, 'group', 0, function(err, isPresent) { should.not.exist(err); isPresent.should.equal(true); lib.putRetentionPolicy.calledWith(aws, 'group', 0).should.equal(true); done(); }); }); it('creates a group if it is not present', function(done) { var err = { name: 'ResourceNotFoundException' }; aws.describeLogStreams = sinon.stub().yields(err); aws.createLogGroup = sinon.stub().yields(null); lib.ensureGroupPresent(aws, 'group', 0, function(err, isPresent) { should.not.exist(err); lib.putRetentionPolicy.calledWith(aws, 'group', 0).should.equal(true); isPresent.should.equal(true); done(); }); }); it('errors if looking for a group errors', function(done) { aws.describeLogStreams = sinon.stub().yields('err'); lib.ensureGroupPresent(aws, 'group', 0, function(err) { err.should.equal('err'); done(); }); }); it('errors if creating a group errors', function(done) { var err = { name: 'ResourceNotFoundException' }; aws.describeLogStreams = sinon.stub().yields(err); aws.createLogGroup = sinon.stub().yields('err'); lib.ensureGroupPresent(aws, 'group', 0, function(err) { err.should.equal('err'); lib.putRetentionPolicy.calledOnce.should.equal(false); done(); }); }); }); describe('getStream', function() { var aws; beforeEach(function() { aws = { describeLogStreams: function(params, cb) { cb(null, { logStreams: [{ logStreamName: 'stream' }, { logStreamName: 'another-stream' }] }); } }; }); it('yields the stream we want', function(done) { lib.getStream(aws, 'group', 'stream', function(err, stream) { stream.logStreamName.should.equal('stream'); done(); }); }); it('errors if getting streams errors', function(done) { aws.describeLogStreams = function(params, cb) { cb('err'); }; lib.getStream(aws, 'group', 'stream', function(err, stream) { should.not.exist(stream); err.should.equal('err'); done(); }); }); it('errors if creating stream errors', function(done) { aws.describeLogStreams = sinon.stub().yields(null, []); aws.createLogStream = function(params, cb) { cb('err'); }; lib.getStream(aws, 'group', 'stream', function(err, stream) { should.not.exist(stream); err.should.equal('err'); done(); }); }); it('ignores in progress error (aborted)', function(done) { aws.describeLogStreams = sinon.stub(); aws.describeLogStreams .onCall(0).yields(null, []) .onCall(1).yields(null, { logStreams: [{ logStreamName: 'stream' }, { logStreamName: 'another-stream' }] }); var err = { name: 'OperationAbortedException' }; aws.createLogStream = sinon.stub().yields(err); lib.getStream(aws, 'group', 'stream', function(err, stream) { should.exist({ logStreamName: 'stream' }); should.not.exist(err); done(); }); }); it('ignores in progress error (already exist)', function(done) { aws.describeLogStreams = sinon.stub(); aws.describeLogStreams .onCall(0).yields(null, []) .onCall(1).yields(null, { logStreams: [{ logStreamName: 'stream' }, { logStreamName: 'another-stream' }] }); err = { name: 'ResourceAlreadyExistsException' }; aws.createLogStream = sinon.stub().yields(err); lib.getStream(aws, 'group', 'stream', function(err, stream) { should.exist({ logStreamName: 'stream' }); should.not.exist(err); done(); }); }); }); describe('ignoreInProgress', function() { it('can be used to filter callback errors', function(done) { function typicalCallback(err, result) { err.should.equal('err'); result.should.equal('result'); done(); } var filter = lib.ignoreInProgress(typicalCallback); filter.should.be.an.instanceOf(Function); filter('err', 'result'); }); it('ignores a OperationAbortedException', function(done) { function runner(cb) { var err = { name: 'OperationAbortedException' }; cb(err); } runner(lib.ignoreInProgress(function(err) { should.not.exist(err); done(); })); }); it('ignores a ResourceAlreadyExistsException', function(done) { function runner(cb) { var err = { name: 'ResourceAlreadyExistsException' }; cb(err); } runner(lib.ignoreInProgress(function(err) { should.not.exist(err); done(); })); }); it('does not ignore any other error', function(done) { function runner(cb) { var err = { code: 'BoatTooLittleException' }; cb(err); } runner(lib.ignoreInProgress(function(err) { should.exist(err); err.code.should.equal('BoatTooLittleException'); done(); })); }); }); describe('submitWithAnotherToken', function() { var aws = {}; beforeEach(function() { aws.putLogEvents = sinon.stub().yields(); sinon.stub(lib, 'getToken').yields(null, 'new-token'); sinon.stub(console, 'error'); }); afterEach(function() { lib.getToken.restore(); console.error.restore(); }); it('gets a token then resubmits', function(done) { lib.submitWithAnotherToken(aws, 'group', 'stream', {}, 0,{ ensureGroupPresent: true }, function() { aws.putLogEvents.calledOnce.should.equal(true); aws.putLogEvents.args[0][0].sequenceToken.should.equal('new-token'); done(); }); }); }); describe('clearSequenceToken', function() { var aws = {}; beforeEach(function() { sinon.stub(lib, 'getToken').yields(null, 'token'); }); it('clears sequence token set by upload', function(done) { var nextSequenceToken = 'abc123'; var group = 'group'; var stream = 'stream'; aws.putLogEvents = sinon.stub().yields(null, { nextSequenceToken: nextSequenceToken }); lib.upload(aws, group, stream, Array(20), 0, {}, function() { lib._nextToken.should.deepEqual({ 'group:stream': nextSequenceToken }); lib.clearSequenceToken(group, stream); lib._nextToken.should.deepEqual({}); done(); }); }); afterEach(function() { lib.getToken.restore(); }); }) });