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:
Brian Krabach
2026-04-03 23:21:05 -07:00
parent 3bba2d44e2
commit 52d0021f06
2 changed files with 139 additions and 1 deletions
+44 -1
View File
@@ -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,7 +996,10 @@ 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":
setup_tls(method=args.method) if args.status:
setup_tls_status()
else:
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
service_install, service_install,
+95
View File
@@ -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