Exemplo n.º 1
0
async def test_when_dispatching_stream_iterators():
    """
    When we're dealing with iterators, the first message should result
    in a StreamingIterator being returned to the caller; subsequent messages
    should push new events to the caller, and the final message should
    terminate iteration.
    """

    conversation = IterStreamEvents("my-stream")
    first_msg = read_stream_events_completed(
        conversation.conversation_id, "my-stream",
        [NewEvent("event", data={"x": 1})])
    second_msg = read_stream_events_completed(
        conversation.conversation_id,
        "my-stream",
        [NewEvent("event", data={"x": 2}),
         NewEvent("event", data={"x": 3})],
    )
    final_msg = read_stream_events_completed(
        conversation.conversation_id,
        "my-stream",
        [NewEvent("event", data={"x": 4})],
        end_of_stream=True,
    )

    dispatcher = MessageDispatcher()
    output_queue = TeeQueue()

    future = await dispatcher.start_conversation(conversation)

    # The first message should result in an iterator being returned to the caller
    # with one event. The conversation should still be active.
    await dispatcher.dispatch(first_msg, output_queue)
    iterator = await asyncio.wait_for(future, 1)

    e = await anext(iterator)
    assert e.event.json()["x"] == 1
    assert dispatcher.has_conversation(conversation.conversation_id)

    # The second message should result in two events on the iterator.
    # The conversation should still be active.
    await dispatcher.dispatch(second_msg, output_queue)

    e = await anext(iterator)
    assert e.event.json()["x"] == 2

    e = await anext(iterator)
    assert e.event.json()["x"] == 3
    assert dispatcher.has_conversation(conversation.conversation_id)

    # The final message should result in one event and the iterator terminating
    await dispatcher.dispatch(final_msg, output_queue)

    [e] = [e async for e in iterator]
    assert e.event.json()["x"] == 4
    assert not dispatcher.has_conversation(conversation.conversation_id)
Exemplo n.º 2
0
async def test_when_a_stream_iterator_fails_midway():
    """
    Sometimes bad things happen. What happens if we're iterating a stream and
    some anarchist decides to delete it from under our feet? Well, I guess we
    should return an error to the caller and stop the iterator.
    """
    dispatcher = MessageDispatcher()
    output_queue = TeeQueue()
    conversation = IterStreamEvents("my-stream")
    first_msg = read_stream_events_completed(
        conversation.conversation_id, "my-stream",
        [NewEvent("event", data={"x": 1})])
    second_msg = read_stream_events_failure(conversation.conversation_id,
                                            ReadStreamResult.StreamDeleted)

    future = await dispatcher.start_conversation(conversation)

    await dispatcher.dispatch(first_msg, output_queue)
    await dispatcher.dispatch(second_msg, output_queue)

    iterator = await asyncio.wait_for(future, 1)

    event = await anext(iterator)
    assert event.event.json()["x"] == 1

    with pytest.raises(StreamDeleted):
        await anext(iterator)

    assert not dispatcher.has_conversation(conversation.conversation_id)
Exemplo n.º 3
0
async def test_when_enqueuing_a_conversation():
    """
    When we enqueue a conversation, we should add the first message
    of the conversation to the outbound queue.
    """

    conversation = Ping()

    output = TeeQueue()
    dispatcher = MessageDispatcher()
    await dispatcher.write_to(output)

    await dispatcher.start_conversation(conversation)

    msg = await output.get()

    assert msg.command == TcpCommand.Ping
    assert dispatcher.has_conversation(conversation.conversation_id)
Exemplo n.º 4
0
async def test_when_a_stream_iterator_fails_at_the_first_hurdle():
    """
    If we receive an error in reply to the first message in an iterator
    conversation, then we ought to raise the error instead of returning
    an iterator.
    """
    dispatcher = MessageDispatcher()
    conversation = IterStreamEvents("my-stream")
    first_msg = read_stream_events_failure(conversation.conversation_id,
                                           ReadStreamResult.StreamDeleted)

    future = await dispatcher.start_conversation(conversation)

    await dispatcher.dispatch(first_msg, None)
    with pytest.raises(StreamDeleted):
        await asyncio.wait_for(future, 1)

    assert not dispatcher.has_conversation(conversation.conversation_id)
Exemplo n.º 5
0
async def test_when_the_conversation_raises_an_error():
    """
    If the conversation raises an error, that error should be returned
    to the caller.
    """
    out_queue = TeeQueue()
    dispatcher = MessageDispatcher()
    conversation = Ping()
    future = await dispatcher.start_conversation(conversation)

    await dispatcher.dispatch(
        InboundMessage(conversation.conversation_id,
                       TcpCommand.NotAuthenticated, bytes()),
        out_queue,
    )

    with pytest.raises(NotAuthenticated):
        await asyncio.wait_for(future, 1)

    assert not dispatcher.has_conversation(conversation.conversation_id)
Exemplo n.º 6
0
async def test_when_the_conversation_receives_an_unexpected_response():
    """
    If the conversation receives an unhandled response, an error should
    be returned to the caller. This is a separate code path for now, but
    should probably be cleaned up in the Conversation. Maybe use a decorator?
    """

    out_queue = TeeQueue()
    dispatcher = MessageDispatcher()
    conversation = Ping()
    future = await dispatcher.start_conversation(conversation)

    await dispatcher.dispatch(
        InboundMessage(conversation.conversation_id,
                       TcpCommand.WriteEventsCompleted, bytes()),
        out_queue,
    )

    with pytest.raises(PayloadUnreadable):
        await asyncio.wait_for(future, 1)

    assert not dispatcher.has_conversation(conversation.conversation_id)