feat: add setup_tls_status() and --status flag to setup-tls subcommand
- Added setup_tls_status() function to display current TLS configuration status - Shows cert path, key path, hostnames, expiry, and status when configured - Shows 'not configured' prompt when TLS is not set up - Shows 'configured but cert not readable' when cert exists in settings but file isn't accessible - Added --status flag to setup-tls argparse subparser - Updated main() dispatch to call setup_tls_status() when --status flag is passed - Added 4 new tests covering all scenarios All 95 tests pass.
This commit is contained in:
@@ -816,6 +816,41 @@ def setup_tls(method: str = "auto") -> None:
|
|||||||
print(" Restart service to apply: muxplex service restart")
|
print(" Restart service to apply: muxplex service restart")
|
||||||
|
|
||||||
|
|
||||||
|
def setup_tls_status() -> None:
|
||||||
|
"""Display the current TLS configuration status."""
|
||||||
|
from muxplex.settings import load_settings # noqa: PLC0415
|
||||||
|
from muxplex.tls import get_cert_info # noqa: PLC0415
|
||||||
|
|
||||||
|
settings = load_settings()
|
||||||
|
tls_cert = settings.get("tls_cert", "")
|
||||||
|
tls_key = settings.get("tls_key", "")
|
||||||
|
|
||||||
|
print("muxplex TLS status")
|
||||||
|
print()
|
||||||
|
|
||||||
|
if not tls_cert or not tls_key:
|
||||||
|
print(" TLS: not configured")
|
||||||
|
print(" Run: muxplex setup-tls")
|
||||||
|
return
|
||||||
|
|
||||||
|
print(f" Certificate: {tls_cert}")
|
||||||
|
print(f" Key: {tls_key}")
|
||||||
|
|
||||||
|
cert_info = get_cert_info(tls_cert)
|
||||||
|
if cert_info is None:
|
||||||
|
print(" Status: configured but cert not readable")
|
||||||
|
return
|
||||||
|
|
||||||
|
hostnames_str = ", ".join(cert_info["hostnames"])
|
||||||
|
expires = cert_info["expires"]
|
||||||
|
expiry_str = (
|
||||||
|
expires.strftime("%Y-%m-%d") if hasattr(expires, "strftime") else str(expires)
|
||||||
|
)
|
||||||
|
print(f" Hostnames: {hostnames_str}")
|
||||||
|
print(f" Expires: {expiry_str}")
|
||||||
|
print(" Status: enabled")
|
||||||
|
|
||||||
|
|
||||||
def _add_serve_flags(parser: argparse.ArgumentParser) -> None:
|
def _add_serve_flags(parser: argparse.ArgumentParser) -> None:
|
||||||
"""Add --host, --port, --auth, --session-ttl, --tls-cert, --tls-key flags to a parser.
|
"""Add --host, --port, --auth, --session-ttl, --tls-cert, --tls-key flags to a parser.
|
||||||
|
|
||||||
@@ -918,6 +953,11 @@ def main() -> None:
|
|||||||
default="auto",
|
default="auto",
|
||||||
help="Certificate generation method (default: auto)",
|
help="Certificate generation method (default: auto)",
|
||||||
)
|
)
|
||||||
|
setup_tls_parser.add_argument(
|
||||||
|
"--status",
|
||||||
|
action="store_true",
|
||||||
|
help="Show current TLS configuration status",
|
||||||
|
)
|
||||||
|
|
||||||
config_parser = sub.add_parser("config", help="View and manage settings")
|
config_parser = sub.add_parser("config", help="View and manage settings")
|
||||||
config_sub = config_parser.add_subparsers(dest="config_command")
|
config_sub = config_parser.add_subparsers(dest="config_command")
|
||||||
@@ -956,6 +996,9 @@ def main() -> None:
|
|||||||
# Default: list (no subcommand or explicit "list")
|
# Default: list (no subcommand or explicit "list")
|
||||||
config_list()
|
config_list()
|
||||||
elif args.command == "setup-tls":
|
elif args.command == "setup-tls":
|
||||||
|
if args.status:
|
||||||
|
setup_tls_status()
|
||||||
|
else:
|
||||||
setup_tls(method=args.method)
|
setup_tls(method=args.method)
|
||||||
elif args.command == "service":
|
elif args.command == "service":
|
||||||
from muxplex.service import ( # noqa: PLC0415
|
from muxplex.service import ( # noqa: PLC0415
|
||||||
|
|||||||
@@ -1785,6 +1785,101 @@ def test_setup_tls_auto_falls_to_selfsigned_when_nothing_available(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# task-5-status-display: setup-tls --status tests
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
def test_setup_tls_status_shows_disabled(tmp_path, monkeypatch, capsys):
|
||||||
|
"""setup_tls_status() shows 'not configured' when no TLS certs are configured."""
|
||||||
|
import muxplex.settings as settings_mod
|
||||||
|
|
||||||
|
# Empty settings — no tls_cert or tls_key
|
||||||
|
settings_file = tmp_path / "settings.json"
|
||||||
|
settings_file.write_text("{}")
|
||||||
|
monkeypatch.setattr(settings_mod, "SETTINGS_PATH", settings_file)
|
||||||
|
|
||||||
|
from muxplex.cli import setup_tls_status
|
||||||
|
|
||||||
|
setup_tls_status()
|
||||||
|
|
||||||
|
out = capsys.readouterr().out
|
||||||
|
out_lower = out.lower()
|
||||||
|
assert "not configured" in out_lower or "disabled" in out_lower, (
|
||||||
|
f"Expected 'not configured' or 'disabled' in output, got: {out!r}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_setup_tls_status_shows_enabled(tmp_path, monkeypatch, capsys):
|
||||||
|
"""setup_tls_status() shows 'enabled' and 'expires' when valid certs are configured."""
|
||||||
|
import json
|
||||||
|
|
||||||
|
import muxplex.settings as settings_mod
|
||||||
|
from muxplex.tls import generate_self_signed
|
||||||
|
|
||||||
|
# Generate real self-signed certs in tmp_path
|
||||||
|
cert_path = tmp_path / "muxplex.crt"
|
||||||
|
key_path = tmp_path / "muxplex.key"
|
||||||
|
generate_self_signed(cert_path, key_path)
|
||||||
|
|
||||||
|
settings_file = tmp_path / "settings.json"
|
||||||
|
settings_file.write_text(
|
||||||
|
json.dumps({"tls_cert": str(cert_path), "tls_key": str(key_path)})
|
||||||
|
)
|
||||||
|
monkeypatch.setattr(settings_mod, "SETTINGS_PATH", settings_file)
|
||||||
|
|
||||||
|
from muxplex.cli import setup_tls_status
|
||||||
|
|
||||||
|
setup_tls_status()
|
||||||
|
|
||||||
|
out = capsys.readouterr().out
|
||||||
|
out_lower = out.lower()
|
||||||
|
assert "enabled" in out_lower or "certificate" in out_lower, (
|
||||||
|
f"Expected 'enabled' or 'certificate' in output, got: {out!r}"
|
||||||
|
)
|
||||||
|
assert "expires" in out_lower, f"Expected 'expires' in output, got: {out!r}"
|
||||||
|
|
||||||
|
|
||||||
|
def test_setup_tls_status_flag_registered():
|
||||||
|
"""setup-tls --status must be accepted by argparse."""
|
||||||
|
import io
|
||||||
|
|
||||||
|
from muxplex.cli import main
|
||||||
|
|
||||||
|
buf = io.StringIO()
|
||||||
|
with patch("sys.argv", ["muxplex", "setup-tls", "--help"]):
|
||||||
|
try:
|
||||||
|
with patch("sys.stdout", buf):
|
||||||
|
main()
|
||||||
|
except SystemExit:
|
||||||
|
pass
|
||||||
|
|
||||||
|
help_text = buf.getvalue()
|
||||||
|
assert "--status" in help_text, (
|
||||||
|
f"Expected '--status' in setup-tls --help output, got:\n{help_text}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_main_dispatches_status_flag_to_setup_tls_status(monkeypatch):
|
||||||
|
"""main() with 'setup-tls --status' must invoke setup_tls_status(), not setup_tls()."""
|
||||||
|
import muxplex.cli as cli_mod
|
||||||
|
|
||||||
|
status_calls = []
|
||||||
|
setup_calls = []
|
||||||
|
monkeypatch.setattr(cli_mod, "setup_tls_status", lambda: status_calls.append(True))
|
||||||
|
monkeypatch.setattr(
|
||||||
|
cli_mod, "setup_tls", lambda method="auto": setup_calls.append(method)
|
||||||
|
)
|
||||||
|
|
||||||
|
with patch("sys.argv", ["muxplex", "setup-tls", "--status"]):
|
||||||
|
cli_mod.main()
|
||||||
|
|
||||||
|
assert len(status_calls) == 1, (
|
||||||
|
"setup_tls_status() must be called once for 'setup-tls --status'"
|
||||||
|
)
|
||||||
|
assert len(setup_calls) == 0, "setup_tls() must NOT be called when --status is used"
|
||||||
|
|
||||||
|
|
||||||
def test_setup_tls_method_choices_expanded():
|
def test_setup_tls_method_choices_expanded():
|
||||||
"""setup-tls --help must show 'tailscale' and 'mkcert' as method choices."""
|
"""setup-tls --help must show 'tailscale' and 'mkcert' as method choices."""
|
||||||
import io
|
import io
|
||||||
|
|||||||
Reference in New Issue
Block a user