We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/kpeacocke/souschef'
If you have feedback or need assistance with the MCP directory API, please join our Discord server
"""Sprint coverage tests targeting uncovered blocks (1700+ lines)."""
import tempfile
from pathlib import Path
import pytest
from souschef.assessment import (
_calculate_complexity_score,
_determine_migration_priority,
_identify_migration_challenges,
)
from souschef.converters.playbook import (
_build_project_context_parts,
_find_recipe_position_in_migration_order,
)
from souschef.server import (
analyse_chef_databag_usage,
generate_inventory_from_chef_environments,
)
# PLAYBOOK TESTS
def test_build_project_context_with_full_data() -> None:
"""Project context should include all recommendation fields."""
recommendations = {
"project_complexity": "Medium",
"migration_strategy": "phased",
"project_effort_days": 20.5,
"dependency_density": 2.1,
"recommendations": ["Rec1", "Rec2"],
"migration_order": [
{
"cookbook": "base",
"phase": "1",
"complexity": "low",
"dependencies": [],
"reason": "foundation",
}
],
}
parts = _build_project_context_parts(recommendations, "recipes/base.rb")
combined = " ".join(parts)
assert "project_complexity" in combined.lower() or "Medium" in combined
assert "phased" in combined.lower()
def test_find_recipe_not_in_order() -> None:
"""Recipe lookup should handle missing recipe."""
order = [{"cookbook": "other", "phase": "1"}]
result = _find_recipe_position_in_migration_order(order, "missing.rb")
assert result is None
# ASSESSMENT TESTS
def test_identify_challenges_no_risks() -> None:
"""No challenges for simple metrics."""
metrics = {"custom_resources": 0, "ruby_blocks": 1}
challenges = _identify_migration_challenges(metrics, 10)
assert isinstance(challenges, list)
def test_identify_challenges_multiple_risks() -> None:
"""Should flag all risk categories."""
metrics = {"custom_resources": 3, "ruby_blocks": 10}
challenges = _identify_migration_challenges(metrics, 80)
assert len(challenges) >= 2
def test_calculate_complexity_zero_recipes() -> None:
"""Complexity score handles zero recipes."""
metrics = {
"recipe_count": 0,
"resource_count": 0,
"custom_resources": 0,
"ruby_blocks": 0,
"template_count": 0,
"file_count": 0,
}
score = _calculate_complexity_score(metrics)
assert score == 0
def test_calculate_complexity_high_density() -> None:
"""Complexity score reflects resource density."""
metrics = {
"recipe_count": 2,
"resource_count": 100,
"custom_resources": 5,
"ruby_blocks": 10,
"template_count": 8,
"file_count": 20,
}
score = _calculate_complexity_score(metrics)
assert score > 50
def test_determine_priority_edges() -> None:
"""Priority determination handles boundaries."""
assert _determine_migration_priority(29) == "low"
assert _determine_migration_priority(30) == "medium"
assert _determine_migration_priority(70) == "medium"
assert _determine_migration_priority(71) == "high"
# SERVER TESTS
def test_analyse_databag_usage_with_recipes(tmp_path: Path) -> None:
"""Data bag analysis with recipe patterns."""
cookbook = tmp_path / "test"
cookbook.mkdir()
(cookbook / "metadata.rb").write_text("name 'test'")
recipes = cookbook / "recipes"
recipes.mkdir()
(recipes / "default.rb").write_text(
"data = Chef::EncryptedDataBagItem.load('bag', 'item')\n"
)
result = analyse_chef_databag_usage(str(cookbook))
assert isinstance(result, str)
def test_generate_inventory_multiple_environments(tmp_path: Path) -> None:
"""Inventory generation with multiple environment files."""
env_dir = tmp_path / "environments"
env_dir.mkdir()
(env_dir / "prod.rb").write_text(
"name 'production'\ndefault_attributes 'version' => '1.0.0'\n"
)
(env_dir / "stage.rb").write_text("name 'staging'\n")
(env_dir / "dev.rb").write_text("name 'development'\n")
result = generate_inventory_from_chef_environments(str(env_dir))
assert isinstance(result, str)
def test_generate_inventory_mixed_formats(tmp_path: Path) -> None:
"""Inventory generation with both YAML and INI requested."""
env_dir = tmp_path / "envs"
env_dir.mkdir()
(env_dir / "test.rb").write_text("name 'test'\n")
yaml_result = generate_inventory_from_chef_environments(str(env_dir), "yaml")
ini_result = generate_inventory_from_chef_environments(str(env_dir), "ini")
both_result = generate_inventory_from_chef_environments(str(env_dir), "both")
assert isinstance(yaml_result, str)
assert isinstance(ini_result, str)
assert isinstance(both_result, str)
# ERROR HANDLING TESTS
def test_playbook_complex_recipe_failure(tmp_path: Path) -> None:
"""Playbook generation should handle malformed recipes."""
with tempfile.TemporaryDirectory() as tmpdir:
recipe = Path(tmpdir) / "bad.rb"
recipe.write_text("package 'nginx' do\n # incomplete\n")
from souschef.converters.playbook import generate_playbook_from_recipe
result = generate_playbook_from_recipe(str(recipe))
assert isinstance(result, str)
def test_complexity_with_missing_metrics() -> None:
"""Complexity calculation with missing metric keys."""
partial_metrics = {
"recipe_count": 1,
"resource_count": 5,
# Missing: custom_resources, ruby_blocks, template_count, file_count
}
# Should not crash even with missing keys
try:
score = _calculate_complexity_score(partial_metrics)
assert isinstance(score, int)
except KeyError:
pytest.skip("Missing metrics not handled")
def test_assessment_priority_all_ranges() -> None:
"""Priority covers full range 0-100."""
for i in [0, 15, 29, 30, 49, 50, 69, 70, 99, 100]:
result = _determine_migration_priority(i)
assert result in ["low", "medium", "high"]