async def test_completing_write_events_twice(): output = Queue() conversation = given_a_write_events_message() await conversation.start(output) await output.get() payload = proto.WriteEventsCompleted() payload.result = msg.OperationResult.Success payload.first_event_number = 73 payload.last_event_number = 73 await conversation.respond_to( msg.InboundMessage( conversation.conversation_id, msg.TcpCommand.WriteEventsCompleted, payload.SerializeToString(), ), output, ) with pytest.raises(asyncio.base_futures.InvalidStateError) as exn: await conversation.respond_to( msg.InboundMessage( conversation.conversation_id, msg.TcpCommand.WriteEventsCompleted, payload.SerializeToString(), ), output, )
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
async def test_one_event_response(): output = Queue() conversation = given_a_write_events_message() await conversation.start(output) await output.get() payload = proto.WriteEventsCompleted() payload.result = msg.OperationResult.Success payload.first_event_number = 73 payload.last_event_number = 73 await conversation.respond_to( msg.InboundMessage( conversation.conversation_id, msg.TcpCommand.WriteEventsCompleted, payload.SerializeToString(), ), output, ) result = await conversation.result assert result.first_event_number == 73 assert result.last_event_number == 73 assert result.result == msg.OperationResult.Success assert conversation.is_complete
async def test_stream_paging(): output = TeeQueue() convo = IterAllEvents() response = proto.ReadAllEventsCompleted() response.result = msg.ReadEventResult.Success response.commit_position = 10 response.prepare_position = 10 response.next_commit_position = 11 response.next_prepare_position = 12 await convo.respond_to( msg.InboundMessage( uuid4(), msg.TcpCommand.ReadStreamEventsForwardCompleted, response.SerializeToString(), ), output, ) reply = await output.get() body = proto.ReadAllEvents() body.ParseFromString(reply.payload) assert body.commit_position == 11 assert body.prepare_position == 12
async def test_read_single_event_success(): event_id = uuid4() convo = ReadEvent("my-stream", 23) response = proto.ReadEventCompleted() response.result = msg.ReadEventResult.Success response.event.event.event_stream_id = "stream-123" response.event.event.event_number = 32 response.event.event.event_id = event_id.bytes_le response.event.event.event_type = "event-type" response.event.event.data_content_type = msg.ContentType.Json response.event.event.metadata_content_type = msg.ContentType.Binary response.event.event.data = """ { 'color': 'red', 'winner': true } """.encode("UTF-8") await convo.respond_to( msg.InboundMessage(uuid4(), msg.TcpCommand.ReadEventCompleted, response.SerializeToString()), None, ) result = await convo.result assert result.event.stream == "stream-123" assert result.event.id == event_id assert result.event.type == "event-type" assert result.event.event_number == 32 assert result.link is None
async def test_not_authenticated(): output = TeeQueue() event_id = uuid4() conversation_id = uuid4() error_message = "Dude, like who even are you?" event_type = "pony_jumped" data = {"pony_name": "Burning Sulphur", "distance": 6} event_data = msg.NewEventData(event_id, event_type, data, None) conversation = WriteEvents("my-stream", [event_data], conversation_id=conversation_id) await conversation.start(output) await conversation.respond_to( msg.InboundMessage( conversation_id, msg.TcpCommand.NotAuthenticated, error_message.encode("UTF-8"), ), output, ) with pytest.raises(exn.NotAuthenticated) as exc: await conversation.result assert exc.message == error_message
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
async def test_bad_request(): output = TeeQueue() event_id = uuid4() conversation_id = uuid4() error_message = "That's not an acceptable message, man" event_type = "pony_jumped" data = {"pony_name": "Burning Sulphur", "distance": 6} event_data = msg.NewEventData(event_id, event_type, data, None) conversation = WriteEvents("my-stream", [event_data], conversation_id=conversation_id) await conversation.start(output) await conversation.respond_to( msg.InboundMessage(conversation_id, msg.TcpCommand.BadRequest, error_message.encode("UTF-8")), output, ) with pytest.raises(exn.BadRequest) as exc: await conversation.result assert exc.message == error_message
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
async def test_read_stream_success(): event_1_id = uuid4() event_2_id = uuid4() convo = ReadStreamEvents("my-stream", 0) 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()), None, ) result = await convo.result assert isinstance(result, msg.StreamSlice) [event_1, event_2] = result.events 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
def read_stream_events_failure(conversation_id, result): payload = proto.ReadStreamEventsCompleted() payload.result = result payload.last_event_number = 1 payload.next_event_number = 1 payload.last_commit_position = 1 payload.is_end_of_stream = False return msg.InboundMessage( conversation_id, msg.TcpCommand.ReadStreamEventsForwardCompleted, payload.SerializeToString(), )
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
async def test_access_denied(): convo = ReadEvent("my-stream", 23) await convo.respond_to( msg.InboundMessage( uuid4(), msg.TcpCommand.ReadEventCompleted, error_result(msg.ReadEventResult.AccessDenied), ), None, ) with pytest.raises(exceptions.AccessDenied) as exn: await convo.result assert exn.conversation_type == "ReadEvent"
async def test_read_error(): convo = ReadEvent("my-stream", 23) await convo.respond_to( msg.InboundMessage( uuid4(), msg.TcpCommand.ReadEventCompleted, error_result(msg.ReadEventResult.Error), ), None, ) with pytest.raises(exceptions.ReadError) as exn: await convo.result assert exn.stream == "my-stream"
async def drop_subscription( convo, output, reason=msg.SubscriptionDropReason.Unsubscribed ): response = proto.SubscriptionDropped() response.reason = reason await convo.respond_to( msg.InboundMessage( uuid.uuid4(), msg.TcpCommand.SubscriptionDropped, response.SerializeToString(), ), output, )
async def test_event_not_found(): convo = ReadEvent("my-stream", 23) await convo.respond_to( msg.InboundMessage( uuid4(), msg.TcpCommand.ReadEventCompleted, error_result(msg.ReadEventResult.NotFound), ), None, ) with pytest.raises(exceptions.EventNotFound) as exn: await convo.result assert exn.stream == "my-stream" assert exn.event_number == 23
async def test_not_master(): output = TeeQueue() payload = proto.NotHandled() payload.reason = msg.NotHandledReason.NotMaster conversation = Ping() await conversation.respond_to( msg.InboundMessage(uuid4(), msg.TcpCommand.NotHandled, payload.SerializeToString()), output, ) with pytest.raises(exn.NotMaster) as exc: await conversation.result assert exc.conversation_id == conversation.conversation_id
async def confirm_subscription(convo, output_queue=None, event_number=1, commit_pos=1): response = proto.SubscriptionConfirmation() response.last_event_number = event_number response.last_commit_position = commit_pos await convo.respond_to( msg.InboundMessage( uuid.uuid4(), msg.TcpCommand.SubscriptionConfirmation, response.SerializeToString(), ), output_queue, ) return await convo.result
async def test_decode_error(): """ If we give the conversation an invalid payload, it should raise PayloadUnreadable. """ output = TeeQueue() conversation = Ping() await conversation.respond_to( msg.InboundMessage(uuid4(), msg.TcpCommand.NotHandled, b"\x08\2A"), output) with pytest.raises(exn.PayloadUnreadable) as exc: await conversation.result assert exc.conversation_id == conversation.conversation_id assert conversation.is_complete
async def test_notready_message(): output = TeeQueue() payload = proto.NotHandled() payload.reason = msg.NotHandledReason.NotReady conversation = Ping() with pytest.raises(exn.NotReady): await conversation.respond_to( msg.InboundMessage(uuid4(), msg.TcpCommand.NotHandled, payload.SerializeToString()), output, ) await conversation.result assert conversation.is_complete
async def test_event_exposes_event_record(): """ See github.com/madedotcom/photon-pump/issues/42 Briefly, the Event type should expose the properties of its wrapped EventRecord to make things more usable. """ event_id = uuid4() convo = ReadEvent("my-stream", 23) response = proto.ReadEventCompleted() response.result = msg.ReadEventResult.Success response.event.event.event_stream_id = "stream-123" response.event.event.event_number = 32 response.event.event.event_id = event_id.bytes_le response.event.event.event_type = "event-type" response.event.event.data_content_type = msg.ContentType.Json response.event.event.metadata_content_type = msg.ContentType.Binary response.event.event.data = """ { 'color': 'red', 'winner': true } """.encode("UTF-8") await convo.respond_to( msg.InboundMessage(uuid4(), msg.TcpCommand.ReadEventCompleted, response.SerializeToString()), None, ) result = await convo.result assert result.stream == "stream-123" assert result.id == event_id assert result.type == "event-type" assert result.event_number == 32 assert result.link is None
async def test_all_events_access_denied(): convo = ReadAllEvents() response = proto.ReadAllEventsCompleted() response.result = msg.ReadAllResult.AccessDenied response.next_commit_position = 10 response.next_prepare_position = 10 response.commit_position = 9 response.prepare_position = 9 await convo.respond_to( msg.InboundMessage(uuid4(), msg.TcpCommand.ReadAllEventsForward, response.SerializeToString()), None, ) with pytest.raises(exceptions.AccessDenied) as exn: await convo.result assert exn.conversation_id == convo.conversation_id assert exn.conversation_type == "ReadAllEvents"
def subscription_event_appeared( conversation_id: UUID, event: msg.NewEventData, stream: str = "my-stream", event_number: int = 1, ): payload = proto.PersistentSubscriptionStreamEventAppeared() e = payload.event.event e.event_stream_id = stream e.event_number = event_number e.event_id = event.id.bytes_le e.event_type = event.type e.data_content_type = msg.ContentType.Json e.metadata_content_type = msg.ContentType.Binary e.data = json.dumps(event.data).encode("UTF-8") return msg.InboundMessage( conversation_id, msg.TcpCommand.PersistentSubscriptionStreamEventAppeared, payload.SerializeToString(), )
async def test_stream_deleted(): convo = ReadStreamEvents("my-stream") response = proto.ReadStreamEventsCompleted() response.result = msg.ReadStreamResult.StreamDeleted 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(), ), None, ) with pytest.raises(exceptions.StreamDeleted) as exn: await convo.result assert exn.stream == "my-stream" assert exn.conversation_id == convo.conversation_id
def read_stream_events_completed(conversation_id, stream, events, end_of_stream=False): payload = proto.ReadStreamEventsCompleted() payload.result = msg.ReadStreamResult.Success payload.is_end_of_stream = end_of_stream for (idx, event) in enumerate(events): payload.next_event_number = idx + 1 payload.last_event_number = idx payload.last_commit_position = idx e = payload.events.add() e.event.event_stream_id = stream e.event.event_number = idx e.event.event_id = event.id.bytes_le e.event.event_type = event.type e.event.data_content_type = msg.ContentType.Json e.event.metadata_content_type = msg.ContentType.Binary e.event.data = json.dumps(event.data).encode("UTF-8") return msg.InboundMessage( conversation_id, msg.TcpCommand.ReadStreamEventsForwardCompleted, payload.SerializeToString(), )
async def test_read_error(): convo = ReadStreamEvents("my-stream") response = proto.ReadStreamEventsCompleted() response.result = msg.ReadStreamResult.Error response.is_end_of_stream = False response.next_event_number = 0 response.last_event_number = 0 response.last_commit_position = 0 response.error = "Something really weird just happened" await convo.respond_to( msg.InboundMessage( uuid4(), msg.TcpCommand.ReadStreamEventsForwardCompleted, response.SerializeToString(), ), None, ) with pytest.raises(exceptions.ReadError) as exn: await convo.result assert exn.stream == "my-stream" assert exn.conversation_id == convo.conversation_id
async def test_all_events_error(): convo = ReadAllEvents() response = proto.ReadAllEventsCompleted() response.result = msg.ReadAllResult.Error response.next_commit_position = 10 response.next_prepare_position = 10 response.commit_position = 9 response.prepare_position = 9 response.error = "Something really weird just happened" await convo.respond_to( msg.InboundMessage( uuid4(), msg.TcpCommand.ReadAllEventsForwardCompleted, response.SerializeToString(), ), None, ) with pytest.raises(exceptions.ReadError) as exn: await convo.result assert exn.stream == "$all" assert exn.conversation_id == convo.conversation_id
async def test_read_all_success(): event_1_id = uuid4() event_2_id = uuid4() convo = ReadAllEvents() response = proto.ReadAllEventsCompleted() response.result = msg.ReadEventResult.Success response.next_commit_position = 10 response.next_prepare_position = 10 response.commit_position = 9 response.prepare_position = 9 event_1 = proto.ResolvedEvent() event_1.commit_position = 8 event_1.prepare_position = 8 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.ResolvedEvent() event_2.CopyFrom(event_1) event_2.event.event_stream_id = "stream-456" event_2.event.event_type = "event-2-type" event_2.event.event_id = event_2_id.bytes_le event_2.event.event_number = 32 response.events.extend([event_1, event_2]) await convo.respond_to( msg.InboundMessage( uuid4(), msg.TcpCommand.ReadAllEventsForwardCompleted, response.SerializeToString(), ), None, ) result = await convo.result assert isinstance(result, msg.AllStreamSlice) [event_1, event_2] = result.events assert event_1.stream == "stream-123" assert event_1.id == event_1_id assert event_1.type == "event-type" assert event_1.event_number == 32 assert event_2.stream == "stream-456" assert event_2.id == event_2_id assert event_2.type == "event-2-type" assert event_2.event_number == 32
async def reply_to(convo, message, output): command, payload = message await convo.respond_to(msg.InboundMessage(uuid.uuid4(), command, payload), output)