Files
DDNS/tests/test_scheduler_base.py
06fceed0f7 feat(cache): add configurable cache max age (#709)
* feat(cache): add configurable cache max age

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* test(cache): cover cache max age integration paths

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* test(cache): fix portable regression test formatting

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* style(test): format cache max age run test

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Future New (WEILAI LIU) <weilail@microsoft.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-08-06 17:59:40 +08:00

233 lines
8.3 KiB
Python

# -*- coding:utf-8 -*-
"""
Unit tests for ddns.scheduler._base module
@author: NewFuture
"""
from __init__ import patch, unittest
from ddns.scheduler._base import BaseScheduler
class MockScheduler(BaseScheduler):
"""Mock scheduler for testing base functionality"""
SCHEDULER_NAME = "mock"
def get_status(self):
return {"scheduler": "mock", "installed": False, "enabled": None, "interval": None}
def is_installed(self):
return False
def install(self, interval, ddns_args=None):
return True
def uninstall(self):
return True
def enable(self):
return True
def disable(self):
return True
class TestBaseScheduler(unittest.TestCase):
"""Test BaseScheduler functionality"""
def setUp(self):
"""Set up test fixtures"""
self.scheduler = MockScheduler()
def test_scheduler_name_property(self):
"""Test scheduler name property"""
self.assertEqual(self.scheduler.SCHEDULER_NAME, "mock")
def test_abstract_methods_exist(self):
"""Test that all abstract methods are implemented"""
required_methods = ["get_status", "is_installed", "install", "uninstall", "enable", "disable"]
for method_name in required_methods:
self.assertTrue(hasattr(self.scheduler, method_name))
method = getattr(self.scheduler, method_name)
self.assertTrue(callable(method))
def test_build_ddns_command_basic(self):
"""Test _build_ddns_command with basic arguments"""
ddns_args = {"dns": "debug", "ipv4": ["test.example.com"]}
command = self.scheduler._build_ddns_command(ddns_args)
self.assertIsInstance(command, list)
command_str = " ".join(command)
self.assertIn("python", command_str.lower())
self.assertIn("-m", command)
self.assertIn("ddns", command)
self.assertIn("--dns", command)
self.assertIn("debug", command)
self.assertIn("--ipv4", command)
self.assertIn("test.example.com", command)
def test_build_ddns_command_with_config(self):
"""Test _build_ddns_command with config files"""
ddns_args = {"dns": "cloudflare", "config": ["config1.json", "config2.json"]}
command = self.scheduler._build_ddns_command(ddns_args)
self.assertIn("--config", command)
self.assertIn("config1.json", command)
self.assertIn("config2.json", command)
def test_build_ddns_command_with_lists(self):
"""Test _build_ddns_command with list arguments"""
ddns_args = {
"dns": "debug",
"ipv4": ["domain1.com", "domain2.com"],
"ipv6": ["ipv6domain.com"],
"proxy": ["http://proxy1:8080", "http://proxy2:8080"],
}
command = self.scheduler._build_ddns_command(ddns_args)
self.assertIn("domain1.com", command)
self.assertIn("domain2.com", command)
self.assertIn("ipv6domain.com", command)
self.assertIn("http://proxy1:8080", command)
def test_build_ddns_command_with_boolean_flags(self):
"""Test _build_ddns_command with boolean flags"""
ddns_args = {"dns": "debug", "ipv4": ["test.com"], "debug": True, "cache": True}
command = self.scheduler._build_ddns_command(ddns_args)
self.assertIn("--debug", command)
self.assertIn("true", command)
self.assertIn("--cache", command)
def test_build_ddns_command_filters_debug_false(self):
"""Test _build_ddns_command filters out debug=False"""
ddns_args = {"dns": "debug", "ipv4": ["test.com"], "debug": False, "cache": True} # This should be filtered out
command = self.scheduler._build_ddns_command(ddns_args)
command_str = " ".join(command)
self.assertNotIn("--debug false", command_str)
self.assertIn("--cache", command)
self.assertIn("true", command)
def test_build_ddns_command_with_single_values(self):
"""Test _build_ddns_command with single value arguments"""
ddns_args = {"dns": "alidns", "id": "test_id", "token": "test_token", "ttl": 600, "log_level": "INFO"}
command = self.scheduler._build_ddns_command(ddns_args)
self.assertIn("--id", command)
self.assertIn("test_id", command)
self.assertIn("--token", command)
self.assertIn("test_token", command)
self.assertIn("--ttl", command)
self.assertIn("600", command)
def test_build_ddns_command_with_cache_max_age(self):
"""Test _build_ddns_command emits the cache max age option."""
command = self.scheduler._build_ddns_command({"cache_max_age": 86400})
self.assertIn("--cache_max_age", command)
self.assertIn("86400", command)
def test_build_ddns_command_excludes_none_values(self):
"""Test _build_ddns_command behavior with None values"""
ddns_args = {"dns": "debug", "ipv4": ["test.com"], "ttl": None, "line": None}
command = self.scheduler._build_ddns_command(ddns_args)
# The current implementation includes None values as strings
# This test verifies the actual behavior
self.assertIn("--ttl", command)
self.assertIn("None", command)
self.assertIn("--line", command)
def test_build_ddns_command_excludes_empty_lists(self):
"""Test _build_ddns_command excludes empty lists"""
ddns_args = {"dns": "debug", "ipv4": ["test.com"], "ipv6": [], "config": []}
command = self.scheduler._build_ddns_command(ddns_args)
# Should not include empty list arguments
self.assertIn("--ipv4", command)
self.assertNotIn("--ipv6", command)
self.assertNotIn("--config", command)
@patch("sys.executable", "/usr/bin/python3.9")
def test_build_ddns_command_uses_current_python(self):
"""Test _build_ddns_command uses current Python executable"""
ddns_args = {"dns": "debug", "ipv4": ["test.com"]}
command = self.scheduler._build_ddns_command(ddns_args)
# Should use sys.executable for Python path
command_str = " ".join(command)
self.assertIn("python", command_str.lower())
def test_build_ddns_command_with_special_characters(self):
"""Test _build_ddns_command handles special characters"""
ddns_args = {"dns": "debug", "ipv4": ["test-domain.example.com"], "token": "test_token_with_special_chars!@#"}
command = self.scheduler._build_ddns_command(ddns_args)
self.assertIsInstance(command, list)
self.assertIn("test-domain.example.com", command)
self.assertIn("test_token_with_special_chars!@#", command)
def test_all_scheduler_interface_methods(self):
"""Test that scheduler implements all interface methods correctly"""
# Test get_status
status = self.scheduler.get_status()
self.assertIsInstance(status, dict)
self.assertIn("scheduler", status)
# Test is_installed
installed = self.scheduler.is_installed()
self.assertIsInstance(installed, bool)
# Test install
result = self.scheduler.install(5, {"dns": "debug"})
self.assertIsInstance(result, bool)
# Test uninstall
result = self.scheduler.uninstall()
self.assertIsInstance(result, bool)
# Test enable
result = self.scheduler.enable()
self.assertIsInstance(result, bool)
# Test disable
result = self.scheduler.disable()
self.assertIsInstance(result, bool)
def test_quote_command_array(self):
"""Test _quote_command_array method"""
# Test basic functionality
cmd_array = ["python", "script.py"]
result = self.scheduler._quote_command_array(cmd_array)
self.assertEqual(result, "python script.py")
# Test with spaces
cmd_array = ["python", "script with spaces.py", "normal_arg"]
result = self.scheduler._quote_command_array(cmd_array)
self.assertEqual(result, 'python "script with spaces.py" normal_arg')
# Test with multiple spaced arguments
cmd_array = ["python", "-m", "ddns", "--config", "config file.json"]
result = self.scheduler._quote_command_array(cmd_array)
self.assertEqual(result, 'python -m ddns --config "config file.json"')
# Test empty array
result = self.scheduler._quote_command_array([])
self.assertEqual(result, "")
if __name__ == "__main__":
unittest.main()