369 lines
12 KiB
Python
369 lines
12 KiB
Python
"""
|
|
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
|