110 lines
5.3 KiB
Python
110 lines
5.3 KiB
Python
from __future__ import annotations
|
|
|
|
import asyncio
|
|
import tempfile
|
|
import unittest
|
|
from pathlib import Path
|
|
from unittest.mock import AsyncMock, patch
|
|
|
|
from src.web_process_backend import WebProcessBackend, build_pm2_start_command
|
|
|
|
|
|
class WebProcessBackendTests(unittest.TestCase):
|
|
def test_build_pm2_start_command_for_python_script_uses_repo_context(self) -> None:
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
base_dir = Path(tmp)
|
|
script = base_dir / "youtube.py"
|
|
venv_python = base_dir / ".venv" / "bin" / "python"
|
|
venv_python.parent.mkdir(parents=True, exist_ok=True)
|
|
venv_python.write_text("#!/usr/bin/env python3\n", encoding="utf-8")
|
|
script.write_text("print('ok')\n", encoding="utf-8")
|
|
|
|
cmd = build_pm2_start_command(base_dir, "youtube2__lane1", "youtube.py")
|
|
|
|
self.assertIsNotNone(cmd)
|
|
assert cmd is not None
|
|
self.assertIn("LIVE_PM2_PROCESS_NAME=youtube2__lane1", cmd)
|
|
self.assertIn("--name youtube2__lane1", cmd)
|
|
self.assertIn("--cwd", cmd)
|
|
self.assertIn(str(script), cmd)
|
|
self.assertIn(str(venv_python), cmd)
|
|
|
|
def test_parse_pm2_describe_marks_unknown_unregistered_process_as_not_found(self) -> None:
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
base_dir = Path(tmp)
|
|
backend = WebProcessBackend(base_dir, base_dir / ".pm2" / "logs")
|
|
backend._use_pm2 = True
|
|
backend.ensure_mode = AsyncMock()
|
|
backend._pm2_registered_names = AsyncMock(return_value=set())
|
|
warn = "[PM2][WARN] Current process list is not synchronized with saved list. App youtube differs."
|
|
with patch("src.web_process_backend.run_pm2", new=AsyncMock(return_value=warn)):
|
|
info = asyncio.run(backend.parse_pm2_describe("youtube"))
|
|
self.assertEqual(info["process_status"], "not_found")
|
|
self.assertIsNone(info["out_path"])
|
|
self.assertIsNone(info["err_path"])
|
|
|
|
def test_parse_pm2_describe_supports_spaced_log_path_keys(self) -> None:
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
base_dir = Path(tmp)
|
|
backend = WebProcessBackend(base_dir, base_dir / ".pm2" / "logs")
|
|
backend._use_pm2 = True
|
|
backend.ensure_mode = AsyncMock()
|
|
backend._pm2_registered_names = AsyncMock(return_value={"youtube"})
|
|
describe = "\n".join(
|
|
[
|
|
"│ status │ online │",
|
|
"│ out log path │ /home/live/.pm2/logs/youtube-out.log │",
|
|
"│ error log path │ /home/live/.pm2/logs/youtube-error.log │",
|
|
]
|
|
)
|
|
with patch("src.web_process_backend.run_pm2", new=AsyncMock(return_value=describe)):
|
|
info = asyncio.run(backend.parse_pm2_describe("youtube"))
|
|
self.assertEqual(info["process_status"], "online")
|
|
self.assertEqual(info["out_path"], "/home/live/.pm2/logs/youtube-out.log")
|
|
self.assertEqual(info["err_path"], "/home/live/.pm2/logs/youtube-error.log")
|
|
|
|
def test_ensure_mode_resurrects_pm2_when_dump_exists_and_jlist_is_empty(self) -> None:
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
base_dir = Path(tmp)
|
|
pm2_home = base_dir / ".pm2-home"
|
|
pm2_home.mkdir(parents=True, exist_ok=True)
|
|
(pm2_home / "pm2.pid").write_text("1234\n", encoding="utf-8")
|
|
(pm2_home / "dump.pm2").write_text('[{"name":"youtube"}]\n', encoding="utf-8")
|
|
|
|
backend = WebProcessBackend(base_dir, base_dir / ".pm2" / "logs")
|
|
backend._use_pm2 = True
|
|
|
|
shell_out = AsyncMock(return_value=(0, "[]"))
|
|
resurrect = AsyncMock(return_value="ok")
|
|
with patch.dict("os.environ", {"PM2_HOME": str(pm2_home)}, clear=False):
|
|
with patch("src.web_process_backend.resolve_pm2_bin", return_value="/usr/bin/pm2"):
|
|
with patch("src.web_process_backend._shell_out", new=shell_out):
|
|
with patch("src.web_process_backend.run_pm2", new=resurrect):
|
|
asyncio.run(backend.ensure_mode())
|
|
|
|
resurrect.assert_awaited_once_with("resurrect", timeout=45.0)
|
|
|
|
def test_ensure_mode_skips_resurrect_without_saved_dump(self) -> None:
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
base_dir = Path(tmp)
|
|
pm2_home = base_dir / ".pm2-home"
|
|
pm2_home.mkdir(parents=True, exist_ok=True)
|
|
(pm2_home / "pm2.pid").write_text("1234\n", encoding="utf-8")
|
|
|
|
backend = WebProcessBackend(base_dir, base_dir / ".pm2" / "logs")
|
|
backend._use_pm2 = True
|
|
|
|
shell_out = AsyncMock(return_value=(0, "[]"))
|
|
resurrect = AsyncMock(return_value="ok")
|
|
with patch.dict("os.environ", {"PM2_HOME": str(pm2_home)}, clear=False):
|
|
with patch("src.web_process_backend.resolve_pm2_bin", return_value="/usr/bin/pm2"):
|
|
with patch("src.web_process_backend._shell_out", new=shell_out):
|
|
with patch("src.web_process_backend.run_pm2", new=resurrect):
|
|
asyncio.run(backend.ensure_mode())
|
|
|
|
resurrect.assert_not_awaited()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|