Files
shelfmark/tests/prowlarr/test_debridlink_settings.py
splitsec2 cad18b8019 feat(clients): add Debrid-Link as a torrent client (#1380)
calibrain said in #1315 that he'd review and merge a Debrid-Link client
if someone took a stab at it, so here it is. It's the fourth debrid
service alongside AllDebrid, Real-Debrid and TorBox and it uses the same
DownloadClient interface as the other three. No frontend changes, the
settings UI picks it up from the backend field definitions.

**I can't test this against the live API.** I don't use debrid and I
don't have a Debrid-Link key. Everything here is built from their v2 API
docs and cross-checked against two independent reference
implementations, so the request shapes should be right, but nobody has
run a real magnet through it yet.

@Tatsu941 you opened the issue and mentioned they give developers free
premium. Would you be able to try it? The things most worth confirming
are the field names on a seedbox torrent, that `premiumLeft` is the
right premium check, and that an API error comes back the way the client
expects. Happy to fix whatever turns up.

Two things about their API shaped how this is written.

Every v2 endpoint wraps its payload in a `{success, value}` envelope and
a failure can arrive with an HTTP 200, so `_request_value` decides the
outcome from the body instead of the status code.

A completed seedbox torrent already carries a direct `downloadUrl` on
every file. The other three clients each need a separate unrestrict or
link-request call per file and this one doesn't, which is why it came
out shorter than them despite doing the same job.

Endpoints used:

| Endpoint | Used for |
|---|---|
| `GET account/infos` | test the key, confirm premium time remains |
| `POST seedbox/add` | magnet as JSON, .torrent as multipart |
| `GET seedbox/list` | poll progress |
| `DELETE seedbox/{id}/remove` | clean up |

Ruff and basedpyright are clean and the 36 new tests pass. The two
failures in `tests/e2e/test_proxy_auth_middleware.py` are already on
main and fail the same way on a pristine checkout. They assume the first
proxy-authenticated user becomes an admin, which only holds while the
user table has no admin in it, so they depend on how many users earlier
tests created.

One thing for review: the download loop uses `download_url` like the
other three clients, which holds each file in memory. I kept the
existing pattern rather than do something different in a new-provider
PR, but say so if you'd rather it streamed here.

Closes #1315
2026-10-03 07:55:36 -04:00

50 lines
2.0 KiB
Python

"""Debrid-Link Download Client settings and connection-action tests."""
from unittest.mock import MagicMock
from shelfmark.core.settings_registry import ActionButton, PasswordField
def _field(fields, key):
return next(field for field in fields if field.key == key)
def test_debridlink_fields_are_registered_with_conditional_visibility():
from shelfmark.download.clients.settings import prowlarr_clients_settings
fields = prowlarr_clients_settings()
client_field = _field(fields, "PROWLARR_TORRENT_CLIENT")
api_key_field = _field(fields, "DEBRIDLINK_API_KEY")
test_action = _field(fields, "test_debridlink")
assert {option["value"] for option in client_field.options} >= {"debridlink"}
assert isinstance(api_key_field, PasswordField)
assert api_key_field.show_when == {"field": "PROWLARR_TORRENT_CLIENT", "value": "debridlink"}
assert isinstance(test_action, ActionButton)
assert test_action.show_when == {"field": "PROWLARR_TORRENT_CLIENT", "value": "debridlink"}
def test_debridlink_connection_action_uses_unsaved_api_key(monkeypatch):
from shelfmark.core.config import config
from shelfmark.download.clients import settings as settings_module
client = MagicMock()
client.test_connection.return_value = (True, "Connected")
client_type = MagicMock(return_value=client)
monkeypatch.setattr("shelfmark.download.clients.debridlink.DebridLinkClient", client_type)
monkeypatch.setattr(config, "get", lambda _key, default="": "saved-key")
result = settings_module._test_debridlink_connection({"DEBRIDLINK_API_KEY": "unsaved-key"})
assert result == {"success": True, "message": "Connected"}
assert client._api_key == "unsaved-key"
def test_debridlink_connection_action_requires_api_key():
from shelfmark.download.clients.settings import _test_debridlink_connection
assert _test_debridlink_connection({"DEBRIDLINK_API_KEY": ""}) == {
"success": False,
"message": "Debrid-Link API Key is required",
}