Files
codex-app-server/cloud/tests/test_chv_to10.py
T

389 lines
15 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""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)