Add additional info to debug endpoint. Closes #142.

This commit is contained in:
Jack Kingsman
2026-04-01 11:31:20 -07:00
parent 416166b07c
commit b4f3d1f14c
2 changed files with 66 additions and 1 deletions

View File

@@ -12,8 +12,9 @@ from meshcore import EventType
from pydantic import BaseModel, Field
from app.config import get_recent_log_lines, settings
from app.models import AppSettings
from app.radio_sync import get_contacts_selected_for_radio_sync, get_radio_channel_limit
from app.repository import MessageRepository, StatisticsRepository
from app.repository import AppSettingsRepository, MessageRepository, StatisticsRepository
from app.routers.health import HealthResponse, build_health_data
from app.services.radio_runtime import radio_runtime
from app.version_info import get_app_build_info, git_output
@@ -103,11 +104,21 @@ class DebugDatabaseInfo(BaseModel):
total_outgoing: int
class DebugAppSettings(BaseModel):
max_radio_contacts: int
auto_decrypt_dm_on_advert: bool
advert_interval: int
flood_scope: str
blocked_keys_count: int
blocked_names_count: int
class DebugSnapshotResponse(BaseModel):
captured_at: str
system: DebugSystemInfo
application: DebugApplicationInfo
health: HealthResponse
settings: DebugAppSettings
runtime: DebugRuntimeInfo
database: DebugDatabaseInfo
radio_probe: DebugRadioProbe
@@ -186,6 +197,17 @@ def _coerce_live_max_channels(device_info: dict[str, Any] | None) -> int | None:
return None
def _build_debug_app_settings(app_settings: AppSettings) -> DebugAppSettings:
return DebugAppSettings(
max_radio_contacts=app_settings.max_radio_contacts,
auto_decrypt_dm_on_advert=app_settings.auto_decrypt_dm_on_advert,
advert_interval=app_settings.advert_interval,
flood_scope=app_settings.flood_scope,
blocked_keys_count=len(app_settings.blocked_keys),
blocked_names_count=len(app_settings.blocked_names),
)
async def _build_contact_audit(
observed_contacts_payload: dict[str, dict[str, Any]],
) -> DebugContactAudit:
@@ -293,6 +315,7 @@ async def _probe_radio() -> DebugRadioProbe:
async def debug_support_snapshot() -> DebugSnapshotResponse:
"""Return a support/debug snapshot with recent logs and live radio state."""
health_data = await build_health_data(radio_runtime.is_connected, radio_runtime.connection_info)
app_settings = await AppSettingsRepository.get()
message_totals = await StatisticsRepository.get_database_message_totals()
radio_probe = await _probe_radio()
channels_with_incoming_messages = (
@@ -303,6 +326,7 @@ async def debug_support_snapshot() -> DebugSnapshotResponse:
system=_build_system_info(),
application=_build_application_info(),
health=HealthResponse(**health_data),
settings=_build_debug_app_settings(app_settings),
runtime=DebugRuntimeInfo(
connection_info=radio_runtime.connection_info,
connection_desired=radio_runtime.connection_desired,

View File

@@ -213,6 +213,47 @@ class TestDebugEndpoint:
assert response.status_code == 200
@pytest.mark.asyncio
async def test_support_snapshot_includes_persisted_app_settings(self, test_db, client):
"""Debug snapshot should expose the stored app settings row."""
pub_key = "ab" * 32
await _insert_contact(pub_key, "Alice")
response = await client.patch(
"/api/settings",
json={
"max_radio_contacts": 321,
"auto_decrypt_dm_on_advert": True,
"sidebar_sort_order": "alpha",
"advert_interval": 7200,
"flood_scope": "US-CA",
"blocked_keys": [pub_key],
"blocked_names": ["Mallory"],
},
)
assert response.status_code == 200
response = await client.post(
"/api/settings/favorites/toggle",
json={"type": "contact", "id": pub_key},
)
assert response.status_code == 200
response = await client.get("/api/debug")
assert response.status_code == 200
payload = response.json()
assert payload["settings"]["max_radio_contacts"] == 321
assert payload["settings"]["auto_decrypt_dm_on_advert"] is True
assert payload["settings"]["advert_interval"] == 7200
assert payload["settings"]["flood_scope"] == "#US-CA"
assert payload["settings"]["blocked_keys_count"] == 1
assert payload["settings"]["blocked_names_count"] == 1
assert "favorites" not in payload["settings"]
assert "blocked_keys" not in payload["settings"]
assert "blocked_names" not in payload["settings"]
assert "sidebar_sort_order" not in payload["settings"]
class TestRadioDisconnectedHandler:
"""Test that RadioDisconnectedError maps to 503."""