|
| 1 | +"""Regression test for issue #186: paramiko 4 removed DSSKey, breaking sshtunnel.""" |
| 2 | + |
| 3 | +from __future__ import annotations |
| 4 | + |
| 5 | +import sys |
| 6 | +import types |
| 7 | + |
| 8 | +import pytest |
| 9 | + |
| 10 | +from sqlit.domains.connections.app.tunnel import ensure_ssh_tunnel_available |
| 11 | +from sqlit.domains.connections.providers.exceptions import MissingDriverError |
| 12 | + |
| 13 | + |
| 14 | +def test_ensure_ssh_tunnel_raises_missing_driver_when_paramiko_lacks_dsskey(monkeypatch): |
| 15 | + """A paramiko without DSSKey (i.e. 4.x) must surface as MissingDriverError, not AttributeError.""" |
| 16 | + fake_paramiko = types.ModuleType("paramiko") |
| 17 | + fake_paramiko.__version__ = "4.0.0" # type: ignore[attr-defined] |
| 18 | + # Intentionally no DSSKey attribute — simulating paramiko 4. |
| 19 | + |
| 20 | + fake_sshtunnel = types.ModuleType("sshtunnel") |
| 21 | + |
| 22 | + monkeypatch.setitem(sys.modules, "paramiko", fake_paramiko) |
| 23 | + monkeypatch.setitem(sys.modules, "sshtunnel", fake_sshtunnel) |
| 24 | + |
| 25 | + with pytest.raises(MissingDriverError) as excinfo: |
| 26 | + ensure_ssh_tunnel_available() |
| 27 | + |
| 28 | + assert excinfo.value.extra_name == "ssh" |
| 29 | + assert "paramiko" in str(excinfo.value.import_error or "").lower() |
| 30 | + |
| 31 | + |
| 32 | +def test_ensure_ssh_tunnel_passes_when_paramiko_has_dsskey(monkeypatch): |
| 33 | + """A paramiko 3.x with DSSKey must not raise.""" |
| 34 | + fake_paramiko = types.ModuleType("paramiko") |
| 35 | + fake_paramiko.__version__ = "3.5.0" # type: ignore[attr-defined] |
| 36 | + fake_paramiko.DSSKey = object # type: ignore[attr-defined] |
| 37 | + |
| 38 | + fake_sshtunnel = types.ModuleType("sshtunnel") |
| 39 | + |
| 40 | + monkeypatch.setitem(sys.modules, "paramiko", fake_paramiko) |
| 41 | + monkeypatch.setitem(sys.modules, "sshtunnel", fake_sshtunnel) |
| 42 | + |
| 43 | + ensure_ssh_tunnel_available() # must not raise |
0 commit comments