github.com/apache/beam/sdks/v2@v2.48.2/python/apache_beam/testing/pipeline_verifiers_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 tests for the test pipeline verifiers""" 19 20 # pytype: skip-file 21 22 import logging 23 import os 24 import tempfile 25 import unittest 26 27 from hamcrest import assert_that as hc_assert_that 28 from mock import Mock 29 from mock import patch 30 31 from apache_beam.io.localfilesystem import LocalFileSystem 32 from apache_beam.runners.runner import PipelineResult 33 from apache_beam.runners.runner import PipelineState 34 from apache_beam.testing import pipeline_verifiers as verifiers 35 from apache_beam.testing.test_utils import patch_retry 36 37 try: 38 # pylint: disable=wrong-import-order, wrong-import-position 39 # pylint: disable=ungrouped-imports 40 from apitools.base.py.exceptions import HttpError 41 from apache_beam.io.gcp.gcsfilesystem import GCSFileSystem 42 except ImportError: 43 HttpError = None 44 GCSFileSystem = None # type: ignore 45 46 47 class PipelineVerifiersTest(unittest.TestCase): 48 def setUp(self): 49 self._mock_result = Mock() 50 patch_retry(self, verifiers) 51 52 def test_pipeline_state_matcher_success(self): 53 """Test PipelineStateMatcher successes when using default expected state 54 and job actually finished in DONE 55 """ 56 pipeline_result = PipelineResult(PipelineState.DONE) 57 hc_assert_that(pipeline_result, verifiers.PipelineStateMatcher()) 58 59 def test_pipeline_state_matcher_given_state(self): 60 """Test PipelineStateMatcher successes when matches given state""" 61 pipeline_result = PipelineResult(PipelineState.FAILED) 62 hc_assert_that( 63 pipeline_result, verifiers.PipelineStateMatcher(PipelineState.FAILED)) 64 65 def test_pipeline_state_matcher_fails(self): 66 """Test PipelineStateMatcher fails when using default expected state 67 and job actually finished in CANCELLED/DRAINED/FAILED/UNKNOWN 68 """ 69 failed_state = [ 70 PipelineState.CANCELLED, 71 PipelineState.DRAINED, 72 PipelineState.FAILED, 73 PipelineState.UNKNOWN 74 ] 75 76 for state in failed_state: 77 pipeline_result = PipelineResult(state) 78 with self.assertRaises(AssertionError): 79 hc_assert_that(pipeline_result, verifiers.PipelineStateMatcher()) 80 81 test_cases = [ 82 { 83 'content': 'Test FileChecksumMatcher with single file', 84 'num_files': 1, 85 'expected_checksum': 'ebe16840cc1d0b4fe1cf71743e9d772fa31683b8' 86 }, 87 { 88 'content': 'Test FileChecksumMatcher with multiple files', 89 'num_files': 3, 90 'expected_checksum': '58b3d3636de3891ac61afb8ace3b5025c3c37d44' 91 }, 92 { 93 'content': '', 94 'num_files': 1, 95 'expected_checksum': 'da39a3ee5e6b4b0d3255bfef95601890afd80709' 96 }, 97 ] 98 99 def create_temp_file(self, content, directory=None): 100 with tempfile.NamedTemporaryFile(delete=False, dir=directory) as f: 101 f.write(content.encode('utf-8')) 102 return f.name 103 104 def test_file_checksum_matcher_success(self): 105 for case in self.test_cases: 106 temp_dir = tempfile.mkdtemp() 107 for _ in range(case['num_files']): 108 self.create_temp_file(case['content'], temp_dir) 109 matcher = verifiers.FileChecksumMatcher( 110 os.path.join(temp_dir, '*'), case['expected_checksum']) 111 hc_assert_that(self._mock_result, matcher) 112 113 @patch.object(LocalFileSystem, 'match') 114 def test_file_checksum_matcher_read_failed(self, mock_match): 115 mock_match.side_effect = IOError('No file found.') 116 matcher = verifiers.FileChecksumMatcher( 117 os.path.join('dummy', 'path'), Mock()) 118 with self.assertRaises(IOError): 119 hc_assert_that(self._mock_result, matcher) 120 self.assertTrue(mock_match.called) 121 self.assertEqual(verifiers.MAX_RETRIES + 1, mock_match.call_count) 122 123 @patch.object(GCSFileSystem, 'match') 124 @unittest.skipIf(HttpError is None, 'google-apitools is not installed') 125 def test_file_checksum_matcher_service_error(self, mock_match): 126 mock_match.side_effect = HttpError( 127 response={'status': '404'}, 128 url='', 129 content='Not Found', 130 ) 131 matcher = verifiers.FileChecksumMatcher('gs://dummy/path', Mock()) 132 with self.assertRaises(HttpError): 133 hc_assert_that(self._mock_result, matcher) 134 self.assertTrue(mock_match.called) 135 self.assertEqual(verifiers.MAX_RETRIES + 1, mock_match.call_count) 136 137 def test_file_checksum_matchcer_invalid_sleep_time(self): 138 with self.assertRaises(ValueError) as cm: 139 verifiers.FileChecksumMatcher( 140 'file_path', 'expected_checksum', 'invalid_sleep_time') 141 self.assertEqual( 142 cm.exception.args[0], 143 'Sleep seconds, if received, must be int. ' 144 'But received: \'invalid_sleep_time\', ' 145 '{}'.format(str)) 146 147 @patch('time.sleep', return_value=None) 148 def test_file_checksum_matcher_sleep_before_verify(self, mocked_sleep): 149 temp_dir = tempfile.mkdtemp() 150 case = self.test_cases[0] 151 self.create_temp_file(case['content'], temp_dir) 152 matcher = verifiers.FileChecksumMatcher( 153 os.path.join(temp_dir, '*'), case['expected_checksum'], 10) 154 hc_assert_that(self._mock_result, matcher) 155 self.assertTrue(mocked_sleep.called) 156 157 158 if __name__ == '__main__': 159 logging.getLogger().setLevel(logging.INFO) 160 unittest.main()