Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion src/wingfoot/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import argparse
import sys
import urllib.error
from urllib.parse import urlsplit

from . import DIRECTORY_PATH, __version__
Expand Down Expand Up @@ -102,7 +103,13 @@ def cmd_sign(args) -> int:
for k, v in signed.headers.items():
print(f"{k}: {v}")
return 0
resp = _http.request(args.url, method=args.method, headers=signed.headers)
try:
resp = _http.request(args.url, method=args.method, headers=signed.headers)
except urllib.error.URLError as exc:
# A connection failure (DNS, refused, timeout) has no HTTP status, so
# `http.request` lets it through. Report it cleanly instead of a traceback.
print(f"{C.red}Could not reach {args.url}{C.reset}: {exc.reason}", file=sys.stderr)
return 1
color = C.green if 200 <= resp.status < 300 else C.red
print(f"{color}HTTP {resp.status}{C.reset}")
for k, v in signed.headers.items():
Expand Down
25 changes: 25 additions & 0 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
"""Bare `wingfoot` greets with the full help instead of an argparse error."""
import urllib.error

import pytest

from wingfoot.cli import main
Expand All @@ -16,3 +18,26 @@ def test_unknown_command_still_errors():
with pytest.raises(SystemExit) as exc:
main(["not-a-command"])
assert exc.value.code == 2


def test_sign_reports_connection_error_cleanly(monkeypatch, capsys):
"""`wingfoot sign <unreachable>` should print a clean error and exit 1,
not surface a urllib traceback to the user."""
def boom(*args, **kwargs):
raise urllib.error.URLError("Connection refused")

monkeypatch.setattr("wingfoot.cli._http.request", boom)
rc = main(["sign", "http://127.0.0.1:1/"])
assert rc == 1
err = capsys.readouterr().err
assert "Could not reach http://127.0.0.1:1/" in err
assert "Connection refused" in err


def test_sign_print_only_needs_no_network(monkeypatch):
"""--print-only must never touch the network."""
def boom(*args, **kwargs):
raise AssertionError("network should not be used with --print-only")

monkeypatch.setattr("wingfoot.cli._http.request", boom)
assert main(["sign", "https://example.com/", "--print-only"]) == 0
Loading