diff options
| author | Christophe Besson <cbesson@gmail.com> | 2026-08-11 22:16:59 +0200 |
|---|---|---|
| committer | Christophe Besson <cbesson@gmail.com> | 2026-08-11 22:16:59 +0200 |
| commit | c66ee41d8476461939c5f4e7fdc71c5d7fb4a85c (patch) | |
| tree | 6aea861849ad38d6c3aa4a0fe9bc1ed5ae7dce9f /packages/meshbay-node/tests | |
| parent | bbfc45925e82c364519b9d796758003365bc9005 (diff) | |
| download | meshbay-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')
| -rw-r--r-- | packages/meshbay-node/tests/test_daemon.py | 94 |
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() |