Spaces:
Sleeping
Sleeping
| import pytest | |
| from unittest.mock import MagicMock, call | |
| from server.actions import ( | |
| ScaleAction, | |
| DeletePodAction, | |
| PatchAction, | |
| RolloutRestartAction, | |
| SetHPAAction, | |
| DrainNodeAction, | |
| DescribeAction, | |
| WaitAction, | |
| ) | |
| from server.executor import execute | |
| from server.models import ClusterObservation | |
| class MockWorld: | |
| def __init__(self): | |
| self.scale_called_with = None | |
| self.delete_pod_called_with = None | |
| self.apply_patch_called_with = None | |
| self.rollout_restart_called_with = None | |
| self.set_hpa_called_with = None | |
| self.drain_node_called_with = None | |
| self.describe_called_with = None | |
| self.tick_called = False | |
| self._observation = ClusterObservation(nodes=[], pods=[], deployments=[], services=[], configmaps=[], hpa=[], events=[], step=0, objective="") | |
| self._raw_state = {"nodes": [], "pods": [], "deployments": [], "services": [], "configmaps": []} | |
| def scale(self, deployment, replicas): | |
| self.scale_called_with = (deployment, replicas) | |
| def delete_pod(self, pod_name): | |
| self.delete_pod_called_with = pod_name | |
| def apply_patch(self, resource_type, name, patch): | |
| self.apply_patch_called_with = (resource_type, name, patch) | |
| def rollout_restart(self, deployment): | |
| self.rollout_restart_called_with = deployment | |
| def set_hpa(self, deployment, min_replicas, max_replicas, cpu_target_percent): | |
| self.set_hpa_called_with = (deployment, min_replicas, max_replicas, cpu_target_percent) | |
| def drain_node(self, node_name): | |
| self.drain_node_called_with = node_name | |
| def describe(self, resource_type, name): | |
| self.describe_called_with = (resource_type, name) | |
| return {"type": resource_type, "name": name, "detail": "mock detail"} | |
| def tick(self): | |
| self.tick_called = True | |
| def get_observation(self): | |
| return self._observation | |
| def get_raw_state(self): | |
| return self._raw_state | |
| class TestExecutorScale: | |
| def test_scale_calls_world_scale_and_ticks(self): | |
| mock_world = MockWorld() | |
| action = ScaleAction(action_type="scale", deployment="frontend", replicas=3) | |
| result = execute(action, mock_world) | |
| assert mock_world.scale_called_with == ("frontend", 3) | |
| assert mock_world.tick_called is True | |
| assert result.tick_advanced is True | |
| assert "Scaled" in result.action_applied | |
| def test_scale_action_applied_message(self): | |
| mock_world = MockWorld() | |
| action = ScaleAction(action_type="scale", deployment="frontend", replicas=5) | |
| result = execute(action, mock_world) | |
| assert result.action_applied == "Scaled 'frontend' to 5 replicas" | |
| class TestExecutorDeletePod: | |
| def test_delete_pod_calls_world_and_ticks(self): | |
| mock_world = MockWorld() | |
| action = DeletePodAction(action_type="delete_pod", pod_name="frontend-7d9f-xkp2") | |
| result = execute(action, mock_world) | |
| assert mock_world.delete_pod_called_with == "frontend-7d9f-xkp2" | |
| assert mock_world.tick_called is True | |
| assert result.tick_advanced is True | |
| class TestExecutorPatch: | |
| def test_patch_calls_world_and_ticks(self): | |
| mock_world = MockWorld() | |
| action = PatchAction( | |
| action_type="patch", | |
| resource_type="deployment", | |
| name="frontend", | |
| patch={"env": [{"name": "DB_HOST", "value": "db.prod.internal"}]} | |
| ) | |
| result = execute(action, mock_world) | |
| assert mock_world.apply_patch_called_with == ( | |
| "deployment", | |
| "frontend", | |
| {"env": [{"name": "DB_HOST", "value": "db.prod.internal"}]} | |
| ) | |
| assert mock_world.tick_called is True | |
| assert result.tick_advanced is True | |
| class TestExecutorRolloutRestart: | |
| def test_rollout_restart_calls_world_and_ticks(self): | |
| mock_world = MockWorld() | |
| action = RolloutRestartAction(action_type="rollout_restart", deployment="frontend") | |
| result = execute(action, mock_world) | |
| assert mock_world.rollout_restart_called_with == "frontend" | |
| assert mock_world.tick_called is True | |
| assert result.tick_advanced is True | |
| class TestExecutorSetHPA: | |
| def test_set_hpa_calls_world_and_ticks(self): | |
| mock_world = MockWorld() | |
| action = SetHPAAction( | |
| action_type="set_hpa", | |
| deployment="api", | |
| min_replicas=2, | |
| max_replicas=10, | |
| cpu_target_percent=70 | |
| ) | |
| result = execute(action, mock_world) | |
| assert mock_world.set_hpa_called_with == ("api", 2, 10, 70) | |
| assert mock_world.tick_called is True | |
| assert result.tick_advanced is True | |
| class TestExecutorDrainNode: | |
| def test_drain_node_calls_world_and_ticks(self): | |
| mock_world = MockWorld() | |
| action = DrainNodeAction(action_type="drain_node", node_name="node-1") | |
| result = execute(action, mock_world) | |
| assert mock_world.drain_node_called_with == "node-1" | |
| assert mock_world.tick_called is True | |
| assert result.tick_advanced is True | |
| class TestExecutorDescribe: | |
| def test_describe_does_not_tick(self): | |
| mock_world = MockWorld() | |
| action = DescribeAction( | |
| action_type="describe", | |
| resource_type="deployment", | |
| name="frontend" | |
| ) | |
| result = execute(action, mock_world) | |
| assert mock_world.describe_called_with == ("deployment", "frontend") | |
| assert mock_world.tick_called is False | |
| assert result.tick_advanced is False | |
| def test_describe_returns_detail(self): | |
| mock_world = MockWorld() | |
| action = DescribeAction( | |
| action_type="describe", | |
| resource_type="deployment", | |
| name="frontend" | |
| ) | |
| result = execute(action, mock_world) | |
| assert result.describe_detail is not None | |
| assert result.describe_detail["type"] == "deployment" | |
| class TestExecutorWait: | |
| def test_wait_ticks_without_other_world_mutations(self): | |
| mock_world = MockWorld() | |
| action = WaitAction(action_type="wait") | |
| result = execute(action, mock_world) | |
| assert mock_world.tick_called is True | |
| assert result.tick_advanced is True | |
| assert result.action_applied == "Waited one simulation tick" | |