aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-node/tests/test_partial_uploads.py
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-10-01 13:24:11 +0200
committerChristophe Besson <cbesson@gmail.com>2026-10-01 13:24:11 +0200
commit1d6189b6e6db7d0d6c126717b081901c5f552174 (patch)
tree69e6e791e234c1c2b6a2dc91808ac44d81aaecae /packages/meshbay-node/tests/test_partial_uploads.py
parentf0019e366fef813b22d2d55cf7604e20f0a08707 (diff)
downloadmeshbay-1d6189b6e6db7d0d6c126717b081901c5f552174.tar.gz
fix(node): the reaper deletes only the .part files the node wrote
Any *.part older than a day in a writable root was deleted — a browser's download in progress in a shared folder included. Only names carrying the node's tag (name.<8 hex>.part) are reaped now (F-28). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-node/tests/test_partial_uploads.py')
-rw-r--r--packages/meshbay-node/tests/test_partial_uploads.py32
1 files changed, 22 insertions, 10 deletions
diff --git a/packages/meshbay-node/tests/test_partial_uploads.py b/packages/meshbay-node/tests/test_partial_uploads.py
index 556b26a..52aa306 100644
--- a/packages/meshbay-node/tests/test_partial_uploads.py
+++ b/packages/meshbay-node/tests/test_partial_uploads.py
@@ -106,7 +106,7 @@ def _old(seconds: float) -> float:
NOW = 1_000_000.0
-FILM = Path("/roots/media/film.mkv.part")
+FILM = Path("/roots/media/film.mkv.0123abcd.part")
def test_a_part_nobody_is_writing_and_nobody_has_touched_is_deleted():
@@ -140,7 +140,7 @@ def test_the_same_name_in_another_directory_does_not_protect_it():
path rebuilt from a root and a relative directory would be a second
implementation that has to agree with the first for ever, and the state
records the path it is writing instead."""
- other = Path("/roots/archive/film.mkv.part")
+ other = Path("/roots/archive/film.mkv.0123abcd.part")
doomed = orphaned_parts([(other, _old(ORPHAN_AFTER_SECS + 1))],
live={FILM}, now=NOW)
assert doomed == [other]
@@ -159,15 +159,15 @@ def test_a_finished_file_is_not_a_candidate():
def test_a_file_from_the_future_is_left_alone():
"""A clock that went backwards is not evidence that a file is abandoned, and
deleting is not reversible."""
- doomed = orphaned_parts([(Path("/roots/media/a.part"), NOW + 10_000)],
+ doomed = orphaned_parts([(Path("/roots/media/a.0123abcd.part"), NOW + 10_000)],
live=set(), now=NOW)
assert doomed == []
def test_the_boundary_is_the_age_itself():
- at = [(Path("/roots/media/a.part"), _old(ORPHAN_AFTER_SECS))]
- just_under = [(Path("/roots/media/a.part"), _old(ORPHAN_AFTER_SECS - 1))]
- assert orphaned_parts(at, set(), NOW) == [Path("/roots/media/a.part")]
+ at = [(Path("/roots/media/a.0123abcd.part"), _old(ORPHAN_AFTER_SECS))]
+ just_under = [(Path("/roots/media/a.0123abcd.part"), _old(ORPHAN_AFTER_SECS - 1))]
+ assert orphaned_parts(at, set(), NOW) == [Path("/roots/media/a.0123abcd.part")]
assert orphaned_parts(just_under, set(), NOW) == []
@@ -245,9 +245,9 @@ def test_the_janitor_deletes_the_abandoned_and_keeps_the_rest(tmp_path):
one somebody is still writing stay, and a finished file is never a
candidate."""
root = _root(tmp_path, "media")
- old = _aged(root.path / "abandoned.mkv.part", ORPHAN_AFTER_SECS + 60)
- recent = _aged(root.path / "fresh.mkv.part", 30)
- live = _aged(root.path / "sending.mkv.part", ORPHAN_AFTER_SECS * 2)
+ old = _aged(root.path / "abandoned.mkv.0123abcd.part", ORPHAN_AFTER_SECS + 60)
+ recent = _aged(root.path / "fresh.mkv.0123abcd.part", 30)
+ live = _aged(root.path / "sending.mkv.0123abcd.part", ORPHAN_AFTER_SECS * 2)
finished = _aged(root.path / "done.mkv", ORPHAN_AFTER_SECS * 5)
uploads = PartialUploads()
@@ -264,7 +264,7 @@ def test_a_group_that_has_never_uploaded_anything_is_handled(tmp_path):
"""No `partial_uploads` in the context yet — it is created on first use, so
a node that has been up for five minutes has none."""
root = _root(tmp_path, "media")
- old = _aged(root.path / "left.mkv.part", ORPHAN_AFTER_SECS + 1)
+ old = _aged(root.path / "left.mkv.0123abcd.part", ORPHAN_AFTER_SECS + 1)
daemon = _daemon({"g1": {"roots": RootSet(roots=[root])}})
assert daemon._reap_once() == 1
assert not old.exists()
@@ -573,3 +573,15 @@ async def test_an_ordinary_file_with_a_near_name_is_accepted(tmp_path):
peer = _peer(ctx)
await peer._do_file_upload(sealed_upload(peer, filename="url-notes.txt", data=b"x"))
assert _errors(peer) == []
+
+
+
+def test_a_part_the_node_did_not_write_is_never_deleted():
+ """A browser's download in progress, a copy the operator is making: a
+ `.part` without the node's tag is somebody else's, however old."""
+ doomed = orphaned_parts(
+ [(Path("/roots/media/film.mkv.part"), _old(ORPHAN_AFTER_SECS * 10)),
+ (Path("/roots/media/report.pdf.part"), _old(ORPHAN_AFTER_SECS * 10)),
+ (Path("/roots/media/film.mkv.0123abcd.part"), _old(ORPHAN_AFTER_SECS * 10))],
+ live=set(), now=NOW)
+ assert doomed == [Path("/roots/media/film.mkv.0123abcd.part")]