Mike Gerwitz

Activist for User Freedom

aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'test/system')
-rw-r--r--test/system/DeltaProcessorTest.ts312
-rw-r--r--test/system/DeltaPublisherTest.ts221
-rw-r--r--test/system/EventLoggerTest.ts103
-rw-r--r--test/system/EventMediatorTest.ts139
-rw-r--r--test/system/MetricsCollectorTest.ts26
-rw-r--r--test/system/StandardLoggerTest.ts178
6 files changed, 772 insertions, 207 deletions
diff --git a/test/system/DeltaProcessorTest.ts b/test/system/DeltaProcessorTest.ts
index d0cd8f8..3998d68 100644
--- a/test/system/DeltaProcessorTest.ts
+++ b/test/system/DeltaProcessorTest.ts
@@ -22,7 +22,8 @@
import { DeltaProcessor as Sut } from '../../src/system/DeltaProcessor';
import { AmqpPublisher } from '../../src/system/AmqpPublisher';
import { DeltaDao } from '../../src/system/db/DeltaDao';
-import { DeltaType } from "../../src/bucket/delta";
+import { DeltaType, DeltaDocument } from "../../src/bucket/delta";
+import { DocumentId } from '../../src/document/Document';
import { EventEmitter } from 'events';
import { expect, use as chai_use } from 'chai';
@@ -308,11 +309,12 @@ describe( 'system.DeltaProcessor', () =>
}[]>[
{
label: 'No deltas are processed',
- docs: [
+ given: [
{
id: 123,
lastUpdate: 123123123,
- bucket: {},
+ data: {},
+ ratedata: {},
rdelta: {},
},
],
@@ -324,7 +326,8 @@ describe( 'system.DeltaProcessor', () =>
{
id: 123,
lastUpdate: 123123123,
- bucket: { foo: [ 'start_bar' ] },
+ data: { foo: [ 'start_bar' ] },
+ ratedata: {},
rdelta: {
data: [
{
@@ -341,14 +344,16 @@ describe( 'system.DeltaProcessor', () =>
],
expected: [
{
- delta: { foo: [ 'first_bar' ] },
- bucket: { foo: [ 'first_bar' ] },
- doc_id: 123,
+ doc_id: 123,
+ delta: { foo: [ 'first_bar' ] },
+ bucket: { foo: [ 'first_bar' ] },
+ ratedata: {},
},
{
- delta: { foo: [ 'second_bar' ] },
- bucket: { foo: [ 'second_bar' ] },
- doc_id: 123,
+ doc_id: 123,
+ delta: { foo: [ 'second_bar' ] },
+ bucket: { foo: [ 'second_bar' ] },
+ ratedata: {},
},
],
},
@@ -358,17 +363,18 @@ describe( 'system.DeltaProcessor', () =>
{
id: 123,
lastUpdate: 123123123,
- bucket: { foo: 'start_bar' },
+ data: { foo: [ 'start_bar_123' ] },
+ ratedata: {},
rdelta: {
data: [
{
- data: { foo: [ 'second_bar' ] },
+ data: { foo: [ 'second_bar_123' ] },
timestamp: 234,
},
],
ratedata: [
{
- data: { foo: [ 'first_bar' ] },
+ data: { foo: [ 'first_bar_123' ] },
timestamp: 123,
},
],
@@ -377,19 +383,20 @@ describe( 'system.DeltaProcessor', () =>
{
id: 234,
lastUpdate: 123123123,
- bucket: { foo: 'start_bar' },
+ data: { foo: [ 'start_bar_234' ] },
+ ratedata: {},
rdelta: {
data: [
{
- data: { foo: [ 'first_bar' ] },
+ data: { foo: [ 'first_bar_234' ] },
timestamp: 123,
},
{
- data: { foo: [ 'second_bar' ] },
+ data: { foo: [ 'second_bar_234' ] },
timestamp: 234,
},
{
- data: { foo: [ 'third_bar' ] },
+ data: { foo: [ 'third_bar_234' ] },
timestamp: 345,
},
],
@@ -398,15 +405,16 @@ describe( 'system.DeltaProcessor', () =>
{
id: 345,
lastUpdate: 123123123,
- bucket: { foo: 'start_bar' },
+ data: { foo: [ 'start_bar_345' ] },
+ ratedata: {},
rdelta: {
ratedata: [
{
- data: { foo: [ 'first_bar' ] },
+ data: { foo: [ 'first_bar_345' ] },
timestamp: 123,
},
{
- data: { foo: [ 'second_bar' ] },
+ data: { foo: [ 'second_bar_345' ] },
timestamp: 234,
},
],
@@ -415,60 +423,73 @@ describe( 'system.DeltaProcessor', () =>
],
expected: [
{
- delta: { foo: [ 'first_bar' ] },
- bucket: { foo: [ 'first_bar' ] },
- doc_id: 123,
+ doc_id: 123,
+ delta: { foo: [ 'first_bar_123' ] },
+ bucket: { foo: [ 'start_bar_123' ] },
+ ratedata: { foo: [ 'first_bar_123' ] },
},
{
- delta: { foo: [ 'second_bar' ] },
- bucket: { foo: [ 'second_bar' ] },
- doc_id: 123,
+ doc_id: 123,
+ delta: { foo: [ 'second_bar_123' ] },
+ bucket: { foo: [ 'second_bar_123' ] },
+ ratedata: { foo: [ 'first_bar_123' ] },
},
{
- delta: { foo: [ 'first_bar' ] },
- bucket: { foo: [ 'first_bar' ] },
- doc_id: 234,
+ doc_id: 234,
+ delta: { foo: [ 'first_bar_234' ] },
+ bucket: { foo: [ 'first_bar_234' ] },
+ ratedata: {},
},
{
- delta: { foo: [ 'second_bar' ] },
- bucket: { foo: [ 'second_bar' ] },
- doc_id: 234,
+ doc_id: 234,
+ delta: { foo: [ 'second_bar_234' ] },
+ bucket: { foo: [ 'second_bar_234' ] },
+ ratedata: {},
},
{
- delta: { foo: [ 'third_bar' ] },
- bucket: { foo: [ 'third_bar' ] },
- doc_id: 234,
+ doc_id: 234,
+ delta: { foo: [ 'third_bar_234' ] },
+ bucket: { foo: [ 'third_bar_234' ] },
+ ratedata: {},
},
{
- delta: { foo: [ 'first_bar' ] },
- bucket: { foo: [ 'first_bar' ] },
- doc_id: 345,
+ doc_id: 345,
+ delta: { foo: [ 'first_bar_345' ] },
+ bucket: { foo: [ 'start_bar_345' ] },
+ ratedata: { foo: [ 'first_bar_345' ] },
},
{
- delta: { foo: [ 'second_bar' ] },
- bucket: { foo: [ 'second_bar' ] },
- doc_id: 345,
+ doc_id: 345,
+ delta: { foo: [ 'second_bar_345' ] },
+ bucket: { foo: [ 'start_bar_345' ] },
+ ratedata: { foo: [ 'second_bar_345' ] },
},
],
},
- ] ).forEach( ( { given, expected, label } ) => it( label, () =>
+ ] ).forEach( ( { label, given, expected } ) => it( label, () =>
{
let published: any = [];
const dao = createMockDeltaDao();
const publisher = createMockDeltaPublisher();
const emitter = new EventEmitter();
- dao.getUnprocessedDocuments = (): Promise<Record<string, any>[]> =>
+ dao.getUnprocessedDocuments = (): Promise<DeltaDocument[]> =>
{
return Promise.resolve( given );
}
- publisher.publish = ( delta, bucket, doc_id ): Promise<void> =>
+ publisher.publish = (
+ doc_id,
+ delta,
+ bucket,
+ ratedata,
+ ): Promise<void> =>
{
published.push( {
- delta: delta.data,
- bucket: bucket,
- doc_id: doc_id,
+ doc_id: doc_id,
+ delta: delta.data,
+ bucket: bucket,
+ ratedata: ratedata,
} );
return Promise.resolve();
@@ -479,6 +500,203 @@ describe( 'system.DeltaProcessor', () =>
.then( _ => expect( published ).to.deep.equal( expected ) );
} ) );
} );
+
+
+ describe( 'Error handling', () =>
+ {
+ it( 'Marks document in error state and continues', () =>
+ {
+ let published: any = [];
+ let error_flag_set = false;
+ const dao = createMockDeltaDao();
+ const publisher = createMockDeltaPublisher();
+ const emitter = new EventEmitter();
+ const doc = <DeltaDocument[]>[ {
+ id: <DocumentId>123,
+ lastUpdate: <UnixTimestamp>123123123,
+ data: { foo: [ 'start_bar' ] },
+ ratedata: {},
+ rdelta: {
+ data: [
+ {
+ data: { foo: [ 'first_bar' ] },
+ timestamp: <UnixTimestamp>123123,
+ type: 'data',
+ }
+ ],
+ ratedata: [],
+ },
+ },
+ {
+ id: <DocumentId>234,
+ lastUpdate: <UnixTimestamp>123123123,
+ data: { foo: [ 'start_bar' ] },
+ ratedata: {},
+ rdelta: {
+ data: [
+ {
+ data: { foo: [ 'first_bar' ] },
+ timestamp: <UnixTimestamp>123123,
+ type: 'data',
+ }
+ ],
+ ratedata: [],
+ },
+ } ];
+
+ const expected_published = [
+ {
+ doc_id: 123,
+ delta: { foo: [ 'first_bar' ] },
+ bucket: { foo: [ 'first_bar' ] },
+ ratedata: {},
+ },
+ {
+ doc_id: 234,
+ delta: { foo: [ 'first_bar' ] },
+ bucket: { foo: [ 'first_bar' ] },
+ ratedata: {},
+ }
+ ];
+
+ const expected_error = 'Uh oh';
+
+ dao.getUnprocessedDocuments = (): Promise<DeltaDocument[]> =>
+ Promise.resolve( doc );
+
+ dao.markDocumentAsProcessed = ( _doc_id, _ts ): Promise<void> =>
+ Promise.reject( new Error( expected_error ) );
+
+ dao.setErrorFlag = (): Promise<void> =>
+ {
+ error_flag_set = true;
+ return Promise.resolve();
+ }
+
+ publisher.publish = (
+ doc_id,
+ delta,
+ bucket,
+ ratedata,
+ ): Promise<void> =>
+ {
+ published.push( {
+ doc_id: doc_id,
+ delta: delta.data,
+ bucket: bucket,
+ ratedata: ratedata,
+ } );
+
+ return Promise.resolve();
+ }
+
+ // Prevent node from converting an error event into an error
+ emitter.on( 'error', () => {} );
+
+ return expect( new Sut( dao, publisher, emitter ).process() )
+ .to.eventually.deep.equal( undefined )
+ .then( _ =>
+ {
+ expect( error_flag_set ).to.be.true;
+ expect( published ).to.deep.equal( expected_published );
+ } );
+ } );
+ } );
+
+
+ describe( 'Error handling', () =>
+ {
+ it( 'Failure to set document error state further processing', () =>
+ {
+ let published: any = [];
+ let caught_error = '';
+ const dao = createMockDeltaDao();
+ const publisher = createMockDeltaPublisher();
+ const emitter = new EventEmitter();
+ const doc = <DeltaDocument[]>[ {
+ id: <DocumentId>123,
+ lastUpdate: <UnixTimestamp>123123123,
+ data: { foo: [ 'start_bar' ] },
+ ratedata: {},
+ rdelta: {
+ data: [
+ {
+ data: { foo: [ 'first_bar' ] },
+ timestamp: <UnixTimestamp>123123,
+ type: 'data',
+ }
+ ],
+ ratedata: [],
+ },
+ },
+ {
+ id: <DocumentId>234,
+ lastUpdate: <UnixTimestamp>123123123,
+ data: { foo: [ 'start_bar' ] },
+ ratedata: {},
+ rdelta: {
+ data: [
+ {
+ data: { foo: [ 'first_bar' ] },
+ timestamp: <UnixTimestamp>123123,
+ type: 'data',
+ }
+ ],
+ ratedata: [],
+ },
+ } ];
+
+ // Only one is published
+ const expected_published = [ {
+ doc_id: 123,
+ delta: { foo: [ 'first_bar' ] },
+ bucket: { foo: [ 'first_bar' ] },
+ ratedata: {},
+ } ];
+
+ const expected_error = 'Uh oh';
+
+ dao.getUnprocessedDocuments = (): Promise<DeltaDocument[]> =>
+ Promise.resolve( doc );
+
+ dao.markDocumentAsProcessed = ( _doc_id, _ts ): Promise<void> =>
+ Promise.reject( new Error( 'Couldn\'t mark document' ) );
+
+ dao.setErrorFlag = (): Promise<void> =>
+ Promise.reject( new Error( expected_error ) );
+
+ publisher.publish = (
+ doc_id,
+ delta,
+ bucket,
+ ratedata,
+ ): Promise<void> =>
+ {
+ published.push( {
+ doc_id: doc_id,
+ delta: delta.data,
+ bucket: bucket,
+ ratedata: ratedata,
+ } );
+
+ return Promise.resolve();
+ }
+
+ // Prevent node from converting an error event into an error
+ emitter.on( 'error', () => {} );
+
+ return expect(
+ new Sut( dao, publisher, emitter ).process()
+ .catch( e => { caught_error = e.message } )
+ )
+ .to.eventually.deep.equal( undefined )
+ .then( _ =>
+ {
+ expect( caught_error ).to.equal( expected_error );
+ expect( published ).to.deep.equal( expected_published );
+ } );
+ } );
+ } );
} );
diff --git a/test/system/DeltaPublisherTest.ts b/test/system/DeltaPublisherTest.ts
index 40663d0..fcb788c 100644
--- a/test/system/DeltaPublisherTest.ts
+++ b/test/system/DeltaPublisherTest.ts
@@ -19,13 +19,25 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+import { AmqpConnection } from '../../src/system/amqp/AmqpConnection';
+import { Delta, DeltaResult, DeltaType } from '../../src/bucket/delta';
import { DeltaPublisher as Sut } from '../../src/system/DeltaPublisher';
-import { AmqpConfig } from '../../src/system/AmqpPublisher';
+import { DocumentId } from '../../src/document/Document';
+import { Duplex } from 'stream';
import { EventEmitter } from "events";
+import { hasContext } from '../../src/error/ContextError';
+import { AmqpError } from '../../src/error/AmqpError';
+import { Channel } from 'amqplib';
+import {
+ createAvroEncoder,
+ AvroEncoderCtr,
+ AvroSchema,
+} from '../../src/system/avro/AvroFactory';
import { expect, use as chai_use } from 'chai';
chai_use( require( 'chai-as-promised' ) );
+const sinon = require( 'sinon' );
describe( 'server.DeltaPublisher', () =>
{
@@ -33,24 +45,96 @@ describe( 'server.DeltaPublisher', () =>
{
it( 'sends a message', () =>
{
- const conf = createMockConf();
- const emitter = new EventEmitter();
+ let publish_called = false;
+ const delta = createMockDelta();
+ const bucket = createMockBucketData();
+ const ratedata = createMockBucketData();
+ const emitter = new EventEmitter();
+ const conn = createMockAmqpConnection();
+ conn.getAmqpChannel = () =>
+ {
+ return <Channel>{
+ publish: ( _: any, __: any, buf: any, ___: any ) =>
+ {
+ expect( buf instanceof Buffer ).to.be.true;
- console.log( new Sut( conf, emitter, ts_ctr ) );
- expect( true ).to.be.true
+ publish_called = true;
+
+ return true;
+ }
+ };
+ };
+
+ const sut = new Sut( emitter, ts_ctr, createAvroEncoder, conn );
+
+ return expect(
+ sut.publish( <DocumentId>123, delta, bucket, ratedata )
+ ).to.eventually.deep.equal( undefined )
+ .then( _ =>
+ {
+ expect( publish_called ).to.be.true;
+ } );
} );
- } );
- describe( '#sendMessage', () =>
- {
- it( 'sends a message', () =>
+ ( <[string, () => Channel | undefined, Error, string ][]>[
+ [
+ 'Throws an error when publishing was unsuccessful',
+ () =>
+ {
+ return <Channel>{
+ publish: ( _: any, __: any, _buf: any, ___: any ) =>
+ {
+ return false;
+ }
+ };
+ },
+ Error,
+ 'Delta publish failed'
+ ],
+ [
+ 'Throws an error when no amqp channel is found',
+ () =>
+ {
+ return undefined;
+ },
+ AmqpError,
+ 'Error sending message: No channel'
+ ]
+ ] ).forEach( ( [ label, getChannelF, error_type, err_msg ] ) =>
+ it( label, () =>
{
- const conf = createMockConf();
- const emitter = new EventEmitter();
+ const delta = createMockDelta();
+ const bucket = createMockBucketData();
+ const ratedata = createMockBucketData();
+ const emitter = new EventEmitter();
+ const conn = createMockAmqpConnection();
+ const doc_id = <DocumentId>123;
+ const expected = {
+ doc_id: doc_id,
+ delta_type: delta.type,
+ delta_ts: delta.timestamp
+ }
- console.log( new Sut( conf, emitter, ts_ctr ) );
- expect( true ).to.be.true
- } );
+ conn.getAmqpChannel = getChannelF;
+
+ const result = new Sut( emitter, ts_ctr, createAvroEncoder, conn )
+ .publish( doc_id, delta, bucket, ratedata );
+
+ return Promise.all( [
+ expect( result ).to.eventually.be.rejectedWith(
+ error_type, err_msg
+ ),
+ result.catch( e =>
+ {
+ if ( !hasContext( e ) )
+ {
+ return expect.fail();
+ }
+
+ return expect( e.context ).to.deep.equal( expected );
+ } )
+ ] );
+ } ) );
} );
describe( '#avroEncode parses', () =>
@@ -137,32 +221,26 @@ describe( 'server.DeltaPublisher', () =>
{
it( label, () =>
{
- let errorCalled = false;
+ const emitter = createMockEventEmitter();
+ const conn = createMockAmqpConnection();
+ const data = createMockData( delta_data );
+ const sut = new Sut(
+ emitter,
+ ts_ctr,
+ createAvroEncoder,
+ conn,
+ );
- const emitter = <EventEmitter>{
- emit( _event_id, _err )
+ sut.avroEncode( data )
+ .then( b =>
{
- errorCalled = true;
-
- console.log( 'server.DeltaPublisher.Error' + _err );
- }
- }
-
- const conf = createMockConf();
- const data = createMockData( delta_data );
- const sut = new Sut( conf, emitter, ts_ctr );
- const buffer = sut.avroEncode( data );
-
- if ( valid )
- {
- expect( typeof(buffer) ).to.equal( 'object' );
- }
- else
- {
- expect( buffer ).to.equal( null );
- }
-
- expect( valid ).to.equal( !errorCalled );
+ expect( typeof(b) ).to.equal( 'object' );
+ expect( valid ).to.be.true;
+ } )
+ .catch( _ =>
+ {
+ expect( valid ).to.be.false;
+ } );
} );
} );
} );
@@ -301,9 +379,16 @@ describe( 'server.DeltaPublisher', () =>
{
it( label, () =>
{
- const emitter = <EventEmitter>{}
- const conf = createMockConf();
- const sut = new Sut( conf, emitter, ts_ctr );
+ const encoded = 'FooBar';
+ const emitter = createMockEventEmitter();
+ const conn = createMockAmqpConnection();
+ const avroEncoderCtr = createMockEncoder( encoded );
+ const sut = new Sut(
+ emitter,
+ ts_ctr,
+ avroEncoderCtr,
+ conn,
+ );
const actual = sut.setDataTypes( delta_data );
expect( actual ).to.deep.equal( expected );
@@ -312,14 +397,39 @@ describe( 'server.DeltaPublisher', () =>
} );
} );
+
function ts_ctr(): UnixTimestamp
{
return <UnixTimestamp>Math.floor( new Date().getTime() / 1000 );
}
-function createMockConf(): AmqpConfig
+
+function createMockEncoder( mock_encoded_data: string ): AvroEncoderCtr
+{
+ return ( _schema: AvroSchema ) =>
+ {
+ const mock = sinon.mock( Duplex );
+
+ mock.on = ( _: string, __: any ) => {};
+ mock.end = ( _: any ) => { return mock_encoded_data; };
+
+ return mock;
+ };
+}
+
+
+function createMockEventEmitter(): EventEmitter
+{
+ return <EventEmitter>{};
+}
+
+
+function createMockAmqpConnection(): AmqpConnection
{
- return <AmqpConfig>{};
+ return <AmqpConnection>{
+ connect: () => {},
+ getExchangeName: () => { 'Foo' },
+ };
}
@@ -339,11 +449,8 @@ function createMockData( delta_data: any ): any
modified: 1573856916,
top_visited_step: '2',
},
- session: {
- entity_name: 'Foobar',
- entity_id: 123123 ,
- },
- data: null,
+ data: null,
+ ratedata: null,
delta: {
Data: {
bucket: delta_data,
@@ -356,4 +463,22 @@ function createMockData( delta_data: any ): any
},
},
};
+}
+
+
+function createMockBucketData(): Record<string, any>
+{
+ return {
+ foo: [ 'bar', 'baz' ]
+ }
+}
+
+
+function createMockDelta(): Delta<any>
+{
+ return <Delta<any>>{
+ type: <DeltaType>'data',
+ timestamp: <UnixTimestamp>123123123,
+ data: <DeltaResult<any>>{},
+ }
} \ No newline at end of file
diff --git a/test/system/EventLoggerTest.ts b/test/system/EventLoggerTest.ts
deleted file mode 100644
index b3d5f0f..0000000
--- a/test/system/EventLoggerTest.ts
+++ /dev/null
@@ -1,103 +0,0 @@
-/**
- * Event logger test
- *
- * Copyright (C) 2010-2019 R-T Specialty, LLC.
- *
- * This file is part of the Liza Data Collection Framework.
- *
- * liza is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-import { EventLogger as Sut } from '../../src/system/EventLogger';
-import { EventEmitter } from "events";
-import { expect } from 'chai';
-
-const sinon = require( 'sinon' );
-
-declare interface MockConsole extends Console {
- getLevel(): string,
-}
-
-describe( 'system.EventLogger captures and logs events', () =>
-{
- [
- {
- event_id: 'document-processed',
- console_level: 'log',
- },
- {
- event_id: 'delta-publish',
- console_level: 'log',
- },
- {
- event_id: 'amqp-conn-error',
- console_level: 'warn',
- },
- {
- event_id: 'amqp-reconnect',
- console_level: 'warn',
- },
- {
- event_id: 'amqp-reconnect-fail',
- console_level: 'error',
- },
- {
- event_id: 'avro-err',
- console_level: 'error',
- },
- {
- event_id: 'dao-err',
- console_level: 'error',
- },
- {
- event_id: 'publish-err',
- console_level: 'error',
- },
- ].forEach( ( { event_id, console_level } ) =>
- {
- it( event_id + ' triggers console output level: ' + console_level, () =>
- {
- const emitter = new EventEmitter();
- const con = createMockConsole();
- const env = 'test';
-
- new Sut( con, env, emitter, ts_ctr );
-
- emitter.emit( event_id );
-
- expect( con.getLevel() ).to.equal( console_level );
- } );
- } );
-} );
-
-
-function ts_ctr(): UnixTimestamp
-{
- return <UnixTimestamp>Math.floor( new Date().getTime() / 1000 );
-}
-
-
-function createMockConsole(): MockConsole
-{
- const mock = sinon.mock( console );
-
- mock.level = '';
- mock.info = ( _str: string ) => { mock.level = 'info'; };
- mock.log = ( _str: string ) => { mock.level = 'log'; };
- mock.warn = ( _str: string ) => { mock.level = 'warn'; };
- mock.error = ( _str: string ) => { mock.level = 'error'; };
- mock.getLevel = () => mock.level;
-
- return mock;
-} \ No newline at end of file
diff --git a/test/system/EventMediatorTest.ts b/test/system/EventMediatorTest.ts
new file mode 100644
index 0000000..581437c
--- /dev/null
+++ b/test/system/EventMediatorTest.ts
@@ -0,0 +1,139 @@
+/**
+ * Event logger test
+ *
+ * Copyright (C) 2010-2019 R-T Specialty, LLC.
+ *
+ * This file is part of the Liza Data Collection Framework.
+ *
+ * liza is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import { EventMediator as Sut } from '../../src/system/EventMediator';
+import { context } from '../../src/error/ContextError';
+import { EventEmitter } from "events";
+import { expect } from 'chai';
+import { PsrLogger } from '../../src/system/PsrLogger';
+
+
+describe( 'system.EventLogger captures and logs events', () =>
+{
+ it( 'document-processed triggers log#notice', () =>
+ {
+ let method_called = false;
+
+ const event_id = 'document-processed';
+ const emitter = new EventEmitter();
+ const log = createMockLogger();
+
+ log.notice = ( _str: string ) => { method_called = true; };
+
+ new Sut( log, emitter );
+
+ emitter.emit( event_id );
+
+ expect( method_called ).to.be.true;
+ } );
+
+ it( 'delta-publish triggers log#notice', () =>
+ {
+ let method_called = false;
+
+ const event_id = 'delta-publish';
+ const emitter = new EventEmitter();
+ const log = createMockLogger();
+
+ log.notice = ( _str: string ) => { method_called = true; };
+
+ new Sut( log, emitter );
+
+ emitter.emit( event_id );
+
+ expect( method_called ).to.be.true;
+ } );
+
+ it( 'amqp-conn-error triggers log#warning', () =>
+ {
+ let method_called = false;
+
+ const event_id = 'amqp-conn-error';
+ const emitter = new EventEmitter();
+ const log = createMockLogger();
+
+ log.warning = ( _str: string ) => { method_called = true; };
+
+ new Sut( log, emitter );
+
+ emitter.emit( event_id );
+
+ expect( method_called ).to.be.true;
+ } );
+
+ it( 'amqp-reconnect triggers log#warning', () =>
+ {
+ let method_called = false;
+
+ const event_id = 'amqp-reconnect';
+ const emitter = new EventEmitter();
+ const log = createMockLogger();
+
+ log.warning = ( _str: string ) => { method_called = true; };
+
+ new Sut( log, emitter );
+
+ emitter.emit( event_id );
+
+ expect( method_called ).to.be.true;
+ } );
+
+ it( 'context is retrieved from error', () =>
+ {
+ let method_called = false;
+
+ const event_id = 'error';
+ const err_msg = 'Foo';
+ const emitter = new EventEmitter();
+ const log = createMockLogger();
+ const err_context = { bar: 'baz' };
+
+ log.error = ( str: string, context: any ) =>
+ {
+ method_called = true;
+
+ expect( str ).to.equal( err_msg );
+ expect( context ).to.equal( err_context );
+ };
+
+ new Sut( log, emitter );
+
+ emitter.emit( event_id, context( new Error( err_msg ), err_context ) );
+
+ expect( method_called ).to.be.true;
+ } );
+} );
+
+
+function createMockLogger(): PsrLogger
+{
+ return <PsrLogger>{
+ debug( _msg: string | object, _context: object ){},
+ info( _msg: string | object, _context: object ){},
+ notice( _msg: string | object, _context: object ){ console.log( 'asdasd msg: ', _msg ); },
+ warning( _msg: string | object, _context: object ){},
+ error( _msg: string | object, _context: object ){},
+ critical( _msg: string | object, _context: object ){},
+ alert( _msg: string | object, _context: object ){},
+ emergency( _msg: string | object, _context: object ){},
+ log( _level: any, _msg: string | object, _context: object ){},
+ };
+} \ No newline at end of file
diff --git a/test/system/MetricsCollectorTest.ts b/test/system/MetricsCollectorTest.ts
index 07867a8..eafc77d 100644
--- a/test/system/MetricsCollectorTest.ts
+++ b/test/system/MetricsCollectorTest.ts
@@ -19,13 +19,15 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import { PrometheusFactory } from '../../src/system/PrometheusFactory';
+import {
+ PrometheusFactory,
+ PrometheusConfig,
+} from '../../src/system/PrometheusFactory';
import { Histogram, Pushgateway, Counter, Gauge } from 'prom-client';
import { EventEmitter } from 'events';
import { expect } from 'chai';
import {
MetricsCollector as Sut,
- PrometheusConfig,
MetricTimer,
} from '../../src/system/MetricsCollector';
@@ -35,8 +37,8 @@ describe( 'system.MetricsCollector captures events and pushes metrics', () =>
{
it( 'process-complete event is hooked', () =>
{
- let histogram_called = false;
- let counter_called = false;
+ let histogram_called = false;
+ let counter_called = false;
const emitter = new EventEmitter();
const conf = createMockConfig();
@@ -46,18 +48,20 @@ describe( 'system.MetricsCollector captures events and pushes metrics', () =>
counter_cb: () => { counter_called = true },
} );
- new Sut( factory, conf, emitter, timer );
+ const sut = new Sut( factory, conf, emitter, timer );
emitter.emit( 'delta-process-end' );
expect( histogram_called ).to.be.true;
expect( counter_called ).to.be.true;
+
+ sut.stop();
} );
it( 'process-error event is hooked', () =>
{
- let counter_called = false;
+ let counter_called = false;
const emitter = new EventEmitter();
const conf = createMockConfig();
@@ -66,11 +70,13 @@ describe( 'system.MetricsCollector captures events and pushes metrics', () =>
counter_cb: () => { counter_called = true },
} );
- new Sut( factory, conf, emitter, timer );
+ const sut = new Sut( factory, conf, emitter, timer );
emitter.emit( 'delta-process-error' );
expect( counter_called ).to.be.true;
+
+ sut.stop();
} );
@@ -80,7 +86,7 @@ describe( 'system.MetricsCollector captures events and pushes metrics', () =>
const uid = 'foo';
const start_time_ns = 1234;
const end_time_ns = 5678;
- const expected_ms = ( end_time_ns - start_time_ns ) / 1000;
+ const expected_ms = ( end_time_ns - start_time_ns ) / 1000000;
const emitter = new EventEmitter();
const conf = createMockConfig();
const timer = createMockTimer( start_time_ns, end_time_ns );
@@ -88,12 +94,14 @@ describe( 'system.MetricsCollector captures events and pushes metrics', () =>
histogram_cb: ( n: number ) => { actual_ms = n },
} );
- new Sut( factory, conf, emitter, timer );
+ const sut = new Sut( factory, conf, emitter, timer );
emitter.emit( 'delta-process-start', uid );
emitter.emit( 'delta-process-end', uid );
expect( actual_ms ).to.be.equal( expected_ms );
+
+ sut.stop();
} );
} );
diff --git a/test/system/StandardLoggerTest.ts b/test/system/StandardLoggerTest.ts
new file mode 100644
index 0000000..918bfd1
--- /dev/null
+++ b/test/system/StandardLoggerTest.ts
@@ -0,0 +1,178 @@
+/**
+ * Event logger test
+ *
+ * Copyright (C) 2010-2019 R-T Specialty, LLC.
+ *
+ * This file is part of the Liza Data Collection Framework.
+ *
+ * liza is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import { StandardLogger as Sut } from '../../src/system/StandardLogger';
+import { LogLevel } from '../../src/system/PsrLogger';
+import { expect } from 'chai';
+
+const sinon = require( 'sinon' );
+
+declare interface MockConsole extends Console {
+ getLevel(): string,
+ getStr(): string,
+}
+
+describe( 'system.EventLogger captures and logs events', () =>
+{
+ it( 'debug triggers console output level: info', () =>
+ {
+ const con = createMockConsole();
+ const env = 'test';
+ const sut = new Sut( con, ts_ctr, env );
+
+ sut.debug( 'Foo' );
+
+ expect( con.getLevel() ).to.equal( 'info' );
+ } );
+
+ it( 'info triggers console output level: info', () =>
+ {
+ const con = createMockConsole();
+ const env = 'test';
+ const sut = new Sut( con, ts_ctr, env );
+
+ sut.info( 'Foo' );
+
+ expect( con.getLevel() ).to.equal( 'info' );
+ } );
+
+ it( 'notice triggers console output level: log', () =>
+ {
+ const con = createMockConsole();
+ const env = 'test';
+ const sut = new Sut( con, ts_ctr, env );
+
+ sut.notice( 'Foo' );
+
+ expect( con.getLevel() ).to.equal( 'log' );
+ } );
+
+ it( 'warning triggers console output level: warn', () =>
+ {
+ const con = createMockConsole();
+ const env = 'test';
+ const sut = new Sut( con, ts_ctr, env );
+
+ sut.warning( 'Foo' );
+
+ expect( con.getLevel() ).to.equal( 'warn' );
+ } );
+
+ it( 'error triggers console output level: error', () =>
+ {
+ const con = createMockConsole();
+ const env = 'test';
+ const sut = new Sut( con, ts_ctr, env );
+
+ sut.error( 'Foo' );
+
+ expect( con.getLevel() ).to.equal( 'error' );
+ } );
+
+ it( 'critical triggers console output level: error', () =>
+ {
+ const con = createMockConsole();
+ const env = 'test';
+ const sut = new Sut( con, ts_ctr, env );
+
+ sut.critical( 'Foo' );
+
+ expect( con.getLevel() ).to.equal( 'error' );
+ } );
+
+ it( 'alert triggers console output level: error', () =>
+ {
+ const con = createMockConsole();
+ const env = 'test';
+ const sut = new Sut( con, ts_ctr, env );
+
+ sut.alert( 'Foo' );
+
+ expect( con.getLevel() ).to.equal( 'error' );
+ } );
+
+ it( 'emergency triggers console output level: error', () =>
+ {
+ const con = createMockConsole();
+ const env = 'test';
+ const sut = new Sut( con, ts_ctr, env );
+
+ sut.emergency( 'Foo' );
+
+ expect( con.getLevel() ).to.equal( 'error' );
+ } );
+
+ it( 'log triggers corresponding log level', () =>
+ {
+ const con = createMockConsole();
+ const env = 'test';
+ const sut = new Sut( con, ts_ctr, env );
+
+ sut.log( LogLevel.ERROR, 'Foo' );
+
+ expect( con.getLevel() ).to.equal( 'error' );
+ } );
+
+ it( 'Context is included in structured output', () =>
+ {
+ const con = createMockConsole();
+ const env = 'test';
+ const sut = new Sut( con, ts_ctr, env );
+ const context = { bar: 'baz' };
+ const expected_output = {
+ message: 'Foo',
+ timestamp: 123123,
+ service: 'quote-server',
+ env: 'test',
+ severity: 'NOTICE',
+ context: {
+ bar: 'baz',
+ },
+ };
+
+ sut.notice( 'Foo', context );
+
+ expect( con.getStr() ).to.deep.equal( expected_output );
+ } );
+} );
+
+
+function ts_ctr(): UnixTimestamp
+{
+ return <UnixTimestamp>123123;
+}
+
+
+function createMockConsole(): MockConsole
+{
+ const mock = sinon.mock( console );
+
+ mock.lvl = '';
+ mock.str = '';
+ mock.info = ( str: string ) => { mock.str = str; mock.lvl = 'info'; };
+ mock.log = ( str: string ) => { mock.str = str; mock.lvl = 'log'; };
+ mock.warn = ( str: string ) => { mock.str = str; mock.lvl = 'warn'; };
+ mock.error = ( str: string ) => { mock.str = str; mock.lvl = 'error'; };
+ mock.getLevel = () => mock.lvl;
+ mock.getStr = () => mock.str;
+
+ return mock;
+}