02ad558330
Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
220 lines
9.0 KiB
Python
220 lines
9.0 KiB
Python
"""Tests for app/utils/config_validator/settings_display.py module."""
|
|
|
|
from unittest.mock import patch
|
|
|
|
import pytest
|
|
|
|
from app.utils.config_validator.settings_display import dump_all_settings, get_settings_for_display
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestDumpAllSettings:
|
|
"""Tests for dump_all_settings function."""
|
|
|
|
def test_dumps_without_error(self):
|
|
"""Test that dump_all_settings runs without error."""
|
|
# This should not raise any exceptions
|
|
dump_all_settings()
|
|
|
|
@patch("app.utils.config_validator.settings_display.logger")
|
|
def test_logs_settings(self, mock_logger):
|
|
"""Test that dump_all_settings logs settings."""
|
|
dump_all_settings()
|
|
# Should have logged start and end markers
|
|
assert mock_logger.info.call_count > 2 # At least start, some settings, and end
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestGetSettingsForDisplay:
|
|
"""Tests for get_settings_for_display function."""
|
|
|
|
def test_returns_dict(self):
|
|
"""Test that it returns a dictionary."""
|
|
result = get_settings_for_display()
|
|
assert isinstance(result, dict)
|
|
|
|
def test_has_system_info(self):
|
|
"""Test that System Info category is present."""
|
|
result = get_settings_for_display()
|
|
assert "System Info" in result
|
|
|
|
def test_system_info_has_version(self):
|
|
"""Test that System Info includes app version."""
|
|
result = get_settings_for_display()
|
|
system_info = result["System Info"]
|
|
names = [item["name"] for item in system_info]
|
|
assert "App Version" in names
|
|
|
|
def test_has_core_settings(self):
|
|
"""Test that Core settings category is present."""
|
|
result = get_settings_for_display()
|
|
assert "Core" in result
|
|
|
|
def test_has_ai_services(self):
|
|
"""Test that AI Services category is present."""
|
|
result = get_settings_for_display()
|
|
assert "AI Services" in result
|
|
|
|
def test_show_values_false_masks_sensitive(self):
|
|
"""Test that sensitive values are masked when show_values is False."""
|
|
result = get_settings_for_display(show_values=False)
|
|
# Check that AI Services settings have masked values
|
|
if "AI Services" in result:
|
|
for item in result["AI Services"]:
|
|
if "key" in item["name"].lower() or "token" in item["name"].lower():
|
|
if item["value"]:
|
|
# Sensitive values should be masked
|
|
assert "****" in str(item["value"]) or isinstance(item["value"], str)
|
|
|
|
def test_show_values_true(self):
|
|
"""Test that settings are returned with show_values=True."""
|
|
result = get_settings_for_display(show_values=True)
|
|
assert isinstance(result, dict)
|
|
assert len(result) > 0
|
|
|
|
def test_items_have_required_keys(self):
|
|
"""Test that each setting item has required keys."""
|
|
result = get_settings_for_display()
|
|
for category, items in result.items():
|
|
for item in items:
|
|
assert "name" in item
|
|
assert "value" in item
|
|
assert "is_configured" in item
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestDumpAllSettingsNotificationUrls:
|
|
"""Tests for the notification_urls special handling in dump_all_settings (lines 47-57)."""
|
|
|
|
def test_notification_urls_list_masked(self):
|
|
"""Covers lines 50-52: notification_urls as a list gets each URL masked."""
|
|
from app.utils.config_validator.settings_display import dump_all_settings
|
|
|
|
with (
|
|
patch("app.utils.config_validator.settings_display.settings") as patched_settings,
|
|
patch("app.utils.config_validator.settings_display.logger"),
|
|
patch("app.utils.notification._mask_sensitive_url", return_value="masked_url", create=True),
|
|
):
|
|
patched_settings.notification_urls = ["https://hooks.slack.com/services/secret/url"]
|
|
patched_settings.model_computed_fields = {}
|
|
patched_settings.model_config = {}
|
|
patched_settings.model_extra = {}
|
|
patched_settings.model_fields = {}
|
|
patched_settings.model_fields_set = set()
|
|
|
|
with patch("builtins.dir", return_value=["notification_urls"]):
|
|
dump_all_settings()
|
|
|
|
def test_notification_urls_string_masked(self):
|
|
"""Covers lines 53-54: notification_urls as a string gets masked."""
|
|
from app.utils.config_validator.settings_display import dump_all_settings
|
|
|
|
with (
|
|
patch("app.utils.config_validator.settings_display.settings") as patched_settings,
|
|
patch("app.utils.notification._mask_sensitive_url", return_value="masked", create=True),
|
|
):
|
|
patched_settings.notification_urls = "https://hooks.slack.com/secret"
|
|
patched_settings.model_computed_fields = {}
|
|
patched_settings.model_config = {}
|
|
patched_settings.model_extra = {}
|
|
patched_settings.model_fields = {}
|
|
patched_settings.model_fields_set = set()
|
|
|
|
with patch("builtins.dir", return_value=["notification_urls"]):
|
|
dump_all_settings() # Should not raise
|
|
|
|
def test_notification_urls_import_error_fallback(self):
|
|
"""Covers lines 56-57: ImportError falls back to default logging."""
|
|
import sys
|
|
|
|
from app.utils.config_validator.settings_display import dump_all_settings
|
|
|
|
with (
|
|
patch("app.utils.config_validator.settings_display.settings") as patched_settings,
|
|
patch(
|
|
"app.utils.config_validator.settings_display.logger",
|
|
),
|
|
):
|
|
patched_settings.notification_urls = ["https://example.com/notify"]
|
|
patched_settings.model_computed_fields = {}
|
|
patched_settings.model_config = {}
|
|
patched_settings.model_extra = {}
|
|
patched_settings.model_fields = {}
|
|
patched_settings.model_fields_set = set()
|
|
|
|
# Force ImportError by removing the module from sys.modules
|
|
import sys
|
|
|
|
modules_backup = sys.modules.get("app.utils.notification")
|
|
sys.modules.pop("app.utils.notification", None)
|
|
|
|
with patch("builtins.dir", return_value=["notification_urls"]):
|
|
try:
|
|
dump_all_settings()
|
|
finally:
|
|
if modules_backup is not None:
|
|
sys.modules["app.utils.notification"] = modules_backup
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestGetSettingsForDisplayBranches:
|
|
"""Tests for uncovered branches in get_settings_for_display (lines 219->223, 226->225, 265->223)."""
|
|
|
|
def test_no_uncategorized_settings_no_other_category(self):
|
|
"""Covers 219->223: if uncategorized is empty, 'Other' category is not added."""
|
|
from app.utils.config_validator.settings_display import get_settings_for_display
|
|
|
|
result = get_settings_for_display()
|
|
# 'Other' should not appear when all settings are categorized (or empty)
|
|
# We just verify the function runs without error
|
|
assert isinstance(result, dict)
|
|
|
|
def test_key_not_in_settings_skipped(self):
|
|
"""Covers 226->225: keys without hasattr(settings, key) are skipped."""
|
|
from unittest.mock import patch
|
|
|
|
from app.utils.config_validator.settings_display import get_settings_for_display
|
|
|
|
# Inject a non-existent key into one category
|
|
with patch("app.utils.config_validator.settings_display.settings") as patched:
|
|
patched.version = "1.0.0"
|
|
patched.build_date = "2024-01-01"
|
|
patched.debug = False
|
|
# Mock hasattr to return False for 'external_hostname' (simulating missing key)
|
|
|
|
original_hasattr = hasattr
|
|
|
|
def fake_hasattr(obj, name):
|
|
if name == "external_hostname":
|
|
return False
|
|
return original_hasattr(obj, name)
|
|
|
|
with patch("builtins.hasattr", side_effect=fake_hasattr):
|
|
result = get_settings_for_display()
|
|
# Should still return a dict without raising
|
|
assert isinstance(result, dict)
|
|
|
|
def test_empty_items_category_excluded(self):
|
|
"""Covers 265->223: categories with no items are excluded from result."""
|
|
from unittest.mock import patch
|
|
|
|
from app.utils.config_validator.settings_display import get_settings_for_display
|
|
|
|
# If all keys in a category are missing from settings, it's excluded
|
|
with patch("app.utils.config_validator.settings_display.settings") as patched:
|
|
patched.version = "1.0.0"
|
|
patched.build_date = "2024-01-01"
|
|
patched.debug = False
|
|
# Make hasattr return False for all non-essential keys
|
|
original_hasattr = hasattr
|
|
|
|
def fake_hasattr(obj, name):
|
|
if name in ("version", "build_date", "debug"):
|
|
return original_hasattr(obj, name)
|
|
return False
|
|
|
|
with patch("builtins.hasattr", side_effect=fake_hasattr):
|
|
result = get_settings_for_display()
|
|
# Should have System Info but other categories may be empty/excluded
|
|
assert "System Info" in result
|