Created IAC reverse generator
This commit is contained in:
193
tests/unit/test_scan_profile_validation.py
Normal file
193
tests/unit/test_scan_profile_validation.py
Normal file
@@ -0,0 +1,193 @@
|
||||
"""Unit tests for ScanProfile validation logic."""
|
||||
|
||||
import pytest
|
||||
|
||||
from iac_reverse.models import (
|
||||
MAX_RESOURCE_TYPE_FILTERS,
|
||||
PROVIDER_SUPPORTED_RESOURCE_TYPES,
|
||||
ProviderType,
|
||||
ScanProfile,
|
||||
)
|
||||
|
||||
|
||||
class TestScanProfileValidationCredentials:
|
||||
"""Tests for credentials validation."""
|
||||
|
||||
def test_empty_credentials_returns_error(self):
|
||||
profile = ScanProfile(
|
||||
provider=ProviderType.KUBERNETES,
|
||||
credentials={},
|
||||
)
|
||||
errors = profile.validate()
|
||||
assert any("credentials must not be empty" in e for e in errors)
|
||||
|
||||
def test_non_empty_credentials_passes(self):
|
||||
profile = ScanProfile(
|
||||
provider=ProviderType.KUBERNETES,
|
||||
credentials={"kubeconfig_path": "/home/user/.kube/config"},
|
||||
)
|
||||
errors = profile.validate()
|
||||
assert errors == []
|
||||
|
||||
|
||||
class TestScanProfileValidationResourceTypeFilters:
|
||||
"""Tests for resource_type_filters count limit."""
|
||||
|
||||
def test_none_filters_passes(self):
|
||||
profile = ScanProfile(
|
||||
provider=ProviderType.DOCKER_SWARM,
|
||||
credentials={"host": "localhost"},
|
||||
resource_type_filters=None,
|
||||
)
|
||||
errors = profile.validate()
|
||||
assert errors == []
|
||||
|
||||
def test_empty_filters_passes(self):
|
||||
profile = ScanProfile(
|
||||
provider=ProviderType.DOCKER_SWARM,
|
||||
credentials={"host": "localhost"},
|
||||
resource_type_filters=[],
|
||||
)
|
||||
errors = profile.validate()
|
||||
assert errors == []
|
||||
|
||||
def test_filters_at_max_limit_passes(self):
|
||||
# Use valid resource types repeated to reach the limit
|
||||
valid_types = PROVIDER_SUPPORTED_RESOURCE_TYPES[ProviderType.WINDOWS]
|
||||
filters = (valid_types * (MAX_RESOURCE_TYPE_FILTERS // len(valid_types) + 1))[
|
||||
:MAX_RESOURCE_TYPE_FILTERS
|
||||
]
|
||||
profile = ScanProfile(
|
||||
provider=ProviderType.WINDOWS,
|
||||
credentials={"host": "win01"},
|
||||
resource_type_filters=filters,
|
||||
)
|
||||
errors = profile.validate()
|
||||
assert errors == []
|
||||
|
||||
def test_filters_exceeding_max_limit_returns_error(self):
|
||||
# Use valid resource types repeated to exceed the limit
|
||||
valid_types = PROVIDER_SUPPORTED_RESOURCE_TYPES[ProviderType.WINDOWS]
|
||||
filters = (valid_types * (MAX_RESOURCE_TYPE_FILTERS // len(valid_types) + 1))[
|
||||
: MAX_RESOURCE_TYPE_FILTERS + 1
|
||||
]
|
||||
profile = ScanProfile(
|
||||
provider=ProviderType.WINDOWS,
|
||||
credentials={"host": "win01"},
|
||||
resource_type_filters=filters,
|
||||
)
|
||||
errors = profile.validate()
|
||||
assert any("at most" in e and "200" in e for e in errors)
|
||||
|
||||
def test_max_limit_is_200(self):
|
||||
assert MAX_RESOURCE_TYPE_FILTERS == 200
|
||||
|
||||
|
||||
class TestScanProfileValidationResourceTypeSupport:
|
||||
"""Tests for resource type validation against provider's supported types."""
|
||||
|
||||
def test_valid_resource_types_for_kubernetes(self):
|
||||
profile = ScanProfile(
|
||||
provider=ProviderType.KUBERNETES,
|
||||
credentials={"kubeconfig_path": "/path"},
|
||||
resource_type_filters=["kubernetes_deployment", "kubernetes_service"],
|
||||
)
|
||||
errors = profile.validate()
|
||||
assert errors == []
|
||||
|
||||
def test_valid_resource_types_for_docker_swarm(self):
|
||||
profile = ScanProfile(
|
||||
provider=ProviderType.DOCKER_SWARM,
|
||||
credentials={"host": "localhost"},
|
||||
resource_type_filters=["docker_service", "docker_network"],
|
||||
)
|
||||
errors = profile.validate()
|
||||
assert errors == []
|
||||
|
||||
def test_unsupported_resource_type_returns_error(self):
|
||||
profile = ScanProfile(
|
||||
provider=ProviderType.KUBERNETES,
|
||||
credentials={"kubeconfig_path": "/path"},
|
||||
resource_type_filters=["kubernetes_deployment", "invalid_type"],
|
||||
)
|
||||
errors = profile.validate()
|
||||
assert any("unsupported resource types" in e for e in errors)
|
||||
assert any("invalid_type" in e for e in errors)
|
||||
|
||||
def test_cross_provider_resource_type_returns_error(self):
|
||||
"""A resource type valid for one provider is invalid for another."""
|
||||
profile = ScanProfile(
|
||||
provider=ProviderType.KUBERNETES,
|
||||
credentials={"kubeconfig_path": "/path"},
|
||||
resource_type_filters=["docker_service"],
|
||||
)
|
||||
errors = profile.validate()
|
||||
assert any("unsupported resource types" in e for e in errors)
|
||||
assert any("docker_service" in e for e in errors)
|
||||
|
||||
def test_multiple_unsupported_types_listed_in_error(self):
|
||||
profile = ScanProfile(
|
||||
provider=ProviderType.SYNOLOGY,
|
||||
credentials={"host": "nas01"},
|
||||
resource_type_filters=["fake_type_a", "fake_type_b"],
|
||||
)
|
||||
errors = profile.validate()
|
||||
assert any("fake_type_a" in e and "fake_type_b" in e for e in errors)
|
||||
|
||||
@pytest.mark.parametrize("provider", list(ProviderType))
|
||||
def test_all_providers_have_supported_types(self, provider):
|
||||
"""Every provider must have at least one supported resource type."""
|
||||
assert provider in PROVIDER_SUPPORTED_RESOURCE_TYPES
|
||||
assert len(PROVIDER_SUPPORTED_RESOURCE_TYPES[provider]) > 0
|
||||
|
||||
@pytest.mark.parametrize("provider", list(ProviderType))
|
||||
def test_all_supported_types_pass_validation(self, provider):
|
||||
"""All listed supported types for a provider should pass validation."""
|
||||
supported = PROVIDER_SUPPORTED_RESOURCE_TYPES[provider]
|
||||
profile = ScanProfile(
|
||||
provider=provider,
|
||||
credentials={"key": "value"},
|
||||
resource_type_filters=supported,
|
||||
)
|
||||
errors = profile.validate()
|
||||
assert errors == []
|
||||
|
||||
|
||||
class TestScanProfileValidationMultipleErrors:
|
||||
"""Tests that all validation errors are returned in a single response."""
|
||||
|
||||
def test_empty_credentials_and_too_many_filters(self):
|
||||
filters = ["invalid_type"] * (MAX_RESOURCE_TYPE_FILTERS + 1)
|
||||
profile = ScanProfile(
|
||||
provider=ProviderType.HARVESTER,
|
||||
credentials={},
|
||||
resource_type_filters=filters,
|
||||
)
|
||||
errors = profile.validate()
|
||||
# Should have at least: credentials error, too many filters, unsupported types
|
||||
assert len(errors) >= 3
|
||||
assert any("credentials" in e for e in errors)
|
||||
assert any("at most" in e for e in errors)
|
||||
assert any("unsupported" in e for e in errors)
|
||||
|
||||
def test_empty_credentials_and_unsupported_types(self):
|
||||
profile = ScanProfile(
|
||||
provider=ProviderType.BARE_METAL,
|
||||
credentials={},
|
||||
resource_type_filters=["nonexistent_type"],
|
||||
)
|
||||
errors = profile.validate()
|
||||
assert len(errors) >= 2
|
||||
assert any("credentials" in e for e in errors)
|
||||
assert any("unsupported" in e for e in errors)
|
||||
|
||||
def test_no_short_circuit_on_first_error(self):
|
||||
"""Validation must not stop at the first error found."""
|
||||
profile = ScanProfile(
|
||||
provider=ProviderType.WINDOWS,
|
||||
credentials={},
|
||||
resource_type_filters=["totally_fake_resource"],
|
||||
)
|
||||
errors = profile.validate()
|
||||
# Both credentials and unsupported type errors should be present
|
||||
assert len(errors) >= 2
|
||||
Reference in New Issue
Block a user