zcbot/tests/test_sandbox_check.py

187 lines
7.2 KiB
Python

"""`main.py sandbox check` 探测函数单元测试。
mock subprocess,验:
- daemon 不可达 / image 缺 / network 缺 / uid 错配的各种分支
- detect_fs_quota 对 xfs/ext4/zfs/btrfs/其他 + prjquota mount option 的判断
- 汇总 exit code:全 ok / 仅 warn / 有 err
"""
from __future__ import annotations
import sys
import unittest
from pathlib import Path
from unittest.mock import patch
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from core.sandbox.check import (
check_docker_daemon,
check_image_present,
check_host_uid_alignment,
detect_fs_quota,
run_sandbox_check,
)
def _mk_run(returns):
"""构造 `_run` 替身:按调用次序返 (rc, stdout, stderr) 列表里的元素。"""
iter_ret = iter(returns)
def fake_run(argv, timeout=10):
return next(iter_ret)
return fake_run
class TestDaemonCheck(unittest.TestCase):
def test_daemon_ok(self):
with patch("core.sandbox.check._run", _mk_run([(0, "24.0.7", "")])):
self.assertTrue(check_docker_daemon())
def test_daemon_cli_missing(self):
with patch("core.sandbox.check._run", _mk_run([(127, "", "docker not found in PATH")])):
self.assertFalse(check_docker_daemon())
def test_daemon_permission_denied(self):
with patch(
"core.sandbox.check._run",
_mk_run([(1, "", "Got permission denied while trying to connect")]),
):
self.assertFalse(check_docker_daemon())
class TestImageCheck(unittest.TestCase):
def test_image_present(self):
with patch("core.sandbox.check._run", _mk_run([(0, "[...]", "")])):
self.assertTrue(check_image_present())
def test_image_missing(self):
with patch("core.sandbox.check._run", _mk_run([(1, "", "No such image")])):
self.assertFalse(check_image_present())
class TestHostUidAlignment(unittest.TestCase):
def test_uid_aligned(self):
if sys.platform == "win32":
self.skipTest("getuid not on Windows")
import os
host_uid = os.getuid() # type: ignore[attr-defined]
with patch(
"core.sandbox.check._run",
_mk_run([(0, str(host_uid), "")]),
):
self.assertTrue(check_host_uid_alignment())
def test_uid_mismatch(self):
if sys.platform == "win32":
self.skipTest("getuid not on Windows")
import os
bad = os.getuid() + 1 # type: ignore[attr-defined]
with patch("core.sandbox.check._run", _mk_run([(0, str(bad), "")])):
self.assertFalse(check_host_uid_alignment())
def test_image_not_built_yet(self):
# docker run 失败 → warn 不 err
with patch(
"core.sandbox.check._run",
_mk_run([(125, "", "Unable to find image")]),
):
self.assertTrue(check_host_uid_alignment())
def test_skipped_on_windows(self):
with patch("core.sandbox.check.sys") as mock_sys, \
patch("core.sandbox.check._run", _mk_run([(0, "1000", "")])):
mock_sys.platform = "win32"
self.assertTrue(check_host_uid_alignment())
class TestDetectFsQuota(unittest.TestCase):
"""detect_fs_quota:不依赖 print,纯返 (level, msg) 便于断言。"""
def test_xfs_with_prjquota(self):
with patch("core.sandbox.check._run", _mk_run([(0, "xfs rw,relatime,prjquota,attr2", "")])), \
patch("core.sandbox.check.sys") as mock_sys:
mock_sys.platform = "linux"
level, msg = detect_fs_quota(Path("/opt/zcbot/workspace/users"))
self.assertEqual(level, "ok")
self.assertIn("xfs with prjquota", msg)
def test_xfs_without_prjquota(self):
with patch("core.sandbox.check._run", _mk_run([(0, "xfs rw,relatime,attr2", "")])), \
patch("core.sandbox.check.sys") as mock_sys:
mock_sys.platform = "linux"
level, msg = detect_fs_quota(Path("/opt"))
self.assertEqual(level, "warn")
self.assertIn("NO prjquota", msg)
def test_ext4_with_project_quota(self):
with patch("core.sandbox.check._run", _mk_run([(0, "ext4 rw,prjquota", "")])), \
patch("core.sandbox.check.sys") as mock_sys:
mock_sys.platform = "linux"
level, msg = detect_fs_quota(Path("/opt"))
self.assertEqual(level, "ok")
self.assertIn("ext4 with project quota", msg)
def test_zfs(self):
with patch("core.sandbox.check._run", _mk_run([(0, "zfs rw,xattr,noacl", "")])), \
patch("core.sandbox.check.sys") as mock_sys:
mock_sys.platform = "linux"
level, msg = detect_fs_quota(Path("/tank/zcbot"))
self.assertEqual(level, "ok")
self.assertIn("zfs", msg)
def test_btrfs_warns(self):
with patch("core.sandbox.check._run", _mk_run([(0, "btrfs rw,relatime", "")])), \
patch("core.sandbox.check.sys") as mock_sys:
mock_sys.platform = "linux"
level, msg = detect_fs_quota(Path("/opt"))
self.assertEqual(level, "warn")
self.assertIn("btrfs", msg)
def test_tmpfs_warns(self):
with patch("core.sandbox.check._run", _mk_run([(0, "tmpfs rw", "")])), \
patch("core.sandbox.check.sys") as mock_sys:
mock_sys.platform = "linux"
level, msg = detect_fs_quota(Path("/tmp"))
self.assertEqual(level, "warn")
def test_findmnt_missing(self):
with patch("core.sandbox.check._run", _mk_run([(127, "", "findmnt not found in PATH")])), \
patch("core.sandbox.check.sys") as mock_sys:
mock_sys.platform = "linux"
level, msg = detect_fs_quota(Path("/opt"))
self.assertEqual(level, "warn")
self.assertIn("findmnt", msg)
def test_windows_skipped(self):
with patch("core.sandbox.check.sys") as mock_sys:
mock_sys.platform = "win32"
level, msg = detect_fs_quota(Path("C:/"))
self.assertEqual(level, "warn")
self.assertIn("Windows", msg)
class TestSummaryExitCode(unittest.TestCase):
"""run_sandbox_check 汇总:err → exit 1,全 ok / 仅 warn → exit 0。"""
def test_all_ok_exits_zero(self):
with patch("core.sandbox.check.check_docker_daemon", return_value=True), \
patch("core.sandbox.check.check_image_present", return_value=True), \
patch("core.sandbox.check.check_network_present", return_value=True), \
patch("core.sandbox.check.check_host_uid_alignment", return_value=True), \
patch("core.sandbox.check.check_fs_quota_capable", return_value=True):
rc = run_sandbox_check()
self.assertEqual(rc, 0)
def test_any_err_exits_one(self):
with patch("core.sandbox.check.check_docker_daemon", return_value=False), \
patch("core.sandbox.check.check_image_present", return_value=True), \
patch("core.sandbox.check.check_network_present", return_value=True), \
patch("core.sandbox.check.check_host_uid_alignment", return_value=True), \
patch("core.sandbox.check.check_fs_quota_capable", return_value=True):
rc = run_sandbox_check()
self.assertEqual(rc, 1)
if __name__ == "__main__":
unittest.main()