fix(config): emit background-ops advisory logs once per process

`_get_background_operations_enabled()` was emitting three advisory log
lines (1 INFO + 2 deprecation WARNINGs) on every call. Because
`get_settings()` is intentionally non-cached and runs on every MCP tool
invocation via `get_client()`, the "Automatically enabled background
operations for semantic search in multi-user mode" INFO line was
firing per-request — 569 entries/hour in one production tenant.

Gate the three log emissions behind a module-level
`_bg_ops_advisories_logged` flag, mirroring the existing
`_warn_missing_secret_once` precedent in
`vector/webhook_receiver.py`. The boolean-derivation path stays
unchanged, so the `Settings` value remains fresh per call.

Extends the autouse `_reload_dynaconf_after_test` fixture to reset the
new flag between tests, and adds two regression tests that call
`get_settings()` five times and assert each advisory fires exactly once.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Chris Coutinho
2026-05-16 13:07:50 +02:00
co-authored by Claude Opus 4.7
parent b6bb3218bc
commit 79ea4e9e21
3 changed files with 118 additions and 22 deletions
+3 -2
View File
@@ -16,6 +16,7 @@ def _reload_dynaconf_after_test():
validation should call _reload_config() explicitly.
"""
yield
from nextcloud_mcp_server.config import _dynaconf
from nextcloud_mcp_server import config as _config
_dynaconf.reload()
_config._dynaconf.reload()
_config._bg_ops_advisories_logged = False
+79
View File
@@ -698,6 +698,85 @@ class TestConfigurationConsolidation:
# Verify background operations were auto-enabled
assert settings.enable_offline_access is True
def test_auto_enable_info_log_emitted_at_most_once(self, caplog):
"""Auto-enable INFO advisory must fire once per process, not per get_settings() call.
Regression: `get_settings()` is non-cached and called per-request from
`get_client()`, so unguarded `logger.info` calls in
`_get_background_operations_enabled()` spammed every MCP tool invocation
(observed: 569 entries/hour in tenant `tenant-e2e-disc-0033`).
"""
import logging
with patch.dict(
os.environ,
{
"NEXTCLOUD_HOST": "http://localhost:8080",
"ENABLE_SEMANTIC_SEARCH": "true",
"QDRANT_LOCATION": ":memory:",
"TOKEN_ENCRYPTION_KEY": "test-key",
"TOKEN_STORAGE_DB": "/tmp/test.db",
# No NEXTCLOUD_USERNAME/PASSWORD → multi-user mode → auto-enable triggers
},
clear=True,
):
from nextcloud_mcp_server.config import get_settings
_reload_config()
caplog.set_level(logging.INFO, logger="nextcloud_mcp_server.config")
for _ in range(5):
settings = get_settings()
assert settings.enable_offline_access is True
auto_enable_records = [
r
for r in caplog.records
if r.name == "nextcloud_mcp_server.config"
and "Automatically enabled background operations" in r.message
]
assert len(auto_enable_records) == 1, (
f"Expected exactly one auto-enable advisory log, "
f"got {len(auto_enable_records)}: "
f"{[r.message for r in auto_enable_records]}"
)
def test_legacy_offline_access_deprecation_warning_emitted_at_most_once(
self, caplog
):
"""Legacy `ENABLE_OFFLINE_ACCESS` deprecation WARNING is also one-shot."""
import logging
with patch.dict(
os.environ,
{
"NEXTCLOUD_HOST": "http://localhost:8080",
"ENABLE_OFFLINE_ACCESS": "true",
"TOKEN_ENCRYPTION_KEY": "test-key",
"TOKEN_STORAGE_DB": "/tmp/test.db",
},
clear=True,
):
from nextcloud_mcp_server.config import get_settings
_reload_config()
caplog.set_level(logging.WARNING, logger="nextcloud_mcp_server.config")
for _ in range(5):
get_settings()
deprecation_records = [
r
for r in caplog.records
if r.name == "nextcloud_mcp_server.config"
and "ENABLE_OFFLINE_ACCESS is deprecated" in r.message
]
assert len(deprecation_records) == 1, (
f"Expected exactly one deprecation warning, "
f"got {len(deprecation_records)}: "
f"{[r.message for r in deprecation_records]}"
)
class TestExplicitModeSelection:
"""Test ADR-021 explicit mode selection via MCP_DEPLOYMENT_MODE.