class JunebugService(MultiService, object): '''Base service that runs the HTTP API, and contains transports as child services''' def __init__(self, config): super(JunebugService, self).__init__() self.config = config @inlineCallbacks def startService(self): '''Starts the HTTP server, and returns the port object that the server is listening on''' super(JunebugService, self).startService() self.api = JunebugApi(self, self.config) yield self.api.setup() self._port = reactor.listenTCP( self.config.port, Site(self.api.app.resource()), interface=self.config.interface) log.msg( 'Junebug is listening on %s:%s' % (self.config.interface, self.config.port)) @inlineCallbacks def stopService(self): '''Stops the HTTP server.''' yield self.api.teardown() yield self._port.stopListening() super(JunebugService, self).stopService()
class JunebugService(MultiService, object): """Base service that runs the HTTP API, and contains transports as child services""" def __init__(self, config): super(JunebugService, self).__init__() self.config = config @inlineCallbacks def startService(self): """Starts the HTTP server, and returns the port object that the server is listening on""" super(JunebugService, self).startService() self.api = JunebugApi(self, self.config) yield self.api.setup() self._port = reactor.listenTCP(self.config.port, Site(self.api.app.resource()), interface=self.config.interface) log.msg("Junebug is listening on %s:%s" % (self.config.interface, self.config.port)) @inlineCallbacks def stopService(self): """Stops the HTTP server.""" yield self.api.teardown() yield self._port.stopListening() super(JunebugService, self).stopService()
class JunebugTestBase(TestCase): '''Base test case that all junebug tests inherit from. Contains useful helper functions''' default_channel_properties = { 'type': 'telnet', 'config': { 'twisted_endpoint': 'tcp:0', }, 'mo_url': 'http://foo.bar', } default_router_properties = { 'type': 'testing', 'config': { 'test': 'pass', }, } default_destination_properties = { 'config': { 'target': 'valid', }, } default_channel_config = { 'ttl': 60, 'routers': { 'testing': 'junebug.tests.helpers.TestRouter', } } def patch_logger(self): ''' Patches the logger with an in-memory logger, which is acccessable at "self.logging_handler".''' self.logging_handler = logging.handlers.MemoryHandler(100) logging.getLogger().addHandler(self.logging_handler) self.addCleanup(self._cleanup_logging_patch) def patch_message_rate_clock(self): '''Patches the message rate clock, and returns the clock''' clock = Clock() self.patch(MessageRateStore, 'get_seconds', lambda _: clock.seconds()) return clock def _cleanup_logging_patch(self): self.logging_handler.close() logging.getLogger().removeHandler(self.logging_handler) def create_channel_properties(self, **kw): properties = deepcopy(self.default_channel_properties) config = kw.pop('config', {}) properties['config'].update(config) properties.update(kw) return properties @inlineCallbacks def create_channel_config(self, **kw): self.persistencehelper = PersistenceHelper() yield self.persistencehelper.setup() self.addCleanup(self.persistencehelper.cleanup) config = deepcopy(self.default_channel_config) config.update(kw) channel_config = self.persistencehelper.mk_config(config) channel_config['redis'] = channel_config['redis_manager'] returnValue(JunebugConfig(channel_config)) def create_router_config(self, **kw): properties = deepcopy(self.default_router_properties) config = kw.pop('config', {}) properties['config'].update(config) properties.update(kw) return properties def create_destination_config(self, **kw): properties = deepcopy(self.default_destination_properties) properties.update(kw) return properties @inlineCallbacks def create_channel(self, service, redis, transport_class=None, properties=default_channel_properties, id=None, config=None, plugins=[]): '''Creates and starts, and saves a channel, with a TelnetServerTransport transport''' self.patch(junebug.logging_service, 'LogFile', DummyLogFile) if transport_class is None: transport_class = 'vumi.transports.telnet.TelnetServerTransport' properties = deepcopy(properties) logpath = self.mktemp() if config is None: config = yield self.create_channel_config( channels={properties['type']: transport_class}, logging_path=logpath) channel = Channel(redis, config, properties, id=id, plugins=plugins) yield channel.start(self.service) properties['config']['transport_name'] = channel.id yield channel.save() self.addCleanup(channel.stop) returnValue(channel) @inlineCallbacks def create_channel_from_id(self, redis, config, id, service): '''Creates an existing channel given the channel id''' config = yield self.create_channel_config(**config) channel = yield Channel.from_id(redis, config, id, service) returnValue(channel) @inlineCallbacks def get_redis(self): '''Creates and returns a redis manager''' if hasattr(self, 'redis'): returnValue(self.redis) persistencehelper = PersistenceHelper() yield persistencehelper.setup() self.redis = yield persistencehelper.get_redis_manager() self.addCleanup(persistencehelper.cleanup) returnValue(self.redis) @inlineCallbacks def start_server(self, config=None): '''Starts a junebug server. Stores the service to "self.service", and the url at "self.url"''' # TODO: This setup is very manual, because we don't call # service.startService. This must be fixed to close mirror the real # program with our tests. if config is None: config = yield self.create_channel_config() self.service = JunebugService(config) self.api = JunebugApi(self.service, config) self.service.api = self.api redis = yield self.get_redis() yield self.api.setup(redis, self.get_message_sender()) self.config = self.api.config self.redis = self.api.redis self.inbounds = self.api.inbounds self.outbounds = self.api.outbounds self.message_sender = self.api.message_sender port = reactor.listenTCP(0, Site(self.api.app.resource()), interface='127.0.0.1') self.service._port = port self.addCleanup(self.stop_server) addr = port.getHost() self.url = "http://%s:%s" % (addr.host, addr.port) @inlineCallbacks def stop_server(self): # TODO: This teardown is very messy, because we don't actually call # service.startService. This needs to be fixed in order to ensure that # our tests are mirroring the real program closely. yield self.service.stopService() for service in self.service: service.disownServiceParent() for service in self.service.namedServices.values(): service.disownServiceParent() def get_message_sender(self): '''Creates a new MessageSender object, with a fake amqp client''' message_sender = MessageSender('amqp-spec-0-8.xml', None) spec = get_spec(vumi_resource_path('amqp-spec-0-8.xml')) client = FakeAmqpClient(spec) message_sender.client = client return message_sender def get_dispatched_messages(self, queue): '''Gets all messages that have been dispatched to the amqp broker. Should only be called after start_server, as it looks in the api for the amqp client''' amqp_client = self.api.message_sender.client return amqp_client.broker.get_messages('vumi', queue) def assert_was_logged(self, msg): self.assertTrue( any(msg in log.getMessage() for log in self.logging_handler.buffer)) def assert_request(self, req, method=None, body=None, headers=None): if method is not None: self.assertEqual(req['request'].method, 'POST') if headers is not None: for name, values in headers.iteritems(): self.assertEqual( req['request'].requestHeaders.getRawHeaders(name), values) if body is not None: self.assertEqual(json.loads(req['body']), body) def assert_body_contains(self, req, **fields): body = json.loads(req['body']) self.assertEqual( dict((k, v) for k, v in body.iteritems() if k in fields), fields) def assert_log(self, log, expected): '''Assert that a log matches what is expected.''' timestamp = log.pop('timestamp') self.assertTrue(isinstance(timestamp, float)) self.assertEqual(log, expected) def generate_status(self, level=None, components={}, inbound_message_rate=0, outbound_message_rate=0, submitted_event_rate=0, rejected_event_rate=0, delivery_succeeded_rate=0, delivery_failed_rate=0, delivery_pending_rate=0): '''Generates a status that the http API would respond with, given the same parameters''' return { 'status': level, 'components': components, 'inbound_message_rate': inbound_message_rate, 'outbound_message_rate': outbound_message_rate, 'submitted_event_rate': submitted_event_rate, 'rejected_event_rate': rejected_event_rate, 'delivery_succeeded_rate': delivery_succeeded_rate, 'delivery_failed_rate': delivery_failed_rate, 'delivery_pending_rate': delivery_pending_rate, } def assert_status(self, status, **kwargs): '''Assert that the current channel status is correct''' self.assertEqual(status, self.generate_status(**kwargs))
class JunebugTestBase(TestCase): '''Base test case that all junebug tests inherit from. Contains useful helper functions''' default_channel_properties = { 'type': 'telnet', 'config': { 'twisted_endpoint': 'tcp:0', }, 'mo_url': 'http://foo.bar', } default_channel_config = { 'ttl': 60, 'amqp': {}, } def patch_logger(self): ''' Patches the logger with an in-memory logger, which is acccessable at "self.logging_handler".''' self.logging_handler = logging.handlers.MemoryHandler(100) logging.getLogger().addHandler(self.logging_handler) self.addCleanup(self._cleanup_logging_patch) def patch_message_rate_clock(self): '''Patches the message rate clock, and returns the clock''' clock = Clock() self.patch(MessageRateStore, 'get_seconds', lambda _: clock.seconds()) return clock def _cleanup_logging_patch(self): self.logging_handler.close() logging.getLogger().removeHandler(self.logging_handler) def create_channel_properties(self, **kw): properties = deepcopy(self.default_channel_properties) properties.update(kw) return properties @inlineCallbacks def create_channel_config(self, **kw): self.persistencehelper = PersistenceHelper() yield self.persistencehelper.setup() self.addCleanup(self.persistencehelper.cleanup) config = deepcopy(self.default_channel_config) config.update(kw) channel_config = self.persistencehelper.mk_config(config) channel_config['redis'] = channel_config['redis_manager'] returnValue(JunebugConfig(channel_config)) @inlineCallbacks def create_channel( self, service, redis, transport_class=None, properties=default_channel_properties, id=None, config=None, plugins=[]): '''Creates and starts, and saves a channel, with a TelnetServerTransport transport''' self.patch(junebug.logging_service, 'LogFile', DummyLogFile) if transport_class is None: transport_class = 'vumi.transports.telnet.TelnetServerTransport' properties = deepcopy(properties) logpath = self.mktemp() if config is None: config = yield self.create_channel_config( channels={ properties['type']: transport_class }, logging_path=logpath) channel = Channel( redis, config, properties, id=id, plugins=plugins) yield channel.start(self.service) properties['config']['transport_name'] = channel.id yield channel.save() self.addCleanup(channel.stop) returnValue(channel) @inlineCallbacks def create_channel_from_id(self, redis, config, id, service): '''Creates an existing channel given the channel id''' config = yield self.create_channel_config(**config) channel = yield Channel.from_id(redis, config, id, service) returnValue(channel) @inlineCallbacks def get_redis(self): '''Creates and returns a redis manager''' if hasattr(self, 'redis'): returnValue(self.redis) persistencehelper = PersistenceHelper() yield persistencehelper.setup() self.redis = yield persistencehelper.get_redis_manager() self.addCleanup(persistencehelper.cleanup) returnValue(self.redis) @inlineCallbacks def start_server(self, config=None): '''Starts a junebug server. Stores the service to "self.service", and the url at "self.url"''' # TODO: This setup is very manual, because we don't call # service.startService. This must be fixed to close mirror the real # program with our tests. if config is None: config = yield self.create_channel_config() self.service = JunebugService(config) self.api = JunebugApi( self.service, config) self.service.api = self.api redis = yield self.get_redis() yield self.api.setup(redis, self.get_message_sender()) self.config = self.api.config self.redis = self.api.redis self.inbounds = self.api.inbounds self.outbounds = self.api.outbounds self.message_sender = self.api.message_sender port = reactor.listenTCP( 0, Site(self.api.app.resource()), interface='127.0.0.1') self.service._port = port self.addCleanup(self.stop_server) addr = port.getHost() self.url = "http://%s:%s" % (addr.host, addr.port) @inlineCallbacks def stop_server(self): # TODO: This teardown is very messy, because we don't actually call # service.startService. This needs to be fixed in order to ensure that # our tests are mirroring the real program closely. yield self.service.stopService() for service in self.service: service.disownServiceParent() for service in self.service.namedServices.values(): service.disownServiceParent() def get_message_sender(self): '''Creates a new MessageSender object, with a fake amqp client''' message_sender = MessageSender('amqp-spec-0-8.xml', None) spec = get_spec(vumi_resource_path('amqp-spec-0-8.xml')) client = FakeAmqpClient(spec) message_sender.client = client return message_sender def get_dispatched_messages(self, queue): '''Gets all messages that have been dispatched to the amqp broker. Should only be called after start_server, as it looks in the api for the amqp client''' amqp_client = self.api.message_sender.client return amqp_client.broker.get_messages( 'vumi', queue) def assert_was_logged(self, msg): self.assertTrue(any( msg in log.getMessage() for log in self.logging_handler.buffer)) def assert_request(self, req, method=None, body=None, headers=None): if method is not None: self.assertEqual(req['request'].method, 'POST') if headers is not None: for name, values in headers.iteritems(): self.assertEqual( req['request'].requestHeaders.getRawHeaders(name), values) if body is not None: self.assertEqual(json.loads(req['body']), body) def assert_body_contains(self, req, **fields): body = json.loads(req['body']) self.assertEqual( dict((k, v) for k, v in body.iteritems() if k in fields), fields) def assert_log(self, log, expected): '''Assert that a log matches what is expected.''' timestamp = log.pop('timestamp') self.assertTrue(isinstance(timestamp, float)) self.assertEqual(log, expected) def generate_status( self, level=None, components={}, inbound_message_rate=0, outbound_message_rate=0, submitted_event_rate=0, rejected_event_rate=0, delivery_succeeded_rate=0, delivery_failed_rate=0, delivery_pending_rate=0): '''Generates a status that the http API would respond with, given the same parameters''' return { 'status': level, 'components': components, 'inbound_message_rate': inbound_message_rate, 'outbound_message_rate': outbound_message_rate, 'submitted_event_rate': submitted_event_rate, 'rejected_event_rate': rejected_event_rate, 'delivery_succeeded_rate': delivery_succeeded_rate, 'delivery_failed_rate': delivery_failed_rate, 'delivery_pending_rate': delivery_pending_rate, } def assert_status(self, status, **kwargs): '''Assert that the current channel status is correct''' self.assertEqual(status, self.generate_status(**kwargs))
class JunebugTestBase(TestCase): '''Base test case that all junebug tests inherit from. Contains useful helper functions''' default_channel_properties = { 'type': 'telnet', 'config': { 'twisted_endpoint': 'tcp:0', 'worker_name': 'unnamed', }, 'mo_url': 'http://foo.bar', } default_channel_config = { 'ttl': 60, 'amqp': {}, } def patch_logger(self): ''' Patches the logger with an in-memory logger, which is acccessable at "self.logging_handler".''' self.logging_handler = logging.handlers.MemoryHandler(100) logging.getLogger().addHandler(self.logging_handler) self.addCleanup(self._cleanup_logging_patch) def _cleanup_logging_patch(self): self.logging_handler.close() logging.getLogger().removeHandler(self.logging_handler) def create_channel_properties(self, **kw): properties = deepcopy(self.default_channel_properties) properties.update(kw) return properties @inlineCallbacks def create_channel_config(self, **kw): self.persistencehelper = PersistenceHelper() yield self.persistencehelper.setup() self.addCleanup(self.persistencehelper.cleanup) config = deepcopy(self.default_channel_config) config.update(kw) channel_config = self.persistencehelper.mk_config(config) channel_config['redis'] = channel_config['redis_manager'] returnValue(JunebugConfig(channel_config)) @inlineCallbacks def create_channel( self, service, redis, transport_class, properties=default_channel_properties, id=None): '''Creates and starts, and saves a channel, with a TelnetServerTransport transport''' properties = deepcopy(properties) config = yield self.create_channel_config() channel = Channel( redis, config, properties, id=id) properties['config']['transport_name'] = channel.id yield channel.start(self.service) yield channel.save() self.addCleanup(channel.stop) returnValue(channel) @inlineCallbacks def create_channel_from_id(self, redis, config, id, service): '''Creates an existing channel given the channel id''' config = yield self.create_channel_config(**config) channel = yield Channel.from_id(redis, config, id, service) returnValue(channel) @inlineCallbacks def get_redis(self): '''Creates and returns a redis manager''' if hasattr(self, 'redis'): returnValue(self.redis) persistencehelper = PersistenceHelper() yield persistencehelper.setup() self.redis = yield persistencehelper.get_redis_manager() self.addCleanup(persistencehelper.cleanup) returnValue(self.redis) @inlineCallbacks def start_server(self): '''Starts a junebug server. Stores the service to "self.service", and the url at "self.url"''' config = yield self.create_channel_config() self.service = JunebugService(config) self.api = JunebugApi( self.service, config) redis = yield self.persistencehelper.get_redis_manager() self.api = JunebugApi(self.service, config) yield self.api.setup(redis, self.get_message_sender()) self.config = self.api.config self.redis = self.api.redis self.inbounds = self.api.inbounds self.outbounds = self.api.outbounds self.message_sender = self.api.message_sender port = reactor.listenTCP( 0, Site(self.api.app.resource()), interface='127.0.0.1') self.addCleanup(port.stopListening) addr = port.getHost() self.url = "http://%s:%s" % (addr.host, addr.port) def get_message_sender(self): '''Creates a new MessageSender object, with a fake amqp client''' message_sender = MessageSender('amqp-spec-0-8.xml', None) spec = get_spec(vumi_resource_path('amqp-spec-0-8.xml')) client = FakeAmqpClient(spec) message_sender.client = client return message_sender def get_dispatched_messages(self, queue): '''Gets all messages that have been dispatched to the amqp broker. Should only be called after start_server, as it looks in the api for the amqp client''' amqp_client = self.api.message_sender.client return amqp_client.broker.get_messages( 'vumi', queue)