async def test_add_person(self, dbsession, root_component): # Simulate adding a row to the "people" table in the application async with Context() as root_ctx: await root_component.start(root_ctx) async with Context() as ctx: session = ctx.require_resource(Session) session.add(Person(name="Another person")) # The testing code should see both rows now assert dbsession.scalar(func.count(Person.id)) == 2
async def test_delete_person(self, dbsession, root_component): # Simulate removing the test person in the application async with Context() as root_ctx: await root_component.start(root_ctx) async with Context() as ctx: session = ctx.require_resource(Session) session.execute(delete(Person)) # The testing code should not see any rows now assert dbsession.scalar(func.count(Person.id)) == 0
async def test_multiple_clients(caplog): """Test that a multiple connection configuration works as intended.""" async with Context() as context: await MongoDBComponent( clients={ 'db1': { 'host': 'localhost:27018' }, 'db2': { 'host': '/tmp/mongodb.sock', 'ssl': True } }).start(context) assert isinstance(context.db1, AsyncIOMotorClient) assert isinstance(context.db2, AsyncIOMotorClient) records = [ record for record in caplog.records if record.name == 'asphalt.mongodb.component' ] records.sort(key=lambda r: r.message) assert len(records) == 4 assert records[0].message == ("Configured MongoDB client (db1 / ctx.db1; " "hosts={'localhost:27018'})") assert records[1].message == ("Configured MongoDB client (db2 / ctx.db2; " "hosts={'/tmp/mongodb.sock'})") assert records[2].message == 'MongoDB client (db1) shut down' assert records[3].message == 'MongoDB client (db2) shut down'
async def start(self, ip: str = "127.0.0.1", port: int = 4444, *, component=None, base_context: Context = None): """ Runs the Kyoukai component asynchronously. This will bypass Asphalt's default runner, and allow you to run your app easily inside something else, for example. :param ip: The IP of the built-in server. :param port: The port of the built-in server. :param component: The component to start the app with. This should be an instance of \ :class:`~.KyoukaiComponent`. :param base_context: The base context that the HTTPRequestContext should be started with. """ if not base_context: base_context = Context() if not component: from kyoukai.asphalt import KyoukaiComponent self.component = KyoukaiComponent(self, ip, port) else: self.component = component # Start the app. await self.component.start(base_context)
async def test_multiple_clients(caplog): """Test that a multiple client configuration works as expected.""" async with Context() as context: await InfluxDBComponent( clients={ 'db1': { 'base_urls': 'http://localhost:9999' }, 'db2': { 'base_urls': 'https://remotehost.example.org:443/influx' } }).start(context) assert isinstance(context.db1, InfluxDBClient) assert isinstance(context.db2, InfluxDBClient) records = [ record for record in caplog.records if record.name == 'asphalt.influxdb.component' ] records.sort(key=lambda r: r.message) assert len(records) == 4 assert records[0].message == ("Configured InfluxDB client (db1 / ctx.db1; " "base_urls=['http://localhost:9999'])") assert records[1].message == ( "Configured InfluxDB client (db2 / ctx.db2; " "base_urls=['https://remotehost.example.org:443/influx'])") assert records[2].message == 'InfluxDB client (db1) shut down' assert records[3].message == 'InfluxDB client (db2) shut down'
async def test_multiple_connections(caplog): """Test that a multiple connection configuration works as intended.""" async with Context() as context: await MemcachedComponent( clients={ 'mc1': { 'host': '1.2.3.4', 'port': 11212 }, 'mc2': { 'host': '127.0.0.2' } }).start(context) assert isinstance(context.mc1, Client) assert isinstance(context.mc2, Client) records = [ record for record in caplog.records if record.name == 'asphalt.memcached.component' ] records.sort(key=lambda r: r.message) assert len(records) == 4 assert records[0].message == 'Configured Memcached client (mc1 / ctx.mc1)' assert records[1].message == 'Configured Memcached client (mc2 / ctx.mc2)' assert records[2].message == 'Memcached client (mc1) shut down' assert records[3].message == 'Memcached client (mc2) shut down'
def test_getattr_parent(self, context): """ Test that accessing a nonexistent attribute on a context retrieves the value from parent. """ child_context = Context(context) context.a = 2 assert child_context.a == 2
async def test_missing_yield(self): @context_teardown async def start(ctx: Context): pass with pytest.raises(RuntimeError) as exc_info: await start(Context()) exc_info.match(' did not do "await yield_\(\)"$')
async def test_rollback(self, dbsession, root_component): # Simulate a rollback happening in a subcontext async with Context() as root_ctx: await root_component.start(root_ctx) async with Context() as ctx: session = ctx.require_resource(Session) try: # No value for a non-nullable column => IntegrityError! session.add(Person()) session.flush() except IntegrityError: # Without the session listener, this row would now be inserted # outside a SAVEPOINT, breaking test isolation session.rollback() session.add(Person(name="Works now!")) session.flush() # The context is gone, but the extra Person should still be around assert dbsession.scalar(func.count(Person.id)) == 2
async def test_add_resource_factory_no_inherit(self, context): """ Test that a subcontext gets its own version of a factory-generated resource even if a parent context has one already. """ context.add_resource_factory(id, int, context_attr='foo') subcontext = Context(context) assert context.foo == id(context) assert subcontext.foo == id(subcontext)
async def test_exception(self): @context_teardown async def start(ctx): raise Exception('dummy error') context = Context() with pytest.raises(Exception) as exc_info: await start(context) exc_info.match('dummy error')
async def test_request_resource_parent_add(self, context, event_loop): """ Test that adding a resource to the parent context will satisfy a resource request in a child context. """ child_context = Context(context) task = event_loop.create_task(child_context.request_resource(int)) event_loop.call_soon(context.add_resource, 6) resource = await task assert resource == 6
def __init__(self, app: Kyoukai, base_context: Context = None): """ :param app: The current application to run. :param base_context: The base context which is used as the parent to the \ HTTPRequestContext created. """ self.app = app self._base_context = base_context if not self._base_context: # Create an empty context as the default base context. self._base_context = Context()
async def test_generate_value(self, thread, context_attr): container = ResourceContainer(lambda ctx: 'foo', (str,), 'default', context_attr, True) context = Context() if thread: value = await context.call_in_executor(container.generate_value, context) else: value = container.generate_value(context) assert value == 'foo' assert context.get_resource(str) == 'foo' if context_attr: assert getattr(context, context_attr) == 'foo'
async def start(self, ctx: Context): # Remove the db file if it exists db_path = self.csv_path.with_name('people.db') if db_path.exists(): db_path.unlink() self.add_component('sqlalchemy', url='sqlite:///{}'.format(db_path)) await super().start(ctx) # Create the table in a subcontext – never use connections or sessions from a long lived # context! async with Context(ctx): Base.metadata.create_all(ctx.sql.bind)
async def test_create_remove_document(): """Test the client against a real MongoDB server.""" async with Context() as context: await MongoDBComponent(host=MONGODB_HOSTNAME).start(context) collection = context.mongo['testdb']['testcollection'] document = {'somekey': 'somevalue'} document2 = {'otherkey': 7} try: await collection.insert(document) await collection.insert(document2) documents = await collection.find().to_list(None) assert documents == [document, document2] finally: await context.mongo.drop_database('testdb')
async def test_default_connection(caplog): """Test that the default connection is started and is available on the context.""" async with Context() as context: await MemcachedComponent().start(context) assert isinstance(context.memcached, Client) records = [ record for record in caplog.records if record.name == 'asphalt.memcached.component' ] records.sort(key=lambda r: r.message) assert len(records) == 2 assert records[ 0].message == "Configured Memcached client (default / ctx.memcached)" assert records[1].message == 'Memcached client (default) shut down'
async def test_default_client(caplog): """Test that the default connection is started and is available on the context.""" async with Context() as context: await MongoDBComponent().start(context) assert isinstance(context.mongo, AsyncIOMotorClient) records = [ record for record in caplog.records if record.name == 'asphalt.mongodb.component' ] records.sort(key=lambda r: r.message) assert len(records) == 2 assert records[0].message == ( "Configured MongoDB client (default / ctx.mongo; " "hosts={'localhost:27017'})") assert records[1].message == 'MongoDB client (default) shut down'
async def test_default_client(caplog): """Test that the default client configuration works as expected.""" async with Context() as context: await InfluxDBComponent().start(context) assert isinstance(context.influxdb, InfluxDBClient) records = [ record for record in caplog.records if record.name == 'asphalt.influxdb.component' ] records.sort(key=lambda r: r.message) assert len(records) == 2 assert records[0].message == ( "Configured InfluxDB client (default / ctx.influxdb; " "base_urls=['http://localhost:8086'])") assert records[1].message == 'InfluxDB client (default) shut down'
async def test_function(self, expected_exc): @context_teardown async def start(ctx: Context): nonlocal phase, received_exception phase = 'started' exc = await yield_() phase = 'finished' received_exception = exc phase = received_exception = None context = Context() await start(context) assert phase == 'started' await context.close(expected_exc) assert phase == 'finished' assert received_exception == expected_exc
async def test_method(self, expected_exc): class SomeComponent: @context_teardown async def start(self, ctx: Context): nonlocal phase, received_exception phase = 'started' exc = await yield_() phase = 'finished' received_exception = exc phase = received_exception = None context = Context() await SomeComponent().start(context) assert phase == 'started' await context.close(expected_exc) assert phase == 'finished' assert received_exception == expected_exc
async def test_clustered_client(caplog): """Test that a clustered client configuration works as expected.""" async with Context() as context: base_urls = [ 'http://influx1.example.org:8086', 'http://influx2.example.org:8087/prefix' ] await InfluxDBComponent(base_urls=base_urls).start(context) assert isinstance(context.influxdb, InfluxDBClient) records = [ record for record in caplog.records if record.name == 'asphalt.influxdb.component' ] records.sort(key=lambda r: r.message) assert len(records) == 2 assert records[0].message == ( "Configured InfluxDB client (default / ctx.influxdb; " "base_urls=['http://influx1.example.org:8086', 'http://influx2.example.org:8087/prefix'])" ) assert records[1].message == 'InfluxDB client (default) shut down'
async def start(self, ip="0.0.0.0", port=4444, component=None): # pragma: no cover """ Run the Kyoukai component asynchronously. This bypasses Asphalt's runner completely and starts Kyoukai as it's own context. :param ip: The IP address to bind to. :param port: The port to bind to. :param component: The component to set on the application. If this is not passed in, it will create an empty one. """ self.logger.warning( "Kyoukai is bypassing Asphalt - contexts will not work.") ctx = Context() if not component: from kyoukai.asphalt import KyoukaiComponent self.component = KyoukaiComponent(self, ip, port) await self.component.start(ctx)
def from_asphalt_config(cls, filename: str) -> 'GunicornAdapter': """ Creates a new :class:`GunicornAdapter` from an Asphalt component config file. """ from ruamel import yaml with open(filename): config_data = yaml.load(filename) # Instantiate the root component try: component_config = config_data.pop('component') except KeyError: raise LookupError('missing configuration key: component') from None else: component = component_types.create_object( **component_config) # type: ContainerComponent # Create a new Context. context = Context() # Run `start()` on the app. loop = asyncio.get_event_loop() # You best hope this doesn't block. loop.run_until_complete(component.start(context)) # Find the KyoukaiComponent. for c in component.child_components: if isinstance(c, KyoukaiBaseComponent): break else: raise TypeError( "Could not find KyoukaiComponent in component list") del component # Create a new adapter. klass = cls(c.app, context) return klass
def test_getattr_attribute_error(self, context): child_context = Context(context) pytest.raises(AttributeError, getattr, child_context, 'foo').\ match('no such context variable: foo')
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._testing_protocol = TestProtocol(self, Context()) self._responses = asyncio.Queue()
def context(event_loop): ctx = Context() yield ctx event_loop.run_until_complete(ctx.close())
def test_parent(self): """Test that the parent property points to the parent context instance, if any.""" parent = Context() child = Context(parent) assert parent.parent is None assert child.parent is parent
def context(event_loop): ctx = Context() yield ctx event_loop.run_until_complete( ctx.finished.dispatch(None, return_future=True))
def context(event_loop): return Context()