Exemple #1
0
  def test_routing_and_task_success(self):
    # Register two types of tasks.
    foo_task_processor = mock.Mock()
    bar_task_processor = mock.Mock()
    self._worker.register(test_task_pb2.FooTaskArgs, foo_task_processor)
    self._worker.register(test_task_pb2.BarTaskArgs, bar_task_processor)

    # Create pubsub messages.
    foo_task_args = test_task_pb2.FooTaskArgs(widget='Water is made of water.')
    foo_message = _make_mock_pubsub_message(foo_task_args)
    bar_task_args = test_task_pb2.BarTaskArgs(best_number=42)
    bar_message = _make_mock_pubsub_message(bar_task_args)

    # Subscribe, and process the messages.
    subscribe_retval = 'grapes of testing'
    self._mock_subscribe([foo_message, bar_message], retval=subscribe_retval)
    self.assertIs(subscribe_retval, self._worker.subscribe('kumquat'))
    self._client.subscribe.assert_called_once_with('kumquat', mock.ANY)

    # Test that the messages were routed to the correct tasks, and that both
    # messages were ACKed.
    foo_task_processor.assert_called_once_with(foo_task_args)
    foo_message.ack.assert_called_once_with()
    bar_task_processor.assert_called_once_with(bar_task_args)
    bar_message.ack.assert_called_once_with()
Exemple #2
0
 def test_callback_error(self):
   foo_task_processor = mock.Mock(side_effect=RuntimeError('foo error'))
   self._worker.register(test_task_pb2.FooTaskArgs, foo_task_processor)
   foo_message = _make_mock_pubsub_message(test_task_pb2.FooTaskArgs())
   self._mock_subscribe([foo_message])
   self._worker.subscribe('kumquat')
   foo_message.nack.assert_called_once_with()
Exemple #3
0
 def test_task_to_string_error(self):
   self._task_to_string.side_effect = RuntimeError(
       'Unable to convert proto to string.')
   self._worker.register(test_task_pb2.FooTaskArgs, mock.Mock())
   foo_message = _make_mock_pubsub_message(test_task_pb2.FooTaskArgs())
   self._mock_subscribe([foo_message])
   self._worker.subscribe('kumquat')
   foo_message.nack.assert_called_once_with()
Exemple #4
0
 def test_publish_is_called_by(self, request_by_args):
   args = test_task_pb2.FooTaskArgs()
   task = task_pb2.Task()
   task.args.Pack(args)
   publish_future = mock.Mock()
   self._client.publish.return_value = publish_future
   if request_by_args:
     returned_future = self._requestor.request('kumquat', args)
   else:
     returned_future = self._requestor.request_task('kumquat', task)
   self.assertIs(publish_future, returned_future)
   self._client.publish.assert_called_once_with('kumquat',
                                                task.SerializeToString())
    def test_retry_failure_then_succeed(self):
        # Fail once, then succeed.
        foo_task_processor = mock.Mock(
            side_effect=[RuntimeError('retry this'), None])
        self._worker.register(test_task_pb2.FooTaskArgs, foo_task_processor)
        subscriber_future = self._worker.subscribe(_SUBSCRIPTION_NAME)

        # Request that foo be worked on.
        foo_task_args = test_task_pb2.FooTaskArgs()
        foo_task_args.widget = 'widget maker maker'
        request_future = self._requestor.request(_TOPIC_NAME, foo_task_args)
        request_future.result(timeout=10)

        # Wait up to a minute for the messages to go through, then stop waiting for
        # more.
        for _ in range(60):
            time.sleep(1)
            if foo_task_processor.call_count >= 2:
                break
        subscriber_future.cancel()

        self.assertEqual(2, foo_task_processor.call_count)
        foo_task_processor.assert_has_calls([mock.call(foo_task_args)] * 2)
Exemple #6
0
class WorkerTest(parameterized.TestCase):

  def setUp(self):
    super().setUp()
    self._client = mock.create_autospec(client.Client)
    self._task_to_string = mock.Mock(side_effect=text_format.MessageToString)
    self._worker = worker.Worker(
        pubsub_subscriber_client=self._client,
        task_to_string=self._task_to_string,
    )

  def test_routing_and_task_success(self):
    # Register two types of tasks.
    foo_task_processor = mock.Mock()
    bar_task_processor = mock.Mock()
    self._worker.register(test_task_pb2.FooTaskArgs, foo_task_processor)
    self._worker.register(test_task_pb2.BarTaskArgs, bar_task_processor)

    # Create pubsub messages.
    foo_task_args = test_task_pb2.FooTaskArgs(widget='Water is made of water.')
    foo_message = _make_mock_pubsub_message(foo_task_args)
    bar_task_args = test_task_pb2.BarTaskArgs(best_number=42)
    bar_message = _make_mock_pubsub_message(bar_task_args)

    # Subscribe, and process the messages.
    subscribe_retval = 'grapes of testing'
    self._mock_subscribe([foo_message, bar_message], retval=subscribe_retval)
    self.assertIs(subscribe_retval, self._worker.subscribe('kumquat'))
    self._client.subscribe.assert_called_once_with('kumquat', mock.ANY)

    # Test that the messages were routed to the correct tasks, and that both
    # messages were ACKed.
    foo_task_processor.assert_called_once_with(foo_task_args)
    foo_message.ack.assert_called_once_with()
    bar_task_processor.assert_called_once_with(bar_task_args)
    bar_message.ack.assert_called_once_with()

  def test_register_after_subscribe_error(self):
    self._worker.subscribe('kumquat')
    with self.assertRaisesRegex(RuntimeError,
                                'register.*after a subscriber is started'):
      self._worker.register(test_task_pb2.FooTaskArgs, mock.Mock())

  @parameterized.named_parameters(
      ('invalid_task_proto', b'this is probably not a valid binary proto'),
      ('unregistered_type', _make_task_bytes(test_task_pb2.FooTaskArgs())))
  def test_unhandled_message(self, message_data):
    self._worker.register(test_task_pb2.BarTaskArgs, mock.Mock())
    msg = mock.create_autospec(message.Message)
    msg.data = message_data
    msg.message_id = str(uuid.uuid4())
    self._mock_subscribe([msg])
    with warnings.catch_warnings():
      # Ignore a warning from trying to parse an invalid binary proto.
      warnings.filterwarnings('ignore', 'Unexpected end-group tag:')
      self._worker.subscribe('kumquat')
    msg.nack.assert_called_once_with()

  def test_task_to_string_error(self):
    self._task_to_string.side_effect = RuntimeError(
        'Unable to convert proto to string.')
    self._worker.register(test_task_pb2.FooTaskArgs, mock.Mock())
    foo_message = _make_mock_pubsub_message(test_task_pb2.FooTaskArgs())
    self._mock_subscribe([foo_message])
    self._worker.subscribe('kumquat')
    foo_message.nack.assert_called_once_with()

  def test_callback_error(self):
    foo_task_processor = mock.Mock(side_effect=RuntimeError('foo error'))
    self._worker.register(test_task_pb2.FooTaskArgs, foo_task_processor)
    foo_message = _make_mock_pubsub_message(test_task_pb2.FooTaskArgs())
    self._mock_subscribe([foo_message])
    self._worker.subscribe('kumquat')
    foo_message.nack.assert_called_once_with()

  def _mock_subscribe(self, messages, retval=None):
    """Set up self._client.subscribe() to mock messages being published.

    Note: This processes messages synchronously in the calling thread, unlike a
    real pubsub subscriber.

    Args:
      messages: Iterable of pubsub messages to treat as if they were received
        from pubsub.
      retval: Value to return from subscribe(). Normally that function would
        return a Future, but that isn't needed for this synchronous
        implementation.
    """

    def side_effect(subscription, callback):
      del subscription  # Unused.
      for msg in messages:
        callback(msg)
      return retval

    self._client.subscribe.side_effect = side_effect