diff options
| author | Christophe Besson <cbesson@gmail.com> | 2026-08-26 00:40:25 +0200 |
|---|---|---|
| committer | Christophe Besson <cbesson@gmail.com> | 2026-08-26 00:40:25 +0200 |
| commit | 704cfe37506fc5316c997b025004fbc9c4d2a47b (patch) | |
| tree | bb51f6dc2ae395f56afc05e6a048a23d5b409fdf /packages/meshbay-node/tests/test_daemon.py | |
| parent | 2af320ba4da49547176ef7e4c081956c33841958 (diff) | |
| parent | 37d8d9c15c982f2da17b2fad4ea1a90613b560a6 (diff) | |
| download | meshbay-704cfe37506fc5316c997b025004fbc9c4d2a47b.tar.gz | |
Merge branch 'feat/cross-group-file-cache'
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013XSohfUQQiaE77qyFLgSv3
Diffstat (limited to 'packages/meshbay-node/tests/test_daemon.py')
| -rw-r--r-- | packages/meshbay-node/tests/test_daemon.py | 65 |
1 files changed, 65 insertions, 0 deletions
diff --git a/packages/meshbay-node/tests/test_daemon.py b/packages/meshbay-node/tests/test_daemon.py index b367a20..71aae78 100644 --- a/packages/meshbay-node/tests/test_daemon.py +++ b/packages/meshbay-node/tests/test_daemon.py @@ -440,6 +440,71 @@ async def test_delta_reflects_additions_and_deletions(tmp_path, shared_dir, gek) @pytest.mark.asyncio +async def test_media_cache_not_pruned_when_another_group_still_has_the_content( + tmp_path, shared_dir, gek): + """ + media_cache.db is node-wide, keyed by content hash — a file shared into + two groups is one row there. Removing it from ONE group's index (root + unshared, group left) must not wipe the thumbnail/tmdb/mbid mapping the + OTHER group's copy still needs, or that surviving group pays for a + redundant re-fetch/re-probe/re-thumbnail for content it never lost. + """ + daemon = _new_daemon_for_group(tmp_path, shared_dir, gek, group_id="a" * 32) + daemon._media_cache = AsyncMock() + daemon._webrtc = MagicMock() + daemon._webrtc._sessions = {} + + indexer_a = DirectoryIndexer(roots=one_root(shared_dir), group_id="a" * 32, + sk_node=Ed25519PrivateKey.generate(), gek=gek) + await indexer_a.initial_scan() + shared_id = indexer_a.index.entries[0].id + + indexer_b = DirectoryIndexer(roots=one_root(shared_dir), group_id="b" * 32, + sk_node=Ed25519PrivateKey.generate(), gek=gek) + await indexer_b.initial_scan() + assert indexer_b.index.get_entry(shared_id) is not None + + daemon._indexers = [indexer_a, indexer_b] + + await daemon._on_index_change(indexer_a) # establishes the snapshot + await asyncio.sleep(0.05) + + indexer_a.index.remove_entry(shared_id) + await daemon._on_index_change(indexer_a) + await asyncio.sleep(0.05) + + daemon._media_cache.prune_file.assert_not_called() + + +@pytest.mark.asyncio +async def test_media_cache_pruned_once_no_group_has_the_content_left( + tmp_path, shared_dir, gek): + """Counterpart of the test above: with only one group ever having held + the content, its removal must still prune media_cache as before — the + fix only withholds pruning when the content genuinely survives + elsewhere, it must not make pruning stop happening altogether.""" + daemon = _new_daemon_for_group(tmp_path, shared_dir, gek, group_id="a" * 32) + daemon._media_cache = AsyncMock() + daemon._webrtc = MagicMock() + daemon._webrtc._sessions = {} + + indexer = DirectoryIndexer(roots=one_root(shared_dir), group_id="a" * 32, + sk_node=Ed25519PrivateKey.generate(), gek=gek) + await indexer.initial_scan() + removed_id = indexer.index.entries[0].id + daemon._indexers = [indexer] + + await daemon._on_index_change(indexer) + await asyncio.sleep(0.05) + + indexer.index.remove_entry(removed_id) + await daemon._on_index_change(indexer) + await asyncio.sleep(0.05) + + daemon._media_cache.prune_file.assert_called_once_with(removed_id) + + +@pytest.mark.asyncio async def test_a_burst_of_changes_produces_one_broadcast(tmp_path, shared_dir, gek): """Coalescing: several _on_index_change calls in quick succession (one per debounced watchdog event) must collapse into a single push.""" |