Files
SnarfCode/tests/unit/test_models.py
2026-05-22 00:19:30 -04:00

404 lines
13 KiB
Python

"""Unit tests for core data models."""
import json
import pytest
from iac_reverse.models import (
ChangeType,
ChangeSummary,
CodeGenerationResult,
CpuArchitecture,
DependencyGraph,
DiscoveredResource,
ExtractedVariable,
GeneratedFile,
PlannedChange,
PlatformCategory,
PROVIDER_PLATFORM_MAP,
ProviderType,
ResourceChange,
ResourceRelationship,
ScanProfile,
ScanProgress,
ScanResult,
StateEntry,
StateFile,
UnresolvedReference,
ValidationError,
ValidationResult,
)
class TestProviderType:
def test_all_values(self):
assert ProviderType.DOCKER_SWARM.value == "docker_swarm"
assert ProviderType.KUBERNETES.value == "kubernetes"
assert ProviderType.SYNOLOGY.value == "synology"
assert ProviderType.HARVESTER.value == "harvester"
assert ProviderType.BARE_METAL.value == "bare_metal"
assert ProviderType.WINDOWS.value == "windows"
def test_member_count(self):
assert len(ProviderType) == 6
class TestPlatformCategory:
def test_all_values(self):
assert PlatformCategory.CONTAINER_ORCHESTRATION.value == "container"
assert PlatformCategory.STORAGE_APPLIANCE.value == "storage"
assert PlatformCategory.HCI.value == "hci"
assert PlatformCategory.BARE_METAL.value == "bare_metal"
assert PlatformCategory.WINDOWS.value == "windows"
def test_member_count(self):
assert len(PlatformCategory) == 5
class TestProviderPlatformMap:
def test_all_providers_mapped(self):
for provider in ProviderType:
assert provider in PROVIDER_PLATFORM_MAP
def test_container_orchestration_providers(self):
assert PROVIDER_PLATFORM_MAP[ProviderType.DOCKER_SWARM] == PlatformCategory.CONTAINER_ORCHESTRATION
assert PROVIDER_PLATFORM_MAP[ProviderType.KUBERNETES] == PlatformCategory.CONTAINER_ORCHESTRATION
def test_other_providers(self):
assert PROVIDER_PLATFORM_MAP[ProviderType.SYNOLOGY] == PlatformCategory.STORAGE_APPLIANCE
assert PROVIDER_PLATFORM_MAP[ProviderType.HARVESTER] == PlatformCategory.HCI
assert PROVIDER_PLATFORM_MAP[ProviderType.BARE_METAL] == PlatformCategory.BARE_METAL
assert PROVIDER_PLATFORM_MAP[ProviderType.WINDOWS] == PlatformCategory.WINDOWS
class TestCpuArchitecture:
def test_all_values(self):
assert CpuArchitecture.AMD64.value == "amd64"
assert CpuArchitecture.ARM.value == "arm"
assert CpuArchitecture.AARCH64.value == "aarch64"
def test_member_count(self):
assert len(CpuArchitecture) == 3
class TestChangeType:
def test_all_values(self):
assert ChangeType.ADDED.value == "added"
assert ChangeType.REMOVED.value == "removed"
assert ChangeType.MODIFIED.value == "modified"
class TestScanProfile:
def test_valid_profile(self):
profile = ScanProfile(
provider=ProviderType.KUBERNETES,
credentials={"kubeconfig_path": "/home/user/.kube/config"},
)
assert profile.validate() == []
def test_empty_credentials_invalid(self):
profile = ScanProfile(
provider=ProviderType.KUBERNETES,
credentials={},
)
errors = profile.validate()
assert len(errors) > 0
assert "credentials" in errors[0]
def test_platform_category_property(self):
profile = ScanProfile(
provider=ProviderType.DOCKER_SWARM,
credentials={"host": "localhost"},
)
assert profile.platform_category == PlatformCategory.CONTAINER_ORCHESTRATION
def test_optional_fields_default_none(self):
profile = ScanProfile(
provider=ProviderType.SYNOLOGY,
credentials={"host": "nas01"},
)
assert profile.endpoints is None
assert profile.resource_type_filters is None
assert profile.authentik_token is None
class TestDiscoveredResource:
def test_creation(self):
resource = DiscoveredResource(
resource_type="kubernetes_deployment",
unique_id="apps/v1/deployments/default/nginx",
name="nginx",
provider=ProviderType.KUBERNETES,
platform_category=PlatformCategory.CONTAINER_ORCHESTRATION,
architecture=CpuArchitecture.AARCH64,
endpoint="https://k8s-api:6443",
attributes={"replicas": 3, "image": "nginx:1.25"},
raw_references=["default/services/nginx-svc"],
)
assert resource.resource_type == "kubernetes_deployment"
assert resource.unique_id == "apps/v1/deployments/default/nginx"
assert resource.provider == ProviderType.KUBERNETES
def test_raw_references_default_empty(self):
resource = DiscoveredResource(
resource_type="windows_service",
unique_id="win01/services/nginx",
name="nginx",
provider=ProviderType.WINDOWS,
platform_category=PlatformCategory.WINDOWS,
architecture=CpuArchitecture.AMD64,
endpoint="win01.internal.lab",
attributes={"state": "running"},
)
assert resource.raw_references == []
class TestScanResult:
def test_creation(self):
result = ScanResult(
resources=[],
warnings=["unsupported type: foo"],
errors=[],
scan_timestamp="2024-01-15T10:30:00Z",
profile_hash="abc123",
)
assert result.is_partial is False
assert len(result.warnings) == 1
def test_partial_scan(self):
result = ScanResult(
resources=[],
warnings=[],
errors=["connection lost"],
scan_timestamp="2024-01-15T10:30:00Z",
profile_hash="abc123",
is_partial=True,
)
assert result.is_partial is True
class TestScanProgress:
def test_creation(self):
progress = ScanProgress(
current_resource_type="kubernetes_deployment",
resources_discovered=15,
resource_types_completed=2,
total_resource_types=5,
)
assert progress.resources_discovered == 15
assert progress.resource_types_completed == 2
class TestResourceRelationship:
def test_creation(self):
rel = ResourceRelationship(
source_id="resource-a",
target_id="resource-b",
relationship_type="dependency",
source_attribute="network_id",
)
assert rel.relationship_type == "dependency"
class TestUnresolvedReference:
def test_creation(self):
ref = UnresolvedReference(
source_resource_id="resource-a",
source_attribute="vpc_id",
referenced_id="vpc-unknown",
suggested_resolution="data_source",
)
assert ref.suggested_resolution == "data_source"
class TestDependencyGraph:
def test_creation(self):
graph = DependencyGraph(
resources=[],
relationships=[],
topological_order=["a", "b", "c"],
cycles=[],
unresolved_references=[],
)
assert graph.topological_order == ["a", "b", "c"]
assert graph.cycles == []
class TestGeneratedFile:
def test_creation(self):
gf = GeneratedFile(
filename="kubernetes_deployment.tf",
content='resource "kubernetes_deployment" "nginx" {}',
resource_count=1,
)
assert gf.filename == "kubernetes_deployment.tf"
assert gf.resource_count == 1
class TestExtractedVariable:
def test_creation(self):
var = ExtractedVariable(
name="environment",
type_expr="string",
default_value="production",
description="Deployment environment",
used_by=["resource-a", "resource-b"],
)
assert len(var.used_by) == 2
def test_used_by_default_empty(self):
var = ExtractedVariable(
name="region",
type_expr="string",
default_value="us-east-1",
description="Region",
)
assert var.used_by == []
class TestCodeGenerationResult:
def test_creation(self):
result = CodeGenerationResult(
resource_files=[GeneratedFile("main.tf", "content", 5)],
variables_file=GeneratedFile("variables.tf", "vars", 0),
provider_file=GeneratedFile("provider.tf", "provider", 0),
outputs_file=None,
skipped_resources=[("res-1", "unsupported type")],
)
assert len(result.resource_files) == 1
assert result.outputs_file is None
assert len(result.skipped_resources) == 1
class TestStateEntry:
def test_creation(self):
entry = StateEntry(
resource_type="kubernetes_deployment",
resource_name="nginx",
provider_id="apps/v1/deployments/default/nginx",
attributes={"namespace": "default"},
sensitive_attributes=["password"],
schema_version=1,
dependencies=["kubernetes_service.nginx_svc"],
)
assert entry.schema_version == 1
assert len(entry.dependencies) == 1
def test_defaults(self):
entry = StateEntry(
resource_type="kubernetes_service",
resource_name="svc",
provider_id="id-123",
attributes={},
)
assert entry.sensitive_attributes == []
assert entry.schema_version == 0
assert entry.dependencies == []
class TestStateFile:
def test_defaults(self):
state = StateFile()
assert state.version == 4
assert state.terraform_version == ""
assert state.serial == 1
assert state.lineage == ""
assert state.resources == []
def test_to_json_structure(self):
state = StateFile(
terraform_version="1.7.0",
resources=[
StateEntry(
resource_type="kubernetes_deployment",
resource_name="nginx",
provider_id="apps/v1/deployments/default/nginx",
attributes={"namespace": "default", "replicas": 3},
schema_version=1,
dependencies=["kubernetes_service.nginx_svc"],
)
],
)
parsed = json.loads(state.to_json())
assert parsed["version"] == 4
assert parsed["terraform_version"] == "1.7.0"
assert parsed["serial"] == 1
assert "lineage" in parsed
assert len(parsed["resources"]) == 1
res = parsed["resources"][0]
assert res["mode"] == "managed"
assert res["type"] == "kubernetes_deployment"
assert res["name"] == "nginx"
assert res["instances"][0]["schema_version"] == 1
assert res["instances"][0]["attributes"]["id"] == "apps/v1/deployments/default/nginx"
def test_to_json_generates_lineage(self):
state = StateFile()
parsed = json.loads(state.to_json())
assert len(parsed["lineage"]) > 0 # UUID generated
class TestValidationResult:
def test_creation(self):
result = ValidationResult(
init_success=True,
validate_success=True,
plan_success=False,
planned_changes=[
PlannedChange(
resource_address="kubernetes_deployment.nginx",
change_type="modify",
details="replicas changed",
)
],
errors=[],
correction_attempts=1,
)
assert result.plan_success is False
assert len(result.planned_changes) == 1
class TestValidationError:
def test_creation_with_line(self):
err = ValidationError(file="main.tf", message="invalid block", line=42)
assert err.line == 42
def test_creation_without_line(self):
err = ValidationError(file="main.tf", message="missing provider")
assert err.line is None
class TestResourceChange:
def test_added(self):
change = ResourceChange(
resource_id="new-resource",
resource_type="kubernetes_service",
resource_name="new-svc",
change_type=ChangeType.ADDED,
)
assert change.changed_attributes is None
def test_modified(self):
change = ResourceChange(
resource_id="existing-resource",
resource_type="kubernetes_deployment",
resource_name="nginx",
change_type=ChangeType.MODIFIED,
changed_attributes={"replicas": {"old": 3, "new": 5}},
)
assert change.changed_attributes is not None
class TestChangeSummary:
def test_creation(self):
summary = ChangeSummary(
added_count=2,
removed_count=1,
modified_count=3,
changes=[],
)
assert summary.added_count == 2
assert summary.removed_count == 1
assert summary.modified_count == 3