Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Socket
Sign inDemoInstall

clues

Package Overview
Dependencies
Maintainers
2
Versions
158
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

clues - npm Package Compare versions

Comparing version 3.5.35 to 3.5.36

test/duration-test.js

2

clues.js

@@ -53,3 +53,3 @@ (function(self) {

if (e && e.notDefined && logic && logic.$external && typeof logic.$external === 'function')
return logic[ref] = logic[ref] || clues(logic,function() { return logic.$external.call(logic,ref); },$global,caller,(fullref ? fullref+'.' : '')+ref);
return logic[ref] = logic[ref] || clues(logic,function() { return logic.$external.call(logic,ref); },$global,ref,(fullref ? fullref+'.' : '')+ref);
else throw e;

@@ -56,0 +56,0 @@ });

{
"name": "clues",
"version": "3.5.35",
"version": "3.5.36",
"description": "Lightweight logic tree solver using promises.",

@@ -20,8 +20,8 @@ "keywords": [

"devDependencies": {
"mocha": "~2.5.3"
"tap": "~10.3.2"
},
"license": "MIT",
"scripts": {
"test": "./node_modules/mocha/bin/mocha"
"test": "tap test --jobs=10 -Rspec --coverage-report=html --no-browser"
}
}

@@ -1,5 +0,5 @@

var clues = require('../clues'),
assert = require('assert');
const clues = require('../clues');
const t = require('tap');
var Logic = {
const Logic = {
a : 41,

@@ -9,3 +9,3 @@ b: 1,

return function(b) {
return a+b;
return a + b;
};

@@ -17,9 +17,4 @@ }

// This test verifies the fix
describe('Inner function arguments',function() {
it('are not affecting function signature',function() {
return clues(Logic,'c')
.then(function(d) {
assert.equal(d,'42');
});
});
t.test('Inner function arguments', async t => {
t.same( await clues(Logic,'c'), 42, 'do not affect function signature');
});

@@ -1,8 +0,7 @@

var clues = require('../clues'),
assert = require('assert'),
Promise = require('bluebird');
const clues = require('../clues');
const Promise = require('bluebird');
const t = require('tap');
describe('Array functions',function() {
var logic = {
t.test('array functions', async t => {
const logic = {
M1 : [function() { return Promise.delay(100,10); }],

@@ -31,44 +30,14 @@ M2 : function() { return Promise.delay(20,300); },

var facts = Object.create(logic);
const facts = Object.create(logic);
it('should resolve to the top',function() {
return clues(facts,'M3')
.then(function(d) {
assert.equal(d,310);
});
});
it('should work for individual functions',function() {
return clues(facts,['M4',function(a) {
assert.equal(a,310);
}]);
});
it('should work for nested structures',function() {
return clues(facts,['nested',function(d) {
assert.equal(d,15);
}]);
});
it('should not affect regular arrays',function() {
return clues(facts,function(regular_array) {
assert.equal(regular_array,logic.regular_array);
});
});
it('should work with partial positional arguments',function() {
return clues(facts, 'partial').then(function(r){
assert.equal(r, 42);
});
});
it('should work recursively',function() {
return clues(facts,'recursive').then(function(d) {
assert.deepEqual(d,[10,[300,310]]);
});
});
t.same(await clues(facts,'M3'),310, 'should resolve to the top');
t.same(await clues(facts,'M4'),310, 'should work for individual functions');
t.same(await clues(facts,'nested'),15,'should work for nested structures');
t.same(await clues(facts,'regular_array'),logic.regular_array,'should not affect regular array');
t.same(await clues(facts,'partial'),42,'partial positional arguments ok');
t.same(await clues(facts,'recursive'),[10,[300,310]],'should work recursively');
it('should only execute arrays once', function() {
var counter = 0;
var otherContext = {
t.test('should only execute arrays once', async function(t) {
let counter = 0;
const otherContext = {
M1: Promise.delay(100, 10),

@@ -78,3 +47,3 @@ M2: Promise.delay(200, 20)

var logic = {
const facts = {
M3: [otherContext, 'M1', 'M2', function(a,b) {

@@ -84,13 +53,10 @@ counter++;

}]
}
var facts = Object.create(logic);
};
return Promise.all([clues(facts,'M3'), clues(facts,'M3')])
.then(function(results) {
assert.equal(results[0],30);
assert.equal(results[1],30);
assert.equal(counter, 1);
});
const d = await Promise.all([clues(facts,'M3'), clues(facts,'M3')]);
t.same(d[0],30,'results ok');
t.same(d[1],30,'results ok');
t.same(counter,1,'fn only executed once');
});
});
});

@@ -1,19 +0,16 @@

var clues = require('../clues'),
assert = require('assert'),
crypto = require('crypto');
const clues = require('../clues');
const crypto = require('crypto');
const t = require('tap');
function shouldError() { throw 'Should not run';}
describe('Array fn private scope',function() {
t.test('Array fn private scope', {autoend:true}, t => {
describe('With only array logic',function() {
t.test('With only array logic', async t => {
function privateObj() {
return {
answer : function(forty,two) {
return forty+two;
},
answer : (forty,two) => forty + two,
forty : 40,
two : 2,
err : function() {
err : () => {
throw 'This is an error';

@@ -24,3 +21,3 @@ }

var pub = {
const pub = {
M1 : 100,

@@ -30,59 +27,31 @@ M2 : 200

it('works without array arguments',function() {
var obj = privateObj();
return clues(null,[obj,function(answer) {
assert.equal(answer,42);
}]);
});
const obj = privateObj();
it('works with array arguments',function() {
var obj = privateObj();
return clues({},[obj,'answer',function(d) {
assert.equal(d,42);
}]);
});
t.same(await clues(null,[obj,'answer',Number]),42,'works without array arguments');
t.same(await clues(null,[privateObj,'answer',Number]),42,'works with private scope');
it('works with private scope defined from function',function() {
return clues({},[privateObj,function(answer) {
assert.equal(answer,42);
}]);
});
let d1 = await clues({},[privateObj,'err',String])
.then(shouldError,e => e);
it('handles errors correctly',function() {
return clues({},[privateObj,'err',String])
.then(function() { console.log(arguments);throw 'Should Error';},function(e) {
assert.equal(e.ref,'err');
assert.equal(e.fullref,'err');
assert.equal(e.message,'This is an error');
});
});
t.same(d1.message,'This is an error','handles errors correctly');
it('works recursively',function() {
return clues({},[Object.create(pub),[['M1',Number],['M2',Number],[privateObj(),'forty',Number],Array],function(d) {
assert.deepEqual(d,[100,200,40]);
}]);
});
let d2 = await clues(null,[Object.create(pub),[['M1',Number],['M2',Number],[privateObj(),'forty',Number],Array],Object]);
t.same(d2,[100,200,40],'works recursively');
});
describe('public/private structure',function() {
t.test('public/private structure', async t => {
var PrivateLogic = {
const PrivateLogic = {
secret : 'Hidden secret',
hash : function(secret,userid) {
return crypto.createHash('sha1')
hash : (secret,userid) => crypto.createHash('sha1')
.update(secret)
.update(userid)
.digest('hex');
},
.digest('hex'),
public : function(hash,_userid) {
return {
hash : hash,
userid : _userid,
};
}
public : (hash,_userid) => ({hash, userid : _userid})
};
var Logic = {
const Logic = {
info : function(_userid) {

@@ -97,20 +66,21 @@ return [

it('provides custom access to private segments',function() {
var obj = Object.create(Logic,{userid: {value:'admin'}});
t.test('custom access to private segments',t => {
const obj = Object.create(Logic,{userid: {value:'admin'}});
return clues(obj,function(info) {
assert.equal(info.userid,'admin');
assert.equal(info.hash,'b21b8fd516dbb99584d651f040d970dba2245b2a');
assert.equal(obj.secret,undefined);
assert.equal(info.secret,undefined);
t.same(info.userid,'admin','username matches');
t.same(info.hash,'b21b8fd516dbb99584d651f040d970dba2245b2a','hash matches');
t.same(obj.secret,undefined,'no obj.secret');
t.same(info.secret,undefined,'no info.secret');
});
});
it('handles errors correctly',function() {
var obj = Object.create(Logic);
t.test('handles errors correctly',t => {
const obj = Object.create(Logic);
return clues(obj,'info')
.then(shouldError,function(e) {
assert.equal(e.ref,'userid');
assert.equal(e.message,'userid not defined');
assert.equal(e.caller,'hash');
assert.equal(e.fullref,'info.public.hash.userid');
t.same(e.error,true,'errors');
t.same(e.ref,'userid','userid as ref');
t.same(e.message,'userid not defined','right error message');
t.same(e.caller,'hash','caller is hash');
t.same(e.fullref,'info.public.hash.userid','fullref');
});

@@ -117,0 +87,0 @@ });

@@ -1,8 +0,7 @@

var clues = require('../clues'),
assert = require('assert'),
Promise = require('bluebird');
const clues = require('../clues');
const Promise = require('bluebird');
const t = require('tap');
describe('Arrow functions',function() {
var logic = {
t.test('Arrow functions', async t => {
const logic = {
M1 : () => Promise.delay(100,10),

@@ -16,18 +15,11 @@ M2 : () => Promise.delay(20,300),

var facts = Object.create(logic);
const facts = Object.create(logic);
const MTOP = await clues(facts,'MTOP');
it('should resolve to the top',function() {
return clues(facts,'MTOP')
.then(function(d) {
assert.equal(d,380);
});
});
it('should update the fact table',function() {
assert.equal(facts.M1.value(),10);
assert.equal(facts.M2.value(),300);
assert.equal(facts.M3.value(),310);
assert.equal(facts.M4.value(),70);
assert.equal(facts.MTOP.value(),380);
});
t.same(MTOP,380,'resolves tree to the top');
t.same(facts.M1.value(),10,'fact M1 resolved');
t.same(facts.M2.value(),300,'fact M2 resolved');
t.same(facts.M3.value(),310,'fact M3 resolved');
t.same(facts.M4.value(),70,'fact M4 resolved');
t.same(facts.MTOP.value(),380,'fact MTOP resolved');
});

@@ -1,17 +0,15 @@

var clues = require('../clues'),
assert = require('assert'),
Promise = require('bluebird');
const clues = require('../clues');
const Promise = require('bluebird');
const t = require('tap');
var Logic = {
const facts = {
answer : async function() {
var value = await Promise.delay(100).then(function() { return 41;});
const value = await Promise.delay(100).then(() => 41);
return value+1;
}
}
};
describe('Async function',function() {
it('should resolve',async function() {
var answer = await clues(Logic,'answer');
assert.equal(answer,42);
})
});
t.test('Async function', async t => {
const answer = await clues(facts,'answer');
t.same(answer,42,'resolves to a value');
});

@@ -1,86 +0,41 @@

var clues = require('../clues'),
assert = require('assert');
const clues = require('../clues');
const t = require('tap');
function obj() {
return {
a : {
b : {
caller : function($caller) { return $caller; },
fullref : function($fullref) { return $fullref; }
}
},
call : ['a.b.caller',String],
fullref : ['a.b.fullref',String]
};
}
const facts = () => ({
a : {
b : {
caller : function($caller) { return $caller; },
fullref : function($fullref) { return $fullref; }
}
},
call : ['a.b.caller',String],
fullref : ['a.b.fullref',String]
});
describe('$caller',function() {
describe('with fn called directly',function() {
it('is null without provided caller',function() {
return clues(obj,'a.b.caller')
.then(function(caller) {
assert.equal(caller,undefined);
});
});
it('shows provided caller',function() {
return clues(obj,'a.b.caller',{},'__user__')
.then(function(caller) {
assert.equal(caller,'__user__');
});
});
t.test('$caller', {autoend:true}, t => {
t.test('with fn called directly', async t => {
t.same(await clues(facts,'a.b.caller'),undefined,'is null without caller');
t.same(await clues(facts,'a.b.caller',{},'__user__'),'__user__','shows with caller');
});
});
describe('with fn called indirectly',function() {
it('show last fn without provided caller',function() {
return clues(obj,'call')
.then(function(caller) {
assert.equal(caller,'call');
});
});
t.test('with fn called indirectly', async t => {
t.same(await clues(facts,'call'),'call','shows last fn without provided caller');
t.same(await clues(facts,'call',{},'__user__'),'call','shows last fn even with caller');
});
it('shows last fn even with provded caller',function() {
return clues(obj,'call',{},'__user__')
.then(function(caller) {
assert.equal(caller,'call');
});
});
});
t.test('with $caller override in factsect', async t => {
const o = facts();
o.a.b.$caller = 'CUSTOM_CALLER';
t.same(await clues(o,'call',{},'__user__'),'CUSTOM_CALLER','returns override');
});
describe('with $caller override in object',function() {
it('returns override',function() {
var o = obj();
o.a.b.$caller = 'CUSTOM_CALLER';
return clues(o,'call',{},'__user__')
.then(function(caller) {
assert.equal(caller,'CUSTOM_CALLER');
});
});
});
t.test('$fullref', async t => {
t.same(await clues(facts,'a.b.fullref'),'a.b.fullref','direct call shows fullref');
t.same(await clues(facts,'fullref'),'fullref.a.b.fullref','indirect call shows fullref');
const o = facts();
o.a.b.$fullref = 'CUSTOM_FULLREF';
t.same(await clues(o,'fullref',{}),'CUSTOM_FULLREF','$fullref override returns override');
});
describe('$fullref',function() {
it('direct call shows fullref',function() {
return clues(obj,'a.b.fullref')
.then(function(fullref) {
assert.equal(fullref,'a.b.fullref');
});
});
it('indirect call shows fullref',function() {
return clues(obj,'fullref')
.then(function(fullref) {
assert.equal(fullref,'fullref.a.b.fullref');
});
});
it('with $fullref override in object returns override',function() {
var o = obj();
o.a.b.$fullref = 'CUSTOM_FULLREF';
return clues(o,'fullref',{})
.then(function(fullref) {
assert.equal(fullref,'CUSTOM_FULLREF');
});
});
});

@@ -1,84 +0,63 @@

var clues = require('../clues'),
Promise = clues.Promise,
assert = require('assert');
const clues = require('../clues');
const Promise = require('bluebird');
const t = require('tap');
Promise.config({cancellation:true});
describe('cancellation',function() {
var cancel = {};
t.test('cancellation', {autoend: true}, async t => {
const cancel = {};
var logic = {
M1 : function() {
return new Promise(function(resolve,reject,onCancel) {
onCancel(function() {
cancel.M1 = true;
});
const Logic = {
M1 : () => new Promise(function(resolve,reject,onCancel) {
onCancel(() => cancel.M1 = true);
setTimeout(() => resolve(10),130);
}),
setTimeout(function() {
resolve(10);
},130);
});
},
M2 : () => new Promise(function(resolve,reject,onCancel) {
onCancel(() => cancel.M2 = true);
setTimeout(() => resolve(50),170);
}),
M2 : function() {
return new Promise(function(resolve,reject,onCancel) {
onCancel(function() {
cancel.M2 = true;
});
M3 : () => new Promise(function(resolve,reject,onCancel) {
onCancel(() => cancel.M3 = true);
setTimeout(() => resolve(10));
}),
setTimeout(function() {
resolve(50);
},170);
});
},
M4 : (M1,M2,M3) => M1+M2+M3,
M3 : function() {
return new Promise(function(resolve,reject,onCancel) {
onCancel(function() {
cancel.M3 = true;
});
MTOP : (M1,M4) => M1+M4
};
setTimeout(function() {
resolve(10);
},10);
});
},
const facts = Object.create(Logic);
let res;
M4 : function(M1,M2,M3) {
return M1+M2+M3;
},
let gotResults;
MTOP : function(M1,M4) {
return M1+M4;
}
};
res = clues(facts,'MTOP')
.then(function() {
gotResults = true;
});
var facts = Object.create(logic),res;
// Cancelling in 100ms
setTimeout(res.cancel.bind(res),100);
it('should not return results',function() {
var gotResults;
await Promise.delay(200);
res = clues(facts,'MTOP')
.then(function() {
gotResults = true;
});
// Cancelling in 100ms
setTimeout(res.cancel.bind(res),100);
return Promise.delay(150)
.then(function() {
assert.equal(gotResults,undefined);
});
t.test('should not return results', t => {
t.same(gotResults,undefined);
t.end();
});
it('should trigger the cancel higher up',function() {
assert.equal(cancel.M1,true);
assert.equal(cancel.M2,true);
t.test('Cancellation affects dependencies', t => {
t.same(cancel.M1,true,'M1 cancelled');
t.same(cancel.M2,true,'M2 cancelled');
t.end();
});
it('should not invalidate results aquired before cancellation',function() {
assert.equal(cancel.M3,undefined);
assert.equal(facts.M3.value(),10);
t.test('Any result resolved before cancellation should not be invalidated',t => {
t.same(cancel.M3,undefined,'M3 not cancelled');
t.same(facts.M3.value(),10,'M3 resolved to a promise with correct value');
t.end();
});
});

@@ -1,8 +0,8 @@

var clues = require('../clues'),
assert = require('assert'),
Promise = require('bluebird');
const clues = require('../clues');
const Promise = require('bluebird');
const t = require('tap');
describe('complex tree',function() {
t.test('Arrow functions',async t => {
var logic = {
const Logic = {
M1 : function() { return Promise.delay(100,10); },

@@ -18,18 +18,11 @@ M2 : function() { return Promise.delay(20,300); },

var facts = Object.create(logic);
const facts = Object.create(Logic);
const MTOP = await clues(facts,'MTOP');
it('should resolve to the top',function() {
return clues(facts,'MTOP')
.then(function(d) {
assert.equal(d,380);
});
});
it('should update the fact table',function() {
assert.equal(facts.M1.value(),10);
assert.equal(facts.M2.value(),300);
assert.equal(facts.M3.value(),310);
assert.equal(facts.M4.value(),70);
assert.equal(facts.MTOP.value(),380);
});
t.same(MTOP,380,'resolves tree to the top');
t.same(facts.M1.value(),10,'fact M1 resolved');
t.same(facts.M2.value(),300,'fact M2 resolved');
t.same(facts.M3.value(),310,'fact M3 resolved');
t.same(facts.M4.value(),70,'fact M4 resolved');
t.same(facts.MTOP.value(),380,'fact MTOP resolved');
});

@@ -1,160 +0,139 @@

var clues = require('../clues'),
assert = require('assert');
const clues = require('../clues');
const t = require('tap');
function shouldErr() { throw 'Should throw an error'; }
describe('error',function() {
var c = {a: function(b) { return b;}};
t.test('error', {autoend: true},t => {
const c = {a: function(b) { return b;}};
describe('when argument can´t be found',function() {
it('should throw an error',function() {
return clues({},'SOMETHING')
.then(shouldErr,function(e) {
assert.equal(e.ref,'SOMETHING');
assert.equal(e.message, 'SOMETHING not defined');
});
t.test('when argument is not found', async t => {
t.test('direct call',async t => {
const e = await clues({},'SOMETHING').then(shouldErr,Object);
//t.equal(e.error,true,'shows errors');
t.same(e.message,'SOMETHING not defined','message: not defined');
t.same(e.ref,'SOMETHING','ref: missing variable');
});
it('should show caller if a named logic function',function() {
return clues(c,'a')
.then(shouldErr,function(e) {
assert.equal(e.ref,'b');
assert.equal(e.message,'b not defined');
assert.equal(e.caller,'a');
});
t.test('caller is logic function', async t => {
const e = await clues(c,'a').catch(Object);
t.same(e.error,true,'errors');
t.same(e.ref,'b','ref is correct');
t.same(e.message,'b not defined','message is correct');
t.same(e.caller,'a','caller is correct');
});
});
describe('thrown',function() {
var logic = {
ERR : function() { throw 'Could not process'; },
DEP : function(ERR) { return 'Where is the error'; }
};
var facts = Object.create(logic);
const Logic = {
ERR : function() { throw 'Could not process'; },
DEP : function(ERR) { return 'Where is the error'; }
};
describe('directly',function() {
it('should show up as first argument (err)',function() {
return clues(facts,'ERR')
.then(shouldErr,function(e) {
assert.equal(e.ref,'ERR');
assert.equal(e.message,'Could not process');
});
});
t.test('throw', {autoend:true}, t => {
it ('should update the facts',function() {
var e = facts.ERR.reason();
assert.equal(e.ref,'ERR');
assert.equal(e.message,'Could not process');
});
t.test('directly', async t => {
const facts = Object.create(Logic);
const e = await clues(facts,'ERR').catch(Object);
const reason = facts.ERR.reason();
t.equal(e.message,'Could not process','message ok');
t.same(e.ref,'ERR','ref ok');
t.same(reason.message,'Could not process','facts register message');
});
describe('indirectly',function() {
it('should throw same error for dependent logic',function() {
return clues(facts,'DEP')
.then(shouldErr,function(e) {
assert.equal(e.ref,'ERR');
assert.equal(e.message,'Could not process');
});
});
t.test('indirectly - dependent fn', async t => {
const facts = Object.create(Logic);
const e = await clues(facts,'DEP').catch(Object);
t.equal(e.message,'Could not process','message ok');
t.same(e.ref,'ERR','ref ok');
t.same(e.caller,'DEP','Should reference the first caller');
});
});
it('should contain reference to the first caller',function() {
facts = Object.create(logic);
return clues(facts,'DEP')
.catch(function() {
return clues(facts,'ERR');
})
.then(shouldErr,function(e) {
assert.equal(e.caller,'DEP');
});
});
t.test('function named $noThrow', {autoend: true}, t => {
const facts = Object.create(Logic);
t.test('thrown rejection', async t => {
const e = await clues(facts,function $noThrow(DEP) { return DEP;});
t.same(e.error,true,'Returns an object with the error');
t.same(e.ref,'ERR','ref ok');
t.equal(e.message,'Could not process','message ok');
});
describe('function named $noThrow',function() {
it('should return the error obj',function() {
return clues(facts,function $noThrow(DEP) {
return DEP;
})
.then(function(e) {
assert.equal(e.ref,'ERR');
assert.equal(e.message,'Could not process');
},function() {
throw 'Should return the error object';
});
});
it('should return the error obj for subsequent fn',function() {
return clues(facts,function $noThrow() {
return ['DEP',Object];
})
.then(function(e) {
assert.equal(e.ref,'ERR');
assert.equal(e.message,'Could not process');
},function() {
throw 'Should return the error object';
});
});
t.test('error within subsequent fn', async t => {
const fn = function $noThrow() {
return ['DEP',Object];
};
const e = await clues(facts,fn);
t.same(e.error,true,'Returns an object with the error');
t.same(e.ref,'ERR','ref ok');
t.equal(e.message,'Could not process','message ok');
});
});
describe('$logError',function() {
var logic = {
stack_error: function() { throw new Error('error');},
stack_error_promise: function() { return clues.Promise.reject(new Error('error'));},
rejection: function() { throw 'error';},
rejection_promise: function() { return Promise.reject('error');},
optional: function(_stack_error,_rejection) {
return 'OK';
}
};
t.test('$logError', {autoend: true}, t => {
it('should log an error with stack',function() {
var facts = Object.create(logic),error,fullref;
return clues(facts,'stack_error',{$logError:function(e,f) {error = e;fullref = f;}})
.catch(Object)
.then(function() {
assert.equal(error && error.message,'error');
assert.equal(fullref,'stack_error');
});
});
const Global = {
$logError : function(e,f) {
this.error = e;
this.fullref = f;
}
};
it('should log a rejected promise with stack',function() {
var facts = Object.create(logic),error,fullref;
return clues(facts,'stack_error_promise',{$logError:function(e,f) {error = e; fullref = f;}})
.catch(Object)
.then(function() {
assert.equal(error && error.message,'error');
assert.equal(fullref,'stack_error_promise');
});
});
t.test('error with a stack', async t => {
const $global = Object.create(Global);
const facts = {
stack_error: function() { throw new Error('error');}
};
await clues(facts,'stack_error',$global).then(shouldErr,Object);
t.same($global.error.message,'error');
t.ok($global.error.stack,'contains a stack');
t.same($global.error.fullref,'stack_error','fullref ok');
});
it('should not log an error with no stack',function() {
var facts = Object.create(logic),error;
return clues(facts,'rejection',{$logError:function(e) {error = e;}})
.catch(Object)
.then(function() {
assert.equal(error,undefined);
});
});
t.test('promise rejected with a stack', async t => {
const $global = Object.create(Global);
const facts = {
stack_error_promise: () => clues.Promise.reject(new Error('error'))
};
it('should not log a rejection with no stack',function() {
var facts = Object.create(logic),error;
return clues(facts,'rejection',{$logError:function(e) {error = e;}})
.catch(Object)
.then(function() {
assert.equal(error,undefined);
});
});
it('should log an error even if only used optionally',function() {
var facts = Object.create(logic),error;
return clues(facts,'optional',{$logError:function(e) {error = e;}})
.catch(Object)
.then(function() {
assert.equal(error.message,'error');
});
});
await clues(facts,'stack_error_promise',$global).then(shouldErr,Object);
t.same($global.error.message,'error','error passed to $logError');
t.ok($global.error.stack,'contains a stack');
t.same($global.error.fullref,'stack_error_promise','fullref ok');
});
t.test('error without a stack (rejection)', async t => {
const $global = Object.create(Global);
const facts = {
rejection: function() { throw 'error';}
};
await clues(facts,'rejection',$global).then(shouldErr,Object);
t.same($global.error,undefined,'should not $logError');
});
t.test('Promise rejection without a stack', async t => {
const $global = Object.create(Global);
const facts = {
rejection_promise: function() { return Promise.reject('error');}
};
await clues(facts,'rejection_promise',$global).then(shouldErr,Object);
t.same($global.error,undefined,'should not $logError');
});
t.test('stack error in an optional dependency', async t => {
const $global = Object.create(Global);
const facts = {
stack_error: () => { throw new Error('error');},
rejection: () => { throw 'error';},
optional: (_stack_error,_rejection) => 'OK'
};
await clues(facts,'optional',$global);
t.same($global.error.message,'error','error passed to $logError');
t.ok($global.error.stack,'contains a stack');
t.same($global.error.fullref,'optional.stack_error','fullref ok');
});
});
});
});

@@ -1,143 +0,144 @@

var clues = require('../clues'),
assert = require('assert');
const clues = require('../clues');
const Promise = require('bluebird');
const t = require('tap');
describe('$external',function() {
var logic = {
a : 5,
simple : Object.create({
count : 0,
$external : function(ref) {
this.count+=1;
if (ref == 'STOP') throw {message:'STOP_ERROR',error:true};
return 'simple:'+ref;
t.test('$external', {autoend:true, jobs: 10}, t => {
t.test('in simple logic with $external', {autoend: true}, t => {
const facts = {
simple: {
count : 0,
$external : function(ref) {
this.count+=1;
if (ref == 'STOP') throw {message:'STOP_ERROR',error:true};
return 'simple:'+ref;
}
}
}),
shorthand : function $external(ref) {
return 'answer:'+ref;
},
shorthandThis : function $external() {
return this;
},
as_argument: function($external) {
return 'answer:'+$external;
},
as_argument_es6: $external => 'answer:'+$external,
concurrent : function $external() {
return clues.Promise.delay(Math.random()*1000)
.then(function() {
return new Date();
});
}
};
};
var facts = Object.create(logic);
describe('in simple logic',function() {
it('runs $external function for non-existent property',function() {
return clues(facts,'simple.test')
.then(function(d) {
assert.equal(d,'simple:test');
assert(facts.simple.test.isFulfilled());
assert.equal(facts.simple.test.value(),'simple:test');
assert.equal(facts.simple.count,1);
});
t.test('asking for non-existent property', async t => {
const d = await clues(facts,'simple.test.1');
t.same(d,'simple:test.1','returns the $external function value');
t.ok(facts.simple['test.1'].isFulfilled(),'resolved promise is placed on logic');
t.same(facts.simple['test.1'].value(),'simple:test.1','promise contains value');
t.same(facts.simple.count,1,'function is called once');
});
it('concurrent requests should return first response',function() {
var requests = Array.apply(null, {length: 10})
.map(function() {
return clues(facts,'concurrent.test_concurrent');
});
return clues.Promise.all(requests)
.then(function(d) {
return d.map(function(e) {
assert.equal(e,d[0],'Return time should be the same');
});
});
t.test('asking for a different property',async t => {
t.same(await clues(facts,'simple.test.2'),'simple:test.2','returns new value');
t.same(facts.simple.count,2,'$external has now been called twice');
});
it('passes the full path as argument',function() {
return clues(facts,'simple.test.a.b.c')
.then(function(d) {
assert.equal(d,'simple:test.a.b.c');
assert.equal(facts.simple.count,2);
});
t.test('results so far are cached', async t => {
const d = await Promise.all([ clues(facts,'simple.test.1'), clues(facts,'simple.test.2')]);
t.same(d[0],'simple:test.1','first is cached');
t.same(d[1],'simple:test.2','second is cached');
t.equal(facts.simple.count,2,'an $external is not run again');
});
it('caches previous results',function() {
return clues(facts,['simple.test','simple.test.a.b.c',function(a,b) {
assert.equal(a,'simple:test');
assert.equal(b,'simple:test.a.b.c');
assert.equal(facts.simple.count,2);
}]);
t.test('error raised in $external', async t => {
const e = await clues(facts,'simple.STOP',{},'__test__').catch(Object);
t.same(e.message,'STOP_ERROR','error message ok');
//t.same(e.ref,'abc','ref is ok');
t.same(e.fullref,'simple.STOP','fullref is ok');
t.same(e.caller,'STOP','caller is ok');
t.equal(facts.simple.STOP.isRejected(),true,'rejected promise in logic');
t.equal(facts.simple.STOP.reason().message,'STOP_ERROR','rejected promise has error');
t.equal(facts.simple.count,3,'$external has now been called 3 times');
});
});
it('handles errors correctly',function() {
return clues(facts,'simple.STOP')
.then(function() {
throw 'Should Error';
},function(e) {
assert.equal(e.error,true);
assert.equal(e.message,'STOP_ERROR');
assert.equal(facts.simple.count,3);
assert.equal(e.fullref,'simple.STOP');
});
t.test('concurrent request', async t => {
const facts = {
concurrent : {
count: 0,
$external : function() {
return clues.Promise.delay(Math.random()*1000)
.then(() => {
this.count += 1;
return new Date();
});
}
}
};
const d = await Promise.map([...Array(10)],() => clues(facts,'concurrent.test.value'));
t.ok( d.every(e => e == d[0]),'should return the same value');
t.same(facts.concurrent.count,1,'and should not rerun the function');
});
t.test('$external inside a function', {autoend: true}, t => {
const facts = {
a : 'ok:',
funct : a => ({
count : 0,
$external : function(ref) {
this.count+=1;
return a+ref;
}
})
};
t.test('asking for non-existent property', async t => {
t.same( await clues(facts,'funct.test.1'), 'ok:test.1', 'returns value');
t.same(facts.funct.value().count,1,'$external is called once');
t.same( await clues(facts,'funct.test.1'), 'ok:test.1', 'previous results are cached');
t.same(facts.funct.value().count,1,'$an fn not run again for same property');
});
});
describe('when function name is $external',function() {
it('acts as a shorthand for empty object with $external',function() {
return clues(facts,'shorthand.first')
.then(function(d) {
assert.equal(d,'answer:first');
assert.equal(facts.shorthand.value().first.value(),'answer:first');
return clues(facts,'shorthand.second');
})
.then(function(d) {
assert.equal(d,'answer:second');
assert.equal(facts.shorthand.value().first.value(),'answer:first');
assert.equal(facts.shorthand.value().second.value(),'answer:second');
});
t.test('when function name is $external', {autoend: true}, t => {
const facts = {
a: 'ok:',
shorthand : function $external(ref) {
return this.a+ref;
}
};
t.test('asking for a property', async t => {
t.same( await clues(facts,'shorthand.test.1'),'ok:test.1','returns right value');
t.same( facts.shorthand.value()['test.1'].value(),'ok:test.1','stores promise with value');
});
it('assumes this of the parent',function() {
return clues(facts,'shorthandThis.test')
.then(function(d) {
assert.equal(d,facts);
});
t.test('asking for another property', async t => {
t.same( await clues(facts,'shorthand.test.2'),'ok:test.2','returns right value');
t.same( facts.shorthand.value()['test.2'].value(),'ok:test.2','stores promise with value');
});
});
describe('when argument name is $external',function() {
it('acts as a shorthand for empty object with $external',function() {
return clues(facts,'as_argument.first')
.then(function(d) {
assert.equal(d,'answer:first');
assert.equal(facts.shorthand.value().first.value(),'answer:first');
return clues(facts,'as_argument.second');
})
.then(function(d) {
assert.equal(d,'answer:second');
assert.equal(facts.shorthand.value().first.value(),'answer:first');
assert.equal(facts.shorthand.value().second.value(),'answer:second');
});
t.test('when argument name is $external', {autoend: true}, t => {
const facts = {
a: 'ok:',
as_argument: function ($external) {
return this.a + $external;
},
as_argument_es6 : a => $external => a + $external
};
t.test('regular function', async t => {
t.test('asking for a property', async t => {
t.same( await clues(facts,'as_argument.test.1'),'ok:test.1','returns right value');
t.same( facts.as_argument.value()['test.1'].value(),'ok:test.1','stores promise with value');
});
t.test('asking for another property', async t => {
t.same( await clues(facts,'as_argument.test.2'),'ok:test.2','returns right value');
t.same( facts.as_argument.value()['test.2'].value(),'ok:test.2','stores promise with value');
});
});
it('as ES6 acts as a shorthand for empty object with $external',function() {
return clues(facts,'as_argument_es6.first')
.then(function(d) {
assert.equal(d,'answer:first');
assert.equal(facts.shorthand.value().first.value(),'answer:first');
return clues(facts,'as_argument_es6.second');
})
.then(function(d) {
assert.equal(d,'answer:second');
assert.equal(facts.shorthand.value().first.value(),'answer:first');
assert.equal(facts.shorthand.value().second.value(),'answer:second');
});
t.test('ES6 fat arrow', async t => {
t.test('asking for a property', async t => {
t.same( await clues(facts,'as_argument_es6.test.1'),'ok:test.1','returns right value');
t.same( facts.as_argument_es6.value()['test.1'].value(),'ok:test.1','stores promise with value');
});
t.test('asking for another property', async t => {
t.same( await clues(facts,'as_argument_es6.test.2'),'ok:test.2','returns right value');
t.same( facts.as_argument_es6.value()['test.2'].value(),'ok:test.2','stores promise with value');
});
});
});
});

@@ -1,7 +0,7 @@

var clues = require('../clues'),
assert = require('assert'),
Promise = require('bluebird');
const clues = require('../clues');
const Promise = require('bluebird');
const t = require('tap');
describe('facts',function() {
var logic = {
t.test('facts',{autoend: true}, t => {
const Logic = {
response : function() { return Promise.delay(500,42);},

@@ -11,27 +11,22 @@ other : function() { return 5; }

var facts = Object.create(logic);
const facts = Object.create(Logic);
it('should return the solved logic when determined',function() {
var start = new Date();
return clues(facts,'response')
.then(function(d) {
var wait = (new Date()) - start;
assert.equal(wait >= 500,true,'wait was '+wait);
assert.equal(d,42);
});
t.test('called first time', async t => {
const start = new Date();
t.same(await clues(facts,'response'),42,'resolves function when called');
const wait = new Date() - start;
t.ok( wait >= 500,true, `wait was ${wait}`);
});
it('should return value immediately when solved for again',function() {
var start = new Date();
return clues(facts,'response')
.then(function(d) {
var wait = (new Date()) - start;
assert.equal(wait <= 20,true,'wait was '+wait);
assert.equal(d,42);
});
t.test('called second time', async t => {
const start = new Date();
t.same(await clues(facts,'response'),42,'resolves function when called');
const wait = new Date() - start;
t.ok( wait <= 100,true, 'should return immediately');
});
it('should not be solved for unrequested logic',function() {
assert(typeof facts.other === 'function');
t.test('unrequested logic function', {autoend: true}, t => {
t.same(typeof facts.other,'function','should not be resolved');
});
});

@@ -1,8 +0,10 @@

var clues = require('../clues'),
assert = require('assert');
const clues = require('../clues');
const t = require('tap');
describe('Frozen logic',function() {
var calls = 0;
const shouldErr = () => { throw 'SHOULD_ERROR'; };
var Logic = {
t.test('Frozen logic', {autoend: true}, t => {
let calls = 0;
const Logic = {
a : b => b,

@@ -16,36 +18,27 @@ b : () => {

Object.freeze(Logic);
describe('direct access',function() {
it('should not run directly ',function() {
return clues(Logic,'a')
.then(function() {
throw 'SHOULD_ERROR';
},function(e) {
assert.equal(e.message,'Object immutable');
assert.equal(e.value,15);
assert.equal(typeof Logic.a,'function');
});
});
t.test('solving property of a frozen object directly', async t => {
const e = await clues(Logic,'a').then(shouldErr,Object);
t.same(e.message,'Object immutable','errors as Object immutable');
//t.same(calls,0,'does not execute function');
t.same(typeof Logic.a,'function','does not modify function');
t.same(typeof Logic.b,'function','does not modify function');
});
describe('cloned access',function() {
var facts = Object.create(Logic);
it('should resolve',function() {
return clues(facts,'a')
.then(function(a) {
assert.equal(a,15);
assert.equal(calls,2);
assert.equal(facts.a.value(),15);
});
t.test('solving on a clone of a frozen object', async t => {
const facts = Object.create(Logic);
t.test('first time', async t => {
const d = await clues(facts,'a');
t.same(d,15,'correct result');
t.same(calls,2,'two calls have been made');
t.same(facts.a.value(),15,'promise is registered');
});
it('should memoize',function() {
return clues(facts,'a')
.then(function(a) {
assert.equal(a,15);
assert.equal(calls,2);
assert.equal(facts.a.value(),15);
});
t.test('second time', async t => {
const d = await clues(facts,'a');
t.same(d,15,'correct result');
t.same(calls,2,'two calls have been made');
t.same(facts.a.value(),15,'promise is registered');
});
});
});
});

@@ -1,6 +0,8 @@

var clues = require('../clues'),
assert = require('assert');
const clues = require('../clues');
const t = require('tap');
describe('Global variable',function() {
var logic = {
const shouldErr = () => { throw 'Should error'; };
t.test('Global variable', {autoend: true}, t => {
const Logic = {
a: 123,

@@ -39,5 +41,5 @@ b: {

var facts = Object.create(logic);
const facts = Object.create(Logic);
var global = {
const global = {
$input : {

@@ -51,32 +53,19 @@ test: 10,

it('should be applied where referenced',function() {
return clues(facts,'b.c',global)
.then(function(d) {
assert.equal(d,133);
assert.equal(facts.b.count,1);
});
t.test('when referenced as a dependency of a function', async t => {
t.same(await clues(facts,'b.c',global), 133, 'uses the global variable');
t.same(facts.b.count,1,'fn called once');
});
it('can not be applied directly',function() {
return clues(facts,'$input',global)
.then(function() {
throw 'Should Error';
},function(e) {
assert.equal(e.message,'$input not defined');
});
t.test('when referenced directly', async t => {
const e = await clues(facts,'$input',global).then(shouldErr,Object);
t.same(e.message,'$input not defined','should error');
});
it('can not be applied as part of dot notation',function() {
return clues(facts,'d.c',global)
.then(function(d) {
assert.equal(d,29);
});
t.test('inside a nested structure', async t => {
t.same(await clues(facts,'d.c',global),29,'uses the global variable');
});
it('can be used as object $global',function() {
return clues(facts,'e.f.g',global)
.then(function(d) {
assert.equal(d,4);
});
t.test('$global object as dependency', async t => {
t.same(await clues(facts,'e.f.g',global), 4, 'uses the $global object');
});
});

@@ -1,12 +0,13 @@

var clues = require('../clues'),
inject = require('../util/inject'),
assert = require('assert');
const clues = require('../clues');
const Promise = require('bluebird');
const inject = require('../util/inject');
const t = require('tap');
var Db = {
user : function(userid) {
return // database.query('users where usedid = ?',[userid]);
}
const shouldErr = () => { throw 'Should Error'; };
const Db = {
user : (userid) => undefined // database.query('users where usedid = ?',[userid]);
};
var Logic = {
const Logic = {
db : function(userid) {

@@ -22,45 +23,47 @@ return Object.create(Db,{

var injected = function($global) {
return inject(Object.create(Logic), {
'userid': function(original_userid) {
return 'test_'+original_userid;
},
'db.user' : function(userid) {
return { desc: 'this is an injected response', userid: userid};
},
'impossible.to.reach' : 43,
'possible' : {},
'possible.value': 42,
'never_traversed.value' : 11
},$global);
};
const injected = $global => inject(Object.create(Logic), {
'userid': function(original_userid) {
return 'test_'+original_userid;
},
'db.user' : function(userid) {
return { desc: 'this is an injected response', userid: userid};
},
'impossible.to.reach' : 43,
'possible' : {},
'possible.value': 42,
'never_traversed.value' : 11
},$global);
describe('inject',function() {
it('modifies the api tree',function() {
return clues(injected,'db.user',{input:{userid:'johndoe'}})
.then(function(d) {
assert.equal(d.userid,'test_johndoe');
});
});
t.test('inject', {autoend: true}, t => {
t.test('simple example', {autoend: true}, t => {
t.test('modified api tree', async t => {
const d = await clues(injected,'db.user',{input:{userid:'johndoe'}});
t.same(d.userid,'test_johndoe','username is now available');
});
it('allows sequential injections',function() {
return clues(injected,'possible.value',{})
.then(function(d) {
assert.equal(d,42);
});
t.test('sequential injection into a nested value', async t => {
const d = await clues(injected,'possible.value',{});
t.same(d,42,'resolves to injected value');
});
t.test('injecting base paths that do not exist', async t => {
const d = await clues(injected,'impossible.to.reach',{});
t.same(d,43,'resolves to injected value');
});
t.test('injected paths that are not traversed', async t => {
const d = await clues({},injected);
t.same(typeof d.never_traversed,'function','are not executed');
});
});
it('creates base paths that do not exists',function() {
return clues(injected,'impossible.to.reach',{})
.then(function(d) {
assert.equal(d,43);
});
t.test('injecting without $global', async t => {
const d = await Promise.try(() =>inject(Object.create(Logic),{'possible.value': 42})).then(shouldErr,Object);
t.same(d.message,'$global not supplied to inject','throws');
});
it('does not execute injected paths that are not traversed',function() {
return clues({},injected)
.then(function(injected) {
assert(typeof injected.never_traversed === 'function');
});
t.test('injecting a non-object', async t => {
const d = await Promise.try(() =>inject(Object.create(Logic),42,{})).then(shouldErr,Object);
t.same(d.message,'Invalid properties passed to inject','throws');
});
});

@@ -1,5 +0,5 @@

var clues = require('../clues'),
assert = require('assert');
const clues = require('../clues');
const t = require('tap');
function logic($global) {
function facts($global) {
$global.$constant = 380;

@@ -9,22 +9,11 @@ return { a : { b : function($constant) { return $constant; } } };

describe('When logic is a function',function() {
it('it is evaluated before solving',function() {
return clues(logic,'a.b')
.then(function(d) {
assert.equal(d,380);
});
});
function wrapped() {
return function($global) {
return facts;
};
}
it('even nested functions are evaluated before solving',function() {
function wrapped() {
return function($global) {
return logic;
};
}
return clues(wrapped,'a.b')
.then(function(d) {
assert.equal(d,380);
});
});
t.test('when logic is a function', async t => {
t.same( await clues(facts,'a.b'),380,'direct call is evaluated before solving');
t.same( await clues(wrapped,'a.b'),380,'nested functions evaluated');
});

@@ -1,20 +0,14 @@

var clues = require('../clues'),
assert = require('assert'),
Promise = require('bluebird');
const clues = require('../clues');
const Promise = require('bluebird');
const t = require('tap');
var logic = Promise.delay(100).then(function() {
const Logic = Promise.delay(100).then(function() {
return {
$constant : 380,
a : function($constant) { return {b: $constant}; }
a : function($constant) { return {b: $constant}; }
};
});
describe('When logic is a promise',function() {
it('is used when resolved',function() {
return clues(logic,'a.b')
.then(function(d) {
assert.equal(d,380);
});
});
t.test('When logic is a promise', async t => {
t.same( await clues(Logic,'a.b'),380,'logic is resolved before solving');
});

@@ -1,7 +0,7 @@

var clues = require('../clues'),
assert = require('assert'),
Promise = require('bluebird');
const clues = require('../clues');
const Promise = require('bluebird');
const t = require('tap');
describe('function name',function() {
var logic = {
t.test('function name', {autoend: true}, t => {
const logic = {
M1 : function() { return Promise.delay(100,10); },

@@ -12,19 +12,15 @@ M2 : function $hide() { return Promise.delay(20,300); },

var facts = Object.create(logic);
const facts = Object.create(logic);
describe('before resolution ',function() {
it('is defined in function',function() {
assert.equal(facts.M2.name,'$hide');
});
t.test('before resolution', t => {
t.same(facts.M2.name,'$hide','name is a property of function');
t.end();
});
describe('after resolution',function() {
it('is retained in promise',function() {
return clues(facts,'M3')
.then(function() {
assert.equal(facts.M3.value(),310);
assert.equal(facts.M2.name,'$hide');
});
});
t.test('after resolution', {autoend:true}, async t => {
await clues(facts,'M3');
t.same(facts.M2.name,'$hide','name is a property of promise');
t.same(facts.M3.value(),310,'and promise resolves to value');
});
});

@@ -1,54 +0,43 @@

var clues = require('../clues'),
assert = require('assert');
const clues = require('../clues');
const Promise = require('bluebird');
const t = require('tap');
function shouldError(e) { throw 'Should error '+e;}
function notDefined(e) { assert.deepEqual(e.message,'test not defined'); }
[null,undefined,4,'a',0,Object,Array,Object].forEach(function(nothing) {
describe('with value '+String(nothing),function() {
describe(' as logic',function() {
t.test('null logic', {autoend: true}, t => {
const values = [null,undefined,4,'a',0,Object,Array,Object];
it('property not defined',function() {
return clues(nothing,'test')
.then(shouldError,notDefined);
});
t.test('solving for a variable of null logic', async t => {
await Promise.map(values, async nothing => {
const value = await clues(nothing,'test').then(shouldError,Object);
t.same(value.message,'test not defined',`${nothing}.test is undefined`);
});
t.end();
});
it('path not defined',function() {
return clues(nothing,'test.test')
.then(shouldError,notDefined);
});
t.test('solving for a nested variable of null logic', async t => {
await Promise.map(values, async nothing => {
const value = await clues(nothing,'test.test').then(shouldError,Object);
t.same(value.message,'test not defined',`${nothing}.test.test is undefined`);
});
t.end();
});
it('context of empty function is empty object',function() {
return clues(nothing,function() {
if (nothing == 'a')
return assert.deepEqual(this,{0:'a'});
else
assert.deepEqual(this,{});
});
});
it('property fallback to global (if exists)',function() {
return clues(nothing,function(test) {
assert.equal(test,'global');
},{test:'global'});
});
t.test('solving for context of null logic', async t => {
await Promise.map(values, async nothing => {
const context = await clues(nothing,function() { return this;});
t.same(context, nothing == 'a' ? {0:'a'} : {},`${nothing} has a context of ${JSON.stringify(context)}`);
});
t.end();
});
describe('as a property',function() {
it('should resolve correctly',function() {
var obj = {};
obj.test = nothing;
return clues(obj,'test')
.then(function(d) {
// functions are always resolved to objects
return assert.deepEqual(d, typeof nothing === 'function' ? nothing() : nothing);
},function(e) {
// we only error if the property is `undefined`
assert.equal(nothing,undefined);
assert.equal(e.message,'test not defined');
});
});
t.test('null logic as a property', async t => {
await Promise.map(values.filter(d => d !== undefined), async nothing => {
const d = await clues({test: nothing},'test');
const expected = typeof nothing === 'function' ? nothing() : nothing;
t.same(d, expected,`test.${nothing} == ${expected}`);
});
t.end();
});
t.end();
});
// See optimization killers https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#1-tooling
const Promise = require('bluebird');
const execAsync = Promise.promisify(require('child_process').exec);
const t = require('tap');
var child_process = require('child_process');
var statusCodes = {
const statusCodes = {
1: ['OK','Clues Optimized'],

@@ -14,3 +15,3 @@ 2: ['ERR','Clues Not Optimized'],

var code = `
const code = `
var clues = require('../clues');

@@ -29,9 +30,5 @@

describe('V8 compiler',function() {
it('optimize the clues function',function(cb) {
child_process.exec('echo "'+code+'" | node --allow-natives-syntax',function(err,stdout,stderr) {
var status = statusCodes[stdout];
cb(err || stderr || (status[0] === 'ERR' && status[1]),status);
});
});
t.test('V8 compiler', async t => {
const d = await execAsync('echo "'+code+'" | node --allow-natives-syntax');
t.same(statusCodes[d][0],'OK');
});

@@ -1,8 +0,10 @@

var clues = require('../clues'),
assert = require('assert'),
Promise = require('bluebird');
const clues = require('../clues');
const Promise = require('bluebird');
const t = require('tap');
describe('optional argument',function() {
const shouldErr = () => { throw 'Should error'; };
var logic = {
t.test('optional argument', {autoend: true}, t => {
const Logic = {
data : function() { return Promise.delay(1000,5); },

@@ -14,40 +16,22 @@ passthrough : function(_optional) { return _optional; },

describe('not supplied',function() {
return it('should return undefined',function() {
clues(Object.create(logic),'passthrough')
.then(function(d) {
assert.deepEqual(d,undefined);
});
});
const facts = () => Object.create(Logic);
t.test('not supplied', async t => {
t.same(await clues(facts,'passthrough'),undefined,'returns undefined');
});
describe('with internal default',function() {
it('should use the internal default',function() {
return clues(Object.create(logic),'internalize')
.then(function(d) {
assert.equal(d,7);
});
});
t.test('internal default', async t => {
t.same(await clues(facts,'internalize'),7,'returns the default');
});
describe('with a set fact',function() {
it('should return the right value',function() {
return clues(Object.create(logic),'passthrough',{optional:10})
.then(function(d) {
assert.equal(d,10);
});
});
t.test('with a set global', async t => {
t.same(await clues(facts,'passthrough',{optional:10}),10,'returns global');
});
describe('with a working function',function() {
it('should return the function results',function() {
return clues(Object.create(logic),'optional_data')
.then(function(d) {
assert.equal(d,5);
});
});
t.test('with a working function', async t => {
t.same(await clues(facts,'optional_data'),5,'returns value');
});
describe('as an error',function() {
var logic2 = {
t.test('as an error', async t => {
const logic2 = {
error : function() { throw '#Error'; },

@@ -59,30 +43,19 @@ optional : function(_error) { return _error; },

it('should return undefined, if optional',function() {
return clues(Object.create(logic2),'optional')
.then(function(d) {
assert.equal(d,undefined);
});
});
const facts2 = () => Object.create(logic2);
it('should raise error if non-optonal',function() {
return clues(Object.create(logic2),'regular')
.then(function() {
throw 'Should error';
},function(e) {
assert.equal(e.message,'#Error');
});
});
let d = await clues(facts2,'optional');
t.same(d,undefined,'_ => undefined');
it('should return the error as an object when prefix is two underscores',function() {
return clues(Object.create(logic2),'showerror')
.then(function(e) {
assert.equal(e.error,true);
assert.equal(e.message,'#Error');
assert.equal(e.fullref,'showerror.error');
assert.equal(e.ref,'error');
});
});
d = await clues(facts2,'regular').then(shouldErr,Object);
t.same(d.message,'#Error','raises error if not optional');
d = await clues(facts2,'showerror');
t.same(d.error,true);
t.same(d.message,'#Error');
t.same(d.fullref,'showerror.error');
t.same(d.ref,'error');
});
});

@@ -1,9 +0,9 @@

var clues = require('../clues'),
assert = require('assert'),
Promise = require('bluebird');
const clues = require('../clues');
const Promise = require('bluebird');
const t = require('tap');
function shouldErr() { throw 'Should throw an error'; }
describe('private functions',function() {
var logic = {
t.test('private functions', {autoend: true}, t => {
const Logic = {
M1 : function() { return Promise.delay(100,10); },

@@ -19,46 +19,30 @@ M2 : function $private() { return Promise.delay(20,300); },

var facts = Object.create(logic);
describe('before resolution ',function() {
it('regular fn not disclosed',function() {
return clues(facts,'M2')
.then(shouldErr,function(e) {
assert.equal(e.message,'M2 not defined');
assert.equal(facts.M2,logic.M2);
});
t.test('before resolution', {autoend: true}, t => {
t.test('private regular function', async t => {
const facts = Object.create(Logic);
const e = await clues(facts,'M2').catch(Object);
t.same(e.message,'M2 not defined','errors as not defined');
t.same(facts.M2,Logic.M2,'fn is not run');
});
it('array defined fn not disclosed',function() {
return clues(facts,'M3')
.then(shouldErr,function(e) {
assert.equal(e.message,'M3 not defined');
assert.equal(facts.M3,logic.M3);
});
});
t.test('private array function', async t => {
const facts = Object.create(Logic);
const e = await clues(facts,'M3').catch(Object);
t.same(e.message,'M3 not defined','errors as not defined')
t.same(facts.M3, Logic.M3,'fn is not run');
})
});
describe('after resolution',function() {
it('should be accessible indirectly',function() {
return clues(facts,'MTOP')
.then(function(d) {
assert.equal(d,380);
});
});
t.test('after resolution', async t => {
const facts = Object.create(Logic);
const MTOP = await clues(facts,'MTOP');
const M2 = await clues(facts,'M2').catch(Object);
const M3 = await clues(facts,'M3').catch(Object);
it('regular fn promise not disclosed',function() {
return clues(facts,'M2')
.then(shouldErr,function(e) {
assert.equal(e.message,'M2 not defined');
assert.equal(facts.M2.value(),300);
});
});
it('array defined fn promise not disclosed',function() {
return clues(facts,'M3')
.then(shouldErr,function(e) {
assert.equal(e.message,'M3 not defined');
assert.equal(facts.M3.value(),310);
});
});
t.same(MTOP,380,'is available indirectly');
t.same(M2.message,'M2 not defined','private fn not available directly');
t.same(facts.M2.value(),300,'private fn promise has the resolved value');
t.same(M3.message,'M3 not defined','private array - not defined');
t.same(facts.M3.value(),310,'private fn promise has the resolved value');
});
});

@@ -1,7 +0,6 @@

var clues = require('../clues'),
assert = require('assert'),
Promise = require('bluebird');
const clues = require('../clues');
const Promise = require('bluebird');
const t = require('tap');
var logic = {
const Logic = {
a : 41,

@@ -18,9 +17,4 @@ test : Promise.resolve(function(a) {

describe('When promise resolve to a function',function() {
it('is used when resolved',function() {
return clues(logic,'test')
.then(function(d) {
assert.equal(d,42);
});
});
});
t.test('when promise resolve to a function', async t => {
t.same(await clues(Logic,'test'),42,'function is evaluated');
});

@@ -1,190 +0,167 @@

var clues = require('../clues'),
assert = require('assert');
const clues = require('../clues');
const Promise = require('bluebird');
const t = require('tap');
describe('$property',function() {
var logic = {
a : 5,
simple : Object.create({
count : 0,
$property : function(ref) {
this.count+=1;
if (isNaN(ref)) throw 'NOT_A_NUMBER';
return +ref+2;
}
}),
funct : function(a) {
return {
t.test('$property', {autoend:true, jobs: 10}, t => {
t.test('in simple logic with $property', {autoend: true}, t => {
const facts = {
simple: {
count : 0,
$property : function(ref) {
this.count+=1;
return +ref+a;
if (isNaN(ref)) throw 'NOT_A_NUMBER';
return +ref+2;
}
};
},
nested : {
$property : function(ref) {
return { a : { b: { c: function() { return +ref+10; } } } };
}
},
shorthand : function $property(ref) {
return +ref * this.a;
},
as_argument: function ($property) {
return $property * this.a;
},
as_argument_es6 : a => $property => $property * a,
concurrent : {
$property : function() {
return clues.Promise.delay(Math.random()*1000)
.then(function() {
return new Date();
});
}}
};
};
var facts = Object.create(logic);
describe('in simple logic',function() {
it('runs $property function for non-existent property',function() {
return clues(facts,'simple.1234')
.then(function(d) {
assert.equal(d,1236);
assert(facts.simple['1234'].isFulfilled());
assert.equal(facts.simple['1234'].value(),1236);
assert.equal(facts.simple.count,1);
});
t.test('asking for non-existent property', async t => {
const d = await clues(facts,'simple.1234');
t.same(d,1236,'returns the $property function value');
t.ok(facts.simple['1234'].isFulfilled(),'resolved promise is placed on logic');
t.same(facts.simple['1234'].value(),1236,'promise contains value');
t.same(facts.simple.count,1,'function is called once');
});
it('concurrent requests should return first response',function() {
var requests = Array.apply(null, {length: 10})
.map(function() {
return clues(facts,'concurrent.test');
});
return clues.Promise.all(requests)
.then(function(d) {
return d.map(function(e) {
assert.equal(e,d[0],'Return time should be the same');
});
});
t.test('asking for a different property',async t => {
t.same(await clues(facts,'simple.1235'),1237,'returns new value');
t.same(facts.simple.count,2,'$property has now been called twice');
});
it('runs $property again for a different property',function() {
return clues(facts,'simple.1235')
.then(function(d) {
assert.equal(d,1237);
assert.equal(facts.simple.count,2);
});
t.test('results so far are cached', async t => {
const d = await Promise.all([ clues(facts,'simple.1234'), clues(facts,'simple.1235')]);
t.same(d[0],1236,'first is cached');
t.same(d[1],1237,'second is cached');
t.equal(facts.simple.count,2,'an $property is not run again');
});
it('previous results are cached',function() {
return clues(facts,['simple.1234','simple.1235',function(a,b) {
assert.equal(a,1236);
assert.equal(b,1237);
assert.equal(facts.simple.count,2);
}]);
t.test('error raised in $property', async t => {
const e = await clues(facts,'simple.abc',{},'__test__').catch(Object);
t.same(e.message,'NOT_A_NUMBER','error message ok');
t.same(e.ref,'abc','ref is ok');
t.same(e.fullref,'simple.abc','fullref is ok');
t.same(e.caller,'__test__','caller is ok');
t.equal(facts.simple.abc.isRejected(),true,'rejected promise in logic');
t.equal(facts.simple.abc.reason().message,'NOT_A_NUMBER','rejected promise has error');
t.equal(facts.simple.count,3,'$property has now been called 3 times');
});
});
it('handles errors correctly',function() {
return clues(facts,'simple.abc',{},'__test__')
.then(function() {
throw 'Should Error';
},function(e) {
assert.equal(e.message,'NOT_A_NUMBER');
assert.equal(e.ref,'abc');
assert.equal(e.fullref,'simple.abc');
assert.equal(e.caller,'__test__');
assert.equal(facts.simple.abc.isRejected(),true);
assert.equal(facts.simple.abc.reason().message,'NOT_A_NUMBER');
assert.equal(facts.simple.count,3);
});
t.test('concurrent request', async t => {
const facts = {
concurrent : {
count: 0,
$property : function() {
return clues.Promise.delay(Math.random()*1000)
.then(() => {
this.count += 1;
return new Date();
});
}
}
};
const d = await Promise.map([...Array(10)],() => clues(facts,'concurrent.test'));
t.ok( d.every(e => e == d[0]),'should return the same value');
t.same(facts.concurrent.count,1,'and should not rerun the function');
});
t.test('$property inside a function', {autoend: true}, t => {
const facts = {
a : 5,
funct : a => ({
count : 0,
$property : function(ref) {
this.count+=1;
return +ref+a;
}
})
};
t.test('asking for non-existent property', async t => {
t.same( await clues(facts,'funct.1234'), 1239, 'returns value');
t.same(facts.funct.value().count,1,'$property is called once');
t.same( await clues(facts,'funct.1234'), 1239, 'previous results are cached');
t.same(facts.funct.value().count,1,'$an fn not run again for same property');
});
});
describe('inside a function',function() {
it('runs $property function for non-existent property',function() {
return clues(facts,'funct.1234')
.then(function(d) {
assert.equal(d,1239);
assert.equal(facts.funct.value().count,1);
});
t.test('$property inside a nested structure', {autoend:true}, t => {
const facts = {
nested : {
$property : function(ref) {
return { a : { b: { c: function() { return +ref+10; } } } };
}
}
};
t.test('asking for non existing property nested', async t => {
t.same( await clues(facts,'nested.1234.a.b.c'),1244,'returns correct value');
t.ok(facts.nested['1234'].value().a.b.c.isFulfilled(),'registers fulfilled promise');
t.same(facts.nested['1234'].value().a.b.c.value(),1244,'promise registers value');
});
it('caches previous results',function() {
return clues(facts,'funct.1234')
.then(function(d) {
assert.equal(d,1239);
assert.equal(facts.funct.value().count,1);
});
t.test('error raised in $property', async t => {
const e = await clues(facts,'nested.1234.a.b.d',{},'__test__').catch(Object);
t.same(e.ref,'d','ref ok');
t.same(e.fullref,'nested.1234.a.b.d','fullref ok');
t.same(e.caller,'__test__','caller ok');
});
});
describe('inside a nested structure',function() {
it('runs $property',function() {
return clues(facts,'nested.1234.a.b.c')
.then(function(d) {
assert.equal(d,1244);
assert(facts.nested['1234'].value().a.b.c.isFulfilled());
assert(facts.nested['1234'].value(),1244);
});
t.test('when function name is $property', {autoend: true}, t => {
const facts = {
a: 5,
shorthand : function $property(ref) {
return +ref * this.a;
}
};
t.test('asking for a property', async t => {
t.same( await clues(facts,'shorthand.2'),10,'returns right value');
t.same( facts.shorthand.value()['2'].value(),10,'stores promise with value');
});
it('handles error correctly',function() {
return clues(facts,'nested.1234.a.b.d',{},'__test__')
.then(function() {
throw 'Should error';
},function(e) {
assert.equal(e.ref,'d');
assert.equal(e.fullref,'nested.1234.a.b.d');
assert.equal(e.caller,'__test__');
});
t.test('asking for another property', async t => {
t.same( await clues(facts,'shorthand.4'),20,'returns right value');
t.same( facts.shorthand.value()['4'].value(),20,'stores promise with value');
});
});
describe('when function name is $property',function() {
it('acts as a shorthand for empty object with $property',function() {
return clues(facts,'shorthand.2')
.then(function(d) {
assert.equal(d,10);
assert.equal(facts.shorthand.value()['2'].value(),10);
return clues(facts,'shorthand.4');
})
.then(function(d) {
assert.equal(d,20);
assert.equal(facts.shorthand.value()['4'].value(),20);
assert.equal(facts.shorthand.value()['2'].value(),10);
});
t.test('when argument name is $property', {autoend: true}, t => {
const facts = {
a: 5,
as_argument: function ($property) {
return $property * this.a;
},
as_argument_es6 : a => $property => $property * a
};
t.test('regular function', async t => {
t.test('asking for a property', async t => {
t.same( await clues(facts,'as_argument.2'),10,'returns right value');
t.same( facts.as_argument.value()['2'].value(),10,'stores promise with value');
});
t.test('asking for another property', async t => {
t.same( await clues(facts,'as_argument.4'),20,'returns right value');
t.same( facts.as_argument.value()['4'].value(),20,'stores promise with value');
});
});
});
describe('when argument name is $property',function() {
it('acts as a shorthand for empty object with $property',function() {
return clues(facts,'as_argument.2')
.then(function(d) {
assert.equal(d,10);
assert.equal(facts.shorthand.value()['2'].value(),10);
return clues(facts,'as_argument.4');
})
.then(function(d) {
assert.equal(d,20);
assert.equal(facts.shorthand.value()['4'].value(),20);
assert.equal(facts.shorthand.value()['2'].value(),10);
});
t.test('ES6 fat arrow', async t => {
t.test('asking for a property', async t => {
t.same( await clues(facts,'as_argument_es6.2'),10,'returns right value');
t.same( facts.as_argument_es6.value()['2'].value(),10,'stores promise with value');
});
t.test('asking for another property', async t => {
t.same( await clues(facts,'as_argument_es6.4'),20,'returns right value');
t.same( facts.as_argument_es6.value()['4'].value(),20,'stores promise with value');
});
});
it('as ES6 acts as a shorthand for empty object with $property',function() {
return clues(facts,'as_argument_es6.2')
.then(function(d) {
assert.equal(d,10);
assert.equal(facts.shorthand.value()['2'].value(),10);
return clues(facts,'as_argument_es6.4');
})
.then(function(d) {
assert.equal(d,20);
assert.equal(facts.shorthand.value()['4'].value(),20);
assert.equal(facts.shorthand.value()['2'].value(),10);
});
});
});
});

@@ -1,7 +0,9 @@

var clues = require('../clues'),
assert = require('assert'),
Promise = require('bluebird');
const clues = require('../clues');
const Promise = require('bluebird');
const t = require('tap');
describe('In recursive logic',function() {
var logic = {
const shouldErr = () => { throw 'Should Error'; };
t.test('In recursive logic',{autoend: true}, t => {
const Logic = {
simple : {

@@ -23,11 +25,11 @@ value : 2,

e : {
f :12,
g : function(f) {
return 2*f;
},
h : function() {
return function(f) {
return f+2;
};
}
f :12,
g : function(f) {
return 2*f;
},
h : function() {
return function(f) {
return f+2;
};
}
}

@@ -42,96 +44,60 @@ }

var facts = Object.create(logic);
const facts = Object.create(Logic);
it('simple nesting works',function() {
return clues(facts,'simple.value')
.then(function(d) {
assert.equal(d,2);
});
t.test('simple nesting',async t => {
t.same( await clues(facts,'simple.value'),2,'works');
});
t.test('medium nesting works', async t => {
t.same( await clues(facts,'medium.bucket.value'),10,'works');
});
it('medium nesting works',function() {
return clues(facts,'medium.bucket.value')
.then(function(value) {
assert.equal(value,10);
});
t.test('ᐅ works as an alias for dot', async t => {
t.same( await clues(facts,'mediumᐅbucketᐅvalue'), 10, 'works');
});
t.test('complex nesting', async t => {
t.same( await clues(facts,'hard.a.b.c.d.id'),102,'works');
});
it('ᐅ works as an alias for dot',function() {
return clues(facts,'mediumᐅbucketᐅvalue')
.then(function(value) {
assert.equal(value,10);
});
t.test('ᐅ as an alias for dot',async t => {
t.same( await clues(facts,'hardᐅaᐅbᐅcᐅdᐅid'),102,'works');
});
describe('complex nesting',function() {
it('works', function() {
return clues(facts,'hard.a.b.c.d.id').then(function(value) {
assert.equal(value,102);
});
});
t.test('on returned functions',async t => {
t.same( await clues(facts,'hard.a.b.c.d.e.h'),14,'works');
});
it('ᐅ works as an alias for dot',function() {
return clues(facts,'hardᐅaᐅbᐅcᐅdᐅid')
.then(function(value) {
assert.equal(value,102);
});
});
t.test('works when fact repeats twice', async t => {
const d = await clues(facts,['hard.a.b.c.d.e','hard.a.b.c.d.e.f','hard.a.b.c.d.e.g',Array]);
t.same(d[1],12,'works');
t.same(d[2],24,'works');
});
it('works on returned functions',function() {
return clues(facts,'hard.a.b.c.d.e.h')
.then(function(h) {
assert.equal(h,14);
});
});
t.test('supports optional', async t => {
t.same( await clues(facts,['_hard.a.b.c.d.id',Number]),102,'works');
});
it('works when clue repeats twice',function() {
return clues(facts,['hard.a.b.c.d.e','hard.a.b.c.d.e.f','hard.a.b.c.d.e.g',function(a,b,c) {
assert.equal(b,12);
assert.equal(c,24);
}]);
});
it('supports optional',function() {
return clues(facts,['_hard.a.b.c.d.id',function(value) {
assert.equal(value,102);
}]);
});
it('can be resolved manually',function() {
return clues(facts,function(hard) {
return clues(hard.a.b,function(c) {
return clues(c.d,function(id) {
assert.equal(id,102);
});
});
t.test('path traversed manually', async t => {
const d = await clues(facts,hard => {
return clues(hard.a.b,c => {
return clues(c.d,'id');
});
});
t.same(d,102,'works');
});
it('bad path returns an error',function() {
return clues(facts,'hard.a.b.c.d.i.oo.oo')
.then(function() {
throw 'We should not arrive here';
},function(e) {
assert.equal(e.ref, 'i');
assert.equal(e.fullref, 'hard.a.b.c.d.i');
});
});
t.test('bad path returns an error', async t => {
const e = await clues(facts,'hard.a.b.c.d.i.oo.oo').then(shouldErr,Object);
t.same(e.message,'i not defined','errors');
t.same(e.ref,'i','ref ok');
t.same(e.fullref,'hard.a.b.c.d.i','fullref ok');
});
it('optional bad path returns undefined',function() {
return clues(facts,['_hard.a.b.e','simple.value',function(a,b) {
assert.equal(a,undefined);
assert.equal(b,2);
}]);
});
it('optional bad path returns undefined',function() {
return clues(facts,'hard.a.b.e')
.then(function() {
throw 'This function should return an error';
},function(e) {
assert.equal(e.message,'e not defined');
});
});
t.test('optional bad path returns undefined',async t => {
const d = await clues(facts,['_hard.a.b.e','simple.value',Array]);
t.same(d[0],undefined,'bad path is undefined');
t.same(d[1],2,'good path returns value');
});
});

@@ -1,59 +0,34 @@

var clues = require('../clues'),
assert = require('assert');
const clues = require('../clues');
const t = require('tap');
describe('$ as a first letter',function() {
t.test('$ as a first letter', {autoend:true}, t => {
var logic = {
const Logic = {
a : 10,
b : 11,
$logic_service : function(a) {
return a;
},
top : function(a) {
return {
$nested_service : function(b) {
return a+b;
}
};
}
$logic_service : a => a,
top : a => ({ $nested_service : b => a + b })
};
var global = {
$global_service : function(b) {
return b;
}
};
const global = { $global_service : b => b };
describe('in logic',function() {
it ('should return a function',function() {
return clues(Object.create(logic),'$logic_service',Object.create(global))
.then(function($logic_service) {
assert.equal(typeof $logic_service,'function');
assert.equal($logic_service(20),20);
});
});
t.test('in logic', async t => {
const d = await clues(Object.create(Logic),'$logic_service',Object.create(global));
t.same(typeof d, 'function','returns a function');
t.same(d(20),20,'function works');
});
t.test('in nested logic', async t => {
const d = await clues(Object.create(Logic),'top.$nested_service',Object.create(global));
t.same(typeof d, 'function','returns a function');
t.same(d(20),30,'function works');
});
describe('in nested logic',function() {
it ('should return a function',function() {
return clues(Object.create(logic),'top.$nested_service',Object.create(global))
.then(function($nested_service) {
assert.equal(typeof $nested_service,'function');
assert.equal($nested_service(20),30);
});
});
t.test('in global', async t => {
await clues(Object.create(Logic),$global_service => {
t.same(typeof $global_service, 'function','returns a function');
t.same($global_service(20),20,'function works');
},Object.create(global));
});
describe('in global',function() {
it('should return a function',function() {
return clues(Object.create(logic),function($logic_service) {
assert.equal(typeof $logic_service,'function');
assert.equal($logic_service(20),20);
},Object.create(global));
});
});
});

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc