8Regression tests for :mod:`oct.mcp.config` — ``McpConfig`` dataclass
13- Verify default values, JSON loading, and field clamping.
14- Verify environment variable overrides.
15- Verify file-size guard and malformed JSON fallback.
22 L3 — assertion details
27- Config files are written to ``tmp_path``; no real config is modified.
30from __future__
import annotations
34from pathlib
import Path
54 assert cfg.profile ==
"default"
57 assert McpConfig().rate_limit_per_minute == 30
60 assert McpConfig().timeout_default_seconds == 60
63 assert McpConfig().timeout_test_seconds == 300
66 assert McpConfig().max_output_bytes == 1_048_576
72 assert McpConfig().auto_approve_read_tools
is True
75 assert McpConfig().dry_run_default_for_writes
is True
78 assert McpConfig().redaction_always_on
is False
81 assert McpConfig().trust_repo_config
is False
87 assert McpConfig().audit_log_max_files == 20
90 assert McpConfig().audit_log_max_size_mb == 5.0
99 assert "default" in VALID_MCP_PROFILES
102 assert "strict" in VALID_MCP_PROFILES
105 assert "airgapped" in VALID_MCP_PROFILES
108 assert "admin" not in VALID_MCP_PROFILES
117 cfg = load_config(tmp_path /
"nonexistent.json")
118 assert cfg.profile ==
"default"
119 assert cfg.safe_mode
is False
122 cfg = load_config(tmp_path /
"nonexistent.json")
123 assert isinstance(cfg, McpConfig)
132 p = tmp_path /
"config.json"
133 p.write_text(json.dumps({
"profile":
"strict"}))
135 assert cfg.profile ==
"strict"
138 p = tmp_path /
"config.json"
139 p.write_text(json.dumps({
"rate_limit_per_minute": 10}))
141 assert cfg.rate_limit_per_minute == 10
144 p = tmp_path /
"config.json"
145 p.write_text(json.dumps({
"timeout_default_seconds": 90}))
147 assert cfg.timeout_default_seconds == 90
150 p = tmp_path /
"config.json"
151 p.write_text(json.dumps({
"trust_repo_config":
True}))
153 assert cfg.trust_repo_config
is True
156 p = tmp_path /
"config.json"
157 custom_path =
"/custom/audit.log"
158 p.write_text(json.dumps({
"audit_log_path": custom_path}))
160 assert cfg.audit_log_path == custom_path
163 p = tmp_path /
"config.json"
164 p.write_text(json.dumps({
"audit_log_max_files": 5}))
166 assert cfg.audit_log_max_files == 5
169 p = tmp_path /
"config.json"
170 p.write_text(json.dumps({
"audit_log_max_size_mb": 10.0}))
172 assert cfg.audit_log_max_size_mb == 10.0
181 p = tmp_path /
"config.json"
182 p.write_text(json.dumps({
"rate_limit_per_minute": 0}))
184 assert cfg.rate_limit_per_minute == 1
187 p = tmp_path /
"config.json"
188 p.write_text(json.dumps({
"rate_limit_per_minute": 9999}))
190 assert cfg.rate_limit_per_minute == 1000
193 p = tmp_path /
"config.json"
194 p.write_text(json.dumps({
"timeout_default_seconds": 1}))
196 assert cfg.timeout_default_seconds == 5
199 p = tmp_path /
"config.json"
200 p.write_text(json.dumps({
"timeout_default_seconds": 9999}))
202 assert cfg.timeout_default_seconds == 300
205 p = tmp_path /
"config.json"
206 p.write_text(json.dumps({
"max_jobs": 0}))
208 assert cfg.max_jobs == 1
211 p = tmp_path /
"config.json"
212 p.write_text(json.dumps({
"max_jobs": 999}))
214 assert cfg.max_jobs == 16
217 p = tmp_path /
"config.json"
218 p.write_text(json.dumps({
"audit_log_max_files": 999}))
220 assert cfg.audit_log_max_files == 100
223 p = tmp_path /
"config.json"
224 p.write_text(json.dumps({
"audit_log_max_size_mb": 500.0}))
226 assert cfg.audit_log_max_size_mb == 100.0
235 p = tmp_path /
"config.json"
236 p.write_text(json.dumps({
"profile":
"strict",
"unknown_key":
"value"}))
238 assert cfg.profile ==
"strict"
241 p = tmp_path /
"config.json"
242 p.write_text(json.dumps({
"profile":
"admin"}))
244 assert cfg.profile ==
"default"
247 p = tmp_path /
"config.json"
248 p.write_text(
"{not valid json}")
250 assert cfg.profile ==
"default"
253 p = tmp_path /
"config.json"
254 p.write_text(
"[1, 2, 3]")
256 assert cfg.profile ==
"default"
260 p = tmp_path /
"config.json"
261 p.write_text(json.dumps({
"auto_approve_read_tools":
"true"}))
263 assert cfg.auto_approve_read_tools
is True
266 p = tmp_path /
"config.json"
268 p.write_bytes(b
'{"profile": "strict"}' + b
" " * (_CONFIG_MAX_BYTES + 100))
270 assert cfg.profile ==
"default"
279 monkeypatch.setenv(
"OCT_MCP_SAFE_MODE",
"1")
280 cfg = load_config(tmp_path /
"nonexistent.json")
281 assert cfg.safe_mode
is True
284 monkeypatch.setenv(
"OCT_MCP_SAFE_MODE",
"0")
285 cfg = load_config(tmp_path /
"nonexistent.json")
286 assert cfg.safe_mode
is False
289 monkeypatch.setenv(
"OCT_TRUST_REPO_CONFIG",
"1")
290 cfg = load_config(tmp_path /
"nonexistent.json")
291 assert cfg.trust_repo_config
is True
294 monkeypatch.setenv(
"OCT_MCP_PROFILE",
"strict")
295 cfg = load_config(tmp_path /
"nonexistent.json")
296 assert cfg.profile ==
"strict"
299 monkeypatch.setenv(
"OCT_MCP_PROFILE",
"airgapped")
300 cfg = load_config(tmp_path /
"nonexistent.json")
301 assert cfg.profile ==
"airgapped"
304 monkeypatch.setenv(
"OCT_MCP_PROFILE",
"superadmin")
305 cfg = load_config(tmp_path /
"nonexistent.json")
306 assert cfg.profile ==
"default"
309 p = tmp_path /
"config.json"
310 p.write_text(json.dumps({
"profile":
"strict"}))
311 monkeypatch.setenv(
"OCT_MCP_PROFILE",
"airgapped")
313 assert cfg.profile ==
"airgapped"
test_rate_limit_clamped_max(self, Path tmp_path)
test_rate_limit_clamped_min(self, Path tmp_path)
test_timeout_clamped_min(self, Path tmp_path)
test_audit_log_max_files_clamped(self, Path tmp_path)
test_max_jobs_clamped_min(self, Path tmp_path)
test_max_jobs_clamped_max(self, Path tmp_path)
test_timeout_clamped_max(self, Path tmp_path)
test_audit_log_max_size_mb_clamped(self, Path tmp_path)
test_profile_env_airgapped(self, Path tmp_path, monkeypatch)
test_env_overrides_file_value(self, Path tmp_path, monkeypatch)
test_profile_env_strict(self, Path tmp_path, monkeypatch)
test_profile_env_invalid_ignored(self, Path tmp_path, monkeypatch)
test_safe_mode_env_zero_not_applied(self, Path tmp_path, monkeypatch)
test_trust_repo_config_env(self, Path tmp_path, monkeypatch)
test_safe_mode_env(self, Path tmp_path, monkeypatch)
test_returns_mcpconfig_instance(self, Path tmp_path)
test_returns_defaults_when_no_file(self, Path tmp_path)
test_file_too_large_returns_defaults(self, Path tmp_path)
test_boolean_string_not_applied(self, Path tmp_path)
test_unknown_field_ignored(self, Path tmp_path)
test_json_array_not_applied(self, Path tmp_path)
test_invalid_profile_ignored(self, Path tmp_path)
test_malformed_json_returns_defaults(self, Path tmp_path)
test_loads_profile(self, Path tmp_path)
test_loads_rate_limit(self, Path tmp_path)
test_loads_audit_log_path(self, Path tmp_path)
test_loads_timeout(self, Path tmp_path)
test_loads_trust_repo_config(self, Path tmp_path)
test_loads_audit_log_max_files(self, Path tmp_path)
test_loads_audit_log_max_size_mb(self, Path tmp_path)
test_profile_default(self)
test_max_output_bytes_default(self)
test_rate_limit_default(self)
test_trust_repo_config_default(self)
test_safe_mode_default(self)
test_audit_log_max_size_mb_default(self)
test_timeout_test_default(self)
test_timeout_default(self)
test_auto_approve_read_tools_default(self)
test_audit_log_max_files_default(self)
test_redaction_always_on_default(self)
test_dry_run_default(self)
test_max_jobs_default(self)
test_does_not_contain_admin(self)
test_contains_strict(self)
test_contains_default(self)
test_contains_airgapped(self)