Пример #1
0
def _evaluate_additional_properties_diffs(
        path,  # type: PathType
        left_spec,  # type: Spec
        right_spec,  # type: Spec
        left_schema,  # type: typing.Optional[typing.Mapping[typing.Text, typing.Any]]
        right_schema,  # type: typing.Optional[typing.Mapping[typing.Text, typing.Any]]
):
    # type: (...) -> typing.List[AdditionalPropertiesDiff]
    result = []  # type: typing.List[AdditionalPropertiesDiff]
    left_additional_properties = None if left_schema is None else left_schema.get(
        'additionalProperties', True)
    right_additional_properties = None if right_schema is None else right_schema.get(
        'additionalProperties', True)
    left_properties = get_properties(left_spec, left_schema) or set()
    right_properties = get_properties(right_spec, right_schema) or set()
    properties_appear_once = left_properties.symmetric_difference(
        right_properties)

    if left_additional_properties == {}:  # Normalize additional properties
        left_additional_properties = True
    if right_additional_properties == {}:  # Normalize additional properties
        right_additional_properties = True

    if not _cycle_safe_compare(left_additional_properties,
                               right_additional_properties):
        result.append(
            AdditionalPropertiesDiff(
                path=path,
                diff_type=DiffType.VALUE,
                additionalProperties=EntityMapping(
                    # casting here is safe as swagger specs are assumed to be valid and bool or dict are the only possible types
                    old=typing.cast(
                        typing.Union[bool, typing.Mapping[typing.Text,
                                                          typing.Any]],
                        left_additional_properties),
                    new=typing.cast(
                        typing.Union[bool, typing.Mapping[typing.Text,
                                                          typing.Any]],
                        right_additional_properties),
                ),
                properties=None,
            ))

    if (left_additional_properties is False or
            right_additional_properties is False) and properties_appear_once:
        result.append(
            AdditionalPropertiesDiff(
                path=path,
                diff_type=DiffType.PROPERTIES,
                additionalProperties=None,
                properties=EntityMapping(
                    old=properties_appear_once.intersection(left_properties),
                    new=properties_appear_once.intersection(right_properties),
                ),
            ))

    return result
Пример #2
0
def test_EntityMapping_equality_and_hash():
    entity_mappint_1 = EntityMapping(old=1, new=2)
    entity_mappint_2 = EntityMapping(old=1, new=2)
    entity_mappint_3 = EntityMapping(old=1, new=3)

    assert hash(entity_mappint_1) == hash(entity_mappint_2)
    assert entity_mappint_1 == entity_mappint_2

    assert hash(entity_mappint_1) != hash(entity_mappint_3)
    assert entity_mappint_1 != entity_mappint_3
def test_get_operation_mappings(minimal_spec, spec_and_operation):
    assert get_operation_mappings(minimal_spec, minimal_spec) == set()

    spec, operation = spec_and_operation
    assert get_operation_mappings(spec, minimal_spec) == set()
    assert get_operation_mappings(
        spec, spec) == {EntityMapping(operation, operation)}
def test_RequiredPropertiesDifferWalker_recursive_definition(
        minimal_spec_dict):

    minimal_spec_dict['definitions'] = {
        'recursive_object': {
            'type': 'object',
            'properties': {
                'property': {
                    '$ref': '#/definitions/model'
                },
                'recursive_property': {
                    '$ref': '#/definitions/recursive_object'
                },
            },
        },
    }
    left_spec_dict = dict(
        minimal_spec_dict,
        definitions={
            'model': {
                'properties': {
                    'old_only': {
                        'type': 'string'
                    },
                },
                'required': ['old_only'],
                'type': 'object',
            },
        },
    )
    right_spec_dict = dict(
        minimal_spec_dict,
        definitions={
            'model': {
                'properties': {
                    'old_only': {
                        'type': 'string'
                    },
                },
                'type': 'object',
            },
        },
    )
    left_spec = load_spec_from_spec_dict(spec_dict=left_spec_dict)
    right_spec = load_spec_from_spec_dict(spec_dict=right_spec_dict)

    assert RequiredPropertiesDifferWalker(
        left_spec=left_spec,
        right_spec=right_spec,
    ).walk() == [
        RequiredPropertiesDiff(path=('definitions', 'model'),
                               mapping=EntityMapping(old={'old_only'},
                                                     new=set()))
    ]
def get_operation_mappings(old_spec, new_spec):
    # type: (Spec, Spec) -> typing.Set[EntityMapping[Operation]]
    old_endpoints = get_endpoints(old_spec)
    old_endpoints_map = {  # Small hack to make endpoint search easy
        endpoint: endpoint
        for endpoint in old_endpoints
    }
    new_endpoints = get_endpoints(new_spec)
    new_endpoints_map = {  # Small hack to make endpoint search easy
        endpoint: endpoint
        for endpoint in new_endpoints
    }

    return {
        EntityMapping(old_endpoints_map[endpoint].operation,
                      new_endpoints_map[endpoint].operation)
        for endpoint in old_endpoints.intersection(new_endpoints)
    }
Пример #6
0
def _different_properties_mapping(
    left_spec,  # type: Spec
    right_spec,  # type: Spec
    left_schema,  # type: typing.Optional[typing.Mapping[typing.Text, typing.Any]]
    right_schema,  # type: typing.Optional[typing.Mapping[typing.Text, typing.Any]]
):
    # type: (...) -> typing.Optional[EntityMapping[typing.Set[typing.Text]]]
    left_required = get_required_properties(swagger_spec=left_spec, schema=left_schema) or set()
    right_required = get_required_properties(swagger_spec=right_spec, schema=right_schema) or set()

    properties_appear_once = left_required.symmetric_difference(right_required)
    if not properties_appear_once:
        # The condition is true if left_required is empty and right_required is not empty or vice-versa
        return None
    else:
        return EntityMapping(
            old=properties_appear_once.intersection(left_required),
            new=properties_appear_once.intersection(right_required),
        )
def iterate_on_responses_status_codes(
        old_operation,  # type: typing.Mapping[typing.Text, typing.Any]
        new_operation,  # type: typing.Mapping[typing.Text, typing.Any]
):
    # type: (...) -> typing.Generator[StatusCodeSchema, None, None]
    old_status_code_schema_mapping = old_operation.get('responses') or {}
    new_status_code_schema_mapping = new_operation.get('responses') or {}

    common_response_codes = set(
        iterkeys(old_status_code_schema_mapping)).intersection(
            set(iterkeys(new_status_code_schema_mapping)), )
    # Compare schemas for the same status code only (TODO: what to do for old=default and new=404?)
    for status_code in common_response_codes:
        yield StatusCodeSchema(
            status_code=status_code,
            mapping=EntityMapping(
                old=old_status_code_schema_mapping[status_code].get('schema'),
                new=new_status_code_schema_mapping[status_code].get('schema'),
            ),
        )
Пример #8
0
def test__evaluate_additional_properties_recursive_schema_reference_with_difference(
        mock_get_properties):
    left_schema = _construct_recursive_additional_properties()
    left_schema['properties']['bar'] = {'type': 'string'}
    right_schema = _construct_recursive_additional_properties()

    assert _evaluate_additional_properties_diffs(
        path=mock.sentinel.PATH,
        left_spec=mock.sentinel.LEFT_SPEC,
        right_spec=mock.sentinel.RIGHT_SPEC,
        left_schema=left_schema,
        right_schema=right_schema,
    ) == [
        AdditionalPropertiesDiff(
            path=mock.sentinel.PATH,
            diff_type=DiffType.VALUE,
            additionalProperties=EntityMapping(
                old=left_schema,
                new=right_schema,
            ),
            properties=None,
        ),
    ]
Пример #9
0
def _different_enum_values_mapping(
        left_spec,  # type: Spec
        right_spec,  # type: Spec
        left_schema,  # type: typing.Optional[typing.Mapping[typing.Text, typing.Any]]
        right_schema,  # type: typing.Optional[typing.Mapping[typing.Text, typing.Any]]
):
    # type: (...) -> typing.Optional[EntityMapping[typing.Set[typing.Text]]]
    left_enum_values = (set(left_schema['enum']) if
                        (left_schema and left_schema.get('type') == 'string'
                         and 'enum' in left_schema) else set())
    right_enum_values = (set(right_schema['enum']) if
                         (right_schema and right_schema.get('type') == 'string'
                          and 'enum' in right_schema) else set())

    enum_values_appear_once = left_enum_values.symmetric_difference(
        right_enum_values)
    if not enum_values_appear_once:
        # The condition is true if left_required is empty and right_required is not empty or vice-versa
        return None
    else:
        return EntityMapping(
            old=enum_values_appear_once.intersection(left_enum_values),
            new=enum_values_appear_once.intersection(right_enum_values),
        )
Пример #10
0
def _different_types_mapping(
        left_spec,  # type: Spec
        right_spec,  # type: Spec
        left_schema,  # type: typing.Optional[typing.Mapping[typing.Text, typing.Any]]
        right_schema,  # type: typing.Optional[typing.Mapping[typing.Text, typing.Any]]
):
    # type: (...) -> typing.Optional[EntityMapping[typing.Optional[typing.Text]]]
    left_type = left_schema.get('type') if left_schema is not None else None
    right_type = right_schema.get('type') if right_schema is not None else None

    if not left_type and left_spec.config[
            'default_type_to_object']:  # Normalize type according to spec configuration
        left_type = 'object'
    if not right_type and right_spec.config[
            'default_type_to_object']:  # Normalize type according to spec configuration
        right_type = 'object'

    if left_type == right_type:
        return None
    else:
        return EntityMapping(
            old=left_type,
            new=right_type,
        )
import mock
import pytest

from swagger_spec_compatibility.spec_utils import load_spec_from_spec_dict
from swagger_spec_compatibility.util import EntityMapping
from swagger_spec_compatibility.walkers.required_properties import _different_properties_mapping
from swagger_spec_compatibility.walkers.required_properties import RequiredPropertiesDiff
from swagger_spec_compatibility.walkers.required_properties import RequiredPropertiesDifferWalker


@pytest.mark.parametrize(
    'left_required_properties, right_required_properties, expected_value',
    [
        (set(), set(), None),
        ({'property'}, {'property'}, None),
        (set(), {'property'}, EntityMapping(set(), {'property'})),
        ({'property'}, set(), EntityMapping({'property'}, set())),
        ({'property', 'old'}, {'property'}, EntityMapping({'old'}, set())),
        ({'property'}, {'property', 'new'}, EntityMapping(set(), {'new'})),
        ({'property', 'old'}, {'property', 'new'
                               }, EntityMapping({'old'}, {'new'})),
    ],
)
@mock.patch(
    'swagger_spec_compatibility.walkers.required_properties.get_required_properties',
    autospec=True)
def test__different_properties_mapping(
    mock_get_required_properties,
    left_required_properties,
    right_required_properties,
    expected_value,
 [
     {
         'responses': {
             '200': {
                 'schema': {}
             }
         }
     },
     {
         'responses': {
             '200': {
                 'schema': {}
             }
         }
     },
     [StatusCodeSchema('200', EntityMapping({}, {}))],
 ],
 [
     {
         'responses': {
             '200': {
                 'schema': {}
             },
             '300': {
                 'schema': {}
             }
         }
     },
     {
         'responses': {
             '200': {
Пример #13
0
         'additionalProperties': {}
     },
     [],
 ),
 (
     {
         'additionalProperties': True
     },
     {
         'additionalProperties': False
     },
     [
         AdditionalPropertiesDiff(
             path=mock.sentinel.PATH,
             diff_type=DiffType.VALUE,
             additionalProperties=EntityMapping(old=True, new=False),
             properties=None,
         ),
     ],
 ),
 (
     {
         'additionalProperties': {}
     },
     {
         'additionalProperties': False
     },
     [
         AdditionalPropertiesDiff(
             path=mock.sentinel.PATH,
             diff_type=DiffType.VALUE,
Пример #14
0
def test_EnumValuesDifferWalker_returns_paths_of_endpoints_responses(minimal_spec_dict):
    old_spec_dict = dict(
        minimal_spec_dict,
        definitions={
            'enum_1': {
                'type': 'string',
                'enum': ['value_to_remove', 'E2', 'E3'],
                'x-model': 'enum_1',
            },
            'enum_2': {
                'type': 'string',
                'enum': ['E1', 'E2', 'E3'],
                'x-model': 'enum_2',
            },
            'object': {
                'properties': {
                    'enum_1': {'$ref': '#/definitions/enum_1'},
                    'enum_2': {'$ref': '#/definitions/enum_2'},
                },
                'type': 'object',
                'x-model': 'object',
            },
        },
        paths={
            '/endpoint': {
                'get': {
                    'parameters': [{
                        'in': 'body',
                        'name': 'body',
                        'required': True,
                        'schema': {
                            '$ref': '#/definitions/object',
                        },
                    }],
                    'responses': {
                        '200': {
                            'description': '',
                            'schema': {
                                '$ref': '#/definitions/object',
                            },
                        },
                    },
                },
            },
        },
    )
    new_spec_dict = deepcopy(old_spec_dict)
    del new_spec_dict['definitions']['enum_1']['enum'][0]
    new_spec_dict['definitions']['enum_2']['enum'].append('new_value')
    old_spec = load_spec_from_spec_dict(old_spec_dict)
    new_spec = load_spec_from_spec_dict(new_spec_dict)

    assert sorted(EnumValuesDifferWalker(old_spec, new_spec).walk()) == sorted([
        EnumValuesDiff(
            path=('definitions', 'enum_2'),
            mapping=EntityMapping(old=set(), new={'new_value'}),
        ),
        EnumValuesDiff(
            path=('definitions', 'object', 'properties', 'enum_2'),
            mapping=EntityMapping(old=set(), new={'new_value'}),
        ),
        EnumValuesDiff(
            path=('definitions', 'object', 'properties', 'enum_1'),
            mapping=EntityMapping(old={'value_to_remove'}, new=set()),
        ),
        EnumValuesDiff(
            path=('definitions', 'enum_1'),
            mapping=EntityMapping(old={'value_to_remove'}, new=set()),
        ),
        EnumValuesDiff(
            path=('paths', '/endpoint', 'get', 'responses', '200', 'schema', 'properties', 'enum_2'),
            mapping=EntityMapping(old=set(), new={'new_value'}),
        ),
        EnumValuesDiff(
            path=('paths', '/endpoint', 'get', 'responses', '200', 'schema', 'properties', 'enum_1'),
            mapping=EntityMapping(old={'value_to_remove'}, new=set()),
        ),
        EnumValuesDiff(
            path=('paths', '/endpoint', 'get', 'parameters', 0, 'schema', 'properties', 'enum_2'),
            mapping=EntityMapping(old=set(), new={'new_value'}),
        ),
        EnumValuesDiff(
            path=('paths', '/endpoint', 'get', 'parameters', 0, 'schema', 'properties', 'enum_1'),
            mapping=EntityMapping(old={'value_to_remove'}, new=set()),
        ),
    ])
Пример #15
0
from swagger_spec_compatibility.spec_utils import load_spec_from_spec_dict
from swagger_spec_compatibility.util import EntityMapping
from swagger_spec_compatibility.walkers.enum_values import _different_enum_values_mapping
from swagger_spec_compatibility.walkers.enum_values import EnumValuesDiff
from swagger_spec_compatibility.walkers.enum_values import EnumValuesDifferWalker


@pytest.mark.parametrize(
    'left_dict, right_dict, expected_value',
    [
        (None, None, None),
        ({}, {}, None),
        ({'type': 'object'}, {}, None),
        ({'enum': ['v1']}, {}, None),
        ({'type': 'string', 'enum': ['v1']}, {}, EntityMapping({'v1'}, set())),
        ({}, {'type': 'string', 'enum': ['v1']}, EntityMapping(set(), {'v1'})),
        ({'type': 'string', 'enum': ['v1']}, {'type': 'string', 'enum': ['v1']}, None),
        ({'type': 'string', 'enum': ['v1', 'v2']}, {'type': 'string', 'enum': ['v2', 'v1']}, None),
        ({'type': 'string', 'enum': ['old', 'common']}, {'type': 'string', 'enum': ['common', 'new']}, EntityMapping({'old'}, {'new'})),
    ],
)
def test__different_enum_values_mapping(left_dict, right_dict, expected_value):
    assert _different_enum_values_mapping(
        left_spec=mock.sentinel.LEFT_SPEC,
        right_spec=mock.sentinel.RIGHT_SPEC,
        left_schema=left_dict,
        right_schema=right_dict,
    ) == expected_value

Пример #16
0
def test_EntityMapping_upacking_works():
    entity_mappint = EntityMapping(old=mock.sentinel.OLD, new=mock.sentinel.NEW)
    old, new = entity_mappint
    assert old == mock.sentinel.OLD
    assert new == mock.sentinel.NEW
from swagger_spec_compatibility.util import EntityMapping
from swagger_spec_compatibility.walkers.changed_types import _different_types_mapping
from swagger_spec_compatibility.walkers.changed_types import ChangedTypesDiff
from swagger_spec_compatibility.walkers.changed_types import ChangedTypesDifferWalker


@pytest.mark.parametrize(
    'default_type_to_object, left_schema, right_schema, expected_value',
    [
        (False, None, None, None),
        (True, None, None, None),
        (False, {}, {}, None),
        (True, {}, {}, None),
        (False, {
            'type': 'object'
        }, {}, EntityMapping(old='object', new=None)),
        (True, {
            'type': 'object'
        }, {}, None),
        (False, {
            'type': 'object'
        }, {
            'type': 'string'
        }, EntityMapping(old='object', new='string')),
        (True, {
            'type': 'object'
        }, {
            'type': 'string'
        }, EntityMapping(old='object', new='string')),
    ],
)