Fix async db handling. Closes #179.

This commit is contained in:
Jack Kingsman
2026-04-12 11:57:37 -07:00
parent 53a4d8186a
commit cde4d1744e
8 changed files with 89 additions and 94 deletions
+4 -2
View File
@@ -1,8 +1,10 @@
import type { FullConfig } from '@playwright/test';
const BASE_URL = 'http://localhost:8001';
const MAX_RETRIES = 10;
const RETRY_DELAY_MS = 2000;
// Post-connect sync (contact offload, channel sync, key export) can take
// 30-60s on a radio with many contacts, so allow generous polling here.
const MAX_RETRIES = 60;
const RETRY_DELAY_MS = 3000;
interface HealthStatus {
radio_connected: boolean;
-1
View File
@@ -63,7 +63,6 @@ export default defineConfig({
timeout: 180_000,
env: {
MESHCORE_DATABASE_PATH: path.join(tmpDir, 'e2e-test.db'),
MESHCORE_SKIP_POST_CONNECT_SYNC: 'true',
// Pass through the serial port from the environment
...(process.env.MESHCORE_SERIAL_PORT
? { MESHCORE_SERIAL_PORT: process.env.MESHCORE_SERIAL_PORT }
+24 -25
View File
@@ -5,7 +5,7 @@ undecrypted count endpoint, and the maintenance endpoint.
"""
import time
from unittest.mock import AsyncMock, patch
from unittest.mock import patch
import pytest
@@ -307,38 +307,37 @@ class TestDecryptHistoricalPackets:
class TestUndecryptedTextPacketStreaming:
@pytest.mark.asyncio
async def test_count_undecrypted_text_messages_uses_batched_streaming(self, test_db):
"""Counting undecrypted DM packets should stream batches and filter by payload type."""
async def test_count_undecrypted_text_messages_uses_keyset_pagination(self, test_db):
"""Counting undecrypted DM packets should use keyset pagination and filter by payload type."""
class FakeCursor:
def __init__(self):
self._batches = [
[
{"id": 1, "data": b"\x09\x00dm", "timestamp": 1000},
{"id": 2, "data": b"\x15\x00chan", "timestamp": 1001},
],
[{"id": 3, "data": b"\x09\x00dm2", "timestamp": 1002}],
[],
]
self.fetchall_called = False
# Simulate keyset pagination: each execute() call returns a cursor
# whose fetchall() yields one batch. The generator stops when a
# batch is empty.
batches = [
[
{"id": 1, "data": b"\x09\x00dm", "timestamp": 1000},
{"id": 2, "data": b"\x15\x00chan", "timestamp": 1001},
],
[{"id": 3, "data": b"\x09\x00dm2", "timestamp": 1002}],
[],
]
async def fetchmany(self, size):
assert size > 0
return self._batches.pop(0)
async def fake_execute(*_args, **_kwargs):
batch = batches.pop(0)
async def close(self):
return None
class FakeCursor:
async def fetchall(self):
return batch
async def fetchall(self):
self.fetchall_called = True
raise AssertionError("fetchall() should not be used")
async def close(self):
pass
fake_cursor = FakeCursor()
return FakeCursor()
with patch.object(test_db.conn, "execute", new=AsyncMock(return_value=fake_cursor)):
with patch.object(test_db.conn, "execute", side_effect=fake_execute):
count = await RawPacketRepository.count_undecrypted_text_messages(batch_size=2)
assert fake_cursor.fetchall_called is False
# header byte 0x09 -> payload type 2 (TEXT_MESSAGE); 0x15 -> type 5 (not TEXT_MESSAGE)
assert count == 2
+6 -20
View File
@@ -352,27 +352,14 @@ class TestPathHashWidthStats:
assert breakdown["triple_byte_pct"] == pytest.approx(100 / 3, rel=1e-3)
@pytest.mark.asyncio
async def test_path_hash_width_scan_uses_batched_fetchmany(self, test_db):
"""Hash-width stats should stream batches instead of calling fetchall()."""
async def test_path_hash_width_scan_fetches_all_then_buckets(self, test_db):
"""Hash-width stats should fetchall() then bucket synchronously."""
fake_rows = [{"data": b"a"}, {"data": b"b"}, {"data": b"c"}]
class FakeCursor:
def __init__(self):
self._batches = [
[{"data": b"a"}, {"data": b"b"}],
[{"data": b"c"}],
[],
]
self.fetchall_called = False
async def fetchmany(self, size):
assert size > 0
return self._batches.pop(0)
async def fetchall(self):
self.fetchall_called = True
raise AssertionError("fetchall() should not be used")
fake_cursor = FakeCursor()
return fake_rows
def fake_parse(raw_packet: bytes):
hash_sizes = {
@@ -386,12 +373,11 @@ class TestPathHashWidthStats:
return SimpleNamespace(hash_size=hash_size)
with (
patch.object(test_db.conn, "execute", new=AsyncMock(return_value=fake_cursor)),
patch.object(test_db.conn, "execute", new=AsyncMock(return_value=FakeCursor())),
patch("app.path_utils.parse_packet_envelope", side_effect=fake_parse),
):
breakdown = await StatisticsRepository._path_hash_width_24h()
assert fake_cursor.fetchall_called is False
assert breakdown["total_packets"] == 3
assert breakdown["single_byte"] == 1
assert breakdown["double_byte"] == 1