mcp-proxy-adapter 4.1.1__py3-none-any.whl → 6.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mcp_proxy_adapter/__main__.py +12 -0
- mcp_proxy_adapter/api/app.py +254 -33
- mcp_proxy_adapter/api/handlers.py +32 -6
- mcp_proxy_adapter/api/middleware/__init__.py +36 -30
- mcp_proxy_adapter/api/middleware/command_permission_middleware.py +148 -0
- mcp_proxy_adapter/api/middleware/error_handling.py +9 -0
- mcp_proxy_adapter/api/middleware/factory.py +243 -0
- mcp_proxy_adapter/api/middleware/logging.py +32 -6
- mcp_proxy_adapter/api/middleware/protocol_middleware.py +135 -0
- mcp_proxy_adapter/api/middleware/transport_middleware.py +122 -0
- mcp_proxy_adapter/api/middleware/unified_security.py +152 -0
- mcp_proxy_adapter/api/middleware/user_info_middleware.py +83 -0
- mcp_proxy_adapter/commands/__init__.py +19 -4
- mcp_proxy_adapter/commands/auth_validation_command.py +408 -0
- mcp_proxy_adapter/commands/base.py +66 -32
- mcp_proxy_adapter/commands/builtin_commands.py +95 -0
- mcp_proxy_adapter/commands/catalog_manager.py +838 -0
- mcp_proxy_adapter/commands/cert_monitor_command.py +620 -0
- mcp_proxy_adapter/commands/certificate_management_command.py +608 -0
- mcp_proxy_adapter/commands/command_registry.py +711 -354
- mcp_proxy_adapter/commands/dependency_manager.py +245 -0
- mcp_proxy_adapter/commands/echo_command.py +81 -0
- mcp_proxy_adapter/commands/health_command.py +7 -0
- mcp_proxy_adapter/commands/help_command.py +21 -14
- mcp_proxy_adapter/commands/hooks.py +200 -167
- mcp_proxy_adapter/commands/key_management_command.py +506 -0
- mcp_proxy_adapter/commands/load_command.py +176 -0
- mcp_proxy_adapter/commands/plugins_command.py +235 -0
- mcp_proxy_adapter/commands/protocol_management_command.py +232 -0
- mcp_proxy_adapter/commands/proxy_registration_command.py +409 -0
- mcp_proxy_adapter/commands/reload_command.py +48 -50
- mcp_proxy_adapter/commands/result.py +1 -0
- mcp_proxy_adapter/commands/role_test_command.py +141 -0
- mcp_proxy_adapter/commands/roles_management_command.py +697 -0
- mcp_proxy_adapter/commands/security_command.py +488 -0
- mcp_proxy_adapter/commands/ssl_setup_command.py +483 -0
- mcp_proxy_adapter/commands/token_management_command.py +529 -0
- mcp_proxy_adapter/commands/transport_management_command.py +144 -0
- mcp_proxy_adapter/commands/unload_command.py +158 -0
- mcp_proxy_adapter/config.py +159 -2
- mcp_proxy_adapter/core/app_factory.py +326 -0
- mcp_proxy_adapter/core/auth_validator.py +606 -0
- mcp_proxy_adapter/core/certificate_utils.py +827 -0
- mcp_proxy_adapter/core/client_security.py +384 -0
- mcp_proxy_adapter/core/config_converter.py +405 -0
- mcp_proxy_adapter/core/config_validator.py +218 -0
- mcp_proxy_adapter/core/logging.py +19 -3
- mcp_proxy_adapter/core/mtls_asgi.py +156 -0
- mcp_proxy_adapter/core/mtls_asgi_app.py +187 -0
- mcp_proxy_adapter/core/protocol_manager.py +235 -0
- mcp_proxy_adapter/core/proxy_client.py +602 -0
- mcp_proxy_adapter/core/proxy_registration.py +522 -0
- mcp_proxy_adapter/core/role_utils.py +426 -0
- mcp_proxy_adapter/core/security_adapter.py +370 -0
- mcp_proxy_adapter/core/security_factory.py +239 -0
- mcp_proxy_adapter/core/security_integration.py +277 -0
- mcp_proxy_adapter/core/server_adapter.py +345 -0
- mcp_proxy_adapter/core/server_engine.py +364 -0
- mcp_proxy_adapter/core/settings.py +1 -0
- mcp_proxy_adapter/core/ssl_utils.py +233 -0
- mcp_proxy_adapter/core/transport_manager.py +292 -0
- mcp_proxy_adapter/core/unified_config_adapter.py +579 -0
- mcp_proxy_adapter/custom_openapi.py +22 -11
- mcp_proxy_adapter/examples/README.md +230 -97
- mcp_proxy_adapter/examples/README_EN.md +258 -0
- mcp_proxy_adapter/examples/SECURITY_TESTING.md +455 -0
- mcp_proxy_adapter/examples/__pycache__/security_configurations.cpython-312.pyc +0 -0
- mcp_proxy_adapter/examples/__pycache__/security_test_client.cpython-312.pyc +0 -0
- mcp_proxy_adapter/examples/basic_framework/configs/http_auth.json +37 -0
- mcp_proxy_adapter/examples/basic_framework/configs/http_simple.json +23 -0
- mcp_proxy_adapter/examples/basic_framework/configs/https_auth.json +39 -0
- mcp_proxy_adapter/examples/basic_framework/configs/https_simple.json +25 -0
- mcp_proxy_adapter/examples/basic_framework/configs/mtls_no_roles.json +39 -0
- mcp_proxy_adapter/examples/basic_framework/configs/mtls_with_roles.json +45 -0
- mcp_proxy_adapter/examples/basic_framework/main.py +63 -0
- mcp_proxy_adapter/examples/basic_framework/roles.json +21 -0
- mcp_proxy_adapter/examples/cert_config.json +9 -0
- mcp_proxy_adapter/examples/certs/admin.crt +32 -0
- mcp_proxy_adapter/examples/certs/admin.key +52 -0
- mcp_proxy_adapter/examples/certs/admin_cert.pem +21 -0
- mcp_proxy_adapter/examples/certs/admin_key.pem +28 -0
- mcp_proxy_adapter/examples/certs/ca_cert.pem +23 -0
- mcp_proxy_adapter/examples/certs/ca_cert.srl +1 -0
- mcp_proxy_adapter/examples/certs/ca_key.pem +28 -0
- mcp_proxy_adapter/examples/certs/cert_config.json +9 -0
- mcp_proxy_adapter/examples/certs/client.crt +32 -0
- mcp_proxy_adapter/examples/certs/client.key +52 -0
- mcp_proxy_adapter/examples/certs/client_admin.crt +32 -0
- mcp_proxy_adapter/examples/certs/client_admin.key +52 -0
- mcp_proxy_adapter/examples/certs/client_user.crt +32 -0
- mcp_proxy_adapter/examples/certs/client_user.key +52 -0
- mcp_proxy_adapter/examples/certs/guest_cert.pem +21 -0
- mcp_proxy_adapter/examples/certs/guest_key.pem +28 -0
- mcp_proxy_adapter/examples/certs/mcp_proxy_adapter_ca_ca.crt +23 -0
- mcp_proxy_adapter/examples/certs/proxy_cert.pem +21 -0
- mcp_proxy_adapter/examples/certs/proxy_key.pem +28 -0
- mcp_proxy_adapter/examples/certs/readonly.crt +32 -0
- mcp_proxy_adapter/examples/certs/readonly.key +52 -0
- mcp_proxy_adapter/examples/certs/readonly_cert.pem +21 -0
- mcp_proxy_adapter/examples/certs/readonly_key.pem +28 -0
- mcp_proxy_adapter/examples/certs/server.crt +32 -0
- mcp_proxy_adapter/examples/certs/server.key +52 -0
- mcp_proxy_adapter/examples/certs/server_cert.pem +32 -0
- mcp_proxy_adapter/examples/certs/server_key.pem +52 -0
- mcp_proxy_adapter/examples/certs/test_ca_ca.crt +20 -0
- mcp_proxy_adapter/examples/certs/user.crt +32 -0
- mcp_proxy_adapter/examples/certs/user.key +52 -0
- mcp_proxy_adapter/examples/certs/user_cert.pem +21 -0
- mcp_proxy_adapter/examples/certs/user_key.pem +28 -0
- mcp_proxy_adapter/examples/client_configs/api_key_client.json +13 -0
- mcp_proxy_adapter/examples/client_configs/basic_auth_client.json +13 -0
- mcp_proxy_adapter/examples/client_configs/certificate_client.json +22 -0
- mcp_proxy_adapter/examples/client_configs/jwt_client.json +15 -0
- mcp_proxy_adapter/examples/client_configs/no_auth_client.json +9 -0
- mcp_proxy_adapter/examples/commands/__init__.py +1 -0
- mcp_proxy_adapter/examples/create_certificates_simple.py +307 -0
- mcp_proxy_adapter/examples/debug_request_state.py +144 -0
- mcp_proxy_adapter/examples/debug_role_chain.py +205 -0
- mcp_proxy_adapter/examples/demo_client.py +341 -0
- mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +99 -0
- mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +106 -0
- mcp_proxy_adapter/examples/full_application/configs/http_auth.json +37 -0
- mcp_proxy_adapter/examples/full_application/configs/http_simple.json +23 -0
- mcp_proxy_adapter/examples/full_application/configs/https_auth.json +39 -0
- mcp_proxy_adapter/examples/full_application/configs/https_simple.json +25 -0
- mcp_proxy_adapter/examples/full_application/configs/mtls_no_roles.json +39 -0
- mcp_proxy_adapter/examples/full_application/configs/mtls_with_roles.json +45 -0
- mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +97 -0
- mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +95 -0
- mcp_proxy_adapter/examples/full_application/main.py +138 -0
- mcp_proxy_adapter/examples/full_application/roles.json +21 -0
- mcp_proxy_adapter/examples/generate_all_certificates.py +429 -0
- mcp_proxy_adapter/examples/generate_certificates.py +121 -0
- mcp_proxy_adapter/examples/keys/ca_key.pem +28 -0
- mcp_proxy_adapter/examples/keys/mcp_proxy_adapter_ca_ca.key +28 -0
- mcp_proxy_adapter/examples/keys/test_ca_ca.key +28 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log +220 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.1 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.2 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.3 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.4 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.5 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log +220 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.1 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.2 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.3 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.4 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.5 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log +2 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.1 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.2 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.3 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.4 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.5 +1 -0
- mcp_proxy_adapter/examples/proxy_registration_example.py +401 -0
- mcp_proxy_adapter/examples/roles.json +38 -0
- mcp_proxy_adapter/examples/run_example.py +81 -0
- mcp_proxy_adapter/examples/run_security_tests.py +326 -0
- mcp_proxy_adapter/examples/run_security_tests_fixed.py +300 -0
- mcp_proxy_adapter/examples/security_test_client.py +743 -0
- mcp_proxy_adapter/examples/server_configs/config_basic_http.json +204 -0
- mcp_proxy_adapter/examples/server_configs/config_http_token.json +238 -0
- mcp_proxy_adapter/examples/server_configs/config_https.json +215 -0
- mcp_proxy_adapter/examples/server_configs/config_https_token.json +231 -0
- mcp_proxy_adapter/examples/server_configs/config_mtls.json +215 -0
- mcp_proxy_adapter/examples/server_configs/config_proxy_registration.json +250 -0
- mcp_proxy_adapter/examples/server_configs/config_simple.json +46 -0
- mcp_proxy_adapter/examples/server_configs/roles.json +38 -0
- mcp_proxy_adapter/examples/test_examples.py +344 -0
- mcp_proxy_adapter/examples/universal_client.py +628 -0
- mcp_proxy_adapter/main.py +186 -0
- mcp_proxy_adapter/utils/config_generator.py +639 -0
- mcp_proxy_adapter/version.py +2 -1
- mcp_proxy_adapter-6.1.0.dist-info/METADATA +205 -0
- mcp_proxy_adapter-6.1.0.dist-info/RECORD +193 -0
- mcp_proxy_adapter-6.1.0.dist-info/entry_points.txt +2 -0
- {mcp_proxy_adapter-4.1.1.dist-info → mcp_proxy_adapter-6.1.0.dist-info}/licenses/LICENSE +2 -2
- mcp_proxy_adapter/api/middleware/auth.py +0 -146
- mcp_proxy_adapter/api/middleware/rate_limit.py +0 -152
- mcp_proxy_adapter/commands/reload_settings_command.py +0 -125
- mcp_proxy_adapter/examples/__init__.py +0 -7
- mcp_proxy_adapter/examples/basic_server/README.md +0 -60
- mcp_proxy_adapter/examples/basic_server/__init__.py +0 -7
- mcp_proxy_adapter/examples/basic_server/basic_custom_settings.json +0 -39
- mcp_proxy_adapter/examples/basic_server/config.json +0 -35
- mcp_proxy_adapter/examples/basic_server/custom_settings_example.py +0 -238
- mcp_proxy_adapter/examples/basic_server/server.py +0 -103
- mcp_proxy_adapter/examples/custom_commands/README.md +0 -127
- mcp_proxy_adapter/examples/custom_commands/__init__.py +0 -27
- mcp_proxy_adapter/examples/custom_commands/advanced_hooks.py +0 -250
- mcp_proxy_adapter/examples/custom_commands/auto_commands/__init__.py +0 -6
- mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_echo_command.py +0 -103
- mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_info_command.py +0 -111
- mcp_proxy_adapter/examples/custom_commands/config.json +0 -35
- mcp_proxy_adapter/examples/custom_commands/custom_health_command.py +0 -169
- mcp_proxy_adapter/examples/custom_commands/custom_help_command.py +0 -215
- mcp_proxy_adapter/examples/custom_commands/custom_openapi_generator.py +0 -76
- mcp_proxy_adapter/examples/custom_commands/custom_settings.json +0 -96
- mcp_proxy_adapter/examples/custom_commands/custom_settings_manager.py +0 -241
- mcp_proxy_adapter/examples/custom_commands/data_transform_command.py +0 -135
- mcp_proxy_adapter/examples/custom_commands/echo_command.py +0 -122
- mcp_proxy_adapter/examples/custom_commands/hooks.py +0 -230
- mcp_proxy_adapter/examples/custom_commands/intercept_command.py +0 -123
- mcp_proxy_adapter/examples/custom_commands/manual_echo_command.py +0 -103
- mcp_proxy_adapter/examples/custom_commands/server.py +0 -228
- mcp_proxy_adapter/examples/custom_commands/test_hooks.py +0 -176
- mcp_proxy_adapter/examples/deployment/README.md +0 -49
- mcp_proxy_adapter/examples/deployment/__init__.py +0 -7
- mcp_proxy_adapter/examples/deployment/config.development.json +0 -8
- mcp_proxy_adapter/examples/deployment/config.json +0 -29
- mcp_proxy_adapter/examples/deployment/config.production.json +0 -12
- mcp_proxy_adapter/examples/deployment/config.staging.json +0 -11
- mcp_proxy_adapter/examples/deployment/docker-compose.yml +0 -31
- mcp_proxy_adapter/examples/deployment/run.sh +0 -43
- mcp_proxy_adapter/examples/deployment/run_docker.sh +0 -84
- mcp_proxy_adapter/schemas/base_schema.json +0 -114
- mcp_proxy_adapter/schemas/openapi_schema.json +0 -314
- mcp_proxy_adapter/tests/__init__.py +0 -0
- mcp_proxy_adapter/tests/api/__init__.py +0 -3
- mcp_proxy_adapter/tests/api/test_cmd_endpoint.py +0 -115
- mcp_proxy_adapter/tests/api/test_custom_openapi.py +0 -617
- mcp_proxy_adapter/tests/api/test_handlers.py +0 -522
- mcp_proxy_adapter/tests/api/test_middleware.py +0 -340
- mcp_proxy_adapter/tests/api/test_schemas.py +0 -546
- mcp_proxy_adapter/tests/api/test_tool_integration.py +0 -531
- mcp_proxy_adapter/tests/commands/__init__.py +0 -3
- mcp_proxy_adapter/tests/commands/test_config_command.py +0 -211
- mcp_proxy_adapter/tests/commands/test_echo_command.py +0 -127
- mcp_proxy_adapter/tests/commands/test_help_command.py +0 -136
- mcp_proxy_adapter/tests/conftest.py +0 -131
- mcp_proxy_adapter/tests/functional/__init__.py +0 -3
- mcp_proxy_adapter/tests/functional/test_api.py +0 -253
- mcp_proxy_adapter/tests/integration/__init__.py +0 -3
- mcp_proxy_adapter/tests/integration/test_cmd_integration.py +0 -129
- mcp_proxy_adapter/tests/integration/test_integration.py +0 -255
- mcp_proxy_adapter/tests/performance/__init__.py +0 -3
- mcp_proxy_adapter/tests/performance/test_performance.py +0 -189
- mcp_proxy_adapter/tests/stubs/__init__.py +0 -10
- mcp_proxy_adapter/tests/stubs/echo_command.py +0 -104
- mcp_proxy_adapter/tests/test_api_endpoints.py +0 -271
- mcp_proxy_adapter/tests/test_api_handlers.py +0 -289
- mcp_proxy_adapter/tests/test_base_command.py +0 -123
- mcp_proxy_adapter/tests/test_batch_requests.py +0 -117
- mcp_proxy_adapter/tests/test_command_registry.py +0 -281
- mcp_proxy_adapter/tests/test_config.py +0 -127
- mcp_proxy_adapter/tests/test_utils.py +0 -65
- mcp_proxy_adapter/tests/unit/__init__.py +0 -3
- mcp_proxy_adapter/tests/unit/test_base_command.py +0 -436
- mcp_proxy_adapter/tests/unit/test_config.py +0 -217
- mcp_proxy_adapter-4.1.1.dist-info/METADATA +0 -200
- mcp_proxy_adapter-4.1.1.dist-info/RECORD +0 -110
- {mcp_proxy_adapter-4.1.1.dist-info → mcp_proxy_adapter-6.1.0.dist-info}/WHEEL +0 -0
- {mcp_proxy_adapter-4.1.1.dist-info → mcp_proxy_adapter-6.1.0.dist-info}/top_level.txt +0 -0
@@ -1,436 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Tests for base command module.
|
3
|
-
|
4
|
-
This module contains comprehensive tests for the base Command class
|
5
|
-
to ensure 90%+ code coverage.
|
6
|
-
"""
|
7
|
-
|
8
|
-
import pytest
|
9
|
-
import inspect
|
10
|
-
from unittest.mock import Mock, patch, MagicMock, AsyncMock
|
11
|
-
from typing import Dict, Any
|
12
|
-
|
13
|
-
from mcp_proxy_adapter.commands.base import Command
|
14
|
-
from mcp_proxy_adapter.commands.result import SuccessResult, ErrorResult
|
15
|
-
from mcp_proxy_adapter.core.errors import (
|
16
|
-
ValidationError, InvalidParamsError, NotFoundError,
|
17
|
-
TimeoutError, CommandError, InternalError
|
18
|
-
)
|
19
|
-
|
20
|
-
|
21
|
-
class MockResultClass:
|
22
|
-
"""Mock result class for testing."""
|
23
|
-
def to_dict(self):
|
24
|
-
return {"status": "success", "data": "test_data"}
|
25
|
-
|
26
|
-
@classmethod
|
27
|
-
def get_schema(cls):
|
28
|
-
return {"type": "object", "properties": {"data": {"type": "string"}}}
|
29
|
-
|
30
|
-
|
31
|
-
class TestCommand(Command):
|
32
|
-
"""Test command class for testing."""
|
33
|
-
name = "test_command"
|
34
|
-
result_class = MockResultClass
|
35
|
-
|
36
|
-
async def execute(self, **kwargs):
|
37
|
-
return SuccessResult(data=kwargs)
|
38
|
-
|
39
|
-
|
40
|
-
def test_success_result():
|
41
|
-
"""Test success result creation."""
|
42
|
-
result = SuccessResult(data="test_data")
|
43
|
-
assert result.data == "test_data"
|
44
|
-
result_dict = result.to_dict()
|
45
|
-
assert result_dict["success"] is True
|
46
|
-
|
47
|
-
|
48
|
-
def test_error_result():
|
49
|
-
"""Test error result creation."""
|
50
|
-
result = ErrorResult(message="Test error", code=400)
|
51
|
-
assert result.message == "Test error"
|
52
|
-
assert result.code == 400
|
53
|
-
|
54
|
-
|
55
|
-
class TestCommandClass:
|
56
|
-
"""Test cases for Command class."""
|
57
|
-
|
58
|
-
@pytest.mark.asyncio
|
59
|
-
async def test_execute(self):
|
60
|
-
"""Test execute method."""
|
61
|
-
command = TestCommand()
|
62
|
-
result = await command.execute(test_param="value")
|
63
|
-
assert isinstance(result, SuccessResult)
|
64
|
-
|
65
|
-
@pytest.mark.asyncio
|
66
|
-
async def test_run(self):
|
67
|
-
"""Test run method (with validation)."""
|
68
|
-
with patch('mcp_proxy_adapter.commands.base.registry', create=True) as mock_registry:
|
69
|
-
mock_registry.get_priority_command.return_value = TestCommand
|
70
|
-
mock_registry.has_instance.return_value = False
|
71
|
-
|
72
|
-
# Mock the command execution to avoid registry lookup
|
73
|
-
with patch.object(TestCommand, 'execute', return_value=SuccessResult(data={"value": "test_value"})):
|
74
|
-
result = await TestCommand.run(value="test_value")
|
75
|
-
assert isinstance(result, SuccessResult)
|
76
|
-
assert result.data == {"value": "test_value"}
|
77
|
-
|
78
|
-
def test_get_schema(self):
|
79
|
-
"""Test get_schema method."""
|
80
|
-
schema = TestCommand.get_schema()
|
81
|
-
assert schema["type"] == "object"
|
82
|
-
assert "properties" in schema
|
83
|
-
|
84
|
-
def test_get_result_schema(self):
|
85
|
-
"""Test get_result_schema method."""
|
86
|
-
schema = TestCommand.get_result_schema()
|
87
|
-
assert schema["type"] == "object"
|
88
|
-
assert "properties" in schema
|
89
|
-
|
90
|
-
def test_get_param_info(self):
|
91
|
-
"""Test get_param_info method."""
|
92
|
-
param_info = TestCommand.get_param_info()
|
93
|
-
assert isinstance(param_info, dict)
|
94
|
-
|
95
|
-
def test_validate_params_none(self):
|
96
|
-
"""Test validate_params with None."""
|
97
|
-
params = TestCommand.validate_params(None)
|
98
|
-
assert params == {}
|
99
|
-
|
100
|
-
def test_validate_params_empty_dict(self):
|
101
|
-
"""Test validate_params with empty dict."""
|
102
|
-
params = TestCommand.validate_params({})
|
103
|
-
assert params == {}
|
104
|
-
|
105
|
-
def test_validate_params_with_none_values(self):
|
106
|
-
"""Test validate_params with None values."""
|
107
|
-
params = TestCommand.validate_params({"param1": None, "param2": "value"})
|
108
|
-
assert "param1" not in params
|
109
|
-
assert params["param2"] == "value"
|
110
|
-
|
111
|
-
def test_validate_params_with_empty_strings(self):
|
112
|
-
"""Test validate_params with empty strings."""
|
113
|
-
params = TestCommand.validate_params({"param1": "", "param2": "null", "param3": "value"})
|
114
|
-
assert "param1" not in params
|
115
|
-
assert "param2" not in params
|
116
|
-
assert params["param3"] == "value"
|
117
|
-
|
118
|
-
def test_validate_params_with_cmdname_none(self):
|
119
|
-
"""Test validate_params with cmdname parameter."""
|
120
|
-
params = TestCommand.validate_params({"cmdname": None, "other": "value"})
|
121
|
-
assert params["cmdname"] is None
|
122
|
-
assert params["other"] == "value"
|
123
|
-
|
124
|
-
def test_validate_params_copy_input(self):
|
125
|
-
"""Test that validate_params doesn't modify input."""
|
126
|
-
input_params = {"param1": "value1", "param2": None}
|
127
|
-
result = TestCommand.validate_params(input_params)
|
128
|
-
assert input_params == {"param1": "value1", "param2": None} # Input unchanged
|
129
|
-
assert "param2" not in result # Result filtered
|
130
|
-
|
131
|
-
@pytest.mark.asyncio
|
132
|
-
async def test_run_with_hooks_skip_processing(self):
|
133
|
-
"""Test run method when hooks skip standard processing."""
|
134
|
-
with patch('mcp_proxy_adapter.commands.base.hooks') as mock_hooks:
|
135
|
-
mock_hooks.execute_before_hooks.return_value = Mock(standard_processing=False)
|
136
|
-
|
137
|
-
result = await TestCommand.run(test_param="value")
|
138
|
-
assert isinstance(result, SuccessResult)
|
139
|
-
assert result.data == {"test_param": "value"}
|
140
|
-
|
141
|
-
@pytest.mark.asyncio
|
142
|
-
async def test_run_command_not_found(self):
|
143
|
-
"""Test run method when command not found."""
|
144
|
-
with patch('mcp_proxy_adapter.commands.base.registry', create=True) as mock_registry:
|
145
|
-
mock_registry.get_priority_command.return_value = None
|
146
|
-
|
147
|
-
result = await TestCommand.run(test_param="value")
|
148
|
-
assert isinstance(result, ErrorResult)
|
149
|
-
assert "not found" in result.message
|
150
|
-
|
151
|
-
@pytest.mark.asyncio
|
152
|
-
async def test_run_with_registry_instance(self):
|
153
|
-
"""Test run method with existing registry instance."""
|
154
|
-
mock_command = Mock()
|
155
|
-
mock_command.execute = AsyncMock(return_value=SuccessResult(data="test"))
|
156
|
-
|
157
|
-
with patch('mcp_proxy_adapter.commands.base.registry', create=True) as mock_registry:
|
158
|
-
mock_registry.get_priority_command.return_value = TestCommand
|
159
|
-
mock_registry.has_instance.return_value = True
|
160
|
-
mock_registry.get_command_instance.return_value = mock_command
|
161
|
-
|
162
|
-
result = await TestCommand.run(test_param="value")
|
163
|
-
assert isinstance(result, SuccessResult)
|
164
|
-
|
165
|
-
@pytest.mark.asyncio
|
166
|
-
async def test_run_validation_error(self):
|
167
|
-
"""Test run method with validation error."""
|
168
|
-
with patch('mcp_proxy_adapter.commands.base.registry', create=True) as mock_registry:
|
169
|
-
mock_registry.get_priority_command.return_value = TestCommand
|
170
|
-
mock_registry.has_instance.return_value = False
|
171
|
-
|
172
|
-
# Mock TestCommand to raise ValidationError
|
173
|
-
with patch.object(TestCommand, 'execute', side_effect=ValidationError("Invalid params")):
|
174
|
-
result = await TestCommand.run(test_param="value")
|
175
|
-
assert isinstance(result, ErrorResult)
|
176
|
-
assert "Invalid params" in result.message
|
177
|
-
|
178
|
-
@pytest.mark.asyncio
|
179
|
-
async def test_run_invalid_params_error(self):
|
180
|
-
"""Test run method with invalid params error."""
|
181
|
-
with patch('mcp_proxy_adapter.commands.base.registry', create=True) as mock_registry:
|
182
|
-
mock_registry.get_priority_command.return_value = TestCommand
|
183
|
-
mock_registry.has_instance.return_value = False
|
184
|
-
|
185
|
-
with patch.object(TestCommand, 'execute', side_effect=InvalidParamsError("Invalid params")):
|
186
|
-
result = await TestCommand.run(test_param="value")
|
187
|
-
assert isinstance(result, ErrorResult)
|
188
|
-
assert "Invalid params" in result.message
|
189
|
-
|
190
|
-
@pytest.mark.asyncio
|
191
|
-
async def test_run_not_found_error(self):
|
192
|
-
"""Test run method with not found error."""
|
193
|
-
with patch('mcp_proxy_adapter.commands.base.registry', create=True) as mock_registry:
|
194
|
-
mock_registry.get_priority_command.return_value = TestCommand
|
195
|
-
mock_registry.has_instance.return_value = False
|
196
|
-
|
197
|
-
with patch.object(TestCommand, 'execute', side_effect=NotFoundError("Not found")):
|
198
|
-
result = await TestCommand.run(test_param="value")
|
199
|
-
assert isinstance(result, ErrorResult)
|
200
|
-
assert "Not found" in result.message
|
201
|
-
|
202
|
-
@pytest.mark.asyncio
|
203
|
-
async def test_run_timeout_error(self):
|
204
|
-
"""Test run method with timeout error."""
|
205
|
-
with patch('mcp_proxy_adapter.commands.base.registry', create=True) as mock_registry:
|
206
|
-
mock_registry.get_priority_command.return_value = TestCommand
|
207
|
-
mock_registry.has_instance.return_value = False
|
208
|
-
|
209
|
-
with patch.object(TestCommand, 'execute', side_effect=TimeoutError("Timeout")):
|
210
|
-
result = await TestCommand.run(test_param="value")
|
211
|
-
assert isinstance(result, ErrorResult)
|
212
|
-
assert "Timeout" in result.message
|
213
|
-
|
214
|
-
@pytest.mark.asyncio
|
215
|
-
async def test_run_command_error(self):
|
216
|
-
"""Test run method with command error."""
|
217
|
-
with patch('mcp_proxy_adapter.commands.base.registry', create=True) as mock_registry:
|
218
|
-
mock_registry.get_priority_command.return_value = TestCommand
|
219
|
-
mock_registry.has_instance.return_value = False
|
220
|
-
|
221
|
-
with patch.object(TestCommand, 'execute', side_effect=CommandError("Command error")):
|
222
|
-
result = await TestCommand.run(test_param="value")
|
223
|
-
assert isinstance(result, ErrorResult)
|
224
|
-
assert "Command error" in result.message
|
225
|
-
|
226
|
-
@pytest.mark.asyncio
|
227
|
-
async def test_run_unexpected_exception(self):
|
228
|
-
"""Test run method with unexpected exception."""
|
229
|
-
with patch('mcp_proxy_adapter.commands.base.registry', create=True) as mock_registry:
|
230
|
-
mock_registry.get_priority_command.return_value = TestCommand
|
231
|
-
mock_registry.has_instance.return_value = False
|
232
|
-
|
233
|
-
with patch.object(TestCommand, 'execute', side_effect=Exception("Unexpected error")):
|
234
|
-
result = await TestCommand.run(test_param="value")
|
235
|
-
assert isinstance(result, ErrorResult)
|
236
|
-
assert "Command execution error" in result.message
|
237
|
-
|
238
|
-
@pytest.mark.asyncio
|
239
|
-
async def test_run_with_none_kwargs(self):
|
240
|
-
"""Test run method with None kwargs."""
|
241
|
-
with patch('mcp_proxy_adapter.commands.base.registry', create=True) as mock_registry:
|
242
|
-
mock_registry.get_priority_command.return_value = TestCommand
|
243
|
-
mock_registry.has_instance.return_value = False
|
244
|
-
|
245
|
-
result = await TestCommand.run()
|
246
|
-
assert isinstance(result, SuccessResult)
|
247
|
-
|
248
|
-
def test_get_metadata(self):
|
249
|
-
"""Test get_metadata method."""
|
250
|
-
metadata = TestCommand.get_metadata()
|
251
|
-
assert isinstance(metadata, dict)
|
252
|
-
assert "name" in metadata
|
253
|
-
assert "summary" in metadata
|
254
|
-
assert "params" in metadata
|
255
|
-
|
256
|
-
def test_get_metadata_with_schema(self):
|
257
|
-
"""Test get_metadata method with schema."""
|
258
|
-
with patch.object(TestCommand, 'get_param_info') as mock_param_info:
|
259
|
-
mock_param_info.return_value = {
|
260
|
-
"param1": {"type": "string", "description": "Test param"}
|
261
|
-
}
|
262
|
-
|
263
|
-
metadata = TestCommand.get_metadata()
|
264
|
-
assert "params" in metadata
|
265
|
-
assert "param1" in metadata["params"]
|
266
|
-
|
267
|
-
def test_generate_examples(self):
|
268
|
-
"""Test _generate_examples method."""
|
269
|
-
params = {
|
270
|
-
"param1": {"type": "string", "description": "Test param"}
|
271
|
-
}
|
272
|
-
|
273
|
-
examples = TestCommand._generate_examples(params)
|
274
|
-
assert isinstance(examples, list)
|
275
|
-
assert len(examples) > 0
|
276
|
-
|
277
|
-
def test_generate_examples_empty_params(self):
|
278
|
-
"""Test _generate_examples with empty params."""
|
279
|
-
examples = TestCommand._generate_examples({})
|
280
|
-
assert isinstance(examples, list)
|
281
|
-
|
282
|
-
def test_get_param_info_with_annotations(self):
|
283
|
-
"""Test get_param_info with type annotations."""
|
284
|
-
class AnnotatedCommand(Command):
|
285
|
-
name = "annotated_command"
|
286
|
-
result_class = MockResultClass
|
287
|
-
|
288
|
-
async def execute(self, param1: str, param2: int = 42):
|
289
|
-
return SuccessResult(data={"param1": param1, "param2": param2})
|
290
|
-
|
291
|
-
param_info = AnnotatedCommand.get_param_info()
|
292
|
-
assert "param1" in param_info
|
293
|
-
assert "param2" in param_info
|
294
|
-
assert param_info["param1"]["required"] is True
|
295
|
-
assert param_info["param2"]["required"] is False
|
296
|
-
|
297
|
-
def test_get_param_info_without_annotations(self):
|
298
|
-
"""Test get_param_info without type annotations."""
|
299
|
-
class NoAnnotationCommand(Command):
|
300
|
-
name = "no_annotation_command"
|
301
|
-
result_class = MockResultClass
|
302
|
-
|
303
|
-
async def execute(self, param1, param2=42):
|
304
|
-
return SuccessResult(data={"param1": param1, "param2": param2})
|
305
|
-
|
306
|
-
param_info = NoAnnotationCommand.get_param_info()
|
307
|
-
assert "param1" in param_info
|
308
|
-
assert "param2" in param_info
|
309
|
-
assert param_info["param1"]["required"] is True
|
310
|
-
assert param_info["param2"]["required"] is False
|
311
|
-
|
312
|
-
def test_get_result_schema_with_result_class(self):
|
313
|
-
"""Test get_result_schema with result class."""
|
314
|
-
class MockResultClassWithSchema:
|
315
|
-
@classmethod
|
316
|
-
def get_schema(cls):
|
317
|
-
return {"type": "object", "properties": {"data": {"type": "string"}}}
|
318
|
-
|
319
|
-
class CommandWithResultClass(Command):
|
320
|
-
name = "command_with_result"
|
321
|
-
result_class = MockResultClassWithSchema
|
322
|
-
|
323
|
-
async def execute(self, **kwargs):
|
324
|
-
return SuccessResult(data="test")
|
325
|
-
|
326
|
-
schema = CommandWithResultClass.get_result_schema()
|
327
|
-
assert schema["type"] == "object"
|
328
|
-
assert "properties" in schema
|
329
|
-
|
330
|
-
def test_get_result_schema_without_result_class(self):
|
331
|
-
"""Test get_result_schema without result class."""
|
332
|
-
class CommandWithoutResultClass(Command):
|
333
|
-
name = "command_without_result"
|
334
|
-
|
335
|
-
async def execute(self, **kwargs):
|
336
|
-
return SuccessResult(data="test")
|
337
|
-
|
338
|
-
schema = CommandWithoutResultClass.get_result_schema()
|
339
|
-
assert schema == {}
|
340
|
-
|
341
|
-
def test_command_name_generation(self):
|
342
|
-
"""Test command name generation from class name."""
|
343
|
-
class TestCommandName(Command):
|
344
|
-
result_class = MockResultClass
|
345
|
-
|
346
|
-
async def execute(self, **kwargs):
|
347
|
-
return SuccessResult(data="test")
|
348
|
-
|
349
|
-
# Test that name is generated from class name
|
350
|
-
# The name will be generated dynamically in the run method
|
351
|
-
# So we test that the class can be instantiated
|
352
|
-
command = TestCommandName()
|
353
|
-
assert command is not None
|
354
|
-
|
355
|
-
def test_command_name_override(self):
|
356
|
-
"""Test command name override."""
|
357
|
-
class CustomNamedCommand(Command):
|
358
|
-
name = "custom_name"
|
359
|
-
result_class = MockResultClass
|
360
|
-
|
361
|
-
async def execute(self, **kwargs):
|
362
|
-
return SuccessResult(data="test")
|
363
|
-
|
364
|
-
assert CustomNamedCommand.name == "custom_name"
|
365
|
-
|
366
|
-
|
367
|
-
class TestCommandEdgeCases:
|
368
|
-
"""Test edge cases for Command class."""
|
369
|
-
|
370
|
-
def test_validate_params_with_various_none_values(self):
|
371
|
-
"""Test validate_params with various None-like values."""
|
372
|
-
params = {
|
373
|
-
"null_str": "null",
|
374
|
-
"none_str": "none",
|
375
|
-
"empty_str": "",
|
376
|
-
"real_none": None,
|
377
|
-
"valid_value": "test"
|
378
|
-
}
|
379
|
-
|
380
|
-
result = TestCommand.validate_params(params)
|
381
|
-
assert "null_str" not in result
|
382
|
-
assert "none_str" not in result
|
383
|
-
assert "empty_str" not in result
|
384
|
-
assert "real_none" not in result
|
385
|
-
assert result["valid_value"] == "test"
|
386
|
-
|
387
|
-
def test_validate_params_case_insensitive(self):
|
388
|
-
"""Test validate_params case insensitive handling."""
|
389
|
-
params = {
|
390
|
-
"null_upper": "NULL",
|
391
|
-
"none_upper": "NONE",
|
392
|
-
"valid_value": "test"
|
393
|
-
}
|
394
|
-
|
395
|
-
result = TestCommand.validate_params(params)
|
396
|
-
assert "null_upper" not in result
|
397
|
-
assert "none_upper" not in result
|
398
|
-
assert result["valid_value"] == "test"
|
399
|
-
|
400
|
-
@pytest.mark.asyncio
|
401
|
-
async def test_run_with_complex_hook_context(self):
|
402
|
-
"""Test run method with complex hook context."""
|
403
|
-
mock_hook_context = Mock()
|
404
|
-
mock_hook_context.standard_processing = False
|
405
|
-
|
406
|
-
with patch('mcp_proxy_adapter.commands.base.hooks') as mock_hooks:
|
407
|
-
mock_hooks.execute_before_hooks.return_value = mock_hook_context
|
408
|
-
|
409
|
-
result = await TestCommand.run(complex_param={"nested": "value"})
|
410
|
-
assert isinstance(result, SuccessResult)
|
411
|
-
|
412
|
-
@pytest.mark.asyncio
|
413
|
-
async def test_run_with_after_hooks(self):
|
414
|
-
"""Test run method with after hooks execution."""
|
415
|
-
with patch('mcp_proxy_adapter.commands.base.registry', create=True) as mock_registry:
|
416
|
-
mock_registry.get_priority_command.return_value = TestCommand
|
417
|
-
mock_registry.has_instance.return_value = False
|
418
|
-
|
419
|
-
with patch('mcp_proxy_adapter.commands.base.hooks') as mock_hooks:
|
420
|
-
mock_hooks.execute_before_hooks.return_value = Mock(standard_processing=True)
|
421
|
-
|
422
|
-
result = await TestCommand.run(test_param="value")
|
423
|
-
|
424
|
-
# Verify after hooks were called
|
425
|
-
mock_hooks.execute_after_hooks.assert_called_once()
|
426
|
-
|
427
|
-
def test_get_metadata_with_examples(self):
|
428
|
-
"""Test get_metadata method with examples generation."""
|
429
|
-
with patch.object(TestCommand, 'get_param_info') as mock_param_info:
|
430
|
-
mock_param_info.return_value = {
|
431
|
-
"param1": {"type": "string", "description": "Test param"}
|
432
|
-
}
|
433
|
-
|
434
|
-
metadata = TestCommand.get_metadata()
|
435
|
-
assert "examples" in metadata
|
436
|
-
assert isinstance(metadata["examples"], list)
|
@@ -1,217 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Unit tests for configuration module.
|
3
|
-
"""
|
4
|
-
|
5
|
-
import json
|
6
|
-
import os
|
7
|
-
import tempfile
|
8
|
-
from typing import Generator
|
9
|
-
|
10
|
-
import pytest
|
11
|
-
|
12
|
-
from mcp_proxy_adapter.config import Config
|
13
|
-
|
14
|
-
|
15
|
-
@pytest.fixture
|
16
|
-
def temp_config_file() -> Generator[str, None, None]:
|
17
|
-
"""
|
18
|
-
Creates temporary configuration file for tests.
|
19
|
-
|
20
|
-
Returns:
|
21
|
-
Path to temporary configuration file.
|
22
|
-
"""
|
23
|
-
# Create temporary file
|
24
|
-
fd, path = tempfile.mkstemp(suffix=".json")
|
25
|
-
|
26
|
-
# Write test configuration
|
27
|
-
test_config = {
|
28
|
-
"server": {
|
29
|
-
"host": "127.0.0.1",
|
30
|
-
"port": 8000
|
31
|
-
},
|
32
|
-
"logging": {
|
33
|
-
"level": "DEBUG",
|
34
|
-
"file": "test.log"
|
35
|
-
},
|
36
|
-
"test_section": {
|
37
|
-
"test_key": "test_value",
|
38
|
-
"nested": {
|
39
|
-
"key1": "value1",
|
40
|
-
"key2": 42
|
41
|
-
}
|
42
|
-
}
|
43
|
-
}
|
44
|
-
|
45
|
-
with os.fdopen(fd, "w") as f:
|
46
|
-
json.dump(test_config, f)
|
47
|
-
|
48
|
-
yield path
|
49
|
-
|
50
|
-
# Remove temporary file after tests
|
51
|
-
os.unlink(path)
|
52
|
-
|
53
|
-
|
54
|
-
@pytest.mark.unit
|
55
|
-
def test_config_load_from_file(temp_config_file: str):
|
56
|
-
"""
|
57
|
-
Test loading configuration from file.
|
58
|
-
|
59
|
-
Args:
|
60
|
-
temp_config_file: Path to temporary configuration file.
|
61
|
-
"""
|
62
|
-
config = Config(temp_config_file)
|
63
|
-
|
64
|
-
# Check loaded values
|
65
|
-
assert config.get("server.host") == "127.0.0.1"
|
66
|
-
assert config.get("server.port") == 8000
|
67
|
-
assert config.get("logging.level") == "DEBUG"
|
68
|
-
assert config.get("logging.file") == "test.log"
|
69
|
-
assert config.get("test_section.test_key") == "test_value"
|
70
|
-
|
71
|
-
|
72
|
-
@pytest.mark.unit
|
73
|
-
def test_config_get_nested_values(temp_config_file: str):
|
74
|
-
"""
|
75
|
-
Test getting nested values from configuration.
|
76
|
-
|
77
|
-
Args:
|
78
|
-
temp_config_file: Path to temporary configuration file.
|
79
|
-
"""
|
80
|
-
config = Config(temp_config_file)
|
81
|
-
|
82
|
-
# Get nested values
|
83
|
-
assert config.get("test_section.nested.key1") == "value1"
|
84
|
-
assert config.get("test_section.nested.key2") == 42
|
85
|
-
|
86
|
-
|
87
|
-
@pytest.mark.unit
|
88
|
-
def test_config_get_with_default(temp_config_file: str):
|
89
|
-
"""
|
90
|
-
Test getting configuration values with default.
|
91
|
-
|
92
|
-
Args:
|
93
|
-
temp_config_file: Path to temporary configuration file.
|
94
|
-
"""
|
95
|
-
config = Config(temp_config_file)
|
96
|
-
|
97
|
-
# Get non-existent values with defaults
|
98
|
-
assert config.get("non_existent", default="default") == "default"
|
99
|
-
assert config.get("server.non_existent", default=123) == 123
|
100
|
-
assert config.get("test_section.nested.non_existent", default=False) is False
|
101
|
-
|
102
|
-
|
103
|
-
@pytest.mark.unit
|
104
|
-
def test_config_get_without_default(temp_config_file: str):
|
105
|
-
"""
|
106
|
-
Test getting non-existent configuration values without default.
|
107
|
-
|
108
|
-
Args:
|
109
|
-
temp_config_file: Path to temporary configuration file.
|
110
|
-
"""
|
111
|
-
config = Config(temp_config_file)
|
112
|
-
|
113
|
-
# Get non-existent values without defaults
|
114
|
-
assert config.get("non_existent") is None
|
115
|
-
assert config.get("server.non_existent") is None
|
116
|
-
assert config.get("test_section.nested.non_existent") is None
|
117
|
-
|
118
|
-
|
119
|
-
@pytest.mark.unit
|
120
|
-
def test_config_set_value(temp_config_file: str):
|
121
|
-
"""
|
122
|
-
Test setting configuration values.
|
123
|
-
|
124
|
-
Args:
|
125
|
-
temp_config_file: Path to temporary configuration file.
|
126
|
-
"""
|
127
|
-
config = Config(temp_config_file)
|
128
|
-
|
129
|
-
# Set values
|
130
|
-
config.set("server.host", "localhost")
|
131
|
-
config.set("logging.level", "INFO")
|
132
|
-
config.set("new_section.new_key", "new_value")
|
133
|
-
|
134
|
-
# Check set values
|
135
|
-
assert config.get("server.host") == "localhost"
|
136
|
-
assert config.get("logging.level") == "INFO"
|
137
|
-
assert config.get("new_section.new_key") == "new_value"
|
138
|
-
|
139
|
-
|
140
|
-
@pytest.mark.unit
|
141
|
-
def test_config_save_and_load(temp_config_file: str):
|
142
|
-
"""
|
143
|
-
Test saving and loading configuration.
|
144
|
-
|
145
|
-
Args:
|
146
|
-
temp_config_file: Path to temporary configuration file.
|
147
|
-
"""
|
148
|
-
# Create and modify configuration
|
149
|
-
config1 = Config(temp_config_file)
|
150
|
-
config1.set("server.host", "localhost")
|
151
|
-
config1.set("new_section.new_key", "new_value")
|
152
|
-
|
153
|
-
# Save configuration
|
154
|
-
config1.save()
|
155
|
-
|
156
|
-
# Load configuration again
|
157
|
-
config2 = Config(temp_config_file)
|
158
|
-
|
159
|
-
# Check values
|
160
|
-
assert config2.get("server.host") == "localhost"
|
161
|
-
assert config2.get("new_section.new_key") == "new_value"
|
162
|
-
|
163
|
-
|
164
|
-
@pytest.mark.unit
|
165
|
-
def test_config_load_updated_file(temp_config_file: str):
|
166
|
-
"""
|
167
|
-
Test loading updated configuration from file.
|
168
|
-
|
169
|
-
Args:
|
170
|
-
temp_config_file: Path to temporary configuration file.
|
171
|
-
"""
|
172
|
-
# Load configuration
|
173
|
-
config = Config(temp_config_file)
|
174
|
-
original_host = config.get("server.host")
|
175
|
-
|
176
|
-
# Modify file directly
|
177
|
-
with open(temp_config_file, "r+") as f:
|
178
|
-
data = json.load(f)
|
179
|
-
data["server"]["host"] = "new_host"
|
180
|
-
f.seek(0)
|
181
|
-
f.truncate()
|
182
|
-
json.dump(data, f)
|
183
|
-
|
184
|
-
# Create new config instance to load updated file
|
185
|
-
updated_config = Config(temp_config_file)
|
186
|
-
|
187
|
-
# Check that value was updated
|
188
|
-
assert updated_config.get("server.host") == "new_host"
|
189
|
-
assert updated_config.get("server.host") != original_host
|
190
|
-
|
191
|
-
|
192
|
-
@pytest.mark.unit
|
193
|
-
def test_config_access_nested_sections(temp_config_file: str):
|
194
|
-
"""
|
195
|
-
Test accessing nested configuration sections directly.
|
196
|
-
|
197
|
-
Args:
|
198
|
-
temp_config_file: Path to temporary configuration file.
|
199
|
-
"""
|
200
|
-
config = Config(temp_config_file)
|
201
|
-
|
202
|
-
# Get parent key then access nested keys
|
203
|
-
server = config.get("server")
|
204
|
-
logging = config.get("logging")
|
205
|
-
test_section = config.get("test_section")
|
206
|
-
|
207
|
-
# Check sections
|
208
|
-
assert isinstance(server, dict)
|
209
|
-
assert isinstance(logging, dict)
|
210
|
-
assert isinstance(test_section, dict)
|
211
|
-
|
212
|
-
assert server["host"] == "127.0.0.1"
|
213
|
-
assert server["port"] == 8000
|
214
|
-
assert logging["level"] == "DEBUG"
|
215
|
-
assert logging["file"] == "test.log"
|
216
|
-
assert test_section["test_key"] == "test_value"
|
217
|
-
assert test_section["nested"]["key1"] == "value1"
|