Marco Ricci commited on 2026-06-21 21:02:26
Zeige 1 geänderte Dateien mit 67 Einfügungen und 66 Löschungen.
Several tests for the `vault` CLI check for failures when storing or retrieving a selected SSH key to/from the `vault` settings file. These tests used to run against every known SSH agent, separately, but not truly interact with the agent at all, because all high-level interaction was stubbed out to return error responses anyway. For externally spawned or interfaced SSH agents, this is wasteful. So we now restrict these tests to run against the stubbed agent, which is specifically designed for these purposes. Half of these tests previously falsely claimed that they don't actually need an external agent; now, they no longer lie. The other half never made any claims; now, they do. There may be other tests that request external SSH agents but don't actually require them, but this is somewhat hard to discover exhaustively. These tests were an obvious first group of tests that could be fixed in one go. Other groups (if any) will be left to future commits.
| ... | ... |
@@ -19,7 +19,6 @@ import copy |
| 19 | 19 |
import errno |
| 20 | 20 |
import json |
| 21 | 21 |
import os |
| 22 |
-import pathlib |
|
| 23 | 22 |
import shutil |
| 24 | 23 |
import types |
| 25 | 24 |
from typing import TYPE_CHECKING |
| ... | ... |
@@ -32,7 +31,6 @@ from derivepassphrase import _types, cli, ssh_agent |
| 32 | 31 |
from derivepassphrase._internals import ( |
| 33 | 32 |
cli_helpers, |
| 34 | 33 |
) |
| 35 |
-from derivepassphrase.ssh_agent import socketprovider |
|
| 36 | 34 |
from tests import data, machinery |
| 37 | 35 |
from tests.data import callables |
| 38 | 36 |
from tests.machinery import hypothesis as hypothesis_machinery |
| ... | ... |
@@ -49,6 +47,34 @@ DUMMY_SERVICE = data.DUMMY_SERVICE |
| 49 | 47 |
DUMMY_KEY1_B64 = data.DUMMY_KEY1_B64 |
| 50 | 48 |
|
| 51 | 49 |
|
| 50 |
+@pytest.fixture |
|
| 51 |
+def use_stub_agent() -> Generator[None, None, None]: |
|
| 52 |
+ """Enforce use of the stubbed SSH agent.""" |
|
| 53 |
+ with pytest.MonkeyPatch.context() as monkeypatch: |
|
| 54 |
+ monkeypatch.setattr( |
|
| 55 |
+ ssh_agent.SSHAgentClient, |
|
| 56 |
+ "SOCKET_PROVIDERS", |
|
| 57 |
+ (_types.BuiltinSSHAgentSocketProvider.STUB_AGENT,), |
|
| 58 |
+ ) |
|
| 59 |
+ monkeypatch.delenv("SSH_AUTH_SOCK", raising=False)
|
|
| 60 |
+ yield |
|
| 61 |
+ |
|
| 62 |
+ |
|
| 63 |
+@pytest.fixture |
|
| 64 |
+def use_stub_agent_with_address() -> Generator[None, None, None]: |
|
| 65 |
+ """Enforce use of the stubbed SSH agent with socket address.""" |
|
| 66 |
+ with pytest.MonkeyPatch.context() as monkeypatch: |
|
| 67 |
+ monkeypatch.setattr( |
|
| 68 |
+ ssh_agent.SSHAgentClient, |
|
| 69 |
+ "SOCKET_PROVIDERS", |
|
| 70 |
+ (_types.BuiltinSSHAgentSocketProvider.STUB_AGENT_WITH_ADDRESS,), |
|
| 71 |
+ ) |
|
| 72 |
+ monkeypatch.setenv( |
|
| 73 |
+ "SSH_AUTH_SOCK", machinery.StubbedSSHAgentSocketWithAddress.ADDRESS |
|
| 74 |
+ ) |
|
| 75 |
+ yield |
|
| 76 |
+ |
|
| 77 |
+ |
|
| 52 | 78 |
def is_harmless_config_import_warning(record: tuple[str, int, str]) -> bool: |
| 53 | 79 |
"""Return true if the warning is harmless, during config import.""" |
| 54 | 80 |
possible_warnings = [ |
| ... | ... |
@@ -786,65 +812,40 @@ class TestStoringConfigurationFailures: |
| 786 | 812 |
) |
| 787 | 813 |
|
| 788 | 814 |
def test_fail_because_no_ssh_agent( |
| 789 |
- self, spawn_ssh_agent: data.SpawnedSSHAgentInfo |
|
| 815 |
+ self, |
|
| 816 |
+ use_stub_agent_with_address: None, |
|
| 790 | 817 |
) -> None: |
| 791 | 818 |
"""Not running an SSH agent during `--config --key` fails. |
| 792 | 819 |
|
| 793 |
- (This test does not actually need a running agent; the agent's |
|
| 794 |
- response is mocked by the test harness.) |
|
| 820 |
+ (This test runs against the stub agent; it does not actually |
|
| 821 |
+ need an agent installed on the user's system.) |
|
| 795 | 822 |
|
| 796 | 823 |
""" |
| 797 |
- del spawn_ssh_agent |
|
| 824 |
+ del use_stub_agent_with_address |
|
| 798 | 825 |
with self._test( |
| 799 | 826 |
["--key"], |
| 800 | 827 |
error_text="Cannot find any running SSH agent", |
| 801 | 828 |
patch_suitable_ssh_keys=False, |
| 802 | 829 |
) as monkeypatch: |
| 803 |
- |
|
| 804 |
- def no_agent( |
|
| 805 |
- *_args: Any, **_kwargs: Any |
|
| 806 |
- ) -> NoReturn: # pragma: no cover [failsafe] |
|
| 807 |
- raise KeyError("SSH_AUTH_SOCK") # noqa: EM101
|
|
| 808 |
- |
|
| 809 |
- monkeypatch.setattr( |
|
| 810 |
- socketprovider.WindowsNamedPipeHandle, "for_pageant", no_agent |
|
| 811 |
- ) |
|
| 812 |
- monkeypatch.setattr( |
|
| 813 |
- socketprovider.WindowsNamedPipeHandle, "for_openssh", no_agent |
|
| 814 |
- ) |
|
| 815 | 830 |
monkeypatch.delenv("SSH_AUTH_SOCK", raising=False)
|
| 816 | 831 |
|
| 817 | 832 |
def test_fail_because_bad_ssh_agent_connection( |
| 818 |
- self, spawn_ssh_agent: data.SpawnedSSHAgentInfo |
|
| 833 |
+ self, |
|
| 834 |
+ use_stub_agent_with_address: None, |
|
| 819 | 835 |
) -> None: |
| 820 |
- """Not running a reachable SSH agent during `--config --key` fails.""" |
|
| 821 |
- del spawn_ssh_agent |
|
| 836 |
+ """Not running a reachable SSH agent during `--config --key` fails. |
|
| 837 |
+ |
|
| 838 |
+ (This test runs against the stub agent; it does not actually |
|
| 839 |
+ need an agent installed on the user's system.) |
|
| 840 |
+ |
|
| 841 |
+ """ |
|
| 842 |
+ del use_stub_agent_with_address |
|
| 822 | 843 |
with self._test( |
| 823 | 844 |
["--key"], |
| 824 | 845 |
error_text="Cannot connect to the SSH agent", |
| 825 | 846 |
patch_suitable_ssh_keys=False, |
| 826 | 847 |
) as monkeypatch: |
| 827 |
- cwd = pathlib.Path.cwd().resolve() |
|
| 828 |
- monkeypatch.setenv( |
|
| 829 |
- "SSH_AUTH_SOCK", |
|
| 830 |
- socketprovider.PIPE_PREFIX if os.name == "nt" else str(cwd), |
|
| 831 |
- ) |
|
| 832 |
- |
|
| 833 |
- def mangled_address( |
|
| 834 |
- *_args: Any, **_kwargs: Any |
|
| 835 |
- ) -> NoReturn: # pragma: no cover [failsafe] |
|
| 836 |
- raise OSError(errno.ENOENT, os.strerror(errno.ENOENT)) |
|
| 837 |
- |
|
| 838 |
- monkeypatch.setattr( |
|
| 839 |
- socketprovider.WindowsNamedPipeHandle, |
|
| 840 |
- "for_pageant", |
|
| 841 |
- mangled_address, |
|
| 842 |
- ) |
|
| 843 |
- monkeypatch.setattr( |
|
| 844 |
- socketprovider.WindowsNamedPipeHandle, |
|
| 845 |
- "for_openssh", |
|
| 846 |
- mangled_address, |
|
| 847 |
- ) |
|
| 848 |
+ monkeypatch.setenv("SSH_AUTH_SOCK", "invalid address format")
|
|
| 848 | 849 |
|
| 849 | 850 |
@Parametrize.TRY_RACE_FREE_IMPLEMENTATION |
| 850 | 851 |
def test_fail_because_read_only_file( |
| ... | ... |
@@ -883,20 +884,20 @@ class TestStoringConfigurationFailures: |
| 883 | 884 |
|
| 884 | 885 |
def test_fail_because_ssh_agent_has_no_keys_loaded( |
| 885 | 886 |
self, |
| 886 |
- request: pytest.FixtureRequest, |
|
| 887 |
- spawn_ssh_agent: data.SpawnedSSHAgentInfo, |
|
| 887 |
+ use_stub_agent: None, |
|
| 888 | 888 |
) -> None: |
| 889 |
- """Not holding any SSH keys during `--config --key` fails.""" |
|
| 889 |
+ """Not holding any SSH keys during `--config --key` fails. |
|
| 890 |
+ |
|
| 891 |
+ (This test does not actually need a running agent; the agent's |
|
| 892 |
+ response is mocked by the test harness.) |
|
| 893 |
+ |
|
| 894 |
+ """ |
|
| 895 |
+ del use_stub_agent |
|
| 890 | 896 |
with self._test( |
| 891 | 897 |
["--key"], |
| 892 | 898 |
error_text="no keys suitable", |
| 893 | 899 |
patch_suitable_ssh_keys=False, |
| 894 | 900 |
) as monkeypatch: |
| 895 |
- pytest_machinery.ensure_singleton_client_if_non_reentrant_agent( |
|
| 896 |
- request=request, |
|
| 897 |
- monkeypatch=monkeypatch, |
|
| 898 |
- client=spawn_ssh_agent.client, |
|
| 899 |
- ) |
|
| 900 | 901 |
|
| 901 | 902 |
def func( |
| 902 | 903 |
*_args: Any, |
| ... | ... |
@@ -908,20 +909,20 @@ class TestStoringConfigurationFailures: |
| 908 | 909 |
|
| 909 | 910 |
def test_store_config_fail_manual_ssh_agent_runtime_error( |
| 910 | 911 |
self, |
| 911 |
- request: pytest.FixtureRequest, |
|
| 912 |
- spawn_ssh_agent: data.SpawnedSSHAgentInfo, |
|
| 912 |
+ use_stub_agent: None, |
|
| 913 | 913 |
) -> None: |
| 914 |
- """Triggering an error in the SSH agent during `--config --key` leads to failure.""" |
|
| 914 |
+ """Triggering an error in the SSH agent during `--config --key` leads to failure. |
|
| 915 |
+ |
|
| 916 |
+ (This test does not actually need a running agent; the agent's |
|
| 917 |
+ response is mocked by the test harness.) |
|
| 918 |
+ |
|
| 919 |
+ """ |
|
| 920 |
+ del use_stub_agent |
|
| 915 | 921 |
with self._test( |
| 916 | 922 |
["--key"], |
| 917 | 923 |
error_text="violates the communication protocol", |
| 918 | 924 |
patch_suitable_ssh_keys=False, |
| 919 | 925 |
) as monkeypatch: |
| 920 |
- pytest_machinery.ensure_singleton_client_if_non_reentrant_agent( |
|
| 921 |
- request=request, |
|
| 922 |
- monkeypatch=monkeypatch, |
|
| 923 |
- client=spawn_ssh_agent.client, |
|
| 924 |
- ) |
|
| 925 | 926 |
|
| 926 | 927 |
def raiser(*_args: Any, **_kwargs: Any) -> None: |
| 927 | 928 |
raise ssh_agent.TrailingDataError() |
| ... | ... |
@@ -930,18 +931,18 @@ class TestStoringConfigurationFailures: |
| 930 | 931 |
|
| 931 | 932 |
def test_store_config_fail_manual_ssh_agent_refuses( |
| 932 | 933 |
self, |
| 933 |
- request: pytest.FixtureRequest, |
|
| 934 |
- spawn_ssh_agent: data.SpawnedSSHAgentInfo, |
|
| 934 |
+ use_stub_agent: None, |
|
| 935 | 935 |
) -> None: |
| 936 |
- """The SSH agent refusing during `--config --key` leads to failure.""" |
|
| 936 |
+ """The SSH agent refusing during `--config --key` leads to failure. |
|
| 937 |
+ |
|
| 938 |
+ (This test does not actually need a running agent; the agent's |
|
| 939 |
+ response is mocked by the test harness.) |
|
| 940 |
+ |
|
| 941 |
+ """ |
|
| 942 |
+ del use_stub_agent |
|
| 937 | 943 |
with self._test( |
| 938 | 944 |
["--key"], error_text="refused to", patch_suitable_ssh_keys=False |
| 939 | 945 |
) as monkeypatch: |
| 940 |
- pytest_machinery.ensure_singleton_client_if_non_reentrant_agent( |
|
| 941 |
- request=request, |
|
| 942 |
- monkeypatch=monkeypatch, |
|
| 943 |
- client=spawn_ssh_agent.client, |
|
| 944 |
- ) |
|
| 945 | 946 |
|
| 946 | 947 |
def func(*_args: Any, **_kwargs: Any) -> NoReturn: |
| 947 | 948 |
raise ssh_agent.SSHAgentFailedError( |
| 948 | 949 |