"""to10 轨新增测试:覆盖率缺口分支(非刷行)。 只测此前 coverage 报告里未覆盖的分支:api_client 的假 socket 错误路径、hostkeys 的 keygen/seed 失败路径、 orphans 的 registry 失败路径、keyscan 过滤、compat 的 fail-closed 分支、config 的类型转换与 assemble 冲突。 不删旧测试、不改分母;每项都是行为断言,不是 grep。 """ import os import socket import tempfile import unittest from cloud.chv import api_client from cloud.chv import compat215 from cloud.chv import config as chv_config from cloud.chv import hostkeys from cloud.chv import orphans from cloud.chv import ssh_transport class _FakeSocket: """按 recv 调用序列供数据的假 socket。""" def __init__(self, chunks, timeout_at=None): self._chunks = list(chunks) self._timeout_at = timeout_at self._calls = 0 self.closed = False def recv(self, _size): self._calls += 1 if self._timeout_at is not None \ and self._calls >= self._timeout_at: raise socket.timeout("fake timeout") if not self._chunks: return b"" return self._chunks.pop(0) def close(self): self.closed = True class ApiClientTest(unittest.TestCase): def test_header_timeout_raises(self): with self.assertRaises(RuntimeError): api_client._read_header(_FakeSocket([], timeout_at=1)) def test_header_early_close_raises(self): with self.assertRaises(RuntimeError): api_client._read_header(_FakeSocket([])) def test_chunked_rejected(self): with self.assertRaises(RuntimeError): api_client._parse_content_length( "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked") def test_no_length_rejected(self): with self.assertRaises(RuntimeError): api_client._parse_content_length("HTTP/1.1 200 OK") def test_illegal_length_rejected(self): with self.assertRaises(RuntimeError): api_client._parse_content_length( "HTTP/1.1 200 OK\r\nContent-Length: xx") def test_body_timeout_truncation(self): with self.assertRaises(RuntimeError): api_client._read_body(_FakeSocket([], timeout_at=1), b"ab", 10) def test_body_early_close_truncation(self): with self.assertRaises(RuntimeError): api_client._read_body(_FakeSocket([]), b"ab", 10) def test_parse_non_http_status_zero(self): status, _body = api_client._parse_response("garbage", b"{}") self.assertEqual(status, 0) def test_parse_non_json_body_is_text(self): _status, body = api_client._parse_response( "HTTP/1.1 200 OK", b"not-json") self.assertEqual(body, "not-json") def test_parse_empty_body_is_empty_dict(self): _status, body = api_client._parse_response( "HTTP/1.1 200 OK", b"") self.assertEqual(body, {}) def test_unix_api_missing_socket_raises(self): with self.assertRaises(RuntimeError): api_client._unix_socket_api("/tmp/no-such-chv.sock", "GET", "/vm.info") class HostkeysTest(unittest.TestCase): def test_keygen_reuse_existing_pair(self): with tempfile.TemporaryDirectory() as tmp: paths = hostkeys.host_key_paths(tmp, "vm1") os.makedirs(os.path.dirname(paths["private"]), exist_ok=True) for key in ("private", "public"): with open(paths[key], "w") as fh: fh.write("x") out = hostkeys.generate_host_keypair( tmp, "vm1", runner=lambda cmd: (_ for _ in ()).throw( AssertionError("reuse 不应调 keygen"))) self.assertEqual(out, paths) def test_keygen_nonzero_rc_raises(self): class _Res: returncode = 1 with tempfile.TemporaryDirectory() as tmp: with self.assertRaises(RuntimeError): hostkeys.generate_host_keypair( tmp, "vm2", runner=lambda cmd: _Res()) def test_keygen_no_files_after_runner_raises(self): class _Res: returncode = 0 with tempfile.TemporaryDirectory() as tmp: with self.assertRaises(RuntimeError): hostkeys.generate_host_keypair( tmp, "vm3", runner=lambda cmd: _Res()) def test_seed_disk_nonzero_rc_raises(self): class _Res: returncode = 1 with tempfile.TemporaryDirectory() as tmp: with self.assertRaises(RuntimeError): hostkeys.build_seed_disk( tmp, os.path.join(tmp, "seed.iso"), runner=lambda cmd: _Res()) def test_seed_disk_missing_output_raises(self): class _Res: returncode = 0 with tempfile.TemporaryDirectory() as tmp: with self.assertRaises(RuntimeError): hostkeys.build_seed_disk( tmp, os.path.join(tmp, "seed.iso"), runner=lambda cmd: _Res()) class OrphansAndKeyscanTest(unittest.TestCase): def test_quarantine_failure_only_warns(self): with self.assertLogs("cloud.cloud_hypervisor", level="WARNING") as logs: orphans._quarantine_registry("/tmp/no-such-dir-x/f.json") self.assertTrue(any("quarantine_failed" in line for line in logs.output)) def test_write_registry_failure_only_warns(self): with tempfile.TemporaryDirectory() as tmp: blocker = os.path.join(tmp, "blocker") with open(blocker, "w") as fh: fh.write("x") with self.assertLogs("cloud.cloud_hypervisor", level="WARNING") as logs: orphans._write_registry( os.path.join(blocker, "registry.json"), {}) self.assertTrue(any("save_failed" in line for line in logs.output)) def test_keyscan_filters_short_and_non_ssh_lines(self): text = "\n".join([ "# comment", "", "only-two", "127.0.0.1 ecdsa-sha2-nistp256 AAAA", "[127.0.0.1]:2222 ssh-ed25519 AAAA-good", "other-host ssh-ed25519 AAAA-other", ]) kept = ssh_transport.parse_keyscan_output(text) self.assertEqual(kept, ["[127.0.0.1]:2222 ssh-ed25519 AAAA-good"]) class CompatAndConfigTest(unittest.TestCase): def test_tmpfs_unknown_executor_fail_closed(self): isolated, _reason = compat215.tmpfs_isolated("unknown-kind", "/tmp") self.assertFalse(isolated) def test_resolve_inside_empty_rel_is_base(self): self.assertEqual(compat215.resolve_inside("/a/b", ""), "/a/b") def test_resolve_inside_absolute_is_none(self): self.assertIsNone(compat215.resolve_inside("/a/b", "/etc/passwd")) def test_to_int_bool_and_float_and_bad(self): with self.assertRaises(ValueError): chv_config._to_int(True) self.assertEqual(chv_config._to_int(3.9), 3) self.assertEqual(chv_config._to_int(" 7 "), 7) with self.assertRaises(ValueError): chv_config._to_int(object()) def test_to_float_bool_and_bad(self): with self.assertRaises(ValueError): chv_config._to_float(False) self.assertEqual(chv_config._to_float("2.5"), 2.5) with self.assertRaises(ValueError): chv_config._to_float(object()) def test_assemble_conflict_raises(self): with self.assertRaises(ValueError): chv_config._assemble_chv_config( [{"cpus": "2"}, {"cpus": "4"}]) if __name__ == "__main__": unittest.main() class WorkerAdapterTest(unittest.TestCase): """to10:worker_adapter 的直接行为测试(worker 薄包装 由旧测试 test_chv_fix/test_chv_gaps 覆盖,此处测本体)。""" def test_call_breakpoint_prefers_signature_kwargs(self): from cloud.chv import worker_adapter seen = {} class _Ex: def goal_resume_breakpoint_task(self, task, model_provider=None, extra_env=None): seen.update(task=task, model_provider=model_provider, extra_env=extra_env) return "ok" out = worker_adapter.call_goal_resume_breakpoint( _Ex(), {"id": "t"}, model_provider="m", extra_env={"A": "1"}) self.assertEqual(out, "ok") self.assertEqual(seen["model_provider"], "m") self.assertEqual(seen["extra_env"], {"A": "1"}) def test_call_breakpoint_single_arg_executor(self): from cloud.chv import worker_adapter class _Ex: def goal_resume_breakpoint_task(self, task): return "single" self.assertEqual( worker_adapter.call_goal_resume_breakpoint(_Ex(), {"id": "t"}), "single") def test_audit_callback_extracts_project_and_swallows(self): from cloud.chv import worker_adapter calls = [] cb = worker_adapter.make_chv_audit_callback(calls.append) # append 只接受 1 参,回调内部 except 应吞掉,不炸 cb("booted", {"project_id": "p1"}, "detail") self.assertEqual(calls, []) got = [] cb2 = worker_adapter.make_chv_audit_callback( lambda pid, ev, detail: got.append((pid, ev, detail))) cb2("booted", {"project_id": "p1"}, "detail") cb2("booted", "not-a-dict", "detail") self.assertEqual(got[0], ("p1", "chv_vm_booted", "detail")) self.assertEqual(got[1], (None, "chv_vm_booted", "detail")) def test_reconcile_non_chv_returns_empty(self): from cloud.chv import worker_adapter self.assertEqual( worker_adapter.reconcile_warm_pool(object(), lambda *a: None, "startup"), []) def test_watchdog_vm_name_error_returns_none(self): from cloud.chv import worker_adapter class _Bad: def vm_name(self, task): raise RuntimeError("boom") self.assertIsNone(worker_adapter.watchdog_vm_name(_Bad(), {})) def test_cancel_watchdog_loop_kills_on_flag(self): import threading from cloud.chv import worker_adapter killed = [] stop = threading.Event() worker_adapter.cancel_watchdog_loop( "t1", "chv-t1", stop, {"t1"}, lambda tid: None, lambda vm: killed.append(vm), None) # stop 未置位时循环每 1 秒轮询:flag 已命中,第一轮即 # 关 VM 并返回;这里不依赖日志(logger 传 None 时 # log_event 内部不炸——若炸则本项失败)。 self.assertEqual(killed, ["chv-t1"]) def test_quota_watchdog_loop_marks_and_kills(self): import threading from cloud.chv import worker_adapter killed = [] marked = [] stop = threading.Event() worker_adapter.quota_watchdog_loop( "t2", "chv-t2", stop, lambda: True, marked.append, lambda vm: killed.append(vm), None, interval=0.01) self.assertEqual(marked, ["t2"]) self.assertEqual(killed, ["chv-t2"]) class AuditSpillTest(unittest.TestCase): """to10:_emit 回调失败时落盘(ADR-14),不中断、不抛。""" def test_spill_on_callback_failure(self): import json as _json from cloud.chv.executor import CloudHypervisorExecutor with tempfile.TemporaryDirectory() as tmp: ex = CloudHypervisorExecutor( binary="/bin/true", kernel="/tmp/k", rootfs="/tmp/r", state_dir=tmp, ssh_key="/tmp/key") calls = [] def _bad(event, task, detail, kind=None): raise RuntimeError("db down") ex.audit_callback = _bad ex._emit("booted", {"id": "t-spill"}, "vm=chv-t-spill") self.assertEqual(ex.audit_failure_count, 1) spill = os.path.join(tmp, "chv-audit-spill.jsonl") self.assertTrue(os.path.exists(spill)) row = _json.loads(open(spill).readline()) self.assertEqual(row["event"], "booted") self.assertEqual(row["task_id"], "t-spill") self.assertEqual(row["detail"], "vm=chv-t-spill") def test_spill_rotation_keeps_old(self): from cloud.chv.executor import CloudHypervisorExecutor with tempfile.TemporaryDirectory() as tmp: ex = CloudHypervisorExecutor( binary="/bin/true", kernel="/tmp/k", rootfs="/tmp/r", state_dir=tmp, ssh_key="/tmp/key") spill = os.path.join(tmp, "chv-audit-spill.jsonl") with open(spill, "w") as fh: fh.write("x" * (1024 * 1024 + 10)) ex._spill_audit("booted", {"id": "t2"}, "d", None) self.assertTrue(os.path.exists(spill + ".old")) self.assertTrue(os.path.getsize(spill) < 1024) class LockfileScriptTest(unittest.TestCase): """to10:generate_lockfile.py 的 generate/verify(合成 BUILDINFO,不碰真实客机清单,不编版本号)。""" def _load_script(self): import importlib.util path = os.path.join( os.path.dirname(os.path.dirname(__file__)), "..", "chv-guest", "generate_lockfile.py") path = os.path.normpath(path) spec = importlib.util.spec_from_file_location( "chv_generate_lockfile", path) mod = importlib.util.module_from_spec(spec) spec.loader.exec_module(mod) return mod def test_generate_reads_real_dpkg_version(self): import json as _json mod = self._load_script() with tempfile.TemporaryDirectory() as tmp: bi = os.path.join(tmp, "bi.json") out = os.path.join(tmp, "lock.json") with open(bi, "w") as fh: _json.dump({"tag": "t", "contents": ["coreutils"]}, fh) self.assertEqual(mod.generate(bi, out), 0) lock = _json.load(open(out)) self.assertEqual(lock["tag"], "t") self.assertEqual(lock["unresolved"], []) self.assertTrue(lock["versions"]["dpkg"][0].startswith( "coreutils ")) def test_verify_fails_on_unresolved(self): import json as _json mod = self._load_script() with tempfile.TemporaryDirectory() as tmp: lock = os.path.join(tmp, "lock.json") with open(lock, "w") as fh: _json.dump({"tag": "t", "versions": {"dpkg": ["a 1.0"], "pip": ["b 2.0"]}, "unresolved": ["zzz"]}, fh) self.assertEqual(mod.verify(lock, None), 1)