示例#1
0
    def test_call_jobs_parallel_future_success(self, mock_present_sounds, mock_past_sounds):
        client = Client({})

        future = client.call_jobs_parallel_future(
            [
                {'service_name': 'future_service', 'actions': [
                    {'action': 'present_sounds', 'body': {'where': 'here'}},
                ]},
                {'service_name': 'future_service', 'actions': [
                    {'action': 'past_sounds', 'body': {'where': 'there'}},
                ]},
            ],
        )

        mock_present_sounds.assert_called_once_with({'where': 'here'})
        mock_past_sounds.assert_called_once_with({'where': 'there'})

        assert len(future.result()) == 2

        assert future.result()[0].actions[0].errors == []
        assert future.result()[0].actions[0].action == 'present_sounds'
        assert future.result()[0].actions[0].body == {'when': 'present'}

        assert future.result()[1].actions[0].errors == []
        assert future.result()[1].actions[0].action == 'past_sounds'
        assert future.result()[1].actions[0].body == {'when': 'past'}
示例#2
0
    def test_client_settings(self):
        """The client is successfully instantiated with settings, Redis Transport"""

        settings_dict = {
            'test_service': {
                'transport': {
                    'path':
                    'pysoa.common.transport.redis_gateway.client:RedisClientTransport',
                    'kwargs': {
                        'backend_type': REDIS_BACKEND_TYPE_STANDARD,
                        'serializer_config': {
                            'path': 'pysoa.common.serializer:JSONSerializer'
                        }
                    }
                },
            },
        }

        client = Client(settings_dict)
        handler = client._get_handler('test_service')

        assert isinstance(handler.transport, RedisClientTransport)
        assert handler.transport._send_queue_name == 'service.test_service'
        assert isinstance(handler.transport.core, RedisTransportCore)
        assert handler.transport.core.backend_type == REDIS_BACKEND_TYPE_STANDARD
        assert isinstance(handler.transport.core.serializer, JSONSerializer)
示例#3
0
    def test_send_request_with_suppress_response_then_get_response_error(self):
        """
        Client.send_request with suppress_response sends a valid request and Client.get_all_responses returns no
        response because the response was suppressed
        """
        action_request = [
            {
                'action': 'action_1',
                'body': {},
            },
            {
                'action': 'action_2',
                'body': {},
            },
        ]
        client = Client(self.client_settings)

        responses = list(client.get_all_responses(SERVICE_NAME))
        self.assertEqual(len(responses), 0)

        request_id = client.send_request(
            SERVICE_NAME,
            action_request,
            switches={1},
            suppress_response=True,
        )
        self.assertTrue(request_id >= 0)
        responses = list(client.get_all_responses(SERVICE_NAME))
        self.assertEqual(len(responses), 0)
示例#4
0
 def test_call_actions_no_raise_action_errors(self):
     action_request = [
         {
             'action': 'action_1',
             'body': {'foo': 'bar'},
         },
         {
             'action': 'action_2',
             'body': {},
         },
     ]
     error_expected = [
         Error(
             code=ERROR_CODE_INVALID,
             message='Invalid input',
             field='foo'
         )
     ]
     self.client_settings[SERVICE_NAME]['transport']['kwargs']['action_map']['action_2'] = {'errors': error_expected}
     client = Client(self.client_settings)
     for actions in (action_request, [ActionRequest(**a) for a in action_request]):
         response = client.call_actions(SERVICE_NAME, actions, raise_action_errors=False)
         self.assertEqual(response.actions[0].body, {'foo': 'bar'})
         self.assertEqual(response.actions[1].errors, error_expected)
         self.assertIsNotNone(response.context['correlation_id'])
示例#5
0
    def test_call_actions_raises_exception_on_action_error(self):
        """Client.call_actions raises CallActionError when any action response is an error."""
        action_request = [
            {
                'action': 'action_1',
                'body': {'foo': 'bar'},
            },
            {
                'action': 'action_2',
                'body': {},
            },
        ]
        error_expected = [
            Error(
                code=ERROR_CODE_INVALID,
                message='Invalid input',
                field='foo',
            )
        ]
        self.client_settings[SERVICE_NAME]['transport']['kwargs']['action_map']['action_1'] = {'errors': error_expected}
        client = Client(self.client_settings)

        for actions in (action_request, [ActionRequest(**a) for a in action_request]):
            with self.assertRaises(Client.CallActionError) as e:
                client.call_actions(SERVICE_NAME, actions)
                self.assertEqual(len(e.value.actions), 1)
                self.assertEqual(e.value.actions[0].action, 'action_1')
                error_response = e.value.actions[0].errors
                self.assertEqual(len(error_response), 1)
                self.assertEqual(error_response[0].code, error_expected[0]['code'])
                self.assertEqual(error_response[0].message, error_expected[0]['message'])
                self.assertEqual(error_response[0].field, error_expected[0]['field'])
示例#6
0
 def test_call_action(self):
     """Client.call_action sends a valid request and returns a valid response without errors."""
     client = Client(self.client_settings)
     response = client.call_action(SERVICE_NAME, 'action_1')
     self.assertTrue(isinstance(response, ActionResponse))
     self.assertEqual(response.action, 'action_1')
     self.assertEqual(response.body['foo'], 'bar')
示例#7
0
    def test_call_action_future_verify_traceback(self, mock_present_sounds):
        client = Client({})

        future = client.call_action_future('future_service', 'present_sounds', body={'hello': 'world'})

        try:
            assert future.result()
            assert False, 'We should not have hit this line of code'
        except client.CallActionError:
            _, __, tb1 = sys.exc_info()

        try:
            assert future.result()
            assert False, 'We should not have hit this line of code'
        except client.CallActionError:
            _, __, tb2 = sys.exc_info()

        try:
            assert future.result()
            assert False, 'We should not have hit this line of code'
        except client.CallActionError:
            _, __, tb3 = sys.exc_info()

        assert traceback.format_tb(tb1)[1:] == traceback.format_tb(tb2)[2:]
        assert traceback.format_tb(tb2)[2:] == traceback.format_tb(tb3)[2:]
示例#8
0
    def test_call_actions_future_success(self, mock_present_sounds):
        client = Client({})

        future = client.call_actions_future(
            'future_service',
            [{'action': 'present_sounds', 'body': {'foo': 'bar'}}],
        )

        mock_present_sounds.assert_called_once_with({'foo': 'bar'})

        assert future.exception() is None

        response = future.result()

        assert response.errors == []
        assert response.actions[0].errors == []
        assert response.actions[0].body == {'baz': 'qux'}
        assert response.context == {}

        assert future.result() is response
        assert future.result() is response

        assert future.exception() is None

        mock_present_sounds.assert_called_once_with({'foo': 'bar'})
示例#9
0
    def test_call_actions_parallel_future_success(self, mock_present_sounds, mock_past_sounds):
        client = Client({})

        future = client.call_actions_parallel_future(
            'future_service',
            [
                {'action': 'present_sounds', 'body': {'where': 'here'}},
                {'action': 'past_sounds', 'body': {'where': 'there'}},
            ],
        )

        mock_present_sounds.assert_called_once_with({'where': 'here'})
        mock_past_sounds.assert_called_once_with({'where': 'there'})

        assert isinstance(future.result(), types.GeneratorType)

        responses = list(future.result())

        assert len(responses) == 2

        assert responses[0].errors == []
        assert responses[0].action == 'present_sounds'
        assert responses[0].body == {'when': 'present'}

        assert responses[1].errors == []
        assert responses[1].action == 'past_sounds'
        assert responses[1].body == {'when': 'past'}
示例#10
0
    def test_call_actions(self):
        """Client.call_actions sends a valid request and returns a valid response without errors."""
        action_request = [
            {
                'action': 'action_1',
                'body': {},
            },
            {
                'action': 'action_2',
                'body': {},
            },
        ]
        client = Client(self.client_settings)

        for actions in (action_request,
                        [ActionRequest(**a) for a in action_request]):
            response = client.call_actions(SERVICE_NAME, actions)
            self.assertTrue(isinstance(response, JobResponse))
            self.assertTrue(
                all([isinstance(a, ActionResponse) for a in response.actions]))
            self.assertEqual(len(response.actions), 2)
            # ensure that the response is structured as expected
            self.assertEqual(response.actions[0].action, 'action_1')
            self.assertEqual(response.actions[0].body['foo'], 'bar')
            self.assertEqual(response.actions[1].action, 'action_2')
            self.assertEqual(response.actions[1].body['baz'], 3)
示例#11
0
 def test_call_actions_raises_exception_on_job_error(self):
     """Client.call_actions raises Client.JobError when a JobError occurs on the server."""
     client = Client(self.client_settings)
     errors = [Error(code=ERROR_CODE_SERVER_ERROR, message='Something went wrong!')]
     with mock.patch.object(
         client._get_handler(SERVICE_NAME).transport.server,
         'execute_job',
         new=mock.Mock(side_effect=JobError(errors)),
     ):
         with self.assertRaises(Client.JobError) as e:
             client.call_action(SERVICE_NAME, 'action_1')
             self.assertEqual(e.errors, errors)
示例#12
0
    def test_call_jobs_parallel_future_error(self, mock_present_sounds, mock_past_sounds):
        client = Client({})

        future = client.call_jobs_parallel_future(
            [
                {'service_name': 'future_service', 'actions': [
                    {'action': 'present_sounds', 'body': {'where': 'here'}},
                ]},
                {'service_name': 'future_service', 'actions': [
                    {'action': 'past_sounds', 'body': {'where': 'there'}},
                ]},
            ],
        )

        mock_present_sounds.assert_called_once_with({'where': 'here'})
        mock_past_sounds.assert_called_once_with({'where': 'there'})

        with self.assertRaises(client.CallActionError) as error_context:
            assert future.result()

        first_exception = error_context.exception

        assert len(error_context.exception.actions[0].errors) == 1

        error = error_context.exception.actions[0].errors[0]
        assert error.code == 'BROKEN'
        assert error.message == 'Broken, too'

        with self.assertRaises(client.CallActionError) as error_context:
            assert future.result()

        assert error_context.exception is first_exception

        assert len(error_context.exception.actions[0].errors) == 1

        error = error_context.exception.actions[0].errors[0]
        assert error.code == 'BROKEN'
        assert error.message == 'Broken, too'

        with self.assertRaises(client.CallActionError) as error_context:
            assert future.result()

        assert error_context.exception is first_exception

        assert len(error_context.exception.actions[0].errors) == 1

        error = error_context.exception.actions[0].errors[0]
        assert error.code == 'BROKEN'
        assert error.message == 'Broken, too'

        mock_present_sounds.assert_called_once_with({'where': 'here'})
        mock_past_sounds.assert_called_once_with({'where': 'there'})
示例#13
0
 def setUp(self):
     self.client = Client({
         SERVICE_NAME: {
             'transport': {
                 'path': 'pysoa.test.stub_service:StubClientTransport',
                 'kwargs': {
                     'action_map': {
                         'action_1': {'body': {}},
                     },
                 },
             }
         }
     })
示例#14
0
def test_raises_only_error_codes_match(test_error, auth_missing_error):
    errors = [test_error, auth_missing_error]
    with raises_only_error_codes(['AUTH_MISSING', 'TEST']) as exc_info:
        raise Client.CallActionError(
            actions=[ActionResponse(action='', errors=errors)])

    assert exc_info.soa_errors == errors
示例#15
0
    def test_check_client_settings_no_settings(self):
        client = Client({})

        action_request = EnrichedActionRequest(action='status',
                                               body={},
                                               switches=None,
                                               client=client)

        response = _CheckOtherServicesAction()(action_request)

        self.assertIsInstance(response, ActionResponse)
        self.assertEqual(
            {
                'conformity': six.text_type(conformity.__version__),
                'pysoa': six.text_type(pysoa.__version__),
                'python': six.text_type(platform.python_version()),
                'version': '8.71.2',
                'healthcheck': {
                    'diagnostics': {},
                    'errors': [],
                    'warnings': []
                },
            },
            response.body,
        )
def test_send_receive_redis5_redis6_round_robin(
        pysoa_client: Client):  # noqa: E999
    for i in range(10):
        response = pysoa_client.call_action('echo',
                                            'status',
                                            body={'verbose': False})
        assert response.body, 'Iteration {} failed'.format(i)
示例#17
0
def test_raises_field_errors_on_match(invalid_event_id_field_error):
    errors = [invalid_event_id_field_error]
    with raises_field_errors({'event_id': 'INVALID'}) as exc_info:
        raise Client.CallActionError(
            actions=[ActionResponse(action='', errors=errors)])

    assert exc_info.soa_errors == errors
示例#18
0
def test_raises_error_only_codes_unexpected_missing(test_error,
                                                    auth_missing_error):
    errors = [test_error, auth_missing_error]
    with pytest.raises(pytest.raises.Exception):
        with raises_only_error_codes('UNAUTHORIZED'):
            raise Client.CallActionError(
                actions=[ActionResponse(action='', errors=errors)])
示例#19
0
    def test_call_action_job_error_not_raised(self):
        client = Client({
            'error_service': {
                'transport': {
                    'path': 'pysoa.common.transport.local:LocalClientTransport',
                    'kwargs': {
                        'server_class': ErrorServer,
                        'server_settings': {},
                    },
                },
            }
        })
        response = client.call_action('error_service', 'job_error', raise_job_errors=False)

        self.assertIsNotNone(response)
        self.assertEqual([Error(code='BAD_JOB', message='You are a bad job')], response)
示例#20
0
def test_raises_call_action_error_on_error(test_error):
    errors = [test_error]
    with raises_call_action_error() as exc_info:
        raise Client.CallActionError(
            actions=[ActionResponse(action='', errors=errors)])

    assert exc_info.soa_errors == errors
示例#21
0
def test_raises_error_codes_multiple(codes):
    errors = [Error(code=code, message='bam') for code in codes]
    with raises_error_codes(['TEST', 'AUTH_MISSING']) as exc_info:
        raise Client.CallActionError(
            actions=[ActionResponse(action='', errors=errors)])

    assert exc_info.soa_errors == errors
示例#22
0
def test_raises_error_codes_on_match(test_error):
    errors = [test_error]
    with raises_error_codes('TEST') as exc_info:
        raise Client.CallActionError(
            actions=[ActionResponse(action='', errors=errors)])

    assert exc_info.soa_errors == errors
示例#23
0
def test_raises_error_only_codes_unexpected_field_error(
        invalid_event_id_field_error, auth_missing_error):
    errors = [invalid_event_id_field_error, auth_missing_error]
    with pytest.raises(pytest.raises.Exception):
        with raises_only_error_codes('AUTH_MISSING'):
            raise Client.CallActionError(
                actions=[ActionResponse(action='', errors=errors)])
示例#24
0
 def setUp(self):
     self.client = Client({
         'service_1': {
             'transport': {
                 'path': 'pysoa.test.stub_service:StubClientTransport',
                 'kwargs': {
                     'action_map': {
                         'action_1': {'body': {'foo': 'bar'}},
                         'action_2': {'body': {'baz': 3}},
                     },
                 },
             },
         },
         'service_2': {
             'transport': {
                 'path': 'pysoa.test.stub_service:StubClientTransport',
                 'kwargs': {
                     'action_map': {
                         'action_3': {'body': {'cat': 'dog'}},
                         'action_4': {'body': {'selected': True, 'count': 7}},
                         'action_with_errors': {
                             'errors': [Error(code=ERROR_CODE_INVALID, message='Invalid input', field='foo')],
                         },
                     },
                 },
             },
         },
         'error_service': {
             'transport': {
                 'path': 'pysoa.common.transport.local:LocalClientTransport',
                 'kwargs': {
                     'server_class': ErrorServer,
                     'server_settings': {},
                 },
             },
         },
         'send_error_service': {
             'transport': {
                 'path': 'tests.client.test_send_receive:SendErrorTransport',
             }
         },
         'receive_error_service': {
             'transport': {
                 'path': 'tests.client.test_send_receive:ReceiveErrorTransport',
             }
         },
     })
示例#25
0
    def test_check_client_settings_with_settings(self):
        client = Client({
            'foo': {'transport': {'path': 'pysoa.test.stub_service:StubClientTransport'}},
            'bar': {'transport': {'path': 'pysoa.test.stub_service:StubClientTransport'}},
            'baz': {'transport': {'path': 'pysoa.test.stub_service:StubClientTransport'}},
            'qux': {'transport': {'path': 'pysoa.test.stub_service:StubClientTransport'}},
        })

        action_request = EnrichedActionRequest(action='status', body={}, switches=None, client=client)

        baz_body = {
            'conformity': '1.2.3',
            'pysoa': '1.0.2',
            'python': '3.7.4',
            'version': '9.7.8',
        }

        with stub_action('foo', 'status') as foo_stub,\
                stub_action('bar', 'status', errors=[Error('BAR_ERROR', 'Bar error')]),\
                stub_action('baz', 'status', body=baz_body),\
                stub_action('qux', 'status') as qux_stub:
            foo_stub.return_value = JobResponse(errors=[Error('FOO_ERROR', 'Foo error')])
            qux_stub.side_effect = MessageReceiveTimeout('Timeout calling qux')

            response = _CheckOtherServicesAction()(action_request)

        self.assertIsInstance(response, ActionResponse)
        self.assertEqual(six.text_type(conformity.__version__), response.body['conformity'])
        self.assertEqual(six.text_type(pysoa.__version__), response.body['pysoa'])
        self.assertEqual(six.text_type(platform.python_version()), response.body['python'])
        self.assertEqual('8.71.2', response.body['version'])
        self.assertIn('healthcheck', response.body)
        self.assertEqual([], response.body['healthcheck']['warnings'])
        self.assertIn(
            ('FOO_CALL_ERROR', six.text_type([Error('FOO_ERROR', 'Foo error')])),
            response.body['healthcheck']['errors'],
        )
        self.assertIn(
            ('BAR_STATUS_ERROR', six.text_type([Error('BAR_ERROR', 'Bar error')])),
            response.body['healthcheck']['errors'],
        )
        self.assertIn(
            ('QUX_TRANSPORT_ERROR', 'Timeout calling qux'),
            response.body['healthcheck']['errors'],
        )
        self.assertEqual(3, len(response.body['healthcheck']['errors']))
        self.assertEqual(
            {
                'services': {
                    'baz': {
                        'conformity': '1.2.3',
                        'pysoa': '1.0.2',
                        'python': '3.7.4',
                        'version': '9.7.8',
                    },
                },
            },
            response.body['healthcheck']['diagnostics'],
        )
示例#26
0
def test_raises_field_errors_missing(code, field):
    errors = [
        Error(code=code, message='test fail', field=field),
    ]
    with pytest.raises(pytest.raises.Exception):
        with raises_field_errors({'event_id': 'UNKNOWN'}):
            raise Client.CallActionError(
                actions=[ActionResponse(action='', errors=errors)])
示例#27
0
    def test_call_actions_future_error(self, mock_present_sounds):
        client = Client({})

        future = client.call_actions_future(
            'future_service',
            [{'action': 'present_sounds', 'body': {'foo': 'bar'}}],
        )

        mock_present_sounds.assert_called_once_with({'foo': 'bar'})

        with self.assertRaises(client.CallActionError) as error_context:
            raise future.exception()

        first_exception = error_context.exception

        assert len(error_context.exception.actions[0].errors) == 1

        error = error_context.exception.actions[0].errors[0]
        assert error.code == 'BROKEN'
        assert error.message == 'Broken, dude'

        with self.assertRaises(client.CallActionError) as error_context:
            assert future.result()

        assert error_context.exception is first_exception

        assert len(error_context.exception.actions[0].errors) == 1

        error = error_context.exception.actions[0].errors[0]
        assert error.code == 'BROKEN'
        assert error.message == 'Broken, dude'

        with self.assertRaises(client.CallActionError) as error_context:
            raise future.exception()

        assert error_context.exception is first_exception

        assert len(error_context.exception.actions[0].errors) == 1

        error = error_context.exception.actions[0].errors[0]
        assert error.code == 'BROKEN'
        assert error.message == 'Broken, dude'

        mock_present_sounds.assert_called_once_with({'foo': 'bar'})
示例#28
0
def test_raises_field_errors_unexpected_only(invalid_event_id_field_error,
                                             unknown_event_id_field_error):
    errors = [
        invalid_event_id_field_error,
        unknown_event_id_field_error,
    ]
    with pytest.raises(pytest.raises.Exception):
        with raises_field_errors({'event_id': ['UNKNOWN']}, only=True):
            raise Client.CallActionError(
                actions=[ActionResponse(action='', errors=errors)])
示例#29
0
def test_raises_only_field_errors_unexpected_error(
        auth_missing_error, invalid_organization_id_field_error):
    errors = [
        auth_missing_error,
        invalid_organization_id_field_error,
    ]
    with pytest.raises(pytest.raises.Exception):
        with raises_only_field_errors({'organization_id': 'INVALID'}):
            raise Client.CallActionError(
                actions=[ActionResponse(action='', errors=errors)])
示例#30
0
def test_raises_only_field_errors_unexpected_missing(
        unknown_event_id_field_error, invalid_organization_id_field_error):
    errors = [
        unknown_event_id_field_error,
        invalid_organization_id_field_error,
    ]
    with pytest.raises(pytest.raises.Exception):
        with raises_only_field_errors({'event_id': 'MISSING'}):
            raise Client.CallActionError(
                actions=[ActionResponse(action='', errors=errors)])