github.com/apache/beam/sdks/v2@v2.48.2/python/apache_beam/io/gcp/tests/pubsub_matcher_test.py (about)

     1  #
     2  # Licensed to the Apache Software Foundation (ASF) under one or more
     3  # contributor license agreements.  See the NOTICE file distributed with
     4  # this work for additional information regarding copyright ownership.
     5  # The ASF licenses this file to You under the Apache License, Version 2.0
     6  # (the "License"); you may not use this file except in compliance with
     7  # the License.  You may obtain a copy of the License at
     8  #
     9  #    http://www.apache.org/licenses/LICENSE-2.0
    10  #
    11  # Unless required by applicable law or agreed to in writing, software
    12  # distributed under the License is distributed on an "AS IS" BASIS,
    13  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14  # See the License for the specific language governing permissions and
    15  # limitations under the License.
    16  #
    17  
    18  """Unit test for PubSub verifier."""
    19  
    20  # pytype: skip-file
    21  
    22  import logging
    23  import unittest
    24  
    25  import mock
    26  from hamcrest import assert_that as hc_assert_that
    27  
    28  from apache_beam.io.gcp.pubsub import PubsubMessage
    29  from apache_beam.io.gcp.tests.pubsub_matcher import PubSubMessageMatcher
    30  from apache_beam.testing.test_utils import PullResponseMessage
    31  from apache_beam.testing.test_utils import create_pull_response
    32  
    33  try:
    34    from google.cloud import pubsub
    35  except ImportError:
    36    pubsub = None
    37  
    38  
    39  @unittest.skipIf(pubsub is None, 'PubSub dependencies are not installed.')
    40  @mock.patch('time.sleep', return_value=None)
    41  @mock.patch('google.cloud.pubsub.SubscriberClient')
    42  class PubSubMatcherTest(unittest.TestCase):
    43    def setUp(self):
    44      self.mock_presult = mock.MagicMock()
    45  
    46    def init_matcher(
    47        self, expected_msg=None, with_attributes=False, strip_attributes=None):
    48      self.pubsub_matcher = PubSubMessageMatcher(
    49          'mock_project',
    50          'mock_sub_name',
    51          expected_msg,
    52          with_attributes=with_attributes,
    53          strip_attributes=strip_attributes)
    54  
    55    def init_counter_matcher(self, expected_msg_len=1):
    56      self.pubsub_matcher = PubSubMessageMatcher(
    57          'mock_project', 'mock_sub_name', expected_msg_len=expected_msg_len)
    58  
    59    def test_message_matcher_success(self, mock_get_sub, unsued_mock):
    60      self.init_matcher(expected_msg=[b'a', b'b'])
    61      mock_sub = mock_get_sub.return_value
    62      mock_sub.pull.side_effect = [
    63          create_pull_response([PullResponseMessage(b'a', {})]),
    64          create_pull_response([PullResponseMessage(b'b', {})]),
    65      ]
    66      hc_assert_that(self.mock_presult, self.pubsub_matcher)
    67      self.assertEqual(mock_sub.pull.call_count, 2)
    68      self.assertEqual(mock_sub.acknowledge.call_count, 2)
    69  
    70    def test_message_matcher_attributes_success(self, mock_get_sub, unsued_mock):
    71      self.init_matcher(
    72          expected_msg=[PubsubMessage(b'a', {'k': 'v'})], with_attributes=True)
    73      mock_sub = mock_get_sub.return_value
    74      mock_sub.pull.side_effect = [
    75          create_pull_response([PullResponseMessage(b'a', {'k': 'v'})])
    76      ]
    77      hc_assert_that(self.mock_presult, self.pubsub_matcher)
    78      self.assertEqual(mock_sub.pull.call_count, 1)
    79      self.assertEqual(mock_sub.acknowledge.call_count, 1)
    80  
    81    def test_message_matcher_attributes_fail(self, mock_get_sub, unsued_mock):
    82      self.init_matcher(
    83          expected_msg=[PubsubMessage(b'a', {})], with_attributes=True)
    84      mock_sub = mock_get_sub.return_value
    85      # Unexpected attribute 'k'.
    86      mock_sub.pull.side_effect = [
    87          create_pull_response([PullResponseMessage(b'a', {'k': 'v'})])
    88      ]
    89      with self.assertRaisesRegex(AssertionError, r'Unexpected'):
    90        hc_assert_that(self.mock_presult, self.pubsub_matcher)
    91      self.assertEqual(mock_sub.pull.call_count, 1)
    92      self.assertEqual(mock_sub.acknowledge.call_count, 1)
    93  
    94    def test_message_matcher_strip_success(self, mock_get_sub, unsued_mock):
    95      self.init_matcher(
    96          expected_msg=[PubsubMessage(b'a', {'k': 'v'})],
    97          with_attributes=True,
    98          strip_attributes=['id', 'timestamp'])
    99      mock_sub = mock_get_sub.return_value
   100      mock_sub.pull.side_effect = [
   101          create_pull_response([
   102              PullResponseMessage(
   103                  b'a', {
   104                      'id': 'foo', 'timestamp': 'bar', 'k': 'v'
   105                  })
   106          ])
   107      ]
   108      hc_assert_that(self.mock_presult, self.pubsub_matcher)
   109      self.assertEqual(mock_sub.pull.call_count, 1)
   110      self.assertEqual(mock_sub.acknowledge.call_count, 1)
   111  
   112    def test_message_matcher_strip_fail(self, mock_get_sub, unsued_mock):
   113      self.init_matcher(
   114          expected_msg=[PubsubMessage(b'a', {'k': 'v'})],
   115          with_attributes=True,
   116          strip_attributes=['id', 'timestamp'])
   117      mock_sub = mock_get_sub.return_value
   118      # Message is missing attribute 'timestamp'.
   119      mock_sub.pull.side_effect = [
   120          create_pull_response(
   121              [PullResponseMessage(b'a', {
   122                  'id': 'foo', 'k': 'v'
   123              })])
   124      ]
   125      with self.assertRaisesRegex(AssertionError, r'Stripped attributes'):
   126        hc_assert_that(self.mock_presult, self.pubsub_matcher)
   127      self.assertEqual(mock_sub.pull.call_count, 1)
   128      self.assertEqual(mock_sub.acknowledge.call_count, 1)
   129  
   130    def test_message_matcher_mismatch(self, mock_get_sub, unused_mock):
   131      self.init_matcher(expected_msg=[b'a'])
   132      mock_sub = mock_get_sub.return_value
   133      mock_sub.pull.side_effect = [
   134          create_pull_response(
   135              [PullResponseMessage(b'c', {}), PullResponseMessage(b'd', {})]),
   136      ]
   137      with self.assertRaises(AssertionError) as error:
   138        hc_assert_that(self.mock_presult, self.pubsub_matcher)
   139      self.assertEqual(mock_sub.pull.call_count, 1)
   140      self.assertCountEqual([b'c', b'd'], self.pubsub_matcher.messages)
   141      self.assertIn(
   142          '\nExpected: Expected 1 messages.\n     but: Got 2 messages.',
   143          str(error.exception.args[0]))
   144      self.assertEqual(mock_sub.pull.call_count, 1)
   145      self.assertEqual(mock_sub.acknowledge.call_count, 1)
   146  
   147    def test_message_matcher_timeout(self, mock_get_sub, unused_mock):
   148      self.init_matcher(expected_msg=[b'a'])
   149      mock_sub = mock_get_sub.return_value
   150      mock_sub.return_value.full_name.return_value = 'mock_sub'
   151      self.pubsub_matcher.timeout = 0.1
   152      with self.assertRaisesRegex(AssertionError, r'Expected 1.*\n.*Got 0'):
   153        hc_assert_that(self.mock_presult, self.pubsub_matcher)
   154      self.assertTrue(mock_sub.pull.called)
   155      self.assertEqual(mock_sub.acknowledge.call_count, 0)
   156  
   157    def test_message_count_matcher_below_fail(self, mock_get_sub, unused_mock):
   158      self.init_counter_matcher(expected_msg_len=1)
   159      mock_sub = mock_get_sub.return_value
   160      mock_sub.pull.side_effect = [
   161          create_pull_response(
   162              [PullResponseMessage(b'c', {}), PullResponseMessage(b'd', {})]),
   163      ]
   164      with self.assertRaises(AssertionError) as error:
   165        hc_assert_that(self.mock_presult, self.pubsub_matcher)
   166      self.assertEqual(mock_sub.pull.call_count, 1)
   167      self.assertIn(
   168          '\nExpected: Expected 1 messages.\n     but: Got 2 messages.',
   169          str(error.exception.args[0]))
   170  
   171    def test_message_count_matcher_above_fail(self, mock_get_sub, unused_mock):
   172      self.init_counter_matcher(expected_msg_len=1)
   173      mock_sub = mock_get_sub.return_value
   174      self.pubsub_matcher.timeout = 0.1
   175      with self.assertRaisesRegex(AssertionError, r'Expected 1.*\n.*Got 0'):
   176        hc_assert_that(self.mock_presult, self.pubsub_matcher)
   177      self.assertTrue(mock_sub.pull.called)
   178      self.assertEqual(mock_sub.acknowledge.call_count, 0)
   179  
   180    def test_message_count_matcher_success(self, mock_get_sub, unused_mock):
   181      self.init_counter_matcher(expected_msg_len=15)
   182      mock_sub = mock_get_sub.return_value
   183      mock_sub.pull.side_effect = [
   184          create_pull_response(
   185              [PullResponseMessage(b'a', {'foo': 'bar'}) for _ in range(15)])
   186      ]
   187      hc_assert_that(self.mock_presult, self.pubsub_matcher)
   188      self.assertEqual(mock_sub.pull.call_count, 1)
   189      self.assertEqual(mock_sub.acknowledge.call_count, 1)
   190  
   191  
   192  if __name__ == '__main__':
   193    logging.getLogger().setLevel(logging.INFO)
   194    unittest.main()