blog.bouzuya.net

2014-06-25 hubot-mock-adapter をためした

hubot-scripts hello をつくった。その過程で hubot-mock-adapter をためした。

hello.js は faithcreates/hubot-scripts にあったもの。faithcreates/sushi へ移植している。

今回の移植では、次のことに対応した。

  • JavaScript -> CoffeeScript
  • テストの追加

前者は簡単だ。もともとほとんど行数がないので、一瞬で終わる。問題は後者のテストである。

スクリプトに渡される robot を mock すればいいのだけど、そうすると正しく動くのかが怪しくなるので、hubot-mock-adapter で adapter を mock する。

# hubot スクリプトの例
module.exports = (robot) ->
  robot.respond /hello$/i, (res) ->
    res.send 'hello'

hubot-url について書いた時にもふれたのだけど、hubot-mock-adapter は機能が弱い。hubot-mock-adapter の README を見ればわかるが、やりたいことの割に記述が冗長すぎる。なので、Hubot 自体と hubot-mock-adapter を扱うための robot-driver をつくった。

まだ汎用性が低いためイマイチだが、汎用化できれば npm に公開したい。

{Promise} = require 'q'
{Robot, TextMessage} = require 'hubot'

class RobotDriver
  TIMEOUT = 100 # ms

  constructor: ->
    @robot = null # initialize in start()
    @adapter = null # initialize in start()
    @started = false
    @users = []

  start: (options) ->
    throw new Error('already started') if @started
    @started = true
    new Promise (resolve, reject) =>
      @robot = new Robot(null, 'mock-adapter', false, 'sushi')
      @robot.adapter.on 'connected', =>
        @adapter = @robot.adapter
        (options.scripts or []).forEach (script) =>
          script @robot
        (options.users or []).forEach (user) =>
          @users.push @robot.brain.userForId(user.id, {
            name: user.name,
            room: user.room
          })
        resolve()
      @robot.run()

  stop: ->
    throw new Error('call receiveMessage() after start()') unless @started
    @robot.shutdown()
    Promise.resolve()

  receiveMessage: (envelope, message) ->
    throw new Error('call receiveMessage() after start()') unless @started
    promise = new Promise (resolve, reject) =>
      setTimeout ->
        reject new Error('timeout')
      , TIMEOUT
      @adapter.on 'send', (envelope, strings) ->
        resolve
          name: 'send'
          envelope: envelope
          strings: strings
    @adapter.receive new TextMessage(envelope, message)
    promise

module.exports = RobotDriver