Files
gitlab-instance-0a899031_sh/d2ypp2/tests/test_install_bootstrap.py
eric 78e1308063 ss
2026-05-19 06:47:07 +00:00

135 lines
6.0 KiB
Python

from __future__ import annotations
import importlib.util
import os
import re
import stat
import sys
import unittest
from pathlib import Path
from unittest.mock import patch
ROOT = Path(__file__).resolve().parents[1]
class InstallBootstrapTests(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
cls.script = (ROOT / "install.sh").read_text(encoding="utf-8")
def test_install_sh_is_remote_bootstrapper_not_thin_wrapper(self) -> None:
mode = (ROOT / "install.sh").stat().st_mode
self.assertTrue(mode & stat.S_IXUSR, "install.sh must stay executable for local installs")
self.assertIn("SHD2YPP2 remote bootstrapper", self.script)
self.assertIn("curl -fsSL <url>/install.sh | bash", self.script)
self.assertIn("DEFAULT_INSTALL_DIR=\"/opt/live/d2ypp\"", self.script)
self.assertIn("DEFAULT_LOG_DIR=\"/var/log/shd2ypp2\"", self.script)
self.assertIn("DEFAULT_LOCK_FILE=\"/run/lock/shd2ypp2-install.lock\"", self.script)
self.assertNotRegex(self.script, r"exec bash \"\\$ROOT_DIR/install-all\\.sh\"")
def test_remote_source_defaults_match_project_layout(self) -> None:
self.assertIn('DEFAULT_REPO_URL="http://gitlab.nomadro.com/gitlab-instance-0a899031/sh.git"', self.script)
self.assertIn('DEFAULT_REPO_REF="best"', self.script)
self.assertIn('DEFAULT_REPO_SUBDIR="d2ypp2"', self.script)
self.assertIn("LIVE_REPO_URL", self.script)
self.assertIn("LIVE_REPO_REF", self.script)
self.assertIn("LIVE_REPO_SUBDIR", self.script)
self.assertIn("git clone --depth 1 --branch", self.script)
self.assertIn("Fetched source does not contain SHD2YPP2 project", self.script)
def test_profiles_are_normalized_for_existing_stack_installer(self) -> None:
self.assertRegex(self.script, r"all\|full\).*PROFILE=\"all\"")
self.assertRegex(self.script, r"hub\|core\|sh\).*PROFILE=\"hub\"")
self.assertRegex(self.script, r"business\|live\|app\|sh2\).*PROFILE=\"business\"")
self.assertIn('bash "$stack" "$PROFILE"', self.script)
def test_bootstrap_has_lock_logging_and_privilege_handling(self) -> None:
self.assertIn("setup_logging()", self.script)
self.assertIn("acquire_lock()", self.script)
self.assertIn("flock -n 9", self.script)
self.assertIn("sudo -E", self.script)
self.assertIn("LIVE_INSTALL_LOG_FILE", self.script)
self.assertIn("tee -a \"$LOG_FILE\"", self.script)
def test_sync_excludes_runtime_state_and_preserves_config(self) -> None:
required_excludes = [
".venv/",
"node_modules/",
"web-console/node_modules/",
"logs/",
"downloads/",
"backup_config/",
".pm2/",
".process_runner/",
"services/neko/data/",
"config/system-stack.env",
]
for item in required_excludes:
self.assertIn(f"--exclude '{item}'", self.script)
self.assertIn("rsync -a --delete", self.script)
self.assertIn('LIVE_STACK_REPO_ROOT="$INSTALL_DIR"', self.script)
self.assertIn('project="$(sync_to_install_dir "$project")"', self.script)
def test_post_install_verifies_real_control_plane(self) -> None:
self.assertIn("post_verify()", self.script)
self.assertIn("http://127.0.0.1:$port/health", self.script)
self.assertIn("http://127.0.0.1:$port/deploy/check", self.script)
self.assertIn("journalctl -u live-console.service -n 120", self.script)
self.assertIn("Control plane did not become healthy", self.script)
self.assertIn('tools/verify_local_install.py', self.script)
self.assertIn('python3 "$verifier" --install-dir "$INSTALL_DIR"', self.script)
def test_launch_exports_cli_host_and_port_to_runtime_environment(self) -> None:
spec = importlib.util.spec_from_file_location("launch_for_test", ROOT / "scripts" / "launch.py")
self.assertIsNotNone(spec)
module = importlib.util.module_from_spec(spec)
assert spec and spec.loader
sys.modules[spec.name] = module
try:
spec.loader.exec_module(module)
finally:
sys.modules.pop(spec.name, None)
captured: dict[str, str | bool] = {}
def fake_run_prod(host: str, port: str, skip_build: bool, reload: bool) -> None:
captured.update({"host": host, "port": port, "skip_build": skip_build, "reload": reload})
old_host = os.environ.pop("HOST", None)
old_port = os.environ.pop("PORT", None)
try:
with patch.object(
sys,
"argv",
["launch.py", "--host", "127.0.0.1", "--port", "18101", "--skip-build", "--no-env-check"],
):
with patch.object(module, "load_runtime_env"):
with patch.object(module, "raise_nofile_limit"):
with patch.object(module, "ensure_runtime_layout", return_value=[]):
with patch.object(module, "run_prod", side_effect=fake_run_prod):
module.main()
self.assertEqual(captured["host"], "127.0.0.1")
self.assertEqual(captured["port"], "18101")
self.assertEqual(os.environ["HOST"], "127.0.0.1")
self.assertEqual(os.environ["PORT"], "18101")
finally:
if old_host is not None:
os.environ["HOST"] = old_host
else:
os.environ.pop("HOST", None)
if old_port is not None:
os.environ["PORT"] = old_port
else:
os.environ.pop("PORT", None)
def test_no_placeholder_language_in_bootstrap_contract(self) -> None:
forbidden = ["TODO", "FIXME", "theoretically", "manual only", "手动修改"]
for marker in forbidden:
self.assertNotIn(marker, self.script)
self.assertIsNone(re.search(r"\bpass\b", self.script))
if __name__ == "__main__":
unittest.main()