Integrate Store into DataValidator, ValidStateMonitor
ValidStateMonitor now uses a Store in place of the original primitive object-based diff format. The original format is translated by DataValidator. The code is in a transitional state, and considering the amount of time we spend on various areas of this project, will likely stay this way for a while. * src/validate/DataValidator.js (__construct): Accept Store factory parameter. (_store_factory): Add field. (_createStores): Add method. (_validate): Handle Store. (updateFailures): Add method. (_populateStore): Add method. * test/validate/DataValidatorTest.js: Add tests. * src/validate/ValidStateMonitor.js (update): Enforce Store diff. Wait to process failures until fixes are calculated. (_checkFailureFix): Handle asynchronous, Promise-based diff. (_checkCauseFix): Extract logic from _checkCauseFix. * test/validate/ValidStateMonitorTest.js: Modify test cases to be Promise-based and handle async calls where appropriate. That was a friggin' expensive mess. DEV-2296master
parent
203b25f10e
commit
2045c76f7e
|
@ -55,6 +55,12 @@ module.exports = Class( 'DataValidator',
|
||||||
*/
|
*/
|
||||||
'private _factory': null,
|
'private _factory': null,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bucket diff store
|
||||||
|
* @type {Store}
|
||||||
|
*/
|
||||||
|
'private _store_factory': null,
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize validator
|
* Initialize validator
|
||||||
|
@ -62,12 +68,35 @@ module.exports = Class( 'DataValidator',
|
||||||
* @param {BucketDataValidator} bucket_validator data validator
|
* @param {BucketDataValidator} bucket_validator data validator
|
||||||
* @param {ValidStateMonitor} field_monitor field state monitor
|
* @param {ValidStateMonitor} field_monitor field state monitor
|
||||||
* @param {ClientDependencyFactory} dep_factory REMOVE ME
|
* @param {ClientDependencyFactory} dep_factory REMOVE ME
|
||||||
|
* @param {function()} store_factory factory for diff store
|
||||||
*/
|
*/
|
||||||
__construct( bucket_validator, field_monitor, dep_factory )
|
__construct(
|
||||||
|
bucket_validator, field_monitor, dep_factory, store_factory
|
||||||
|
)
|
||||||
{
|
{
|
||||||
|
if ( typeof store_factory !== 'function' )
|
||||||
|
{
|
||||||
|
throw TypeError( "Expected function for parameter store_factory" );
|
||||||
|
}
|
||||||
|
|
||||||
this._bucket_validator = bucket_validator;
|
this._bucket_validator = bucket_validator;
|
||||||
this._field_monitor = field_monitor;
|
this._field_monitor = field_monitor;
|
||||||
this._factory = dep_factory;
|
this._factory = dep_factory;
|
||||||
|
|
||||||
|
this._createStores( store_factory );
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create internal diff stores
|
||||||
|
*
|
||||||
|
* @param {function()} store_factory function to produce stores
|
||||||
|
*
|
||||||
|
* @return {undefined}
|
||||||
|
*/
|
||||||
|
'private _createStores': function( store_factory )
|
||||||
|
{
|
||||||
|
this._bucket_store = store_factory();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
@ -89,7 +118,7 @@ module.exports = Class( 'DataValidator',
|
||||||
|
|
||||||
let failures = {};
|
let failures = {};
|
||||||
|
|
||||||
this._bucket_validator.validate( diff, ( name, value, i ) =>
|
_self._bucket_validator.validate( diff, ( name, value, i ) =>
|
||||||
{
|
{
|
||||||
diff[ name ][ i ] = undefined;
|
diff[ name ][ i ] = undefined;
|
||||||
|
|
||||||
|
@ -100,6 +129,53 @@ module.exports = Class( 'DataValidator',
|
||||||
validatef && validatef( diff, failures );
|
validatef && validatef( diff, failures );
|
||||||
|
|
||||||
// XXX: this assumes that the above is synchronous
|
// XXX: this assumes that the above is synchronous
|
||||||
return this._field_monitor.update( diff, failures );
|
return this.updateFailures( diff, failures );
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update failures from external validation
|
||||||
|
*
|
||||||
|
* TODO: This is a transitional API---we should handle all validations,
|
||||||
|
* not allow external systems to meddle in our affairs.
|
||||||
|
*
|
||||||
|
* @param {Object} diff bucket diff
|
||||||
|
* @param {Object} failures failures per field name and index
|
||||||
|
*
|
||||||
|
* @return {Promise} promise to populate internal store
|
||||||
|
*/
|
||||||
|
'public updateFailures'( diff, failures )
|
||||||
|
{
|
||||||
|
const _self = this;
|
||||||
|
|
||||||
|
return this._populateStore( diff ).then( () =>
|
||||||
|
{
|
||||||
|
return _self._field_monitor.update(
|
||||||
|
_self._bucket_store, failures
|
||||||
|
);
|
||||||
|
} );
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Populate store with diff
|
||||||
|
*
|
||||||
|
* This effectively converts a basic array into a `Store`. This is
|
||||||
|
* surprisingly performant on v8. If the stores mix in traits, there
|
||||||
|
* may be a slight performance hit for trait-overridden methods.
|
||||||
|
*
|
||||||
|
* @param {Object} diff bucket diff
|
||||||
|
*
|
||||||
|
* @return {Promise} when all items have been added to the store
|
||||||
|
*/
|
||||||
|
'private _populateStore'( diff )
|
||||||
|
{
|
||||||
|
var bstore = this._bucket_store;
|
||||||
|
|
||||||
|
return Promise.all(
|
||||||
|
Object.keys( diff ).map(
|
||||||
|
key => bstore.add( key, diff[ key ] )
|
||||||
|
)
|
||||||
|
);
|
||||||
},
|
},
|
||||||
} );
|
} );
|
||||||
|
|
|
@ -19,9 +19,10 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var Class = require( 'easejs' ).Class,
|
var Class = require( 'easejs' ).Class;
|
||||||
EventEmitter = require( 'events' ).EventEmitter,
|
var EventEmitter = require( 'events' ).EventEmitter;
|
||||||
Failure = require( './Failure' );
|
var Failure = require( './Failure' );
|
||||||
|
var Store = require( '../store/Store' );
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -50,9 +51,8 @@ module.exports = Class( 'ValidStateMonitor' )
|
||||||
* should omitted from the value if they are not failures.
|
* should omitted from the value if they are not failures.
|
||||||
*
|
*
|
||||||
* The return value is a promise that is accepted once all fix checks
|
* The return value is a promise that is accepted once all fix checks
|
||||||
* have been performed (after which the `fix` event is emitted if
|
* have been performed. The `failure` event is always emitted _before_
|
||||||
* appropriate). The `failure` event is emitted synchronously if any
|
* the fix event.
|
||||||
* additional failures are detected.
|
|
||||||
*
|
*
|
||||||
* @param {Object} data key-value field data
|
* @param {Object} data key-value field data
|
||||||
* @param {Object} failures key-value field errors
|
* @param {Object} failures key-value field errors
|
||||||
|
@ -61,19 +61,25 @@ module.exports = Class( 'ValidStateMonitor' )
|
||||||
*/
|
*/
|
||||||
'public update': function( data, failures )
|
'public update': function( data, failures )
|
||||||
{
|
{
|
||||||
var _self = this;
|
if ( !Class.isA( Store, data ) )
|
||||||
|
|
||||||
var fixed = this.detectFixes( data, this._failures, failures ),
|
|
||||||
count_new = this.mergeFailures( this._failures, failures );
|
|
||||||
|
|
||||||
if ( this.hasFailures() && ( count_new > 0 ) )
|
|
||||||
{
|
{
|
||||||
this.emit( 'failure', this._failures );
|
throw TypeError(
|
||||||
|
'Bucket diff data must be a Store; given ' + data
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// failures is synchronous, fixes async
|
var _self = this;
|
||||||
|
var fixed = this.detectFixes( data, this._failures, failures );
|
||||||
|
|
||||||
return fixed.then( function( fixes )
|
return fixed.then( function( fixes )
|
||||||
{
|
{
|
||||||
|
var count_new = _self.mergeFailures( _self._failures, failures );
|
||||||
|
|
||||||
|
if ( _self.hasFailures() && ( count_new > 0 ) )
|
||||||
|
{
|
||||||
|
_self.emit( 'failure', _self._failures );
|
||||||
|
}
|
||||||
|
|
||||||
if ( fixes !== null )
|
if ( fixes !== null )
|
||||||
{
|
{
|
||||||
_self.emit( 'fix', fixes );
|
_self.emit( 'fix', fixes );
|
||||||
|
@ -238,50 +244,98 @@ module.exports = Class( 'ValidStateMonitor' )
|
||||||
*/
|
*/
|
||||||
'private _checkFailureFix': function( name, fail, past_fail, data, fixed )
|
'private _checkFailureFix': function( name, fail, past_fail, data, fixed )
|
||||||
{
|
{
|
||||||
var has_fixed = false;
|
var _self = this;
|
||||||
|
|
||||||
// we must check each individual index because it is possible that
|
// we must check each individual index because it is possible that
|
||||||
// not every index was modified or fixed (we must loop through like
|
// not every index was modified or fixed (we must loop through like
|
||||||
// this because this is treated as a hash table, not an array)
|
// this because this is treated as a hash table, not an array)
|
||||||
for ( var i in past_fail )
|
return Promise.all( past_fail.map( function( failure, fail_i )
|
||||||
{
|
{
|
||||||
var causes = past_fail[ i ] && past_fail[ i ].getCauses();
|
var causes = failure && failure.getCauses() || [];
|
||||||
|
|
||||||
for ( var cause_i in causes )
|
// to short-circuit checks, the promise will be _rejected_ once
|
||||||
|
// a match is found (see catch block)
|
||||||
|
return causes
|
||||||
|
.reduce(
|
||||||
|
_self._checkCauseFix.bind( _self, data, fail ),
|
||||||
|
Promise.resolve( true )
|
||||||
|
)
|
||||||
|
.then( function()
|
||||||
{
|
{
|
||||||
var cause = causes[ cause_i ],
|
// no fixes
|
||||||
cause_name = cause.getName(),
|
return false;
|
||||||
cause_index = cause.getIndex(),
|
} )
|
||||||
field = data[ cause_name ];
|
.catch( function( result )
|
||||||
|
|
||||||
// if datum is unchanged, ignore it
|
|
||||||
if ( field === undefined )
|
|
||||||
{
|
{
|
||||||
continue;
|
if ( result instanceof Error )
|
||||||
|
{
|
||||||
|
throw result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// to be marked as fixed, there must both me no failure and
|
// looks like it has been resolved
|
||||||
// there must be data for this index for the field in question
|
( fixed[ name ] = fixed[ name ] || [] )[ fail_i ] = result;
|
||||||
// (if the field wasn't touched, then of course there's no
|
|
||||||
// failure!)
|
delete past_fail[ fail_i ];
|
||||||
|
return true;
|
||||||
|
} );
|
||||||
|
} ) ).then( function( result ) {
|
||||||
|
return result.some( function( val )
|
||||||
|
{
|
||||||
|
return val === true;
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check past failure causes
|
||||||
|
*
|
||||||
|
* Each past failure in `fail` will be checked against the data in
|
||||||
|
* `diff` to determine whether it should be considered a possible
|
||||||
|
* fix. If so, the promise is fulfilled with the fix data. It is the
|
||||||
|
* responsibility of the caller to handle removing past failures.
|
||||||
|
*
|
||||||
|
* @param {Object} data validated data
|
||||||
|
* @param {Object} fail failure records
|
||||||
|
* @param {Promise} causep cause promise to chain onto
|
||||||
|
* @param {Field} cause field that caused the error
|
||||||
|
*
|
||||||
|
* @return {Promise} whether a field should be fixed
|
||||||
|
*/
|
||||||
|
'private _checkCauseFix': function( data, fail, causep, cause )
|
||||||
|
{
|
||||||
|
var cause_name = cause.getName();
|
||||||
|
var cause_index = cause.getIndex();
|
||||||
|
|
||||||
|
return causep.then( function()
|
||||||
|
{
|
||||||
|
return new Promise( function( keepgoing, found )
|
||||||
|
{
|
||||||
|
data.get( cause_name ).then( function( field )
|
||||||
|
{
|
||||||
|
// to be marked as fixed, there must both me no failure
|
||||||
|
// and there must be data for this index for the field
|
||||||
|
// in question (if the field wasn't touched, then of
|
||||||
|
// course there's no failure!)
|
||||||
if ( ( fail === undefined )
|
if ( ( fail === undefined )
|
||||||
|| ( !( fail[ cause_index ] )
|
|| ( !( fail[ cause_index ] )
|
||||||
&& ( field[ cause_index ] !== undefined ) )
|
&& ( field[ cause_index ] !== undefined ) )
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
// looks like it has been resolved
|
found( field[ cause_index ] );
|
||||||
( fixed[ name ] = fixed[ name ] || [] )[ i ] =
|
return;
|
||||||
field[ cause_index ]
|
|
||||||
|
|
||||||
has_fixed = true;
|
|
||||||
|
|
||||||
delete past_fail[ i ];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// preparation for future use of Store, which is async
|
// keep searching
|
||||||
return Promise.resolve( has_fixed );
|
keepgoing( true );
|
||||||
}
|
} )
|
||||||
|
.catch( function( e )
|
||||||
|
{
|
||||||
|
// doesn't exist, so just keep searching (it
|
||||||
|
// wasn't fixed)
|
||||||
|
keepgoing( true );
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
},
|
||||||
} );
|
} );
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
const root = require( '../../' );
|
const root = require( '../../' );
|
||||||
const validate = root.validate;
|
const validate = root.validate;
|
||||||
const Sut = validate.DataValidator;
|
const Sut = validate.DataValidator;
|
||||||
|
const MemoryStore = root.store.MemoryStore;
|
||||||
const chai = require( 'chai' );
|
const chai = require( 'chai' );
|
||||||
const expect = chai.expect;
|
const expect = chai.expect;
|
||||||
const sinon = require( 'sinon' );
|
const sinon = require( 'sinon' );
|
||||||
|
@ -55,6 +56,7 @@ describe( 'DataValidator', () =>
|
||||||
|
|
||||||
const vmonitor = ValidStateMonitor();
|
const vmonitor = ValidStateMonitor();
|
||||||
const dep_factory = createMockDependencyFactory();
|
const dep_factory = createMockDependencyFactory();
|
||||||
|
const getStore = createStubStore();
|
||||||
|
|
||||||
const mock_vmonitor = sinon.mock( vmonitor );
|
const mock_vmonitor = sinon.mock( vmonitor );
|
||||||
const mock_dep_factory = sinon.mock( dep_factory );
|
const mock_dep_factory = sinon.mock( dep_factory );
|
||||||
|
@ -68,7 +70,7 @@ describe( 'DataValidator', () =>
|
||||||
|
|
||||||
mock_vmonitor.expects( 'update' )
|
mock_vmonitor.expects( 'update' )
|
||||||
.once()
|
.once()
|
||||||
.withExactArgs( diff, expected_failures )
|
.withExactArgs( getStore(), expected_failures )
|
||||||
.returns( Promise.resolve( undefined ) );
|
.returns( Promise.resolve( undefined ) );
|
||||||
|
|
||||||
mock_dep_factory.expects( 'createFieldFailure' )
|
mock_dep_factory.expects( 'createFieldFailure' )
|
||||||
|
@ -76,19 +78,17 @@ describe( 'DataValidator', () =>
|
||||||
.withExactArgs( 'foo', 1, expected_value )
|
.withExactArgs( 'foo', 1, expected_value )
|
||||||
.returns( expected_failure );
|
.returns( expected_failure );
|
||||||
|
|
||||||
const retp = Sut( bvalidator, vmonitor, dep_factory )
|
return Sut( bvalidator, vmonitor, dep_factory, getStore )
|
||||||
.validate( diff );
|
.validate( diff )
|
||||||
|
.then( () =>
|
||||||
// cleared on call to err in above mock validator
|
{
|
||||||
expect( diff.foo ).to.deep.equal(
|
|
||||||
[ 'a', undefined, 'c' ]
|
|
||||||
);
|
|
||||||
|
|
||||||
mock_vmonitor.verify();
|
mock_vmonitor.verify();
|
||||||
mock_dep_factory.verify();
|
mock_dep_factory.verify();
|
||||||
|
|
||||||
// the promise
|
// cleared on call to err in above mock validator
|
||||||
return retp;
|
return expect( getStore().get( 'foo' ) )
|
||||||
|
.to.eventually.deep.equal( [ 'a', undefined, 'c' ] );
|
||||||
|
} );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
|
||||||
|
@ -106,6 +106,7 @@ describe( 'DataValidator', () =>
|
||||||
|
|
||||||
const vmonitor = ValidStateMonitor();
|
const vmonitor = ValidStateMonitor();
|
||||||
const dep_factory = createMockDependencyFactory();
|
const dep_factory = createMockDependencyFactory();
|
||||||
|
const getStore = createStubStore();
|
||||||
|
|
||||||
const diff = { foo: [ 'a', 'b', 'c' ] };
|
const diff = { foo: [ 'a', 'b', 'c' ] };
|
||||||
const expected_failures = {
|
const expected_failures = {
|
||||||
|
@ -129,14 +130,14 @@ describe( 'DataValidator', () =>
|
||||||
sinon.mock( vmonitor )
|
sinon.mock( vmonitor )
|
||||||
.expects( 'update' )
|
.expects( 'update' )
|
||||||
.once()
|
.once()
|
||||||
.withExactArgs( diff, expected_failures )
|
.withExactArgs( getStore(), expected_failures )
|
||||||
.returns( Promise.resolve( undefined ) );
|
.returns( Promise.resolve( undefined ) );
|
||||||
|
|
||||||
sinon.mock( dep_factory )
|
sinon.mock( dep_factory )
|
||||||
.expects( 'createFieldFailure' )
|
.expects( 'createFieldFailure' )
|
||||||
.returns( expected_failure );
|
.returns( expected_failure );
|
||||||
|
|
||||||
return Sut( bvalidator, vmonitor, dep_factory )
|
return Sut( bvalidator, vmonitor, dep_factory, getStore )
|
||||||
.validate( diff, validatef );
|
.validate( diff, validatef );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
@ -155,7 +156,7 @@ describe( 'DataValidator', () =>
|
||||||
.returns( Promise.reject( expected_e ) );
|
.returns( Promise.reject( expected_e ) );
|
||||||
|
|
||||||
return expect(
|
return expect(
|
||||||
Sut( bvalidator, vmonitor, dep_factory )
|
Sut( bvalidator, vmonitor, dep_factory, createStubStore() )
|
||||||
.validate( {} )
|
.validate( {} )
|
||||||
).to.eventually.be.rejectedWith( expected_e );
|
).to.eventually.be.rejectedWith( expected_e );
|
||||||
} );
|
} );
|
||||||
|
@ -181,3 +182,11 @@ function createMockDependencyFactory( map )
|
||||||
createFieldFailure: () => {},
|
createFieldFailure: () => {},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function createStubStore()
|
||||||
|
{
|
||||||
|
const store = MemoryStore();
|
||||||
|
|
||||||
|
return () => store;
|
||||||
|
}
|
||||||
|
|
|
@ -19,17 +19,20 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var root = require( '../../' ),
|
"use strict";
|
||||||
Sut = root.validate.ValidStateMonitor,
|
|
||||||
chai = require( 'chai' ),
|
const root = require( '../../' );
|
||||||
expect = chai.expect,
|
const Sut = root.validate.ValidStateMonitor;
|
||||||
Failure = root.validate.Failure,
|
const chai = require( 'chai' );
|
||||||
Field = root.field.BucketField;
|
const expect = chai.expect;
|
||||||
|
const Failure = root.validate.Failure;
|
||||||
|
const Field = root.field.BucketField;
|
||||||
|
const MemoryStore = root.store.MemoryStore;
|
||||||
|
|
||||||
chai.use( require( 'chai-as-promised' ) );
|
chai.use( require( 'chai-as-promised' ) );
|
||||||
|
|
||||||
|
|
||||||
var nocall = function( type )
|
const nocall = function( type )
|
||||||
{
|
{
|
||||||
return function()
|
return function()
|
||||||
{
|
{
|
||||||
|
@ -37,7 +40,7 @@ var nocall = function( type )
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
var mkfail = function( name, arr )
|
const mkfail = function( name, arr )
|
||||||
{
|
{
|
||||||
return arr.map( function( value, i )
|
return arr.map( function( value, i )
|
||||||
{
|
{
|
||||||
|
@ -53,20 +56,26 @@ describe( 'ValidStateMonitor', function()
|
||||||
describe( '#update', function()
|
describe( '#update', function()
|
||||||
{
|
{
|
||||||
it( 'does nothing with no data or failures', function()
|
it( 'does nothing with no data or failures', function()
|
||||||
|
{
|
||||||
|
return mkstore( {} ).then( empty =>
|
||||||
{
|
{
|
||||||
return Sut()
|
return Sut()
|
||||||
.on( 'failure', nocall( 'failure' ) )
|
.on( 'failure', nocall( 'failure' ) )
|
||||||
.on( 'fix', nocall( 'fix' ) )
|
.on( 'fix', nocall( 'fix' ) )
|
||||||
.update( {}, {} );
|
.update( empty, {} );
|
||||||
|
} );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
|
||||||
it( 'does nothing with data but no failures', function()
|
it( 'does nothing with data but no failures', function()
|
||||||
|
{
|
||||||
|
return mkstore( { foo: mkfail( 'foo', [ 'bar' ] ) } ).then( store =>
|
||||||
{
|
{
|
||||||
return Sut()
|
return Sut()
|
||||||
.on( 'failure', nocall( 'failure' ) )
|
.on( 'failure', nocall( 'failure' ) )
|
||||||
.on( 'fix', nocall( 'fix' ) )
|
.on( 'fix', nocall( 'fix' ) )
|
||||||
.update( { foo: mkfail( 'foo', [ 'bar' ] ) }, {} );
|
.update( store, {} );
|
||||||
|
} );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
|
||||||
|
@ -74,43 +83,61 @@ describe( 'ValidStateMonitor', function()
|
||||||
// need the data
|
// need the data
|
||||||
describe( 'given failures', function()
|
describe( 'given failures', function()
|
||||||
{
|
{
|
||||||
it( 'marks failures even when given no data', function( done )
|
it( 'marks failures even when given no data', function()
|
||||||
{
|
{
|
||||||
var fail = mkfail( 'foo', [ 'bar', 'baz' ] );
|
var fail = mkfail( 'foo', [ 'bar', 'baz' ] );
|
||||||
|
|
||||||
Sut()
|
return mkstore( {} ).then( empty =>
|
||||||
|
{
|
||||||
|
return new Promise( accept =>
|
||||||
|
{
|
||||||
|
return Sut()
|
||||||
.on( 'failure', function( failures )
|
.on( 'failure', function( failures )
|
||||||
{
|
{
|
||||||
expect( failures )
|
expect( failures )
|
||||||
.to.deep.equal( { foo: [ fail[ 0 ], fail[ 1 ] ] } );
|
.to.deep.equal(
|
||||||
done();
|
{ foo: [ fail[ 0 ], fail[ 1 ] ] }
|
||||||
|
);
|
||||||
|
accept();
|
||||||
} )
|
} )
|
||||||
.on( 'fix', nocall( 'fix' ) )
|
.on( 'fix', nocall( 'fix' ) )
|
||||||
.update( {}, { foo: fail } );
|
.update( empty, { foo: fail } );
|
||||||
|
} );
|
||||||
|
} );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
|
||||||
it( 'marks failures with index gaps', function( done )
|
it( 'marks failures with index gaps', function()
|
||||||
{
|
{
|
||||||
var fail = mkfail( 'foo', [ undefined, 'baz' ] );
|
var fail = mkfail( 'foo', [ undefined, 'baz' ] );
|
||||||
|
|
||||||
|
return mkstore( {} ).then( empty =>
|
||||||
|
{
|
||||||
|
return new Promise( accept =>
|
||||||
|
{
|
||||||
Sut()
|
Sut()
|
||||||
.on( 'failure', function( failures )
|
.on( 'failure', function( failures )
|
||||||
{
|
{
|
||||||
expect( failures )
|
expect( failures )
|
||||||
.to.deep.equal( { foo: [ undefined, fail[ 1 ] ] } );
|
.to.deep.equal(
|
||||||
done();
|
{ foo: [ undefined, fail[ 1 ] ] }
|
||||||
|
);
|
||||||
|
accept();
|
||||||
} )
|
} )
|
||||||
.on( 'fix', nocall( 'fix' ) )
|
.on( 'fix', nocall( 'fix' ) )
|
||||||
.update( {}, { foo: fail } );
|
.update( empty, { foo: fail } );
|
||||||
|
} );
|
||||||
|
} );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
|
||||||
it( 'retains past failures when setting new', function( done )
|
it( 'retains past failures when setting new', function()
|
||||||
{
|
{
|
||||||
var sut = Sut(),
|
var sut = Sut(),
|
||||||
fail = mkfail( 'foo', [ 'bar', 'baz' ] );
|
fail = mkfail( 'foo', [ 'bar', 'baz' ] );
|
||||||
|
|
||||||
|
return new Promise( ( accept, reject ) =>
|
||||||
|
{
|
||||||
var test_first = function( failures )
|
var test_first = function( failures )
|
||||||
{
|
{
|
||||||
expect( failures )
|
expect( failures )
|
||||||
|
@ -124,38 +151,50 @@ describe( 'ValidStateMonitor', function()
|
||||||
expect( failures )
|
expect( failures )
|
||||||
.to.deep.equal( { foo: [ fail[ 0 ], fail[ 1 ] ] } );
|
.to.deep.equal( { foo: [ fail[ 0 ], fail[ 1 ] ] } );
|
||||||
|
|
||||||
done();
|
accept();
|
||||||
};
|
};
|
||||||
|
|
||||||
sut
|
mkstore( {} ).then( empty =>
|
||||||
|
{
|
||||||
|
return sut
|
||||||
.once( 'failure', test_first )
|
.once( 'failure', test_first )
|
||||||
.on( 'fix', nocall( 'fix' ) )
|
.on( 'fix', nocall( 'fix' ) )
|
||||||
.update( {}, { foo: [ undefined, fail[ 1 ] ] } )
|
.update( empty, { foo: [ undefined, fail[ 1 ] ] } )
|
||||||
.then( () =>
|
.then( () =>
|
||||||
{
|
{
|
||||||
return sut.update( {}, { foo: [ fail[ 0 ] ] } );
|
return sut.update( empty, { foo: [ fail[ 0 ] ] } );
|
||||||
|
} );
|
||||||
|
} ).catch( e => reject( e ) );
|
||||||
} );
|
} );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
|
||||||
// deprecated
|
// deprecated
|
||||||
it( 'accepts failures as string for BC', function( done )
|
it( 'accepts failures as string for BC', function()
|
||||||
{
|
{
|
||||||
var fail = [ 'foo', 'bar' ];
|
var fail = [ 'foo', 'bar' ];
|
||||||
|
|
||||||
Sut()
|
return new Promise( ( accept, reject ) =>
|
||||||
|
{
|
||||||
|
return mkstore( {} ).then( empty =>
|
||||||
|
{
|
||||||
|
return Sut()
|
||||||
.on( 'failure', function( failures )
|
.on( 'failure', function( failures )
|
||||||
{
|
{
|
||||||
expect( failures )
|
expect( failures )
|
||||||
.to.deep.equal( { foo: fail } );
|
.to.deep.equal( { foo: fail } );
|
||||||
done();
|
|
||||||
|
accept();
|
||||||
} )
|
} )
|
||||||
.on( 'fix', nocall( 'fix' ) )
|
.on( 'fix', nocall( 'fix' ) )
|
||||||
.update( {}, { foo: fail } );
|
.update( empty, { foo: fail } );
|
||||||
|
} )
|
||||||
|
.catch( e => reject( e ) );
|
||||||
|
} );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
|
||||||
it( 'does not discard existing failures', function( done )
|
it( 'does not discard existing failures', function()
|
||||||
{
|
{
|
||||||
var sut = Sut();
|
var sut = Sut();
|
||||||
|
|
||||||
|
@ -176,29 +215,43 @@ describe( 'ValidStateMonitor', function()
|
||||||
// the second failure has fewer causes than the first;
|
// the second failure has fewer causes than the first;
|
||||||
// we need to make sure that it doesn't overwrite,
|
// we need to make sure that it doesn't overwrite,
|
||||||
// leading to fewer caues
|
// leading to fewer caues
|
||||||
sut
|
return new Promise( ( accept, reject ) =>
|
||||||
.update( {}, { foo: [ fail1 ] } )
|
{
|
||||||
|
return mkstore( {} ).then( empty =>
|
||||||
|
{
|
||||||
|
return sut
|
||||||
|
.update( empty, { foo: [ fail1 ] } )
|
||||||
.then( () =>
|
.then( () =>
|
||||||
{
|
{
|
||||||
return sut.update( {}, { foo: [ fail2 ] } );
|
return sut.update( empty, { foo: [ fail2 ] } );
|
||||||
} )
|
} )
|
||||||
.then( () =>
|
.then( () =>
|
||||||
|
{
|
||||||
|
const update = {
|
||||||
|
foo: [ 'moo' ],
|
||||||
|
cause1: [ 'causefix1' ]
|
||||||
|
};
|
||||||
|
|
||||||
|
return mkstore( update ).then( store =>
|
||||||
{
|
{
|
||||||
return sut
|
return sut
|
||||||
.once( 'fix', function( fixed )
|
.once( 'fix', function( fixed )
|
||||||
{
|
{
|
||||||
expect( fixed )
|
expect( fixed ).to.deep.equal(
|
||||||
.to.deep.equal( { foo: [ 'causefix1' ] } );
|
{ foo: [ 'causefix1' ] }
|
||||||
|
);
|
||||||
|
|
||||||
// and then we should have no failures
|
// and then we should have no failures
|
||||||
expect( sut.hasFailures() ).to.be.false;
|
expect( sut.hasFailures() )
|
||||||
|
.to.be.false;
|
||||||
|
|
||||||
done();
|
accept( true );
|
||||||
} )
|
} )
|
||||||
.update(
|
.update( store, {} );
|
||||||
{ foo: [ 'moo' ], cause1: [ 'causefix1' ] },
|
} );
|
||||||
{}
|
} );
|
||||||
);
|
} )
|
||||||
|
.catch( e => reject( e ) );
|
||||||
} );
|
} );
|
||||||
} );
|
} );
|
||||||
} );
|
} );
|
||||||
|
@ -206,40 +259,50 @@ describe( 'ValidStateMonitor', function()
|
||||||
|
|
||||||
describe( 'given data with absence of failure', function()
|
describe( 'given data with absence of failure', function()
|
||||||
{
|
{
|
||||||
it( 'removes non-failures if field is present', function( done )
|
it( 'removes non-failures if field is present', function()
|
||||||
{
|
{
|
||||||
var data = { foo: [ 'bardata', 'baz' ] },
|
const fail = mkfail( 'foo', [ 'bar', 'baz' ] );
|
||||||
fail = mkfail( 'foo', [ 'bar', 'baz' ] );
|
const sut = Sut();
|
||||||
|
|
||||||
var sut = Sut();
|
return new Promise( ( accept, reject ) =>
|
||||||
|
{
|
||||||
sut
|
return mkstore( { foo: [ 'bardata', 'baz' ] } ).then( data =>
|
||||||
|
{
|
||||||
|
return sut
|
||||||
.on( 'fix', function( fixed )
|
.on( 'fix', function( fixed )
|
||||||
{
|
{
|
||||||
expect( fixed )
|
expect( fixed )
|
||||||
.to.deep.equal( { foo: [ 'bardata' ] } );
|
.to.deep.equal( { foo: [ 'bardata' ] } );
|
||||||
done();
|
accept();
|
||||||
} )
|
} )
|
||||||
.update( data, { foo: [ fail[ 0 ], fail[ 1 ] ] } )
|
.update( data, { foo: [ fail[ 0 ], fail[ 1 ] ] } )
|
||||||
.then( () =>
|
.then( () =>
|
||||||
{
|
{
|
||||||
return sut.update( data, { foo: [ undefined, fail[ 1 ] ] } );
|
return sut.update( data, {
|
||||||
|
foo: [ undefined, fail[ 1 ] ]
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
} )
|
||||||
|
.catch( e => reject( e ) );
|
||||||
} );
|
} );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
|
||||||
it( 'keeps failures if field is missing', function( done )
|
it( 'keeps failures if field is missing', function()
|
||||||
{
|
{
|
||||||
var data = { bar: [ 'baz', 'quux' ] },
|
const fail_foo = mkfail( 'foo', [ 'bar', 'baz' ] );
|
||||||
fail_foo = mkfail( 'foo', [ 'bar', 'baz' ] ),
|
const fail_bar = mkfail( 'bar', [ 'moo', 'cow' ] );
|
||||||
fail_bar = mkfail( 'bar', [ 'moo', 'cow' ] );
|
|
||||||
|
|
||||||
Sut()
|
return new Promise( ( accept, reject ) =>
|
||||||
|
{
|
||||||
|
return mkstore( { bar: [ 'baz', 'quux' ] } ).then( data =>
|
||||||
|
{
|
||||||
|
return Sut()
|
||||||
.on( 'fix', function( fixed )
|
.on( 'fix', function( fixed )
|
||||||
{
|
{
|
||||||
expect( fixed )
|
expect( fixed )
|
||||||
.to.deep.equal( { bar: [ 'baz', 'quux' ] } );
|
.to.deep.equal( { bar: [ 'baz', 'quux' ] } );
|
||||||
done();
|
accept();
|
||||||
} )
|
} )
|
||||||
.update( data, {
|
.update( data, {
|
||||||
foo: fail_foo, // does not exist in data
|
foo: fail_foo, // does not exist in data
|
||||||
|
@ -249,6 +312,9 @@ describe( 'ValidStateMonitor', function()
|
||||||
{
|
{
|
||||||
return sut.update( data, {} );
|
return sut.update( data, {} );
|
||||||
} );
|
} );
|
||||||
|
} )
|
||||||
|
.catch( e => reject( e ) );
|
||||||
|
} );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
|
||||||
|
@ -256,116 +322,143 @@ describe( 'ValidStateMonitor', function()
|
||||||
{
|
{
|
||||||
var called = 0;
|
var called = 0;
|
||||||
|
|
||||||
Sut()
|
return mkstore( {} ).then( empty =>
|
||||||
|
{
|
||||||
|
return Sut()
|
||||||
.on( 'failure', function()
|
.on( 'failure', function()
|
||||||
{
|
{
|
||||||
called++;
|
called++;
|
||||||
} )
|
} )
|
||||||
.update( {}, { foo: mkfail( 'foo', [ 'bar' ] ) } )
|
.update( empty, { foo: mkfail( 'foo', [ 'bar' ] ) } )
|
||||||
.then( sut =>
|
.then( sut =>
|
||||||
{
|
{
|
||||||
return sut.update( {}, {} ); // do not trigger failure event
|
return sut.update( empty, {} ); // do not trigger failure event
|
||||||
} )
|
} )
|
||||||
.then( sut =>
|
.then( sut =>
|
||||||
{
|
{
|
||||||
expect( called ).to.equal( 1 );
|
expect( called ).to.equal( 1 );
|
||||||
} );
|
} );
|
||||||
} );
|
} );
|
||||||
|
} );
|
||||||
|
|
||||||
|
|
||||||
describe( 'given a cause', function()
|
describe( 'given a cause', function()
|
||||||
{
|
{
|
||||||
it( 'considers when recognizing fix', function( done )
|
it( 'considers when recognizing fix', function()
|
||||||
{
|
{
|
||||||
// same index
|
// same index
|
||||||
var data = { cause: [ 'bar' ] },
|
const field = Field( 'foo', 0 );
|
||||||
field = Field( 'foo', 0 ),
|
const cause = Field( 'cause', 0 );
|
||||||
cause = Field( 'cause', 0 ),
|
const fail = Failure( field, 'reason', [ cause ] );
|
||||||
fail = Failure( field, 'reason', [ cause ] );
|
|
||||||
|
|
||||||
Sut()
|
return new Promise( ( accept, reject ) =>
|
||||||
|
{
|
||||||
|
return mkstore( { cause: [ 'bar' ] } ).then( data =>
|
||||||
|
{
|
||||||
|
return Sut()
|
||||||
.on( 'fix', function( fixed )
|
.on( 'fix', function( fixed )
|
||||||
{
|
{
|
||||||
expect( fixed )
|
expect( fixed )
|
||||||
.to.deep.equal( { foo: [ 'bar' ] } );
|
.to.deep.equal( { foo: [ 'bar' ] } );
|
||||||
|
|
||||||
done();
|
accept();
|
||||||
} )
|
} )
|
||||||
.update( data, { foo: [ fail ] } )
|
.update( data, { foo: [ fail ] } )
|
||||||
.then( sut =>
|
.then( sut =>
|
||||||
{
|
{
|
||||||
return sut.update( data, {} );
|
return sut.update( data, {} );
|
||||||
} );
|
} );
|
||||||
|
} )
|
||||||
|
.catch( e => reject( e ) );
|
||||||
|
} );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
|
||||||
it( 'considers different cause index', function( done )
|
it( 'considers different cause index', function()
|
||||||
{
|
{
|
||||||
// different index
|
// different index
|
||||||
var data = { cause: [ undefined, 'bar' ] },
|
const update_data = { cause: [ undefined, 'bar' ] };
|
||||||
field = Field( 'foo', 0 ),
|
const field = Field( 'foo', 0 );
|
||||||
cause = Field( 'cause', 1 ),
|
const cause = Field( 'cause', 1 );
|
||||||
fail = Failure( field, 'reason', [ cause ] );
|
const fail = Failure( field, 'reason', [ cause ] );
|
||||||
|
|
||||||
Sut()
|
return new Promise( ( accept, reject ) =>
|
||||||
|
{
|
||||||
|
return mkstore( update_data ).then( data =>
|
||||||
|
{
|
||||||
|
return Sut()
|
||||||
.on( 'fix', function( fixed )
|
.on( 'fix', function( fixed )
|
||||||
{
|
{
|
||||||
expect( fixed )
|
expect( fixed )
|
||||||
.to.deep.equal( { foo: [ 'bar' ] } );
|
.to.deep.equal( { foo: [ 'bar' ] } );
|
||||||
|
|
||||||
done();
|
accept();
|
||||||
} )
|
} )
|
||||||
.update( data, { foo: [ fail ] } )
|
.update( data, { foo: [ fail ] } )
|
||||||
.then( sut =>
|
.then( sut =>
|
||||||
{
|
{
|
||||||
return sut.update( data, {} );
|
return sut.update( data, {} );
|
||||||
} );
|
} );
|
||||||
|
} )
|
||||||
|
.catch( e => reject( e ) );
|
||||||
|
} );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
|
||||||
it( 'considers any number of causes', function( done )
|
it( 'considers any number of causes', function()
|
||||||
{
|
{
|
||||||
// different index
|
// different index
|
||||||
var data = { cause_fix: [ undefined, 'bar' ] },
|
const update_data = { cause_fix: [ undefined, 'bar' ] };
|
||||||
field = Field( 'foo', 0 ),
|
const field = Field( 'foo', 0 );
|
||||||
cause1 = Field( 'cause_no', 1 ),
|
const cause1 = Field( 'cause_no', 1 );
|
||||||
cause2 = Field( 'cause_fix', 1 ),
|
const cause2 = Field( 'cause_fix', 1 );
|
||||||
fail = Failure(
|
|
||||||
|
const fail = Failure(
|
||||||
field,
|
field,
|
||||||
'reason',
|
'reason',
|
||||||
[ cause1, cause2 ]
|
[ cause1, cause2 ]
|
||||||
);
|
);
|
||||||
|
|
||||||
Sut()
|
return new Promise( ( accept, reject ) =>
|
||||||
|
{
|
||||||
|
return mkstore( update_data ).then( data =>
|
||||||
|
{
|
||||||
|
return Sut()
|
||||||
.on( 'fix', function( fixed )
|
.on( 'fix', function( fixed )
|
||||||
{
|
{
|
||||||
expect( fixed )
|
expect( fixed )
|
||||||
.to.deep.equal( { foo: [ 'bar' ] } );
|
.to.deep.equal( { foo: [ 'bar' ] } );
|
||||||
|
|
||||||
done();
|
accept();
|
||||||
} )
|
} )
|
||||||
.update( data, { foo: [ fail ] } )
|
.update( data, { foo: [ fail ] } )
|
||||||
.then( sut =>
|
.then( sut =>
|
||||||
{
|
{
|
||||||
return sut.update( data, {} );
|
return sut.update( data, {} );
|
||||||
} );
|
} );
|
||||||
|
} )
|
||||||
|
.catch( e => reject( e ) );
|
||||||
|
} );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
|
||||||
it( 'recognizes non-fix', function()
|
it( 'recognizes non-fix', function()
|
||||||
{
|
{
|
||||||
// no cause data
|
// no cause data
|
||||||
var data = { noncause: [ undefined, 'bar' ] },
|
const update_data = mkstore( { noncause: [ undefined, 'bar' ] } );
|
||||||
field = Field( 'foo', 0 ),
|
const field = Field( 'foo', 0 );
|
||||||
cause1 = Field( 'cause', 1 ),
|
const cause1 = Field( 'cause', 1 );
|
||||||
cause2 = Field( 'cause', 2 ),
|
const cause2 = Field( 'cause', 2 );
|
||||||
fail = Failure(
|
|
||||||
|
const fail = Failure(
|
||||||
field,
|
field,
|
||||||
'reason',
|
'reason',
|
||||||
[ cause1, cause2 ]
|
[ cause1, cause2 ]
|
||||||
);
|
);
|
||||||
|
|
||||||
Sut()
|
return mkstore( update_data ).then( data =>
|
||||||
|
{
|
||||||
|
return Sut()
|
||||||
.on( 'fix', nocall )
|
.on( 'fix', nocall )
|
||||||
.update( data, { foo: [ fail ] } )
|
.update( data, { foo: [ fail ] } )
|
||||||
.then( sut =>
|
.then( sut =>
|
||||||
|
@ -375,20 +468,24 @@ describe( 'ValidStateMonitor', function()
|
||||||
} );
|
} );
|
||||||
} );
|
} );
|
||||||
} );
|
} );
|
||||||
|
} );
|
||||||
|
|
||||||
|
|
||||||
it( 'can emit both failure and fix', function( done )
|
it( 'can emit both failure and fix', function()
|
||||||
{
|
{
|
||||||
var data = { bar: [ 'baz', 'quux' ] },
|
var fail_foo = mkfail( 'foo', [ 'bar' ] );
|
||||||
fail_foo = mkfail( 'foo', [ 'bar' ] );
|
|
||||||
|
|
||||||
Sut()
|
return mkstore( { bar: [ 'baz', 'quux' ] } ).then( data =>
|
||||||
|
{
|
||||||
|
return Sut()
|
||||||
.update( data, {
|
.update( data, {
|
||||||
bar: mkfail( 'bar', [ 'moo', 'cow' ] ) // fail
|
bar: mkfail( 'bar', [ 'moo', 'cow' ] ) // fail
|
||||||
} )
|
} )
|
||||||
.then( sut =>
|
.then( sut =>
|
||||||
{
|
{
|
||||||
return sut.on( 'failure', function( failed )
|
return new Promise( ( accept, reject ) =>
|
||||||
|
{
|
||||||
|
sut.on( 'failure', function( failed )
|
||||||
{
|
{
|
||||||
expect( failed )
|
expect( failed )
|
||||||
.to.deep.equal( {
|
.to.deep.equal( {
|
||||||
|
@ -398,12 +495,24 @@ describe( 'ValidStateMonitor', function()
|
||||||
.on( 'fix', function( fixed )
|
.on( 'fix', function( fixed )
|
||||||
{
|
{
|
||||||
expect( fixed )
|
expect( fixed )
|
||||||
.to.deep.equal( { bar: [ 'baz', 'quux' ] } );
|
.to.deep.equal(
|
||||||
done();
|
{ bar: [ 'baz', 'quux' ] }
|
||||||
|
);
|
||||||
|
|
||||||
|
// note that the documentation for #update
|
||||||
|
// states that failure will always be
|
||||||
|
// emitted before fix
|
||||||
|
accept( true );
|
||||||
} )
|
} )
|
||||||
.update( data, {
|
.update( data, {
|
||||||
foo: fail_foo, // fail
|
foo: fail_foo, // fail
|
||||||
// fixes bar
|
// fixes bar
|
||||||
|
} )
|
||||||
|
.catch( e =>
|
||||||
|
{
|
||||||
|
reject( e );
|
||||||
|
} );
|
||||||
|
} );
|
||||||
} );
|
} );
|
||||||
} );
|
} );
|
||||||
} );
|
} );
|
||||||
|
@ -425,9 +534,11 @@ describe( 'ValidStateMonitor', function()
|
||||||
{
|
{
|
||||||
var fail = mkfail( 'foo', [ 'fail' ] );
|
var fail = mkfail( 'foo', [ 'fail' ] );
|
||||||
|
|
||||||
|
return mkstore( {} ).then( empty =>
|
||||||
|
{
|
||||||
return expect(
|
return expect(
|
||||||
Sut()
|
Sut()
|
||||||
.update( {}, { foo: fail } )
|
.update( empty, { foo: fail } )
|
||||||
.then( sut =>
|
.then( sut =>
|
||||||
{
|
{
|
||||||
return sut.getFailures()
|
return sut.getFailures()
|
||||||
|
@ -435,6 +546,7 @@ describe( 'ValidStateMonitor', function()
|
||||||
).to.eventually.deep.equal( { foo: fail } );
|
).to.eventually.deep.equal( { foo: fail } );
|
||||||
} );
|
} );
|
||||||
} );
|
} );
|
||||||
|
} );
|
||||||
|
|
||||||
|
|
||||||
describe( '#hasFailures', function()
|
describe( '#hasFailures', function()
|
||||||
|
@ -447,10 +559,12 @@ describe( 'ValidStateMonitor', function()
|
||||||
|
|
||||||
|
|
||||||
it( 'is true when failures exist', function()
|
it( 'is true when failures exist', function()
|
||||||
|
{
|
||||||
|
return mkstore( {} ).then( empty =>
|
||||||
{
|
{
|
||||||
return expect(
|
return expect(
|
||||||
Sut()
|
Sut()
|
||||||
.update( {}, { foo: mkfail( 'foo', [ 'bar' ] ) } )
|
.update( empty, { foo: mkfail( 'foo', [ 'bar' ] ) } )
|
||||||
.then( sut =>
|
.then( sut =>
|
||||||
{
|
{
|
||||||
return sut.hasFailures();
|
return sut.hasFailures();
|
||||||
|
@ -459,3 +573,14 @@ describe( 'ValidStateMonitor', function()
|
||||||
} );
|
} );
|
||||||
} );
|
} );
|
||||||
} );
|
} );
|
||||||
|
} );
|
||||||
|
|
||||||
|
|
||||||
|
function mkstore( data )
|
||||||
|
{
|
||||||
|
let store = MemoryStore();
|
||||||
|
|
||||||
|
return Promise.all(
|
||||||
|
Object.keys( data ).map( key => store.add( key, data[ key ] ) )
|
||||||
|
).then( () => store );
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue