Skip to content

Commit 5c04c6b

Browse files
author
Cache Hamm
committed
Engine results
1 parent 22dcd7d commit 5c04c6b

File tree

4 files changed

+203
-32
lines changed

4 files changed

+203
-32
lines changed

src/almanac.js

+16
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export default class Almanac {
2323
this.allowUndefinedFacts = Boolean(options.allowUndefinedFacts)
2424
this.pathResolver = options.pathResolver || defaultPathResolver
2525
this.successEvents = []
26+
this.ruleResults = []
2627

2728
for (const factId in runtimeFacts) {
2829
let fact
@@ -52,6 +53,21 @@ export default class Almanac {
5253
return this.successEvents
5354
}
5455

56+
/**
57+
* Adds a rule result
58+
* @param {Object} event
59+
*/
60+
addRuleResult (ruleResult) {
61+
this.ruleResults.push(ruleResult)
62+
}
63+
64+
/**
65+
* retrieve successful events
66+
*/
67+
getRuleResults () {
68+
return this.ruleResults
69+
}
70+
5571
/**
5672
* Retrieve fact by id, raising an exception if it DNE
5773
* @param {String} factId

src/engine.js

+14-2
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ class Engine extends EventEmitter {
191191
}
192192
return rule.evaluate(almanac).then((ruleResult) => {
193193
debug(`engine::run ruleResult:${ruleResult.result}`)
194+
almanac.addRuleResult(ruleResult)
194195
if (ruleResult.result) {
195196
almanac.addSuccessEvent(ruleResult.event)
196197
return this.emitAsync('success', ruleResult.event, almanac, ruleResult)
@@ -232,9 +233,20 @@ class Engine extends EventEmitter {
232233
debug('engine::run completed')
233234
return almanac.getSuccessEvents()
234235
}).then(events => {
236+
const ruleResults = almanac.getRuleResults()
237+
const { successResults, failureResults } = ruleResults.reduce((hash, ruleResult) => {
238+
const group = ruleResult.result ? 'successResults' : 'failureResults'
239+
hash[group].push(ruleResult)
240+
return hash
241+
}, { successResults: [], failureResults: [] })
242+
235243
resolve({
236-
events,
237-
almanac
244+
successResults,
245+
failureResults,
246+
almanac,
247+
248+
// deprecated:
249+
events
238250
})
239251
}).catch(reject)
240252
})

test/acceptance/acceptance.js

+80
Original file line numberDiff line numberDiff line change
@@ -148,9 +148,85 @@ describe('Acceptance', () => {
148148

149149
const engineResult = await engine.run({ baseIndex: 1 })
150150

151+
// results
152+
expect(engineResult.successResults.length).to.equal(2)
153+
expect(engineResult.successResults[0]).to.deep.equal({
154+
conditions: {
155+
all: [
156+
{
157+
fact: 'high-priority',
158+
factResult: [
159+
2
160+
],
161+
operator: 'contains',
162+
params: {
163+
factParam: 1
164+
},
165+
path: '$.values',
166+
result: true,
167+
value: 2
168+
},
169+
{
170+
fact: 'low-priority',
171+
factResult: 2,
172+
operator: 'in',
173+
result: true,
174+
value: [
175+
2
176+
]
177+
}
178+
],
179+
operator: 'all',
180+
priority: 1
181+
},
182+
event: {
183+
params: {
184+
eventParam: 1
185+
},
186+
type: 'event-1'
187+
},
188+
name: 'first',
189+
priority: 10,
190+
result: true
191+
})
192+
expect(engineResult.successResults[1]).to.deep.equal({
193+
conditions: {
194+
all: [
195+
{
196+
fact: 'high-priority',
197+
factResult: [
198+
2
199+
],
200+
operator: 'containsDivisibleValuesOf',
201+
params: {
202+
factParam: 1
203+
},
204+
path: '$.values',
205+
result: true,
206+
value: {
207+
fact: 'rule-created-fact',
208+
path: '$.array'
209+
}
210+
}
211+
],
212+
operator: 'all',
213+
priority: 1
214+
},
215+
event: {
216+
type: 'event-2'
217+
},
218+
name: 'second',
219+
priority: 1,
220+
result: true
221+
})
222+
expect(engineResult.failureResults).to.be.empty()
223+
224+
// events
151225
expect(engineResult.events.length).to.equal(2)
152226
expect(engineResult.events[0]).to.deep.equal(event1)
153227
expect(engineResult.events[1]).to.deep.equal(event2)
228+
229+
// callbacks
154230
expect(successSpy).to.have.been.calledTwice()
155231
expect(successSpy).to.have.been.calledWith(event1)
156232
expect(successSpy).to.have.been.calledWith(event2)
@@ -166,6 +242,10 @@ describe('Acceptance', () => {
166242

167243
const engineResult = await engine.run({ baseIndex: 1, 'rule-created-fact': '' })
168244

245+
expect(engineResult.successResults.length).to.equal(0)
246+
expect(engineResult.failureResults.length).to.equal(2)
247+
expect(engineResult.failureResults.every(rr => rr.result === false)).to.be.true()
248+
169249
expect(engineResult.events.length).to.equal(0)
170250
expect(failureSpy).to.have.been.calledTwice()
171251
expect(failureSpy).to.have.been.calledWith(event1)

test/engine-event.test.js

+93-30
Original file line numberDiff line numberDiff line change
@@ -87,37 +87,54 @@ describe('Engine: event', () => {
8787
it('"success" passes the event, almanac, and results', async () => {
8888
const failureSpy = sandbox.spy()
8989
const successSpy = sandbox.spy()
90-
engine.on('success', function (e, almanac, ruleResult) {
91-
expect(e).to.eql(event)
92-
expect(almanac).to.be.an.instanceof(Almanac)
90+
function assertResult (ruleResult) {
9391
expect(ruleResult.result).to.be.true()
9492
expect(ruleResult.conditions.any[0].result).to.be.true()
9593
expect(ruleResult.conditions.any[0].factResult).to.equal(21)
9694
expect(ruleResult.conditions.any[1].result).to.be.false()
9795
expect(ruleResult.conditions.any[1].factResult).to.equal(false)
96+
}
97+
engine.on('success', function (e, almanac, ruleResult) {
98+
expect(e).to.eql(event)
99+
expect(almanac).to.be.an.instanceof(Almanac)
100+
assertResult(ruleResult)
98101
successSpy()
99102
})
100103
engine.on('failure', failureSpy)
101-
await engine.run()
104+
105+
const { successResults, failureResults } = await engine.run()
106+
107+
expect(failureResults).to.have.lengthOf(0)
108+
expect(successResults).to.have.lengthOf(1)
109+
assertResult(successResults[0])
102110
expect(failureSpy.callCount).to.equal(0)
103111
expect(successSpy.callCount).to.equal(1)
104112
})
105113

106114
it('"event.type" passes the event parameters, almanac, and results', async () => {
107115
const failureSpy = sandbox.spy()
108116
const successSpy = sandbox.spy()
109-
engine.on(event.type, function (params, almanac, ruleResult) {
110-
expect(params).to.eql(event.params)
111-
expect(almanac).to.be.an.instanceof(Almanac)
117+
function assertResult (ruleResult) {
112118
expect(ruleResult.result).to.be.true()
113119
expect(ruleResult.conditions.any[0].result).to.be.true()
114120
expect(ruleResult.conditions.any[0].factResult).to.equal(21)
115121
expect(ruleResult.conditions.any[1].result).to.be.false()
116122
expect(ruleResult.conditions.any[1].factResult).to.equal(false)
123+
}
124+
engine.on(event.type, function (params, almanac, ruleResult) {
125+
expect(params).to.eql(event.params)
126+
expect(almanac).to.be.an.instanceof(Almanac)
127+
assertResult(ruleResult)
117128
successSpy()
118129
})
119130
engine.on('failure', failureSpy)
120-
await engine.run()
131+
132+
const { successResults, failureResults } = await engine.run()
133+
134+
expect(failureResults).to.have.lengthOf(0)
135+
expect(successResults).to.have.lengthOf(1)
136+
assertResult(successResults[0])
137+
121138
expect(failureSpy.callCount).to.equal(0)
122139
expect(successSpy.callCount).to.equal(1)
123140
})
@@ -126,19 +143,29 @@ describe('Engine: event', () => {
126143
const AGE = 10
127144
const failureSpy = sandbox.spy()
128145
const successSpy = sandbox.spy()
129-
engine.on('failure', function (e, almanac, ruleResult) {
130-
expect(e).to.eql(event)
131-
expect(almanac).to.be.an.instanceof(Almanac)
146+
function assertResult (ruleResult) {
132147
expect(ruleResult.result).to.be.false()
133148
expect(ruleResult.conditions.any[0].result).to.be.false()
134149
expect(ruleResult.conditions.any[0].factResult).to.equal(AGE)
135150
expect(ruleResult.conditions.any[1].result).to.be.false()
136151
expect(ruleResult.conditions.any[1].factResult).to.equal(false)
152+
}
153+
154+
engine.on('failure', function (e, almanac, ruleResult) {
155+
expect(e).to.eql(event)
156+
expect(almanac).to.be.an.instanceof(Almanac)
157+
assertResult(ruleResult)
137158
failureSpy()
138159
})
139160
engine.on('success', successSpy)
140161
engine.addFact('age', AGE) // age fails
141-
await engine.run()
162+
163+
const { successResults, failureResults } = await engine.run()
164+
165+
expect(failureResults).to.have.lengthOf(1)
166+
expect(successResults).to.have.lengthOf(0)
167+
assertResult(failureResults[0])
168+
142169
expect(failureSpy.callCount).to.equal(1)
143170
expect(successSpy.callCount).to.equal(0)
144171
})
@@ -186,9 +213,8 @@ describe('Engine: event', () => {
186213
it('"success" passes the event, almanac, and results', async () => {
187214
const failureSpy = sandbox.spy()
188215
const successSpy = sandbox.spy()
189-
engine.on('success', function (e, almanac, ruleResult) {
190-
expect(e).to.eql(event)
191-
expect(almanac).to.be.an.instanceof(Almanac)
216+
217+
function assertResult (ruleResult) {
192218
expect(ruleResult.result).to.be.true()
193219
expect(ruleResult.conditions.any[0].result).to.be.false()
194220
expect(ruleResult.conditions.any[0].factResult).to.equal(10)
@@ -199,10 +225,21 @@ describe('Engine: event', () => {
199225
expect(ruleResult.conditions.any[2].all[0].factResult).to.equal(80403)
200226
expect(ruleResult.conditions.any[2].all[1].result).to.be.true()
201227
expect(ruleResult.conditions.any[2].all[1].factResult).to.equal('male')
228+
}
229+
230+
engine.on('success', function (e, almanac, ruleResult) {
231+
expect(e).to.eql(event)
232+
expect(almanac).to.be.an.instanceof(Almanac)
233+
assertResult(ruleResult)
202234
successSpy()
203235
})
204236
engine.on('failure', failureSpy)
205-
await engine.run()
237+
238+
const { successResults, failureResults } = await engine.run()
239+
240+
assertResult(successResults[0])
241+
expect(failureResults).to.have.lengthOf(0)
242+
expect(successResults).to.have.lengthOf(1)
206243
expect(failureSpy.callCount).to.equal(0)
207244
expect(successSpy.callCount).to.equal(1)
208245
})
@@ -212,9 +249,7 @@ describe('Engine: event', () => {
212249
const GENDER = 'female'
213250
const failureSpy = sandbox.spy()
214251
const successSpy = sandbox.spy()
215-
engine.on('failure', function (e, almanac, ruleResult) {
216-
expect(e).to.eql(event)
217-
expect(almanac).to.be.an.instanceof(Almanac)
252+
function assertResult (ruleResult) {
218253
expect(ruleResult.result).to.be.false()
219254
expect(ruleResult.conditions.any[0].result).to.be.false()
220255
expect(ruleResult.conditions.any[0].factResult).to.equal(10)
@@ -225,12 +260,23 @@ describe('Engine: event', () => {
225260
expect(ruleResult.conditions.any[2].all[0].factResult).to.equal(ZIP_CODE)
226261
expect(ruleResult.conditions.any[2].all[1].result).to.be.false()
227262
expect(ruleResult.conditions.any[2].all[1].factResult).to.equal(GENDER)
263+
}
264+
engine.on('failure', function (e, almanac, ruleResult) {
265+
expect(e).to.eql(event)
266+
expect(almanac).to.be.an.instanceof(Almanac)
267+
assertResult(ruleResult)
228268
failureSpy()
229269
})
230270
engine.on('success', successSpy)
231271
engine.addFact('zipCode', ZIP_CODE) // zipCode fails
232272
engine.addFact('gender', GENDER) // gender fails
233-
await engine.run()
273+
274+
const { successResults, failureResults } = await engine.run()
275+
276+
assertResult(failureResults[0])
277+
expect(failureResults).to.have.lengthOf(1)
278+
expect(successResults).to.have.lengthOf(0)
279+
234280
expect(failureSpy.callCount).to.equal(1)
235281
expect(successSpy.callCount).to.equal(0)
236282
})
@@ -262,19 +308,29 @@ describe('Engine: event', () => {
262308
const failureSpy = sandbox.spy()
263309
const successSpy = sandbox.spy()
264310
const rule = engine.rules[0]
265-
rule.on('success', function (e, almanac, ruleResult) {
266-
expect(e).to.eql(event)
267-
expect(almanac).to.be.an.instanceof(Almanac)
268-
expect(failureSpy.callCount).to.equal(0)
311+
function assertResult (ruleResult) {
269312
expect(ruleResult.result).to.be.true()
270313
expect(ruleResult.conditions.any[0].result).to.be.true()
271314
expect(ruleResult.conditions.any[0].factResult).to.equal(21)
272315
expect(ruleResult.conditions.any[1].result).to.be.false()
273316
expect(ruleResult.conditions.any[1].factResult).to.equal(false)
317+
}
318+
319+
rule.on('success', function (e, almanac, ruleResult) {
320+
expect(e).to.eql(event)
321+
expect(almanac).to.be.an.instanceof(Almanac)
322+
expect(failureSpy.callCount).to.equal(0)
323+
assertResult(ruleResult)
274324
successSpy()
275325
})
276326
rule.on('failure', failureSpy)
277-
await engine.run()
327+
328+
const { successResults, failureResults } = await engine.run()
329+
330+
assertResult(successResults[0])
331+
expect(failureResults).to.have.lengthOf(0)
332+
expect(successResults).to.have.lengthOf(1)
333+
278334
expect(successSpy.callCount).to.equal(1)
279335
expect(failureSpy.callCount).to.equal(0)
280336
})
@@ -284,21 +340,28 @@ describe('Engine: event', () => {
284340
const successSpy = sandbox.spy()
285341
const failureSpy = sandbox.spy()
286342
const rule = engine.rules[0]
287-
rule.on('failure', function (e, almanac, ruleResult) {
288-
expect(e).to.eql(event)
289-
expect(almanac).to.be.an.instanceof(Almanac)
290-
expect(successSpy.callCount).to.equal(0)
343+
function assertResult (ruleResult) {
291344
expect(ruleResult.result).to.be.false()
292345
expect(ruleResult.conditions.any[0].result).to.be.false()
293346
expect(ruleResult.conditions.any[0].factResult).to.equal(AGE)
294347
expect(ruleResult.conditions.any[1].result).to.be.false()
295348
expect(ruleResult.conditions.any[1].factResult).to.equal(false)
349+
}
350+
rule.on('failure', function (e, almanac, ruleResult) {
351+
expect(e).to.eql(event)
352+
expect(almanac).to.be.an.instanceof(Almanac)
353+
expect(successSpy.callCount).to.equal(0)
354+
assertResult(ruleResult)
296355
failureSpy()
297356
})
298357
rule.on('success', successSpy)
299358
// both conditions will fail
300359
engine.addFact('age', AGE)
301-
await engine.run()
360+
const { successResults, failureResults } = await engine.run()
361+
362+
assertResult(failureResults[0])
363+
expect(failureResults).to.have.lengthOf(1)
364+
expect(successResults).to.have.lengthOf(0)
302365
expect(failureSpy.callCount).to.equal(1)
303366
expect(successSpy.callCount).to.equal(0)
304367
})

0 commit comments

Comments
 (0)