예제 #1
0
class ContextTests(unittest.TestCase):
    def setUp(self):
        self.context = Context()

    def tearDown(self):
        del self.context
        collected = gc.collect()
        if collected:
            logging.info("Garbage collector: collected %d objects." %
                         (collected))

    def test_init(self):

        settings = {
            'bot': {
                'name': 'testy',
                'version': '17.4.1'
            },
        }

        self.context = Context(settings)
        self.assertEqual(self.context.get('bot.name'), 'testy')
        self.assertEqual(self.context.get('bot.version'), '17.4.1')

    def test_init_filter(self):

        self.context = Context(filter=lambda x, y: x + '...')
        self.context.apply({'my.var': 'my value'})
        self.context.check('my.var', filter=True)
        self.assertEqual(self.context.get('my.var'), 'my value...')

    def test_apply(self):

        self.assertEqual(self.context.get('port'), None)

        settings = {
            'spark': {
                'CISCO_SPARK_BTTN_BOT': 'who_knows'
            },
            'spark.room': 'title',
            'DEBUG': True,
            'server': {
                'port': 80,
                'url': 'http://www.acme.com/'
            },
            'bot.store': {
                'planets': ['Uranus', 'Mercury']
            },
        }

        self.context.apply(settings)

        self.assertEqual(self.context.get('DEBUG'), True)
        self.assertEqual(self.context.get('spark.CISCO_SPARK_BTTN_BOT'),
                         'who_knows')
        self.assertEqual(self.context.get('spark.room'), 'title')
        self.assertEqual(self.context.get('server.port'), 80)
        self.assertEqual(self.context.get('server.url'),
                         'http://www.acme.com/')
        self.assertEqual(self.context.get('bot.store.planets'),
                         ['Uranus', 'Mercury'])
        self.assertEqual(self.context.get('bot.store'),
                         {'planets': ['Uranus', 'Mercury']})

    def test_clear(self):

        self.assertEqual(self.context.get('port'), None)

        settings = {
            'spark': {
                'CISCO_SPARK_BTTN_BOT': 'who_knows'
            },
            'spark.room': 'title',
            'DEBUG': True,
            'server': {
                'port': 80,
                'url': 'http://www.acme.com/'
            },
        }

        self.context.apply(settings)

        self.assertEqual(self.context.get('DEBUG'), True)
        self.assertEqual(self.context.get('spark.CISCO_SPARK_BTTN_BOT'),
                         'who_knows')
        self.assertEqual(self.context.get('spark.room'), 'title')
        self.assertEqual(self.context.get('server.port'), 80)
        self.assertEqual(self.context.get('server.url'),
                         'http://www.acme.com/')

        self.context.clear()

        self.assertEqual(self.context.get('DEBUG'), None)
        self.assertEqual(self.context.get('spark.CISCO_SPARK_BTTN_BOT'), None)
        self.assertEqual(self.context.get('spark.room'), None)
        self.assertEqual(self.context.get('server.port'), None)
        self.assertEqual(self.context.get('server.url'), None)

    def test_is_empty(self):

        self.assertTrue(self.context.is_empty)

        # set a key
        self.context.set('hello', 'world')
        self.assertEqual(self.context.get('hello'), 'world')
        self.assertFalse(self.context.is_empty)

        self.context.clear()
        self.assertTrue(self.context.is_empty)

        settings = {
            'spark': {
                'CISCO_SPARK_BTTN_BOT': 'who_knows'
            },
            'spark.room': 'title',
            'DEBUG': True,
            'server': {
                'port': 80,
                'url': 'http://www.acme.com/'
            },
        }

        self.context.apply(settings)
        self.assertFalse(self.context.is_empty)

    def test_check(self):

        self.assertEqual(self.context.get('spark.room'), None)

        settings = {
            'spark': {
                'room': 'My preferred channel',
                'participants':
                ['*****@*****.**', '*****@*****.**'],
                'team': 'Anchor team',
                'token': 'hkNWEtMJNkODk3ZDZLOGQ0OVGlZWU1NmYtyY',
                'weird_token': '$MY_FUZZY_SPARK_TOKEN',
                'fuzzy_token': '$MY_FUZZY_SPARK_TOKEN',
                'webhook': "http://73a1e282.ngrok.io",
            }
        }

        self.context.apply(settings)

        self.context.check('spark.room', is_mandatory=True)
        self.assertEqual(self.context.get('spark.room'),
                         'My preferred channel')

        self.context.check('spark.team')
        self.assertEqual(self.context.get('spark.team'), 'Anchor team')

        self.context.check('spark.*not*present')  # will be set to None
        self.assertEqual(self.context.get('spark.*not*present'), None)

        self.context.check('spark.absent_list', default=[])
        self.assertEqual(self.context.get('spark.absent_list'), [])

        self.context.check('spark.absent_dict', default={})
        self.assertEqual(self.context.get('spark.absent_dict'), {})

        self.context.check('spark.absent_text', default='*born')
        self.assertEqual(self.context.get('spark.absent_text'), '*born')

        # is_mandatory is useless if default is set
        self.context.check('spark.*not*present',
                           default='*born',
                           is_mandatory=True)
        self.assertEqual(self.context.get('spark.*not*present'), '*born')

        # missing key
        self.assertEqual(self.context.get('spark.*unknown*key*'), None)

        # we need the missing key
        with self.assertRaises(KeyError):
            self.context.check('spark.*unknown*key*', is_mandatory=True)

        # validate implies is_mandatory
        with self.assertRaises(KeyError):
            self.context.check('spark.*unknown*key*',
                               validate=lambda line: True)

        # exception when is_mandatory is explicit
        with self.assertRaises(KeyError):
            self.context.check('spark.*unknown*key*',
                               is_mandatory=True,
                               filter=True)

        # yet filter does not imply is_mandatory by itself
        self.context.check('spark.*unknown*key*',
                           filter=True)  # warning in log

        # a web link has been set
        self.assertEqual(self.context.get('spark.webhook'),
                         "http://73a1e282.ngrok.io")

        # validate http
        self.context.check('spark.webhook',
                           validate=lambda line: line.startswith('http'))

        # validate https
        with self.assertRaises(ValueError):
            self.context.check('spark.webhook',
                               validate=lambda line: line.startswith('https'))

        # a token has been set
        self.assertEqual(self.context.get('spark.token'),
                         'hkNWEtMJNkODk3ZDZLOGQ0OVGlZWU1NmYtyY')

        # validate length of token
        with self.assertRaises(ValueError):
            self.context.check('spark.token',
                               validate=lambda line: len(line) == 32)

        # we rely on the environment for this key
        self.assertEqual(self.context.get('spark.weird_token'),
                         '$MY_FUZZY_SPARK_TOKEN')

        # no change to the value
        self.context.check('spark.weird_token')

        # lookup the environment and change the value to None
        self.context.check('spark.weird_token', filter=True)  # warning in log
        self.assertEqual(self.context.get('spark.weird_token'), None)

        # ensure the environment is clean
        def clear_env(name):
            try:
                os.environ.pop(name)
            except:
                pass

        clear_env('MY_FUZZY_SPARK_TOKEN')

        # a value based on the environment
        self.context.set('spark.fuzzy_token', '$MY_FUZZY_SPARK_TOKEN')
        self.context.check('spark.fuzzy_token')
        self.assertEqual(self.context.get('spark.fuzzy_token'),
                         '$MY_FUZZY_SPARK_TOKEN')

        # default has no effect, mandatory is ok
        self.context.set('spark.fuzzy_token', '$MY_FUZZY_SPARK_TOKEN')
        self.context.check('spark.fuzzy_token', default='hello there')
        self.context.check('spark.fuzzy_token', is_mandatory=True)
        self.assertEqual(self.context.get('spark.fuzzy_token'),
                         '$MY_FUZZY_SPARK_TOKEN')

        # default value is used if key is absent from the environment
        self.context.set('spark.fuzzy_token', '$MY_FUZZY_SPARK_TOKEN')
        self.context.check('spark.fuzzy_token',
                           default='hello there',
                           filter=True)
        self.assertEqual(self.context.get('spark.fuzzy_token'), 'hello there')

        # is_mandatory is useless in that case
        self.context.set('spark.fuzzy_token', '$MY_FUZZY_SPARK_TOKEN')
        self.context.check('spark.fuzzy_token', is_mandatory=True, filter=True)
        self.assertEqual(self.context.get('spark.fuzzy_token'), None)

        # set the value to ''
        self.context.set('spark.fuzzy_token', '$MY_FUZZY_SPARK_TOKEN')
        os.environ['MY_FUZZY_SPARK_TOKEN'] = ''
        self.context.check('spark.fuzzy_token', filter=True)
        self.assertEqual(self.context.get('spark.fuzzy_token'), '')

        # set the value to '' -- default value is useless in that case
        self.context.set('spark.fuzzy_token', '$MY_FUZZY_SPARK_TOKEN')
        os.environ['MY_FUZZY_SPARK_TOKEN'] = ''
        self.context.check('spark.fuzzy_token', default='ok?', filter=True)
        self.assertEqual(self.context.get('spark.fuzzy_token'), '')

        # set the value to 'hello'
        self.context.set('spark.fuzzy_token', '$MY_FUZZY_SPARK_TOKEN')
        os.environ['MY_FUZZY_SPARK_TOKEN'] = 'hello'
        self.context.check('spark.fuzzy_token', filter=True)
        self.assertEqual(self.context.get('spark.fuzzy_token'), 'hello')

        # set the value to 'hello' -- default value is useless in that case
        self.context.set('spark.fuzzy_token', '$MY_FUZZY_SPARK_TOKEN')
        os.environ['MY_FUZZY_SPARK_TOKEN'] = 'hello again'
        self.context.check('spark.fuzzy_token', default='ok?', filter=True)
        self.assertEqual(self.context.get('spark.fuzzy_token'), 'hello again')

        # pass the variable name as default value
        self.context.set('spark.fuzzy_token', None)
        os.environ['MY_FUZZY_SPARK_TOKEN'] = 'hello'
        self.context.check('spark.fuzzy_token',
                           default='$MY_FUZZY_SPARK_TOKEN',
                           filter=True)
        self.assertEqual(self.context.get('spark.fuzzy_token'), 'hello')

        # pass the variable name as default value -- no effect
        self.context.set('spark.fuzzy_token', '$MY_FUZZY_SPARK_TOKEN')
        os.environ['MY_FUZZY_SPARK_TOKEN'] = 'hello'
        self.context.check('spark.fuzzy_token',
                           default='$MY_FUZZY_SPARK_TOKEN',
                           filter=True)
        self.assertEqual(self.context.get('spark.fuzzy_token'), 'hello')

        # pass as default the name of an empty variable -- tricky case
        self.context.set('spark.fuzzy_token', '$MY_FUZZY_SPARK_TOKEN')
        clear_env('MY_FUZZY_SPARK_TOKEN')
        self.context.check('spark.fuzzy_token',
                           default='$MY_FUZZY_SPARK_TOKEN',
                           filter=True)
        self.assertEqual(self.context.get('spark.fuzzy_token'), None)

    def test__filter(self):

        self.assertEqual(Context._filter(None), None)

        self.assertEqual(Context._filter(''), '')

        self.assertEqual(Context._filter('ZLOGQ0OVGlZWU1NmYtyY'),
                         'ZLOGQ0OVGlZWU1NmYtyY')

        if os.environ.get('PATH') is not None:
            self.assertTrue(Context._filter('$PATH') != '$PATH')

        Context._filter('$TOTALLY*UNKNOWN*HERE')  # warning in log

    def test_has(self):

        self.context.apply({
            'spark': {
                'room': 'My preferred channel',
                'participants':
                ['*****@*****.**', '*****@*****.**'],
                'team': 'Anchor team',
                'token': 'hkNWEtMJNkODk3ZDZLOGQ0OVGlZWU1NmYtyY',
                'fuzzy_token': '$MY_FUZZY_SPARK_TOKEN',
                'webhook': "http://73a1e282.ngrok.io",
            }
        })

        # undefined prefix
        self.assertFalse(self.context.has('hello'))

        # top-level prefix
        self.assertTrue(self.context.has('spark'))

        # 2-level prefix
        self.assertTrue(self.context.has('spark.team'))

        # undefined 2-level prefix
        self.assertFalse(self.context.has('.token'))

    def test_getter(self):

        # undefined key
        self.assertEqual(self.context.get('hello'), None)

        # undefined key with default value
        whatever = 'whatever'
        self.assertEqual(self.context.get('hello', whatever), whatever)

        # set a key
        self.context.set('hello', 'world')
        self.assertEqual(self.context.get('hello'), 'world')

        # default value is meaningless when key has been set
        self.assertEqual(self.context.get('hello', 'whatever'), 'world')

        # except when set to None
        self.context.set('special', None)
        self.assertEqual(self.context.get('special', []), [])

    def test_unicode(self):

        self.context.set('hello', 'world')
        self.assertEqual(self.context.get('hello'), 'world')
        self.assertEqual(self.context.get(u'hello'), 'world')

        self.context.set('hello', u'wôrld')
        self.assertEqual(self.context.get('hello'), u'wôrld')

        self.context.set(u'hello', u'wôrld')
        self.assertEqual(self.context.get(u'hello'), u'wôrld')

    def test_increment(self):

        self.assertEqual(self.context.get('gauge'), None)
        value = self.context.increment('gauge')
        self.assertEqual(value, 1)

        self.context.set('gauge', 'world')
        self.assertEqual(self.context.get('gauge'), 'world')
        value = self.context.increment('gauge')
        self.assertEqual(value, 1)

    def test_decrement(self):

        self.assertEqual(self.context.get('gauge'), None)
        value = self.context.decrement('gauge')
        self.assertEqual(value, -1)

        self.context.set('gauge', 'world')
        self.assertEqual(self.context.get('gauge'), 'world')
        value = self.context.decrement('gauge')
        self.assertEqual(value, -1)

    def test_gauge(self):

        # undefined key
        self.assertEqual(self.context.get('gauge'), None)

        # see if type mismatch would create an error
        self.context.set('gauge', 'world')
        self.assertEqual(self.context.get('gauge'), 'world')

        # increment and decrement the counter
        value = self.context.increment('gauge')
        self.assertEqual(value, 1)
        self.assertEqual(self.context.get('gauge'), 1)

        self.assertEqual(self.context.decrement('gauge', 2), -1)

        self.assertEqual(self.context.increment('gauge', 4), 3)
        self.assertEqual(self.context.decrement('gauge', 10), -7)
        self.assertEqual(self.context.increment('gauge', 27), 20)
        self.assertEqual(self.context.get('gauge'), 20)

        # default value is meaningless when key has been set
        self.assertEqual(self.context.get('gauge', 'world'), 20)

        # reset the gauge
        self.context.set('gauge', 123)
        self.assertEqual(self.context.get('gauge'), 123)

    def test_concurrency(self):
        def worker(id, context):
            for i in range(4):
                r = random.random()
                time.sleep(r)
                value = context.increment('gauge')
                logging.info('worker %d:counter=%d' % (id, value))
            logging.info('worker %d:done' % id)

        logging.info('Creating a counter')
        self.counter = Context()

        logging.info('Launching incrementing workers')
        workers = []
        for i in range(4):
            p = Process(target=worker, args=(
                i,
                self.counter,
            ))
            p.start()
            workers.append(p)

        logging.info('Waiting for worker threads')
        for p in workers:
            p.join()

        logging.info('Counter: %d' % self.counter.get('gauge'))
        self.assertEqual(self.counter.get('gauge'), 16)
예제 #2
0
class EngineTests(unittest.TestCase):
    def setUp(self):
        self.context = Context()
        self.engine = Engine(context=self.context, mouth=Queue())
        self.space = LocalSpace(context=self.context)
        self.engine.space = self.space

    def tearDown(self):
        del self.space
        del self.engine
        del self.context
        collected = gc.collect()
        if collected:
            logging.info("Garbage collector: collected %d objects." %
                         (collected))

    def test_init(self):

        logging.info('*** init ***')

        engine = Engine(context=self.context)

        self.assertEqual(engine.context, self.context)
        self.assertTrue(engine.mouth is None)
        self.assertTrue(engine.speaker is not None)
        self.assertTrue(engine.ears is None)
        self.assertTrue(engine.listener is not None)
        self.assertTrue(engine.fan is None)
        self.assertTrue(engine.observer is not None)
        self.assertTrue(engine.registered is not None)
        self.assertEqual(engine.bots, {})
        self.assertFalse(engine.space is None)
        self.assertTrue(engine.server is None)
        self.assertTrue(engine.shell is not None)
        self.assertEqual(engine.driver, ShellBot)
        self.assertEqual(engine.machine_factory, None)
        self.assertEqual(engine.updater_factory, None)

        del engine

        engine = Engine(context=self.context,
                        type='local',
                        mouth='m',
                        ears='e',
                        fan='f')

        self.assertEqual(engine.context, self.context)
        self.assertEqual(engine.mouth, 'm')
        self.assertTrue(engine.speaker is not None)
        self.assertEqual(engine.ears, 'e')
        self.assertTrue(engine.listener is not None)
        self.assertEqual(engine.fan, 'f')
        self.assertTrue(engine.observer is not None)
        self.assertTrue(engine.registered is not None)
        self.assertEqual(engine.bots, {})
        self.assertTrue(engine.space is not None)
        self.assertTrue(engine.server is None)
        self.assertTrue(engine.shell is not None)
        self.assertEqual(engine.driver, ShellBot)
        self.assertEqual(engine.machine_factory, None)
        self.assertEqual(engine.updater_factory, None)

        del engine

        engine = Engine(context=self.context,
                        space=self.space,
                        mouth='m',
                        ears='e',
                        fan='f')

        self.assertEqual(engine.context, self.context)
        self.assertEqual(engine.mouth, 'm')
        self.assertTrue(engine.speaker is not None)
        self.assertEqual(engine.ears, 'e')
        self.assertTrue(engine.listener is not None)
        self.assertEqual(engine.fan, 'f')
        self.assertTrue(engine.observer is not None)
        self.assertTrue(engine.registered is not None)
        self.assertEqual(engine.bots, {})
        self.assertEqual(engine.space, self.space)
        self.assertTrue(engine.server is None)
        self.assertTrue(engine.shell is not None)
        self.assertEqual(engine.driver, ShellBot)
        self.assertEqual(engine.machine_factory, None)
        self.assertEqual(engine.updater_factory, None)

        del engine

        engine = Engine(
            context=self.context,
            driver=FakeBot,
            machine_factory=MachineFactory,
            updater_factory=MyUpdaterFactory,
        )

        self.assertEqual(engine.context, self.context)
        self.assertEqual(engine.mouth, None)
        self.assertTrue(engine.speaker is not None)
        self.assertEqual(engine.ears, None)
        self.assertTrue(engine.listener is not None)
        self.assertTrue(engine.fan is None)
        self.assertTrue(engine.observer is not None)
        self.assertTrue(engine.registered is not None)
        self.assertEqual(engine.bots, {})
        self.assertTrue(engine.space is not None)
        self.assertTrue(engine.server is None)
        self.assertTrue(engine.shell is not None)
        self.assertEqual(engine.driver, FakeBot)
        self.assertEqual(engine.machine_factory, MachineFactory)
        self.assertEqual(engine.updater_factory, MyUpdaterFactory)

        self.context.apply({
            'bot': {
                'name': 'testy',
                'version': '17.4.1'
            },
        })
        engine = Engine(context=self.context)
        self.assertEqual(engine.name, 'testy')
        self.assertEqual(engine.version, '17.4.1')

        del engine

    def test_configure(self):

        logging.info('*** configure ***')

        self.engine.configure({})
        self.assertEqual(self.engine.space.ears, self.engine.ears)
        self.assertTrue(self.engine.list_factory is not None)

        self.engine.context.clear()
        settings = {
            'bot': {
                'on_enter': 'Hello!',
                'on_exit': 'Bye!',
            },
            'localized': {
                'hello world': "What'up, Doc?",
                'another string': 'Bye!',
            },
            'space': {
                'title': 'space name',
                'participants': ['*****@*****.**'],
            },
            'server': {
                'url': 'http://to.no.where',
                'hook': '/hook',
                'binding': '0.0.0.0',
                'port': 8080,
            },
        }
        self.engine.configure(settings)
        self.assertEqual(self.engine.get('bot.on_enter'), 'Hello!')
        self.assertEqual(self.engine.get('bot.on_exit'), 'Bye!')
        self.assertEqual(self.engine.get('space.title'), 'space name')
        self.assertEqual(self.engine.get('space.participants'),
                         ['*****@*****.**'])
        self.assertEqual(self.engine.get('server.url'), 'http://to.no.where')
        self.assertEqual(self.engine.get('server.hook'), '/hook')

        self.assertEqual(_('hello world'), "What'up, Doc?")
        self.assertEqual(_('not localized'), 'not localized')

        self.engine.context.clear()
        self.engine.configure_from_path(
            os.path.join(os.path.dirname(os.path.abspath(__file__)),
                         'test_settings', 'regular.yaml'))
        self.assertEqual(self.engine.get('bot.on_enter'),
                         'How can I help you?')
        self.assertEqual(self.engine.get('bot.on_exit'), 'Bye for now')
        self.assertEqual(self.engine.get('space.title'), 'Support channel')
        self.assertEqual(self.engine.get('space.participants'),
                         ['*****@*****.**', '*****@*****.**'])
        self.assertEqual(self.engine.get('server.url'), None)
        self.assertEqual(self.engine.get('server.hook'), None)
        self.assertEqual(self.engine.get('server.binding'), None)
        self.assertEqual(self.engine.get('server.port'), None)

        items = [x for x in self.engine.list_factory.get_list('SupportTeam')]
        self.assertEqual(items,
                         ['*****@*****.**', '*****@*****.**'])

        items = [x for x in self.engine.list_factory.get_list('*unknown*list')]
        self.assertEqual(items, [])

        names = self.engine.list_factory.list_commands()
        self.assertEqual(sorted(names), ['SupportTeam'])

    def test_configuration_2(self):

        logging.info('*** configure 2 ***')

        settings = {
            'bot': {
                'on_enter': 'Hello!',
                'on_exit': 'Bye!',
            },
            'space': {
                'title': 'Support channel',
            },
            'server': {
                'url': 'http://to.nowhere/',
                'trigger': '/trigger',
                'hook': '/hook',
                'binding': '0.0.0.0',
                'port': 8080,
            },
        }

        clear_env('CHANNEL_DEFAULT_PARTICIPANTS')
        context = Context(settings)
        engine = Engine(context=context, configure=True)
        self.assertEqual(engine.get('bot.on_enter'), 'Hello!')
        self.assertEqual(engine.get('bot.on_exit'), 'Bye!')
        self.assertEqual(engine.get('space.title'), 'Support channel')
        self.assertEqual(engine.get('space.participants'), None)
        self.assertEqual(engine.get('server.url'), 'http://to.nowhere/')
        self.assertEqual(engine.get('server.hook'), '/hook')
        self.assertEqual(engine.get('server.trigger'), '/trigger')
        self.assertEqual(engine.get('server.binding'), None)
        self.assertEqual(engine.get('server.port'), 8080)

    def test_configure_default(self):

        logging.info('*** configure with default values ***')

        clear_env("BOT_BANNER_TEXT")
        clear_env("BOT_BANNER_CONTENT")
        clear_env("BOT_BANNER_FILE")
        clear_env("BOT_ON_ENTER")
        clear_env("BOT_ON_EXIT")
        clear_env("CHAT_ROOM_TITLE")
        clear_env("CHANNEL_DEFAULT_PARTICIPANTS")
        clear_env("CISCO_SPARK_BOT_TOKEN")
        clear_env("SERVER_URL")
        self.engine.configure()

        #        logging.debug(self.engine.context.values)

        self.assertEqual(self.engine.get('bot.banner.text'), None)
        self.assertEqual(self.engine.get('bot.banner.content'), None)
        self.assertEqual(self.engine.get('bot.banner.file'), None)

        self.assertEqual(self.engine.get('bot.on_enter'), None)
        self.assertEqual(self.engine.get('bot.on_exit'), None)

        self.assertEqual(self.engine.get('space.title'), 'Collaboration space')
        self.assertEqual(self.engine.get('space.participants'), None)
        self.assertEqual(self.engine.get('space.unknown'), None)

        self.assertEqual(self.engine.get('server.url'), '$SERVER_URL')
        self.assertEqual(self.engine.get('server.hook'), '/hook')
        self.assertEqual(self.engine.get('server.binding'), None)
        self.assertEqual(self.engine.get('server.port'), 8080)

        self.assertEqual(self.engine.space.ears, self.engine.ears)
        self.assertTrue(self.engine.bus is not None)
        self.assertTrue(self.engine.publisher is not None)

    def test_configure_environment(self):

        logging.info('*** configure from the environment ***')

        os.environ["BOT_BANNER_TEXT"] = 'some text'
        os.environ["BOT_BANNER_CONTENT"] = 'some content'
        os.environ["BOT_BANNER_FILE"] = 'some link'
        os.environ["BOT_ON_ENTER"] = 'Hello!'
        os.environ["BOT_ON_EXIT"] = 'Bye!'
        os.environ["CHAT_ROOM_TITLE"] = 'Support channel'
        os.environ["CHANNEL_DEFAULT_PARTICIPANTS"] = '*****@*****.**'
        os.environ["CISCO_SPARK_BOT_TOKEN"] = '*token'
        os.environ["SERVER_URL"] = 'http://to.nowhere/'
        self.engine.configure()

        #        logging.debug(self.engine.context.values)

        self.assertEqual(self.engine.get('bot.banner.text'), 'some text')
        self.assertEqual(self.engine.get('bot.banner.content'), 'some content')
        self.assertEqual(self.engine.get('bot.banner.file'), 'some link')

        self.assertEqual(self.engine.get('bot.on_enter'), 'Hello!')
        self.assertEqual(self.engine.get('bot.on_exit'), 'Bye!')

        self.assertEqual(self.engine.get('space.title'), 'Support channel')
        self.assertEqual(self.engine.get('space.participants'),
                         '*****@*****.**')
        self.assertEqual(self.engine.get('space.unknown'), None)

        self.assertEqual(self.engine.get('server.url'), '$SERVER_URL')
        self.assertEqual(self.engine.get('server.hook'), '/hook')
        self.assertEqual(self.engine.get('server.binding'), None)
        self.assertEqual(self.engine.get('server.port'), 8080)

    def test_get(self):

        logging.info('*** get ***')

        os.environ["BOT_ON_ENTER"] = 'Hello!'
        os.environ["BOT_ON_EXIT"] = 'Bye!'
        os.environ["CHAT_ROOM_TITLE"] = 'Support channel'
        os.environ["CHANNEL_DEFAULT_PARTICIPANTS"] = '*****@*****.**'
        os.environ["CISCO_SPARK_BOT_TOKEN"] = '*token'
        os.environ["SERVER_URL"] = 'http://to.nowhere/'

        settings = {
            'bot': {
                'on_enter': 'Hello!',
                'on_exit': 'Bye!',
            },
            'space': {
                'title': '$CHAT_ROOM_TITLE',
            },
            'server': {
                'url': '$SERVER_URL',
                'hook': '/hook',
                'binding': '0.0.0.0',
                'port': 8080,
            },
        }

        self.engine.configure(settings=settings)

        self.assertEqual(self.engine.get('bot.on_enter'), 'Hello!')
        self.assertEqual(self.engine.get('bot.on_exit'), 'Bye!')
        self.assertEqual(self.engine.get('space.title'), 'Support channel')
        self.assertEqual(self.engine.get('space.participants'),
                         '*****@*****.**')

        self.assertEqual(self.engine.get('space.unknown'), None)

        self.assertEqual(self.engine.get('server.url'), 'http://to.nowhere/')
        self.assertEqual(self.engine.get('server.hook'), '/hook')
        self.assertEqual(self.engine.get('server.binding'), None)
        self.assertEqual(self.engine.get('server.port'), 8080)

    def test_set(self):

        logging.info('*** set ***')

        self.engine.set('hello', 'world')
        self.assertEqual(self.engine.get('hello'), 'world')
        self.assertEqual(self.engine.get(u'hello'), 'world')

        self.engine.set('hello', u'wôrld')
        self.assertEqual(self.engine.get('hello'), u'wôrld')

        self.engine.set(u'hello', u'wôrld')
        self.assertEqual(self.engine.get(u'hello'), u'wôrld')

    def test_name(self):

        logging.info('*** name ***')
        self.assertEqual(self.engine.name, 'Shelly')

    def test_version(self):

        logging.info('*** version ***')
        self.assertEqual(self.engine.version, '*unknown*')

    def test_register(self):

        logging.info('*** register ***')

        with self.assertRaises(AttributeError):
            self.engine.register('*unknown*event', lambda: 'ok')
        with self.assertRaises(AttributeError):
            self.engine.register('bond', lambda: 'ok')
        with self.assertRaises(AttributeError):
            self.engine.register('dispose', lambda: 'ok')

        counter = MyCounter('counter #1')
        with self.assertRaises(AssertionError):
            self.engine.register(None, counter)
        with self.assertRaises(AssertionError):
            self.engine.register('', counter)
        with self.assertRaises(AssertionError):
            self.engine.register(1.2, counter)

        self.engine.register('bond', counter)
        self.engine.register('dispose', counter)

        with self.assertRaises(AttributeError):
            self.engine.register('start', counter)
        with self.assertRaises(AttributeError):
            self.engine.register('stop', counter)
        with self.assertRaises(AttributeError):
            self.engine.register('*unknown*event', counter)

        self.engine.register('bond', MyCounter('counter #2'))

        class AllEvents(object):
            def on_bond(self, bot):
                pass

            def on_dispose(self):
                pass

            def on_start(self):
                pass

            def on_stop(self):
                pass

            def on_message(self):
                pass

            def on_join(self):
                pass

            def on_leave(self):
                pass

            def on_enter(self):
                pass

            def on_exit(self):
                pass

            def on_inbound(self):
                pass

            def on_some_custom_event(self):
                pass

        all_events = AllEvents()
        self.engine.register('bond', all_events)
        self.engine.register('dispose', all_events)
        self.engine.register('start', all_events)
        self.engine.register('stop', all_events)
        self.engine.register('message', all_events)
        self.engine.register('join', all_events)
        self.engine.register('leave', all_events)
        self.engine.register('enter', all_events)
        self.engine.register('exit', all_events)
        self.engine.register('inbound', all_events)
        self.engine.register('some_custom_event', all_events)

        self.assertEqual(len(self.engine.registered['bond']), 3)
        self.assertEqual(len(self.engine.registered['dispose']), 2)
        self.assertEqual(len(self.engine.registered['start']), 1)
        self.assertEqual(len(self.engine.registered['stop']), 1)
        self.assertEqual(len(self.engine.registered['message']), 1)
        self.assertEqual(len(self.engine.registered['join']), 1)
        self.assertEqual(len(self.engine.registered['leave']), 1)
        self.assertEqual(len(self.engine.registered['enter']), 1)
        self.assertEqual(len(self.engine.registered['exit']), 1)
        self.assertEqual(len(self.engine.registered['inbound']), 1)
        self.assertEqual(len(self.engine.registered['some_custom_event']), 1)

    def test_dispatch(self):

        logging.info('*** dispatch ***')

        counter = MyCounter('counter #1')
        self.engine.register('bond', counter)
        self.engine.register('dispose', counter)

        self.engine.register('bond', MyCounter('counter #2'))
        self.engine.register('dispose', MyCounter('counter #3'))

        class AllEvents(object):
            def __init__(self):
                self.events = []

            def on_bond(self, bot):
                self.events.append('bond')

            def on_dispose(self):
                self.events.append('dispose')

            def on_start(self):
                self.events.append('start')

            def on_stop(self):
                self.events.append('stop')

            def on_message(self, received):
                assert received == '*void'
                self.events.append('message')

            def on_join(self, received):
                assert received == '*void'
                self.events.append('join')

            def on_leave(self, received):
                assert received == '*void'
                self.events.append('leave')

            def on_enter(self, received):
                assert received == '*void'
                self.events.append('enter')

            def on_exit(self, received):
                assert received == '*void'
                self.events.append('exit')

            def on_inbound(self, received):
                assert received == '*void'
                self.events.append('inbound')

            def on_some_custom_event(self, data):
                assert data == '*data'
                self.events.append('some_custom_event')

        all_events = AllEvents()
        self.engine.register('bond', all_events)
        self.engine.register('dispose', all_events)
        self.engine.register('start', all_events)
        self.engine.register('stop', all_events)
        self.engine.register('message', all_events)
        self.engine.register('join', all_events)
        self.engine.register('leave', all_events)
        self.engine.register('enter', all_events)
        self.engine.register('exit', all_events)
        self.engine.register('inbound', all_events)
        self.engine.register('some_custom_event', all_events)

        self.engine.dispatch('bond', bot='*dummy')
        self.engine.dispatch('dispose')
        self.engine.dispatch('start')
        self.engine.dispatch('stop')
        self.engine.dispatch('message', received='*void')
        self.engine.dispatch('join', received='*void')
        self.engine.dispatch('leave', received='*void')
        self.engine.dispatch('enter', received='*void')
        self.engine.dispatch('exit', received='*void')
        self.engine.dispatch('inbound', received='*void')
        self.engine.dispatch('some_custom_event', data='*data')

        with self.assertRaises(AssertionError):
            self.engine.dispatch('*unknown*event')

        self.assertEqual(counter.count, 2)
        self.assertEqual(all_events.events, [
            'bond', 'dispose', 'start', 'stop', 'message', 'join', 'leave',
            'enter', 'exit', 'inbound', 'some_custom_event'
        ])

    def test_load_commands(self):

        logging.info('*** load_commands ***')

        with mock.patch.object(self.engine.shell,
                               'load_commands',
                               return_value=None) as mocked:
            self.engine.load_commands(['a', 'b', 'c', 'd'])
            mocked.assert_called_with(['a', 'b', 'c', 'd'])

    def test_load_command(self):

        logging.info('*** load_command ***')

        with mock.patch.object(self.engine.shell,
                               'load_command',
                               return_value=None) as mocked:
            self.engine.load_command('a')
            mocked.assert_called_with('a')

    def test_hook(self):

        logging.info('*** hook ***')

        self.context.set('server.url', 'http://here.you.go:123')
        server = mock.Mock()
        with mock.patch.object(self.engine.space,
                               'register',
                               return_value=None) as mocked:

            self.engine.hook(server=server)
            self.assertFalse(mocked.called)

            self.context.set('server.binding', '0.0.0.0')
            self.engine.hook(server=server)
            mocked.assert_called_with(hook_url='http://here.you.go:123/hook')

    def test_get_hook(self):

        logging.info('*** get_hook ***')

        self.context.set('server.url', 'http://here.you.go:123')
        self.assertEqual(self.engine.get_hook(), self.engine.space.webhook)

    def test_run(self):

        logging.info('*** run ***')

        engine = Engine(context=self.context)
        engine.space = LocalSpace(context=self.context)

        engine.start = mock.Mock()
        engine.space.run = mock.Mock()

        engine.run()
        self.assertTrue(engine.start.called)
        self.assertTrue(engine.space.run.called)

        class MyServer(object):
            def __init__(self, engine):
                self.engine = engine

            def add_route(self, route, **kwargs):
                pass

            def run(self):
                self.engine.set("has_been_ran", True)

        server = MyServer(engine=engine)
        engine.run(server=server)
        self.assertTrue(engine.get("has_been_ran"))

    def test_start(self):

        logging.info('*** start ***')

        engine = Engine(context=self.context)
        engine.space = LocalSpace(context=self.context)

        engine.start_processes = mock.Mock()
        engine.on_start = mock.Mock()

        engine.start()
        self.assertTrue(engine.ears is not None)
        self.assertTrue(engine.mouth is not None)
        self.assertTrue(engine.start_processes.called)
        self.assertTrue(engine.on_start.called)

    def test_static(self):

        logging.info('*** static test ***')

        self.engine.configure()
        self.context.set('bus.address', 'tcp://127.0.0.1:6666')
        self.engine.listener.DEFER_DURATION = 0.0
        self.engine.publisher.DEFER_DURATION = 0.0
        self.engine.start()
        self.engine.stop()

        self.assertEqual(self.engine.get('listener.counter', 0), 0)
        self.assertEqual(self.engine.get('speaker.counter', 0), 0)

    def test_bond(self):

        logging.info("*** bond")

        self.space.delete = mock.Mock()

        channel = self.engine.bond(title=None)
        self.assertEqual(channel.id, '*local')
        self.assertEqual(channel.title, 'Collaboration space')

        channel = self.engine.bond(title='')
        self.assertEqual(channel.id, '*local')
        self.assertEqual(channel.title, 'Collaboration space')

        channel = self.engine.bond(title='hello world')
        self.assertEqual(channel.id, '*local')
        self.assertEqual(channel.title, 'hello world')

        self.assertFalse(self.space.delete.called)
        channel = self.engine.bond(reset=True)
        self.assertTrue(self.space.delete.called)

        self.space.add_participants = mock.Mock()
        self.engine.dispatch = mock.Mock()

        with mock.patch.object(self.space, 'get_by_title',
                               return_value=None) as mocked:
            self.engine.bond(
                title='my title',
                participants=['c', 'd'],
            )
            mocked.assert_called_with(title='my title')
            self.space.add_participants.assert_called_with(id='*local',
                                                           persons=['c', 'd'])

        self.space.configure(
            settings={
                'space': {
                    'title':
                    'Another title',
                    'participants':
                    ['*****@*****.**', '*****@*****.**'],
                }
            })
        with mock.patch.object(self.space, 'get_by_title',
                               return_value=None) as mocked:
            self.engine.bond()
            mocked.assert_called_with(title='Another title')
            self.space.add_participants.assert_called_with(
                id='*local',
                persons=['*****@*****.**', '*****@*****.**'])

    def test_enumerate_bots(self):

        logging.info('*** enumerate_bots ***')

        self.engine.bots = {
            '123': FakeBot(self.engine, '123'),
            '456': FakeBot(self.engine, '456'),
            '789': FakeBot(self.engine, '789'),
        }

        for bot in self.engine.enumerate_bots():
            self.assertTrue(bot.id in ['123', '456', '789'])

    def test_get_bot(self):

        logging.info('*** get_bot ***')

        self.engine.bots = {
            '123': FakeBot(self.engine, '123'),
            '456': FakeBot(self.engine, '456'),
            '789': FakeBot(self.engine, '789'),
        }

        bot = self.engine.get_bot('123')
        self.assertEqual(bot.id, '123')
        self.assertEqual(bot, self.engine.bots['123'])

        bot = self.engine.get_bot('456')
        self.assertEqual(bot.id, '456')
        self.assertEqual(bot, self.engine.bots['456'])

        bot = self.engine.get_bot('789')
        self.assertEqual(bot.id, '789')
        self.assertEqual(bot, self.engine.bots['789'])

        with mock.patch.object(self.engine,
                               'build_bot',
                               return_value=FakeBot(self.engine,
                                                    '*bot')) as mocked:

            bot = self.engine.get_bot()
            self.assertEqual(bot.id, '*bot')
            self.assertTrue('*bot' in self.engine.bots.keys())

    def test_build_bot(self):

        logging.info('*** build_bot ***')

        self.engine.context.apply(self.engine.DEFAULT_SETTINGS)
        self.engine.bots = {}

        bot = self.engine.build_bot('123', FakeBot)
        self.assertEqual(bot.id, '123')
        bot.store.remember('a', 'b')
        self.assertEqual(bot.store.recall('a'), 'b')

        bot = self.engine.build_bot('456', FakeBot)
        self.assertEqual(bot.id, '456')
        bot.store.remember('c', 'd')
        self.assertEqual(bot.store.recall('a'), None)
        self.assertEqual(bot.store.recall('c'), 'd')

        bot = self.engine.build_bot('789', FakeBot)
        self.assertEqual(bot.id, '789')
        bot.store.remember('e', 'f')
        self.assertEqual(bot.store.recall('a'), None)
        self.assertEqual(bot.store.recall('c'), None)
        self.assertEqual(bot.store.recall('e'), 'f')

    def test_on_build(self):

        logging.info('*** on_build ***')

        bot = ShellBot(engine=self.engine)
        self.engine.on_build(bot)

    def test_build_store(self):

        logging.info('*** build_store ***')

        store_1 = self.engine.build_store('123')
        store_1.append('names', 'Alice')
        store_1.append('names', 'Bob')
        self.assertEqual(store_1.recall('names'), ['Alice', 'Bob'])

        store_2 = self.engine.build_store('456')
        store_2.append('names', 'Chloe')
        store_2.append('names', 'David')
        self.assertEqual(store_2.recall('names'), ['Chloe', 'David'])

        self.assertEqual(store_1.recall('names'), ['Alice', 'Bob'])
        store_2.forget()
        self.assertEqual(store_1.recall('names'), ['Alice', 'Bob'])
        self.assertEqual(store_2.recall('names'), None)

    def test_initialize_store(self):

        logging.info('*** initialize_store ***')

        settings = {'bot.store': {'planets': ['Uranus', 'Mercury']}}
        self.engine.context.apply(settings)
        print(self.engine.get('bot.store'))
        bot = self.engine.build_bot('123', FakeBot)
        self.assertEqual(bot.store.recall('planets'), ['Uranus', 'Mercury'])

    def test_build_machine(self):

        logging.info('*** build_machine ***')

        bot = ShellBot(engine=self.engine)
        machine = self.engine.build_machine(bot)

        previous = self.engine.machine_factory
        self.engine.machine_factory = MachineFactory(
            module='shellbot.machines.base', name='Machine')
        machine = self.engine.build_machine(bot)
        self.engine.machine_factory = previous

    def test_build_updater(self):

        logging.info('*** build_updater ***')

        updater = self.engine.build_updater('*id')
        self.assertEqual(updater, None)

        self.engine.updater_factory = MyUpdaterFactory()
        updater = self.engine.build_updater('*id')
        self.assertEqual(updater.id, '*id')
예제 #3
0
class SpaceTests(unittest.TestCase):
    def setUp(self):
        self.context = Context()
        self.space = Space(context=self.context)

    def tearDown(self):
        del self.space
        del self.context
        collected = gc.collect()
        if collected:
            logging.info("Garbage collector: collected %d objects." %
                         (collected))

    def test_init(self):

        logging.info("*** init")

        logging.debug("- default init")
        self.assertEqual(self.space.ears, None)
        self.assertEqual(self.space.fan, None)

        logging.debug("- unknown parameter")
        space = Space(context=self.context, weird='w')
        with self.assertRaises(AttributeError):
            self.assertEqual(space.weird, 'w')

        logging.debug("- extended parameters")

        class ExSpace(Space):
            def on_init(self, ex_token=None, **kwargs):
                self.token = ex_token

        space = ExSpace(context=self.context,
                        ears='e',
                        fan='f',
                        ex_token='*token',
                        ex_unknown='*weird')
        self.assertEqual(space.ears, 'e')
        self.assertEqual(space.fan, 'f')
        self.assertEqual(space.token, '*token')
        with self.assertRaises(AttributeError):
            self.assertTrue(space.unknown is not None)
        with self.assertRaises(AttributeError):
            self.assertTrue(space.ex_unknown is not None)

        logging.debug("- initialised via configuration")
        space = LocalSpace(context=self.context)
        space.configure({
            'space': {
                'title': 'Another title',
                'participants':
                ['*****@*****.**', '*****@*****.**'],
            }
        })
        self.assertEqual(space.configured_title(), 'Another title')

    def test_on_start(self):

        logging.info("*** on_start")

        self.space.on_start()

    def test_on_stop(self):

        logging.info("*** on_stop")

        self.space.on_stop()

    def test_configure(self):

        logging.info("*** configure")

        class ExSpace(Space):
            def check(self):
                self.context.check('space.title', is_mandatory=True)

        settings = {'space.key': 'my value'}

        space = ExSpace(context=self.context)
        with self.assertRaises(KeyError):
            space.configure(settings=settings)

        self.assertEqual(space.context.get('space.title'), None)
        self.assertEqual(space.context.get('space.key'), 'my value')

    def test_configured_title(self):

        logging.info("*** configured_title")

        space = Space(context=self.context)

        self.assertEqual(space.configured_title(), space.DEFAULT_SPACE_TITLE)

        settings = {'space.title': 'my channel'}

        space.configure(settings=settings)
        self.assertEqual(self.context.get('space.title'), 'my channel')
        self.assertEqual(space.context.get('space.title'), 'my channel')
        self.assertEqual(space.configured_title(), 'my channel')

    def test_connect(self):

        logging.info("*** connect")

        self.space.connect()

    def test_list_group_channels(self):

        logging.info("*** list_group_channels")

        with self.assertRaises(NotImplementedError):
            channels = self.space.list_group_channels()

    def test_create(self):

        logging.info("*** create")

        with self.assertRaises(NotImplementedError):
            channel = self.space.create(title='*title')

    def test_get_by_title(self):

        logging.info("*** get_by_title")

        with self.assertRaises(AssertionError):
            self.space.get_by_title(None)

        with self.assertRaises(AssertionError):
            self.space.get_by_title('')

        self.assertEqual(self.space.get_by_title(title='*unknown'), None)

    def test_get_by_id(self):

        logging.info("*** get_by_id")

        with self.assertRaises(AssertionError):
            self.space.get_by_id(None)

        with self.assertRaises(AssertionError):
            self.space.get_by_id('')

        self.assertEqual(self.space.get_by_id(id='*unknown'), None)

    def test_get_by_person(self):

        logging.info("*** get_by_person")

        with self.assertRaises(AssertionError):
            self.space.get_by_person(None)

        with self.assertRaises(AssertionError):
            self.space.get_by_person('')

        self.assertEqual(self.space.get_by_person('*unknown'), None)

    def test_update(self):

        logging.info("*** update")

        with self.assertRaises(NotImplementedError):
            self.space.update(channel=FakeChannel())

    def test_delete(self):

        logging.info("*** delete")

        with self.assertRaises(NotImplementedError):
            self.space.delete(id='*id')

    def test_list_participants(self):

        logging.info("*** list_participants")

        with self.assertRaises(NotImplementedError):
            self.space.list_participants(id='*id')

    def test_add_participants(self):

        logging.info("*** add_participants")

        with mock.patch.object(self.space, 'add_participant') as mocked:

            self.space.add_participants(id='*id', persons=[])
            self.assertFalse(mocked.called)

        class MySpace(Space):
            def on_init(self):
                self._persons = []

            def add_participant(self, id, person):
                self._persons.append(person)

        space = MySpace(context=self.context)
        space.add_participants(id='*id',
                               persons=['*****@*****.**', '*****@*****.**'])

        self.assertEqual(space._persons, ['*****@*****.**', '*****@*****.**'])

    def test_add_participant(self):

        logging.info("*** add_participant")

        with self.assertRaises(NotImplementedError):
            self.space.add_participant(id='*id', person='*****@*****.**')

    def test_remove_participants(self):

        logging.info("*** remove_participants")

        with mock.patch.object(self.space, 'remove_participant') as mocked:

            self.space.remove_participants(id='*id', persons=[])
            self.assertFalse(mocked.called)

        class MySpace(Space):
            def on_init(self):
                self._persons = ['*****@*****.**', '*****@*****.**']

            def remove_participant(self, id, person):
                self._persons.remove(person)

        space = MySpace(context=self.context)
        space.remove_participants(id='*id',
                                  persons=['*****@*****.**', '*****@*****.**'])

        self.assertEqual(space._persons, [])

    def test_remove_participant(self):

        logging.info("*** remove_participant")

        with self.assertRaises(NotImplementedError):
            self.space.remove_participant(id='*id', person='*****@*****.**')

    def test_walk_messages(self):

        logging.info("*** walk_messages")

        with self.assertRaises(NotImplementedError):
            message = next(self.space.walk_messages(id='*id'))

    def test_post_message(self):

        logging.info("*** post_message")

        with self.assertRaises(AssertionError):  # missing id and person
            self.space.post_message(text="What's up, Doc?")

        with self.assertRaises(AssertionError):  # too much: id and person
            self.space.post_message(id='1', person='2', text="What's up, Doc?")

        with self.assertRaises(NotImplementedError):
            self.space.post_message(id='*id', text="What's up, Doc?")

        with self.assertRaises(NotImplementedError):
            self.space.post_message(person='*****@*****.**', text="What's up, Doc?")

    def test_webhook(self):

        logging.info("*** webhook")

        with self.assertRaises(NotImplementedError):
            self.space.webhook()

    def test_register(self):

        logging.info("*** register")

        with self.assertRaises(NotImplementedError):
            self.space.register(hook_url='http://no.where/')

    def test_deregister(self):

        logging.info("*** deregister")

        self.space.deregister()

    def test_start(self):

        logging.info("*** start")

        space = Space(context=self.context)
        space.register = mock.Mock()
        space.pull = mock.Mock()
        space.start(hook_url='http:/who.knows/')
        space.register.assert_called_with(hook_url='http:/who.knows/')
        self.assertFalse(space.pull.called)

        class ExSpace(Space):
            def on_reset(self):
                self._bot_id = None

            def on_start(self):
                self._bot_id = 123

        space = ExSpace(context=self.context)
        space.register = mock.Mock()
        space.pull = mock.Mock()
        space.PULL_INTERVAL = 0.01
        space.start()
        time.sleep(0.1)
        while space._bot_id is None:
            time.sleep(0.1)
        while self.context.get('puller.counter', 0) < 4:
            time.sleep(0.1)
        self.context.set('general.switch', 'off')
        self.assertFalse(space.register.called)
        self.assertEqual(space._bot_id, 123)
        self.assertTrue(self.context.get('puller.counter') > 3)

    def test_run(self):

        logging.info("*** run")

        space = Space(context=self.context)
        space.pull = mock.Mock(side_effect=Exception('TEST'))
        space.run()
        self.assertEqual(self.context.get('puller.counter'), 0)

        space = Space(context=self.context)
        space.pull = mock.Mock(side_effect=KeyboardInterrupt('ctl-C'))
        space.run()
        self.assertEqual(self.context.get('puller.counter'), 0)

    def test_pull(self):

        logging.info("*** pull")

        with self.assertRaises(NotImplementedError):
            self.space.pull()
예제 #4
0
class BotTests(unittest.TestCase):
    def setUp(self):
        self.context = Context()
        self.engine = Engine(context=self.context, ears=Queue(), mouth=Queue())
        self.engine.bus = Bus(self.context)
        self.engine.bus.check()
        self.engine.publisher = self.engine.bus.publish()
        self.space = LocalSpace(context=self.context, ears=self.engine.ears)
        self.store = MemoryStore(context=self.context)
        self.bot = ShellBot(engine=self.engine,
                            space=self.space,
                            store=self.store)
        self.channel = Channel({'id': '*id', 'title': '*title'})

    def tearDown(self):
        del self.channel
        del self.bot
        del self.store
        del self.space
        del self.engine.publisher
        del self.engine.bus
        del self.engine
        del self.context
        collected = gc.collect()
        if collected:
            logging.info("Garbage collector: collected %d objects." %
                         (collected))

    def test_init(self):

        logging.info('*** init ***')

        bot = ShellBot(engine=self.engine)

        self.assertEqual(bot.engine, self.engine)
        self.assertTrue(bot.space is not None)
        self.assertTrue(bot.channel is None)
        self.assertFalse(bot.store is None)
        self.assertFalse(bot.fan is None)
        self.assertTrue(bot.machine is None)

        bot = ShellBot(engine=self.engine,
                       space=self.space,
                       store=self.store,
                       fan='f')

        self.assertEqual(bot.engine, self.engine)
        self.assertEqual(bot.space, self.space)
        self.assertEqual(bot.store, self.store)
        self.assertEqual(bot.fan, 'f')

    def test_on_init(self):

        logging.info('*** on_init ***')

        bot = ShellBot(engine=self.engine, fan='f')
        bot.on_init()

    def test_is_ready(self):

        logging.info("*** is_ready")

        self.bot.channel = None
        self.assertFalse(self.bot.is_ready)

        self.bot.channel = self.channel
        self.assertTrue(self.bot.is_ready)

    def test_id(self):

        logging.info("*** id")

        self.bot.channel = None
        self.assertEqual(self.bot.id, None)

        self.bot.channel = self.channel
        self.assertEqual(self.bot.id, '*id')

    def test_title(self):

        logging.info("*** title")

        self.bot.channel = None
        self.assertEqual(self.bot.title, None)

        self.bot.channel = self.channel
        self.assertEqual(self.bot.title, '*title')

    def test_reset(self):

        logging.info("*** reset")

        self.bot.channel = self.channel
        self.bot.reset()
        self.assertEqual(self.bot.channel, None)

    def test_on_reset(self):

        logging.info("*** on_reset")

        self.bot.on_reset()

    def test_bond(self):

        logging.info("*** bond")

        self.bot.channel = self.channel
        self.bot.bond()

    def test_on_bond(self):

        logging.info("*** on_bond")

        self.bot.on_bond()

    def test_on_enter(self):

        logging.info("*** on_enter")

        self.bot.on_enter()

    def test_dispose(self):

        logging.info("*** dispose")

        self.engine.dispatch = mock.Mock()
        self.space.delete = mock.Mock()

        self.bot.dispose()
        self.assertFalse(self.engine.dispatch.called)
        self.assertFalse(self.space.delete.called)

        self.bot.channel = self.channel
        self.bot.dispose()
        self.assertTrue(self.engine.dispatch.called)
        self.assertTrue(self.space.delete.called)
        self.assertEqual(self.bot.channel, None)

    def test_on_exit(self):

        logging.info("*** on_exit")

        self.bot.on_exit()

    def test_add_participants(self):

        logging.info('*** add_participants ***')

        self.bot.channel = self.channel
        with mock.patch.object(self.bot.space,
                               'add_participants',
                               return_value=None) as mocked:
            self.bot.add_participants(['a', 'b', 'c', 'd'])
            mocked.assert_called_with(id='*id', persons=['a', 'b', 'c', 'd'])

    def test_add_participant(self):

        logging.info('*** add_participant ***')

        self.bot.channel = self.channel
        with mock.patch.object(self.bot.space,
                               'add_participant',
                               return_value=None) as mocked:
            self.bot.add_participant('*****@*****.**')
            mocked.assert_called_with(id='*id',
                                      is_moderator=False,
                                      person='*****@*****.**')

    def test_remove_participants(self):

        logging.info('*** remove_participants ***')

        self.bot.channel = self.channel
        with mock.patch.object(self.bot.space,
                               'remove_participants',
                               return_value=None) as mocked:
            self.bot.remove_participants(['a', 'b', 'c', 'd'])
            mocked.assert_called_with(id='*id', persons=['a', 'b', 'c', 'd'])

    def test_remove_participant(self):

        logging.info('*** remove_participant ***')

        self.bot.channel = self.channel
        with mock.patch.object(self.bot.space,
                               'remove_participant',
                               return_value=None) as mocked:
            self.bot.remove_participant('*****@*****.**')
            mocked.assert_called_with(id='*id', person='*****@*****.**')

    def test_say(self):

        logging.info('*** say ***')

        self.bot.say('*not*said*because*not*ready')

        self.bot.channel = self.channel

        self.bot.say('')
        self.bot.say(None)

        with mock.patch.object(self.engine.mouth, 'put',
                               return_value=None) as mocked:

            self.bot.say('test')
            self.bot.say('test', content='test')
            self.bot.say('test', file='test.yaml')

            self.bot.say('test', person='*****@*****.**')
            self.bot.say('test', content='test', person='*****@*****.**')
            self.bot.say('test', file='test.yaml', person='*****@*****.**')

        message_0 = None
        self.bot.say(message_0)
        with self.assertRaises(Exception):
            self.engine.mouth.get_nowait()

        message_0 = ''
        self.bot.say(message_0)
        with self.assertRaises(Exception):
            self.engine.mouth.get_nowait()

        message_1 = 'hello'
        self.bot.say(message_1)
        item = self.engine.mouth.get()
        self.assertEqual(item.text, message_1)
        self.assertEqual(item.channel_id, self.bot.id)
        self.assertEqual(item.person, None)

        self.bot.say(message_1, person='*****@*****.**')
        item = self.engine.mouth.get()
        self.assertEqual(item.text, message_1)
        self.assertEqual(item.channel_id, None)
        self.assertEqual(item.person, '*****@*****.**')

        message_2 = 'world'
        self.bot.say(message_2)
        self.assertEqual(self.engine.mouth.get().text, message_2)

        message_3 = 'hello'
        content_3 = 'world'
        self.bot.say(message_3, content=content_3)
        item = self.engine.mouth.get()
        self.assertEqual(item.text, message_3)
        self.assertEqual(item.content, content_3)
        self.assertEqual(item.file, None)

        message_4 = "What'sup Doc?"
        file_4 = 'http://some.server/some/file'
        self.bot.say(message_4, file=file_4)
        item = self.engine.mouth.get()
        self.assertEqual(item.text, message_4)
        self.assertEqual(item.content, None)
        self.assertEqual(item.file, file_4)

        message_5 = 'hello'
        content_5 = 'world'
        file_5 = 'http://some.server/some/file'
        self.bot.say(message_5, content=content_5, file=file_5)
        item = self.engine.mouth.get()
        self.assertEqual(item.text, message_5)
        self.assertEqual(item.content, content_5)
        self.assertEqual(item.file, file_5)

        content_6 = 'life is *good*'
        file_6 = 'http://some.server/some/file'
        self.bot.say(content=content_6, file=file_6)
        item = self.engine.mouth.get()
        self.assertEqual(item.text, None)
        self.assertEqual(item.content, content_6)
        self.assertEqual(item.file, file_6)

        content_7 = 'life is _very_ *good*'
        self.bot.say(content=content_7)
        item = self.engine.mouth.get()
        self.assertEqual(item.text, None)
        self.assertEqual(item.content, content_7)
        self.assertEqual(item.file, None)

    def test_say_banner(self):

        logging.info("*** say_banner")

        self.bot.channel = self.channel

        # banner settings have not been defined at all
        self.context.set('bot.banner.text', None)
        self.context.set('bot.banner.content', None)
        self.context.set('bot.banner.file', None)
        self.bot.say_banner()
        with self.assertRaises(Exception):
            self.engine.mouth.get_nowait()

        # banner settings are empty strings
        self.context.set('bot.banner.text', '')
        self.context.set('bot.banner.content', '')
        self.context.set('bot.banner.file', '')
        self.bot.say_banner()
        with self.assertRaises(Exception):
            self.engine.mouth.get_nowait()

        # plain banner with text, rich content, and some document upload
        self.context.set('bot.banner.text',
                         u"Type '@{} help' for more information")
        self.context.set('bot.banner.content',
                         u"Type ``@{} help`` for more information")
        self.context.set('bot.banner.file', "http://on.line.doc/guide.pdf")
        self.bot.say_banner()
        item = self.engine.mouth.get()
        self.assertEqual(item.text, "Type '@Shelly help' for more information")
        self.assertEqual(item.content,
                         'Type ``@Shelly help`` for more information')
        self.assertEqual(item.file, 'http://on.line.doc/guide.pdf')
        with self.assertRaises(Exception):
            self.engine.mouth.get_nowait()

    def test_remember(self):

        logging.info('***** remember')

        self.assertEqual(self.bot.recall('sca.lar'), None)
        self.bot.remember('sca.lar', 'test')
        self.assertEqual(self.bot.recall('sca.lar'), 'test')

        self.assertEqual(self.bot.recall('list'), None)
        self.bot.remember('list', ['hello', 'world'])
        self.assertEqual(self.bot.recall('list'), ['hello', 'world'])

        self.assertEqual(self.bot.recall('dict'), None)
        self.bot.remember('dict', {'hello': 'world'})
        self.assertEqual(self.bot.recall('dict'), {'hello': 'world'})

    def test_recall(self):

        logging.info('***** recall')

        # undefined key
        self.assertEqual(self.bot.recall('hello'), None)

        # undefined key with default value
        whatever = 'whatever'
        self.assertEqual(self.bot.recall('hello', whatever), whatever)

        # set the key
        self.bot.remember('hello', 'world')
        self.assertEqual(self.bot.recall('hello'), 'world')

        # default value is meaningless when key has been set
        self.assertEqual(self.bot.recall('hello', 'whatever'), 'world')

        # except when set to None
        self.bot.remember('special', None)
        self.assertEqual(self.bot.recall('special', []), [])

    def test_forget(self):

        logging.info('***** forget')

        # set the key and then forget it
        self.bot.remember('hello', 'world')
        self.assertEqual(self.bot.recall('hello'), 'world')
        self.bot.forget('hello')
        self.assertEqual(self.bot.recall('hello'), None)

        # set multiple keys and then forget all of them
        self.bot.remember('hello', 'world')
        self.bot.remember('bunny', "What'up, Doc?")
        self.assertEqual(self.bot.recall('hello'), 'world')
        self.assertEqual(self.bot.recall('bunny'), "What'up, Doc?")
        self.bot.forget()
        self.assertEqual(self.bot.recall('hello'), None)
        self.assertEqual(self.bot.recall('bunny'), None)

    def test_append(self):

        logging.info('***** append')

        self.bot.append('famous', 'hello, world')
        self.bot.append('famous', "What'up, Doc?")
        self.assertEqual(self.bot.recall('famous'),
                         ['hello, world', "What'up, Doc?"])

    def test_update(self):

        logging.info('***** update')

        self.bot.update('input', 'PO#', '1234A')
        self.bot.update('input', 'description', 'part does not fit')
        self.assertEqual(self.bot.recall('input'), {
            u'PO#': u'1234A',
            u'description': u'part does not fit'
        })
예제 #5
0
class BusTests(unittest.TestCase):
    def setUp(self):
        self.context = Context()
        self.context.set('general.switch', 'on')
        self.context.set('bus.address', 'tcp://127.0.0.1:6666')
        self.bus = Bus(context=self.context)

    def tearDown(self):
        del self.bus
        del self.context
        collected = gc.collect()
        if collected:
            logging.info("Garbage collector: collected %d objects." %
                         (collected))

    def test_bus_init(self):

        logging.info(u"***** bus/init")

        self.assertEqual(self.bus.DEFAULT_ADDRESS, 'tcp://127.0.0.1:5555')
        self.assertEqual(self.bus.context, self.context)

    def test_bus_check(self):

        logging.info(u"***** bus/check")

        self.context.set('bus.address', None)
        self.bus.check()
        self.assertEqual(self.context.get('bus.address'),
                         self.bus.DEFAULT_ADDRESS)

    def test_bus_subscribe(self):

        logging.info(u"***** bus/subscribe")

        with self.assertRaises(AssertionError):
            subscriber = self.bus.subscribe(None)

        with self.assertRaises(AssertionError):
            subscriber = self.bus.subscribe([])

        with self.assertRaises(AssertionError):
            subscriber = self.bus.subscribe(())

        subscriber = self.bus.subscribe('channel')

    def test_bus_publish(self):

        logging.info(u"***** bus/publish")

        publisher = self.bus.publish()

    def test_subscriber_init(self):

        logging.info(u"***** subscriber/init")

        subscriber = Subscriber(context=self.context, channels='channel')
        self.assertEqual(subscriber.context, self.context)
        self.assertTrue(subscriber.socket is None)

    def test_subscriber_get(self):

        logging.info(u"***** subscriber/get")

        subscriber = self.bus.subscribe('dummy')
        subscriber.socket = mock.Mock()
        with mock.patch.object(
                subscriber.socket, 'recv',
                return_value='dummy {"hello": "world"}') as mocked:

            message = subscriber.get()
            self.assertEqual(message, {u'hello': u'world'})

    def test_publisher_init(self):

        logging.info(u"***** publisher/init")

        publisher = Publisher(context=self.context)
        self.assertEqual(publisher.context, self.context)
        self.assertTrue(publisher.socket is None)

    def test_publisher_run(self):

        logging.info(u"***** publisher/run")

        publisher = Publisher(context=self.context)

        self.context.set('general.switch', 'off')
        publisher.run()

        self.context.set('general.switch', 'on')
        publisher.put('*weird', '*message')
        publisher.fan.put(None)
        publisher.run()

    def test_publisher_static_test(self):

        logging.info(u"***** publisher/static test")

        publisher = Publisher(context=self.context)
        publisher.DEFER_DURATION = 0.0
        self.context.set('general.switch', 'on')
        publisher.start()

        publisher.join(0.1)
        if publisher.is_alive():
            logging.info('Stopping publisher')
            self.context.set('general.switch', 'off')
            publisher.join()

        self.assertFalse(publisher.is_alive())
        self.assertEqual(self.context.get('publisher.counter', 0), 0)

    def test_publisher_dynamic_test(self):

        logging.info(u"***** publisher/dynamic test")

        publisher = Publisher(context=self.context)
        self.context.set('general.switch', 'on')

        items = [
            ('channel_A', "hello"),
            ('channel_B', "world"),
            ('channel_C', {
                "hello": "world"
            }),
        ]

        for (channel, message) in items:
            publisher.put(channel, message)

        publisher.fan.put(None)

        class MySocket(object):
            def __init__(self, context):
                self.context = context

            def send_string(self, item):
                pipe = self.context.get('pipe', [])
                pipe.append(item)
                self.context.set('pipe', pipe)

            def close(self):
                pass

        publisher.socket = MySocket(self.context)
        publisher.run()

        self.assertEqual(self.context.get('publisher.counter', 0), 3)
        self.assertEqual(self.context.get('pipe'), [
            'channel_A "hello"', 'channel_B "world"',
            'channel_C {"hello": "world"}'
        ])

    def test_publisher_put(self):

        logging.info(u"***** publisher/put")

        publisher = self.bus.publish()

        with self.assertRaises(AssertionError):
            publisher.put(None, 'message')

        with self.assertRaises(AssertionError):
            publisher.put('', 'message')

        with self.assertRaises(AssertionError):
            publisher.put([], 'message')

        with self.assertRaises(AssertionError):
            publisher.put((), 'message')

        with self.assertRaises(AssertionError):
            publisher.put('channel', None)

        with self.assertRaises(AssertionError):
            publisher.put('channel', '')

        with mock.patch.object(publisher.fan, 'put') as mocked:

            publisher.put('channel', 'message')
            mocked.assert_called_with('channel "message"')

            publisher.put(['channel'], 'message')
            mocked.assert_called_with('channel "message"')

    def test_life_cycle(self):

        logging.info(u"***** life cycle")

        class Listener(Process):
            def __init__(self, bus):
                Process.__init__(self)
                self.daemon = True
                self.bus = bus

            def run(self):
                subscriber = self.bus.subscribe('')
                logging.info(u"Starting subscriber")

                while self.bus.context.get('general.switch', 'off') == 'on':

                    message = subscriber.get()
                    if not message:
                        time.sleep(0.001)
                        continue

                    self.bus.context.set('received', message)
                    logging.info(u"- {}".format(message))

                logging.info(u"Stopping subscriber")

        listener = Listener(self.bus)
        listener.start()

        publisher = self.bus.publish()
        publisher.start()

        for value in range(1, 10):
            publisher.put('channel', {'counter': value})
        publisher.fan.put(None)

        publisher.join()
        time.sleep(0.5)
        self.bus.context.set('general.switch', 'off')
        time.sleep(0.5)
        listener.join()
예제 #6
0
class SparkSpaceTests(unittest.TestCase):
    def setUp(self):
        self.context = Context()
        self.ears = Queue()
        self.fan = Queue()
        self.space = SparkSpace(context=self.context,
                                ears=self.ears,
                                fan=self.fan)

    def tearDown(self):
        del self.space
        del self.fan
        del self.ears
        del self.context
        collected = gc.collect()
        if collected:
            logging.info("Garbage collector: collected %d objects." %
                         (collected))

    def test_on_init(self):

        logging.info("*** on_init")

        self.assertEqual(self.space.context.get('space.token'), None)
        self.assertEqual(self.space.api, None)
        self.assertEqual(self.space._last_message_id, 0)

        space = SparkSpace(context=self.context, token='b')
        self.assertEqual(space.context.get('space.token'), 'b')

    def test_configure(self):

        logging.info("*** configure")

        settings = {  # from settings to member attributes
            'space': {
                'type': 'spark',
                'room': 'My preferred room',
                'participants':
                ['*****@*****.**', '*****@*****.**'],
                'token': 'hkNWEtMJNkODVGlZWU1NmYtyY',
            }
        }
        self.space.configure(settings=settings)
        self.assertEqual(self.space.context.get('space.room'),
                         'My preferred room')
        self.assertEqual(self.space.configured_title(), 'My preferred room')
        self.assertEqual(self.space.context.get('space.participants'),
                         ['*****@*****.**', '*****@*****.**'])
        self.assertEqual(self.space.context.get('space.token'),
                         'hkNWEtMJNkODVGlZWU1NmYtyY')

        self.space.context.clear()
        self.space.configure({
            'space': {
                'type': 'spark',
                'room': 'My preferred room',
                'participants': '*****@*****.**',
            }
        })
        self.assertEqual(self.space.context.get('space.room'),
                         'My preferred room')
        self.assertEqual(self.space.context.get('space.participants'),
                         ['*****@*****.**'])

        with self.assertRaises(KeyError):  # missing key
            self.space.context.clear()
            self.space.configure({
                'spark': {
                    'participants':
                    ['*****@*****.**', '*****@*****.**'],
                    'team':
                    'Anchor team',
                    'token':
                    'hkNWEtMJNkODk3ZDZLOGQ0OVGlZWU1NmYtyY',
                }
            })

    def test_connect(self):

        logging.info("*** connect")

        def my_factory(access_token):
            return FakeApi(access_token=access_token)

        self.space.context.set('space.token', None)
        with self.assertRaises(AssertionError):
            self.space.connect()

        self.space.context.set('space.token', 'a')
        self.space.connect(factory=my_factory)
        self.assertEqual(self.space.api.token, 'a')
        self.assertEqual(self.space.audit_api, None)

        self.space.context.set('space.token', 'a')
        self.space.context.set('space.audit_token', 'b')
        self.space.connect(factory=my_factory)
        self.assertEqual(self.space.api.token, 'a')
        self.assertEqual(self.space.audit_api.token, 'b')

    def test_on_connect(self):

        logging.info("*** on_connect")

        self.space.api = FakeApi(me=FakeBot())
        self.space.on_connect()
        self.assertTrue(self.space.api.people.me.called)
        self.assertEqual(self.context.get('bot.address'), '*****@*****.**')
        self.assertEqual(self.context.get('bot.name'), 'shelly')
        self.assertTrue(len(self.context.get('bot.id')) > 20)

    def test_list_group_channels(self):

        logging.info("*** list_group_channels")

        self.space.api = FakeApi()
        channels = self.space.list_group_channels()
        self.assertEqual(len(channels), 1)
        self.assertTrue(self.space.api.rooms.list.called)
        channel = channels[0]
        self.assertEqual(channel.id, '*id')
        self.assertEqual(channel.title, '*title')

    def test_create(self):

        logging.info("*** create")

        with self.assertRaises(AssertionError):
            self.space.create(title=None)

        with self.assertRaises(AssertionError):
            self.space.create(title='')

        with self.assertRaises(AssertionError):
            self.space.create(title='*title')

        self.space.api = FakeApi()
        channel = self.space.create(title='*title')
        self.assertTrue(self.space.api.rooms.create.called)
        self.assertEqual(channel.id, '*id')
        self.assertEqual(channel.title, '*title')

    def test_get_by_title(self):

        logging.info("*** get_by_title")

        with self.assertRaises(AssertionError):
            channel = self.space.get_by_title(None)

        with self.assertRaises(AssertionError):
            channel = self.space.get_by_title('')

        with self.assertRaises(AssertionError):
            channel = self.space.get_by_title('*no*api*anyway')

        self.space.api = FakeApi()
        channel = self.space.get_by_title('*does*not*exist')
        self.assertEqual(channel, None)
        self.assertTrue(self.space.api.rooms.list.called)

        channel = self.space.get_by_title('*title')
        self.assertEqual(
            channel,
            Channel({
                "id": "*id",
                "is_direct": False,
                "is_group": True,
                "is_moderated": True,
                "is_team": False,
                "team_id": "*team",
                "title": "*title",
                "type": "group",
            }))

        class Intruder(object):
            def list(self, **kwargs):
                raise Exception('TEST')

        self.space.api.rooms = Intruder()
        channel = self.space.get_by_title('*title')
        self.assertEqual(channel, None)

    def test_get_by_id(self):

        logging.info("*** get_by_id")

        with self.assertRaises(AssertionError):
            channel = self.space.get_by_id(None)

        with self.assertRaises(AssertionError):
            channel = self.space.get_by_id('')

        with self.assertRaises(AssertionError):
            channel = self.space.get_by_id('*no*api*anyway')

        self.space.api = FakeApi()

        channel = self.space.get_by_id('*id')
        self.assertEqual(
            channel,
            Channel({
                "id": "*id",
                "is_direct": False,
                "is_group": True,
                "is_moderated": True,
                "is_team": False,
                "team_id": "*team",
                "title": "*title",
                "type": "group",
            }))

        class Intruder(object):
            def get(self, label, **kwargs):
                raise Exception('TEST')

        self.space.api.rooms = Intruder()
        channel = self.space.get_by_id('*id')
        self.assertEqual(channel, None)

    def test_get_by_person(self):

        logging.info("*** get_by_person")

        with self.assertRaises(AssertionError):
            channel = self.space.get_by_person(None)

        with self.assertRaises(AssertionError):
            channel = self.space.get_by_person('')

        with self.assertRaises(AssertionError):
            channel = self.space.get_by_person('*no*api*anyway')

        self.space.api = FakeApi(room=FakeDirectRoom())

        channel = self.space.get_by_person('Marcel Jones')
        self.assertEqual(
            channel,
            Channel({
                "id": "*direct_id",
                "is_direct": True,
                "is_group": False,
                "is_moderated": False,
                "is_team": False,
                "team_id": None,
                "title": "Marcel Jones",
                "type": "direct",
            }))

        class Intruder(object):
            def list(self, **kwargs):
                raise Exception('TEST')

        self.space.api.rooms = Intruder()
        channel = self.space.get_by_person('Marcel Jones')
        self.assertEqual(channel, None)

    def test_update(self):

        logging.info("*** update")

        self.space.api = FakeApi()
        self.space.update(channel=FakeChannel())

    def test_delete(self):

        logging.info("*** delete")

        # explicit id, room exists
        self.space.api = FakeApi()
        self.space.delete(id='*id')
        self.assertTrue(self.space.api.rooms.delete.called)

        # explicit id, room does not exists
        self.space.api = FakeApi()
        self.space.delete(id='*ghost*room')
        self.assertTrue(self.space.api.rooms.delete.called)

    def test_get_team(self):

        logging.info("*** get_team")

        class Team(object):
            name = '*name'
            id = '456'

        self.space.api = FakeApi(teams=[Team()])
        team = self.space.get_team(name='*name')
        self.assertTrue(self.space.api.teams.list.called)
        self.assertEqual(team.name, '*name')
        self.assertEqual(team.id, '456')

        self.space.api = FakeApi(teams=[Team()])
        team = self.space.get_team(name='*unknown')
        self.assertTrue(self.space.api.teams.list.called)
        self.assertEqual(team, None)

    def test_list_participants(self):

        logging.info("*** list_participants")

        self.space.api = FakeApi()
        self.space.list_participants(id='*id')
        self.assertTrue(self.space.api.memberships.list.called)

    def test_add_participants(self):

        logging.info("*** add_participants")

        with mock.patch.object(self.space, 'add_participant') as mocked:

            self.space.add_participants(id='*id', persons=['*****@*****.**'])
            mocked.assert_called_with(id='*id', person='*****@*****.**')

    def test_add_participant(self):

        logging.info("*** add_participant")

        self.space.api = FakeApi()
        self.space.add_participant(id='*id', person='*****@*****.**')
        self.assertTrue(self.space.api.memberships.create.called)

    def test_remove_participants(self):

        logging.info("*** remove_participants")

        with mock.patch.object(self.space, 'remove_participant') as mocked:

            self.space.remove_participants(id='*id',
                                           persons=['*****@*****.**'])
            mocked.assert_called_with(id='*id', person='*****@*****.**')

    def test_remove_participant(self):

        logging.info("*** remove_participant")

        self.space.api = FakeApi()
        self.space.remove_participant(id='*id', person='*****@*****.**')
        self.assertTrue(self.space.api.memberships.delete.called)

    def test_post_message(self):

        logging.info("*** post_message")

        self.space.api = FakeApi()
        self.space.post_message(id='*id', text='hello world')
        self.assertTrue(self.space.api.messages.create.called)

        self.space.api = FakeApi()
        self.space.post_message(person='*****@*****.**', text='hello world')
        self.assertTrue(self.space.api.messages.create.called)

        self.space.api = FakeApi()
        self.space.post_message(id='*id', content='hello world')
        self.assertTrue(self.space.api.messages.create.called)

        self.space.api = FakeApi()
        self.space.post_message(person='*****@*****.**', content='hello world')
        self.assertTrue(self.space.api.messages.create.called)

        self.space.api = FakeApi()
        with self.assertRaises(AssertionError):
            self.space.post_message(text='hello world',
                                    content='hello world',
                                    file='./test_messages/sample.png')

        self.space.api = FakeApi()
        with self.assertRaises(AssertionError):
            self.space.post_message(id='*id',
                                    person='*****@*****.**',
                                    text='hello world',
                                    content='hello world',
                                    file='./test_messages/sample.png')

        self.space.api = FakeApi()
        self.space.post_message(id='*id',
                                text='hello world',
                                content='hello world',
                                file='./test_messages/sample.png')
        self.assertTrue(self.space.api.messages.create.called)

        self.space.api = FakeApi()
        self.space.post_message(person='*****@*****.**',
                                text='hello world',
                                content='hello world',
                                file='./test_messages/sample.png')
        self.assertTrue(self.space.api.messages.create.called)

    def test_register(self):

        logging.info("*** register")

        self.context.set('bot.id', '*id')
        self.context.set('spark.token', '*token')

        self.space.api = FakeApi()
        self.space.register('*hook')
        self.assertTrue(self.space.api.webhooks.create.called)
        self.assertFalse(self.context.get('audit.has_been_armed'))

        self.space.api = FakeApi()
        self.space.audit_api = FakeApi()
        self.space.register('*hook')
        self.assertTrue(self.space.api.webhooks.create.called)
        self.assertTrue(self.context.get('audit.has_been_armed'))

    def test_deregister(self):

        logging.info("*** deregister")

        self.space.api = FakeApi()
        self.context.set('bot.id', '*id')
        self.space.deregister()
        self.assertTrue(self.space.api.webhooks.list.called)

    def test_run(self):

        logging.info("*** run")

        self.space.api = FakeApi()

        self.space.PULL_INTERVAL = 0.001
        mocked = mock.Mock(return_value=[])
        self.space.pull = mocked

        p = self.space.start()
        p.join(0.01)
        if p.is_alive():
            logging.info('Stopping puller')
            self.context.set('general.switch', 'off')
            p.join()

        self.assertFalse(p.is_alive())

    def test_webhook(self):

        logging.info("*** webhook")

        fake_message = {
            u'status': u'active',
            u'resource': u'messages',
            u'name': u'shellbot-messages',
            u'created': u'2017-07-30T20:14:24.050Z',
            u'appId': u'Y2lzY29zcGFyazovL3VzLmM3ZDUxNWNiNGEwY2M5MWFh',
            u'id': u'Y2lzY29zcGFyazovL3VzjI0MTM2ZjgwY2Yy',
            u'orgId': u'Y2lzY29zcGFyazovL3VYjU1ZS00ODYzY2NmNzIzZDU',
            u'createdBy': u'Y2lzY29zcGFyazovL3VzLS01ZGI5M2Y5MjI5MWM',
            u'targetUrl': u'http://0dab1.ngrok.io/hook',
            u'ownedBy': u'creator',
            u'actorId': u'Y2lzY29zcGFyazovL3VzL1BFkyMzU',
            u'data': {
                u'roomType': u'group',
                u'created': u'2017-07-30T20:14:50.882Z',
                u'personId': u'Y2lzY29zcGFyayYi1mYWYwZWQwMjkyMzU',
                u'personEmail': u'*****@*****.**',
                u'mentionedPeople': [u'Y2lzY29zcGFyazovL3VGI5M2Y5MjI5MWM'],
                u'roomId': u'Y2lzY29zcGFyazovL3VzL1NzUtYzc2ZDMyOGY0Y2Rj',
                u'id': '*123',
            },
            u'event': u'created',
        }
        self.space.api = FakeApi()
        self.assertEqual(self.space.webhook(fake_message), 'OK')
        self.assertTrue(self.space.api.messages.get.called)
        data = self.space.ears.get()
        self.assertEqual(
            yaml.safe_load(data), {
                'text': '*message',
                'content': '*message',
                'from_id': None,
                'from_label': None,
                'hook': 'shellbot-messages',
                'stamp': '2017-07-19T05:29:23.962Z',
                'created': '2017-07-19T05:29:23.962Z',
                'channel_id': None,
                'type': 'message',
                'is_direct': False,
                'mentioned_ids': []
            })

        with self.assertRaises(Exception):
            print(self.space.ears.get_nowait())
        with self.assertRaises(Exception):
            print(self.space.fan.get_nowait())

        fake_message = {
            u'status': u'active',
            u'resource': u'messages',
            u'name': u'shellbot-audit',
            u'created': u'2017-07-30T20:25:29.924Z',
            u'appId': u'Y2lzY29zcGFyazovL3VzL0FQUE2YyNjZhYmY2NmM5OTllYzFm',
            u'id': u'Y2lzY29zcGFyazovL3VzL1dFC00NzllLTg0MDQtZGQ2NGJiNTk3Nzdi',
            u'orgId': u'Y2lzY29zcGFyazovL3VzL09SR0FOSVpBVY2NmNzIzZDU',
            u'createdBy': u'Y2lzY29zcGFyazovL3VzL1BFTTIyYi1mYWYwZWQwMjkyMzU',
            u'targetUrl': u'http://0dab1.ngrok.io/hook',
            u'ownedBy': u'creator',
            u'actorId': u'Y2lzY29zcGFyazovL3VzLM2Y5MjI5MWM',
            u'data': {
                u'files':
                [u'http://hydra-a5.wbx2.com/contents/Y2lzY29zcGFWY5LzA'],
                u'roomType': u'group',
                u'created': u'2017-07-30T20:25:33.803Z',
                u'personId': u'Y2lzY29zcGFyazovL3VzL1BFT5M2Y5MjI5MWM',
                u'personEmail': u'*****@*****.**',
                u'roomId': u'Y2lzY29zcGFyazovL3VzL1JPTyNmFhNWYxYTY4',
                u'id': u'*123',
            },
            u'event': u'created',
        }

        self.space.audit_api = FakeApi()
        self.assertEqual(self.space.webhook(fake_message), 'OK')
        self.assertTrue(self.space.audit_api.messages.get.called)
        data = self.space.fan.get()
        self.assertEqual(
            yaml.safe_load(data), {
                'text': '*message',
                'content': '*message',
                'from_id': None,
                'from_label': None,
                'hook': 'shellbot-audit',
                'stamp': '2017-07-19T05:29:23.962Z',
                'created': '2017-07-19T05:29:23.962Z',
                'channel_id': None,
                'type': 'message',
                'is_direct': False,
                'mentioned_ids': []
            })

        with self.assertRaises(Exception):
            print(self.space.ears.get_nowait())
        with self.assertRaises(Exception):
            print(self.space.fan.get_nowait())

    def test_pull(self):

        logging.info("*** pull")

        self.space.api = FakeApi(messages=[FakeMessage()])

        self.assertEqual(self.space._last_message_id, 0)
        self.space.pull()
        self.assertEqual(self.context.get('puller.counter'), 1)
        self.assertTrue(self.space.api.messages.list.called)
        self.assertEqual(self.space._last_message_id, '*123')

        self.space.pull()
        self.assertEqual(self.context.get('puller.counter'), 2)
        self.assertEqual(self.space._last_message_id, '*123')

        self.space.pull()
        self.assertEqual(self.context.get('puller.counter'), 3)
        self.assertEqual(self.space._last_message_id, '*123')

        self.assertEqual(
            yaml.safe_load(self.ears.get()), {
                'text': '*message',
                'content': '*message',
                'from_id': None,
                'from_label': None,
                'hook': 'pull',
                'stamp': '2017-07-19T05:29:23.962Z',
                'created': '2017-07-19T05:29:23.962Z',
                'channel_id': None,
                'type': 'message',
                'is_direct': False,
                'mentioned_ids': []
            })
        with self.assertRaises(Exception):
            print(self.ears.get_nowait())

    def test_on_message(self):

        logging.info("*** on_message")

        class MySpace(SparkSpace):
            def name_attachment(self, url, token=None):
                return 'some_file.pdf'

            def get_attachment(self, url, token=None):
                return b'hello world'

        self.space = MySpace(context=self.context)

        self.space.on_message(my_message, self.ears)
        message = my_message.copy()
        message.update({"type": "message"})
        message.update({"content": message['text']})
        message.update({"attachment": "some_file.pdf"})
        message.update({"url": "http://www.example.com/images/media.png"})
        message.update({"from_id": '*matt*id'})
        message.update({"from_label": '*****@*****.**'})
        message.update({'is_direct': False})
        message.update({"mentioned_ids": ['*matt*id', '*julie*id']})
        message.update({"channel_id": '*id1'})
        message.update({"stamp": '2015-10-18T14:26:16+00:00'})
        self.maxDiff = None
        self.assertEqual(yaml.safe_load(self.ears.get()), message)

        self.space.on_message(my_private_message, self.ears)
        message = my_private_message.copy()
        message.update({"type": "message"})
        message.update({"content": message['text']})
        message.update({"from_id": '*foo*id'})
        message.update({"from_label": '*****@*****.**'})
        message.update({'is_direct': True})
        message.update({"mentioned_ids": []})
        message.update({"channel_id": '*direct*id'})
        message.update({"stamp": '2017-07-22T16:49:22.008Z'})
        self.maxDiff = None
        self.assertEqual(yaml.safe_load(self.ears.get()), message)

        with self.assertRaises(Exception):
            print(self.ears.get_nowait())

    def test_download_attachment(self):

        logging.info("*** download_attachment")

        class MySpace(SparkSpace):
            def name_attachment(self, url, token=None):
                return 'some_file.pdf'

            def get_attachment(self, url, token=None):
                return BytesIO(b'hello world')

        space = MySpace(context=self.context)
        outcome = space.download_attachment(url='/dummy')

        with open(outcome, "rb") as handle:
            self.assertEqual(handle.read(),
                             space.get_attachment('/dummy').read())

        try:
            os.remove(outcome)
        except:
            pass

    def test_name_attachment(self):

        logging.info("*** name_attachment")

        class MyResponse(object):
            def __init__(self, status_code=200, headers={}):
                self.status_code = status_code
                self.headers = headers

        self.space.token = None
        response = MyResponse(headers={'Content-Disposition': 'who cares'})
        self.assertEqual(
            self.space.name_attachment(url='/dummy', response=response),
            'downloadable')

        self.space.token = '*void'
        response = MyResponse(
            headers={'Content-Disposition': 'filename="some_file.pdf"'})
        self.assertEqual(
            self.space.name_attachment(url='/dummy', response=response),
            'some_file.pdf')

        self.space.token = None
        response = MyResponse(
            status_code=400,
            headers={'Content-Disposition': 'filename="some_file.pdf"'})
        with self.assertRaises(Exception):
            name = self.space.name_attachment(url='/dummy', response=response)

    def test_get_attachment(self):

        logging.info("*** get_attachment")

        class MyResponse(object):
            def __init__(self, status_code=200, headers={}):
                self.status_code = status_code
                self.headers = headers
                self.encoding = 'encoding'
                self.content = b'content'

        self.space.token = None
        response = MyResponse(headers={})
        content = self.space.get_attachment(url='/dummy',
                                            response=response).getvalue()
        self.assertEqual(content, b'content')

        self.space.token = '*void'
        response = MyResponse(headers={})
        content = self.space.get_attachment(url='/dummy',
                                            response=response).getvalue()
        self.assertEqual(content, b'content')

        self.space.token = None
        response = MyResponse(status_code=400, headers={})
        with self.assertRaises(Exception):
            content = self.space.get_attachment(url='/dummy',
                                                response=response)

    def test_on_join(self):

        logging.info("*** on_join")

        self.space.on_join(my_join, self.ears)
        item = my_join.copy()
        item.update({"type": "join"})
        item.update(
            {"actor_id": 'Y2lzY29zcGFyazovL3VRiMTAtODZkYy02YzU0Yjg5ODA5N2U'})
        item.update({"actor_address": '*****@*****.**'})
        item.update({"actor_label": 'Foo Bar'})
        item.update({
            "channel_id":
            'Y2lzY29zcGFyazovL3VzL1JP3LTk5MDAtMDU5MDI2YjBiNDUz'
        })
        item.update({"stamp": '2017-05-31T21:25:30.424Z'})
        self.maxDiff = None
        self.assertEqual(yaml.safe_load(self.ears.get()), item)

    def test_on_leave(self):

        logging.info("*** on_leave")

        self.space.on_leave(my_leave, self.ears)
        item = my_leave.copy()
        item.update({"type": "leave"})
        item.update(
            {"actor_id": 'Y2lzY29zcGFyazovL3VRiMTAtODZkYy02YzU0Yjg5ODA5N2U'})
        item.update({"actor_address": '*****@*****.**'})
        item.update({"actor_label": 'Foo Bar'})
        item.update({
            "channel_id":
            'Y2lzY29zcGFyazovL3VzL1JP3LTk5MDAtMDU5MDI2YjBiNDUz'
        })
        item.update({"stamp": '2017-05-31T21:25:30.424Z'})
        self.maxDiff = None
        self.assertEqual(yaml.safe_load(self.ears.get()), item)

    def test__to_channel(self):

        logging.info("*** _to_channel")

        channel = self.space._to_channel(FakeRoom())
        self.assertEqual(channel.id, '*id')
        self.assertEqual(channel.title, '*title')
        self.assertFalse(channel.is_direct)
        self.assertTrue(channel.is_group)
        self.assertFalse(channel.is_team)
        self.assertTrue(channel.is_moderated)

        channel = self.space._to_channel(FakeDirectRoom())
        self.assertEqual(channel.id, '*direct_id')
        self.assertEqual(channel.title, 'Marcel Jones')
        self.assertTrue(channel.is_direct)
        self.assertFalse(channel.is_group)
        self.assertFalse(channel.is_team)
        self.assertFalse(channel.is_moderated)

        channel = self.space._to_channel(FakeTeamRoom())
        self.assertEqual(channel.id, '*team_id')
        self.assertEqual(channel.title, '*team_title')
        self.assertFalse(channel.is_direct)
        self.assertTrue(channel.is_group)
        self.assertTrue(channel.is_team)
        self.assertFalse(channel.is_moderated)