""" Tests for netbox_librenms_plugin.import_validation_helpers module. Phase 2 tests covering validation state updates, model retrieval, and selection extraction functions. """ from unittest.mock import MagicMock # ============================================================================= # TestGetModelById - 4 tests # ============================================================================= class TestFetchModelById: """Test generic model retrieval helper.""" def test_fetch_model_by_id_success(self): """Return model instance when found.""" mock_model_class = MagicMock() mock_instance = MagicMock(id=1, name="Access Switch") mock_model_class.objects.get.return_value = mock_instance from netbox_librenms_plugin.import_validation_helpers import fetch_model_by_id result = fetch_model_by_id(mock_model_class, 1) assert result == mock_instance mock_model_class.objects.get.assert_called_once_with(pk=1) def test_fetch_model_by_id_not_found(self): """Return None when ID doesn't exist.""" mock_model_class = MagicMock() mock_model_class.DoesNotExist = Exception mock_model_class.objects.get.side_effect = mock_model_class.DoesNotExist from netbox_librenms_plugin.import_validation_helpers import fetch_model_by_id result = fetch_model_by_id(mock_model_class, 999) assert result is None def test_fetch_model_by_id_invalid_id(self): """Handle invalid ID gracefully.""" mock_model_class = MagicMock() mock_model_class.DoesNotExist = type("DoesNotExist", (Exception,), {}) from netbox_librenms_plugin.import_validation_helpers import fetch_model_by_id result = fetch_model_by_id(mock_model_class, "not-a-number") assert result is None def test_fetch_model_by_id_none_id(self): """Handle None ID gracefully.""" mock_model_class = MagicMock() from netbox_librenms_plugin.import_validation_helpers import fetch_model_by_id result = fetch_model_by_id(mock_model_class, None) assert result is None mock_model_class.objects.get.assert_not_called() # ============================================================================= # TestExtractSelections - 4 tests # ============================================================================= class TestExtractDeviceSelections: """Test extraction of device selections from request.""" def test_extract_selections_all_present(self): """All selections extracted from POST request.""" from netbox_librenms_plugin.import_validation_helpers import ( extract_device_selections, ) mock_request = MagicMock() mock_request.method = "POST" mock_request.POST = { "cluster_1234": "5", "role_1234": "10", "rack_1234": "15", } result = extract_device_selections(mock_request, device_id=1234) assert result["cluster_id"] == "5" assert result["role_id"] == "10" assert result["rack_id"] == "15" def test_extract_selections_partial(self): """Missing fields return None.""" from netbox_librenms_plugin.import_validation_helpers import ( extract_device_selections, ) mock_request = MagicMock() mock_request.method = "POST" mock_request.POST = { "role_1234": "10", } result = extract_device_selections(mock_request, device_id=1234) assert result["cluster_id"] is None assert result["role_id"] == "10" assert result["rack_id"] is None def test_extract_selections_from_get(self): """Selections extracted from GET request.""" from netbox_librenms_plugin.import_validation_helpers import ( extract_device_selections, ) mock_request = MagicMock() mock_request.method = "GET" mock_request.GET = { "cluster_999": "3", "role_999": "7", "rack_999": "11", } result = extract_device_selections(mock_request, device_id=999) assert result["cluster_id"] == "3" assert result["role_id"] == "7" assert result["rack_id"] == "11" def test_extract_selections_empty_values(self): """Empty strings handled correctly.""" from netbox_librenms_plugin.import_validation_helpers import ( extract_device_selections, ) mock_request = MagicMock() mock_request.method = "POST" mock_request.POST = { "cluster_1234": "", "role_1234": "", "rack_1234": "", } result = extract_device_selections(mock_request, device_id=1234) # Empty strings are returned as-is (caller decides meaning) assert result["cluster_id"] == "" assert result["role_id"] == "" assert result["rack_id"] == "" # ============================================================================= # TestValidationStateUpdates - 10 tests # ============================================================================= class TestValidationStateUpdates: """Test validation state mutation functions.""" def test_apply_role_to_validation_success(self): """Role selection updates state correctly.""" from netbox_librenms_plugin.import_validation_helpers import ( apply_role_to_validation, ) mock_role = MagicMock(id=1, name="Access Switch") validation = { "device_role": {"found": False, "role": None}, "issues": ["Device role must be manually selected before import"], "can_import": False, "is_ready": False, "site": {"found": True}, "device_type": {"found": True}, } apply_role_to_validation(validation, mock_role, is_vm=False) assert validation["device_role"]["found"] is True assert validation["device_role"]["role"] == mock_role def test_apply_role_to_validation_clears_issue(self): """Selecting role should clear 'role' related validation issue.""" from netbox_librenms_plugin.import_validation_helpers import ( apply_role_to_validation, ) mock_role = MagicMock(id=1, name="Access Switch") validation = { "device_role": {"found": False, "role": None}, "issues": ["Device role must be manually selected before import"], "can_import": False, "is_ready": False, "site": {"found": True}, "device_type": {"found": True}, } apply_role_to_validation(validation, mock_role, is_vm=False) assert len(validation["issues"]) == 0 def test_apply_cluster_to_validation_success(self): """Cluster selection updates state for VM import.""" from netbox_librenms_plugin.import_validation_helpers import ( apply_cluster_to_validation, ) mock_cluster = MagicMock(id=1, name="VMware Cluster 1") validation = { "cluster": {"found": False, "cluster": None}, "issues": ["Cluster must be manually selected before import"], "can_import": False, "is_ready": False, } apply_cluster_to_validation(validation, mock_cluster) assert validation["cluster"]["found"] is True assert validation["cluster"]["cluster"] == mock_cluster def test_apply_rack_to_validation_success(self): """Rack selection updates state for device import.""" from netbox_librenms_plugin.import_validation_helpers import ( apply_rack_to_validation, ) mock_rack = MagicMock(id=1, name="Rack A1") validation = { "issues": [], "can_import": True, "is_ready": True, } apply_rack_to_validation(validation, mock_rack) assert validation["rack"]["found"] is True assert validation["rack"]["rack"] == mock_rack def test_remove_validation_issue_single(self): """Remove single issue by keyword.""" from netbox_librenms_plugin.import_validation_helpers import ( remove_validation_issue, ) validation = { "issues": [ "Device role must be manually selected before import", "Site not found for location 'DC1'", ] } remove_validation_issue(validation, "role") assert len(validation["issues"]) == 1 assert "Site not found" in validation["issues"][0] def test_remove_validation_issue_multiple(self): """Remove multiple matching issues.""" from netbox_librenms_plugin.import_validation_helpers import ( remove_validation_issue, ) validation = { "issues": [ "Device role must be selected", "Role is required for import", "Site not found", ] } remove_validation_issue(validation, "role") assert len(validation["issues"]) == 1 assert "Site not found" in validation["issues"][0] def test_remove_validation_issue_no_match(self): """No change when keyword not found.""" from netbox_librenms_plugin.import_validation_helpers import ( remove_validation_issue, ) validation = { "issues": [ "Site not found for location 'DC1'", "Device type not matched", ] } remove_validation_issue(validation, "cluster") assert len(validation["issues"]) == 2 def test_recalculate_can_import_all_ready_device(self): """can_import=True when all requirements met for device.""" from netbox_librenms_plugin.import_validation_helpers import ( recalculate_validation_status, ) validation = { "issues": [], "can_import": False, "is_ready": False, "site": {"found": True}, "device_type": {"found": True}, "device_role": {"found": True}, } recalculate_validation_status(validation, is_vm=False) assert validation["can_import"] is True assert validation["is_ready"] is True def test_recalculate_can_import_missing_required_device(self): """can_import=False when required field missing for device.""" from netbox_librenms_plugin.import_validation_helpers import ( recalculate_validation_status, ) validation = { "issues": ["Site not found"], "can_import": True, # Should become False "is_ready": True, "site": {"found": False}, "device_type": {"found": True}, "device_role": {"found": True}, } recalculate_validation_status(validation, is_vm=False) assert validation["can_import"] is False assert validation["is_ready"] is False def test_recalculate_can_import_vm_cluster_required(self): """VM import requires cluster to be ready.""" from netbox_librenms_plugin.import_validation_helpers import ( recalculate_validation_status, ) validation = { "issues": [], "can_import": False, "is_ready": False, "cluster": {"found": True}, } recalculate_validation_status(validation, is_vm=True) assert validation["can_import"] is True assert validation["is_ready"] is True def test_recalculate_can_import_vm_missing_cluster(self): """VM import not ready without cluster.""" from netbox_librenms_plugin.import_validation_helpers import ( recalculate_validation_status, ) validation = { "issues": [], "can_import": False, "is_ready": False, "cluster": {"found": False}, } recalculate_validation_status(validation, is_vm=True) assert validation["can_import"] is True # No issues assert validation["is_ready"] is False # But not ready without cluster