def test_get_custom_strategy_change_remove_add_patches_multiple_changes():
    patches = [
        Dictdiffer_Conflict(
            ('change', 'a.c', ('2', '4')),
            ('remove', '', [('a', {'c': '2', 'b': '1'})])
        ),
        Dictdiffer_Conflict(
            ('change', 'a.b', ('1', '3')),
            ('remove', '', [('a', {'c': '2', 'b': '1'})])
        ),
        Dictdiffer_Conflict(
            ('add', 'a', [('d', '5')]),
            ('remove', '', [('a', {'c': '2', 'b': '1'})])
        ),
        Dictdiffer_Conflict(
            ('add', 'a', [('z', '7')]),
            ('remove', '', [('a', {'c': '2', 'b': '1'})])
        )
    ]

    custom_ops = {
        'a': DictMergerOps.FALLBACK_KEEP_UPDATE
    }

    expected_strategies = ['s', 's', 's', 's']

    m = SkipListsMerger(
        {}, {}, {},
        DictMergerOps.FALLBACK_KEEP_HEAD,
        custom_ops=custom_ops
    )

    strategies = [m._get_custom_strategy(patch) for patch in patches]

    assert expected_strategies == strategies
def test_get_custom_strategy_mixed_patches_with_nesting_after_list():
    patches = [
        Dictdiffer_Conflict(
            ('add', ['a', 'c', 0], [('d', '3')]),
            ('add', ['a', 'c', 0], [('d', '4')])
        ),
        Dictdiffer_Conflict(
            ('add', ['a', 'c', 0, 'b'], [('z', '5')]),
            ('add', ['a', 'c', 0, 'b'], [('z', '6')])
        )
    ]

    custom_ops = {
        'a': DictMergerOps.FALLBACK_KEEP_UPDATE,
        'a.c.b': DictMergerOps.FALLBACK_KEEP_HEAD
    }

    expected_strategies = ['s', 'f']

    m = SkipListsMerger(
        {}, {}, {},
        DictMergerOps.FALLBACK_KEEP_HEAD,
        custom_ops=custom_ops
    )

    strategies = [m._get_custom_strategy(patch) for patch in patches]

    assert expected_strategies == strategies
def test_get_custom_strategy_mixed_patches():
    patches = [
        Dictdiffer_Conflict(
            ('change', 'b', ('1', '2')),
            ('remove', '', [('b', '1')])
        ),
        Dictdiffer_Conflict(
            ('add', '', [('a', '1')]),
            ('add', '', [('a', 'd')])
        )
    ]

    custom_ops = {
        'b': DictMergerOps.FALLBACK_KEEP_UPDATE,
        'a': DictMergerOps.FALLBACK_KEEP_UPDATE
    }

    expected_strategies = ['s', 's']

    m = SkipListsMerger(
        {}, {}, {},
        DictMergerOps.FALLBACK_KEEP_HEAD,
        custom_ops=custom_ops
    )

    strategies = [m._get_custom_strategy(patch) for patch in patches]

    assert expected_strategies == strategies
Beispiel #4
0
def test_get_rule_for_field_looks_at_correct_path():
    def keep_spam(head, update, down_path):
        return 's' if update == 'spam' else 'f'

    custom_ops = {'a.b': keep_spam}
    head = {
        'a': {
            'b': [
                'egg',
                'bacon',
            ],
            'c': 'egg',
        },
    }
    update = {
        'a': {
            'b': [
                'spam',
                'bread',
            ],
        },
    }

    m = SkipListsMerger({},
                        head,
                        update,
                        DictMergerOps.FALLBACK_KEEP_HEAD,
                        custom_ops=custom_ops,
                        data_lists='a.b')

    expected = 's'

    output = m._get_rule_for_field(['a', 'b', 0])

    assert expected == output
def test_get_custom_strategy_add_change_patches_very_nested():
    patches = [
        Dictdiffer_Conflict(
            ('change', 'a.b.c.d', ('1', '4')),
            ('change', 'a.b.c.d', ('1', '5'))
        ),
        Dictdiffer_Conflict(
            ('add', 'a.b.c', [('e', '2')]),
            ('add', 'a.b.c', [('e', '3')])
        )
    ]

    custom_ops = {
        'b': DictMergerOps.FALLBACK_KEEP_UPDATE,
        'a': DictMergerOps.FALLBACK_KEEP_UPDATE
    }

    expected_strategies = ['s', 's']

    m = SkipListsMerger(
        {}, {}, {},
        DictMergerOps.FALLBACK_KEEP_HEAD,
        custom_ops=custom_ops
    )

    strategies = [m._get_custom_strategy(patch) for patch in patches]

    assert expected_strategies == strategies
Beispiel #6
0
def test_get_custom_strategies():
    patches = [
        Dictdiffer_Conflict(('change', 'p.foo', ('baa', 'bab')),
                            ('change', 'p.foo', ('baa', 'bac'))),
        Dictdiffer_Conflict(('change', 'p.spam', ('eg', 'egg')),
                            ('change', 'p.spam', ('eg', 'eggs'))),
        Dictdiffer_Conflict(('change', 'r.foo', ('baa', 'bab')),
                            ('change', 'r.foo', ('baa', 'bac'))),
        Dictdiffer_Conflict(('change', 'r.spam', ('eg', 'egg')),
                            ('change', 'r.spam', ('eg', 'eggs')))
    ]

    custom_ops = {
        'r': DictMergerOps.FALLBACK_KEEP_UPDATE,
        'p': DictMergerOps.FALLBACK_KEEP_UPDATE
    }

    expected_strategies = ['s', 's', 's', 's']

    m = SkipListsMerger({}, {}, {},
                        DictMergerOps.FALLBACK_KEEP_HEAD,
                        custom_ops=custom_ops)

    strategies = m._get_custom_strategies(patches)

    assert expected_strategies == strategies
def test_get_custom_strategy_mixed_patches_with_nesting_after_list():
    patches = [
        Dictdiffer_Conflict(
            ('add', ['a', 'c', 0], [('d', '3')]),
            ('add', ['a', 'c', 0], [('d', '4')])
        ),
        Dictdiffer_Conflict(
            ('add', ['a', 'c', 0, 'b'], [('z', '5')]),
            ('add', ['a', 'c', 0, 'b'], [('z', '6')])
        )
    ]

    custom_ops = {
        'a': DictMergerOps.FALLBACK_KEEP_UPDATE,
        'a.c.b': DictMergerOps.FALLBACK_KEEP_HEAD
    }

    expected_strategies = ['s', 'f']

    m = SkipListsMerger(
        {}, {}, {},
        DictMergerOps.FALLBACK_KEEP_HEAD,
        custom_ops=custom_ops
    )

    strategies = [m._get_custom_strategy(patch) for patch in patches]

    assert expected_strategies == strategies
def test_get_custom_strategy_add_change_patches_very_nested():
    patches = [
        Dictdiffer_Conflict(
            ('change', 'a.b.c.d', ('1', '4')),
            ('change', 'a.b.c.d', ('1', '5'))
        ),
        Dictdiffer_Conflict(
            ('add', 'a.b.c', [('e', '2')]),
            ('add', 'a.b.c', [('e', '3')])
        )
    ]

    custom_ops = {
        'b': DictMergerOps.FALLBACK_KEEP_UPDATE,
        'a': DictMergerOps.FALLBACK_KEEP_UPDATE
    }

    expected_strategies = ['s', 's']

    m = SkipListsMerger(
        {}, {}, {},
        DictMergerOps.FALLBACK_KEEP_HEAD,
        custom_ops=custom_ops
    )

    strategies = [m._get_custom_strategy(patch) for patch in patches]

    assert expected_strategies == strategies
def test_get_custom_strategy_mixed_patches():
    patches = [
        Dictdiffer_Conflict(
            ('change', 'b', ('1', '2')),
            ('remove', '', [('b', '1')])
        ),
        Dictdiffer_Conflict(
            ('add', '', [('a', '1')]),
            ('add', '', [('a', 'd')])
        )
    ]

    custom_ops = {
        'b': DictMergerOps.FALLBACK_KEEP_UPDATE,
        'a': DictMergerOps.FALLBACK_KEEP_UPDATE
    }

    expected_strategies = ['s', 's']

    m = SkipListsMerger(
        {}, {}, {},
        DictMergerOps.FALLBACK_KEEP_HEAD,
        custom_ops=custom_ops
    )

    strategies = [m._get_custom_strategy(patch) for patch in patches]

    assert expected_strategies == strategies
def test_get_custom_strategies_change_remove_add_patches_multiple_changes():
    patches = [
        Dictdiffer_Conflict(('change', 'a.c', ('2', '4')),
                            ('remove', '', [('a', {
                                'c': '2',
                                'b': '1'
                            })])),
        Dictdiffer_Conflict(('change', 'a.b', ('1', '3')),
                            ('remove', '', [('a', {
                                'c': '2',
                                'b': '1'
                            })])),
        Dictdiffer_Conflict(('add', 'a', [('d', '5')]),
                            ('remove', '', [('a', {
                                'c': '2',
                                'b': '1'
                            })])),
        Dictdiffer_Conflict(('add', 'a', [('z', '7')]),
                            ('remove', '', [('a', {
                                'c': '2',
                                'b': '1'
                            })]))
    ]

    custom_ops = {'a': DictMergerOps.FALLBACK_KEEP_UPDATE}

    expected_strategies = ['s', 's', 's', 's']

    m = SkipListsMerger({}, {}, {},
                        DictMergerOps.FALLBACK_KEEP_HEAD,
                        custom_ops=custom_ops)

    strategies = m._get_custom_strategies(patches)

    assert expected_strategies == strategies
Beispiel #11
0
def test_conflict_with_custom_ops_update_and_head_with_nested_rules():
    r = {'r': {'foo': 'baa', 'spam': 'eg'}, 'p': {'foo': 'baa', 'spam': 'eg'}}
    h = {
        'r': {
            'foo': 'bab',
            'spam': 'egg'
        },
        'p': {
            'foo': 'bab',
            'spam': 'egg'
        }
    }
    u = {
        'r': {
            'foo': 'bac',
            'spam': 'eggs'
        },
        'p': {
            'foo': 'bac',
            'spam': 'eggs'
        }
    }

    custom_ops = {
        'r': DictMergerOps.FALLBACK_KEEP_UPDATE,
        'p': DictMergerOps.FALLBACK_KEEP_HEAD,
        'r.foo': DictMergerOps.FALLBACK_KEEP_HEAD,
        'p.spam': DictMergerOps.FALLBACK_KEEP_UPDATE
    }
    expected = {
        'r': {
            'foo': 'bab',
            'spam': 'eggs'
        },
        'p': {
            'foo': 'bab',
            'spam': 'eggs'
        }
    }

    m = SkipListsMerger(r,
                        h,
                        u,
                        DictMergerOps.FALLBACK_KEEP_HEAD,
                        custom_ops=custom_ops)
    with pytest.raises(MergeError) as excinfo:
        m.merge()

    assert m.merged_root == expected

    assert m.conflicts == excinfo.value.content
    assert len(m.conflicts) == 4
    assert Conflict(ConflictType.SET_FIELD, ('r', 'spam'),
                    'egg') in m.conflicts
    assert Conflict(ConflictType.SET_FIELD, ('p', 'spam'),
                    'egg') in m.conflicts
    assert Conflict(ConflictType.SET_FIELD, ('r', 'foo'), 'bac') in m.conflicts
    assert Conflict(ConflictType.SET_FIELD, ('p', 'foo'), 'bac') in m.conflicts
def test_data_lists_bare_lists():
    r = [[1, 2, 3], [4, 5, 6]]
    h = [[1, 2, 3], [4, 5, 6]]
    u = [[3, 3, 3], [4, 5, 6]]

    m = SkipListsMerger(r, h, u, DictMergerOps.FALLBACK_KEEP_HEAD)
    m.merge()
    assert m.merged_root == u
    assert not m.skipped_lists
def test_simple_behavior():
    r = {}
    h = {'foo': 'bar'}
    u = {'bar': 'baz'}

    m = SkipListsMerger(r, h, u, DictMergerOps.FALLBACK_KEEP_HEAD)
    m.merge()

    assert m.merged_root == {'foo': 'bar', 'bar': 'baz'}
def test_merge_handles_duplicated_remove_patches():
    root = {'foo': 'bar', 'baz': 'spam'}
    head = {'foo': 'bar'}
    update = {'foo': 'bar'}

    m = SkipListsMerger(root, head, update, DictMergerOps.FALLBACK_KEEP_HEAD)
    m.merge()

    assert m.merged_root == {'foo': 'bar'}
Beispiel #15
0
def test_data_lists_bare_lists():
    r = [[1, 2, 3], [4, 5, 6]]
    h = [[1, 2, 3], [4, 5, 6]]
    u = [[3, 3, 3], [4, 5, 6]]

    m = SkipListsMerger(r, h, u, DictMergerOps.FALLBACK_KEEP_HEAD)
    m.merge()
    assert m.merged_root == u
    assert not m.skipped_lists
Beispiel #16
0
def test_merge_handles_duplicated_remove_patches():
    root = {'foo': 'bar', 'baz': 'spam'}
    head = {'foo': 'bar'}
    update = {'foo': 'bar'}

    m = SkipListsMerger(root, head, update, DictMergerOps.FALLBACK_KEEP_HEAD)
    m.merge()

    assert m.merged_root == {'foo': 'bar'}
Beispiel #17
0
def test_simple_behavior():
    r = {}
    h = {'foo': 'bar'}
    u = {'bar': 'baz'}

    m = SkipListsMerger(r, h, u, DictMergerOps.FALLBACK_KEEP_HEAD)
    m.merge()

    assert m.merged_root == {'foo': 'bar', 'bar': 'baz'}
def test_data_lists_nested():
    r = {'r': [[1, 2, 3], [4, 5, 6]]}
    h = {'r': [[1, 2, 3], [4, 5, 6]]}
    u = {'r': [[3, 3, 3], [4, 5, 6]]}

    m = SkipListsMerger(r, h, u, DictMergerOps.FALLBACK_KEEP_HEAD,
                        data_lists=['r'])
    m.merge()
    assert m.merged_root == u
    assert not m.skipped_lists
def test_update_deletes_root_list_no_conflict():
    r = {'r': {'x': 1, 'l': [1, 2, 3]}}
    h = {'r': {'x': 1, 'l': [1, 2, 3]}}
    u = {'r': {'x': 2}}

    m = SkipListsMerger(r, h, u, DictMergerOps.FALLBACK_KEEP_HEAD)
    m.merge()

    assert m.merged_root == {'r': {'x': 2}}
    assert len(m.skipped_lists) == 0
Beispiel #20
0
def test_update_deletes_root_list_no_conflict():
    r = {'r': {'x': 1, 'l': [1, 2, 3]}}
    h = {'r': {'x': 1, 'l': [1, 2, 3]}}
    u = {'r': {'x': 2}}

    m = SkipListsMerger(r, h, u, DictMergerOps.FALLBACK_KEEP_HEAD)
    m.merge()

    assert m.merged_root == {'r': {'x': 2}}
    assert len(m.skipped_lists) == 0
Beispiel #21
0
def test_head_only_list_add_no_skipped_lists():
    r = {'r': {'x': 1}}
    h = {'r': {'x': 1, 'l': [1, 2, 3]}}
    u = {'r': {'x': 2}}

    m = SkipListsMerger(r, h, u, DictMergerOps.FALLBACK_KEEP_HEAD)
    m.merge()

    assert m.merged_root == {'r': {'x': 2, 'l': [1, 2, 3]}}
    assert len(m.skipped_lists) == 0
def test_head_only_list_add_no_skipped_lists():
    r = {'r': {'x': 1}}
    h = {'r': {'x': 1, 'l': [1, 2, 3]}}
    u = {'r': {'x': 2}}

    m = SkipListsMerger(r, h, u, DictMergerOps.FALLBACK_KEEP_HEAD)
    m.merge()

    assert m.merged_root == {'r': {'x': 2, 'l': [1, 2, 3]}}
    assert len(m.skipped_lists) == 0
def test_data_lists_nested():
    r = {'r': [[1, 2, 3], [4, 5, 6]]}
    h = {'r': [[1, 2, 3], [4, 5, 6]]}
    u = {'r': [[3, 3, 3], [4, 5, 6]]}

    m = SkipListsMerger(r, h, u, DictMergerOps.FALLBACK_KEEP_HEAD,
                        data_lists=['r'])
    m.merge()
    assert m.merged_root == u
    assert not m.skipped_lists
Beispiel #24
0
def test_conflict_with_custom_ops_update():
    r = {'r': {'foo': 'baa', 'spam': 'eg'}, 'p': {'foo': 'baa', 'spam': 'eg'}}
    h = {
        'r': {
            'foo': 'bab',
            'spam': 'egg'
        },
        'p': {
            'foo': 'bab',
            'spam': 'egg'
        }
    }
    u = {
        'r': {
            'foo': 'bac',
            'spam': 'eggs'
        },
        'p': {
            'foo': 'bac',
            'spam': 'eggs'
        }
    }

    # set different strategies compare to the default one
    custom_ops = {
        'r': DictMergerOps.FALLBACK_KEEP_UPDATE,
        'p': DictMergerOps.FALLBACK_KEEP_UPDATE
    }
    expected = {
        'r': {
            'foo': 'bac',
            'spam': 'eggs'
        },
        'p': {
            'foo': 'bac',
            'spam': 'eggs'
        }
    }

    m = SkipListsMerger(r,
                        h,
                        u,
                        DictMergerOps.FALLBACK_KEEP_HEAD,
                        custom_ops=custom_ops)

    with pytest.raises(MergeError) as excinfo:
        m.merge()

    assert m.merged_root == expected
    assert m.conflicts == excinfo.value.content
    assert len(m.conflicts) == 4
    assert Conflict(ConflictType.SET_FIELD, ('p', 'foo'), 'bab') in m.conflicts
    assert Conflict(ConflictType.SET_FIELD, ('p', 'foo'), 'bab') in m.conflicts
    assert Conflict(ConflictType.SET_FIELD, ('p', 'foo'), 'bab') in m.conflicts
    assert Conflict(ConflictType.SET_FIELD, ('p', 'foo'), 'bab') in m.conflicts
Beispiel #25
0
def test_get_all_the_related_path_no_match():
    custom_ops = {'a.b': DictMergerOps.FALLBACK_KEEP_UPDATE}

    m = SkipListsMerger({}, {}, {},
                        DictMergerOps.FALLBACK_KEEP_HEAD,
                        custom_ops=custom_ops)

    expected = 'f'

    output = m._get_related_path('a.l.c.d')

    assert expected == output
Beispiel #26
0
def test_get_rule_for_field_no_match():
    custom_ops = {'a.b': DictMergerOps.FALLBACK_KEEP_UPDATE}

    m = SkipListsMerger({}, {}, {},
                        DictMergerOps.FALLBACK_KEEP_HEAD,
                        custom_ops=custom_ops)

    expected = 'f'

    output = m._get_rule_for_field(['a', 'l', 'c', 'd'])

    assert expected == output
Beispiel #27
0
def test_simple_conflicts_keep_head():
    r = {}
    h = {'foo': 'bar'}
    u = {'foo': 'baz'}

    m = SkipListsMerger(r, h, u, DictMergerOps.FALLBACK_KEEP_HEAD)
    with pytest.raises(MergeError) as excinfo:
        m.merge()

    assert m.merged_root == {'foo': 'bar'}
    assert m.conflicts == excinfo.value.content
    assert len(m.conflicts) == 1
    assert Conflict(ConflictType.SET_FIELD, ('foo', ), 'baz') in m.conflicts
Beispiel #28
0
def test_custom_fallback():
    r = {'r': {'foo': 'baa'}}
    h = {'r': {'foo': 'bab'}}
    u = {'r': {'foo': 'bac'}}

    m = SkipListsMerger(r, h, u, DictMergerOps.FALLBACK_KEEP_HEAD)
    with pytest.raises(MergeError) as excinfo:
        m.merge()

    assert m.merged_root == {'r': {'foo': 'bab'}}
    assert m.conflicts == excinfo.value.content
    assert len(m.conflicts) == 1
    assert Conflict(ConflictType.SET_FIELD, ('r', 'foo'), 'bac') in m.conflicts
def test_simple_conflicts_keep_head():
    r = {}
    h = {'foo': 'bar'}
    u = {'foo': 'baz'}

    m = SkipListsMerger(r, h, u, DictMergerOps.FALLBACK_KEEP_HEAD)
    with pytest.raises(MergeError) as excinfo:
        m.merge()

    assert m.merged_root == {'foo': 'bar'}
    assert m.conflicts == excinfo.value.content
    assert len(m.conflicts) == 1
    assert Conflict(ConflictType.SET_FIELD, ('foo', ), 'baz') in m.conflicts
Beispiel #30
0
def test_head_and_update_list_add_skipped_lists():
    r = {'r': {'x': 1}}
    h = {'r': {'x': 1, 'l': [1, 2, 3]}}
    u = {'r': {'x': 2, 'l': [1, 2, 3]}}

    m = SkipListsMerger(r, h, u, DictMergerOps.FALLBACK_KEEP_HEAD)
    m.merge()

    assert m.merged_root == {'r': {'x': 2}}
    # Knowing the list backup method check that m.merge is idempotent.
    assert r == r
    assert h == h
    assert u == u
    assert m.skipped_lists == set([('r', 'l')])
Beispiel #31
0
def test_simple_remove_conflict():
    r = {'foo1': 'bar', 'foo2': 'bar'}
    h = {'foo1': 'baz', 'foo2': 'baz'}
    u = {}

    m = SkipListsMerger(r, h, u, DictMergerOps.FALLBACK_KEEP_HEAD)
    with pytest.raises(MergeError) as excinfo:
        m.merge()

    assert m.merged_root == {'foo1': 'baz', 'foo2': 'baz'}
    assert m.conflicts == excinfo.value.content
    assert len(m.conflicts) == 2
    assert Conflict(ConflictType.REMOVE_FIELD, ('foo1', ), None) in m.conflicts
    assert Conflict(ConflictType.REMOVE_FIELD, ('foo2', ), None) in m.conflicts
Beispiel #32
0
def test_one_list_delete_touched_in_head_raises_conflict():
    r = {'r': {'x': 1, 'l': [1, 2, 3]}}
    h = {'r': {'x': 1, 'l': [4, 3, 2, 1]}}
    u = {'r': {'x': 2}}

    m = SkipListsMerger(r, h, u, DictMergerOps.FALLBACK_KEEP_HEAD)
    with pytest.raises(MergeError) as excinfo:
        m.merge()

    assert m.merged_root == {'r': {'x': 2, 'l': [4, 3, 2, 1]}}
    assert len(m.skipped_lists) == 0
    assert m.conflicts == excinfo.value.content
    assert len(m.conflicts) == 1
    assert Conflict(ConflictType.REMOVE_FIELD, ('r', 'l'), None) in m.conflicts
def test_simple_remove_conflict():
    r = {'foo1': 'bar', 'foo2': 'bar'}
    h = {'foo1': 'baz', 'foo2': 'baz'}
    u = {}

    m = SkipListsMerger(r, h, u, DictMergerOps.FALLBACK_KEEP_HEAD)
    with pytest.raises(MergeError) as excinfo:
        m.merge()

    assert m.merged_root == {'foo1': 'baz', 'foo2': 'baz'}
    assert m.conflicts == excinfo.value.content
    assert len(m.conflicts) == 2
    assert Conflict(ConflictType.REMOVE_FIELD, ('foo1', ), None) in m.conflicts
    assert Conflict(ConflictType.REMOVE_FIELD, ('foo2', ), None) in m.conflicts
def test_one_list_delete_touched_in_head_raises_conflict():
    r = {'r': {'x': 1, 'l': [1, 2, 3]}}
    h = {'r': {'x': 1, 'l': [4, 3, 2, 1]}}
    u = {'r': {'x': 2}}

    m = SkipListsMerger(r, h, u, DictMergerOps.FALLBACK_KEEP_HEAD)
    with pytest.raises(MergeError) as excinfo:
        m.merge()

    assert m.merged_root == {'r': {'x': 2, 'l': [4, 3, 2, 1]}}
    assert len(m.skipped_lists) == 0
    assert m.conflicts == excinfo.value.content
    assert len(m.conflicts) == 1
    assert Conflict(ConflictType.REMOVE_FIELD, ('r', 'l'), None) in m.conflicts
def test_head_and_update_list_add_skipped_lists():
    r = {'r': {'x': 1}}
    h = {'r': {'x': 1, 'l': [1, 2, 3]}}
    u = {'r': {'x': 2, 'l': [1, 2, 3]}}

    m = SkipListsMerger(r, h, u, DictMergerOps.FALLBACK_KEEP_HEAD)
    m.merge()

    assert m.merged_root == {'r': {'x': 2}}
    # Knowing the list backup method check that m.merge is idempotent.
    assert r == r
    assert h == h
    assert u == u
    assert m.skipped_lists == set([('r', 'l')])
Beispiel #36
0
def test_two_list_edit_skipped_lists():
    r = {'r': {'x': 1, 'l1': [1, 2, 3], 'l2': [1]}}
    h = {'r': {'x': 1, 'l1': [4, 3, 2, 1], 'l2': [2]}}
    u = {'r': {'x': 2, 'l1': [1, 2, 3], 'l2': [2]}}

    m = SkipListsMerger(r, h, u, DictMergerOps.FALLBACK_KEEP_HEAD)
    m.merge()

    # The lists are kept as seen in root
    assert m.merged_root == {'r': {'x': 2, 'l1': [1, 2, 3], 'l2': [1]}}
    assert m.skipped_lists == set([('r', 'l1'), ('r', 'l2')])
    # Knowing the list backup method check that m.merge is idempotent.
    assert r == r
    assert h == h
    assert u == u
Beispiel #37
0
def test_get_custom_strategy_add_patch_in_the_root():
    patch = Dictdiffer_Conflict(('add', '', [('a', '1')]),
                                ('add', '', [('a', '2')]))

    custom_ops = {'a': DictMergerOps.FALLBACK_KEEP_UPDATE}

    expected_strategy = 's'

    m = SkipListsMerger({}, {}, {},
                        DictMergerOps.FALLBACK_KEEP_HEAD,
                        custom_ops=custom_ops)

    strategy = m._get_custom_strategy(patch)

    assert expected_strategy == strategy
Beispiel #38
0
def test_base_values():
    m = SkipListsMerger(1, 2, 1, DictMergerOps.FALLBACK_KEEP_HEAD)
    m.merge()
    assert m.merged_root == 2

    m = SkipListsMerger(1, 1, 2, DictMergerOps.FALLBACK_KEEP_HEAD)
    m.merge()
    assert m.merged_root == 2
Beispiel #39
0
def test_get_rule_for_field_uses_key_path():
    custom_ops = {
        'a.b.c': DictMergerOps.FALLBACK_KEEP_UPDATE,
    }

    m = SkipListsMerger({}, {}, {},
                        DictMergerOps.FALLBACK_KEEP_HEAD,
                        custom_ops=custom_ops,
                        key_path=['a', 0])

    expected = 's'

    output = m._get_rule_for_field(['b', 'c'])

    assert expected == output
def test_custom_fallback():
    r = {'r': {'foo': 'baa'}}
    h = {'r': {'foo': 'bab'}}
    u = {'r': {'foo': 'bac'}}

    m = SkipListsMerger(r, h, u, DictMergerOps.FALLBACK_KEEP_HEAD)
    with pytest.raises(MergeError) as excinfo:
        m.merge()

    assert m.merged_root == {'r': {'foo': 'bab'}}
    assert m.conflicts == excinfo.value.content
    assert len(m.conflicts) == 1
    assert Conflict(
        ConflictType.SET_FIELD, ('r', 'foo'), 'bac'
    ) in m.conflicts
def test_two_list_edit_skipped_lists():
    r = {'r': {'x': 1, 'l1': [1, 2, 3], 'l2': [1]}}
    h = {'r': {'x': 1, 'l1': [4, 3, 2, 1], 'l2': [2]}}
    u = {'r': {'x': 2, 'l1': [1, 2, 3], 'l2': [2]}}

    m = SkipListsMerger(r, h, u, DictMergerOps.FALLBACK_KEEP_HEAD)
    m.merge()

    # The lists are kept as seen in root
    assert m.merged_root == {'r': {'x': 2, 'l1': [1, 2, 3], 'l2': [1]}}
    assert m.skipped_lists == set([('r', 'l1'), ('r', 'l2')])
    # Knowing the list backup method check that m.merge is idempotent.
    assert r == r
    assert h == h
    assert u == u
def test_get_rule_for_field_no_match():
    custom_ops = {
        'a.b': DictMergerOps.FALLBACK_KEEP_UPDATE
    }

    m = SkipListsMerger(
        {}, {}, {},
        DictMergerOps.FALLBACK_KEEP_HEAD,
        custom_ops=custom_ops
    )

    expected = 'f'

    output = m._get_rule_for_field(['a', 'l', 'c', 'd'])

    assert expected == output
def test_get_rule_for_field_uses_key_path():
    custom_ops = {
        'a.b.c': DictMergerOps.FALLBACK_KEEP_UPDATE,
    }

    m = SkipListsMerger(
        {}, {}, {},
        DictMergerOps.FALLBACK_KEEP_HEAD,
        custom_ops=custom_ops, key_path=['a', 0]
    )

    expected = 's'

    output = m._get_rule_for_field(['b', 'c'])

    assert expected == output
def test_get_custom_strategies_add_patches_in_the_root():
    patches = [
        Dictdiffer_Conflict(('add', '', [('a', '1')]),
                            ('add', '', [('a', '2')]))
    ]

    custom_ops = {'a': DictMergerOps.FALLBACK_KEEP_UPDATE}

    expected_strategies = ['s']

    m = SkipListsMerger({}, {}, {},
                        DictMergerOps.FALLBACK_KEEP_HEAD,
                        custom_ops=custom_ops)

    strategies = m._get_custom_strategies(patches)

    assert expected_strategies == strategies
Beispiel #45
0
def test_get_custom_strategy_change_remove_patch():
    patch = Dictdiffer_Conflict(('change', 'a.b', ('1', '2')),
                                ('remove', '', [('a', {
                                    'b': '1'
                                })]))

    custom_ops = {'a.b': DictMergerOps.FALLBACK_KEEP_UPDATE}

    expected_strategy = 's'

    m = SkipListsMerger({}, {}, {},
                        DictMergerOps.FALLBACK_KEEP_HEAD,
                        custom_ops=custom_ops)

    strategy = m._get_custom_strategy(patch)

    assert expected_strategy == strategy
Beispiel #46
0
def test_get_custom_strategy_mixed_patches_with_nested_changes():
    patch = Dictdiffer_Conflict(('change', 'a.b', ('1', '2')),
                                ('change', 'a.b', ('1', '3')))

    custom_ops = {
        'a.b': DictMergerOps.FALLBACK_KEEP_UPDATE,
    }

    expected_strategy = 's'

    m = SkipListsMerger({}, {}, {},
                        DictMergerOps.FALLBACK_KEEP_HEAD,
                        custom_ops=custom_ops)

    strategy = m._get_custom_strategy(patch)

    assert expected_strategy == strategy
Beispiel #47
0
def test_get_custom_strategy_mixed_patch_with_list():
    patch = Dictdiffer_Conflict(('add', ['a', 'c', 0], [('b', '1')]),
                                ('add', ['a', 'c', 0], [('b', '2')]))

    custom_ops = {
        'a': DictMergerOps.FALLBACK_KEEP_UPDATE,
        'a.c.b': DictMergerOps.FALLBACK_KEEP_HEAD
    }

    expected_strategy = 'f'

    m = SkipListsMerger({}, {}, {},
                        DictMergerOps.FALLBACK_KEEP_HEAD,
                        custom_ops=custom_ops)

    strategy = m._get_custom_strategy(patch)

    assert expected_strategy == strategy
Beispiel #48
0
def test_get_custom_strategy_add_patch_very_nested():
    patch = Dictdiffer_Conflict(('add', 'a.b.c', [('e', '2')]),
                                ('add', 'a.b.c', [('e', '3')]))

    custom_ops = {
        'b': DictMergerOps.FALLBACK_KEEP_UPDATE,
        'a': DictMergerOps.FALLBACK_KEEP_UPDATE
    }

    expected_strategy = 's'

    m = SkipListsMerger({}, {}, {},
                        DictMergerOps.FALLBACK_KEEP_HEAD,
                        custom_ops=custom_ops)

    strategy = m._get_custom_strategy(patch)

    assert expected_strategy == strategy
def test_merge_with_nothing():
    m = SkipListsMerger(1, {'some': 'other object'}, NOTHING,
                        DictMergerOps.FALLBACK_KEEP_HEAD)
    m.merge()
    assert m.merged_root == {'some': 'other object'}

    m = SkipListsMerger(1, NOTHING, {'some': 'other object'},
                        DictMergerOps.FALLBACK_KEEP_HEAD)
    m.merge()
    assert m.merged_root == {'some': 'other object'}

    m = SkipListsMerger(NOTHING, {'some': 'other object'}, NOTHING,
                        DictMergerOps.FALLBACK_KEEP_HEAD)
    m.merge()
    assert m.merged_root == {'some': 'other object'}
def test_merge_uses_custom_rules_for_dicts():
    custom_ops = {
        'a': DictMergerOps.FALLBACK_KEEP_UPDATE
    }

    m = SkipListsMerger(
        {}, {'a': 'head'}, {'a': 'update'},
        DictMergerOps.FALLBACK_KEEP_HEAD,
        custom_ops=custom_ops,
    )
    try:
        m.merge()
    except MergeError:
        pass

    expected = {'a': 'update'}
    result = m.merged_root

    assert expected == result
def test_conflict_with_custom_ops_update_and_head_with_nested_rules():
    r = {'r': {'foo': 'baa', 'spam': 'eg'},
         'p': {'foo': 'baa', 'spam': 'eg'}}
    h = {'r': {'foo': 'bab', 'spam': 'egg'},
         'p': {'foo': 'bab', 'spam': 'egg'}}
    u = {'r': {'foo': 'bac', 'spam': 'eggs'},
         'p': {'foo': 'bac', 'spam': 'eggs'}}

    custom_ops = {
        'r': DictMergerOps.FALLBACK_KEEP_UPDATE,
        'p': DictMergerOps.FALLBACK_KEEP_HEAD,
        'r.foo': DictMergerOps.FALLBACK_KEEP_HEAD,
        'p.spam': DictMergerOps.FALLBACK_KEEP_UPDATE
    }
    expected = {
        'r': {'foo': 'bab', 'spam': 'eggs'},
        'p': {'foo': 'bab', 'spam': 'eggs'}
    }

    m = SkipListsMerger(
        r, h, u,
        DictMergerOps.FALLBACK_KEEP_HEAD,
        custom_ops=custom_ops
    )
    with pytest.raises(MergeError) as excinfo:
        m.merge()

    assert m.merged_root == expected

    assert m.conflicts == excinfo.value.content
    assert len(m.conflicts) == 4
    assert Conflict(
        ConflictType.SET_FIELD, ('r', 'spam'), 'egg'
    ) in m.conflicts
    assert Conflict(
        ConflictType.SET_FIELD, ('p', 'spam'), 'egg'
    ) in m.conflicts
    assert Conflict(
        ConflictType.SET_FIELD, ('r', 'foo'), 'bac'
    ) in m.conflicts
    assert Conflict(
        ConflictType.SET_FIELD, ('p', 'foo'), 'bac'
    ) in m.conflicts
def test_merge_uses_custom_rules_for_base_values():
    custom_ops = {
        'a': DictMergerOps.FALLBACK_KEEP_UPDATE
    }

    m = SkipListsMerger(
        '', 'head', 'update',
        DictMergerOps.FALLBACK_KEEP_HEAD,
        custom_ops=custom_ops, key_path=['a'],
    )
    try:
        m.merge()
    except MergeError:
        pass

    expected = 'update'
    result = m.merged_root

    assert expected == result
def test_conflict_with_custom_ops_update():
    r = {'r': {'foo': 'baa', 'spam': 'eg'},
         'p': {'foo': 'baa', 'spam': 'eg'}}
    h = {'r': {'foo': 'bab', 'spam': 'egg'},
         'p': {'foo': 'bab', 'spam': 'egg'}}
    u = {'r': {'foo': 'bac', 'spam': 'eggs'},
         'p': {'foo': 'bac', 'spam': 'eggs'}}

    # set different strategies compare to the default one
    custom_ops = {
        'r': DictMergerOps.FALLBACK_KEEP_UPDATE,
        'p': DictMergerOps.FALLBACK_KEEP_UPDATE
    }
    expected = {
        'r': {'foo': 'bac', 'spam': 'eggs'},
        'p': {'foo': 'bac', 'spam': 'eggs'}
    }

    m = SkipListsMerger(
        r, h, u,
        DictMergerOps.FALLBACK_KEEP_HEAD,
        custom_ops=custom_ops
    )

    with pytest.raises(MergeError) as excinfo:
        m.merge()

    assert m.merged_root == expected
    assert m.conflicts == excinfo.value.content
    assert len(m.conflicts) == 4
    assert Conflict(
        ConflictType.SET_FIELD, ('p', 'foo'), 'bab'
    ) in m.conflicts
    assert Conflict(
        ConflictType.SET_FIELD, ('p', 'foo'), 'bab'
    ) in m.conflicts
    assert Conflict(
        ConflictType.SET_FIELD, ('p', 'foo'), 'bab'
    ) in m.conflicts
    assert Conflict(
        ConflictType.SET_FIELD, ('p', 'foo'), 'bab'
    ) in m.conflicts
def test_get_custom_strategy_add_patch_very_nested():
    patch = Dictdiffer_Conflict(
        ('add', 'a.b.c', [('e', '2')]),
        ('add', 'a.b.c', [('e', '3')]))

    custom_ops = {
        'b': DictMergerOps.FALLBACK_KEEP_UPDATE,
        'a': DictMergerOps.FALLBACK_KEEP_UPDATE
    }

    expected_strategy = 's'

    m = SkipListsMerger(
        {}, {}, {},
        DictMergerOps.FALLBACK_KEEP_HEAD,
        custom_ops=custom_ops
    )

    strategy = m._get_custom_strategy(patch)

    assert expected_strategy == strategy
def test_get_custom_strategy_change_remove_patch():
    patch = Dictdiffer_Conflict(
        ('change', 'a.b', ('1', '2')),
        ('remove', '', [('a', {'b': '1'})])
    )

    custom_ops = {
        'a.b': DictMergerOps.FALLBACK_KEEP_UPDATE
    }

    expected_strategy = 's'

    m = SkipListsMerger(
        {}, {}, {},
        DictMergerOps.FALLBACK_KEEP_HEAD,
        custom_ops=custom_ops
    )

    strategy = m._get_custom_strategy(patch)

    assert expected_strategy == strategy
def test_get_custom_strategy_mixed_patches_with_nested_changes():
    patch = Dictdiffer_Conflict(
        ('change', 'a.b', ('1', '2')),
        ('change', 'a.b', ('1', '3'))
    )

    custom_ops = {
        'a.b': DictMergerOps.FALLBACK_KEEP_UPDATE,
    }

    expected_strategy = 's'

    m = SkipListsMerger(
        {}, {}, {},
        DictMergerOps.FALLBACK_KEEP_HEAD,
        custom_ops=custom_ops
    )

    strategy = m._get_custom_strategy(patch)

    assert expected_strategy == strategy
def test_get_custom_strategy_add_patch_in_the_root():
    patch = Dictdiffer_Conflict(
        ('add', '', [('a', '1')]),
        ('add', '', [('a', '2')])
    )

    custom_ops = {
        'a': DictMergerOps.FALLBACK_KEEP_UPDATE
    }

    expected_strategy = 's'

    m = SkipListsMerger(
        {}, {}, {},
        DictMergerOps.FALLBACK_KEEP_HEAD,
        custom_ops=custom_ops
    )

    strategy = m._get_custom_strategy(patch)

    assert expected_strategy == strategy
def test_get_custom_strategy_mixed_patch_with_list():
    patch = Dictdiffer_Conflict(
        ('add', ['a', 'c', 0], [('b', '1')]),
        ('add', ['a', 'c', 0], [('b', '2')])
    )

    custom_ops = {
        'a': DictMergerOps.FALLBACK_KEEP_UPDATE,
        'a.c.b': DictMergerOps.FALLBACK_KEEP_HEAD
    }

    expected_strategy = 'f'

    m = SkipListsMerger(
        {}, {}, {},
        DictMergerOps.FALLBACK_KEEP_HEAD,
        custom_ops=custom_ops
    )

    strategy = m._get_custom_strategy(patch)

    assert expected_strategy == strategy
def test_base_values():
    m = SkipListsMerger(1, 2, 1, DictMergerOps.FALLBACK_KEEP_HEAD)
    m.merge()
    assert m.merged_root == 2

    m = SkipListsMerger(1, 1, 2, DictMergerOps.FALLBACK_KEEP_HEAD)
    m.merge()
    assert m.merged_root == 2
def test_get_rule_for_field_looks_at_correct_path():
    def keep_spam(head, update, down_path):
        return 's' if update == 'spam' else 'f'

    custom_ops = {
        'a.b': keep_spam
    }
    head = {
        'a': {
            'b': [
                'egg',
                'bacon',
            ],
            'c': 'egg',
        },
    }
    update = {
        'a': {
            'b': [
                'spam',
                'bread',
            ],
        },
    }

    m = SkipListsMerger(
        {}, head, update,
        DictMergerOps.FALLBACK_KEEP_HEAD,
        custom_ops=custom_ops, data_lists='a.b'
    )

    expected = 's'

    output = m._get_rule_for_field(['a', 'b', 0])

    assert expected == output