SurfSense/surfsense_backend/tests/unit/utils/captcha/test_solvers.py
DESKTOP-RTLN3BA\$punk c0ebb62fb2 chore: linting
2026-07-17 14:39:39 -07:00

90 lines
2.7 KiB
Python

"""Offline checks for the in-house captcha-solver seam.
The network solve can't be unit-tested, but the boundary logic can: a wrong
proxy reformat (2captcha's ``ERROR_PROXY_FORMAT``) or dispatching to a provider
we haven't wired silently burns paid solves / leaks the key to the wrong vendor.
"""
import pytest
from app.utils.captcha import solvers
from app.utils.captcha.config import CaptchaConfig
pytestmark = pytest.mark.unit
def _cfg(**overrides) -> CaptchaConfig:
base = {
"enabled": True,
"solving_site": "2captcha",
"api_key": "key-123",
"max_attempts_per_url": 1,
"timeout_s": 30,
"captcha_type_default": "v2",
"v3_min_score": 0.7,
"v3_action": "verify",
}
base.update(overrides)
return CaptchaConfig(**base)
# --- proxy reformat (2captcha wants login:pass@host:port, NO scheme) --------
def test_proxy_login_form_strips_scheme():
got = solvers.proxy_login_form("http://user:pass@gw.dataimpulse.com:15673")
assert got == "user:pass@gw.dataimpulse.com:15673"
def test_proxy_login_form_without_credentials():
assert (
solvers.proxy_login_form("http://gw.dataimpulse.com:823")
== "gw.dataimpulse.com:823"
)
def test_proxy_login_form_none_on_missing_or_bad():
assert solvers.proxy_login_form(None) is None
assert solvers.proxy_login_form("not a url") is None
# --- capsolver proxy reformat (colon-delimited scheme:host:port:user:pass) --
def test_capsolver_proxy_colon_delimited_with_creds():
got = solvers.capsolver_proxy("http://user:pass@gw.dataimpulse.com:823")
assert got == "http:gw.dataimpulse.com:823:user:pass"
def test_capsolver_proxy_without_credentials():
assert (
solvers.capsolver_proxy("http://gw.dataimpulse.com:823")
== "http:gw.dataimpulse.com:823"
)
def test_capsolver_proxy_none_on_missing_or_bad():
assert solvers.capsolver_proxy(None) is None
assert solvers.capsolver_proxy("not a url") is None
# --- dispatch --------------------------------------------------------------
def test_unsupported_provider_raises_solvererror():
# An unconfigured provider must fail loudly (latch) rather than POST the key
# to a wired vendor's endpoint under the wrong account.
with pytest.raises(solvers.SolverUnsupportedError):
solvers.solve(
_cfg(solving_site="anticaptcha"),
challenge_type="v2",
sitekey="SK",
page_url="https://t.test",
)
assert issubclass(solvers.SolverUnsupportedError, solvers.SolverError)
def test_wired_providers_are_registered():
supported = solvers.supported_providers()
assert "2captcha" in supported
assert "capsolver" in supported