From 75d1f8b93dfa0bffda3a59a6d143b06dcc3ca67f Mon Sep 17 00:00:00 2001 From: Christophe Besson Date: Mon, 24 Aug 2026 20:55:52 +0200 Subject: fix(node): skip indexing audio files under 50KB, likely-corrupt source MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "P.H. Theme" failure investigated earlier turned out to be a genuinely corrupt 1256-byte source file with no audio stream at all, just an ID3 tag — a real, if rare, corruption pattern worth guarding against directly rather than only handling gracefully at playback time. Scoped to audio only, applied wherever a file actually gets hashed/typed (fresh scan and the cache-miss rehash path alike) — a tiny file of any other type is still indexed normally. --- packages/meshbay-node/tests/test_indexer.py | 37 +++++++++++++++++++++- .../meshbay-node/tests/test_root_availability.py | 6 +++- 2 files changed, 41 insertions(+), 2 deletions(-) (limited to 'packages/meshbay-node/tests') diff --git a/packages/meshbay-node/tests/test_indexer.py b/packages/meshbay-node/tests/test_indexer.py index 56571a4..4bae620 100644 --- a/packages/meshbay-node/tests/test_indexer.py +++ b/packages/meshbay-node/tests/test_indexer.py @@ -28,7 +28,11 @@ def shared_dir(tmp_path): d = tmp_path / "shared" d.mkdir() (d / "video.mkv").write_bytes(os.urandom(1024)) - (d / "music.mp3").write_bytes(os.urandom(512)) + # Above MIN_AUDIO_SIZE_BYTES: these tests use this file as a generic + # "here's an audio entry" stand-in, not as a test of the tiny-file gate + # itself (see test_is_indexable_size_* / test_initial_scan_skips_a_ + # corrupt_tiny_audio_file below for that). + (d / "music.mp3").write_bytes(os.urandom(indexer_mod.MIN_AUDIO_SIZE_BYTES + 1024)) (d / "readme.md").write_bytes(b"# Hello MeshBay") subdir = d / "docs" subdir.mkdir() @@ -157,6 +161,37 @@ def test_type_detection_covers_wma_and_musepack(): assert indexer_mod._detect_type(Path("track.mpc")) == "audio" +def test_is_indexable_size_rejects_tiny_audio(tmp_path): + assert indexer_mod._is_indexable_size(Path("track.mp3"), 1256) is False + assert indexer_mod._is_indexable_size( + Path("track.mp3"), indexer_mod.MIN_AUDIO_SIZE_BYTES) is True + assert indexer_mod._is_indexable_size( + Path("track.mp3"), indexer_mod.MIN_AUDIO_SIZE_BYTES - 1) is False + + +def test_is_indexable_size_does_not_apply_to_other_types(): + """A tiny document/image is still worth indexing — the rule exists + because a truncated *audio* file is a known corruption signature, not + because small files in general are suspect.""" + assert indexer_mod._is_indexable_size(Path("readme.txt"), 10) is True + assert indexer_mod._is_indexable_size(Path("icon.png"), 10) is True + + +@pytest.mark.asyncio +async def test_initial_scan_skips_a_corrupt_tiny_audio_file(tmp_path, sk_node, gek): + d = tmp_path / "shared" + d.mkdir() + (d / "good.mp3").write_bytes(os.urandom(indexer_mod.MIN_AUDIO_SIZE_BYTES + 1)) + (d / "corrupt.mp3").write_bytes(os.urandom(1256)) + + indexer = DirectoryIndexer(roots=one_root(d), group_id="g", sk_node=sk_node, gek=gek) + await indexer.initial_scan() + + names = {e.name for e in indexer.index.entries} + assert "good.mp3" in names + assert "corrupt.mp3" not in names + + @pytest.mark.asyncio async def test_hidden_files_excluded(tmp_path, sk_node, gek): d = tmp_path / "dir" diff --git a/packages/meshbay-node/tests/test_root_availability.py b/packages/meshbay-node/tests/test_root_availability.py index 028c308..0201c1f 100644 --- a/packages/meshbay-node/tests/test_root_availability.py +++ b/packages/meshbay-node/tests/test_root_availability.py @@ -14,6 +14,7 @@ straightforward implementation does. """ import asyncio +import os from pathlib import Path import pytest @@ -90,7 +91,10 @@ async def test_one_root_going_away_leaves_the_others_alone(tmp_path): films.mkdir() music.mkdir() (films / "a.mkv").write_bytes(b"a") - (music / "b.mp3").write_bytes(b"b") + # Above the tiny-audio-file cutoff (indexer.py's MIN_AUDIO_SIZE_BYTES) — + # this test is about root availability, not that gate, so the content + # just needs to actually get indexed as an entry. + (music / "b.mp3").write_bytes(os.urandom(60 * 1024)) idx = await _indexer(_roots(films, music)) assert _names(idx) == {"a.mkv", "b.mp3"} -- cgit v1.2.3