diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 4461efe..9ec1167 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -3,12 +3,13 @@ image: $BUILD_IMAGE
stages:
- build
- deploy
+ - publish
build:
stage: build
script:
- autoreconf -fvi
- - ./configure --with-srcuri="$SRCURI"
+ - ./configure --with-srcuri="$SRCURI" --enable-code-coverage
- npm install
- make all
- make info pdf html
@@ -16,6 +17,7 @@ build:
artifacts:
paths:
- doc/
+ - coverage/
expire_in: 30 min
pages:
@@ -23,9 +25,11 @@ pages:
script:
- mkdir -p public/doc
- mv doc/liza.html/* doc/liza.pdf doc/liza.info public/
+ - mkdir -p public/coverage
+ - mv coverage/* public/coverage
artifacts:
paths:
- public/
expire_in: 30 min
only:
- - tags
+ - tags
\ No newline at end of file
diff --git a/Makefile.am b/Makefile.am
index 9e0d6f8..2e7a45f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -67,10 +67,11 @@ check-ts-out:
test: check
check: $(tsout) check-ts-out
- PATH="$(PATH):$(CURDIR)/node_modules/mocha/bin" \
- mocha @NODE_DESTRUCTURE@ \
+ PATH="$(PATH):$(CURDIR)/node_modules/.bin" \
+ @CODE_COV@ mocha @NODE_DESTRUCTURE@ \
--require $(path_test)/pre.js \
--recursive \
+ @COV_ARGS@ \
$(TESTARGS)
FORCE:
diff --git a/configure.ac b/configure.ac
index 3a0fbc5..c5fc84d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -79,6 +79,16 @@ AC_ARG_WITH(
AC_SUBST([SET_SRCURI], [$set_srcuri])
+AC_ARG_ENABLE(
+ [code_coverage],
+ [AS_HELP_STRING([--enable-code-coverage],
+ [Generate a code coverage report when tests are run
+ (disabled by default)])],
+ AC_SUBST([CODE_COV], ["nyc --reporter=html --reporter=text --all --include=src \
+ --exclude=**/index.js --exclude=src/version.js"])
+ AC_SUBST([COV_ARGS], ["--require ts-node/register/transpile-only \
+ --require source-map-support/register"]))
+
AC_SUBST([AUTOGENERATED],
["THIS FILE IS AUTOGENERATED! DO NOT MODIFY! See *.in."])
diff --git a/package.json.in b/package.json.in
index 35abffe..4a99f08 100644
--- a/package.json.in
+++ b/package.json.in
@@ -43,7 +43,9 @@
"@types/mocha": "5.2.0",
"sinon": ">=1.17.4",
"es6-promise": "~3",
- "@types/amqplib": "0.5.13"
+ "@types/amqplib": "0.5.13",
+ "nyc": "15.0.0",
+ "ts-node": "^8.6.2"
},
"licenses": [
diff --git a/src/server/rater/DslRater.js b/src/server/rater/DslRater.js
index 490cd80..7c430bb 100644
--- a/src/server/rater/DslRater.js
+++ b/src/server/rater/DslRater.js
@@ -115,7 +115,8 @@ module.exports = Class( 'DslRater' )
{
try
{
- var single = rater( data );
+ var can_term = context.canTerm();
+ var single = rater( data, can_term );
// ensures that any previous eligibility errors are cleared out
single.ineligible = '';
diff --git a/src/server/rater/DslRaterContext.js b/src/server/rater/DslRaterContext.js
index 79dfaef..ebc5d88 100644
--- a/src/server/rater/DslRaterContext.js
+++ b/src/server/rater/DslRaterContext.js
@@ -37,13 +37,13 @@ module.exports = Class( 'DslRaterContext' )
* Hash of classes that will result in a global submit
* @type {Object}
*/
- 'private _globalSubmits': {},
+ 'private _global_submits': {},
/**
* Whether a particular global submit has been triggered
* @type {Object}
*/
- 'private _hasGsubmit': {},
+ 'private _has_g_submit': {},
/**
* Rater corestrictions
@@ -57,6 +57,12 @@ module.exports = Class( 'DslRaterContext' )
*/
'private _data': null,
+ /**
+ * Whether to immediately terminate on assertion failure
+ * @type {boolean}
+ */
+ 'private _can_term': true,
+
/**
* Result sets
* @type {Object}
@@ -67,18 +73,19 @@ module.exports = Class( 'DslRaterContext' )
* Number of available results
* @type {number}
*/
- 'private _availCount': 0,
+ 'private _avail_count': 0,
/**
* Total number of results
* @type {number}
*/
- 'private _totalCount': 0,
+ 'private _total_count': 0,
- __construct: function( data )
+ __construct: function( data, can_term )
{
- this._data = data;
+ this._data = data;
+ this._can_term = ( can_term == undefined ) ? true : !!can_term;
this.init();
},
@@ -90,6 +97,12 @@ module.exports = Class( 'DslRaterContext' )
},
+ 'public canTerm': function()
+ {
+ return this._can_term;
+ },
+
+
'virtual protected init': function()
{
// may be implemented by subtypes
@@ -118,8 +131,8 @@ module.exports = Class( 'DslRaterContext' )
*/
'public addResultSet': function( name, set )
{
- this._totalCount += set.getResultCount();
- this._availCount += set.getAvailableCount();
+ this._total_count += set.getResultCount();
+ this._avail_count += set.getAvailableCount();
this._checkGlobalSubmits( set );
@@ -146,11 +159,11 @@ module.exports = Class( 'DslRaterContext' )
return;
}
- for ( var cname in _self._globalSubmits )
+ for ( var cname in _self._global_submits )
{
if ( result.__classes[ cname ] )
{
- _self._hasGsubmit[ cname ] = true;
+ _self._has_g_submit[ cname ] = true;
}
}
} );
@@ -162,7 +175,7 @@ module.exports = Class( 'DslRaterContext' )
var i = submits.length;
while ( i-- )
{
- this._globalSubmits[ submits[ i ] ] = true;
+ this._global_submits[ submits[ i ] ] = true;
}
return this;
@@ -185,8 +198,8 @@ module.exports = Class( 'DslRaterContext' )
'public complete': function()
{
// allow context some time to manipulate the results mercilessly
- this._availCount = this.processCompleted(
- this._results, this._availCount
+ this._avail_count = this.processCompleted(
+ this._results, this._avail_count
);
this._processGlobalSubmits();
@@ -291,7 +304,7 @@ module.exports = Class( 'DslRaterContext' )
*/
'private _processGlobalSubmits': function()
{
- for ( var cname in this._hasGsubmit )
+ for ( var cname in this._has_g_submit )
{
this._results.forEach( function( set )
{
@@ -302,7 +315,7 @@ module.exports = Class( 'DslRaterContext' )
cname;
result._unavailable = '1';
- this._availCount--;
+ this._avail_count--;
} );
} );
}
@@ -340,7 +353,7 @@ module.exports = Class( 'DslRaterContext' )
} );
- ret.__prem_avail_count = [ this._availCount ];
+ ret.__prem_avail_count = [ this._avail_count ];
return ret;
}
diff --git a/test/server/rater/DslRaterContextTest.js b/test/server/rater/DslRaterContextTest.js
new file mode 100644
index 0000000..3481831
--- /dev/null
+++ b/test/server/rater/DslRaterContextTest.js
@@ -0,0 +1,66 @@
+/**
+ * Tests DslRaterContext
+ *
+ * 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 .
+ */
+
+'use strict';
+
+const root = require( '../../..' );
+const expect = require( 'chai' ).expect;
+
+const {
+ DslRaterContext: Sut,
+} = root.server.rater;
+
+describe( 'DslRaterContext', () =>
+{
+ describe( 'Defaults', () =>
+ {
+ it( `canTerm is true if not included`, () =>
+ {
+ const expected = true;
+ const data = { foo: 'bar' };
+ const sut = Sut( data );
+ const actual = sut.canTerm();
+
+ expect( actual ).to.equal( expected );
+ } );
+
+
+ it( `canTerm can be set to false`, () =>
+ {
+ const expected = false;
+ const data = { foo: 'bar' };
+ const sut = Sut( data, expected );
+ const actual = sut.canTerm();
+
+ expect( actual ).to.equal( expected );
+ } );
+
+
+ it( `data can be retrieved`, () =>
+ {
+ const expected = { foo: 'bar' };
+ const sut = Sut( expected );
+ const actual = sut.getSourceData();
+
+ expect( actual ).to.deep.equal( expected );
+ } );
+ } );
+} );
diff --git a/test/server/rater/DslRaterTest.js b/test/server/rater/DslRaterTest.js
new file mode 100644
index 0000000..6e83b00
--- /dev/null
+++ b/test/server/rater/DslRaterTest.js
@@ -0,0 +1,245 @@
+/**
+ * Tests DslRater
+ *
+ * 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 .
+ */
+
+'use strict';
+
+const root = require( '../../..' );
+const expect = require( 'chai' ).expect;
+const Class = require( 'easejs' ).Class;
+
+const {
+ DslRater: Sut,
+ DslRaterContext,
+} = root.server.rater;
+
+describe( 'DslRater', () =>
+{
+ describe( 'rate', () =>
+ {
+ it( `Calls context#rate`, () =>
+ {
+ let called = false;
+
+ const raters = [ getRaterStub(), getRaterStub() ];
+ const resultSet = getResultSetStub();
+ const override = {
+ 'override public rate': function( name, meta, rate, complete )
+ {
+ called = true
+ }
+ };
+
+ const context = getDslContext( override );
+ const sut = Sut( raters, resultSet );
+
+ sut.rate( context );
+
+ expect( called ).to.equal( true );
+ } );
+
+
+ it( `Throws exception on invalid context`, () =>
+ {
+ const raters = [ getRaterStub(), getRaterStub() ];
+ const resultSet = getResultSetStub();
+ const context = {};
+ const sut = Sut( raters, resultSet );
+
+ expect( () => { sut.rate( context ) } )
+ .to.throw( "Invalid DslRaterContext provided" );
+
+ } );
+
+
+ it( `Undefined rater calls context#complete`, () =>
+ {
+ let called = false;
+ let rateCalled = false;
+
+ const raters = [ undefined ];
+ const resultSet = getResultSetStub();
+ const override = {
+ 'override public rate': function( name, meta, rate, complete )
+ {
+ rateCalled = true
+ },
+
+ 'override public processCompleted': function( results, count )
+ {
+ called = true
+ }
+ };
+
+ const context = getDslContext( override );
+ const sut = Sut( raters, resultSet );
+
+ sut.rate( context );
+
+ expect( called ).to.equal( true );
+ expect( rateCalled ).to.equal( false );
+ } );
+
+
+ it( `Calls rater with canTerm from context`, () =>
+ {
+ let actual;
+
+ const expected = false;
+ const callback = ( data, canTerm ) =>
+ {
+ actual = canTerm;
+ called = true;
+ }
+
+ const raters = [ getRaterStub( callback ) ];
+ const resultSet = getResultSetStub();
+ const context = DslRaterContext( null, expected );
+ const sut = Sut( raters, resultSet );
+
+ sut.rate( context );
+
+ expect( actual ).to.equal( expected );
+ } );
+
+
+ it( `Submit or prohibit sets _unavailable flag`, () =>
+ {
+ let called = false;
+ let actual = {};
+
+ const expected = {
+ _unavailable: '1',
+ ineligible: '',
+ submit: 'true',
+ __classes: {
+ 'submit': true,
+ 'submit-foo': true,
+ },
+ };
+ const raterCb = ( _, __ ) => {};
+ const resultCb = ( name, set ) =>
+ {
+ actual = name;
+ called = true;
+ };
+ const classes = {
+ 'submit-foo': true,
+ submit: true,
+ };
+ const raters = [ getRaterStub( raterCb, classes ) ];
+ const resultSet = getResultSetStub( resultCb );
+ const context = DslRaterContext( null, expected );
+ const sut = Sut( raters, resultSet );
+
+ sut.rate( context );
+
+ expect( called ).to.equal( true );
+ expect( actual ).to.deep.equal( expected );
+ } );
+ } );
+
+
+ it( `_unavailable flag defaults to false`, () =>
+ {
+ let called = false;
+ let actual = {};
+
+ const expected = {
+ _unavailable: '0',
+ ineligible: '',
+ submit: '',
+ __classes: {
+ 'foo': true,
+ 'submit': false,
+ },
+ };
+ const raterCb = ( _, __ ) => {};
+ const resultCb = ( name, set ) =>
+ {
+ actual = name;
+ called = true;
+ };
+ const classes = {
+ 'foo': true,
+ submit: false,
+ };
+ const raters = [ getRaterStub( raterCb, classes ) ];
+ const resultSet = getResultSetStub( resultCb );
+ const context = DslRaterContext( null, expected );
+ const sut = Sut( raters, resultSet );
+
+ sut.rate( context );
+
+ expect( called ).to.equal( true );
+ expect( actual ).to.deep.equal( expected );
+ } );
+
+
+ function getRaterStub( callback = ( _, __ ) => {}, classes = [] )
+ {
+ let raterStub = ( function()
+ {
+ var raterStub = function( data, canTerm )
+ {
+ callback( data, canTerm );
+
+ return {
+ __classes: classes
+ };
+ }
+
+ raterStub.supplier = 'bar';
+ raterStub.rater = {
+ meta: 'foo',
+ classify: {
+ desc: classes
+ },
+ };
+
+ return raterStub;
+ } )();
+
+ return raterStub;
+ }
+
+
+ function getResultSetStub( callback = ( _, __ ) => {} )
+ {
+ return ( str ) => {
+ return {
+ addResult( name, set ){
+ callback( name, set );
+ }
+ }
+ };
+ }
+
+
+ function getDslContext( override, data, canTerm )
+ {
+ const ContextCtr = Class( 'DummyDslRaterContext' ).extend(
+ DslRaterContext,
+ override
+ );
+
+ return ContextCtr( data, canTerm );
+ }
+} );