@@ -56,19 +56,25 @@ def test_diffsync_sync_self_with_no_data_is_noop(generic_diffsync):
5656    assert  not  generic_diffsync .sync_complete .called 
5757
5858
59- def  test_diffsync_get_with_no_data_is_none (generic_diffsync ):
60-     assert  generic_diffsync .get ("anything" , "myname" ) is  None 
61-     assert  generic_diffsync .get (DiffSyncModel , "" ) is  None 
59+ def  test_diffsync_get_with_no_data_fails (generic_diffsync ):
60+     with  pytest .raises (ObjectNotFound ):
61+         generic_diffsync .get ("anything" , "myname" )
62+     with  pytest .raises (ObjectNotFound ):
63+         generic_diffsync .get (DiffSyncModel , "" )
6264
6365
6466def  test_diffsync_get_all_with_no_data_is_empty_list (generic_diffsync ):
6567    assert  list (generic_diffsync .get_all ("anything" )) ==  []
6668    assert  list (generic_diffsync .get_all (DiffSyncModel )) ==  []
6769
6870
69- def  test_diffsync_get_by_uids_with_no_data_is_empty_list (generic_diffsync ):
70-     assert  generic_diffsync .get_by_uids (["any" , "another" ], "anything" ) ==  []
71-     assert  generic_diffsync .get_by_uids (["any" , "another" ], DiffSyncModel ) ==  []
71+ def  test_diffsync_get_by_uids_with_no_data (generic_diffsync ):
72+     assert  generic_diffsync .get_by_uids ([], "anything" ) ==  []
73+     assert  generic_diffsync .get_by_uids ([], DiffSyncModel ) ==  []
74+     with  pytest .raises (ObjectNotFound ):
75+         generic_diffsync .get_by_uids (["any" , "another" ], "anything" )
76+     with  pytest .raises (ObjectNotFound ):
77+         generic_diffsync .get_by_uids (["any" , "another" ], DiffSyncModel )
7278
7379
7480def  test_diffsync_add (generic_diffsync , generic_diffsync_model ):
@@ -83,12 +89,15 @@ def test_diffsync_get_with_generic_model(generic_diffsync, generic_diffsync_mode
8389    # The generic_diffsync_model has an empty identifier/unique-id 
8490    assert  generic_diffsync .get (DiffSyncModel , "" ) ==  generic_diffsync_model 
8591    assert  generic_diffsync .get (DiffSyncModel .get_type (), "" ) ==  generic_diffsync_model 
86-     # DiffSync doesn't know how to construct a uid str for a "diffsyncmodel" 
87-     assert  generic_diffsync .get (DiffSyncModel .get_type (), {}) is  None 
92+     # DiffSync doesn't know how to construct a uid str for a "diffsyncmodel" (it needs the class or instance, not a str) 
93+     with  pytest .raises (ValueError ):
94+         generic_diffsync .get (DiffSyncModel .get_type (), {})
8895    # Wrong object-type - no match 
89-     assert  generic_diffsync .get ("" , "" ) is  None 
96+     with  pytest .raises (ObjectNotFound ):
97+         generic_diffsync .get ("" , "" )
9098    # Wrong unique-id - no match 
91-     assert  generic_diffsync .get (DiffSyncModel , "myname" ) is  None 
99+     with  pytest .raises (ObjectNotFound ):
100+         generic_diffsync .get (DiffSyncModel , "myname" )
92101
93102
94103def  test_diffsync_get_all_with_generic_model (generic_diffsync , generic_diffsync_model ):
@@ -104,9 +113,11 @@ def test_diffsync_get_by_uids_with_generic_model(generic_diffsync, generic_diffs
104113    assert  generic_diffsync .get_by_uids (["" ], DiffSyncModel ) ==  [generic_diffsync_model ]
105114    assert  generic_diffsync .get_by_uids (["" ], DiffSyncModel .get_type ()) ==  [generic_diffsync_model ]
106115    # Wrong unique-id - no match 
107-     assert  generic_diffsync .get_by_uids (["myname" ], DiffSyncModel ) ==  []
108-     # Valid unique-id mixed in with unknown ones - return the successful matches? 
109-     assert  generic_diffsync .get_by_uids (["aname" , "" , "anothername" ], DiffSyncModel ) ==  [generic_diffsync_model ]
116+     with  pytest .raises (ObjectNotFound ):
117+         generic_diffsync .get_by_uids (["myname" ], DiffSyncModel )
118+     # Valid unique-id mixed in with unknown ones 
119+     with  pytest .raises (ObjectNotFound ):
120+         generic_diffsync .get_by_uids (["aname" , "" , "anothername" ], DiffSyncModel )
110121
111122
112123def  test_diffsync_remove_with_generic_model (generic_diffsync , generic_diffsync_model ):
@@ -115,18 +126,19 @@ def test_diffsync_remove_with_generic_model(generic_diffsync, generic_diffsync_m
115126    with  pytest .raises (ObjectNotFound ):
116127        generic_diffsync .remove (generic_diffsync_model )
117128
118-     assert  generic_diffsync .get (DiffSyncModel , "" ) is  None 
129+     with  pytest .raises (ObjectNotFound ):
130+         generic_diffsync .get (DiffSyncModel , "" )
119131    assert  list (generic_diffsync .get_all (DiffSyncModel )) ==  []
120-     assert  generic_diffsync .get_by_uids (["" ], DiffSyncModel ) ==  []
132+     with  pytest .raises (ObjectNotFound ):
133+         generic_diffsync .get_by_uids (["" ], DiffSyncModel )
121134
122135
123- def  test_diffsync_subclass_validation ():
124-     """Test the declaration-time checks on a DiffSync subclass.""" 
136+ def  test_diffsync_subclass_validation_name_mismatch ():
125137    # pylint: disable=unused-variable 
126138    with  pytest .raises (AttributeError ) as  excinfo :
127139
128140        class  BadElementName (DiffSync ):
129-             """Model  with a DiffSyncModel attribute whose name does not match the modelname.""" 
141+             """DiffSync  with a DiffSyncModel attribute whose name does not match the modelname.""" 
130142
131143            dev_class  =  Device   # should be device = Device 
132144
@@ -135,6 +147,35 @@ class BadElementName(DiffSync):
135147    assert  "dev_class"  in  str (excinfo .value )
136148
137149
150+ def  test_diffsync_subclass_validation_missing_top_level ():
151+     # pylint: disable=unused-variable 
152+     with  pytest .raises (AttributeError ) as  excinfo :
153+ 
154+         class  MissingTopLevel (DiffSync ):
155+             """DiffSync whose top_level references an attribute that does not exist on the class.""" 
156+ 
157+             top_level  =  ["missing" ]
158+ 
159+     assert  "top_level"  in  str (excinfo .value )
160+     assert  "missing"  in  str (excinfo .value )
161+     assert  "is not a class attribute"  in  str (excinfo .value )
162+ 
163+ 
164+ def  test_diffsync_subclass_validation_top_level_not_diffsyncmodel ():
165+     # pylint: disable=unused-variable 
166+     with  pytest .raises (AttributeError ) as  excinfo :
167+ 
168+         class  TopLevelNotDiffSyncModel (DiffSync ):
169+             """DiffSync whose top_level references an attribute that is not a DiffSyncModel subclass.""" 
170+ 
171+             age  =  0 
172+             top_level  =  ["age" ]
173+ 
174+     assert  "top_level"  in  str (excinfo .value )
175+     assert  "age"  in  str (excinfo .value )
176+     assert  "is not a DiffSyncModel"  in  str (excinfo .value )
177+ 
178+ 
138179def  test_diffsync_dict_with_data (backend_a ):
139180    assert  backend_a .dict () ==  {
140181        "device" : {
@@ -238,7 +279,9 @@ def test_diffsync_str_with_data(backend_a):
238279          interface: rdu-spine2__eth0: {'interface_type': 'ethernet', 'description': 'Interface 0'} 
239280          interface: rdu-spine2__eth1: {'interface_type': 'ethernet', 'description': 'Interface 1'} 
240281    people 
241-       person: Glenn Matthews: {}""" 
282+       person: Glenn Matthews: {} 
283+ unused: []\  
284+ 
242285    )
243286
244287
@@ -308,17 +351,21 @@ def test_diffsync_sync_from(backend_a, backend_b):
308351    site_atl_a  =  backend_a .get ("site" , "atl" )
309352    assert  isinstance (site_atl_a , Site )
310353    assert  site_atl_a .name  ==  "atl" 
311-     assert  backend_a .get (Site , "rdu" ) is  None 
312-     assert  backend_a .get ("nothing" , "" ) is  None 
354+     with  pytest .raises (ObjectNotFound ):
355+         backend_a .get (Site , "rdu" )
356+     with  pytest .raises (ObjectNotFound ):
357+         backend_a .get ("nothing" , "" )
313358
314359    assert  list (backend_a .get_all (Site )) ==  [site_nyc_a , site_sfo_a , site_atl_a ]
315360    assert  list (backend_a .get_all ("site" )) ==  [site_nyc_a , site_sfo_a , site_atl_a ]
316361    assert  list (backend_a .get_all ("nothing" )) ==  []
317362
318363    assert  backend_a .get_by_uids (["nyc" , "sfo" ], Site ) ==  [site_nyc_a , site_sfo_a ]
319364    assert  backend_a .get_by_uids (["sfo" , "nyc" ], "site" ) ==  [site_sfo_a , site_nyc_a ]
320-     assert  backend_a .get_by_uids (["nyc" , "sfo" ], Device ) ==  []
321-     assert  backend_a .get_by_uids (["nyc" , "sfo" ], "device" ) ==  []
365+     with  pytest .raises (ObjectNotFound ):
366+         backend_a .get_by_uids (["nyc" , "sfo" ], Device )
367+     with  pytest .raises (ObjectNotFound ):
368+         backend_a .get_by_uids (["nyc" , "sfo" ], "device" )
322369
323370
324371def  test_diffsync_subclass_default_name_type (backend_a ):
@@ -354,6 +401,18 @@ def test_diffsync_add_get_remove_with_subclass_and_data(backend_a):
354401        backend_a .remove (site_atl_a )
355402
356403
404+ def  test_diffsync_remove_missing_child (log , backend_a ):
405+     rdu_spine1  =  backend_a .get (Device , "rdu-spine1" )
406+     rdu_spine1_eth0  =  backend_a .get (Interface , "rdu-spine1__eth0" )
407+     # Usage error - remove rdu_spine1_eth0 from backend_a, but rdu_spine1 still has a reference to it 
408+     backend_a .remove (rdu_spine1_eth0 )
409+     # Should log an error but continue removing other child objects 
410+     backend_a .remove (rdu_spine1 , remove_children = True )
411+     assert  log .has ("Unable to remove child rdu-spine1__eth0 of device rdu-spine1 - not found!" , diffsync = backend_a )
412+     with  pytest .raises (ObjectNotFound ):
413+         backend_a .get (Interface , "rdu-spine1__eth1" )
414+ 
415+ 
357416def  test_diffsync_sync_from_exceptions_are_not_caught_by_default (error_prone_backend_a , backend_b ):
358417    with  pytest .raises (ObjectCrudException ):
359418        error_prone_backend_a .sync_from (backend_b )
@@ -430,8 +489,10 @@ def test_diffsync_diff_with_skip_unmatched_both_flag(
430489def  test_diffsync_sync_with_skip_unmatched_src_flag (backend_a , backend_a_with_extra_models ):
431490    backend_a .sync_from (backend_a_with_extra_models , flags = DiffSyncFlags .SKIP_UNMATCHED_SRC )
432491    # New objects should not have been created 
433-     assert  backend_a .get (backend_a .site , "lax" ) is  None 
434-     assert  backend_a .get (backend_a .device , "nyc-spine3" ) is  None 
492+     with  pytest .raises (ObjectNotFound ):
493+         backend_a .get (backend_a .site , "lax" )
494+     with  pytest .raises (ObjectNotFound ):
495+         backend_a .get (backend_a .device , "nyc-spine3" )
435496    assert  "nyc-spine3"  not  in backend_a .get (backend_a .site , "nyc" ).devices 
436497
437498
@@ -491,7 +552,8 @@ class NoDeleteInterfaceDiffSync(BackendA):
491552    # NoDeleteInterface.delete() should not be called since we're deleting its parent only 
492553    extra_models .sync_from (backend_a )
493554    # The extra interface should have been removed from the DiffSync without calling its delete() method 
494-     assert  extra_models .get (extra_models .interface , extra_interface .get_unique_id ()) is  None 
555+     with  pytest .raises (ObjectNotFound ):
556+         extra_models .get (extra_models .interface , extra_interface .get_unique_id ())
495557    # The sync should be complete, regardless 
496558    diff  =  extra_models .diff_from (backend_a )
497559    print (diff .str ())  # for debugging of any failure 
0 commit comments