Skip to content

Commit

Permalink
fix(thought): Processing uses initial path state every time
Browse files Browse the repository at this point in the history
There was a bug when clearing unforced branches in one thought process could leave the next process
without any branches to process. Now processing uses copy of path, instead of passing by reference,
to keep each process isolated.
  • Loading branch information
timkinnane committed Sep 23, 2018
1 parent 6a87737 commit bbbe4ef
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 9 deletions.
8 changes: 8 additions & 0 deletions src/lib/path.spec.ts
Expand Up @@ -49,6 +49,14 @@ describe('[path]', () => {
const id = path.customNLU(() => null, () => null)
expect(path.understand[id]).to.be.instanceof(bot.CustomBranch)
})
it('.process calls callback on matching message', async () => {
const path = new bot.Path()
const callback = sinon.spy()
const message = new bot.TextMessage(user, 'testing custom NLU')
const id = path.customNLU(() => true, callback, { id: 'test-custom-nlu' })
await path.understand[id].process(new bot.State({ message }), middleware)
sinon.assert.calledOnce(callback)
})
})
describe('.enter', () => {
it('.process calls callback on enter messages', async () => {
Expand Down
6 changes: 3 additions & 3 deletions src/lib/path.ts
Expand Up @@ -17,9 +17,9 @@ export class Path implements IPath {

constructor (init: Path | IPath = {}) {
this.scope = (init.scope) ? init.scope : 'global'
this.listen = (init.listen) ? init.listen : {}
this.understand = (init.understand) ? init.understand : {}
this.act = (init.act) ? init.act : {}
this.listen = (init.listen) ? Object.assign({}, init.listen) : {}
this.understand = (init.understand) ? Object.assign({}, init.understand) : {}
this.act = (init.act) ? Object.assign({}, init.act) : {}
}

/** Remove all but forced branches from collection, return remaining size. */
Expand Down
24 changes: 21 additions & 3 deletions src/lib/thought.spec.ts
Expand Up @@ -541,7 +541,7 @@ describe('[thought]', () => {
])
sinon.assert.calledOnce((bot.adapters.storage!.keep as sinon.SinonStub))
})
describe('receive', () => {
describe('.receive', () => {
it('timestamps all actioned processes', async () => {
bot.global.custom(() => true, (b) => b.respond('ping'))
const now = Date.now()
Expand All @@ -557,8 +557,26 @@ describe('[thought]', () => {
expect(b.sequence).to.equal('receive')
expect(b.scope).to.equal('global')
})
it('consecutive calls isolate thought and path', async () => {
const listenCallback = sinon.spy()
const understandCallback = sinon.spy()
bot.global.text(/foo/i, listenCallback, {
id: 'receive-text'
})
bot.global.customNLU(() => true, understandCallback, {
id: 'receive-custom-nlu'
})
bot.settings.set('nlu-min-length', 2)
const messageA = new bot.TextMessage(new bot.User(), 'foo')
const messageB = new bot.TextMessage(new bot.User(), 'bar')
await bot.receive(messageA)
await bot.receive(messageB)
sinon.assert.calledOnce(listenCallback)
sinon.assert.calledOnce(understandCallback)
bot.settings.unset('nlu-min-length')
})
})
describe('respond', () => {
describe('.respond', () => {
it('timestamps all actioned processes', async () => {
const b = new bot.State({ message })
b.respondEnvelope().write('ping')
Expand All @@ -572,7 +590,7 @@ describe('[thought]', () => {
expect(b.scope).to.equal('global')
})
})
describe('dispatch', () => {
describe('.dispatch', () => {
it('timestamps all actioned processes', async () => {
const now = Date.now()
const envelope = new bot.Envelope({ user: new bot.User() }).write('hello')
Expand Down
11 changes: 8 additions & 3 deletions src/lib/thought.ts
Expand Up @@ -125,7 +125,7 @@ export class Thoughts {
path?: bot.Path
) {
this.b = state
this.path = (path) ? path : bot.global
this.path = (path) ? new bot.Path(path) : new bot.Path(bot.global)
const { b } = this

// Define processes with validation and post processing actions
Expand Down Expand Up @@ -238,8 +238,13 @@ export class Thoughts {
* Branch callbacks may also respond. Final state is remembered.
*/
export async function receive (message: bot.Message, path?: bot.Path) {
bot.logger.info(`[thought] receive message ID ${message.id}`)
return new Thoughts(new bot.State({ message }), path).start('receive')
const thought = new Thoughts(new bot.State({ message }), path)
bot.logger.info(`[thought] receive message ID ${message.id} against ${
Object.keys(thought.path.listen).length + ' listen branches, ' +
Object.keys(thought.path.understand).length + ' understand branches, ' +
Object.keys(thought.path.act).length + ' act branches'
}`)
return thought.start('receive')
}

/**
Expand Down

0 comments on commit bbbe4ef

Please sign in to comment.