feat: add 'muxplex generate-federation-key' CLI command
This commit is contained in:
@@ -125,6 +125,19 @@ def _check_for_update(info: dict) -> tuple[bool, str]:
|
||||
return True, "unknown install source — upgrading to be safe"
|
||||
|
||||
|
||||
def generate_federation_key() -> None:
|
||||
"""Generate a random federation key and write it to FEDERATION_KEY_PATH."""
|
||||
import muxplex.settings as settings_mod
|
||||
|
||||
path = settings_mod.FEDERATION_KEY_PATH
|
||||
path.parent.mkdir(mode=0o700, parents=True, exist_ok=True)
|
||||
key = _secrets.token_urlsafe(32)
|
||||
path.write_text(key + "\n")
|
||||
path.chmod(0o600)
|
||||
print(f"Federation key written to {path}")
|
||||
print(f"Key: {key}")
|
||||
|
||||
|
||||
def reset_secret() -> None:
|
||||
"""Regenerate the signing secret and warn that all sessions are now invalid."""
|
||||
path = get_secret_path()
|
||||
@@ -675,6 +688,11 @@ def main() -> None:
|
||||
"reset-secret", help="Regenerate signing secret (invalidates sessions)"
|
||||
)
|
||||
|
||||
sub.add_parser(
|
||||
"generate-federation-key",
|
||||
help="Generate a random federation key and write it to disk",
|
||||
)
|
||||
|
||||
sub.add_parser("doctor", help="Check dependencies and system status")
|
||||
|
||||
upgrade_parser = sub.add_parser(
|
||||
@@ -707,6 +725,8 @@ def main() -> None:
|
||||
show_password()
|
||||
elif args.command == "reset-secret":
|
||||
reset_secret()
|
||||
elif args.command == "generate-federation-key":
|
||||
generate_federation_key()
|
||||
elif args.command == "doctor":
|
||||
doctor()
|
||||
elif args.command in ("upgrade", "update"):
|
||||
|
||||
@@ -1021,6 +1021,42 @@ def test_config_subcommand_registered():
|
||||
assert "reset" in result.stdout
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# task-3: generate-federation-key subcommand tests
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_generate_federation_key_creates_file(tmp_path, monkeypatch, capsys):
|
||||
"""generate_federation_key() creates key file with mode 0600 and prints key info."""
|
||||
import muxplex.settings as settings_mod
|
||||
|
||||
key_file = tmp_path / ".config" / "muxplex" / "federation_key"
|
||||
monkeypatch.setattr(settings_mod, "FEDERATION_KEY_PATH", key_file)
|
||||
|
||||
from muxplex.cli import generate_federation_key
|
||||
|
||||
generate_federation_key()
|
||||
|
||||
# File must exist
|
||||
assert key_file.exists(), "Federation key file must be created"
|
||||
|
||||
# Content must be longer than 20 chars (stripping the trailing newline)
|
||||
content = key_file.read_text().strip()
|
||||
assert len(content) > 20, f"Key must be > 20 chars, got {len(content)}"
|
||||
|
||||
# File mode must be 0600
|
||||
file_mode = stat.S_IMODE(key_file.stat().st_mode)
|
||||
assert file_mode == 0o600, f"Expected 0o600, got {oct(file_mode)}"
|
||||
|
||||
# Output must include key info
|
||||
captured = capsys.readouterr()
|
||||
assert "federation" in captured.out.lower() or "key" in captured.out.lower(), (
|
||||
f"Output must mention key info, got: {captured.out!r}"
|
||||
)
|
||||
# The actual key value must appear in output
|
||||
assert content in captured.out, "Key value must appear in output"
|
||||
|
||||
|
||||
def test_upgrade_uses_service_module_install(monkeypatch, capsys):
|
||||
"""upgrade() must call muxplex.service.service_install."""
|
||||
import subprocess
|
||||
|
||||
Reference in New Issue
Block a user