def test_on_response():
    # Create mock Executor so we can verify calls to executor.submit().
    executor = mock.create_autospec(futures.Executor, instance=True)

    callback = mock.Mock(spec=())
    policy = create_and_open_policy(callback, executor=executor)

    # Set up the messages.
    response = types.StreamingPullResponse(received_messages=[
        types.ReceivedMessage(ack_id='fack',
                              message=types.PubsubMessage(data=b'foo',
                                                          message_id='1')),
        types.ReceivedMessage(ack_id='back',
                              message=types.PubsubMessage(data=b'bar',
                                                          message_id='2')),
    ], )

    # Actually run the method and prove that modack and executor.submit
    # are called in the expected way.
    modack_patch = mock.patch.object(policy,
                                     'modify_ack_deadline',
                                     autospec=True)
    with modack_patch as modack:
        policy.on_response(response)

    modack.assert_called_once_with(
        [base.ModAckRequest('fack', 10),
         base.ModAckRequest('back', 10)])

    submit_calls = [m for m in executor.method_calls if m[0] == 'submit']
    assert len(submit_calls) == 2
    for call in submit_calls:
        assert call[1][0] == policy._callback
        assert isinstance(call[1][1], message.Message)
예제 #2
0
def test_blocking_commit():
    batch = create_batch()
    futures = (
        batch.publish({'data': b'This is my message.'}),
        batch.publish({'data': b'This is another message.'}),
    )

    # Set up the underlying API publish method to return a PublishResponse.
    with mock.patch.object(type(batch.client.api), 'publish') as publish:
        publish.return_value = types.PublishResponse(message_ids=['a', 'b'])

        # Actually commit the batch.
        batch._commit()

        # Establish that the underlying API call was made with expected
        # arguments.
        publish.assert_called_once_with('topic_name', [
            types.PubsubMessage(data=b'This is my message.'),
            types.PubsubMessage(data=b'This is another message.'),
        ])

    # Establish that all of the futures are done, and that they have the
    # expected values.
    assert all([f.done() for f in futures])
    assert futures[0].result() == 'a'
    assert futures[1].result() == 'b'
def test__on_response_delivery_attempt():
    manager, _, dispatcher, leaser, _, scheduler = make_running_manager()
    manager._callback = mock.sentinel.callback

    # Set up the messages.
    response = types.StreamingPullResponse(received_messages=[
        types.ReceivedMessage(ack_id="fack",
                              message=types.PubsubMessage(data=b"foo",
                                                          message_id="1")),
        types.ReceivedMessage(
            ack_id="back",
            message=types.PubsubMessage(data=b"bar", message_id="2"),
            delivery_attempt=6,
        ),
    ])

    # adjust message bookkeeping in leaser
    fake_leaser_add(leaser, init_msg_count=0, assumed_msg_size=42)

    manager._on_response(response)

    schedule_calls = scheduler.schedule.mock_calls
    assert len(schedule_calls) == 2
    msg1 = schedule_calls[0][1][1]
    assert msg1.delivery_attempt is None
    msg2 = schedule_calls[1][1][1]
    assert msg2.delivery_attempt == 6
def test_on_response():
    callback = mock.Mock(spec=())

    # Set up the policy.
    policy = create_policy()
    policy._callback = callback

    # Set up the messages to send.
    messages = (
        types.PubsubMessage(data=b'foo', message_id='1'),
        types.PubsubMessage(data=b'bar', message_id='2'),
    )

    # Set up a valid response.
    response = types.StreamingPullResponse(received_messages=[
        {
            'ack_id': 'fack',
            'message': messages[0]
        },
        {
            'ack_id': 'back',
            'message': messages[1]
        },
    ], )

    # Actually run the method and prove that the callback was
    # called in the expected way.
    policy.on_response(response)
    assert callback.call_count == 2
    for call in callback.mock_calls:
        assert isinstance(call[1][0], message.Message)
def test_incorrectly_releasing_too_many_messages():
    settings = types.PublishFlowControl(
        message_limit=1,
        byte_limit=150,
        limit_exceeded_behavior=types.LimitExceededBehavior.ERROR,
    )
    flow_controller = FlowController(settings)

    msg1 = types.PubsubMessage(data=b"x" * 100)
    msg2 = types.PubsubMessage(data=b"y" * 100)
    msg3 = types.PubsubMessage(data=b"z" * 100)

    # Releasing a message that would make the load negative should result in a warning.
    with warnings.catch_warnings(record=True) as warned:
        flow_controller.release(msg1)

    assert len(warned) == 1
    assert issubclass(warned[0].category, RuntimeWarning)
    warning_msg = str(warned[0].message)
    assert "never added or already released" in warning_msg

    # Incorrectly removing a message does not mess up internal stats, we can
    # still only add a single message at a time to this flow.
    flow_controller.add(msg2)

    with pytest.raises(exceptions.FlowControlLimitError) as error:
        flow_controller.add(msg3)

    error_msg = str(error.value)
    assert "messages: 2 / 1" in error_msg
    total_size = msg2.ByteSize() + msg3.ByteSize()
    expected_size_info = "bytes: {} / 150".format(total_size)
    assert expected_size_info in error_msg
예제 #6
0
def test_publish_with_ordering_key():
    creds = mock.Mock(spec=credentials.Credentials)
    publisher_options = types.PublisherOptions(enable_message_ordering=True)
    client = publisher.Client(publisher_options=publisher_options,
                              credentials=creds)

    # Use a mock in lieu of the actual batch class.
    batch = mock.Mock(spec=client._batch_class)
    # Set the mock up to claim indiscriminately that it accepts all messages.
    batch.will_accept.return_value = True
    batch.publish.side_effect = (mock.sentinel.future1, mock.sentinel.future2)

    topic = "topic/path"
    ordering_key = "k1"
    client._set_batch(topic, batch, ordering_key=ordering_key)

    # Begin publishing.
    future1 = client.publish(topic, b"spam", ordering_key=ordering_key)
    future2 = client.publish(topic,
                             b"foo",
                             bar="baz",
                             ordering_key=ordering_key)

    assert future1 is mock.sentinel.future1
    assert future2 is mock.sentinel.future2

    # Check mock.
    batch.publish.assert_has_calls([
        mock.call(types.PubsubMessage(data=b"spam", ordering_key="k1")),
        mock.call(
            types.PubsubMessage(data=b"foo",
                                attributes={"bar": "baz"},
                                ordering_key="k1")),
    ])
예제 #7
0
def test_publish_exceed_max_messages():
    max_messages = 4
    batch = create_batch(max_messages=max_messages)
    messages = (
        types.PubsubMessage(data=b"foobarbaz"),
        types.PubsubMessage(data=b"spameggs"),
        types.PubsubMessage(data=b"1335020400"),
    )

    # Publish each of the messages, which should save them to the batch.
    with mock.patch.object(batch, "commit") as commit:
        futures = [batch.publish(message) for message in messages]
        assert batch._futures == futures
        assert len(futures) == max_messages - 1

        # Commit should not yet have been called.
        assert commit.call_count == 0

        # When a fourth message is published, commit should be called.
        # No future will be returned in this case.
        future = batch.publish(types.PubsubMessage(data=b"last one"))
        commit.assert_called_once_with()

        assert future is None
        assert batch._futures == futures
예제 #8
0
def test_blocking__commit():
    batch = create_batch()
    futures = (
        batch.publish({"data": b"This is my message."}),
        batch.publish({"data": b"This is another message."}),
    )

    # Set up the underlying API publish method to return a PublishResponse.
    publish_response = types.PublishResponse(message_ids=["a", "b"])
    patch = mock.patch.object(type(batch.client.api),
                              "publish",
                              return_value=publish_response)
    with patch as publish:
        batch._commit()

    # Establish that the underlying API call was made with expected
    # arguments.
    publish.assert_called_once_with(
        "topic_name",
        [
            types.PubsubMessage(data=b"This is my message."),
            types.PubsubMessage(data=b"This is another message."),
        ],
    )

    # Establish that all of the futures are done, and that they have the
    # expected values.
    assert futures[0].done()
    assert futures[0].result() == "a"
    assert futures[1].done()
    assert futures[1].result() == "b"
def test__on_response_no_leaser_overload():
    manager, _, dispatcher, leaser, _, scheduler = make_running_manager()
    manager._callback = mock.sentinel.callback

    # Set up the messages.
    response = types.StreamingPullResponse(received_messages=[
        types.ReceivedMessage(ack_id="fack",
                              message=types.PubsubMessage(data=b"foo",
                                                          message_id="1")),
        types.ReceivedMessage(ack_id="back",
                              message=types.PubsubMessage(data=b"bar",
                                                          message_id="2")),
    ])

    # adjust message bookkeeping in leaser
    fake_leaser_add(leaser, init_msg_count=0, assumed_msg_size=42)

    # Actually run the method and prove that modack and schedule
    # are called in the expected way.
    manager._on_response(response)

    dispatcher.modify_ack_deadline.assert_called_once_with([
        requests.ModAckRequest("fack", 10),
        requests.ModAckRequest("back", 10)
    ])

    schedule_calls = scheduler.schedule.mock_calls
    assert len(schedule_calls) == 2
    for call in schedule_calls:
        assert call[1][0] == mock.sentinel.callback
        assert isinstance(call[1][1], message.Message)

    # the leaser load limit not hit, no messages had to be put on hold
    assert manager._messages_on_hold.size == 0
예제 #10
0
def test_publish():
    creds = mock.Mock(spec=credentials.Credentials)
    client = publisher.Client(credentials=creds)

    # Use a mock in lieu of the actual batch class.
    batch = mock.Mock(spec=client._batch_class)
    # Set the mock up to claim indiscriminately that it accepts all messages.
    batch.will_accept.return_value = True
    batch.publish.side_effect = (mock.sentinel.future1, mock.sentinel.future2)

    topic = "topic/path"
    client._batches[topic] = batch

    # Begin publishing.
    future1 = client.publish(topic, b"spam")
    future2 = client.publish(topic, b"foo", bar="baz")

    assert future1 is mock.sentinel.future1
    assert future2 is mock.sentinel.future2

    # Check mock.
    batch.publish.assert_has_calls([
        mock.call(types.PubsubMessage(data=b"spam")),
        mock.call(types.PubsubMessage(data=b"foo", attributes={"bar": "baz"})),
    ])
예제 #11
0
def test_publish_exceed_max_messages():
    max_messages = 4
    batch = create_batch(max_messages=max_messages)
    messages = (
        types.PubsubMessage(data=b'foobarbaz'),
        types.PubsubMessage(data=b'spameggs'),
        types.PubsubMessage(data=b'1335020400'),
    )

    # Publish each of the messages, which should save them to the batch.
    with mock.patch.object(batch, 'commit') as commit:
        futures = [batch.publish(message) for message in messages]
        assert batch._futures == futures
        assert len(futures) == max_messages - 1

        # Commit should not yet have been called.
        assert commit.call_count == 0

        # When a fourth message is published, commit should be called.
        future = batch.publish(types.PubsubMessage(data=b'last one'))
        commit.assert_called_once_with()

        futures.append(future)
        assert batch._futures == futures
        assert len(futures) == max_messages
def test_on_response():
    manager, _, dispatcher, _, scheduler = make_running_manager()
    manager._callback = mock.sentinel.callback

    # Set up the messages.
    response = types.StreamingPullResponse(
        received_messages=[
            types.ReceivedMessage(
                ack_id='fack',
                message=types.PubsubMessage(data=b'foo', message_id='1')
            ),
            types.ReceivedMessage(
                ack_id='back',
                message=types.PubsubMessage(data=b'bar', message_id='2')
            ),
        ],
    )

    # Actually run the method and prove that modack and schedule
    # are called in the expected way.
    manager._on_response(response)

    dispatcher.modify_ack_deadline.assert_called_once_with(
        [requests.ModAckRequest('fack', 10),
         requests.ModAckRequest('back', 10)]
    )

    schedule_calls = scheduler.schedule.mock_calls
    assert len(schedule_calls) == 2
    for call in schedule_calls:
        assert call[1][0] == mock.sentinel.callback
        assert isinstance(call[1][1], message.Message)
예제 #13
0
def test_threads_posting_large_messages_do_not_starve():
    settings = types.PublishFlowControl(
        message_limit=100,
        byte_limit=110,
        limit_exceeded_behavior=types.LimitExceededBehavior.BLOCK,
    )
    flow_controller = FlowController(settings)

    large_msg = types.PubsubMessage(data=b"x" * 100)  # close to entire byte limit

    adding_initial_done = threading.Event()
    adding_large_done = threading.Event()
    adding_busy_done = threading.Event()
    releasing_busy_done = threading.Event()
    releasing_large_done = threading.Event()

    # Occupy some of the flow capacity, then try to add a large message. Releasing
    # enough messages should eventually allow the large message to come through, even
    # if more messages are added after it (those should wait for the large message).
    initial_messages = [types.PubsubMessage(data=b"x" * 10)] * 5
    _run_in_daemon(flow_controller, "add", initial_messages, adding_initial_done)
    assert adding_initial_done.wait(timeout=0.1)

    _run_in_daemon(flow_controller, "add", [large_msg], adding_large_done)

    # Continuously keep adding more messages after the large one.
    messages = [types.PubsubMessage(data=b"x" * 10)] * 10
    _run_in_daemon(flow_controller, "add", messages, adding_busy_done, action_pause=0.1)

    # At the same time, gradually keep releasing the messages - the freeed up
    # capacity should be consumed by the large message, not the other small messages
    # being added after it.
    _run_in_daemon(
        flow_controller, "release", messages, releasing_busy_done, action_pause=0.1
    )

    # Sanity check - releasing should have completed by now.
    if not releasing_busy_done.wait(timeout=1.1):
        pytest.fail("Releasing messages blocked or errored.")

    # Enough messages released, the large message should have come through in
    # the meantime.
    if not adding_large_done.wait(timeout=0.1):
        pytest.fail("A thread adding a large message starved.")

    if adding_busy_done.wait(timeout=0.1):
        pytest.fail("Adding multiple small messages did not block.")

    # Releasing the large message should unblock adding the remaining "busy" messages
    # that have not been added yet.
    _run_in_daemon(flow_controller, "release", [large_msg], releasing_large_done)
    if not releasing_large_done.wait(timeout=0.1):
        pytest.fail("Releasing a message blocked or errored.")

    if not adding_busy_done.wait(timeout=1.0):
        pytest.fail("Adding messages blocked or errored.")
예제 #14
0
def test__on_response_with_leaser_overload():
    manager, _, dispatcher, leaser, _, scheduler = make_running_manager()
    manager._callback = mock.sentinel.callback

    # Set up the messages.
    response = types.StreamingPullResponse(
        received_messages=[
            types.ReceivedMessage(
                ack_id="fack", message=types.PubsubMessage(data=b"foo", message_id="1")
            ),
            types.ReceivedMessage(
                ack_id="back", message=types.PubsubMessage(data=b"bar", message_id="2")
            ),
            types.ReceivedMessage(
                ack_id="zack", message=types.PubsubMessage(data=b"baz", message_id="3")
            ),
        ]
    )

    # Adjust message bookkeeping in leaser. Pick 999 messages, which is just below
    # the default FlowControl.max_messages limit.
    fake_leaser_add(leaser, init_msg_count=999, assumed_msg_size=10)

    # Actually run the method and prove that modack and schedule
    # are called in the expected way.
    manager._on_response(response)

    # all messages should be added to the lease management and have their ACK
    # deadline extended, even those not dispatched to callbacks
    dispatcher.modify_ack_deadline.assert_called_once_with(
        [
            requests.ModAckRequest("fack", 10),
            requests.ModAckRequest("back", 10),
            requests.ModAckRequest("zack", 10),
        ]
    )

    # one message should be scheduled, the flow control limits allow for it
    schedule_calls = scheduler.schedule.mock_calls
    assert len(schedule_calls) == 1
    call_args = schedule_calls[0][1]
    assert call_args[0] == mock.sentinel.callback
    assert isinstance(call_args[1], message.Message)
    assert call_args[1].message_id == "1"

    # the rest of the messages should have been put on hold
    assert manager._messages_on_hold.size == 2
    while True:
        msg = manager._messages_on_hold.get()
        if msg is None:
            break
        else:
            assert isinstance(msg, message.Message)
            assert msg.message_id in ("2", "3")
예제 #15
0
def test_overflow_no_error_on_ignore():
    settings = types.PublishFlowControl(
        message_limit=1,
        byte_limit=2,
        limit_exceeded_behavior=types.LimitExceededBehavior.IGNORE,
    )
    flow_controller = FlowController(settings)

    # there should be no overflow errors
    flow_controller.add(types.PubsubMessage(data=b"foo"))
    flow_controller.add(types.PubsubMessage(data=b"bar"))
예제 #16
0
def test_publish_max_bytes_enforced():
    batch = create_batch(topic="topic_foo", max_bytes=15)

    message = types.PubsubMessage(data=b"foobarbaz")
    message2 = types.PubsubMessage(data=b"foobarbaz2")

    future = batch.publish(message)
    future2 = batch.publish(message2)

    assert future is not None
    assert future2 is None
    assert len(batch.messages) == 1
    assert len(batch._futures) == 1
예제 #17
0
def test_message_count_overflow_error():
    settings = types.PublishFlowControl(
        message_limit=1,
        byte_limit=10000,
        limit_exceeded_behavior=types.LimitExceededBehavior.ERROR,
    )
    flow_controller = FlowController(settings)

    flow_controller.add(types.PubsubMessage(data=b"foo"))
    with pytest.raises(exceptions.FlowControlLimitError) as error:
        flow_controller.add(types.PubsubMessage(data=b"bar"))

    assert "messages: 2 / 1" in str(error.value)
def test_on_response():
    callback = mock.Mock(spec=())

    # Create mock ThreadPoolExecutor, pass into create_policy(), and verify
    # that both executor.submit() and future.add_done_callback are called
    # twice.
    future = mock.Mock()
    attrs = {'submit.return_value': future}
    executor = mock.Mock(**attrs)

    # Set up the policy.
    policy = create_policy(executor=executor)
    policy._callback = callback

    # Set up the messages to send.
    messages = (
        types.PubsubMessage(data=b'foo', message_id='1'),
        types.PubsubMessage(data=b'bar', message_id='2'),
    )

    # Set up a valid response.
    response = types.StreamingPullResponse(received_messages=[
        {
            'ack_id': 'fack',
            'message': messages[0]
        },
        {
            'ack_id': 'back',
            'message': messages[1]
        },
    ], )

    # Actually run the method and prove that modack and executor.submit
    # are called in the expected way.
    modack_patch = mock.patch.object(policy,
                                     'modify_ack_deadline',
                                     autospec=True)
    with modack_patch as modack:
        policy.on_response(response)

    modack.assert_called_once_with(
        [base.ModAckRequest('fack', 10),
         base.ModAckRequest('back', 10)])

    submit_calls = [m for m in executor.method_calls if m[0] == 'submit']
    assert len(submit_calls) == 2
    for call in submit_calls:
        assert call[1][0] == callback
        assert isinstance(call[1][1], message.Message)
def test_on_response():
    callback = mock.Mock(spec=())

    # Create mock ThreadPoolExecutor, pass into create_policy(), and verify
    # that both executor.submit() and future.add_done_callback are called
    # twice.
    future = mock.Mock()
    attrs = {'submit.return_value': future}
    executor = mock.Mock(**attrs)

    # Set up the policy.
    policy = create_policy(executor=executor)
    policy._callback = callback

    # Set up the messages to send.
    messages = (
        types.PubsubMessage(data=b'foo', message_id='1'),
        types.PubsubMessage(data=b'bar', message_id='2'),
    )

    # Set up a valid response.
    response = types.StreamingPullResponse(received_messages=[
        {
            'ack_id': 'fack',
            'message': messages[0]
        },
        {
            'ack_id': 'back',
            'message': messages[1]
        },
    ], )

    # Actually run the method and prove that executor.submit and
    # future.add_done_callback were called in the expected way.
    policy.on_response(response)

    submit_calls = [m for m in executor.method_calls if m[0] == 'submit']
    assert len(submit_calls) == 2
    for call in submit_calls:
        assert call[1][0] == callback
        assert isinstance(call[1][1], message.Message)

    add_done_callback_calls = [
        m for m in future.method_calls if m[0] == 'add_done_callback'
    ]
    assert len(add_done_callback_calls) == 2
    for call in add_done_callback_calls:
        assert call[1][0] == thread._callback_completed
예제 #20
0
def test_will_accept_oversize():
    batch = create_batch(
        settings=types.BatchSettings(max_bytes=10),
        status=BatchStatus.ACCEPTING_MESSAGES,
    )
    message = types.PubsubMessage(data=b"abcdefghijklmnopqrstuvwxyz")
    assert batch.will_accept(message) is True
    def publish(self, message):
        """Publish a single message.

        Add the given message to this object; this will cause it to be
        published once the batch either has enough messages or a sufficient
        period of time has elapsed.

        This method is called by :meth:`~.PublisherClient.publish`.

        Args:
            message (~.pubsub_v1.types.PubsubMessage): The Pub/Sub message.

        Returns:
            ~.pubsub_v1.publisher.futures.Future: An object conforming to
                the :class:`concurrent.futures.Future` interface.
        """
        # Coerce the type, just in case.
        if not isinstance(message, types.PubsubMessage):
            message = types.PubsubMessage(**message)

        # Add the size to the running total of the size, so we know
        # if future messages need to be rejected.
        self._size += message.ByteSize()

        # Store the actual message in the batch's message queue.
        self._messages.append(message)

        # Return a Future. That future needs to be aware of the status
        # of this batch.
        f = futures.Future()
        self._futures.append(f)
        return f
예제 #22
0
def test_error_if_mesage_would_block_indefinitely():
    settings = types.PublishFlowControl(
        message_limit=0,  # simulate non-sane settings
        byte_limit=1,
        limit_exceeded_behavior=types.LimitExceededBehavior.BLOCK,
    )
    flow_controller = FlowController(settings)

    msg = types.PubsubMessage(data=b"xyz")
    adding_done = threading.Event()
    error_event = threading.Event()

    _run_in_daemon(flow_controller, "add", [msg], adding_done, error_event=error_event)

    assert error_event.wait(timeout=0.1), "No error on adding too large a message."

    # Now that we know that an error occurs, we can check its type directly
    # without the fear of blocking indefinitely.
    flow_controller = FlowController(settings)  # we want a fresh controller
    with pytest.raises(exceptions.FlowControlLimitError) as error_info:
        flow_controller.add(msg)

    error_msg = str(error_info.value)
    assert "would block forever" in error_msg
    assert "messages: 1 / 0" in error_msg
    assert "bytes: {} / 1".format(msg.ByteSize()) in error_msg
예제 #23
0
def test_will_not_accept_number():
    batch = create_batch(
        settings=types.BatchSettings(max_messages=-1),
        status=BatchStatus.ACCEPTING_MESSAGES,
    )
    message = types.PubsubMessage(data=b"abc")
    assert batch.will_accept(message) is False
예제 #24
0
def test_publish():
    batch = create_batch()
    message = types.PubsubMessage()
    future = batch.publish(message)

    assert len(batch.messages) == 1
    assert batch._futures == [future]
def test_on_response_nacks_on_error():
    # Create a callback that always errors.
    callback = mock.Mock(spec=(), side_effect=ValueError)
    executor = mock.create_autospec(futures.Executor, instance=True)
    executor.submit.side_effect = _callback_side_effect
    policy = create_and_open_policy(callback, executor=executor)

    # Set up the messages.
    message = types.PubsubMessage(data=b'foo', message_id='1')
    response = types.StreamingPullResponse(received_messages=[
        types.ReceivedMessage(ack_id='fack', message=message),
    ], )

    # Actually run the method and prove that nack is called because the
    # callback errored.
    policy.on_response(response)

    # Make sure the callback was executed.
    callback.assert_called_once_with(mock.ANY)

    # Process outstanding requests, the callback should've queued a nack
    # request.
    nack_patch = mock.patch.object(policy, 'nack', autospec=True)
    with nack_patch as nack:
        policy.dispatch_callback(policy._request_queue.queue)

    nack.assert_called_once_with(
        [base.NackRequest('fack', message.ByteSize())])
예제 #26
0
def test_stop():
    creds = mock.Mock(spec=credentials.Credentials)
    client = publisher.Client(credentials=creds)

    batch = client._batch("topic1", autocommit=False)
    batch2 = client._batch("topic2", autocommit=False)

    pubsub_msg = types.PubsubMessage(data=b"msg")

    patch = mock.patch.object(batch, "commit")
    patch2 = mock.patch.object(batch2, "commit")

    with patch as commit_mock, patch2 as commit_mock2:
        batch.publish(pubsub_msg)
        batch2.publish(pubsub_msg)

        client.stop()

        # check if commit() called
        commit_mock.assert_called()
        commit_mock2.assert_called()

    # check that closed publisher doesn't accept new messages
    with pytest.raises(RuntimeError):
        client.publish("topic1", b"msg2")

    with pytest.raises(RuntimeError):
        client.stop()
예제 #27
0
def test_publish_new_batch_needed():
    creds = mock.Mock(spec=credentials.Credentials)
    client = publisher.Client(credentials=creds)

    # Use mocks in lieu of the actual batch class.
    batch1 = mock.Mock(spec=client._batch_class)
    batch2 = mock.Mock(spec=client._batch_class)
    # Set the first mock up to claim indiscriminately that it rejects all
    # messages and the second accepts all.
    batch1.publish.return_value = None
    batch2.publish.return_value = mock.sentinel.future

    topic = "topic/path"
    client._batches[topic] = batch1

    # Actually mock the batch class now.
    batch_class = mock.Mock(spec=(), return_value=batch2)
    client._batch_class = batch_class

    # Publish a message.
    future = client.publish(topic, b"foo", bar=b"baz")
    assert future is mock.sentinel.future

    # Check the mocks.
    batch_class.assert_called_once_with(autocommit=True,
                                        client=client,
                                        settings=client.batch_settings,
                                        topic=topic)
    message_pb = types.PubsubMessage(data=b"foo", attributes={"bar": u"baz"})
    batch1.publish.assert_called_once_with(message_pb)
    batch2.publish.assert_called_once_with(message_pb)
예제 #28
0
파일: client.py 프로젝트: slz250/hackumass
    def publish(self, topic, data, **attrs):
        """Publish a single message.

        .. note::
            Messages in Pub/Sub are blobs of bytes. They are *binary* data,
            not text. You must send data as a bytestring
            (``bytes`` in Python 3; ``str`` in Python 2), and this library
            will raise an exception if you send a text string.

            The reason that this is so important (and why we do not try to
            coerce for you) is because Pub/Sub is also platform independent
            and there is no way to know how to decode messages properly on
            the other side; therefore, encoding and decoding is a required
            exercise for the developer.

        Add the given message to this object; this will cause it to be
        published once the batch either has enough messages or a sufficient
        period of time has elapsed.

        Example:
            >>> from google.cloud.pubsub_v1 import publisher_client
            >>> client = publisher_client.PublisherClient()
            >>> topic = client.topic_path('[PROJECT]', '[TOPIC]')
            >>> data = b'The rain in Wales falls mainly on the snails.'
            >>> response = client.publish(topic, data, username='******')

        Args:
            topic (str): The topic to publish messages to.
            data (bytes): A bytestring representing the message body. This
                must be a bytestring.
            attrs (Mapping[str, str]): A dictionary of attributes to be
                sent as metadata. (These may be text strings or byte strings.)

        Returns:
            ~concurrent.futures.Future: An object conforming to the
            ``concurrent.futures.Future`` interface.
        """
        # Sanity check: Is the data being sent as a bytestring?
        # If it is literally anything else, complain loudly about it.
        if not isinstance(data, six.binary_type):
            raise TypeError('Data being published to Pub/Sub must be sent '
                            'as a bytestring.')

        # Coerce all attributes to text strings.
        for k, v in copy.copy(attrs).items():
            if isinstance(v, six.text_type):
                continue
            if isinstance(v, six.binary_type):
                attrs[k] = v.decode('utf-8')
                continue
            raise TypeError('All attributes being published to Pub/Sub must '
                            'be sent as text strings.')

        # Create the Pub/Sub message object.
        message = types.PubsubMessage(data=data, attributes=attrs)

        # Delegate the publishing to the batch.
        return self.batch(topic, message=message).publish(message)
예제 #29
0
def test_do_not_commit_when_full_when_flag_is_off():
    max_messages = 4
    # Set commit_when_full flag to False
    batch = create_batch(max_messages=max_messages, commit_when_full=False)
    messages = (
        types.PubsubMessage(data=b"foobarbaz"),
        types.PubsubMessage(data=b"spameggs"),
        types.PubsubMessage(data=b"1335020400"),
    )

    with mock.patch.object(batch, "commit") as commit:
        # Publish 3 messages.
        futures = [batch.publish(message) for message in messages]
        assert len(futures) == 3

        # When a fourth message is published, commit should not be called.
        future = batch.publish(types.PubsubMessage(data=b"last one"))
        assert commit.call_count == 0
        assert future is None
def test_batch_without_autocreate():
    client = create_client()
    message = types.PubsubMessage(data=b'foo')

    # If `create=False` is sent, then when the batch is not found, None
    # is returned instead.
    ante = len(client._batches)
    batch = client.batch('topic_name', message, create=False)
    assert batch is None
    assert len(client._batches) == ante