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)
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')
class SpaceFactoryTests(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_build_space(self): logging.info("***** build generic space from settings") self.context.apply( settings={ # from settings to member attributes 'space': { 'title': 'My preferred channel', 'participants': ['*****@*****.**', '*****@*****.**'], 'team': 'Anchor team', 'token': 'hkNWEtMJNkODVGlZWU1NmYtyY', 'webhook': "http://73a1e282.ngrok.io", } }) space = SpaceFactory.build(context=self.context) self.assertEqual(self.context.get('space.title'), 'My preferred channel') self.assertEqual(space.configured_title(), 'My preferred channel') def test_build_local(self): logging.info("***** build local space from settings") self.context.apply( settings={ # from settings to member attributes 'space': { 'title': 'My preferred channel', 'participants': ['*****@*****.**', '*****@*****.**'], 'input': ['help', 'version'], } }) space = SpaceFactory.build(context=self.context) self.assertEqual(self.context.get('space.title'), 'My preferred channel') self.assertEqual(space.configured_title(), 'My preferred channel') def test_build_spark(self): logging.info("***** build Cisco Spark space from settings") self.context.apply( settings={ # from settings to member attributes 'space': { 'type': 'spark', 'room': 'My preferred channel', 'participants': ['*****@*****.**', '*****@*****.**'], 'team': 'Anchor team', 'token': 'hkNWEtMJNkODVGlZWU1NmYtyY', } }) space = SpaceFactory.build(context=self.context) self.assertEqual(space.context.get('space.token'), 'hkNWEtMJNkODVGlZWU1NmYtyY') self.assertEqual(space.context.get('space.room'), 'My preferred channel') self.assertEqual(space.configured_title(), 'My preferred channel') def test_sense_space(self): logging.info("***** sense generic space") self.context.apply( settings={ # sense='space' 'space': { 'room': 'My preferred channel', 'participants': ['*****@*****.**', '*****@*****.**'], 'team': 'Anchor team', 'token': 'hkNWEtMJNkODk3ZDZLOGQ0OVGlZWU1NmYtyY', 'fuzzy_token': '$MY_FUZZY_SPARK_TOKEN', } }) self.assertEqual(SpaceFactory.sense(self.context), 'space') def test_sense_local(self): logging.info("***** sense local space") self.context.apply( settings={ # sense='local' 'space': { 'type': 'local', 'title': 'My preferred channel', 'participants': ['*****@*****.**', '*****@*****.**'], 'input': ['help', 'version'], } }) self.assertEqual(SpaceFactory.sense(self.context), 'local') def test_sense_spark(self): logging.info("***** sense Cisco Spark space") self.context.apply( settings={ # sense='spark' 'space': { 'type': 'spark', 'room': 'My preferred channel', 'participants': ['*****@*****.**', '*****@*****.**'], 'team': 'Anchor team', 'token': 'hkNWEtMJNkODk3ZDZLOGQ0OVGlZWU1NmYtyY', 'fuzzy_token': '$MY_FUZZY_SPARK_TOKEN', } }) self.assertEqual(SpaceFactory.sense(self.context), 'spark') def test_sense_void(self): logging.info("***** sense nothing on bad configuration") self.context.apply(settings={ # no recognizable space type 'space': { 'type': 'not_a_space_type', 'room': 'My preferred channel', 'participants': ['*****@*****.**', '*****@*****.**'], 'team': 'Anchor team', 'token': 'hkNWEtMJNkODk3ZDZLOGQ0OVGlZWU1NmYtyY', 'fuzzy_token': '$MY_FUZZY_SPARK_TOKEN', }, }) with self.assertRaises(ValueError): SpaceFactory.sense(self.context) def test_get_space(self): logging.info("***** get generic space") space = SpaceFactory.get(type='space') space = SpaceFactory.get(type='space', context='c', weird='w') self.assertEqual(space.context, 'c') with self.assertRaises(AttributeError): self.assertEqual(space.weird, 'w') def test_get_local(self): logging.info("***** get local space") space = SpaceFactory.get(type='local', input=['hello', 'world']) self.assertEqual(space.participants, []) def test_get_spark(self): logging.info("***** get Cisco Spark space") space = SpaceFactory.get(type='spark', context=self.context, token='b') self.assertEqual(space.context.get('space.token'), 'b') def test_get_unknown(self): logging.info("***** get invalid space") with self.assertRaises(ValueError): space = SpaceFactory.get(type='*unknown', ex_token='b', ex_ears='c')
class ListFactoryTests(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): logging.info("***** init") factory = ListFactory(context='a') self.assertEqual(factory.context, 'a') self.assertEqual(factory.lists, {}) factory = ListFactory(context=self.context) self.assertEqual(factory.context, self.context) self.assertEqual(factory.lists, {}) def test_configure(self): logging.info("***** configure") settings = yaml.load(my_yaml) self.context.apply(settings) factory = ListFactory(context=self.context) factory.configure() self.assertEqual(sorted(factory.lists.keys()), ['supportteam', 'the famous four']) settings = yaml.load(dict_instead_of_list_yaml) self.context.clear() self.context.apply(settings) factory = ListFactory(context=self.context) factory.configure() self.assertEqual(len(factory.lists.keys()), 0) settings = yaml.load(list_instead_of_dict_yaml) self.context.clear() self.context.apply(settings) factory = ListFactory(context=self.context) factory.configure() self.assertEqual(len(factory.lists.keys()), 0) settings = yaml.load(missing_names_yaml) self.context.clear() self.context.apply(settings) factory = ListFactory(context=self.context) factory.configure() self.assertEqual(len(factory.lists.keys()), 0) def test_build_list(self): logging.info("***** build_list") factory = ListFactory(self.context) with self.assertRaises(AssertionError): list = factory.build_list(None) with self.assertRaises(AssertionError): list = factory.build_list('*weird') list = factory.build_list({}) self.assertEqual(list.items, []) self.assertEqual(list.as_command, False) list = factory.build_list({ 'items': ['*****@*****.**', '*****@*****.**'], 'as_command': True }) self.assertEqual(list.items, ['*****@*****.**', '*****@*****.**']) self.assertEqual(list.as_command, True) def test_get_list(self): logging.info("***** get_list") factory = ListFactory(self.context) settings = yaml.load(my_yaml) self.context.apply(settings) factory = ListFactory(context=self.context) factory.configure() list = factory.get_list("*unknown") self.assertEqual(list, []) list = factory.get_list("The Famous Four") self.assertEqual(list.items, [ '*****@*****.**', '*****@*****.**', '*****@*****.**', '*****@*****.**' ]) list = factory.get_list("the famous four") self.assertEqual(list.items, [ '*****@*****.**', '*****@*****.**', '*****@*****.**', '*****@*****.**' ]) list = factory.get_list("SupportTeam") self.assertEqual(list.items, ['*****@*****.**', '*****@*****.**']) list = factory.get_list("supportteam") self.assertEqual(list.items, ['*****@*****.**', '*****@*****.**']) def test_list_commands(self): logging.info("***** list_commands") factory = ListFactory(self.context) settings = yaml.load(my_yaml) self.context.apply(settings) factory = ListFactory(context=self.context) factory.configure() names = [x for x in factory.list_commands()] self.assertEqual(sorted(names), ['SupportTeam']) def test_apply_to_list(self): logging.info("***** aply_to_list") factory = ListFactory(self.context) settings = yaml.load(my_yaml) self.context.apply(settings) factory = ListFactory(context=self.context) factory.configure() class Counter(object): value = 0 def consume(self, item): logging.debug(u"- {}".format(item)) self.value += 1 my_counter = Counter() factory.apply_to_list(name='SupportTeam', apply=lambda x: my_counter.consume(x)) self.assertEqual(my_counter.value, 2) factory.apply_to_list(name='supportteam', apply=lambda x: my_counter.consume(x)) self.assertEqual(my_counter.value, 4)
class StoreFactoryTests(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_build_memory(self): store = StoreFactory.build(context=self.context) self.assertTrue(isinstance(store, MemoryStore)) def test_build_sqlite(self): self.context.apply(settings={ # from settings to member attributes 'sqlite': { 'db': 'self.store.db', } }) store = StoreFactory.build(context=self.context) self.assertTrue(isinstance(store, SqliteStore)) self.assertEqual(self.context.get('sqlite.db'), 'self.store.db') def test_sense(self): self.context.apply(settings={ # default is 'memory' 'oracle': { 'db': 'self.store.db', } }) self.assertEqual(StoreFactory.sense(self.context), 'memory') self.context.clear() self.context.apply(settings={ # sense='sqlite' 'sqlite': { 'db': 'self.store.db', } }) self.assertEqual(StoreFactory.sense(self.context), 'sqlite') def test_get_memory(self): store = StoreFactory.get(type='memory') self.assertTrue(isinstance(store, MemoryStore)) store = StoreFactory.get(type='memory', context=self.context, weird='w') self.assertEqual(store.context, self.context) with self.assertRaises(AttributeError): self.assertEqual(store.weird, 'w') def test_get_sqlite(self): store = StoreFactory.get(type='sqlite', context=self.context) self.assertTrue(isinstance(store, SqliteStore)) self.assertEqual(store.context, self.context) def test_get_unknown(self): with self.assertRaises(ValueError): store = StoreFactory.get(type='*unknown', ex_token='b', ex_ears='c')