def _make_contract(name, illegal_dependencies=None): contract = Contract(name, [], []) if illegal_dependencies is None: illegal_dependencies = [ ('foo.upstream', 'foo.downstream'), ] contract.illegal_dependencies = illegal_dependencies return contract
def test_kept_contract(self): contract = Contract( name='Foo contract', packages=('foo', ), layers=( Layer('three'), Layer('two'), Layer('one'), ), whitelisted_paths=mock.sentinel.whitelisted_paths, ) dep_graph = mock.Mock() dep_graph.find_path.return_value = None dep_graph.get_descendants.return_value = [] contract.check_dependencies(dep_graph) assert contract.is_kept is True # Check that each of the possible disallowed imports were checked dep_graph.find_path.assert_has_calls(( mock.call(downstream='foo.one', upstream='foo.two', ignore_paths=mock.sentinel.whitelisted_paths), mock.call(downstream='foo.one', upstream='foo.three', ignore_paths=mock.sentinel.whitelisted_paths), mock.call(downstream='foo.two', upstream='foo.three', ignore_paths=mock.sentinel.whitelisted_paths), ))
def test_broken_contract(self): contract = Contract( name='Foo contract', packages=('foo', ), layers=( Layer('three'), Layer('two'), Layer('one'), ), ) dep_graph = mock.Mock() dep_graph.get_descendants.return_value = [] # Mock that one imports two and three, and two imports three dep_graph.find_path.side_effect = [ None, ['foo.one', 'foo.two'], ['foo.one', 'foo.three'], ['foo.two', 'foo.three'] ] contract.check_dependencies(dep_graph) assert contract.is_kept is False # Check that each of the possible disallowed imports are checked dep_graph.find_path.assert_has_calls(( mock.call(downstream='foo.one', upstream='foo.two', ignore_paths=[]), mock.call(downstream='foo.one', upstream='foo.three', ignore_paths=[]), mock.call(downstream='foo.two', upstream='foo.three', ignore_paths=[]), ))
def test_broken_contract_via_other_layer(self): # If an illegal import happens via another layer, we don't want to report it # (as it will already be reported). contract = Contract( name='Foo contract', packages=('foo', ), layers=( Layer('three'), Layer('two'), Layer('one'), ), ) dep_graph = mock.Mock() dep_graph.get_descendants.return_value = [] # Mock that one imports two, and two imports three dep_graph.find_path.side_effect = [ ['foo.one', 'foo.two'], ['foo.one', 'foo.two', 'foo.three'], ['foo.two', 'foo.three'], ] contract.check_dependencies(dep_graph) assert contract.illegal_dependencies == [ ['foo.one', 'foo.two'], ['foo.two', 'foo.three'], ]
def test_only_shortest_violation_is_reported(self, longer_first): contract = Contract( name='Foo contract', containers=( 'foo', ), layers=( Layer('two'), Layer('one'), ), ) # These are both dependency violations, but it's more useful just to report # the more direct violation. if longer_first: paths = { Module('foo.two'): { Module('foo.one.alpha'): [ Module('foo.one.alpha'), Module('foo.one.alpha.green'), Module('foo.another'), Module('foo.two'), ], Module('foo.one.alpha.green'): [ Module('foo.one.alpha.green'), Module('foo.another'), Module('foo.two'), ], }, } else: paths = { Module('foo.two'): { Module('foo.one.alpha'): [ Module('foo.one.alpha'), Module('foo.another'), Module('foo.two'), ], Module('foo.one.alpha.green'): [ Module('foo.one.alpha.green'), Module('foo.one.alpha'), Module('foo.another'), Module('foo.two'), ], }, } graph = StubDependencyGraph( descendants={ Module('foo.one'): [Module('foo.one.alpha'), Module('foo.one.beta'), Module('foo.one.alpha.blue'), Module('foo.one.alpha.green')], }, paths=paths, modules=[Module('foo.one'), Module('foo.two')] ) contract.check_dependencies(graph) if longer_first: assert contract.illegal_dependencies == [ [Module('foo.one.alpha.green'), Module('foo.another'), Module('foo.two')], ] else: assert contract.illegal_dependencies == [ [Module('foo.one.alpha'), Module('foo.another'), Module('foo.two')], ]
def test_broken_contract(self): contract = Contract( name='Foo contract', containers=( Module('foo.blue'), Module('foo.green'), ), layers=( Layer('three'), Layer('two'), Layer('one'), ), ) graph = StubDependencyGraph( descendants={ Module('foo.green.one'): [ Module('foo.green.one.alpha'), Module('foo.green.one.beta'), ], Module('foo.green.three'): [ Module('foo.green.three.alpha'), Module('foo.green.three.beta'), ], }, paths={ Module('foo.blue.two'): { # An allowed path: layer directly importing a layer below it. Module('foo.blue.three'): [Module('foo.blue.three'), Module('foo.blue.two')], # Disallowed path: layer directly importing a layer above it. Module('foo.blue.one'): [Module('foo.blue.one'), Module('foo.blue.two')], }, Module('foo.green.three.alpha'): { # Module inside layer importing a module inside a higher layer. Module('foo.green.one.alpha'): [Module('foo.green.one.alpha'), Module('foo.green.three.alpha')], }, }, modules=[ Module('foo.green'), Module('foo.green.one'), Module('foo.green.one.alpha'), Module('foo.green.one.beta'), Module('foo.green.two'), Module('foo.green.three'), Module('foo.blue'), Module('foo.blue.one'), Module('foo.blue.two'), Module('foo.blue.three'), ] ) contract.check_dependencies(graph) assert contract.is_kept is False assert contract.illegal_dependencies == [ [Module('foo.blue.one'), Module('foo.blue.two')], [Module('foo.green.one.alpha'), Module('foo.green.three.alpha')] ]
def test_unchecked_contract_raises_exception(self): contract = Contract( name='Foo contract', packages=('foo', ), layers=( Layer('three'), Layer('two'), Layer('one'), ), ) with pytest.raises(RuntimeError) as excinfo: contract.is_kept assert 'Cannot check whether contract is ' \ 'kept until check_dependencies is called.' in str(excinfo.value)
def test_broken_contract_children(self): contract = Contract( name='Foo contract', packages=('foo', ), layers=( Layer('two'), Layer('one'), ), ) dep_graph = mock.Mock() # Mock some deeper submodules dep_graph.get_descendants.side_effect = [ # For foo.one [ 'foo.one.alpha', 'foo.one.alpha.red', 'foo.one.alpha.green', 'foo.one.beta' ], # For foo.two [], ] # Mock that foo.one.alpha.red imports foo.two dep_graph.find_path.side_effect = [ None, None, ['foo.one.alpha.red', 'foo.two'], None, None ] contract.check_dependencies(dep_graph) assert contract.is_kept is False # Check that each of the possible disallowed imports are checked dep_graph.find_path.assert_has_calls(( mock.call(downstream='foo.one', upstream='foo.two', ignore_paths=[]), mock.call(downstream='foo.one.alpha', upstream='foo.two', ignore_paths=[]), mock.call(downstream='foo.one.alpha.red', upstream='foo.two', ignore_paths=[]), mock.call(downstream='foo.one.alpha.green', upstream='foo.two', ignore_paths=[]), mock.call(downstream='foo.one.beta', upstream='foo.two', ignore_paths=[]), ))
def test_broken_contract_via_other_layer(self): # If an illegal import happens via another layer, we don't want to report it # (as it will already be reported). contract = Contract( name='Foo contract', containers=( 'foo', ), layers=( Layer('three'), Layer('two'), Layer('one'), ), ) graph = StubDependencyGraph( descendants={}, paths={ Module('foo.three'): { Module('foo.two'): [Module('foo.two'), Module('foo.three')], Module('foo.one'): [Module('foo.one'), Module('foo.two'), Module('foo.three')], }, Module('foo.two'): { Module('foo.one'): [Module('foo.one'), Module('foo.two')], }, }, modules=[ Module('foo.one'), Module('foo.two'), Module('foo.three'), ] ) contract.check_dependencies(graph) assert contract.illegal_dependencies == [ [Module('foo.one'), Module('foo.two')], [Module('foo.two'), Module('foo.three')], ]
def test_missing_contract(self, is_optional): contract = Contract( name='Foo contract', containers=( 'foo.one', 'foo.two', ), layers=( Layer('blue'), Layer('yellow', is_optional=is_optional), # Missing from foo.two. Layer('green'), ), ) graph = StubDependencyGraph( modules=[ Module('foo.one'), Module('foo.one.blue'), Module('foo.one.blue.alpha'), Module('foo.one.yellow'), Module('foo.one.green'), Module('foo.two'), Module('foo.two.blue'), Module('foo.two.blue.alpha'), Module('foo.two.green'), ] ) if is_optional: # Should pass. contract.check_dependencies(graph) else: with pytest.raises(ValueError) as e: contract.check_dependencies(graph) assert str(e.value) == ( "Missing layer in container 'foo.two': module foo.two.yellow does not exist." )
def _make_contract(): contract = Contract('Foo contract', [], []) contract.check_dependencies(None) return contract
def test_only_shortest_violation_is_reported(self, longer_first): contract = Contract( name='Foo contract', packages=('foo', ), layers=( Layer('two'), Layer('one'), ), ) dep_graph = mock.Mock() dep_graph.get_descendants.side_effect = [ # For foo.one [ 'foo.one.alpha', 'foo.one.alpha.red', 'foo.one.alpha.green', 'foo.one.beta' ], # For foo.two [], ] # These are both dependency violations, but it's more useful just to report # the more direct violation (the second one in this case). if longer_first: dep_graph.find_path.side_effect = [ # foo.one <- foo.two None, # foo.one.alpha <- foo.two [ 'foo.one.alpha', 'foo.one.alpha.green', 'foo.x.alpha', 'foo.two' ], # foo.one.alpha.red <- foo.two [ 'foo.one.alpha.red', 'foo.one.alpha.green', 'foo.x.alpha', 'foo.two' ], # foo.one.alhpa.green <- foo.two ['foo.one.alpha.green', 'foo.x.alpha', 'foo.two'], # foo.one.beta <- foo.two None, ] else: dep_graph.find_path.side_effect = [ None, ['foo.one.alpha', 'foo.x.alpha', 'foo.two'], None, [ 'foo.one.alpha.green', 'foo.one.alpha', 'foo.x.alpha', 'foo.two' ], None, ] contract.check_dependencies(dep_graph) if longer_first: assert contract.illegal_dependencies == [ ['foo.one.alpha.green', 'foo.x.alpha', 'foo.two'], ] else: assert contract.illegal_dependencies == [ ['foo.one.alpha', 'foo.x.alpha', 'foo.two'], ]
def test_kept_contract(self): contract = Contract( name='Foo contract', containers=( Module('foo.blue'), Module('foo.green'), ), layers=( Layer('three'), Layer('two'), Layer('one'), ), whitelisted_paths=mock.sentinel.whitelisted_paths, ) graph = StubDependencyGraph( descendants={ Module('foo.green.one'): [ Module('foo.green.one.alpha'), Module('foo.green.one.beta'), ], Module('foo.green.three'): [ Module('foo.green.three.alpha'), Module('foo.green.three.beta'), ], }, paths={ # Include some allowed paths. Module('foo.blue.two'): { # Layer directly importing a layer below it. Module('foo.blue.three'): [Module('foo.blue.three'), Module('foo.blue.two')], }, Module('foo.blue.one'): { # Layer directly importing two layers below. Module('foo.blue.three'): [Module('foo.blue.three'), Module('foo.blue.one')], }, Module('foo.green.three'): { # Layer importing higher up layer, but from another container. Module('foo.blue.one'): [Module('foo.blue.one'), Module('foo.green.three')], }, Module('foo.green.three.beta'): { # Module inside layer importing another module in same layer. Module('foo.green.three.alpha'): [Module('foo.green.three.alpha'), Module('foo.green.three.beta')], }, Module('foo.green.one.alpha'): { # Module inside layer importing a module inside a lower layer. Module('foo.green.three.alpha'): [Module('foo.green.three.alpha'), Module('foo.green.one.alpha')] }, }, modules=[ Module('foo.green'), Module('foo.green.one'), Module('foo.green.one.alpha'), Module('foo.green.one.beta'), Module('foo.green.two'), Module('foo.green.three'), Module('foo.blue'), Module('foo.blue.one'), Module('foo.blue.two'), Module('foo.blue.three'), ] ) contract.check_dependencies(graph) assert contract.is_kept is True