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 ); + } +} );