OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-social / hyper-daemon / test / triggers.test.js

229 lines · javascript ref:
import test from 'brittle'
import { TriggerEngine } from '../lib/triggers.js'

test('TriggerEngine - register and fire', async (t) => {
  const engine = new TriggerEngine()
  let called = false

  engine.register('test1', {
    type: 'message',
    action: async (data) => {
      called = true
      return data.text
    }
  })

  const results = await engine.fire('message', { text: 'hello' })
  t.ok(called)
  t.is(results.length, 1)
  t.is(results[0].id, 'test1')
  t.ok(results[0].ok)
  t.is(results[0].result, 'hello')

  engine.destroy()
})

test('TriggerEngine - no match for different event type', async (t) => {
  const engine = new TriggerEngine()

  engine.register('msg-handler', {
    type: 'message',
    action: async () => 'handled'
  })

  const results = await engine.fire('file-change', {})
  t.is(results.length, 0)

  engine.destroy()
})

test('TriggerEngine - filter by channel', async (t) => {
  const engine = new TriggerEngine()

  engine.register('chan-filter', {
    type: 'message',
    filter: { channel: '#important' },
    action: async () => 'matched'
  })

  const r1 = await engine.fire('message', { channel: '#important', text: 'hi' })
  t.is(r1.length, 1)

  const r2 = await engine.fire('message', { channel: '#general', text: 'hi' })
  t.is(r2.length, 0)

  engine.destroy()
})

test('TriggerEngine - filter by contains', async (t) => {
  const engine = new TriggerEngine()

  engine.register('keyword', {
    type: 'message',
    filter: { contains: '#public' },
    action: async () => 'matched'
  })

  const r1 = await engine.fire('message', { text: 'Hey everyone #public' })
  t.is(r1.length, 1)

  const r2 = await engine.fire('message', { text: 'private message' })
  t.is(r2.length, 0)

  engine.destroy()
})

test('TriggerEngine - filter by pattern', async (t) => {
  const engine = new TriggerEngine()

  engine.register('url-detect', {
    type: 'message',
    filter: { pattern: 'https?://\\S+' },
    action: async () => 'url found'
  })

  const r1 = await engine.fire('message', { text: 'Check https://example.com' })
  t.is(r1.length, 1)

  const r2 = await engine.fire('message', { text: 'no urls here' })
  t.is(r2.length, 0)

  engine.destroy()
})

test('TriggerEngine - filter by author', async (t) => {
  const engine = new TriggerEngine()

  engine.register('alice-only', {
    type: 'message',
    filter: { author: 'alice' },
    action: async () => 'from alice'
  })

  const r1 = await engine.fire('message', { author: 'alice', text: 'hi' })
  t.is(r1.length, 1)

  const r2 = await engine.fire('message', { author: 'bob', text: 'hi' })
  t.is(r2.length, 0)

  engine.destroy()
})

test('TriggerEngine - filter by tags', async (t) => {
  const engine = new TriggerEngine()

  engine.register('tagged', {
    type: 'message',
    filter: { tags: ['urgent', 'critical'] },
    action: async () => 'tagged'
  })

  const r1 = await engine.fire('message', { text: 'This is URGENT please help' })
  t.is(r1.length, 1)

  const r2 = await engine.fire('message', { text: 'normal message' })
  t.is(r2.length, 0)

  engine.destroy()
})

test('TriggerEngine - enable/disable', async (t) => {
  const engine = new TriggerEngine()

  engine.register('toggle', {
    type: 'message',
    action: async () => 'fired'
  })

  engine.disable('toggle')
  const r1 = await engine.fire('message', { text: 'hi' })
  t.is(r1.length, 0)

  engine.enable('toggle')
  const r2 = await engine.fire('message', { text: 'hi' })
  t.is(r2.length, 1)

  engine.destroy()
})

test('TriggerEngine - unregister', async (t) => {
  const engine = new TriggerEngine()

  engine.register('temp', {
    type: 'message',
    action: async () => 'fired'
  })

  t.is(engine.count, 1)
  engine.unregister('temp')
  t.is(engine.count, 0)

  const results = await engine.fire('message', {})
  t.is(results.length, 0)

  engine.destroy()
})

test('TriggerEngine - list triggers', (t) => {
  const engine = new TriggerEngine()

  engine.register('a', { type: 'message', action: async () => {} })
  engine.register('b', { type: 'file-change', filter: { channel: 'x' }, action: async () => {} })

  const list = engine.list()
  t.is(list.length, 2)
  t.ok(list.find(t => t.id === 'a' && t.type === 'message'))
  t.ok(list.find(t => t.id === 'b' && t.hasFilter === true))

  engine.destroy()
})

test('TriggerEngine - multiple triggers fire in order', async (t) => {
  const engine = new TriggerEngine()
  const order = []

  engine.register('first', {
    type: 'message',
    action: async () => { order.push('first') }
  })
  engine.register('second', {
    type: 'message',
    action: async () => { order.push('second') }
  })

  await engine.fire('message', {})
  t.alike(order, ['first', 'second'])

  engine.destroy()
})

test('TriggerEngine - action error captured', async (t) => {
  const engine = new TriggerEngine()

  engine.register('broken', {
    type: 'message',
    action: async () => { throw new Error('boom') }
  })

  const results = await engine.fire('message', {})
  t.is(results.length, 1)
  t.absent(results[0].ok)
  t.is(results[0].error, 'boom')

  engine.destroy()
})

test('TriggerEngine - schedule trigger', async (t) => {
  const engine = new TriggerEngine()
  let count = 0

  engine.startSchedule('ticker', 30, async () => {
    count++
  })

  await new Promise(r => setTimeout(r, 80))
  t.ok(count >= 2, `scheduled ran ${count} times`)

  engine.destroy()
})