예제 #1
0
    def test_iterate_over_pageable_resource_with_multiple_pages(self):
        objects = [
            {
                'items': ['foo']
            },
            {
                'items': ['bar']
            },
            {
                'items': ['buzz']
            },
            {
                'items': []
            },
        ]
        resource_func = mock.Mock(side_effect=objects)

        items = iterate_over_pageable_resource(resource_func,
                                               {'query_params': {}})
        assert ['foo'] == list(items)

        resource_func.reset_mock()
        resource_func = mock.Mock(side_effect=objects)
        items = iterate_over_pageable_resource(resource_func,
                                               {'query_params': {
                                                   'limit': 1
                                               }})
        assert ['foo', 'bar', 'buzz'] == list(items)
예제 #2
0
    def test_iterate_over_pageable_resource_should_preserve_query_params(self):
        resource_func = mock.Mock(return_value={'items': []})

        items = iterate_over_pageable_resource(resource_func, {'query_params': {'filter': 'name:123'}})

        assert [] == list(items)
        resource_func.assert_called_once_with(params={'query_params': {'filter': 'name:123', 'offset': 0, 'limit': 10}})
예제 #3
0
    def test_iterate_over_pageable_resource_with_no_items(self):
        resource_func = mock.Mock(return_value={'items': []})

        items = iterate_over_pageable_resource(resource_func,
                                               {'query_params': {}})

        assert [] == list(items)
예제 #4
0
    def test_iterate_over_pageable_resource_should_pass_with_string_offset_and_limit(
            self):
        resource_func = mock.Mock(side_effect=[
            {
                'items': ['foo']
            },
            {
                'items': []
            },
        ])

        items = iterate_over_pageable_resource(
            resource_func, {'query_params': {
                'offset': '1',
                'limit': '1'
            }})

        assert ['foo'] == list(items)
        resource_func.assert_has_calls([
            call(params={'query_params': {
                'offset': '1',
                'limit': '1'
            }}),
            call(params={'query_params': {
                'offset': 2,
                'limit': '1'
            }})
        ])
예제 #5
0
    def test_iterate_over_pageable_resource_raises_exception_when_server_returned_more_items_than_requested(
            self):
        resource_func = mock.Mock(side_effect=[
            {
                'items': ['foo', 'redundant_bar']
            },
            {
                'items': []
            },
        ])

        with pytest.raises(FtdUnexpectedResponse):
            list(
                iterate_over_pageable_resource(
                    resource_func,
                    {'query_params': {
                        'offset': '1',
                        'limit': '1'
                    }}))

        resource_func.assert_has_calls(
            [call(params={'query_params': {
                'offset': '1',
                'limit': '1'
            }})])
예제 #6
0
 def _connection_response(response, status=200):
     response_mock = mock.Mock()
     response_mock.getcode.return_value = status
     response_text = json.dumps(
         response) if type(response) is dict else response
     response_data = BytesIO(
         response_text.encode() if response_text else ''.encode())
     return response_mock, response_data
    def test_check_compatibility(self, mock__init__):
        for num, case in enumerate(data_check_compatibility):
            config = vmware_host_sriov.VmwareAdapterConfigManager()
            config.module = mock.Mock()
            config.module.check_mode = False
            config.module.fail_json.side_effect = fail_json

            config.__dict__.update(gen_mock_attrs(case["user_input"]))

            try:
                result = config.check_compatibility(
                    case["before"], case["user_input"]["esxi_host_name"])
                self.assertIsInstance(result, type(case["expected"]),
                                      "test=" + str(num))
                self.assertEqual(result, case["expected"], "test=" + str(num))

            except Exception as e:
                self.assertIsInstance(e, type(case["expected"]),
                                      "test=" + str(num))
                self.assertEqual(e.args[0], case["expected"].args[0],
                                 "test=" + str(num))

        for num, case in enumerate(data_check_compatibility):
            config = vmware_host_sriov.VmwareAdapterConfigManager()
            config.module = mock.Mock()
            config.module.check_mode = False
            config.module.fail_json.side_effect = fail_json

            config.__dict__.update(gen_mock_attrs(case["user_input"]))

            try:
                result = config.check_compatibility(
                    case["before"], case["user_input"]["esxi_host_name"])
                self.assertIsInstance(result, type(case["expected"]),
                                      "test=" + str(num))
                self.assertEqual(result, case["expected"], "test=" + str(num))

            except Exception as e:
                self.assertIsInstance(e, type(case["expected"]),
                                      "test=" + str(num))
                self.assertEqual(e.args[0], case["expected"].args[0],
                                 "test=" + str(num))
예제 #8
0
    def test_iterate_over_pageable_resource_with_multiple_pages(self):
        resource_func = mock.Mock(side_effect=[
            {'items': ['foo']},
            {'items': ['bar']},
            {'items': ['buzz']},
            {'items': []},
        ])

        items = iterate_over_pageable_resource(resource_func)

        assert ['foo', 'bar', 'buzz'] == list(items)
예제 #9
0
    def test_iterate_over_pageable_resource_with_one_page(self):
        resource_func = mock.Mock(side_effect=[
            {'items': ['foo', 'bar']},
            {'items': []},
        ])

        items = iterate_over_pageable_resource(resource_func, {'query_params': {}})

        assert ['foo', 'bar'] == list(items)
        resource_func.assert_has_calls([
            call(params={'query_params': {'offset': 0, 'limit': 10}})
        ])
예제 #10
0
    def test_iterate_over_pageable_resource_should_preserve_offset(self):
        resource_func = mock.Mock(side_effect=[
            {'items': ['foo']},
            {'items': []},
        ])

        items = iterate_over_pageable_resource(resource_func, {'query_params': {'offset': 3}})

        assert ['foo'] == list(items)
        resource_func.assert_has_calls([
            call(params={'query_params': {'offset': 3, 'limit': 10}}),
        ])
예제 #11
0
 def test_import_key_with_http_proxy(self):
     m_mock = mock.Mock()
     m_mock.run_command.return_value = (0, '', '')
     apt_key.import_key(m_mock,
                        keyring=None,
                        keyserver='keyserver.example.com',
                        key_id='0xDEADBEEF')
     self.assertEqual(
         m_mock.run_command.call_args_list[0][0][0],
         '/usr/bin/apt-key adv --no-tty --keyserver keyserver.example.com'
         ' --keyserver-options http-proxy=proxy.example.com'
         ' --recv 0xDEADBEEF')
예제 #12
0
    def test_download_file_should_extract_filename_from_headers(self):
        filename = 'test_file.txt'
        response = mock.Mock()
        response.info.return_value = {'Content-Disposition': 'attachment; filename="%s"' % filename}
        dummy, response_data = self._connection_response('File content')
        self.connection_mock.send.return_value = response, response_data

        open_mock = mock_open()
        with patch('%s.open' % BUILTINS_NAME, open_mock):
            self.ftd_plugin.download_file('/files/1', '/tmp/')

        open_mock.assert_called_once_with('/tmp/%s' % filename, 'wb')
        open_mock().write.assert_called_once_with(b'File content')
    def test_make_diff(self, mock__init__):
        for num, case in enumerate(data_make_diff):
            config = vmware_host_sriov.VmwareAdapterConfigManager()
            config.module = mock.Mock()
            config.module.check_mode = False
            config.module.fail_json.side_effect = fail_json

            config.__dict__.update(gen_mock_attrs(case["user_input"]))

            result = config.make_diff(case["before"],
                                      case["user_input"]["esxi_host_name"])
            self.assertIsInstance(result, type(case["expected"]),
                                  "test=" + str(num))
            self.assertEqual(result, case["expected"], "test=" + str(num))
예제 #14
0
 def setUp(self):
     self.connection_mock = mock.Mock()
     self.ftd_plugin = FakeFtdHttpApiPlugin(self.connection_mock)
     self.ftd_plugin.access_token = 'ACCESS_TOKEN'
     self.ftd_plugin._load_name = 'httpapi'
예제 #15
0
def fake_ansible_module():
    ret = mock.Mock()
    ret.params = test_data[3][0]
    ret.tmpdir = None
    ret.fail_json.side_effect = FailJsonException()
    return ret
예제 #16
0
def fake_connect_to_api(module, return_si=None):
    return mock.Mock()
def gen_mock_attrs(user_input):
    mock_attrs = dict(user_input)
    mock_attrs["results"] = {"before": {}, "after": {}, "changes": {}}
    mock_host = mock.Mock()
    mock_attrs["hosts"] = [mock_host]
    return mock_attrs
예제 #18
0
def test_virt_net_recreate(virt_net_obj, dummy_libvirt):
    virt_net_obj.conn.create = mock.Mock()
    dummy_libvirt.libvirtError.error_code = 'VIR_ERR_NETWORK_EXIST'
    virt_net_obj.conn.create.side_effect = dummy_libvirt.libvirtError
    assert virt_net_obj.create("active_net") is None
예제 #19
0
 def setUp(self):
     self.connection_mock = mock.Mock()
     self.checkpoint_plugin = FakeCheckpointHttpApiPlugin(
         self.connection_mock)
     self.checkpoint_plugin._load_name = 'httpapi'
예제 #20
0
def test_virt_stop_active(virt_net_obj, monkeypatch):
    virt_net_obj.conn.destroy = mock.Mock()
    virt_net_obj.stop('active_net')
    virt_net_obj.conn.destroy.assert_called_with('active_net')
예제 #21
0
def test_virt_stop_ignore_inactive(virt_net_obj):
    virt_net_obj.conn.destroy = mock.Mock()
    virt_net_obj.stop('inactive_net')
    virt_net_obj.conn.destroy.assert_not_called()
예제 #22
0
def test_virt_net_create_already_active(virt_net_obj, dummy_libvirt):
    virt_net_obj.conn.create = mock.Mock()
    assert virt_net_obj.create("active_net") is None
    virt_net_obj.conn.create.assert_not_called()
예제 #23
0
 def test_pkgname_expands(self):
     foo = ["apt*"]
     m_mock = mock.Mock()
     self.assertEqual(
         expand_pkgspec_from_fnmatches(m_mock, foo, self.fake_cache),
         ["apt", "apt-utils"])
예제 #24
0
 def test_pkgname_wildcard_version_wildcard(self):
     foo = ["apt*=1.0*"]
     m_mock = mock.Mock()
     self.assertEqual(
         expand_pkgspec_from_fnmatches(m_mock, foo, self.fake_cache),
         ['apt', 'apt-utils'])
예제 #25
0
def test_connect_to_api_validate_certs(monkeypatch, fake_ansible_module):
    monkeypatch.setattr(vmware_module_utils, 'connect', mock.Mock())

    def MockSSLContext(proto):
        ssl_context.proto = proto
        return ssl_context

    # New Python with SSLContext + validate_certs=True
    vmware_module_utils.connect.reset_mock()
    ssl_context = mock.Mock()
    monkeypatch.setattr(vmware_module_utils.ssl, 'SSLContext', MockSSLContext)
    fake_ansible_module.params['validate_certs'] = True
    vmware_module_utils.connect_to_api(fake_ansible_module)
    assert ssl_context.proto == ssl.PROTOCOL_SSLv23
    assert ssl_context.verify_mode == ssl.CERT_REQUIRED
    assert ssl_context.check_hostname is True
    vmware_module_utils.connect.SmartConnect.assert_called_once_with(
        host='esxi1',
        port=443,
        pwd='Esxi@123$%',
        user='******',
        sslContext=ssl_context)

    # New Python with SSLContext + validate_certs=False
    vmware_module_utils.connect.reset_mock()
    ssl_context = mock.Mock()
    monkeypatch.setattr(vmware_module_utils.ssl, 'SSLContext', MockSSLContext)
    fake_ansible_module.params['validate_certs'] = False
    vmware_module_utils.connect_to_api(fake_ansible_module)
    assert ssl_context.proto == ssl.PROTOCOL_SSLv23
    assert ssl_context.verify_mode == ssl.CERT_NONE
    assert ssl_context.check_hostname is False
    vmware_module_utils.connect.SmartConnect.assert_called_once_with(
        host='esxi1',
        port=443,
        pwd='Esxi@123$%',
        user='******',
        sslContext=ssl_context)

    # Old Python with no SSLContext + validate_certs=True
    vmware_module_utils.connect.reset_mock()
    ssl_context = mock.Mock()
    ssl_context.proto = None
    monkeypatch.delattr(vmware_module_utils.ssl, 'SSLContext')
    fake_ansible_module.params['validate_certs'] = True
    with pytest.raises(FailJsonException):
        vmware_module_utils.connect_to_api(fake_ansible_module)
    assert ssl_context.proto is None
    fake_ansible_module.fail_json.assert_called_once_with(
        msg=('pyVim does not support changing verification mode with python '
             '< 2.7.9. Either update python or use validate_certs=false.'))
    assert not vmware_module_utils.connect.SmartConnect.called

    # Old Python with no SSLContext + validate_certs=False
    vmware_module_utils.connect.reset_mock()
    ssl_context = mock.Mock()
    ssl_context.proto = None
    monkeypatch.delattr(vmware_module_utils.ssl, 'SSLContext', raising=False)
    fake_ansible_module.params['validate_certs'] = False
    vmware_module_utils.connect_to_api(fake_ansible_module)
    assert ssl_context.proto is None
    vmware_module_utils.connect.SmartConnect.assert_called_once_with(
        host='esxi1',
        port=443,
        pwd='Esxi@123$%',
        user='******')
예제 #26
0
class TestFtdHttpApi(unittest.TestCase):
    def setUp(self):
        self.connection_mock = mock.Mock()
        self.ftd_plugin = FakeFtdHttpApiPlugin(self.connection_mock)
        self.ftd_plugin.access_token = 'ACCESS_TOKEN'
        self.ftd_plugin._load_name = 'httpapi'

    def test_login_should_request_tokens_when_no_refresh_token(self):
        self.connection_mock.send.return_value = self._connection_response({
            'access_token':
            'ACCESS_TOKEN',
            'refresh_token':
            'REFRESH_TOKEN'
        })

        self.ftd_plugin.login('foo', 'bar')

        assert 'ACCESS_TOKEN' == self.ftd_plugin.access_token
        assert 'REFRESH_TOKEN' == self.ftd_plugin.refresh_token
        assert {
            'Authorization': 'Bearer ACCESS_TOKEN'
        } == self.ftd_plugin.connection._auth
        expected_body = json.dumps({
            'grant_type': 'password',
            'username': '******',
            'password': '******'
        })
        self.connection_mock.send.assert_called_once_with(mock.ANY,
                                                          expected_body,
                                                          headers=mock.ANY,
                                                          method=mock.ANY)

    def test_login_should_update_tokens_when_refresh_token_exists(self):
        self.ftd_plugin.refresh_token = 'REFRESH_TOKEN'
        self.connection_mock.send.return_value = self._connection_response({
            'access_token':
            'NEW_ACCESS_TOKEN',
            'refresh_token':
            'NEW_REFRESH_TOKEN'
        })

        self.ftd_plugin.login('foo', 'bar')

        assert 'NEW_ACCESS_TOKEN' == self.ftd_plugin.access_token
        assert 'NEW_REFRESH_TOKEN' == self.ftd_plugin.refresh_token
        assert {
            'Authorization': 'Bearer NEW_ACCESS_TOKEN'
        } == self.ftd_plugin.connection._auth
        expected_body = json.dumps({
            'grant_type': 'refresh_token',
            'refresh_token': 'REFRESH_TOKEN'
        })
        self.connection_mock.send.assert_called_once_with(mock.ANY,
                                                          expected_body,
                                                          headers=mock.ANY,
                                                          method=mock.ANY)

    def test_login_should_use_host_variable_when_set(self):
        temp_token_path = self.ftd_plugin.hostvars['token_path']
        self.ftd_plugin.hostvars['token_path'] = '/testFakeLoginUrl'
        self.connection_mock.send.return_value = self._connection_response({
            'access_token':
            'ACCESS_TOKEN',
            'refresh_token':
            'REFRESH_TOKEN'
        })

        self.ftd_plugin.login('foo', 'bar')

        self.connection_mock.send.assert_called_once_with('/testFakeLoginUrl',
                                                          mock.ANY,
                                                          headers=mock.ANY,
                                                          method=mock.ANY)
        self.ftd_plugin.hostvars['token_path'] = temp_token_path

    def test_login_raises_exception_when_no_refresh_token_and_no_credentials(
            self):
        with self.assertRaises(AnsibleConnectionFailure) as res:
            self.ftd_plugin.login(None, None)
        assert 'Username and password are required' in str(res.exception)

    def test_login_raises_exception_when_invalid_response(self):
        self.connection_mock.send.return_value = self._connection_response(
            {'no_access_token': 'ACCESS_TOKEN'})

        with self.assertRaises(ConnectionError) as res:
            self.ftd_plugin.login('foo', 'bar')

        assert 'Server returned response without token info during connection authentication' in str(
            res.exception)

    def test_login_raises_exception_when_http_error(self):
        self.connection_mock.send.side_effect = HTTPError(
            'http://testhost.com', 400, '', {},
            StringIO('{"message": "Failed to authenticate user"}'))

        with self.assertRaises(ConnectionError) as res:
            self.ftd_plugin.login('foo', 'bar')

        assert 'Failed to authenticate user' in str(res.exception)

    def test_logout_should_revoke_tokens(self):
        self.ftd_plugin.access_token = 'ACCESS_TOKEN_TO_REVOKE'
        self.ftd_plugin.refresh_token = 'REFRESH_TOKEN_TO_REVOKE'
        self.connection_mock.send.return_value = self._connection_response(
            None)

        self.ftd_plugin.logout()

        assert self.ftd_plugin.access_token is None
        assert self.ftd_plugin.refresh_token is None
        expected_body = json.dumps({
            'grant_type': 'revoke_token',
            'access_token': 'ACCESS_TOKEN_TO_REVOKE',
            'token_to_revoke': 'REFRESH_TOKEN_TO_REVOKE'
        })
        self.connection_mock.send.assert_called_once_with(mock.ANY,
                                                          expected_body,
                                                          headers=mock.ANY,
                                                          method=mock.ANY)

    def test_send_request_should_send_correct_request(self):
        exp_resp = {'id': '123', 'name': 'foo'}
        self.connection_mock.send.return_value = self._connection_response(
            exp_resp)

        resp = self.ftd_plugin.send_request('/test/{objId}',
                                            HTTPMethod.PUT,
                                            body_params={'name': 'foo'},
                                            path_params={'objId': '123'},
                                            query_params={'at': 0})

        assert {
            ResponseParams.SUCCESS: True,
            ResponseParams.STATUS_CODE: 200,
            ResponseParams.RESPONSE: exp_resp
        } == resp
        self.connection_mock.send.assert_called_once_with(
            '/test/123?at=0',
            '{"name": "foo"}',
            method=HTTPMethod.PUT,
            headers=EXPECTED_BASE_HEADERS)

    def test_send_request_should_return_empty_dict_when_no_response_data(self):
        self.connection_mock.send.return_value = self._connection_response(
            None)

        resp = self.ftd_plugin.send_request('/test', HTTPMethod.GET)

        assert {
            ResponseParams.SUCCESS: True,
            ResponseParams.STATUS_CODE: 200,
            ResponseParams.RESPONSE: {}
        } == resp
        self.connection_mock.send.assert_called_once_with(
            '/test',
            None,
            method=HTTPMethod.GET,
            headers=EXPECTED_BASE_HEADERS)

    def test_send_request_should_return_error_info_when_http_error_raises(
            self):
        self.connection_mock.send.side_effect = HTTPError(
            'http://testhost.com', 500, '', {},
            StringIO('{"errorMessage": "ERROR"}'))

        resp = self.ftd_plugin.send_request('/test', HTTPMethod.GET)

        assert {
            ResponseParams.SUCCESS: False,
            ResponseParams.STATUS_CODE: 500,
            ResponseParams.RESPONSE: {
                'errorMessage': 'ERROR'
            }
        } == resp

    def test_send_request_raises_exception_when_invalid_response(self):
        self.connection_mock.send.return_value = self._connection_response(
            'nonValidJson')

        with self.assertRaises(ConnectionError) as res:
            self.ftd_plugin.send_request('/test', HTTPMethod.GET)

        assert 'Invalid JSON response' in str(res.exception)

    def test_handle_httperror_should_update_tokens_and_retry_on_auth_errors(
            self):
        self.ftd_plugin.refresh_token = 'REFRESH_TOKEN'
        self.connection_mock.send.return_value = self._connection_response({
            'access_token':
            'NEW_ACCESS_TOKEN',
            'refresh_token':
            'NEW_REFRESH_TOKEN'
        })

        retry = self.ftd_plugin.handle_httperror(
            HTTPError('http://testhost.com', 401, '', {}, None))

        assert retry
        assert 'NEW_ACCESS_TOKEN' == self.ftd_plugin.access_token
        assert 'NEW_REFRESH_TOKEN' == self.ftd_plugin.refresh_token

    def test_handle_httperror_should_not_retry_on_non_auth_errors(self):
        assert not self.ftd_plugin.handle_httperror(
            HTTPError('http://testhost.com', 500, '', {}, None))

    def test_handle_httperror_should_not_retry_when_ignoring_http_errors(self):
        self.ftd_plugin._ignore_http_errors = True
        assert not self.ftd_plugin.handle_httperror(
            HTTPError('http://testhost.com', 401, '', {}, None))

    @patch('os.path.isdir', mock.Mock(return_value=False))
    def test_download_file(self):
        self.connection_mock.send.return_value = self._connection_response(
            'File content')

        open_mock = mock_open()
        with patch('%s.open' % BUILTINS, open_mock):
            self.ftd_plugin.download_file('/files/1', '/tmp/test.txt')

        open_mock.assert_called_once_with('/tmp/test.txt', 'wb')
        open_mock().write.assert_called_once_with(b'File content')

    @patch('os.path.isdir', mock.Mock(return_value=True))
    def test_download_file_should_extract_filename_from_headers(self):
        filename = 'test_file.txt'
        response = mock.Mock()
        response.info.return_value = {
            'Content-Disposition': 'attachment; filename="%s"' % filename
        }
        dummy, response_data = self._connection_response('File content')
        self.connection_mock.send.return_value = response, response_data

        open_mock = mock_open()
        with patch('%s.open' % BUILTINS, open_mock):
            self.ftd_plugin.download_file('/files/1', '/tmp/')

        open_mock.assert_called_once_with('/tmp/%s' % filename, 'wb')
        open_mock().write.assert_called_once_with(b'File content')

    @patch('os.path.basename', mock.Mock(return_value='test.txt'))
    @patch('ansible.plugins.httpapi.ftd.encode_multipart_formdata',
           mock.Mock(return_value=('--Encoded data--', 'multipart/form-data')))
    def test_upload_file(self):
        self.connection_mock.send.return_value = self._connection_response(
            {'id': '123'})

        open_mock = mock_open()
        with patch('%s.open' % BUILTINS, open_mock):
            resp = self.ftd_plugin.upload_file('/tmp/test.txt', '/files')

        assert {'id': '123'} == resp
        exp_headers = dict(EXPECTED_BASE_HEADERS)
        exp_headers['Content-Length'] = len('--Encoded data--')
        exp_headers['Content-Type'] = 'multipart/form-data'
        self.connection_mock.send.assert_called_once_with(
            '/files',
            data='--Encoded data--',
            headers=exp_headers,
            method=HTTPMethod.POST)
        open_mock.assert_called_once_with('/tmp/test.txt', 'rb')

    @patch('os.path.basename', mock.Mock(return_value='test.txt'))
    @patch('ansible.plugins.httpapi.ftd.encode_multipart_formdata',
           mock.Mock(return_value=('--Encoded data--', 'multipart/form-data')))
    def test_upload_file_raises_exception_when_invalid_response(self):
        self.connection_mock.send.return_value = self._connection_response(
            'invalidJsonResponse')

        open_mock = mock_open()
        with patch('%s.open' % BUILTINS, open_mock):
            with self.assertRaises(ConnectionError) as res:
                self.ftd_plugin.upload_file('/tmp/test.txt', '/files')

        assert 'Invalid JSON response' in str(res.exception)

    @patch.object(FdmSwaggerParser, 'parse_spec')
    def test_get_operation_spec(self, parse_spec_mock):
        self.connection_mock.send.return_value = self._connection_response(
            None)
        parse_spec_mock.return_value = {
            SpecProp.OPERATIONS: {
                'testOp': 'Specification for testOp'
            }
        }

        assert 'Specification for testOp' == self.ftd_plugin.get_operation_spec(
            'testOp')
        assert self.ftd_plugin.get_operation_spec('nonExistingTestOp') is None

    @patch.object(FdmSwaggerParser, 'parse_spec')
    def test_get_model_spec(self, parse_spec_mock):
        self.connection_mock.send.return_value = self._connection_response(
            None)
        parse_spec_mock.return_value = {
            SpecProp.MODELS: {
                'TestModel': 'Specification for TestModel'
            }
        }

        assert 'Specification for TestModel' == self.ftd_plugin.get_model_spec(
            'TestModel')
        assert self.ftd_plugin.get_model_spec('NonExistingTestModel') is None

    @patch.object(FdmSwaggerParser, 'parse_spec')
    def test_get_model_spec(self, parse_spec_mock):
        self.connection_mock.send.return_value = self._connection_response(
            None)
        operation1 = {'modelName': 'TestModel'}
        op_model_name_is_none = {'modelName': None}
        op_without_model_name = {'url': 'testUrl'}

        parse_spec_mock.return_value = {
            SpecProp.MODEL_OPERATIONS: {
                'TestModel': {
                    'testOp1': operation1,
                    'testOp2': 'spec2'
                },
                'TestModel2': {
                    'testOp10': 'spec10',
                    'testOp20': 'spec20'
                }
            },
            SpecProp.OPERATIONS: {
                'testOp1': operation1,
                'testOp10': {
                    'modelName': 'TestModel2'
                },
                'testOpWithoutModelName': op_without_model_name,
                'testOpModelNameIsNone': op_model_name_is_none
            }
        }

        assert {
            'testOp1': operation1,
            'testOp2': 'spec2'
        } == self.ftd_plugin.get_operation_specs_by_model_name('TestModel')
        assert None is self.ftd_plugin.get_operation_specs_by_model_name(
            'testOpModelNameIsNone')

        assert None is self.ftd_plugin.get_operation_specs_by_model_name(
            'testOpWithoutModelName')

        assert self.ftd_plugin.get_operation_specs_by_model_name(
            'nonExistingOperation') is None

    @staticmethod
    def _connection_response(response, status=200):
        response_mock = mock.Mock()
        response_mock.getcode.return_value = status
        response_text = json.dumps(
            response) if type(response) is dict else response
        response_data = BytesIO(
            response_text.encode() if response_text else ''.encode())
        return response_mock, response_data