"""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