aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-node/tests/test_daemon.py
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-08-11 22:16:59 +0200
committerChristophe Besson <cbesson@gmail.com>2026-08-11 22:16:59 +0200
commitc66ee41d8476461939c5f4e7fdc71c5d7fb4a85c (patch)
tree6aea861849ad38d6c3aa4a0fe9bc1ed5ae7dce9f /packages/meshbay-node/tests/test_daemon.py
parentbbfc45925e82c364519b9d796758003365bc9005 (diff)
downloadmeshbay-c66ee41d8476461939c5f4e7fdc71c5d7fb4a85c.tar.gz
feat(node): index push to WebRTC peers + swarm registration (11.5, 11.9)
When watchdog detects file changes, the daemon now: - Pushes INDEX_SYNC to all connected WebRTC peers in that group - Registers file hashes with hub /v1/swarm/register endpoint Also registers all file hashes on startup for initial discovery. hub_client: add register_swarm() method for bulk hash registration. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-node/tests/test_daemon.py')
-rw-r--r--packages/meshbay-node/tests/test_daemon.py94
1 files changed, 93 insertions, 1 deletions
diff --git a/packages/meshbay-node/tests/test_daemon.py b/packages/meshbay-node/tests/test_daemon.py
index e74f7fc..faf12e3 100644
--- a/packages/meshbay-node/tests/test_daemon.py
+++ b/packages/meshbay-node/tests/test_daemon.py
@@ -2,7 +2,8 @@
Integration test: Node daemon wires all components correctly.
Phase 11 — verifies that NodeDaemon creates chat stores, WebRTC transport,
-and shuts down cleanly. Hub interaction is mocked.
+index push on change, swarm registration, and shuts down cleanly.
+Hub interaction is mocked.
"""
import asyncio
@@ -16,6 +17,7 @@ from unittest.mock import AsyncMock, MagicMock, patch
from meshbay_common.crypto import generate_gek
from meshbay_node.config import Config, HubConfig, NodeConfig, GroupConfig, KeystoreConfig
from meshbay_node.daemon import NodeDaemon
+from meshbay_node.indexer import DirectoryIndexer
@pytest.fixture
@@ -169,3 +171,93 @@ async def test_daemon_no_groups_exits(tmp_path):
assert daemon._state["status"] == "starting"
assert len(daemon._chat_stores) == 0
+
+
+@pytest.mark.asyncio
+async def test_daemon_index_change_pushes_to_peers(tmp_path, shared_dir, gek, hub_pk_pem):
+ """Index change callback pushes updated index to WebRTC peers."""
+ config = Config(
+ hub=HubConfig(url="http://localhost:9999", username="testuser", password="testpass"),
+ node=NodeConfig(port=29000, quic_port=29010, http_port=29001, ui_port=28000),
+ groups=[GroupConfig(
+ id="a" * 32,
+ name="test-group",
+ shared_dir=str(shared_dir),
+ visibility="private",
+ port=29000, quic_port=29010, http_port=29001,
+ )],
+ keystore=KeystoreConfig(path=tmp_path / "keystore.enc"),
+ data_dir=tmp_path / "data",
+ )
+ daemon = NodeDaemon(config)
+ daemon._hub = AsyncMock()
+ daemon._hub.register_swarm = AsyncMock(return_value=2)
+ daemon._state["endpoint_hint"] = "node123"
+
+ sk_node = Ed25519PrivateKey.generate()
+ indexer = DirectoryIndexer(
+ root=shared_dir, group_id="a" * 32,
+ sk_node=sk_node, gek=gek)
+ await indexer.initial_scan()
+
+ mock_session = MagicMock()
+ mock_session._group_id = "a" * 32
+ mock_session._send = MagicMock()
+
+ mock_webrtc = MagicMock()
+ mock_webrtc._sessions = {"peer1": mock_session}
+ daemon._webrtc = mock_webrtc
+
+ await daemon._on_index_change(indexer)
+
+ mock_session._send.assert_called_once()
+ msg = mock_session._send.call_args[0][0]
+ assert msg["type"] == "index_sync"
+ assert msg["group_id"] == "a" * 32
+ assert len(msg["entries"]) == indexer.index.count
+
+ await asyncio.sleep(0.1)
+ daemon._hub.register_swarm.assert_called_once()
+ call_args = daemon._hub.register_swarm.call_args
+ assert len(call_args[0][0]) == indexer.index.count
+
+
+@pytest.mark.asyncio
+async def test_daemon_index_change_skips_other_group_peers(
+ tmp_path, shared_dir, gek, hub_pk_pem
+):
+ """Index change only pushes to peers in the same group."""
+ config = Config(
+ hub=HubConfig(url="http://localhost:9999", username="testuser", password="testpass"),
+ node=NodeConfig(),
+ groups=[],
+ keystore=KeystoreConfig(path=tmp_path / "keystore.enc"),
+ data_dir=tmp_path / "data",
+ )
+ daemon = NodeDaemon(config)
+ daemon._hub = AsyncMock()
+ daemon._hub.register_swarm = AsyncMock(return_value=0)
+ daemon._state["endpoint_hint"] = "node123"
+
+ sk_node = Ed25519PrivateKey.generate()
+ indexer = DirectoryIndexer(
+ root=shared_dir, group_id="a" * 32,
+ sk_node=sk_node, gek=gek)
+ await indexer.initial_scan()
+
+ same_group = MagicMock()
+ same_group._group_id = "a" * 32
+ same_group._send = MagicMock()
+
+ other_group = MagicMock()
+ other_group._group_id = "b" * 32
+ other_group._send = MagicMock()
+
+ mock_webrtc = MagicMock()
+ mock_webrtc._sessions = {"p1": same_group, "p2": other_group}
+ daemon._webrtc = mock_webrtc
+
+ await daemon._on_index_change(indexer)
+
+ same_group._send.assert_called_once()
+ other_group._send.assert_not_called()