Created IAC reverse generator
This commit is contained in:
414
tests/unit/test_provider_block.py
Normal file
414
tests/unit/test_provider_block.py
Normal file
@@ -0,0 +1,414 @@
|
||||
"""Unit tests for the ProviderBlockGenerator."""
|
||||
|
||||
import pytest
|
||||
|
||||
from iac_reverse.models import ProviderType, ScanProfile
|
||||
from iac_reverse.generator import ProviderBlockGenerator
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Helpers
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def make_profile(
|
||||
provider: ProviderType,
|
||||
credentials: dict[str, str] | None = None,
|
||||
) -> ScanProfile:
|
||||
"""Create a ScanProfile with sensible defaults for testing."""
|
||||
default_creds: dict[ProviderType, dict[str, str]] = {
|
||||
ProviderType.KUBERNETES: {
|
||||
"host": "https://k8s-api.local:6443",
|
||||
"cluster_ca_certificate": "/path/to/ca.crt",
|
||||
"token": "my-token-123",
|
||||
},
|
||||
ProviderType.DOCKER_SWARM: {
|
||||
"host": "tcp://swarm-manager:2376",
|
||||
"cert_path": "/home/user/.docker/certs",
|
||||
},
|
||||
ProviderType.SYNOLOGY: {
|
||||
"url": "https://nas.local:5001",
|
||||
"username": "admin",
|
||||
"password": "secret123",
|
||||
},
|
||||
ProviderType.HARVESTER: {
|
||||
"kubeconfig": "/home/user/.kube/harvester.yaml",
|
||||
},
|
||||
ProviderType.BARE_METAL: {
|
||||
"endpoint": "https://bmc.local/redfish/v1",
|
||||
"username": "root",
|
||||
"password": "calvin",
|
||||
},
|
||||
ProviderType.WINDOWS: {
|
||||
"host": "win-server-01.local",
|
||||
"username": "Administrator",
|
||||
"password": "P@ssw0rd",
|
||||
"winrm_port": "5986",
|
||||
"winrm_use_ssl": "true",
|
||||
},
|
||||
}
|
||||
creds = credentials if credentials is not None else default_creds[provider]
|
||||
return ScanProfile(provider=provider, credentials=creds)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Tests: Single provider generates one provider block
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestSingleProvider:
|
||||
"""Tests for generating a single provider block."""
|
||||
|
||||
def test_kubernetes_generates_one_provider_block(self):
|
||||
"""A single Kubernetes provider generates one provider block."""
|
||||
profile = make_profile(ProviderType.KUBERNETES)
|
||||
generator = ProviderBlockGenerator()
|
||||
|
||||
result = generator.generate(
|
||||
profiles=[profile],
|
||||
provider_types={ProviderType.KUBERNETES},
|
||||
)
|
||||
|
||||
assert result.filename == "providers.tf"
|
||||
assert result.content.count('provider "kubernetes"') == 1
|
||||
|
||||
def test_docker_generates_one_provider_block(self):
|
||||
"""A single Docker Swarm provider generates one provider block."""
|
||||
profile = make_profile(ProviderType.DOCKER_SWARM)
|
||||
generator = ProviderBlockGenerator()
|
||||
|
||||
result = generator.generate(
|
||||
profiles=[profile],
|
||||
provider_types={ProviderType.DOCKER_SWARM},
|
||||
)
|
||||
|
||||
assert result.content.count('provider "docker"') == 1
|
||||
|
||||
def test_synology_generates_one_provider_block(self):
|
||||
"""A single Synology provider generates one provider block."""
|
||||
profile = make_profile(ProviderType.SYNOLOGY)
|
||||
generator = ProviderBlockGenerator()
|
||||
|
||||
result = generator.generate(
|
||||
profiles=[profile],
|
||||
provider_types={ProviderType.SYNOLOGY},
|
||||
)
|
||||
|
||||
assert result.content.count('provider "synology"') == 1
|
||||
|
||||
def test_harvester_generates_one_provider_block(self):
|
||||
"""A single Harvester provider generates one provider block."""
|
||||
profile = make_profile(ProviderType.HARVESTER)
|
||||
generator = ProviderBlockGenerator()
|
||||
|
||||
result = generator.generate(
|
||||
profiles=[profile],
|
||||
provider_types={ProviderType.HARVESTER},
|
||||
)
|
||||
|
||||
assert result.content.count('provider "harvester"') == 1
|
||||
|
||||
def test_bare_metal_generates_one_provider_block(self):
|
||||
"""A single Bare Metal provider generates one provider block."""
|
||||
profile = make_profile(ProviderType.BARE_METAL)
|
||||
generator = ProviderBlockGenerator()
|
||||
|
||||
result = generator.generate(
|
||||
profiles=[profile],
|
||||
provider_types={ProviderType.BARE_METAL},
|
||||
)
|
||||
|
||||
assert result.content.count('provider "redfish"') == 1
|
||||
|
||||
def test_windows_generates_one_provider_block(self):
|
||||
"""A single Windows provider generates one provider block."""
|
||||
profile = make_profile(ProviderType.WINDOWS)
|
||||
generator = ProviderBlockGenerator()
|
||||
|
||||
result = generator.generate(
|
||||
profiles=[profile],
|
||||
provider_types={ProviderType.WINDOWS},
|
||||
)
|
||||
|
||||
assert result.content.count('provider "windows"') == 1
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Tests: Multiple providers generate multiple blocks
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestMultipleProviders:
|
||||
"""Tests for generating multiple provider blocks."""
|
||||
|
||||
def test_two_providers_generate_two_blocks(self):
|
||||
"""Two distinct providers generate two provider blocks."""
|
||||
profiles = [
|
||||
make_profile(ProviderType.KUBERNETES),
|
||||
make_profile(ProviderType.DOCKER_SWARM),
|
||||
]
|
||||
generator = ProviderBlockGenerator()
|
||||
|
||||
result = generator.generate(
|
||||
profiles=profiles,
|
||||
provider_types={ProviderType.KUBERNETES, ProviderType.DOCKER_SWARM},
|
||||
)
|
||||
|
||||
assert 'provider "kubernetes"' in result.content
|
||||
assert 'provider "docker"' in result.content
|
||||
|
||||
def test_three_providers_generate_three_blocks(self):
|
||||
"""Three distinct providers generate three provider blocks."""
|
||||
profiles = [
|
||||
make_profile(ProviderType.KUBERNETES),
|
||||
make_profile(ProviderType.SYNOLOGY),
|
||||
make_profile(ProviderType.WINDOWS),
|
||||
]
|
||||
generator = ProviderBlockGenerator()
|
||||
|
||||
result = generator.generate(
|
||||
profiles=profiles,
|
||||
provider_types={
|
||||
ProviderType.KUBERNETES,
|
||||
ProviderType.SYNOLOGY,
|
||||
ProviderType.WINDOWS,
|
||||
},
|
||||
)
|
||||
|
||||
assert 'provider "kubernetes"' in result.content
|
||||
assert 'provider "synology"' in result.content
|
||||
assert 'provider "windows"' in result.content
|
||||
|
||||
def test_all_six_providers(self):
|
||||
"""All six provider types generate six provider blocks."""
|
||||
all_types = set(ProviderType)
|
||||
profiles = [make_profile(pt) for pt in all_types]
|
||||
generator = ProviderBlockGenerator()
|
||||
|
||||
result = generator.generate(
|
||||
profiles=profiles,
|
||||
provider_types=all_types,
|
||||
)
|
||||
|
||||
assert 'provider "kubernetes"' in result.content
|
||||
assert 'provider "docker"' in result.content
|
||||
assert 'provider "synology"' in result.content
|
||||
assert 'provider "harvester"' in result.content
|
||||
assert 'provider "redfish"' in result.content
|
||||
assert 'provider "windows"' in result.content
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Tests: Provider-specific configuration is included
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestProviderSpecificConfig:
|
||||
"""Tests for provider-specific configuration attributes."""
|
||||
|
||||
def test_kubernetes_includes_host_ca_token(self):
|
||||
"""Kubernetes provider block includes host, cluster_ca_certificate, token."""
|
||||
profile = make_profile(ProviderType.KUBERNETES)
|
||||
generator = ProviderBlockGenerator()
|
||||
|
||||
result = generator.generate(
|
||||
profiles=[profile],
|
||||
provider_types={ProviderType.KUBERNETES},
|
||||
)
|
||||
|
||||
assert "https://k8s-api.local:6443" in result.content
|
||||
assert "/path/to/ca.crt" in result.content
|
||||
assert "my-token-123" in result.content
|
||||
assert "host" in result.content
|
||||
assert "cluster_ca_certificate" in result.content
|
||||
assert "token" in result.content
|
||||
|
||||
def test_docker_includes_host_cert_path(self):
|
||||
"""Docker provider block includes host and cert_path."""
|
||||
profile = make_profile(ProviderType.DOCKER_SWARM)
|
||||
generator = ProviderBlockGenerator()
|
||||
|
||||
result = generator.generate(
|
||||
profiles=[profile],
|
||||
provider_types={ProviderType.DOCKER_SWARM},
|
||||
)
|
||||
|
||||
assert "tcp://swarm-manager:2376" in result.content
|
||||
assert "/home/user/.docker/certs" in result.content
|
||||
assert "host" in result.content
|
||||
assert "cert_path" in result.content
|
||||
|
||||
def test_synology_includes_url_username_password(self):
|
||||
"""Synology provider block includes url, username, password."""
|
||||
profile = make_profile(ProviderType.SYNOLOGY)
|
||||
generator = ProviderBlockGenerator()
|
||||
|
||||
result = generator.generate(
|
||||
profiles=[profile],
|
||||
provider_types={ProviderType.SYNOLOGY},
|
||||
)
|
||||
|
||||
assert "https://nas.local:5001" in result.content
|
||||
assert "admin" in result.content
|
||||
assert "secret123" in result.content
|
||||
assert "url" in result.content
|
||||
assert "username" in result.content
|
||||
assert "password" in result.content
|
||||
|
||||
def test_harvester_includes_kubeconfig(self):
|
||||
"""Harvester provider block includes kubeconfig."""
|
||||
profile = make_profile(ProviderType.HARVESTER)
|
||||
generator = ProviderBlockGenerator()
|
||||
|
||||
result = generator.generate(
|
||||
profiles=[profile],
|
||||
provider_types={ProviderType.HARVESTER},
|
||||
)
|
||||
|
||||
assert "/home/user/.kube/harvester.yaml" in result.content
|
||||
assert "kubeconfig" in result.content
|
||||
|
||||
def test_bare_metal_includes_endpoint_username_password(self):
|
||||
"""Bare Metal (redfish) provider block includes endpoint, username, password."""
|
||||
profile = make_profile(ProviderType.BARE_METAL)
|
||||
generator = ProviderBlockGenerator()
|
||||
|
||||
result = generator.generate(
|
||||
profiles=[profile],
|
||||
provider_types={ProviderType.BARE_METAL},
|
||||
)
|
||||
|
||||
assert "https://bmc.local/redfish/v1" in result.content
|
||||
assert "root" in result.content
|
||||
assert "calvin" in result.content
|
||||
assert "endpoint" in result.content
|
||||
assert "username" in result.content
|
||||
assert "password" in result.content
|
||||
|
||||
def test_windows_includes_host_username_password_winrm(self):
|
||||
"""Windows provider block includes host, username, password, winrm settings."""
|
||||
profile = make_profile(ProviderType.WINDOWS)
|
||||
generator = ProviderBlockGenerator()
|
||||
|
||||
result = generator.generate(
|
||||
profiles=[profile],
|
||||
provider_types={ProviderType.WINDOWS},
|
||||
)
|
||||
|
||||
assert "win-server-01.local" in result.content
|
||||
assert "Administrator" in result.content
|
||||
assert "P@ssw0rd" in result.content
|
||||
assert "winrm" in result.content
|
||||
assert "5986" in result.content
|
||||
assert "use_ssl" in result.content
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Tests: required_providers block is generated
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestRequiredProvidersBlock:
|
||||
"""Tests for the terraform { required_providers { ... } } block."""
|
||||
|
||||
def test_required_providers_block_present(self):
|
||||
"""The output contains a terraform { required_providers { ... } } block."""
|
||||
profile = make_profile(ProviderType.KUBERNETES)
|
||||
generator = ProviderBlockGenerator()
|
||||
|
||||
result = generator.generate(
|
||||
profiles=[profile],
|
||||
provider_types={ProviderType.KUBERNETES},
|
||||
)
|
||||
|
||||
assert "terraform {" in result.content
|
||||
assert "required_providers {" in result.content
|
||||
|
||||
def test_required_providers_includes_source(self):
|
||||
"""The required_providers block includes source for each provider."""
|
||||
profile = make_profile(ProviderType.KUBERNETES)
|
||||
generator = ProviderBlockGenerator()
|
||||
|
||||
result = generator.generate(
|
||||
profiles=[profile],
|
||||
provider_types={ProviderType.KUBERNETES},
|
||||
)
|
||||
|
||||
assert 'source = "hashicorp/kubernetes"' in result.content
|
||||
|
||||
def test_required_providers_includes_version(self):
|
||||
"""The required_providers block includes version constraint."""
|
||||
profile = make_profile(ProviderType.KUBERNETES)
|
||||
generator = ProviderBlockGenerator()
|
||||
|
||||
result = generator.generate(
|
||||
profiles=[profile],
|
||||
provider_types={ProviderType.KUBERNETES},
|
||||
)
|
||||
|
||||
assert 'version = "~> 2.0"' in result.content
|
||||
|
||||
def test_multiple_providers_all_in_required_providers(self):
|
||||
"""Multiple providers are all listed in required_providers."""
|
||||
profiles = [
|
||||
make_profile(ProviderType.KUBERNETES),
|
||||
make_profile(ProviderType.DOCKER_SWARM),
|
||||
make_profile(ProviderType.BARE_METAL),
|
||||
]
|
||||
generator = ProviderBlockGenerator()
|
||||
|
||||
result = generator.generate(
|
||||
profiles=profiles,
|
||||
provider_types={
|
||||
ProviderType.KUBERNETES,
|
||||
ProviderType.DOCKER_SWARM,
|
||||
ProviderType.BARE_METAL,
|
||||
},
|
||||
)
|
||||
|
||||
assert 'source = "hashicorp/kubernetes"' in result.content
|
||||
assert 'source = "kreuzwerker/docker"' in result.content
|
||||
assert 'source = "dell/redfish"' in result.content
|
||||
|
||||
def test_docker_swarm_maps_to_kreuzwerker_docker(self):
|
||||
"""Docker Swarm maps to kreuzwerker/docker provider source."""
|
||||
profile = make_profile(ProviderType.DOCKER_SWARM)
|
||||
generator = ProviderBlockGenerator()
|
||||
|
||||
result = generator.generate(
|
||||
profiles=[profile],
|
||||
provider_types={ProviderType.DOCKER_SWARM},
|
||||
)
|
||||
|
||||
assert "docker = {" in result.content
|
||||
assert 'source = "kreuzwerker/docker"' in result.content
|
||||
|
||||
def test_harvester_maps_to_harvester_source(self):
|
||||
"""Harvester maps to harvester/harvester provider source."""
|
||||
profile = make_profile(ProviderType.HARVESTER)
|
||||
generator = ProviderBlockGenerator()
|
||||
|
||||
result = generator.generate(
|
||||
profiles=[profile],
|
||||
provider_types={ProviderType.HARVESTER},
|
||||
)
|
||||
|
||||
assert "harvester = {" in result.content
|
||||
assert 'source = "harvester/harvester"' in result.content
|
||||
|
||||
def test_provider_type_without_matching_profile_gets_placeholder(self):
|
||||
"""A provider type with no matching profile gets a placeholder block."""
|
||||
# Profile is for Kubernetes, but we request Synology too
|
||||
profile = make_profile(ProviderType.KUBERNETES)
|
||||
generator = ProviderBlockGenerator()
|
||||
|
||||
result = generator.generate(
|
||||
profiles=[profile],
|
||||
provider_types={ProviderType.KUBERNETES, ProviderType.SYNOLOGY},
|
||||
)
|
||||
|
||||
# Synology should still appear in required_providers
|
||||
assert "synology = {" in result.content
|
||||
assert 'source = "synology-community/synology"' in result.content
|
||||
# And should have a placeholder provider block
|
||||
assert '# No profile provided' in result.content
|
||||
Reference in New Issue
Block a user