async def test_finish_commit(raise_exception, tmp_path):
    """
    Tests that the session is automatically committed if and only if the context was not
    exited with an exception.

    """
    db_path = tmp_path / "test.db"
    engine = create_engine(f"sqlite:///{db_path}", poolclass=NullPool)
    with engine.begin() as connection:
        connection.execute(text("CREATE TABLE foo (id INTEGER PRIMARY KEY)"))

        component = SQLAlchemyComponent(url={
            "drivername": "sqlite",
            "database": str(db_path)
        }, )
        with ExitStack() as stack:
            async with Context() as ctx:
                await component.start(ctx)
                session = ctx.require_resource(Session)
                session.execute(text("INSERT INTO foo (id) VALUES(3)"))
                if raise_exception:
                    stack.enter_context(pytest.raises(Exception,
                                                      match="dummy"))
                    raise Exception("dummy")

        rows = connection.execute(text("SELECT * FROM foo")).fetchall()
        assert len(rows) == (0 if raise_exception else 1)
Ejemplo n.º 2
0
async def test_resources():
    mailer = SMTPMailer(tls_context='contextresource')
    context = Context()
    sslcontext = ssl.create_default_context()
    context.add_resource(sslcontext, 'contextresource')
    await mailer.start(context)
    assert mailer.tls_context is sslcontext
async def test_component_multiple(caplog):
    caplog.set_level(logging.INFO, logger='asphalt.mailer.component')
    component = MailerComponent(
        mailers={
            'smtp': {
                'backend': 'smtp',
                'context_attr': 'mailer1'
            },
            'sendmail': {
                'backend': 'sendmail',
                'context_attr': 'mailer2'
            }
        })
    async with Context() as ctx:
        await component.start(ctx)
        assert isinstance(ctx.mailer1, Mailer)
        assert isinstance(ctx.mailer2, Mailer)

    records = [
        record for record in caplog.records
        if record.name == 'asphalt.mailer.component'
    ]
    records.sort(key=lambda r: r.message)
    assert len(records) == 4
    assert records[0].message.startswith(
        "Configured mailer (sendmail / ctx.mailer2; class=SendmailMailer)")
    assert records[1].message.startswith(
        "Configured mailer (smtp / ctx.mailer1; class=SMTPMailer)")
    assert records[2].message.startswith('Mailer (sendmail) stopped')
    assert records[3].message.startswith('Mailer (smtp) stopped')
Ejemplo n.º 4
0
    async def test_get_resources_include_parents(self, context):
        subcontext = Context(context)
        resource1 = context.publish_resource(6, 'int1')
        resource2 = subcontext.publish_resource(8, 'int2')
        resource3 = context.publish_resource('foo', 'str')

        assert subcontext.get_resources() == [resource1, resource2, resource3]
        assert subcontext.get_resources(include_parents=False) == [resource2]
Ejemplo n.º 5
0
    def test_get_parent_attribute(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
Ejemplo n.º 6
0
def mailer(event_loop, unused_tcp_port, client_tls_context, smtp_server, tls):
    mailer = SMTPMailer(port=unused_tcp_port,
                        timeout=1,
                        tls=tls,
                        tls_context=client_tls_context)
    with Context() as ctx:
        event_loop.run_until_complete(mailer.start(ctx))
        yield mailer
Ejemplo n.º 7
0
async def test_default_config():
    component = SerializationComponent(backend='json')
    async with Context() as ctx:
        await component.start(ctx)

        resource = ctx.require_resource(Serializer)
        assert isinstance(resource, JSONSerializer)
        assert ctx.json is resource
Ejemplo n.º 8
0
    def test_run_return_5(self, event_loop):
        class DummyCLIComponent(CLIApplicationComponent):
            async def run(self, ctx: Context):
                return 5

        component = DummyCLIComponent()
        event_loop.run_until_complete(component.start(Context()))
        exc = pytest.raises(SystemExit, event_loop.run_forever)
        assert exc.value.code == 5
Ejemplo n.º 9
0
async def test_multiple_engines():
    component = SQLAlchemyComponent(engines={'db1': {}, 'db2': {}}, url='sqlite:///:memory:')
    async with Context() as ctx:
        await component.start(ctx)

        engine1 = ctx.require_resource(Engine, 'db1')
        engine2 = ctx.require_resource(Engine, 'db2')
        assert ctx.db1.bind is engine1
        assert ctx.db2.bind is engine2
Ejemplo n.º 10
0
async def test_bind():
    """Test that a Connection can be passed as "bind" in place of "url"."""
    engine = create_engine('sqlite:///:memory:')
    connection = engine.connect()
    component = SQLAlchemyComponent(bind=connection)
    async with Context() as ctx:
        await component.start(ctx)

        assert ctx.require_resource(Engine) is engine
        assert ctx.sql.bind is connection
Ejemplo n.º 11
0
async def test_memory_leak():
    """Test that creating a session in a context does not leak memory."""
    component = SQLAlchemyComponent(url='sqlite:///:memory:')
    async with Context() as ctx:
        await component.start(ctx)
        assert isinstance(ctx.sql, Session)

    del ctx
    gc.collect()  # needed on PyPy
    assert next((x for x in gc.get_objects() if isinstance(x, Context)), None) is None
async def test_component_start_async():
    """Test that the component creates all the expected (asynchronous) resources."""
    url = URL.create("sqlite+aiosqlite", database=":memory:")
    component = SQLAlchemyComponent(url=url)
    async with Context() as ctx:
        await component.start(ctx)

        ctx.require_resource(AsyncEngine)
        ctx.require_resource(sessionmaker)
        ctx.require_resource(AsyncSession)
Ejemplo n.º 13
0
async def test_component_start(poolclass):
    """Test that the component creates all the expected resources."""
    component = SQLAlchemyComponent(url='sqlite:///:memory:', poolclass=poolclass)
    async with Context() as ctx:
        await component.start(ctx)

        engine = ctx.require_resource(Engine)
        ctx.require_resource(sessionmaker)
        assert ctx.sql is ctx.require_resource(Session)
        assert ctx.sql.bind is engine
Ejemplo n.º 14
0
    async def test_request_resource_parent_add(self, context, event_loop):
        """
        Test that publishing 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))
        context.publish_resource(6)
        resource = await task
        assert resource == 6
async def test_bind_async():
    """Test that a Connection can be passed as "bind" in place of "url"."""
    engine = create_async_engine("sqlite+aiosqlite:///:memory:")
    connection = await engine.connect()
    component = SQLAlchemyComponent(bind=connection)
    async with Context() as ctx:
        await component.start(ctx)

        assert ctx.require_resource(AsyncEngine) is engine
        assert ctx.require_resource(AsyncSession).bind is connection

    await connection.close()
Ejemplo n.º 16
0
async def test_single_client(event_loop):
    ctx = Context()
    component = WAMPComponent(ssl='default', serializer='default')
    ssl_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
    serializer = JSONSerializer()
    ctx.publish_resource(serializer, types=[Serializer])
    ctx.publish_resource(ssl_context)
    await component.start(ctx)

    assert isinstance(ctx.wamp, WAMPClient)
    assert ctx.wamp.ssl is ssl_context
    assert ctx.wamp.serializer is serializer
Ejemplo n.º 17
0
    def test_run_return_invalid_type(self, event_loop):
        class DummyCLIComponent(CLIApplicationComponent):
            async def run(self, ctx: Context):
                return 'foo'

        component = DummyCLIComponent()
        event_loop.run_until_complete(component.start(Context()))
        with pytest.warns(UserWarning) as record:
            exc = pytest.raises(SystemExit, event_loop.run_forever)

        assert exc.value.code == 1
        assert len(record) == 1
        assert str(record[0].message) == 'run() must return an integer or None, not str'
Ejemplo n.º 18
0
    def test_run_return_invalid_value(self, event_loop):
        class DummyCLIComponent(CLIApplicationComponent):
            async def run(self, ctx: Context):
                return 128

        component = DummyCLIComponent()
        event_loop.run_until_complete(component.start(Context()))
        with pytest.warns(UserWarning) as record:
            exc = pytest.raises(SystemExit, event_loop.run_forever)

        assert exc.value.code == 1
        assert len(record) == 1
        assert str(record[0].message) == 'exit code out of range: 128'
Ejemplo n.º 19
0
def test_event_context():
    parent = Context()
    session_details = SessionDetails('default', 5)
    event_details = EventDetails(publication=15, publisher=8, publisher_authid='user',
                                 publisher_authrole='role', topic='topic')
    context = EventContext(parent, session_details, event_details)

    assert context.session_id == 5
    assert context.publisher_session_id == 8
    assert context.publisher_auth_id == 'user'
    assert context.publisher_auth_role == 'role'
    assert context.publication_id == 15
    assert context.topic == 'topic'
    assert context.enc_algo is None
Ejemplo n.º 20
0
async def test_component_start():
    component = SerializationComponent(serializers={
        'json': {'encoder_options': {'allow_nan': False}},
        'msgpack': {'unpacker_options': {'encoding': 'iso-8859-1'}},
        'pickle': {'protocol': 3},
        'yaml': {'safe': False}
    })
    async with Context() as ctx:
        await component.start(ctx)

        assert ctx.json
        assert ctx.msgpack
        assert ctx.pickle
        assert ctx.yaml
Ejemplo n.º 21
0
async def test_null_configs():
    component = SerializationComponent(serializers={
        'json': None,
        'msgpack': None,
        'pickle': None,
        'yaml': None
    })
    async with Context() as ctx:
        await component.start(ctx)

        assert ctx.json
        assert ctx.msgpack
        assert ctx.pickle
        assert ctx.yaml
async def test_close_twice_async(asyncpg_url):
    """Test that closing a session releases connection resources, but remains usable."""
    component = SQLAlchemyComponent(url=asyncpg_url)
    async with Context() as ctx:
        await component.start(ctx)
        session = ctx.require_resource(AsyncSession)
        pool = session.bind.pool
        await session.execute(text("SELECT 1"))
        assert pool.checkedout() == 1
        await session.close()
        assert pool.checkedout() == 0
        await session.execute(text("SELECT 1"))
        assert pool.checkedout() == 1

    assert pool.checkedout() == 0
async def test_component_single(caplog, backend):
    caplog.set_level(logging.INFO, logger='asphalt.mailer.component')
    component = MailerComponent(backend=backend)
    async with Context() as ctx:
        await component.start(ctx)
        assert isinstance(ctx.mailer, Mailer)

    records = [
        record for record in caplog.records
        if record.name == 'asphalt.mailer.component'
    ]
    records.sort(key=lambda r: r.message)
    assert len(records) == 2
    assert records[0].message.startswith(
        "Configured mailer (default / ctx.mailer; class=%s)" %
        ctx.mailer.__class__.__name__)
    assert records[1].message.startswith('Mailer (default) stopped')
Ejemplo n.º 24
0
    async def test_context_teardown(self, expected_exc):
        @context_teardown
        async def start(ctx: Context):
            nonlocal phase, received_exception
            phase = 'started'
            exc = 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
Ejemplo n.º 25
0
def test_call_context():
    def progress(arg):
        pass

    parent = Context()
    session_details = SessionDetails('default', 5)
    call_details = CallDetails(progress=progress, caller=8, caller_authid='user',
                               caller_authrole='role', procedure='procedurename')
    context = CallContext(parent, session_details, call_details)

    assert context.session_id == 5
    assert context.caller_session_id == 8
    assert context.caller_auth_id == 'user'
    assert context.caller_auth_role == 'role'
    assert context.procedure == 'procedurename'
    assert context.enc_algo is None
    assert context.progress is progress
Ejemplo n.º 26
0
async def test_single_renderer():
    async with Context() as ctx:
        ctx.add_resource("åäö")
        component = TemplatingComponent(backend="jinja2",
                                        options={"package_name": "tests"})
        await component.start(ctx)

        renderer = ctx.require_resource(TemplateRenderer)
        assert isinstance(renderer, TemplateRendererProxy)

        assert type(renderer.environment).__name__ == "Environment"
        assert (renderer.render("jinja2_context.html", str=str) == """\
<div>
    This is a sample
    Test variable: åäö
</div>""")
        assert (renderer.render_string(
            "This is testvar: {{ ctx.require_resource(str) }}",
            str=str) == "This is testvar: åäö")
Ejemplo n.º 27
0
async def test_finish_commit(raise_exception, executor, commit_executor, tmpdir):
    """
    Tests that the session is automatically committed if and only if the context was not exited
    with an exception.

    """
    db_path = tmpdir.join('test.db')
    engine = create_engine('sqlite:///%s' % db_path, poolclass=NullPool)
    engine.execute('CREATE TABLE foo (id INTEGER PRIMARY KEY)')

    component = SQLAlchemyComponent(
        url={'drivername': 'sqlite', 'database': str(db_path)},
        commit_executor=executor if commit_executor == 'instance' else commit_executor)
    ctx = Context()
    ctx.add_resource(executor, types=[Executor])
    await component.start(ctx)
    ctx.sql.execute('INSERT INTO foo (id) VALUES(3)')
    await ctx.close(Exception('dummy') if raise_exception else None)

    rows = engine.execute('SELECT * FROM foo').fetchall()
    assert len(rows) == (0 if raise_exception else 1)
Ejemplo n.º 28
0
async def test_ready_callback(asynchronous):
    def ready_callback(engine, factory):
        nonlocal engine2, factory2
        engine2 = engine
        factory2 = factory

    async def ready_callback_async(engine, factory):
        nonlocal engine2, factory2
        engine2 = engine
        factory2 = factory

    engine2 = factory2 = None
    callback = ready_callback_async if asynchronous else ready_callback
    component = SQLAlchemyComponent(url='sqlite:///:memory:', ready_callback=callback)
    async with Context() as ctx:
        await component.start(ctx)

        engine = ctx.require_resource(Engine)
        factory = ctx.require_resource(sessionmaker)
        assert engine is engine2
        assert factory is factory2
async def test_session_event_async(request, asyncpg_url, psycopg2_url):
    """Test that creating a session in a context does not leak memory."""
    listener_session: Session
    listener_thread: Thread

    def listener(session: Session) -> None:
        nonlocal listener_session, listener_thread
        try:
            async_session = get_resource(AsyncSession)
        except NoCurrentContext:
            return

        if async_session and session is async_session.sync_session:
            listener_session = session
            listener_thread = current_thread()

    listen(Session, "before_commit", listener)
    request.addfinalizer(lambda: remove(Session, "before_commit", listener))
    component = SQLAlchemyComponent(url=asyncpg_url)
    async with Context() as ctx:
        await component.start(ctx)
        dbsession = ctx.require_resource(AsyncSession)
        await dbsession.run_sync(
            lambda session: Person.metadata.create_all(session.bind))
        dbsession.add(Person(name="Test person"))

    assert listener_session is dbsession.sync_session
    assert listener_thread is current_thread()

    engine = create_engine(psycopg2_url)
    with Session(engine) as sess:
        sess.add(Person(name="Test person 2"))
        sess.commit()

    engine.dispose()

    assert listener_session is dbsession.sync_session
    assert listener_thread is current_thread()
async def test_session_event_sync(psycopg2_url):
    """Test that creating a session in a context does not leak memory."""
    listener_session: Session
    listener_thread: Thread

    def listener(session: Session) -> None:
        nonlocal listener_session, listener_thread
        current_context()
        listener_session = session
        listener_thread = current_thread()

    component = SQLAlchemyComponent(url=psycopg2_url)
    async with Context() as ctx:
        await component.start(ctx)
        Person.metadata.create_all(ctx.require_resource(Engine))
        session_factory = ctx.require_resource(sessionmaker)
        listen(session_factory, "before_commit", listener)

        dbsession = ctx.require_resource(Session)
        dbsession.add(Person(name="Test person"))

    assert listener_session is dbsession
    assert listener_thread != current_thread()