async def test_use_new_output_when_reconnected(): first_output = TeeQueue() second_output = TeeQueue() convo = ConnectPersistentSubscription("my-subscription", "my-stream", max_in_flight=57) await confirm_subscription(convo, subscription_id=convo.name, queue=first_output) event_id = uuid4() subscription = await convo.result await confirm_subscription(convo, subscription_id=convo.name, queue=second_output) await subscription.ack(stub_event(event_id)) ack = await second_output.get() assert ack.command == TcpCommand.PersistentSubscriptionAckEvents assert ack.conversation_id == convo.conversation_id assert not first_output.items
async def test_dropped_on_connect(): convo = ConnectPersistentSubscription("my-subscription", "my-stream", max_in_flight=57) with pytest.raises(exn.SubscriptionCreationFailed): await drop_subscription(convo, SubscriptionDropReason.Unsubscribed) await convo.result
async def test_subscription_unsubscribed_midway(): convo = ConnectPersistentSubscription("my-subscription", "my-stream", max_in_flight=57) await confirm_subscription(convo) subscription = await convo.result await drop_subscription(convo, SubscriptionDropReason.Unsubscribed) with pytest.raises(StopAsyncIteration): await subscription.events.anext()
async def test_subscription_failed_midway(): convo = ConnectPersistentSubscription("my-subscription", "my-stream", max_in_flight=57) await confirm_subscription(convo) subscription = await convo.result await drop_subscription(convo, SubscriptionDropReason.AccessDenied) with pytest.raises(exn.SubscriptionFailed): await subscription.events.anext()
async def test_confirmation(): convo = ConnectPersistentSubscription("my-subscription", "my-stream", max_in_flight=57) await confirm_subscription(convo, subscription_id="my-subscription", event_number=10) subscription = convo.result.result() assert subscription.name == "my-subscription" assert subscription.stream == "my-stream" assert subscription.last_event_number == 10
async def test_connect_request(): output = TeeQueue() convo = ConnectPersistentSubscription("my-subscription", "my-stream", max_in_flight=57) await convo.start(output) [request] = output.items payload = proto.ConnectToPersistentSubscription() payload.ParseFromString(request.payload) assert request.command == TcpCommand.ConnectToPersistentSubscription assert payload.subscription_id == "my-subscription" assert payload.event_stream_id == "my-stream" assert payload.allowed_in_flight_messages == 57
async def test_when_connector_reconnected_retry_active_conversations(): """ if we give the dispatcher a new output queue, he should restart his active conversations. """ dispatcher = MessageDispatcher() out_queue = TeeQueue() conversation = ConnectPersistentSubscription("my-sub", "my-stream") await dispatcher.start_conversation(conversation) await dispatcher.write_to(out_queue) message = await out_queue.get() assert message.command == TcpCommand.ConnectToPersistentSubscription
async def test_when_running_a_persistent_subscription(): """ We ought to be able to connect a persistent subscription, and receive some events. """ dispatcher = MessageDispatcher() out_queue = TeeQueue() conversation = ConnectPersistentSubscription("my-sub", "my-stream") first_msg = persistent_subscription_confirmed(conversation.conversation_id, "my-sub") # The subscription confirmed message should result in our having a # PersistentSubscription to play with. future = await dispatcher.start_conversation(conversation) await dispatcher.dispatch(first_msg, out_queue) subscription = await asyncio.wait_for(future, 1) # A subsequent PersistentSubscriptionStreamEventAppeared message should # enqueue the event onto our iterator await dispatcher.dispatch( subscription_event_appeared(conversation.conversation_id, NewEvent("event", data={"x": 2})), out_queue, ) event = await anext(subscription.events) assert event.json()["x"] == 2 # Acknowledging the event should place an Ack message on the out_queue await subscription.ack(event) expected_payload = proto.PersistentSubscriptionAckEvents() expected_payload.subscription_id = "my-sub" expected_payload.processed_event_ids.append(event.event.id.bytes_le) expected_message = OutboundMessage( conversation.conversation_id, TcpCommand.PersistentSubscriptionAckEvents, expected_payload.SerializeToString(), ) ack = await out_queue.get() assert ack == expected_message
async def test_when_a_persistent_subscription_fails_on_connection(): """ If a persistent subscription fails to connect we should raise the error to the caller. We're going to use an authentication error here and leave the vexing issue of NotHandled for another day. """ dispatcher = MessageDispatcher() conversation = ConnectPersistentSubscription("my-sub", "my-stream") future = await dispatcher.start_conversation(conversation) await dispatcher.dispatch( persistent_subscription_dropped(conversation.conversation_id, SubscriptionDropReason.AccessDenied), None, ) with pytest.raises(SubscriptionCreationFailed): await asyncio.wait_for(future, 1)
async def test_stream_event_appeared(): convo = ConnectPersistentSubscription("my-subscription", "my-stream", max_in_flight=57) event_id = uuid4() await confirm_subscription(convo) response = event_appeared(event_id) await convo.respond_to( InboundMessage( uuid4(), TcpCommand.PersistentSubscriptionStreamEventAppeared, response.SerializeToString(), ), None, ) subscription = await convo.result event = await subscription.events.anext() assert event.event.id == event_id
async def test_when_a_persistent_subscription_is_unsubscribed(): """ If a persistent subscription gets unsubscribed while processing we should log an info and exit gracefully """ dispatcher = MessageDispatcher() conversation = ConnectPersistentSubscription("my-sub", "my-stream") future = await dispatcher.start_conversation(conversation) await dispatcher.dispatch( persistent_subscription_confirmed(conversation.conversation_id, "my-sub"), None) subscription = await asyncio.wait_for(future, 1) await dispatcher.dispatch( persistent_subscription_dropped(conversation.conversation_id, SubscriptionDropReason.Unsubscribed), None, ) [] = [e async for e in subscription.events]
async def test_acknowledge_event(): output = TeeQueue() convo = ConnectPersistentSubscription("my-subscription", "my-stream", max_in_flight=57) await confirm_subscription(convo, subscription_id=convo.name, queue=output) event_id = uuid4() subscription = await convo.result await subscription.ack(stub_event(event_id)) ack = await output.get() assert ack.command == TcpCommand.PersistentSubscriptionAckEvents assert ack.conversation_id == convo.conversation_id expected_payload = proto.PersistentSubscriptionAckEvents() expected_payload.subscription_id = convo.name expected_payload.processed_event_ids.append(event_id.bytes_le) assert expected_payload.SerializeToString() == ack.payload
async def test_when_a_persistent_subscription_fails(): """ If a persistent subscription fails with something non-recoverable then we should raise the error to the caller """ dispatcher = MessageDispatcher() conversation = ConnectPersistentSubscription("my-sub", "my-stream") future = await dispatcher.start_conversation(conversation) await dispatcher.dispatch( persistent_subscription_confirmed(conversation.conversation_id, "my-sub"), None) subscription = await asyncio.wait_for(future, 1) await dispatcher.dispatch( persistent_subscription_dropped(conversation.conversation_id, SubscriptionDropReason.AccessDenied), None, ) with pytest.raises(SubscriptionFailed): await anext(subscription.events)
async def test_read_deleted_event_processing(): """ When a stream with events is deleted from evenstore, the events don"t disappear from projections. Projections hold a link to a deleted event and eventstore would return object with a link a but no event data. This test case check when such deleted event is given to persistent subscription, subscription will be able to build an event object, which then could be acknowledged by the client service. """ async with message_reader() as (stream, messages, _): stream.feed_data(deleted_event) received = await messages.get() assert received.command == TcpCommand.PersistentSubscriptionStreamEventAppeared assert received.length == 185 convo = ConnectPersistentSubscription("random-subscription", "random-stream", max_in_flight=57) await confirm_subscription(convo) await convo.respond_to(received, None) subscription = await convo.result event = await subscription.events.anext() assert event.id == uuid.UUID("7a8c472c-c92e-4520-990d-a749f6020ba1") assert event.data == b"0@some" assert event.event_number == 0 assert event.link == None assert event.event != None assert event.stream == "$et-turtle"