mirror of
https://github.com/calibrain/shelfmark.git
synced 2026-11-15 07:28:26 +00:00
Fixes four defects in the Soulseek (slskd) A peer names its own files, and Shelfmark works out where slskd stored one from the last folder and the file name. `remote_directory_name` returned the whole directory when its last segment was empty. So `../../../etc//passwd.epub` or `/abs/path//book.epub` resolved outside the downloads folder, and Shelfmark imported whatever was there. For a multi-file release it moved those files into staging (`STAGE_MOVE`). - New `safe_remote_segments` refuses a name with an empty, `.` or `..` segment (which covers absolute and UNC paths), a NUL, or a drive anywhere but a Windows share root (`C:\Books\…`, which never reaches a local path). - Search (`iter_peer_files`, so `build_releases`) drops such files. - `download` refuses them before anything is queued, in both modes. The spec arrives with the queued release payload, so search filtering alone is not enough. - `local_path_for` also refuses a path that resolves outside the root (`resolve()` + `is_relative_to`), for example through a symlinked folder. `_poll`, used when isolation is off and as the fallback for slskd < 0.26, never asked for an activity grace. A file "Queued, Remotely" sent the same status and progress on every poll, so the orchestrator cancelled it after 300s and `SLSKD_QUEUE_TIMEOUT_MINUTES` never applied. It now requests, renews and releases the grace the same way isolated mode does. `search` caught every exception per query, so slskd being down or rejecting the API key showed as "no releases". When no query gets an answer it now raises `SourceUnavailableError`, as Prowlarr does, and the error reaches the user. One failed query among answered ones is still not an outage. A retry reuses the task's folder, and slskd renames a new file when one is already there. Files from a failed attempt therefore meant `_folder_complete`'s exact count could never match again. Now: - a failed or cancelled attempt removes its folder; - a new attempt removes any finished files an earlier one left (for example after a restart) before it queues the batch. `tests/slskd/conftest.py` stubbed `flask_socketio` whenever it had not been imported yet, even though it is installed. Running `pytest tests/slskd tests/` therefore broke the Flask app tests in the same worker (285 errors, also on10710ab). It now stubs only when the module is missing. - 25 new regression tests. All of them fail on10710ab. - `make python-checks` passes. - `uv run pytest tests/slskd tests/ -m "not integration and not e2e"` passes: 4047 tests one83bab9.
30 lines
959 B
Python
30 lines
959 B
Python
"""
|
|
slskd test configuration.
|
|
|
|
Stubs out optional heavy dependencies (flask_socketio) that are not available
|
|
in lightweight dev/CI environments. These are only needed by the IRC source,
|
|
which is unrelated to the slskd plugin under test.
|
|
"""
|
|
|
|
import importlib.util
|
|
import sys
|
|
import types
|
|
|
|
|
|
def _stub_module(name: str) -> None:
|
|
"""Insert a minimal stub module into sys.modules if the real one is not installed.
|
|
|
|
A stub over an installed module would break every later test in the same worker that
|
|
needs the real one (the Flask app imports ``flask_socketio.emit``).
|
|
"""
|
|
if name not in sys.modules and importlib.util.find_spec(name) is None:
|
|
stub = types.ModuleType(name)
|
|
# Provide no-op stand-ins for the symbols IRC source imports.
|
|
stub.SocketIO = object
|
|
stub.join_room = lambda *a, **kw: None
|
|
stub.leave_room = lambda *a, **kw: None
|
|
sys.modules[name] = stub
|
|
|
|
|
|
_stub_module("flask_socketio")
|