import json from pathlib import Path import subprocess import sys import tempfile import unittest from unittest.mock import patch from console.catalog import upload from console.settings import Settings from console.store import Store @unittest.skipUnless(sys.platform == "linux", "worker dispatcher runs on Linux") class WorkerFinalizationTests(unittest.TestCase): def test_unmount_failure_changes_both_record_and_archived_summary(self): from console.worker import Worker with tempfile.TemporaryDirectory() as temp: settings = Settings(Path(temp), []) store = Store(settings) script = upload(store, b"print('test')\n", "test.py") run = store.queue(script, "test-request", "127.0.0.1") store.claim() worker = Worker(settings, {}) directory = settings.runs / run["id"] real_popen = subprocess.Popen def command(value, target): (target / "work").mkdir() (target / "work/result.txt").write_text("test output") return ["create"], {} def fake_docker(*args, **kwargs): if args[0] == "inspect": return json.dumps([{"Config": {"Labels": {"xas.console": "dedicated"}}, "State": {"ExitCode": 0, "Running": False}}]) return "owned-test-container" def process(*args, **kwargs): return real_popen([sys.executable, "-c", "import sys;sys.stdin.readline();print('output')"], **kwargs) with patch.object(worker, "command", command), patch("console.worker.docker", fake_docker), \ patch("console.worker.subprocess.Popen", process), \ patch("console.worker.workspace.mounts", return_value=[directory / "work"]), \ patch("console.worker.workspace.remove", side_effect=OSError("simulated unmount failure")): worker.execute(run) result = store.run(run["id"]) self.assertEqual(result["state"], "interrupted") summary = next(a for a in result["artifacts"] if a["name"] == "summary.json") archived = json.loads((directory / "artifacts" / summary["id"]).read_text()) self.assertEqual(archived["state"], result["state"]) self.assertEqual(archived["message"], result["message"]) self.assertTrue(worker.stopping)