@@ -520,6 +520,119 @@ def test_sync_without_location_returns_error(
520520
521521 assert result == EXIT_STATUS_FAILURE
522522
523+ @patch ("understack_workflows.oslo_event.nautobot_device_sync.IronicClient" )
524+ @patch ("understack_workflows.oslo_event.nautobot_device_sync.fetch_device_info" )
525+ @patch (
526+ "understack_workflows.oslo_event.nautobot_device_sync.sync_interfaces_from_data"
527+ )
528+ def test_sync_finds_device_by_name_with_matching_uuid (
529+ self , mock_sync_interfaces , mock_fetch , mock_ironic_class , mock_nautobot
530+ ):
531+ """Test that device found by name with matching UUID is updated."""
532+ node_uuid = str (uuid .uuid4 ())
533+ device_info = DeviceInfo (
534+ uuid = node_uuid ,
535+ name = "Dell-ABC123" ,
536+ manufacturer = "Dell" ,
537+ model = "PowerEdge R640" ,
538+ location_id = "location-uuid" ,
539+ status = "Active" ,
540+ )
541+ mock_fetch .return_value = (device_info , {}, [])
542+
543+ # First get by ID returns None
544+ # Second get by name returns device with same UUID
545+ existing_device = MagicMock ()
546+ existing_device .id = node_uuid # Same UUID
547+ existing_device .status = MagicMock (name = "Planned" )
548+ existing_device .name = "Dell-ABC123"
549+ existing_device .serial = None
550+ existing_device .location = None
551+ existing_device .rack = None
552+ existing_device .tenant = None
553+ existing_device .custom_fields = {}
554+
555+ mock_nautobot .dcim .devices .get .side_effect = [None , existing_device ]
556+ mock_sync_interfaces .return_value = EXIT_STATUS_SUCCESS
557+
558+ result = sync_device_to_nautobot (node_uuid , mock_nautobot )
559+
560+ assert result == EXIT_STATUS_SUCCESS
561+ # Should NOT delete since UUIDs match
562+ existing_device .delete .assert_not_called ()
563+ # Should NOT create new device
564+ mock_nautobot .dcim .devices .create .assert_not_called ()
565+
566+ @patch ("understack_workflows.oslo_event.nautobot_device_sync.IronicClient" )
567+ @patch ("understack_workflows.oslo_event.nautobot_device_sync.fetch_device_info" )
568+ @patch (
569+ "understack_workflows.oslo_event.nautobot_device_sync.sync_interfaces_from_data"
570+ )
571+ def test_sync_recreates_device_with_mismatched_uuid (
572+ self , mock_sync_interfaces , mock_fetch , mock_ironic_class , mock_nautobot
573+ ):
574+ """Test device with mismatched UUID is deleted and recreated."""
575+ node_uuid = str (uuid .uuid4 ())
576+ old_uuid = str (uuid .uuid4 ()) # Different UUID
577+ device_info = DeviceInfo (
578+ uuid = node_uuid ,
579+ name = "Dell-ABC123" ,
580+ manufacturer = "Dell" ,
581+ model = "PowerEdge R640" ,
582+ location_id = "location-uuid" ,
583+ status = "Active" ,
584+ )
585+ mock_fetch .return_value = (device_info , {}, [])
586+
587+ # First get by ID returns None
588+ # Second get by name returns device with different UUID
589+ existing_device = MagicMock ()
590+ existing_device .id = old_uuid # Different UUID
591+ existing_device .status = MagicMock (name = "Planned" )
592+ existing_device .name = "Dell-ABC123"
593+
594+ mock_nautobot .dcim .devices .get .side_effect = [None , existing_device ]
595+ mock_nautobot .dcim .devices .create .return_value = MagicMock ()
596+ mock_sync_interfaces .return_value = EXIT_STATUS_SUCCESS
597+
598+ result = sync_device_to_nautobot (node_uuid , mock_nautobot )
599+
600+ assert result == EXIT_STATUS_SUCCESS
601+ # Should delete old device
602+ existing_device .delete .assert_called_once ()
603+ # Should create new device with correct UUID
604+ mock_nautobot .dcim .devices .create .assert_called_once ()
605+
606+ @patch ("understack_workflows.oslo_event.nautobot_device_sync.IronicClient" )
607+ @patch ("understack_workflows.oslo_event.nautobot_device_sync.fetch_device_info" )
608+ @patch (
609+ "understack_workflows.oslo_event.nautobot_device_sync.sync_interfaces_from_data"
610+ )
611+ def test_sync_device_not_found_by_name_creates_new (
612+ self , mock_sync_interfaces , mock_fetch , mock_ironic_class , mock_nautobot
613+ ):
614+ """Test that device not found by UUID or name is created."""
615+ node_uuid = str (uuid .uuid4 ())
616+ device_info = DeviceInfo (
617+ uuid = node_uuid ,
618+ name = "Dell-ABC123" ,
619+ manufacturer = "Dell" ,
620+ model = "PowerEdge R640" ,
621+ location_id = "location-uuid" ,
622+ status = "Active" ,
623+ )
624+ mock_fetch .return_value = (device_info , {}, [])
625+
626+ # Both lookups return None
627+ mock_nautobot .dcim .devices .get .side_effect = [None , None ]
628+ mock_nautobot .dcim .devices .create .return_value = MagicMock ()
629+ mock_sync_interfaces .return_value = EXIT_STATUS_SUCCESS
630+
631+ result = sync_device_to_nautobot (node_uuid , mock_nautobot )
632+
633+ assert result == EXIT_STATUS_SUCCESS
634+ mock_nautobot .dcim .devices .create .assert_called_once ()
635+
523636
524637class TestDeleteDeviceFromNautobot :
525638 """Test cases for delete_device_from_nautobot function."""
0 commit comments