Files
shelfmark/tests/download/test_activity_key.py
T
splitsec2 ac14e4dd24 fix(downloads): a swarm note change is not activity for the stall timer (#1572)
The torrent stall timer can be deferred for as long as the swarm keeps
changing. Since #1494 the torrent handler appends a note to its progress
message (`0% · 2 seeds`, and `· 0 seeds, giving up in 7m` while the
no-seeders clock runs). `update_download_status` treats any changed
message as activity, so a seed count that goes 2, 3, 1 between polls, or
the give-up countdown ticking down every minute, refreshes
`_last_activity` and the torrent never stalls. A torrent whose seed
count stays constant still stalls, which is why this is easy to miss.

`shelfmark/download/activity.py` already says what counts: a changed
status event or a changed progress value, and a message that changes on
a timer proves nothing. So `update_download_status` now compares
messages through a small `activity_key()` that drops that trailing seed
note. The card still gets every message and shows the live seed count,
only the stall clock ignores a change in the note. A new progress
percentage, a different client message or a status change still counts
as activity.

The handler builds the note in `_seed_note` and `activity_key` has to
match it, so one test builds the note for a range of seed counts and
countdowns and checks the key strips all of them. That also covers the
gap #1494 left, where no test connected a handler's message to the
orchestrator: the other two drive `update_download_status` with real
notes and a fake clock. Without the change the note test fails and the
key tests cannot even import `activity_key`. The other orchestrator test
is a control that a progress or status change still counts. Full suite
passes, and each new test also passes when run alone.
2026-10-26 00:45:28 -06:00

34 lines
1.1 KiB
Python

"""activity_key drops the swarm note the torrent handler adds beside a progress message."""
import pytest
from shelfmark.download.activity import activity_key
def _seed_note(seeds, give_up_in):
# Importing the orchestrator first keeps the handler module out of its import cycle.
import shelfmark.download.orchestrator # noqa: F401
from shelfmark.download.clients.base_handler import ExternalClientHandler
return ExternalClientHandler._seed_note(seeds, give_up_in)
@pytest.mark.parametrize("seeds", [0, 1, 2, 15, 1200])
@pytest.mark.parametrize("give_up_in", [None, 0.0, 30.0, 61.0, 3599.0])
def test_activity_key_strips_whatever_the_handler_appends(seeds, give_up_in):
message = f"42%{_seed_note(seeds, give_up_in)}"
assert activity_key(message) == "42%"
@pytest.mark.parametrize(
"message",
["42%", "Fetching metadata", "Seeding (stalled)", "Paused", "No seeders: 0 found, 1 needed"],
)
def test_activity_key_leaves_other_messages_alone(message):
assert activity_key(message) == message
def test_activity_key_keeps_none():
assert activity_key(None) is None