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)
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
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")), ])
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
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
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"})), ])
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)
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.")
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")
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"))
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
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
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
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
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
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())])
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()
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)
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)
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