from __future__ import annotations from unittest.mock import Mock from tests.test_lifecycle import LifecycleTestCase from uvm.errors import ConflictError, NotFoundError, UvmError, ValidationError from uvm.tool_catalog import ToolSelection class ToolLifecycleTests(LifecycleTestCase): def test_create_persists_tool_intent_before_boot(self): def configure(config): vm_id = next(iter(self.store.load().vms)) jobs = self.service._tool_store._read(vm_id)["jobs"] self.assertEqual(jobs[0]["selections"], [{"id": "python", "version": "3.12"}]) self.assertEqual(jobs[0]["status"], "queued") original = self.service._client_factory def client(socket, timeout): result = original(socket, timeout) result.configure_and_start = configure return result self.service._client_factory = client vm = self.create_vm(tools=(ToolSelection("python", "3.12"),)) self.assertEqual(vm.status, "running") self.assertEqual(self.service._tool_store.read(vm.id)["jobs"][0]["source"], "create") def test_invalid_tools_fail_before_reserving_a_vm_and_failed_boot_removes_intent(self): with self.assertRaises(ValidationError): self.create_vm(tools=({"id": "invalid"},)) self.assertEqual(self.store.load().vms, {}) self.host.failures["client.configure", None] = UvmError("failed boot") with self.assertRaises(UvmError): self.create_vm(tools=(ToolSelection("git"),)) self.assertEqual(list(self.settings.vms_dir.iterdir()), []) def test_stopped_installation_starts_only_when_requested_and_aliases_are_rejected(self): vm = self.create_vm(name="worker") self.service.stop(vm.id) for alias in (vm.name, vm.guest_ip): with self.assertRaises(NotFoundError): self.service.queue_tools(alias, [ToolSelection("git")], start_if_stopped=True) with self.assertRaises(ConflictError): self.service.queue_tools(vm.id, [ToolSelection("git")]) job = self.service.queue_tools(vm.id, [ToolSelection("git")], start_if_stopped=True) self.assertEqual(self.store.get(vm.id).status, "stopped") ready = self.service.prepare_tool_install(vm.id, start_if_stopped=job["start_if_stopped"]) self.assertEqual(ready.status, "running") self.assertEqual(ready.guest_ip, vm.guest_ip) def test_a_tool_step_cannot_reconnect_to_a_restarted_vm(self): vm = self.create_vm() identity = vm.pid, vm.process_start_time, vm.boot_id self.service.restart(vm.id) self.service._guest_connector = Mock() with self.assertRaisesRegex(ConflictError, "restarted"): self.service.execute(vm.id, "id", expected_identity=identity) self.service._guest_connector.assert_not_called()