import importlib import io import tempfile import unittest from contextlib import redirect_stdout from pathlib import Path from unittest import mock class LoginDouyinModuleTests(unittest.TestCase): def test_build_login_command_uses_expected_chrome_arguments(self) -> None: module = importlib.import_module("login_douyin") command = module.build_login_command( chrome_path="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome", profile_dir=Path("/tmp/douyin-profile"), browser_port=9223, user_url="https://www.douyin.com/user/example", ) self.assertEqual( command, [ "open", "-na", "/Applications/Google Chrome.app", "--args", "--user-data-dir=/tmp/douyin-profile", "--remote-debugging-port=9223", "https://www.douyin.com/user/example", ], ) def test_build_parser_uses_expected_defaults(self) -> None: module = importlib.import_module("login_douyin") args = module.build_parser().parse_args([]) self.assertEqual(args.browser_port, 9223) self.assertEqual(args.chrome_path, module.DEFAULT_CHROME_PATH) self.assertEqual(args.user_url, module.DEFAULT_USER_URL) def test_main_creates_profile_dir_and_prints_next_step(self) -> None: module = importlib.import_module("login_douyin") with tempfile.TemporaryDirectory() as temp_dir: profile_dir = Path(temp_dir) / "profile" stdout = io.StringIO() with redirect_stdout(stdout): with mock.patch.object(module, "launch_browser") as mocked_launch: with mock.patch.object(module, "wait_for_browser_debug_port") as mocked_wait: exit_code = module.main( [ "--chrome-path", "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome", "--profile-dir", str(profile_dir), "--browser-port", "9333", ] ) self.assertEqual(exit_code, 0) self.assertTrue(profile_dir.exists()) mocked_launch.assert_called_once() mocked_wait.assert_called_once_with(9333) self.assertIn("9333", stdout.getvalue()) self.assertIn("./.venv/bin/python Douyin.py --browser-port 9333", stdout.getvalue()) def test_main_uses_zero_argument_next_step_for_default_browser_port(self) -> None: module = importlib.import_module("login_douyin") with tempfile.TemporaryDirectory() as temp_dir: profile_dir = Path(temp_dir) / "profile" stdout = io.StringIO() with redirect_stdout(stdout): with mock.patch.object(module, "launch_browser"): with mock.patch.object(module, "wait_for_browser_debug_port"): exit_code = module.main( [ "--chrome-path", "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome", "--profile-dir", str(profile_dir), ] ) self.assertEqual(exit_code, 0) self.assertIn("./.venv/bin/python Douyin.py", stdout.getvalue()) self.assertNotIn("--browser-port 9223", stdout.getvalue()) def test_main_returns_error_when_chrome_path_missing(self) -> None: module = importlib.import_module("login_douyin") stdout = io.StringIO() with redirect_stdout(stdout): exit_code = module.main(["--chrome-path", "/tmp/does-not-exist-chrome"]) self.assertEqual(exit_code, 1) self.assertIn("Chrome", stdout.getvalue()) self.assertIn("不存在", stdout.getvalue()) def test_main_returns_error_when_debug_port_never_becomes_ready(self) -> None: module = importlib.import_module("login_douyin") with tempfile.TemporaryDirectory() as temp_dir: profile_dir = Path(temp_dir) / "profile" stdout = io.StringIO() with redirect_stdout(stdout): with mock.patch.object(module, "launch_browser"): with mock.patch.object( module, "wait_for_browser_debug_port", side_effect=RuntimeError("端口未就绪"), ): exit_code = module.main( [ "--chrome-path", "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome", "--profile-dir", str(profile_dir), ] ) self.assertEqual(exit_code, 1) self.assertIn("端口未就绪", stdout.getvalue()) if __name__ == "__main__": unittest.main()