Ensure the SSH agent unhappy path/misbehavior tests always run
Marco Ricci

Marco Ricci commited on 2026-06-14 10:05:10
Zeige 1 geänderte Dateien mit 14 Einfügungen und 13 Löschungen.


The tests for various unhappy paths or misbehavior of the SSH agent now
run against the stubbed SSH agent, instead of requiring an external SSH
agent (and failing or skipping if that external agent is unavailable).
The tests need to intercept all I/O from the agent anyway, so there is
little point in requiring to connect to an external agent but then not
doing any I/O with the agent.  It just introduces another unrelated and
unnecessary failure mode (e.g., SSH_AUTH_SOCK not set up).

We put the fixture for forcing use of the stubbed SSH agent at the top
level of the `ssh_agent` module tests, because that currently seems to
be the only place where we need to force use of an internal agent over
an external one.
... ...
@@ -38,6 +38,19 @@ if sys.version_info < (3, 11):
38 38
     from exceptiongroup import ExceptionGroup
39 39
 
40 40
 
41
+@pytest.fixture
42
+def use_stub_agent() -> Iterator[None]:
43
+    """Enforce use of the stubbed SSH agent."""
44
+    with pytest.MonkeyPatch.context() as monkeypatch:
45
+        monkeypatch.setattr(
46
+            ssh_agent.SSHAgentClient,
47
+            "SOCKET_PROVIDERS",
48
+            (_types.BuiltinSSHAgentSocketProvider.STUB_AGENT,),
49
+        )
50
+        monkeypatch.delenv("SSH_AUTH_SOCK", raising=False)
51
+        yield
52
+
53
+
41 54
 class Parametrize(types.SimpleNamespace):
42 55
     BAD_ENTRY_POINTS = pytest.mark.parametrize(
43 56
         "additional_entry_points",
... ...
@@ -1154,6 +1167,7 @@ class TestOtherConstructorFeatures:
1154 1167
         ssh_agent.SSHAgentClient(socket=conn)
1155 1168
 
1156 1169
 
1170
+@pytest.mark.usefixtures("use_stub_agent")
1157 1171
 class TestAgentErrorResponses:
1158 1172
     """Test actually talking to the SSH agent: errors from the SSH agent."""
1159 1173
 
... ...
@@ -1190,11 +1204,9 @@ class TestAgentErrorResponses:
1190 1204
     @Parametrize.TRUNCATED_AGENT_RESPONSES
1191 1205
     def test_truncated_server_response(
1192 1206
         self,
1193
-        running_ssh_agent: data.RunningSSHAgentInfo,
1194 1207
         response: bytes,
1195 1208
     ) -> None:
1196 1209
         """Fail on truncated responses from the SSH agent."""
1197
-        del running_ssh_agent
1198 1210
         client = ssh_agent.SSHAgentClient()
1199 1211
         response_stream = io.BytesIO(response)
1200 1212
 
... ...
@@ -1214,7 +1226,6 @@ class TestAgentErrorResponses:
1214 1226
     @Parametrize.LIST_KEYS_ERROR_RESPONSES
1215 1227
     def test_list_keys_error_responses(
1216 1228
         self,
1217
-        running_ssh_agent: data.RunningSSHAgentInfo,
1218 1229
         response_code: _types.SSH_AGENT,
1219 1230
         response: bytes | bytearray,
1220 1231
         exc_type: type[Exception],
... ...
@@ -1230,7 +1241,6 @@ class TestAgentErrorResponses:
1230 1241
           - The agent response is overlong.
1231 1242
 
1232 1243
         """
1233
-        del running_ssh_agent
1234 1244
         with self._setup_agent_and_request_handler({
1235 1245
             data.AgentProtocolRequest(
1236 1246
                 _types.SSH_AGENTC.REQUEST_IDENTITIES, b""
... ...
@@ -1243,7 +1253,6 @@ class TestAgentErrorResponses:
1243 1253
     @Parametrize.SIGN_ERROR_RESPONSES
1244 1254
     def test_sign_error_responses(
1245 1255
         self,
1246
-        running_ssh_agent: data.RunningSSHAgentInfo,
1247 1256
         key: bytes | bytearray,
1248 1257
         check: bool,
1249 1258
         response_code: _types.SSH_AGENT,
... ...
@@ -1260,7 +1269,6 @@ class TestAgentErrorResponses:
1260 1269
             failed.
1261 1270
 
1262 1271
         """
1263
-        del running_ssh_agent
1264 1272
         Pair: TypeAlias = _types.SSHKeyCommentPair
1265 1273
         com = b"no comment"
1266 1274
         loaded_keys = [
... ...
@@ -1285,7 +1293,6 @@ class TestAgentErrorResponses:
1285 1293
     @Parametrize.REQUEST_ERROR_RESPONSES
1286 1294
     def test_request_error_responses(
1287 1295
         self,
1288
-        running_ssh_agent: data.RunningSSHAgentInfo,
1289 1296
         request_code: _types.SSH_AGENTC,
1290 1297
         response_code: _types.SSH_AGENT,
1291 1298
         exc_type: type[Exception],
... ...
@@ -1300,8 +1307,6 @@ class TestAgentErrorResponses:
1300 1307
             failed.
1301 1308
 
1302 1309
         """
1303
-        del running_ssh_agent
1304
-
1305 1310
         # TODO(the-13th-letter): Rewrite using parenthesized
1306 1311
         # with-statements.
1307 1312
         # https://the13thletter.info/derivepassphrase/latest/pycompatibility/#after-eol-py3.9
... ...
@@ -1313,11 +1318,9 @@ class TestAgentErrorResponses:
1313 1318
     @Parametrize.QUERY_EXTENSIONS_MALFORMED_RESPONSES
1314 1319
     def test_query_extensions_malformed_responses(
1315 1320
         self,
1316
-        running_ssh_agent: data.RunningSSHAgentInfo,
1317 1321
         response_data: bytes,
1318 1322
     ) -> None:
1319 1323
         """Fail on malformed responses while querying extensions."""
1320
-        del running_ssh_agent
1321 1324
         with self._setup_agent_and_request_handler({
1322 1325
             data.AgentProtocolRequest(
1323 1326
                 _types.SSH_AGENTC.EXTENSION,
... ...
@@ -1430,7 +1433,6 @@ class TestAgentErrorResponses:
1430 1433
     )
1431 1434
     def test_tolerating_extra_success_after_extension_response(
1432 1435
         self,
1433
-        running_ssh_agent: data.RunningSSHAgentInfo,
1434 1436
         success_after_query: bool,
1435 1437
         extensions: list[str] | data.AgentProtocolResponse,
1436 1438
     ) -> None:
... ...
@@ -1450,7 +1452,6 @@ class TestAgentErrorResponses:
1450 1452
         [OPENSSH_BZ3967]: https://bugzilla.mindrot.org/show_bug.cgi?id=3967
1451 1453
 
1452 1454
         """
1453
-        del running_ssh_agent
1454 1455
         query_response = (
1455 1456
             extensions
1456 1457
             if isinstance(extensions, data.AgentProtocolResponse)
1457 1458