Add intervalized repeater metrics collection. Closes #151.

This commit is contained in:
Jack Kingsman
2026-04-03 13:45:39 -07:00
parent 35981d8f8b
commit be2b2604df
22 changed files with 624 additions and 31 deletions
+16 -16
View File
@@ -1249,8 +1249,8 @@ class TestMigration039:
applied = await run_migrations(conn)
assert applied == 14
assert await get_version(conn) == 52
assert applied == 15
assert await get_version(conn) == 53
cursor = await conn.execute(
"""
@@ -1321,8 +1321,8 @@ class TestMigration039:
applied = await run_migrations(conn)
assert applied == 14
assert await get_version(conn) == 52
assert applied == 15
assert await get_version(conn) == 53
cursor = await conn.execute(
"""
@@ -1388,8 +1388,8 @@ class TestMigration039:
applied = await run_migrations(conn)
assert applied == 8
assert await get_version(conn) == 52
assert applied == 9
assert await get_version(conn) == 53
cursor = await conn.execute(
"""
@@ -1441,8 +1441,8 @@ class TestMigration040:
applied = await run_migrations(conn)
assert applied == 13
assert await get_version(conn) == 52
assert applied == 14
assert await get_version(conn) == 53
await conn.execute(
"""
@@ -1503,8 +1503,8 @@ class TestMigration041:
applied = await run_migrations(conn)
assert applied == 12
assert await get_version(conn) == 52
assert applied == 13
assert await get_version(conn) == 53
await conn.execute(
"""
@@ -1556,8 +1556,8 @@ class TestMigration042:
applied = await run_migrations(conn)
assert applied == 11
assert await get_version(conn) == 52
assert applied == 12
assert await get_version(conn) == 53
await conn.execute(
"""
@@ -1696,8 +1696,8 @@ class TestMigration046:
applied = await run_migrations(conn)
assert applied == 7
assert await get_version(conn) == 52
assert applied == 8
assert await get_version(conn) == 53
cursor = await conn.execute(
"""
@@ -1790,8 +1790,8 @@ class TestMigration047:
applied = await run_migrations(conn)
assert applied == 6
assert await get_version(conn) == 52
assert applied == 7
assert await get_version(conn) == 53
cursor = await conn.execute(
"""
+85 -2
View File
@@ -3,15 +3,18 @@
from unittest.mock import AsyncMock, patch
import pytest
from fastapi import HTTPException
from app.models import AppSettings
from app.repository import AppSettingsRepository
from app.models import CONTACT_TYPE_REPEATER, AppSettings, ContactUpsert
from app.repository import AppSettingsRepository, ContactRepository
from app.routers.settings import (
AppSettingsUpdate,
FavoriteRequest,
MigratePreferencesRequest,
TrackedTelemetryRequest,
migrate_preferences,
toggle_favorite,
toggle_tracked_telemetry,
update_settings,
)
@@ -202,3 +205,83 @@ class TestMigratePreferences:
assert response.migrated is False
assert response.settings.preferences_migrated is True
class TestToggleTrackedTelemetry:
"""Tests for POST /settings/tracked-telemetry/toggle."""
async def _create_repeater(self, key: str, name: str = "TestRepeater") -> None:
await ContactRepository.upsert(
ContactUpsert(public_key=key, name=name, type=CONTACT_TYPE_REPEATER)
)
@pytest.mark.asyncio
async def test_add_repeater_to_tracking(self, test_db):
key = "aa" * 32
await self._create_repeater(key)
result = await toggle_tracked_telemetry(TrackedTelemetryRequest(public_key=key))
assert key in result.tracked_telemetry_repeaters
assert result.names[key] == "TestRepeater"
# Verify persisted
settings = await AppSettingsRepository.get()
assert key in settings.tracked_telemetry_repeaters
@pytest.mark.asyncio
async def test_remove_repeater_from_tracking(self, test_db):
key = "bb" * 32
await self._create_repeater(key)
await AppSettingsRepository.update(tracked_telemetry_repeaters=[key])
result = await toggle_tracked_telemetry(TrackedTelemetryRequest(public_key=key))
assert key not in result.tracked_telemetry_repeaters
@pytest.mark.asyncio
async def test_rejects_non_repeater_contact(self, test_db):
key = "cc" * 32
await ContactRepository.upsert(ContactUpsert(public_key=key, name="Client", type=1))
with pytest.raises(HTTPException) as exc_info:
await toggle_tracked_telemetry(TrackedTelemetryRequest(public_key=key))
assert exc_info.value.status_code == 400
@pytest.mark.asyncio
async def test_rejects_unknown_contact(self, test_db):
with pytest.raises(HTTPException) as exc_info:
await toggle_tracked_telemetry(TrackedTelemetryRequest(public_key="dd" * 32))
assert exc_info.value.status_code == 404
@pytest.mark.asyncio
async def test_rejects_when_limit_reached(self, test_db):
existing_keys = []
for i in range(8):
key = f"{i:02x}" * 32
await self._create_repeater(key, name=f"Repeater{i}")
existing_keys.append(key)
await AppSettingsRepository.update(tracked_telemetry_repeaters=existing_keys)
new_key = "ff" * 32
await self._create_repeater(new_key, name="NewRepeater")
with pytest.raises(HTTPException) as exc_info:
await toggle_tracked_telemetry(TrackedTelemetryRequest(public_key=new_key))
assert exc_info.value.status_code == 409
detail = exc_info.value.detail
assert len(detail["tracked_telemetry_repeaters"]) == 8
@pytest.mark.asyncio
async def test_remove_still_works_when_limit_reached(self, test_db):
"""Toggling OFF an already-tracked repeater should work even at max capacity."""
keys = []
for i in range(8):
key = f"{i:02x}" * 32
await self._create_repeater(key)
keys.append(key)
await AppSettingsRepository.update(tracked_telemetry_repeaters=keys)
result = await toggle_tracked_telemetry(TrackedTelemetryRequest(public_key=keys[0]))
assert keys[0] not in result.tracked_telemetry_repeaters
assert len(result.tracked_telemetry_repeaters) == 7