223 lines
8.2 KiB
Python
223 lines
8.2 KiB
Python
"""Property-based tests for resource inventory completeness.
|
|
|
|
**Validates: Requirements 1.2**
|
|
|
|
Property 1: Resource inventory completeness
|
|
For any discovered resource from any on-premises provider (Docker Swarm, Kubernetes,
|
|
Synology, Harvester, Bare Metal, Windows), the resulting inventory entry SHALL contain
|
|
non-empty values for resource_type, unique_id, name, provider, platform_category,
|
|
architecture, and attributes fields.
|
|
"""
|
|
|
|
from hypothesis import given, settings
|
|
from hypothesis import strategies as st
|
|
|
|
from iac_reverse.models import (
|
|
CpuArchitecture,
|
|
DiscoveredResource,
|
|
PlatformCategory,
|
|
ProviderType,
|
|
ScanResult,
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Hypothesis Strategies
|
|
# ---------------------------------------------------------------------------
|
|
|
|
provider_type_strategy = st.sampled_from(list(ProviderType))
|
|
platform_category_strategy = st.sampled_from(list(PlatformCategory))
|
|
cpu_architecture_strategy = st.sampled_from(list(CpuArchitecture))
|
|
|
|
non_empty_text_strategy = st.text(
|
|
min_size=1,
|
|
max_size=100,
|
|
alphabet=st.characters(whitelist_categories=("L", "N", "P", "S")),
|
|
).filter(lambda s: s.strip() != "")
|
|
|
|
resource_type_strategy = st.text(
|
|
min_size=1,
|
|
max_size=50,
|
|
alphabet=st.characters(whitelist_categories=("L", "N"), whitelist_characters="_"),
|
|
).filter(lambda s: len(s) > 0 and s.strip() != "")
|
|
|
|
unique_id_strategy = st.text(
|
|
min_size=1,
|
|
max_size=200,
|
|
alphabet=st.characters(whitelist_categories=("L", "N", "P", "S")),
|
|
).filter(lambda s: s.strip() != "")
|
|
|
|
name_strategy = st.text(
|
|
min_size=1,
|
|
max_size=100,
|
|
alphabet=st.characters(whitelist_categories=("L", "N", "P", "S")),
|
|
).filter(lambda s: s.strip() != "")
|
|
|
|
endpoint_strategy = st.text(
|
|
min_size=1,
|
|
max_size=200,
|
|
alphabet=st.characters(whitelist_categories=("L", "N", "P", "S")),
|
|
).filter(lambda s: s.strip() != "")
|
|
|
|
non_empty_attributes_strategy = st.dictionaries(
|
|
keys=st.text(
|
|
min_size=1,
|
|
max_size=30,
|
|
alphabet=st.characters(whitelist_categories=("L", "N"), whitelist_characters="_"),
|
|
),
|
|
values=st.one_of(
|
|
st.text(min_size=1, max_size=50),
|
|
st.integers(min_value=0, max_value=10000),
|
|
st.booleans(),
|
|
),
|
|
min_size=1,
|
|
max_size=10,
|
|
)
|
|
|
|
raw_references_strategy = st.lists(
|
|
st.text(min_size=0, max_size=100),
|
|
min_size=0,
|
|
max_size=5,
|
|
)
|
|
|
|
|
|
discovered_resource_strategy = st.builds(
|
|
DiscoveredResource,
|
|
resource_type=resource_type_strategy,
|
|
unique_id=unique_id_strategy,
|
|
name=name_strategy,
|
|
provider=provider_type_strategy,
|
|
platform_category=platform_category_strategy,
|
|
architecture=cpu_architecture_strategy,
|
|
endpoint=endpoint_strategy,
|
|
attributes=non_empty_attributes_strategy,
|
|
raw_references=raw_references_strategy,
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Property Tests
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class TestResourceInventoryCompleteness:
|
|
"""Property 1: Resource inventory completeness.
|
|
|
|
**Validates: Requirements 1.2**
|
|
|
|
For any discovered resource from any on-premises provider, the resulting
|
|
inventory entry SHALL contain non-empty values for resource_type, unique_id,
|
|
name, provider, platform_category, architecture, and attributes fields.
|
|
"""
|
|
|
|
@given(resource=discovered_resource_strategy)
|
|
def test_resource_type_is_non_empty(self, resource: DiscoveredResource):
|
|
"""resource_type field must be a non-empty string."""
|
|
assert isinstance(resource.resource_type, str)
|
|
assert len(resource.resource_type) > 0
|
|
assert resource.resource_type.strip() != ""
|
|
|
|
@given(resource=discovered_resource_strategy)
|
|
def test_unique_id_is_non_empty(self, resource: DiscoveredResource):
|
|
"""unique_id field must be a non-empty string."""
|
|
assert isinstance(resource.unique_id, str)
|
|
assert len(resource.unique_id) > 0
|
|
assert resource.unique_id.strip() != ""
|
|
|
|
@given(resource=discovered_resource_strategy)
|
|
def test_name_is_non_empty(self, resource: DiscoveredResource):
|
|
"""name field must be a non-empty string."""
|
|
assert isinstance(resource.name, str)
|
|
assert len(resource.name) > 0
|
|
assert resource.name.strip() != ""
|
|
|
|
@given(resource=discovered_resource_strategy)
|
|
def test_provider_is_valid_enum(self, resource: DiscoveredResource):
|
|
"""provider field must be a valid ProviderType enum value."""
|
|
assert isinstance(resource.provider, ProviderType)
|
|
assert resource.provider is not None
|
|
|
|
@given(resource=discovered_resource_strategy)
|
|
def test_platform_category_is_valid_enum(self, resource: DiscoveredResource):
|
|
"""platform_category field must be a valid PlatformCategory enum value."""
|
|
assert isinstance(resource.platform_category, PlatformCategory)
|
|
assert resource.platform_category is not None
|
|
|
|
@given(resource=discovered_resource_strategy)
|
|
def test_architecture_is_valid_enum(self, resource: DiscoveredResource):
|
|
"""architecture field must be a valid CpuArchitecture enum value."""
|
|
assert isinstance(resource.architecture, CpuArchitecture)
|
|
assert resource.architecture is not None
|
|
|
|
@given(resource=discovered_resource_strategy)
|
|
def test_attributes_is_non_empty_dict(self, resource: DiscoveredResource):
|
|
"""attributes field must be a non-empty dictionary."""
|
|
assert isinstance(resource.attributes, dict)
|
|
assert len(resource.attributes) > 0
|
|
|
|
@given(resource=discovered_resource_strategy)
|
|
def test_all_mandatory_fields_populated(self, resource: DiscoveredResource):
|
|
"""All mandatory fields must be non-empty/non-None simultaneously."""
|
|
# resource_type
|
|
assert isinstance(resource.resource_type, str) and len(resource.resource_type) > 0
|
|
# unique_id
|
|
assert isinstance(resource.unique_id, str) and len(resource.unique_id) > 0
|
|
# name
|
|
assert isinstance(resource.name, str) and len(resource.name) > 0
|
|
# provider
|
|
assert isinstance(resource.provider, ProviderType)
|
|
# platform_category
|
|
assert isinstance(resource.platform_category, PlatformCategory)
|
|
# architecture
|
|
assert isinstance(resource.architecture, CpuArchitecture)
|
|
# attributes
|
|
assert isinstance(resource.attributes, dict) and len(resource.attributes) > 0
|
|
|
|
@given(
|
|
resources=st.lists(discovered_resource_strategy, min_size=1, max_size=10),
|
|
warnings=st.lists(st.text(min_size=0, max_size=50), max_size=3),
|
|
errors=st.lists(st.text(min_size=0, max_size=50), max_size=3),
|
|
)
|
|
@settings(max_examples=50)
|
|
def test_scan_result_resources_all_have_mandatory_fields(
|
|
self, resources, warnings, errors
|
|
):
|
|
"""When a mock plugin produces resources in a ScanResult, every resource has all required fields."""
|
|
scan_result = ScanResult(
|
|
resources=resources,
|
|
warnings=warnings,
|
|
errors=errors,
|
|
scan_timestamp="2024-01-15T10:30:00Z",
|
|
profile_hash="test_hash_abc123",
|
|
is_partial=False,
|
|
)
|
|
|
|
for resource in scan_result.resources:
|
|
# resource_type is non-empty string
|
|
assert isinstance(resource.resource_type, str)
|
|
assert len(resource.resource_type) > 0
|
|
assert resource.resource_type.strip() != ""
|
|
|
|
# unique_id is non-empty string
|
|
assert isinstance(resource.unique_id, str)
|
|
assert len(resource.unique_id) > 0
|
|
assert resource.unique_id.strip() != ""
|
|
|
|
# name is non-empty string
|
|
assert isinstance(resource.name, str)
|
|
assert len(resource.name) > 0
|
|
assert resource.name.strip() != ""
|
|
|
|
# provider is valid enum
|
|
assert isinstance(resource.provider, ProviderType)
|
|
|
|
# platform_category is valid enum
|
|
assert isinstance(resource.platform_category, PlatformCategory)
|
|
|
|
# architecture is valid enum
|
|
assert isinstance(resource.architecture, CpuArchitecture)
|
|
|
|
# attributes is non-empty dict
|
|
assert isinstance(resource.attributes, dict)
|
|
assert len(resource.attributes) > 0
|