test_scheduler_init.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. # -*- coding:utf-8 -*-
  2. """
  3. Test scheduler initialization and real functionality
  4. @author: NewFuture
  5. """
  6. import os
  7. import platform
  8. import subprocess
  9. from __init__ import unittest
  10. from ddns.scheduler import get_scheduler
  11. class TestSchedulerInit(unittest.TestCase):
  12. """Test scheduler initialization and functionality"""
  13. def test_auto_detection_returns_scheduler(self):
  14. """Test that auto detection returns a valid scheduler instance"""
  15. scheduler = get_scheduler("auto")
  16. self.assertIsNotNone(scheduler)
  17. # On Windows, should return SchtasksScheduler
  18. if platform.system().lower() == "windows":
  19. from ddns.scheduler.schtasks import SchtasksScheduler
  20. self.assertIsInstance(scheduler, SchtasksScheduler)
  21. def test_explicit_scheduler_selection(self):
  22. """Test explicit scheduler selection"""
  23. test_cases = [
  24. ("systemd", "SystemdScheduler"),
  25. ("cron", "CronScheduler"),
  26. ("launchd", "LaunchdScheduler"),
  27. ("schtasks", "SchtasksScheduler"),
  28. ]
  29. for scheduler_type, expected_class_name in test_cases:
  30. try:
  31. scheduler = get_scheduler(scheduler_type)
  32. self.assertIsNotNone(scheduler, "Failed to get scheduler for type: {}".format(scheduler_type))
  33. self.assertEqual(
  34. scheduler.__class__.__name__,
  35. expected_class_name,
  36. "Expected {} but got {} for scheduler type: {}".format(
  37. expected_class_name, scheduler.__class__.__name__, scheduler_type
  38. ),
  39. )
  40. except Exception as e:
  41. self.fail("Failed for scheduler_type {}: {}".format(scheduler_type, e))
  42. def test_invalid_scheduler_raises_error(self):
  43. """Test that invalid scheduler raises ValueError"""
  44. with self.assertRaises(ValueError):
  45. get_scheduler("invalid_scheduler")
  46. def test_auto_and_none_equivalent(self):
  47. """Test that auto and None return the same scheduler type"""
  48. auto_scheduler = get_scheduler("auto")
  49. none_scheduler = get_scheduler(None)
  50. self.assertEqual(type(auto_scheduler), type(none_scheduler))
  51. class TestSchedulerRealFunctionality(unittest.TestCase):
  52. """Test real scheduler functionality when supported on current system"""
  53. def setUp(self):
  54. """Set up test environment"""
  55. self.current_system = platform.system().lower()
  56. self.scheduler = get_scheduler("auto")
  57. self.test_ddns_args = {"dns": "debug", "ipv4": ["test.example.com"], "config": []}
  58. def _is_command_available(self, command):
  59. """Check if a command is available on the current system"""
  60. try:
  61. if self.current_system == "windows":
  62. result = subprocess.run(["where", command], capture_output=True, check=False, shell=True)
  63. else:
  64. result = subprocess.run(["which", command], capture_output=True, check=False)
  65. return result.returncode == 0
  66. except Exception:
  67. return False
  68. def _is_scheduler_available(self, scheduler_name):
  69. """Check if a scheduler is available on the current system"""
  70. try:
  71. if scheduler_name == "systemd":
  72. # Check if systemd is running and we have systemctl
  73. return (
  74. self.current_system == "linux"
  75. and os.path.exists("/proc/1/comm")
  76. and self._is_command_available("systemctl")
  77. )
  78. elif scheduler_name == "cron":
  79. # Check if cron is available
  80. return self.current_system in ["linux", "darwin"] and self._is_command_available("crontab")
  81. elif scheduler_name == "launchd":
  82. # Check if we're on macOS and launchctl is available
  83. return self.current_system == "darwin" and self._is_command_available("launchctl")
  84. elif scheduler_name == "schtasks":
  85. # Check if we're on Windows and schtasks is available
  86. return self.current_system == "windows" and self._is_command_available("schtasks")
  87. except Exception:
  88. return False
  89. return False
  90. def test_scheduler_status_call(self):
  91. """Test that get_status() works on current system"""
  92. try:
  93. status = self.scheduler.get_status()
  94. self.assertIsInstance(status, dict)
  95. # Basic keys should always be present
  96. basic_keys = ["installed", "scheduler"]
  97. for key in basic_keys:
  98. self.assertIn(key, status, "Status missing basic key: {}".format(key))
  99. # Scheduler name should be a valid string
  100. self.assertIsInstance(status["scheduler"], str)
  101. self.assertGreater(len(status["scheduler"]), 0)
  102. # Additional keys are only present when installed
  103. if status.get("installed", False):
  104. additional_keys = ["enabled"]
  105. for key in additional_keys:
  106. self.assertIn(key, status, "Status missing key for installed scheduler: {}".format(key))
  107. except Exception as e:
  108. self.fail("get_status() failed: {}".format(e))
  109. def test_scheduler_is_installed_call(self):
  110. """Test that is_installed() works on current system"""
  111. try:
  112. installed = self.scheduler.is_installed()
  113. self.assertIsInstance(installed, bool)
  114. except Exception as e:
  115. self.fail("is_installed() failed: {}".format(e))
  116. def test_scheduler_build_ddns_command(self):
  117. """Test that _build_ddns_command works correctly"""
  118. try:
  119. command = self.scheduler._build_ddns_command(self.test_ddns_args)
  120. self.assertIsInstance(command, list)
  121. command_str = " ".join(command)
  122. self.assertIn("python", command_str.lower())
  123. self.assertIn("ddns", command_str)
  124. # Should contain debug provider
  125. self.assertIn("debug", command_str)
  126. except Exception as e:
  127. self.fail("_build_ddns_command() failed: {}".format(e))
  128. @unittest.skipUnless(platform.system().lower() == "windows", "Windows-specific test")
  129. def test_windows_scheduler_real_calls(self):
  130. """Test Windows scheduler real functionality"""
  131. if not self._is_scheduler_available("schtasks"):
  132. self.skipTest("schtasks not available")
  133. from ddns.scheduler.schtasks import SchtasksScheduler
  134. scheduler = SchtasksScheduler()
  135. # Test status call
  136. status = scheduler.get_status()
  137. self.assertIsInstance(status, dict)
  138. self.assertEqual(status["scheduler"], "schtasks")
  139. # Test is_installed call
  140. installed = scheduler.is_installed()
  141. self.assertIsInstance(installed, bool)
  142. # Test command building
  143. command = scheduler._build_ddns_command(self.test_ddns_args)
  144. self.assertIsInstance(command, list)
  145. command_str = " ".join(command)
  146. self.assertIn("python", command_str.lower())
  147. @unittest.skipUnless(platform.system().lower() == "linux", "Linux-specific test")
  148. def test_systemd_scheduler_real_calls(self):
  149. """Test systemd scheduler real functionality"""
  150. if not self._is_scheduler_available("systemd"):
  151. self.skipTest("systemd not available")
  152. from ddns.scheduler.systemd import SystemdScheduler
  153. scheduler = SystemdScheduler()
  154. # Test status call
  155. status = scheduler.get_status()
  156. self.assertIsInstance(status, dict)
  157. self.assertEqual(status["scheduler"], "systemd")
  158. # Test is_installed call
  159. installed = scheduler.is_installed()
  160. self.assertIsInstance(installed, bool)
  161. @unittest.skipUnless(platform.system().lower() in ["linux", "darwin"], "Unix-specific test")
  162. def test_cron_scheduler_real_calls(self):
  163. """Test cron scheduler real functionality"""
  164. if not self._is_scheduler_available("cron"):
  165. self.skipTest("cron not available")
  166. from ddns.scheduler.cron import CronScheduler
  167. scheduler = CronScheduler()
  168. # Test status call
  169. status = scheduler.get_status()
  170. self.assertIsInstance(status, dict)
  171. self.assertEqual(status["scheduler"], "cron")
  172. # Test is_installed call
  173. installed = scheduler.is_installed()
  174. self.assertIsInstance(installed, bool)
  175. @unittest.skipUnless(platform.system().lower() == "darwin", "macOS-specific test")
  176. def test_launchd_scheduler_real_calls(self):
  177. """Test launchd scheduler real functionality"""
  178. if not self._is_scheduler_available("launchd"):
  179. self.skipTest("launchctl not available")
  180. from ddns.scheduler.launchd import LaunchdScheduler
  181. scheduler = LaunchdScheduler()
  182. # Test status call
  183. status = scheduler.get_status()
  184. self.assertIsInstance(status, dict)
  185. self.assertEqual(status["scheduler"], "launchd")
  186. # Test is_installed call
  187. installed = scheduler.is_installed()
  188. self.assertIsInstance(installed, bool)
  189. def test_scheduler_methods_exist(self):
  190. """Test that required scheduler methods exist and are callable"""
  191. required_methods = ["get_status", "is_installed", "install", "uninstall", "enable", "disable"]
  192. for method_name in required_methods:
  193. try:
  194. self.assertTrue(
  195. hasattr(self.scheduler, method_name), "Scheduler missing method: {}".format(method_name)
  196. )
  197. method = getattr(self.scheduler, method_name)
  198. self.assertTrue(callable(method), "Scheduler method not callable: {}".format(method_name))
  199. except Exception as e:
  200. self.fail("Failed for method {}: {}".format(method_name, e))
  201. def test_scheduler_safe_operations(self):
  202. """Test scheduler operations that are safe to run (won't modify system)"""
  203. # Test status (safe operation)
  204. status = self.scheduler.get_status()
  205. self.assertIsInstance(status, dict)
  206. # Test is_installed (safe operation)
  207. installed = self.scheduler.is_installed()
  208. self.assertIsInstance(installed, bool)
  209. # Test command building (safe operation)
  210. command = self.scheduler._build_ddns_command(self.test_ddns_args)
  211. self.assertIsInstance(command, list)
  212. self.assertGreater(len(command), 0)
  213. def test_scheduler_real_integration(self):
  214. """Test real scheduler integration - comprehensive test for current platform"""
  215. # Get current platform scheduler
  216. current_scheduler = get_scheduler("auto")
  217. # Test basic properties
  218. self.assertIsNotNone(current_scheduler)
  219. self.assertTrue(hasattr(current_scheduler, "get_status"))
  220. self.assertTrue(hasattr(current_scheduler, "is_installed"))
  221. # Test status method returns valid structure
  222. status = current_scheduler.get_status()
  223. self.assertIsInstance(status, dict)
  224. self.assertIn("scheduler", status)
  225. self.assertIn("installed", status)
  226. # Additional keys are only present when installed
  227. if status.get("installed", False):
  228. self.assertIn("enabled", status)
  229. # Test is_installed returns boolean
  230. installed = current_scheduler.is_installed()
  231. self.assertIsInstance(installed, bool)
  232. # Test command building with various args
  233. test_args = {
  234. "dns": "debug",
  235. "ipv4": ["test.domain.com"],
  236. "ipv6": ["test6.domain.com"],
  237. "config": ["config.json"],
  238. "ttl": 600,
  239. }
  240. command = current_scheduler._build_ddns_command(test_args)
  241. self.assertIsInstance(command, list)
  242. self.assertGreater(len(command), 0)
  243. # Verify command contains expected elements
  244. command_str = " ".join(command)
  245. self.assertIn("python", command_str.lower())
  246. self.assertIn("debug", command_str)
  247. self.assertIn("test.domain.com", command_str)
  248. if __name__ == "__main__":
  249. unittest.main()