Example #1
0
async def test_stream_paging():

    output = TeeQueue()
    convo = IterStreamEvents("my-stream")
    response = proto.ReadStreamEventsCompleted()
    response.result = msg.ReadEventResult.Success
    response.next_event_number = 10
    response.last_event_number = 9
    response.is_end_of_stream = False
    response.last_commit_position = 8

    await convo.respond_to(
        msg.InboundMessage(
            uuid4(),
            msg.TcpCommand.ReadStreamEventsForwardCompleted,
            response.SerializeToString(),
        ),
        output,
    )

    reply = await output.get()
    body = proto.ReadStreamEvents()
    body.ParseFromString(reply.payload)

    assert body.from_event_number == 10
Example #2
0
async def test_stream_not_found():

    output = TeeQueue()
    convo = IterStreamEvents("my-stream")
    response = proto.ReadStreamEventsCompleted()
    response.result = msg.ReadStreamResult.NoStream
    response.is_end_of_stream = False
    response.next_event_number = 0
    response.last_event_number = 0
    response.last_commit_position = 0

    await convo.respond_to(
        msg.InboundMessage(
            uuid4(),
            msg.TcpCommand.ReadStreamEventsForwardCompleted,
            response.SerializeToString(),
        ),
        output,
    )

    with pytest.raises(exceptions.StreamNotFound) as exn:
        await convo.result
        assert exn.stream == "my-stream"
        assert exn.conversation_id == convo.conversation_id

    assert not output.items
async def test_end_of_stream():

    output = TeeQueue()
    event_1_id = uuid4()
    event_2_id = uuid4()

    convo = IterStreamEvents("my-stream")
    response = proto.ReadStreamEventsCompleted()
    response.result = msg.ReadEventResult.Success
    response.next_event_number = 10
    response.last_event_number = 9
    response.is_end_of_stream = True
    response.last_commit_position = 8

    event_1 = proto.ResolvedIndexedEvent()
    event_1.event.event_stream_id = "stream-123"
    event_1.event.event_number = 32
    event_1.event.event_id = event_1_id.bytes_le
    event_1.event.event_type = "event-type"
    event_1.event.data_content_type = msg.ContentType.Json
    event_1.event.metadata_content_type = msg.ContentType.Binary
    event_1.event.data = """
    {
        'color': 'red',
        'winner': true
    }
    """.encode(
        "UTF-8"
    )

    event_2 = proto.ResolvedIndexedEvent()
    event_2.CopyFrom(event_1)
    event_2.event.event_type = "event-2-type"
    event_2.event.event_id = event_2_id.bytes_le
    event_2.event.event_number = 33

    response.events.extend([event_1, event_2])

    await convo.respond_to(
        msg.InboundMessage(
            uuid4(), msg.TcpCommand.ReadEventCompleted, response.SerializeToString()
        ),
        output,
    )

    # Todo: Use a slice here so that we can give information
    # to the iterator about its position in the stream?
    # assert isinstance(reply.result, msg.StreamSlice)

    result = await convo.result
    [event_1, event_2] = [e async for e in result]
    assert event_1.event.stream == "stream-123"
    assert event_1.event.id == event_1_id
    assert event_1.event.type == "event-type"
    assert event_1.event.event_number == 32

    assert event_2.event.stream == "stream-123"
    assert event_2.event.id == event_2_id
    assert event_2.event.type == "event-2-type"
    assert event_2.event.event_number == 33
Example #4
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)
Example #5
0
async def test_reconnect_at_last_event_number():

    output = TeeQueue()
    event_1_id = uuid4()
    event_2_id = uuid4()
    convo = IterStreamEvents("my-stream", from_event=32)
    await convo.start(output)

    response = proto.ReadStreamEventsCompleted()
    response.result = msg.ReadEventResult.Success
    response.next_event_number = 32
    response.last_event_number = 31
    response.is_end_of_stream = False
    response.last_commit_position = 31

    event_1 = proto.ResolvedIndexedEvent()
    event_1.event.event_stream_id = "stream-123"
    event_1.event.event_number = 32
    event_1.event.event_id = event_1_id.bytes_le
    event_1.event.event_type = "event-type"
    event_1.event.data_content_type = msg.ContentType.Json
    event_1.event.metadata_content_type = msg.ContentType.Binary
    event_1.event.data = """
    {
        'color': 'red',
        'winner': true
    }
    """.encode("UTF-8")

    event_2 = proto.ResolvedIndexedEvent()
    event_2.CopyFrom(event_1)
    event_2.event.event_type = "event-2-type"
    event_2.event.event_id = event_2_id.bytes_le
    event_2.event.event_number = 33

    response.events.extend([event_1, event_2])

    await convo.respond_to(
        msg.InboundMessage(
            uuid4(),
            msg.TcpCommand.ReadStreamEventsForwardCompleted,
            response.SerializeToString(),
        ),
        output,
    )

    await convo.start(output)
    request = output.items[-1]

    body = proto.ReadStreamEvents()
    body.ParseFromString(request.payload)

    assert request.command is msg.TcpCommand.ReadStreamEventsForward
    assert body.event_stream_id == "my-stream"
    assert body.from_event_number == 33
    assert body.resolve_link_tos is True
    assert body.require_master is False
    assert body.max_count == 100
Example #6
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)
Example #7
0
async def test_read_stream_request():

    output = TeeQueue()
    convo = IterStreamEvents("my-stream")
    await convo.start(output)
    request = await output.get()

    body = proto.ReadStreamEvents()
    body.ParseFromString(request.payload)

    assert request.command is msg.TcpCommand.ReadStreamEventsForward
    assert body.event_stream_id == "my-stream"
    assert body.from_event_number == 0
    assert body.resolve_link_tos is True
    assert body.require_master is False
    assert body.max_count == 100
Example #8
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)
Example #9
0
async def test_error_mid_stream():

    output = TeeQueue()
    convo = IterStreamEvents("my-stream")
    response = proto.ReadStreamEventsCompleted()
    response.result = msg.ReadStreamResult.Success
    response.is_end_of_stream = False
    response.next_event_number = 0
    response.last_event_number = 0
    response.last_commit_position = 0

    await convo.respond_to(
        msg.InboundMessage(
            uuid4(),
            msg.TcpCommand.ReadStreamEventsForwardCompleted,
            response.SerializeToString(),
        ),
        output,
    )

    response.result = msg.ReadStreamResult.AccessDenied

    await convo.respond_to(
        msg.InboundMessage(
            uuid4(),
            msg.TcpCommand.ReadStreamEventsForwardCompleted,
            response.SerializeToString(),
        ),
        output,
    )

    iterator = await convo.result
    with pytest.raises(exceptions.AccessDenied) as exn:
        await iterator.anext()

        assert exn.conversation_id == convo.conversation_id
        assert exn.conversation_type == "IterStreamEvents"

    assert len(output.items) == 1