Spaces:
Sleeping
Sleeping
| """ | |
| 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 | |
| def test_load_scenario_returns_dict(self, task_id): | |
| scenario = load_scenario(task_id) | |
| assert isinstance(scenario, dict) | |
| assert scenario["task_id"] == task_id | |
| 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") | |
| 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" | |
| 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" | |
| 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: | |
| def test_load_solution_returns_dict(self, task_id): | |
| solution = load_solution(task_id) | |
| assert isinstance(solution, dict) | |
| assert solution["task_id"] == task_id | |
| 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 | |
| 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 | |
| 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" | |
| 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: | |
| 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 | |