mirror of
https://github.com/jkingsman/Remote-Terminal-for-MeshCore.git
synced 2026-03-28 17:43:05 +01:00
Phase 3: Add path size inference and also bin some stupid migration tests while we're at it
This commit is contained in:
@@ -221,7 +221,9 @@ async def on_path_update(event: "Event") -> None:
|
||||
)
|
||||
return
|
||||
|
||||
await ContactRepository.update_path(contact.public_key, str(path), normalized_path_len)
|
||||
await ContactRepository.update_path(
|
||||
contact.public_key, str(path), normalized_path_len
|
||||
)
|
||||
|
||||
|
||||
async def on_new_contact(event: "Event") -> None:
|
||||
|
||||
@@ -2,6 +2,8 @@ from typing import Literal
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from app.path_utils import infer_hash_size
|
||||
|
||||
|
||||
class Contact(BaseModel):
|
||||
public_key: str = Field(description="Public key (64-char hex)")
|
||||
@@ -32,6 +34,7 @@ class Contact(BaseModel):
|
||||
"flags": self.flags,
|
||||
"out_path": self.last_path or "",
|
||||
"out_path_len": self.last_path_len,
|
||||
"out_path_hash_mode": infer_hash_size(self.last_path or "", self.last_path_len) - 1,
|
||||
"adv_lat": self.lat if self.lat is not None else 0.0,
|
||||
"adv_lon": self.lon if self.lon is not None else 0.0,
|
||||
"last_advert": self.last_advert if self.last_advert is not None else 0,
|
||||
|
||||
@@ -25,8 +25,8 @@ class ContactRepository:
|
||||
await db.conn.execute(
|
||||
"""
|
||||
INSERT INTO contacts (public_key, name, type, flags, last_path, last_path_len,
|
||||
last_advert, lat, lon, last_seen, on_radio, last_contacted,
|
||||
first_seen)
|
||||
last_advert, lat, lon, last_seen,
|
||||
on_radio, last_contacted, first_seen)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
ON CONFLICT(public_key) DO UPDATE SET
|
||||
name = COALESCE(excluded.name, contacts.name),
|
||||
@@ -200,9 +200,12 @@ class ContactRepository:
|
||||
return [ContactRepository._row_to_contact(row) for row in rows]
|
||||
|
||||
@staticmethod
|
||||
async def update_path(public_key: str, path: str, path_len: int) -> None:
|
||||
async def update_path(
|
||||
public_key: str, path: str, path_len: int
|
||||
) -> None:
|
||||
await db.conn.execute(
|
||||
"UPDATE contacts SET last_path = ?, last_path_len = ?, last_seen = ? WHERE public_key = ?",
|
||||
"""UPDATE contacts SET last_path = ?, last_path_len = ?,
|
||||
last_seen = ? WHERE public_key = ?""",
|
||||
(path, path_len, int(time.time()), public_key.lower()),
|
||||
)
|
||||
await db.conn.commit()
|
||||
|
||||
@@ -98,10 +98,7 @@ class TestMigration001:
|
||||
await conn.commit()
|
||||
|
||||
# Run migrations
|
||||
applied = await run_migrations(conn)
|
||||
|
||||
assert applied == 38 # All migrations run
|
||||
assert await get_version(conn) == 38
|
||||
await run_migrations(conn)
|
||||
|
||||
# Verify columns exist by inserting and selecting
|
||||
await conn.execute(
|
||||
@@ -183,9 +180,8 @@ class TestMigration001:
|
||||
applied1 = await run_migrations(conn)
|
||||
applied2 = await run_migrations(conn)
|
||||
|
||||
assert applied1 == 38 # All migrations run
|
||||
assert applied1 > 0 # Migrations were applied
|
||||
assert applied2 == 0 # No migrations on second run
|
||||
assert await get_version(conn) == 38
|
||||
finally:
|
||||
await conn.close()
|
||||
|
||||
@@ -246,8 +242,7 @@ class TestMigration001:
|
||||
applied = await run_migrations(conn)
|
||||
|
||||
# All migrations applied (version incremented) but no error
|
||||
assert applied == 38
|
||||
assert await get_version(conn) == 38
|
||||
assert applied > 0
|
||||
finally:
|
||||
await conn.close()
|
||||
|
||||
@@ -374,10 +369,8 @@ class TestMigration013:
|
||||
)
|
||||
await conn.commit()
|
||||
|
||||
# Run migration 13 (plus 14-38 which also run)
|
||||
applied = await run_migrations(conn)
|
||||
assert applied == 26
|
||||
assert await get_version(conn) == 38
|
||||
# Run migration 13 (plus remaining which also run)
|
||||
await run_migrations(conn)
|
||||
|
||||
# Bots were migrated from app_settings to fanout_configs (migration 37)
|
||||
# and the bots column was dropped (migration 38)
|
||||
@@ -495,7 +488,6 @@ class TestMigration018:
|
||||
assert await cursor.fetchone() is not None
|
||||
|
||||
await run_migrations(conn)
|
||||
assert await get_version(conn) == 38
|
||||
|
||||
# Verify autoindex is gone
|
||||
cursor = await conn.execute(
|
||||
@@ -572,9 +564,7 @@ class TestMigration018:
|
||||
)
|
||||
await conn.commit()
|
||||
|
||||
applied = await run_migrations(conn)
|
||||
assert applied == 21 # Migrations 18-38 run (18+19 skip internally)
|
||||
assert await get_version(conn) == 38
|
||||
await run_migrations(conn)
|
||||
finally:
|
||||
await conn.close()
|
||||
|
||||
@@ -646,7 +636,6 @@ class TestMigration019:
|
||||
assert await cursor.fetchone() is not None
|
||||
|
||||
await run_migrations(conn)
|
||||
assert await get_version(conn) == 38
|
||||
|
||||
# Verify autoindex is gone
|
||||
cursor = await conn.execute(
|
||||
@@ -711,9 +700,7 @@ class TestMigration020:
|
||||
cursor = await conn.execute("PRAGMA journal_mode")
|
||||
assert (await cursor.fetchone())[0] == "delete"
|
||||
|
||||
applied = await run_migrations(conn)
|
||||
assert applied == 19 # Migrations 20-38
|
||||
assert await get_version(conn) == 38
|
||||
await run_migrations(conn)
|
||||
|
||||
# Verify WAL mode
|
||||
cursor = await conn.execute("PRAGMA journal_mode")
|
||||
@@ -742,8 +729,7 @@ class TestMigration020:
|
||||
await conn.commit()
|
||||
await set_version(conn, 20)
|
||||
|
||||
applied = await run_migrations(conn)
|
||||
assert applied == 18 # Migrations 21-38 still run
|
||||
await run_migrations(conn)
|
||||
|
||||
# Still WAL + INCREMENTAL
|
||||
cursor = await conn.execute("PRAGMA journal_mode")
|
||||
@@ -800,9 +786,7 @@ class TestMigration028:
|
||||
)
|
||||
await conn.commit()
|
||||
|
||||
applied = await run_migrations(conn)
|
||||
assert applied == 11
|
||||
assert await get_version(conn) == 38
|
||||
await run_migrations(conn)
|
||||
|
||||
# Verify payload_hash column is now BLOB
|
||||
cursor = await conn.execute("PRAGMA table_info(raw_packets)")
|
||||
@@ -870,9 +854,7 @@ class TestMigration028:
|
||||
)
|
||||
await conn.commit()
|
||||
|
||||
applied = await run_migrations(conn)
|
||||
assert applied == 11 # Version still bumped
|
||||
assert await get_version(conn) == 38
|
||||
await run_migrations(conn)
|
||||
|
||||
# Verify data unchanged
|
||||
cursor = await conn.execute("SELECT payload_hash FROM raw_packets")
|
||||
@@ -920,9 +902,7 @@ class TestMigration032:
|
||||
await conn.execute("INSERT INTO app_settings (id) VALUES (1)")
|
||||
await conn.commit()
|
||||
|
||||
applied = await run_migrations(conn)
|
||||
assert applied == 7
|
||||
assert await get_version(conn) == 38
|
||||
await run_migrations(conn)
|
||||
|
||||
# Community MQTT columns were added by migration 32 and dropped by migration 38.
|
||||
# Verify community settings were NOT migrated (no community config existed).
|
||||
@@ -987,9 +967,7 @@ class TestMigration034:
|
||||
""")
|
||||
await conn.commit()
|
||||
|
||||
applied = await run_migrations(conn)
|
||||
assert applied == 5
|
||||
assert await get_version(conn) == 38
|
||||
await run_migrations(conn)
|
||||
|
||||
# Verify column exists with correct default
|
||||
cursor = await conn.execute("SELECT flood_scope FROM app_settings WHERE id = 1")
|
||||
@@ -1030,9 +1008,7 @@ class TestMigration033:
|
||||
""")
|
||||
await conn.commit()
|
||||
|
||||
applied = await run_migrations(conn)
|
||||
assert applied == 6
|
||||
assert await get_version(conn) == 38
|
||||
await run_migrations(conn)
|
||||
|
||||
cursor = await conn.execute(
|
||||
"SELECT key, name, is_hashtag, on_radio FROM channels WHERE key = ?",
|
||||
|
||||
@@ -309,6 +309,7 @@ class TestAdvertisementPipeline:
|
||||
short_packet_info = MagicMock()
|
||||
short_packet_info.path_length = 1
|
||||
short_packet_info.path = bytes.fromhex("aa")
|
||||
short_packet_info.path_hash_size = 1
|
||||
short_packet_info.payload = b"" # Will be parsed by parse_advertisement
|
||||
|
||||
# Mock parse_advertisement to return our test contact
|
||||
@@ -333,6 +334,7 @@ class TestAdvertisementPipeline:
|
||||
long_packet_info = MagicMock()
|
||||
long_packet_info.path_length = 5
|
||||
long_packet_info.path = bytes.fromhex("aabbccddee")
|
||||
long_packet_info.path_hash_size = 1
|
||||
|
||||
with patch("app.packet_processor.broadcast_event", mock_broadcast):
|
||||
with patch("app.packet_processor.parse_advertisement") as mock_parse:
|
||||
@@ -381,6 +383,7 @@ class TestAdvertisementPipeline:
|
||||
packet_info = MagicMock()
|
||||
packet_info.path_length = 3
|
||||
packet_info.path = bytes.fromhex("aabbcc")
|
||||
packet_info.path_hash_size = 1
|
||||
|
||||
with patch("app.packet_processor.broadcast_event", mock_broadcast):
|
||||
with patch("app.packet_processor.parse_advertisement") as mock_parse:
|
||||
@@ -433,6 +436,7 @@ class TestAdvertisementPipeline:
|
||||
long_packet_info = MagicMock()
|
||||
long_packet_info.path_length = 4
|
||||
long_packet_info.path = bytes.fromhex("aabbccdd")
|
||||
long_packet_info.path_hash_size = 1
|
||||
long_packet_info.payload = b""
|
||||
|
||||
with patch("app.packet_processor.broadcast_event", mock_broadcast):
|
||||
|
||||
9
uv.lock
generated
9
uv.lock
generated
@@ -497,16 +497,17 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "meshcore"
|
||||
version = "2.2.5"
|
||||
version = "2.2.28"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "bleak" },
|
||||
{ name = "pycayennelpp" },
|
||||
{ name = "pycryptodome" },
|
||||
{ name = "pyserial-asyncio-fast" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/be/c2/0927a793d9742832613a42bb5c138adda75e201fc0dafe83c0bd444ab9f7/meshcore-2.2.5.tar.gz", hash = "sha256:15818150a6a8380883c2b272356fabad0bab107f3b9438cfd665bc9ca03d1d17", size = 56968 }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/7d/29/4dc795be22670ebabf18ce8b12625f39551f1e9073dfd3494c8bd1b2291d/meshcore-2.2.28.tar.gz", hash = "sha256:87ece4b3d9e32c6baccab73da5eabcf5a84d520df8be715c62879985b5baec83", size = 68652 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d5/e6/557d0fade6ba87e955750b980ca4c7468f529c911e5a9b2fe55d49b4cfd8/meshcore-2.2.5-py3-none-any.whl", hash = "sha256:b422816f6e4c6e621be99730b92973f78ab983d4e01111ad4c8fd50c2b2b3a9c", size = 45053 },
|
||||
{ url = "https://files.pythonhosted.org/packages/3a/10/0cf1ca1c948049344a303478d86425b86c6699e8cdeb7f0e611125f937c1/meshcore-2.2.28-py3-none-any.whl", hash = "sha256:e015dbf756f844d2c4191d5a9cd1c1c70ec16a235d222cdcfc88a6a760de2847", size = 52213 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1090,7 +1091,7 @@ requires-dist = [
|
||||
{ name = "fastapi", specifier = ">=0.115.0" },
|
||||
{ name = "httpx", specifier = ">=0.28.1" },
|
||||
{ name = "httpx", marker = "extra == 'test'", specifier = ">=0.27.0" },
|
||||
{ name = "meshcore" },
|
||||
{ name = "meshcore", specifier = "==2.2.28" },
|
||||
{ name = "pycryptodome", specifier = ">=3.20.0" },
|
||||
{ name = "pydantic-settings", specifier = ">=2.0.0" },
|
||||
{ name = "pynacl", specifier = ">=1.5.0" },
|
||||
|
||||
Reference in New Issue
Block a user