예제 #1
0
def test_layabout_can_connect_to_slack_with_existing_slack_client():
    """
    Test that layabout can use an existing SlackClient as a connector.
    """
    layabout = Layabout()
    _, slack = mock_slack(connections=(True, ))

    layabout.run(connector=slack, until=lambda e: False)
예제 #2
0
def test_layabout_raises_type_error_with_string_connector():
    layabout = Layabout()

    with pytest.raises(TypeError) as exc:
        layabout.run(connector='', until=lambda e: False)

    assert str(exc.value) == ('Use layabout.Token or layabout.EnvVar '
                              'instead of str')
예제 #3
0
def test_layabout_raises_missing_token_with_empty_token():
    """
    Test that layabout raises a MissingToken if given an empty Slack API token.
    """
    layabout = Layabout()

    with pytest.raises(MissingToken) as exc:
        # until will exit early here just to be safe.
        layabout.run(connector=Token(''), until=lambda e: False)

    assert str(exc.value) == 'The empty string is an invalid Slack API token'
예제 #4
0
def test_layabout_can_register_handler():
    """
    Test that layabout can register Slack API event handlers normally.
    """
    layabout = Layabout()

    def handle_hello(slack, event):
        pass

    layabout.handle(type='hello')(fn=handle_hello)

    assert len(layabout._handlers) == 1
예제 #5
0
def test_layabout_raises_type_error_with_invalid_connector():
    """
    Test that layabout cannot connect to Slack with a pineapple.
    """
    layabout = Layabout()

    class Pineapple:
        pass

    with pytest.raises(TypeError):
        # Turns out you can't connect to Slack with a Pineapple.
        layabout.run(connector=Pineapple)
예제 #6
0
def test_layabout_can_connect_to_slack_with_token(monkeypatch):
    """
    Test that layabout can connect to the Slack API when given a valid Slack
    API token.
    """
    layabout = Layabout()
    SlackClient, slack = mock_slack(connections=(True, ))
    monkeypatch.setattr('layabout.SlackClient', SlackClient)

    layabout.run(connector=TOKEN, until=lambda e: False)

    # Verify we instantiated a SlackClient with the given token.
    SlackClient.assert_called_with(token=TOKEN)
예제 #7
0
def test_layabout_can_register_handler_with_kwargs():
    """
    Test that layabout can register Slack API event handlers with keyword
    arguments normally.
    """
    layabout = Layabout()
    kwargs = dict(spam='🍳')

    def handle_hello(slack, event):
        pass

    layabout.handle(type='hello', kwargs=kwargs)(fn=handle_hello)

    assert len(layabout._handlers) == 1
예제 #8
0
def test_layabout_raises_missing_token_without_token(monkeypatch):
    """
    Test that layabout raises a MissingToken if there is no Slack API token for
    it to use.
    """
    environ = dict()
    layabout = Layabout()

    monkeypatch.setattr(os, 'environ', environ)

    with pytest.raises(MissingToken) as exc:
        # until will exit early here just to be safe.
        layabout.run(until=lambda e: False)

    assert str(exc.value) == 'Could not acquire token from LAYABOUT_TOKEN'
예제 #9
0
def test_layabout_raises_type_error_with_invalid_handler():
    """
    Test that layabout raises a TypeError if an event handler that doesn't meet
    the minimum criteria to be called correctly is registered.
    """
    layabout = Layabout()

    def invalid_handler():
        pass

    with pytest.raises(TypeError) as exc:
        layabout.handle(type='hello')(fn=invalid_handler)

    expected = ("invalid_handler() missing 2 required positional arguments: "
                "'slack' and 'event'")
    assert str(exc.value) == expected
예제 #10
0
def test_layabout_can_continue_after_successful_reconnection(monkeypatch):
    """
    Test that layabout can continue to handle events after successfully
    reconnecting to the Slack API.
    """
    layabout = Layabout()
    # Succeed with the first connection and the subsequent reconnection.
    connections = (True, True)
    # Raise an exception on the first read and return empty events next.
    reads = (TimeoutError, [])
    SlackClient, _ = mock_slack(connections=connections, reads=reads)

    monkeypatch.setattr('layabout.SlackClient', SlackClient)

    layabout.run(connector=TOKEN,
                 retries=1,
                 backoff=lambda r: 0,
                 until=lambda e: False)
예제 #11
0
def test_layabout_can_connect_to_slack_with_env_var(monkeypatch):
    """
    Test that layabout can discover and use a Slack API token from an
    environment variable when not given one directly.
    """
    env_var = EnvVar('_TEST_LAYABOUT_TOKEN')
    environ = {env_var: TOKEN}
    layabout = Layabout()
    SlackClient, slack = mock_slack(connections=(True, ))

    monkeypatch.setattr(os, 'environ', environ)
    monkeypatch.setattr('layabout.SlackClient', SlackClient)

    # Purposefully don't provide a connector so we have to use an env var.
    layabout.run(connector=env_var, until=lambda e: False)

    # Verify we instantiated a SlackClient with the given token and used it to
    # connect to the Slack API.
    SlackClient.assert_called_with(token=TOKEN)
예제 #12
0
def test_layabout_can_register_handler_via_decorator():
    """
    Test that layabout can register Slack API event handlers via decorator.
    """
    layabout = Layabout()

    @layabout.handle('hello')
    def handle_hello(slack, event):
        pass

    assert len(layabout._handlers) == 1
예제 #13
0
def test_layabout_can_register_handler_with_kwargs_via_decorator():
    """
    Test that layabout can register Slack API event handlers with keyword
    arguments via decorator.
    """
    layabout = Layabout()
    kwargs = dict(spam='🍳')

    @layabout.handle('hello', kwargs=kwargs)
    def handle_hello(slack, event, spam):
        pass

    assert len(layabout._handlers) == 1
예제 #14
0
def test_layabout_can_reuse_an_existing_client_to_reconnect(monkeypatch):
    """
    Test that layabout can reuse an existing SlackClient instance to reconnect
    to the Slack API rather than needlessly instantiating a new one on each
    reconnection attempt.
    """
    layabout = Layabout()
    # Fail initial connection, fail reconnection, succeed at last.
    connections = (False, False, True)
    SlackClient, _ = mock_slack(connections=connections)

    monkeypatch.setattr('layabout.SlackClient', SlackClient)

    # Retry connecting twice to verify reconnection logic was evaluated.
    # until will exit early just to be safe.
    layabout.run(connector=TOKEN,
                 retries=2,
                 backoff=lambda r: 0,
                 until=lambda e: False)

    # Make sure the SlackClient was only instantiated once so we know that we
    # reused the existing instance.
    SlackClient.assert_called_once_with(token=TOKEN)
예제 #15
0
def test_layabout_handlers_can_be_decorated_and_used_normally():
    """
    Test that layabout can decorate event handlers that can still be used as
    though they were undecorated. Most importantly, that they can still return
    and that their docstrings are intact.
    """
    layabout = Layabout()
    cheese_shop = dict(shop='🧀')

    @layabout.handle('hello')
    def handle_hello(slack, event):
        """ This docstring must be preserved. """
        return cheese_shop

    assert handle_hello(None, None) == cheese_shop
    assert handle_hello.__doc__ == ' This docstring must be preserved. '
예제 #16
0
def test_layabout_raises_type_error_with_invalid_decorated_handler():
    """
    Test that layabout raises a TypeError if an event handler that doesn't meet
    the minimum criteria to be called correctly is registered via decorator.
    """
    layabout = Layabout()

    with pytest.raises(TypeError) as exc:

        @layabout.handle('hello')
        def invalid_handler(slack):
            pass

    expected = ("invalid_handler(slack) missing 1 required positional "
                "argument: 'event'")
    assert str(exc.value) == expected
예제 #17
0
def test_layabout_can_handle_multiple_events(events, run_once, monkeypatch):
    """
    Test that layabout calls all handlers for their respective events.
    """
    layabout = Layabout()
    SlackClient, slack = mock_slack(connections=(True, ), reads=(events, []))
    handle_hello, handle_goodbye = MagicMock(), MagicMock()

    monkeypatch.setattr('layabout.SlackClient', SlackClient)

    layabout.handle('hello')(handle_hello)
    layabout.handle('goodbye')(handle_goodbye)
    layabout.run(connector=TOKEN,
                 interval=0,
                 retries=0,
                 backoff=lambda r: 0,
                 until=run_once)

    handle_hello.assert_called_once_with(slack, events[0])
    handle_goodbye.assert_called_once_with(slack, events[1])
예제 #18
0
def test_layabout_can_handle_events_with_normal_and_splat_handlers(
        events, run_once, monkeypatch):
    """
    Test that layabout can handle an event with normal handlers as well as
    a splat handler.
    """
    layabout = Layabout()
    SlackClient, slack = mock_slack(connections=(True, ), reads=(events, []))
    handle_hello, splat = MagicMock(), MagicMock()

    monkeypatch.setattr('layabout.SlackClient', SlackClient)

    layabout.handle('hello')(handle_hello)
    layabout.handle('*')(splat)
    layabout.run(connector=TOKEN,
                 interval=0,
                 retries=0,
                 backoff=lambda r: 0,
                 until=run_once)

    handle_hello.assert_called_once_with(slack, events[0])
    splat.assert_has_calls([call(slack, e) for e in events])
예제 #19
0
def test_layabout_can_handle_an_event(events, run_once, monkeypatch):
    """
    Test that layabout can handle an event.
    """
    layabout = Layabout()
    SlackClient, slack = mock_slack(connections=(True, ), reads=(events, []))
    handle_hello = MagicMock()

    monkeypatch.setattr('layabout.SlackClient', SlackClient)

    layabout.handle('hello')(handle_hello)
    layabout.run(connector=TOKEN,
                 interval=0,
                 retries=0,
                 backoff=lambda r: 0,
                 until=run_once)

    handle_hello.assert_called_once_with(slack, events[0])
예제 #20
0
def test_layabout_can_handle_all_events_with_a_splat_handler(
        events, run_once, monkeypatch):
    """
    Test that layabout can handle all events with a splat handler.
    """
    layabout = Layabout()
    SlackClient, slack = mock_slack(connections=(True, ), reads=(events, []))
    splat = MagicMock()

    monkeypatch.setattr('layabout.SlackClient', SlackClient)

    layabout.handle('*')(splat)
    layabout.run(connector=TOKEN,
                 interval=0,
                 retries=0,
                 backoff=lambda r: 0,
                 until=run_once)

    splat.assert_has_calls([call(slack, e) for e in events])
예제 #21
0
def test_layabout_can_only_handle_events_that_happen(events, run_once,
                                                     monkeypatch):
    """
    Test that layabout only handles events that actually happen.
    """
    layabout = Layabout()
    SlackClient, _ = mock_slack(connections=(True, ), reads=(events, []))
    aint_happenin = MagicMock()

    monkeypatch.setattr('layabout.SlackClient', SlackClient)

    layabout.handle('this will never happen')(aint_happenin)
    layabout.run(connector=TOKEN,
                 interval=0,
                 retries=0,
                 backoff=lambda r: 0,
                 until=run_once)

    aint_happenin.assert_not_called()
예제 #22
0
def test_layabout_can_handle_an_event_with_kwargs(events, run_once,
                                                  monkeypatch):
    """
    Test that layabout can call an event handler that requires kwargs.
    """
    layabout = Layabout()
    SlackClient, slack = mock_slack(connections=(True, ), reads=(events, []))
    kwargs = dict(caerbannog='🐰')
    handle_hello = MagicMock()

    monkeypatch.setattr('layabout.SlackClient', SlackClient)

    layabout.handle('hello', kwargs=kwargs)(handle_hello)
    layabout.run(connector=TOKEN,
                 interval=0,
                 retries=0,
                 backoff=lambda r: 0,
                 until=run_once)

    handle_hello.assert_called_once_with(slack, events[0], **kwargs)
예제 #23
0
def test_layabout_can_handle_one_event_multiple_times(events, run_once,
                                                      monkeypatch):
    """
    Test that layabout calls all handlers for a given event.
    """
    layabout = Layabout()
    SlackClient, slack = mock_slack(connections=(True, ), reads=(events, []))
    handle_hello1, handle_hello2 = MagicMock(), MagicMock()

    monkeypatch.setattr('layabout.SlackClient', SlackClient)

    layabout.handle('hello')(handle_hello1)
    layabout.handle('hello')(handle_hello2)
    layabout.run(connector=TOKEN,
                 interval=0,
                 retries=0,
                 backoff=lambda r: 0,
                 until=run_once)

    handle_hello1.assert_called_once_with(slack, events[0])
    handle_hello2.assert_called_once_with(slack, events[0])
예제 #24
0
from pprint import pformat

from layabout import Layabout

app = Layabout()


def channel_to_id(slack, channel):
    """ Surely there's a better way to do this... """
    channels = slack.api_call('channels.list').get('channels') or []
    groups = slack.api_call('groups.list').get('groups') or []

    if not channels and not groups:
        raise RuntimeError("Couldn't get channels and groups.")

    ids = [c['id'] for c in channels + groups if c['name'] == channel]

    if not ids:
        raise ValueError(f"Couldn't find #{channel}")

    return ids[0]


def main():
    channel_name = input('Which channel would you like to debug? ')
    print(f"Debugging events in #{channel_name}. Press Ctrl-C to quit.")

    @app.handle('*')
    def debug(slack, event):
        """ Close over ``channel_name`` and debug only those events. """
        debug_channel = channel_to_id(slack, channel_name)
예제 #25
0
from pprint import pformat
from layabout import Layabout

app = Layabout()


@app.handle('*')
def print_all(slack, event):
    """ * is a special event type that allows you to handle all event types """
    print(f"Got event:\n{pformat(event)}\n")


# We don't need to pass anything to run. By default the API token will
# be fetched from the LAYABOUT_TOKEN environment variable.
if __name__ == "__main__":
    print('Printing all events. Press Ctrl-C to quit.\n')
    app.run()