CloudFinOpsEnv / tests /test_generator.py
jacklachan's picture
Upload folder using huggingface_hub
daace4d verified
Raw
History Blame Contribute Delete
7.61 kB
"""
Tests for data/generator.py β€” Verify all scenarios, solutions, and pricing load correctly.
"""
import pytest
import sys
import os
# Add project root to path
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from data.generator import (
load_scenario, load_solution, load_pricing,
get_available_tasks, get_optimal_savings, get_valid_resize_targets,
)
from models import Resource, ResourceType, ResourceStatus, UsageMetrics
# ─── Scenario Loading ─────────────────────────────────────────────────────
class TestScenarioLoading:
def test_available_tasks(self):
tasks = get_available_tasks()
assert len(tasks) == 3
assert "easy_orphan_cleanup" in tasks
assert "medium_rightsize" in tasks
assert "hard_dependency_migration" in tasks
@pytest.mark.parametrize("task_id", get_available_tasks())
def test_load_scenario_returns_dict(self, task_id):
scenario = load_scenario(task_id)
assert isinstance(scenario, dict)
assert scenario["task_id"] == task_id
@pytest.mark.parametrize("task_id", get_available_tasks())
def test_scenario_has_required_keys(self, task_id):
scenario = load_scenario(task_id)
required = ["task_id", "task_difficulty", "task_description", "max_steps",
"resources", "critical_resources", "dependency_graph",
"wasteful_resources", "_cost_analysis"]
for key in required:
assert key in scenario, f"Missing key: {key}"
def test_easy_has_10_resources(self):
scenario = load_scenario("easy_orphan_cleanup")
assert len(scenario["resources"]) == 10
def test_medium_has_20_resources(self):
scenario = load_scenario("medium_rightsize")
assert len(scenario["resources"]) == 20
def test_hard_has_35_resources(self):
scenario = load_scenario("hard_dependency_migration")
assert len(scenario["resources"]) == 35
def test_invalid_task_raises(self):
with pytest.raises(ValueError, match="Unknown task_id"):
load_scenario("nonexistent_task")
@pytest.mark.parametrize("task_id", get_available_tasks())
def test_critical_resources_exist_in_resources(self, task_id):
scenario = load_scenario(task_id)
resource_ids = {r["resource_id"] for r in scenario["resources"]}
for crit_id in scenario["critical_resources"]:
assert crit_id in resource_ids, f"Critical resource {crit_id} not in resources"
@pytest.mark.parametrize("task_id", get_available_tasks())
def test_wasteful_resources_exist_in_resources(self, task_id):
scenario = load_scenario(task_id)
resource_ids = {r["resource_id"] for r in scenario["resources"]}
for waste_id in scenario["wasteful_resources"]:
assert waste_id in resource_ids, f"Wasteful resource {waste_id} not in resources"
@pytest.mark.parametrize("task_id", get_available_tasks())
def test_no_overlap_critical_wasteful(self, task_id):
"""Critical and wasteful sets must not overlap."""
scenario = load_scenario(task_id)
crit = set(scenario["critical_resources"])
waste = set(scenario["wasteful_resources"])
overlap = crit & waste
assert len(overlap) == 0, f"Overlap: {overlap}"
# ─── Solution Loading ─────────────────────────────────────────────────────
class TestSolutionLoading:
@pytest.mark.parametrize("task_id", get_available_tasks())
def test_load_solution_returns_dict(self, task_id):
solution = load_solution(task_id)
assert isinstance(solution, dict)
assert solution["task_id"] == task_id
@pytest.mark.parametrize("task_id", get_available_tasks())
def test_solution_has_optimal_savings(self, task_id):
solution = load_solution(task_id)
assert "optimal_savings_monthly" in solution
assert solution["optimal_savings_monthly"] > 0
@pytest.mark.parametrize("task_id", get_available_tasks())
def test_solution_has_action_sequence(self, task_id):
solution = load_solution(task_id)
assert "optimal_action_sequence" in solution
assert len(solution["optimal_action_sequence"]) > 0
@pytest.mark.parametrize("task_id", get_available_tasks())
def test_solution_ends_with_commit(self, task_id):
solution = load_solution(task_id)
last_action = solution["optimal_action_sequence"][-1]
assert last_action["action_type"] == "commit_changes"
@pytest.mark.parametrize("task_id", get_available_tasks())
def test_optimal_savings_matches_cost_analysis(self, task_id):
scenario = load_scenario(task_id)
solution = load_solution(task_id)
assert abs(
solution["optimal_savings_monthly"]
- scenario["_cost_analysis"]["optimal_savings_monthly"]
) < 0.01
def test_get_optimal_savings_helper(self):
savings = get_optimal_savings("easy_orphan_cleanup")
assert savings == 63.22
# ─── Pricing Loading ──────────────────────────────────────────────────────
class TestPricingLoading:
def test_load_pricing_returns_dict(self):
pricing = load_pricing()
assert isinstance(pricing, dict)
def test_pricing_has_ec2_instances(self):
pricing = load_pricing()
ec2 = pricing["ec2_instances"]
assert "t3.micro" in ec2
assert "m5.xlarge" in ec2
def test_pricing_has_rds_instances(self):
pricing = load_pricing()
rds = pricing["rds_instances"]
assert "db.t3.medium" in rds
assert "db.r5.2xlarge" in rds
def test_pricing_has_resize_paths(self):
pricing = load_pricing()
assert "valid_resize_paths" in pricing
assert "m5.xlarge" in pricing["valid_resize_paths"]
def test_get_valid_resize_targets(self):
targets = get_valid_resize_targets("m5.xlarge")
assert len(targets) > 0
assert "t3.small" in targets # cross-family resize
def test_ec2_pricing_values(self):
pricing = load_pricing()
t3_micro = pricing["ec2_instances"]["t3.micro"]
assert t3_micro["cost_per_hour"] == 0.0104
assert t3_micro["vcpu"] == 2
# ─── Resource Model Parsing ───────────────────────────────────────────────
class TestResourceParsing:
@pytest.mark.parametrize("task_id", get_available_tasks())
def test_resources_parse_to_pydantic(self, task_id):
"""All scenario resources should parse into valid Resource models."""
scenario = load_scenario(task_id)
for rdata in scenario["resources"]:
# Remove internal field
clean = {k: v for k, v in rdata.items() if not k.startswith("_")}
if clean.get("metrics"):
clean["metrics"] = UsageMetrics(**clean["metrics"])
resource = Resource(**clean)
assert resource.resource_id is not None
assert resource.cost_per_hour >= 0