Files
2026-07-13 12:24:33 +08:00

312 lines
9.9 KiB
Python

# SPDX-License-Identifier: Apache-2.0
"""
Test script for LMCache Controller Configuration
This script tests the ControllerConfig class and its functionality:
- Loading from environment variables
- Loading from YAML file
- Command-line parameter overrides
- Global singleton pattern
"""
# Standard
import os
import tempfile
# First Party
from lmcache.logging import init_logger
from lmcache.v1.cache_controller.config import (
ControllerConfig,
controller_get_or_create_config,
load_controller_config_with_overrides,
override_controller_config_from_dict,
)
logger = init_logger(__name__)
def test_from_env():
"""Test loading configuration from environment variables"""
logger.info("=" * 80)
logger.info("Testing: Loading from environment variables")
logger.info("=" * 80)
# Set some environment variables
os.environ["LMCACHE_CONTROLLER_CONTROLLER_PORT"] = "9001"
os.environ["LMCACHE_CONTROLLER_HEALTH_CHECK_INTERVAL"] = "30"
config = ControllerConfig.from_env()
config.validate()
config.log_config()
# Verify values
assert config.controller_port == 9001
assert config.health_check_interval == 30
logger.info("✓ Environment variable loading test passed")
def test_from_file():
"""Test loading configuration from YAML file"""
logger.info("=" * 80)
logger.info("Testing: Loading from YAML file")
logger.info("=" * 80)
# Create a temporary YAML config file
config_content = """
controller_port: 9002
health_check_interval: 60
"""
with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f:
f.write(config_content)
config_file = f.name
try:
config = ControllerConfig.from_file(config_file)
config.validate()
config.log_config()
# Verify values
assert config.controller_port == 9002
assert config.health_check_interval == 60
logger.info("✓ File loading test passed")
finally:
os.unlink(config_file)
def test_override_from_dict():
"""Test overriding configuration with dictionary"""
logger.info("=" * 80)
logger.info("Testing: Override configuration with dictionary")
logger.info("=" * 80)
config = ControllerConfig.from_env()
# Override with dictionary
overrides = {
"controller_port": 9003,
"health_check_interval": 45,
}
override_controller_config_from_dict(config, overrides)
config.validate()
config.log_config()
# Verify overrides
assert config.controller_port == 9003
assert config.health_check_interval == 45
logger.info("✓ Dictionary override test passed")
def test_singleton_pattern():
"""Test thread-safe singleton pattern"""
logger.info("=" * 80)
logger.info("Testing: Singleton pattern")
logger.info("=" * 80)
# Clear any existing instance using the new reset method
controller_get_or_create_config.reset()
# Set environment variable for config file
os.environ["LMCACHE_CONTROLLER_CONTROLLER_PORT"] = "9004"
# Get config instance multiple times
config1 = controller_get_or_create_config()
config2 = controller_get_or_create_config()
# Verify they are the same instance
assert config1 is config2
assert config1.controller_port == 9004
# Test reset functionality
controller_get_or_create_config.reset()
os.environ["LMCACHE_CONTROLLER_CONTROLLER_PORT"] = "9005"
config3 = controller_get_or_create_config()
assert config3.controller_port == 9005
assert config3 is not config1 # Should be a new instance after reset
logger.info("✓ Singleton pattern test passed")
def test_to_from_dict():
"""Test dictionary serialization/deserialization"""
logger.info("=" * 80)
logger.info("Testing: Dictionary serialization")
logger.info("=" * 80)
config = ControllerConfig.from_env()
config.controller_port = 9005
# Convert to dictionary
config_dict = config.to_dict()
# Create new config from dictionary
new_config = ControllerConfig.from_dict(config_dict)
# Verify they are equivalent
assert new_config.controller_port == config.controller_port
logger.info("✓ Dictionary serialization test passed")
def test_to_from_json():
"""Test JSON serialization/deserialization"""
logger.info("=" * 80)
logger.info("Testing: JSON serialization")
logger.info("=" * 80)
config = ControllerConfig.from_env()
config.controller_port = 9006
# Convert to JSON
json_str = config.to_json()
# Create new config from JSON
new_config = ControllerConfig.from_json(json_str)
# Verify they are equivalent
assert new_config.controller_port == config.controller_port
logger.info("✓ JSON serialization test passed")
def test_controller_monitor_ports_json_parsing():
"""Test that controller_monitor_ports JSON string is correctly parsed as dict"""
logger.info("=" * 80)
logger.info("Testing: controller_monitor_ports JSON parsing")
logger.info("=" * 80)
# Test 1: From environment variable (string JSON)
original_env = os.environ.get("LMCACHE_CONTROLLER_CONTROLLER_MONITOR_PORTS")
os.environ["LMCACHE_CONTROLLER_CONTROLLER_MONITOR_PORTS"] = (
'{"pull": 8300, "reply": 8400}'
)
try:
config = ControllerConfig.from_env()
config.validate()
# Check that controller_monitor_ports is a dict, not a string
expected_type_msg = (
f"Expected dict but got {type(config.controller_monitor_ports)}: "
f"{config.controller_monitor_ports}"
)
assert isinstance(config.controller_monitor_ports, dict), expected_type_msg
assert config.controller_monitor_ports["pull"] == 8300
assert config.controller_monitor_ports["reply"] == 8400
logger.info("✓ Environment variable JSON parsing test passed")
# Test 2: From file with YAML string
config_content = """
controller_monitor_ports: '{"pull": 8500, "reply": 8600}'
"""
with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f:
f.write(config_content)
config_file = f.name
try:
config2 = ControllerConfig.from_file(config_file)
config2.validate()
expected_type_msg2 = (
f"Expected dict but got {type(config2.controller_monitor_ports)}: "
f"{config2.controller_monitor_ports}"
)
assert isinstance(config2.controller_monitor_ports, dict), (
expected_type_msg2
)
assert config2.controller_monitor_ports["pull"] == 8500
assert config2.controller_monitor_ports["reply"] == 8600
logger.info("✓ File YAML JSON parsing test passed")
# Test 3: Dictionary override with JSON string
config3 = ControllerConfig.from_env()
override_dict = {
"controller_monitor_ports": '{"pull": 8700, "reply": 8800}'
}
override_controller_config_from_dict(config3, override_dict)
expected_type_msg3 = (
f"Expected dict but got {type(config3.controller_monitor_ports)}: "
f"{config3.controller_monitor_ports}"
)
assert isinstance(config3.controller_monitor_ports, dict), (
expected_type_msg3
)
assert config3.controller_monitor_ports["pull"] == 8700
assert config3.controller_monitor_ports["reply"] == 8800
logger.info("✓ Dictionary override JSON parsing test passed")
# Test 4: Dictionary override with actual dict
config4 = ControllerConfig.from_env()
override_dict2 = {"controller_monitor_ports": {"pull": 8900, "reply": 9000}}
override_controller_config_from_dict(config4, override_dict2)
expected_type_msg4 = (
f"Expected dict but got {type(config4.controller_monitor_ports)}: "
f"{config4.controller_monitor_ports}"
)
assert isinstance(config4.controller_monitor_ports, dict), (
expected_type_msg4
)
assert config4.controller_monitor_ports["pull"] == 8900
assert config4.controller_monitor_ports["reply"] == 9000
logger.info("✓ Dictionary override with dict test passed")
finally:
os.unlink(config_file)
finally:
# Restore original environment variable
if original_env is not None:
os.environ["LMCACHE_CONTROLLER_CONTROLLER_MONITOR_PORTS"] = original_env
else:
os.environ.pop("LMCACHE_CONTROLLER_CONTROLLER_MONITOR_PORTS", None)
logger.info("✓ controller_monitor_ports JSON parsing test passed")
def test_load_controller_config_with_overrides():
"""
Test loading controller config with overrides,
especially for controller_monitor_ports
"""
logger.info("=" * 80)
logger.info("Testing: load_controller_config_with_overrides")
logger.info("=" * 80)
# Test with monitor ports as JSON string in overrides
overrides = {
"controller_host": "localhost",
"controller_port": 8000,
"controller_monitor_ports": '{"pull": 8300, "reply": 8400}',
}
# Load config without config file (from env with overrides)
config = load_controller_config_with_overrides(overrides=overrides)
# Verify values
assert config.controller_host == "localhost"
assert config.controller_port == 8000
expected_type_msg = (
f"Expected dict but got {type(config.controller_monitor_ports)}: "
f"{config.controller_monitor_ports}"
)
assert isinstance(config.controller_monitor_ports, dict), expected_type_msg
assert config.controller_monitor_ports["pull"] == 8300
assert config.controller_monitor_ports["reply"] == 8400
# Log config to see the output
config.log_config()
logger.info("✓ load_controller_config_with_overrides test passed")