"""Expanded tests for souschef.server module - covering uncovered functions."""
import json
from typing import Any
from souschef.server import (
_convert_attributes,
_convert_recipes,
_convert_templates,
convert_all_cookbooks_comprehensive,
parse_chef_migration_assessment,
)
# Tests for parse_chef_migration_assessment
class TestParseChefMigrationAssessment:
"""Tests for parse_chef_migration_assessment function."""
def test_parse_single_cookbook_full_scope(self, tmp_path):
"""Test assessment of single cookbook with full scope."""
cookbook_dir = tmp_path / "test_cookbook"
cookbook_dir.mkdir()
# Create minimal cookbook structure
metadata_file = cookbook_dir / "metadata.rb"
metadata_file.write_text('name "test_cookbook"\nversion "1.0.0"')
recipes_dir = cookbook_dir / "recipes"
recipes_dir.mkdir()
(recipes_dir / "default.rb").write_text(
"package 'curl' do\n action :install\nend"
)
result = parse_chef_migration_assessment(
str(cookbook_dir), migration_scope="full", target_platform="ansible_awx"
)
assert isinstance(result, dict)
assert "cookbook_assessments" in result or "complexity" in result
assert result is not None
def test_parse_multiple_cookbooks_comma_separated(self, tmp_path):
"""Test assessment with multiple comma-separated cookbook paths."""
cookbook1 = tmp_path / "cookbook1"
cookbook2 = tmp_path / "cookbook2"
cookbook1.mkdir()
cookbook2.mkdir()
# Create minimal metadata for both
for cb_dir in [cookbook1, cookbook2]:
metadata_file = cb_dir / "metadata.rb"
metadata_file.write_text(f'name "{cb_dir.name}"\nversion "1.0.0"')
recipes_dir = cb_dir / "recipes"
recipes_dir.mkdir()
(recipes_dir / "default.rb").write_text("service 'test'")
result = parse_chef_migration_assessment(
f"{cookbook1},{cookbook2}", migration_scope="recipes_only"
)
assert isinstance(result, dict)
assert result is not None
def test_parse_assessment_with_infrastructure_scope(self, tmp_path):
"""Test assessment with infrastructure_only scope."""
cookbook_dir = tmp_path / "infra_cookbook"
cookbook_dir.mkdir()
metadata_file = cookbook_dir / "metadata.rb"
metadata_file.write_text('name "infra_cookbook"\nversion "1.0.0"')
recipes_dir = cookbook_dir / "recipes"
recipes_dir.mkdir()
(recipes_dir / "default.rb").write_text(
"execute 'setup' do\n command 'echo test'\nend"
)
result = parse_chef_migration_assessment(
str(cookbook_dir),
migration_scope="infrastructure_only",
target_platform="ansible_core",
)
assert isinstance(result, dict)
assert result is not None
def test_parse_assessment_different_target_platforms(self, tmp_path):
"""Test assessment with different target platforms."""
cookbook_dir = tmp_path / "platform_test"
cookbook_dir.mkdir()
metadata_file = cookbook_dir / "metadata.rb"
metadata_file.write_text('name "platform_test"\nversion "1.0.0"')
recipes_dir = cookbook_dir / "recipes"
recipes_dir.mkdir()
(recipes_dir / "default.rb").write_text("package 'nginx'")
platforms = ["ansible_awx", "ansible_core", "ansible_tower"]
for platform in platforms:
result = parse_chef_migration_assessment(
str(cookbook_dir), target_platform=platform
)
assert isinstance(result, dict)
# Tests for _convert_recipes
class TestConvertRecipes:
"""Tests for _convert_recipes function."""
def test_convert_recipes_success(self, tmp_path):
"""Test successful conversion of recipes to Ansible tasks."""
cookbook_dir = tmp_path / "cookbook_with_recipes"
cookbook_dir.mkdir()
# Create recipes directory with test recipe
recipes_dir = cookbook_dir / "recipes"
recipes_dir.mkdir()
(recipes_dir / "default.rb").write_text(
"package 'curl' do\n action :install\nend"
)
# Create role directory
role_dir = tmp_path / "test_role"
role_dir.mkdir()
tasks_dir = role_dir / "tasks"
tasks_dir.mkdir()
conversion_summary: dict[str, Any] = {
"converted_files": [],
"errors": [],
"warnings": [],
}
_convert_recipes(cookbook_dir, role_dir, conversion_summary)
# Check that recipe was processed
assert (
len(conversion_summary["converted_files"]) > 0
or len(conversion_summary["errors"]) == 0
)
# At least we should have attempted conversion
assert isinstance(conversion_summary, dict)
def test_convert_recipes_no_recipes_dir(self, tmp_path):
"""Test conversion when recipes directory doesn't exist."""
cookbook_dir = tmp_path / "cookbook_no_recipes"
cookbook_dir.mkdir()
role_dir = tmp_path / "role_no_recipes"
role_dir.mkdir()
conversion_summary: dict[str, Any] = {
"converted_files": [],
"errors": [],
"warnings": [],
}
_convert_recipes(cookbook_dir, role_dir, conversion_summary)
# Should add warning when recipes dir doesn't exist
assert len(conversion_summary["warnings"]) > 0
def test_convert_recipes_no_recipe_files(self, tmp_path):
"""Test conversion when recipes directory exists but is empty."""
cookbook_dir = tmp_path / "cookbook_empty_recipes"
cookbook_dir.mkdir()
recipes_dir = cookbook_dir / "recipes"
recipes_dir.mkdir()
role_dir = tmp_path / "role_empty_recipes"
role_dir.mkdir()
conversion_summary: dict[str, Any] = {
"converted_files": [],
"errors": [],
"warnings": [],
}
_convert_recipes(cookbook_dir, role_dir, conversion_summary)
# Should add warning when no recipe files found
assert len(conversion_summary["warnings"]) > 0
def test_convert_recipes_with_error_handling(self, tmp_path):
"""Test error handling during recipe conversion."""
cookbook_dir = tmp_path / "cookbook_error_test"
cookbook_dir.mkdir()
recipes_dir = cookbook_dir / "recipes"
recipes_dir.mkdir()
# Create a recipe with invalid syntax
(recipes_dir / "broken.rb").write_text(
"package 'test' do\n invalid_syntax [\n"
)
role_dir = tmp_path / "role_error_test"
role_dir.mkdir()
tasks_dir = role_dir / "tasks"
tasks_dir.mkdir()
conversion_summary: dict[str, Any] = {
"converted_files": [],
"errors": [],
"warnings": [],
}
_convert_recipes(cookbook_dir, role_dir, conversion_summary)
# Errors should be recorded
assert isinstance(conversion_summary["errors"], list)
# Tests for _convert_templates
class TestConvertTemplates:
"""Tests for _convert_templates function."""
def test_convert_templates_success(self, tmp_path):
"""Test successful conversion of ERB templates to Jinja2."""
cookbook_dir = tmp_path / "cookbook_with_templates"
cookbook_dir.mkdir()
# Create templates directory with ERB template
templates_dir = cookbook_dir / "templates"
templates_dir.mkdir()
(templates_dir / "config.erb").write_text(
"# Generated by Chef\n<%= @variable %>"
)
# Create role directory
role_dir = tmp_path / "test_role_templates"
role_dir.mkdir()
templates_role_dir = role_dir / "templates"
templates_role_dir.mkdir()
conversion_summary: dict[str, Any] = {
"converted_files": [],
"errors": [],
"warnings": [],
}
_convert_templates(cookbook_dir, role_dir, conversion_summary)
# Template conversion should be attempted
assert isinstance(conversion_summary, dict)
assert "converted_files" in conversion_summary
assert "errors" in conversion_summary
def test_convert_templates_no_templates_dir(self, tmp_path):
"""Test conversion when templates directory doesn't exist."""
cookbook_dir = tmp_path / "cookbook_no_templates"
cookbook_dir.mkdir()
role_dir = tmp_path / "role_no_templates"
role_dir.mkdir()
conversion_summary: dict[str, Any] = {
"converted_files": [],
"errors": [],
"warnings": [],
}
_convert_templates(cookbook_dir, role_dir, conversion_summary)
# Should handle gracefully - no warnings needed for missing templates
assert isinstance(conversion_summary, dict)
def test_convert_templates_nested_structure(self, tmp_path):
"""Test conversion of templates with nested directory structure."""
cookbook_dir = tmp_path / "cookbook_nested_templates"
cookbook_dir.mkdir()
# Create nested templates directory
templates_dir = cookbook_dir / "templates"
templates_dir.mkdir()
nested_dir = templates_dir / "conf"
nested_dir.mkdir()
(nested_dir / "app.conf.erb").write_text("port: <%= @port %>")
# Create role directory
role_dir = tmp_path / "role_nested_templates"
role_dir.mkdir()
templates_role_dir = role_dir / "templates"
templates_role_dir.mkdir()
conversion_summary: dict[str, Any] = {
"converted_files": [],
"errors": [],
"warnings": [],
}
_convert_templates(cookbook_dir, role_dir, conversion_summary)
assert isinstance(conversion_summary, dict)
# Tests for _convert_attributes
class TestConvertAttributes:
"""Tests for _convert_attributes function."""
def test_convert_attributes_success(self, tmp_path):
"""Test successful conversion of Chef attributes to Ansible variables."""
cookbook_dir = tmp_path / "cookbook_with_attributes"
cookbook_dir.mkdir()
# Create attributes directory
attributes_dir = cookbook_dir / "attributes"
attributes_dir.mkdir()
(attributes_dir / "default.rb").write_text(
"default['app']['port'] = 8080\ndefault['app']['name'] = 'myapp'"
)
# Create role directory
role_dir = tmp_path / "test_role_attributes"
role_dir.mkdir()
defaults_dir = role_dir / "defaults"
defaults_dir.mkdir()
conversion_summary: dict[str, Any] = {
"converted_files": [],
"errors": [],
"warnings": [],
}
_convert_attributes(cookbook_dir, role_dir, conversion_summary)
# Attributes conversion should be attempted
assert isinstance(conversion_summary, dict)
def test_convert_attributes_no_attributes_dir(self, tmp_path):
"""Test conversion when attributes directory doesn't exist."""
cookbook_dir = tmp_path / "cookbook_no_attributes"
cookbook_dir.mkdir()
role_dir = tmp_path / "role_no_attributes"
role_dir.mkdir()
conversion_summary: dict[str, Any] = {
"converted_files": [],
"errors": [],
"warnings": [],
}
_convert_attributes(cookbook_dir, role_dir, conversion_summary)
# Should handle gracefully
assert isinstance(conversion_summary, dict)
def test_convert_attributes_multiple_files(self, tmp_path):
"""Test conversion of multiple attribute files."""
cookbook_dir = tmp_path / "cookbook_multi_attributes"
cookbook_dir.mkdir()
# Create attributes directory with multiple files
attributes_dir = cookbook_dir / "attributes"
attributes_dir.mkdir()
(attributes_dir / "default.rb").write_text("default['version'] = '1.0'")
(attributes_dir / "production.rb").write_text("default['environment'] = 'prod'")
# Create role directory
role_dir = tmp_path / "role_multi_attributes"
role_dir.mkdir()
defaults_dir = role_dir / "defaults"
defaults_dir.mkdir()
conversion_summary: dict[str, Any] = {
"converted_files": [],
"errors": [],
"warnings": [],
}
_convert_attributes(cookbook_dir, role_dir, conversion_summary)
assert isinstance(conversion_summary, dict)
# Tests for convert_all_cookbooks_comprehensive
class TestConvertAllCookbooksComprehensive:
"""Tests for convert_all_cookbooks_comprehensive function."""
def test_convert_single_cookbook_comprehensive(self, tmp_path):
"""Test comprehensive conversion of a single cookbook."""
cookbook_dir = tmp_path / "test_cookbook"
cookbook_dir.mkdir()
# Create complete cookbook structure
metadata_file = cookbook_dir / "metadata.rb"
metadata_file.write_text('name "test_cookbook"\nversion "1.0.0"')
recipes_dir = cookbook_dir / "recipes"
recipes_dir.mkdir()
(recipes_dir / "default.rb").write_text("package 'curl'")
# Create output directory
output_dir = tmp_path / "output"
output_dir.mkdir()
result = convert_all_cookbooks_comprehensive(
cookbooks_path=str(cookbook_dir),
output_path=str(output_dir),
assessment_data="",
include_templates=True,
include_attributes=True,
include_recipes=True,
)
assert isinstance(result, str)
assert "converted" in result.lower() or "error" in result.lower()
def test_convert_multiple_cookbooks_comprehensive(self, tmp_path):
"""Test comprehensive conversion of multiple cookbooks."""
# Create cookbook directory with multiple cookbooks
cookbooks_root = tmp_path / "cookbooks"
cookbooks_root.mkdir()
for i in range(2):
cookbook_dir = cookbooks_root / f"cookbook{i}"
cookbook_dir.mkdir()
metadata_file = cookbook_dir / "metadata.rb"
metadata_file.write_text(f'name "cookbook{i}"\nversion "1.0.0"')
recipes_dir = cookbook_dir / "recipes"
recipes_dir.mkdir()
(recipes_dir / "default.rb").write_text(f"package 'pkg{i}'")
# Create output directory
output_dir = tmp_path / "output_multi"
output_dir.mkdir()
result = convert_all_cookbooks_comprehensive(
cookbooks_path=str(cookbooks_root),
output_path=str(output_dir),
)
assert isinstance(result, str)
def test_convert_with_selective_components(self, tmp_path):
"""Test conversion with selective component inclusion."""
cookbook_dir = tmp_path / "selective_cookbook"
cookbook_dir.mkdir()
# Create complete structure
metadata_file = cookbook_dir / "metadata.rb"
metadata_file.write_text('name "selective"\nversion "1.0.0"')
recipes_dir = cookbook_dir / "recipes"
recipes_dir.mkdir()
(recipes_dir / "default.rb").write_text("service 'nginx'")
templates_dir = cookbook_dir / "templates"
templates_dir.mkdir()
(templates_dir / "nginx.conf.erb").write_text("port: <%= @port %>")
attributes_dir = cookbook_dir / "attributes"
attributes_dir.mkdir()
(attributes_dir / "default.rb").write_text("default['port'] = 80")
output_dir = tmp_path / "output_selective"
output_dir.mkdir()
# Test with only recipes
result = convert_all_cookbooks_comprehensive(
cookbooks_path=str(cookbook_dir),
output_path=str(output_dir),
include_templates=False,
include_attributes=False,
include_recipes=True,
)
assert isinstance(result, str)
def test_convert_excludes_templates(self, tmp_path):
"""Test conversion with templates excluded."""
cookbook_dir = tmp_path / "named_cookbook"
cookbook_dir.mkdir()
metadata_file = cookbook_dir / "metadata.rb"
metadata_file.write_text('name "original_name"\nversion "1.0.0"')
recipes_dir = cookbook_dir / "recipes"
recipes_dir.mkdir()
(recipes_dir / "default.rb").write_text("package 'test'")
templates_dir = cookbook_dir / "templates"
templates_dir.mkdir()
(templates_dir / "config.erb").write_text("# test")
output_dir = tmp_path / "output_named"
output_dir.mkdir()
result = convert_all_cookbooks_comprehensive(
cookbooks_path=str(cookbook_dir),
output_path=str(output_dir),
include_templates=False,
)
assert isinstance(result, str)
def test_convert_with_assessment_data(self, tmp_path):
"""Test conversion with assessment data context."""
cookbook_dir = tmp_path / "assessed_cookbook"
cookbook_dir.mkdir()
metadata_file = cookbook_dir / "metadata.rb"
metadata_file.write_text('name "assessed"\nversion "1.0.0"')
recipes_dir = cookbook_dir / "recipes"
recipes_dir.mkdir()
(recipes_dir / "default.rb").write_text("execute 'deploy'")
output_dir = tmp_path / "output_assessed"
output_dir.mkdir()
assessment_data = json.dumps(
{
"complexity": "High",
"recommendations": ["Use handlers", "Consider docker"],
}
)
result = convert_all_cookbooks_comprehensive(
cookbooks_path=str(cookbook_dir),
output_path=str(output_dir),
assessment_data=assessment_data,
)
assert isinstance(result, str)
def test_convert_invalid_cookbook_path(self, tmp_path):
"""Test conversion with invalid cookbook path."""
invalid_path = tmp_path / "nonexistent"
output_dir = tmp_path / "output_invalid"
output_dir.mkdir()
result = convert_all_cookbooks_comprehensive(
cookbooks_path=str(invalid_path),
output_path=str(output_dir),
)
# Should return error message
assert isinstance(result, str)
def test_convert_without_metadata(self, tmp_path):
"""Test conversion of cookbook without metadata.rb."""
cookbook_dir = tmp_path / "no_metadata_cookbook"
cookbook_dir.mkdir()
recipes_dir = cookbook_dir / "recipes"
recipes_dir.mkdir()
(recipes_dir / "default.rb").write_text("package 'curl'")
output_dir = tmp_path / "output_no_metadata"
output_dir.mkdir()
result = convert_all_cookbooks_comprehensive(
cookbooks_path=str(cookbook_dir),
output_path=str(output_dir),
)
# Should attempt conversion even without metadata
assert isinstance(result, str)