feat: add remote_instances and device_name to settings

- Add 'remote_instances': [] to DEFAULT_SETTINGS for peer instance configs
- Add 'device_name': '' to DEFAULT_SETTINGS (empty for clean JSON serialization)
- Add import socket to settings.py
- In load_settings(), fill empty device_name with socket.gethostname() so the
  hostname is always current even if the machine is renamed
- Add 7 new tests covering defaults, hostname fallback, round-trips, and
  mutation isolation
- Update 2 existing tests that compared result == DEFAULT_SETTINGS to account
  for the hostname fill-in behavior

Co-authored-by: Amplifier <amplifier@amplified.dev>
This commit is contained in:
Brian Krabach
2026-03-30 20:48:38 -07:00
parent e94a27d2f8
commit 395278818f
2 changed files with 95 additions and 4 deletions
+5
View File
@@ -6,6 +6,7 @@ Settings are stored at ~/.config/muxplex/settings.json.
import copy
import json
import socket
from pathlib import Path
SETTINGS_PATH = Path.home() / ".config" / "muxplex" / "settings.json"
@@ -17,6 +18,8 @@ DEFAULT_SETTINGS: dict = {
"window_size_largest": False,
"auto_open_created": True,
"new_session_template": "tmux new-session -d -s {name}",
"remote_instances": [],
"device_name": "",
}
@@ -35,6 +38,8 @@ def load_settings() -> dict:
result[key] = data[key]
except (FileNotFoundError, json.JSONDecodeError):
pass
if not result["device_name"]:
result["device_name"] = socket.gethostname()
return result
+90 -4
View File
@@ -34,10 +34,14 @@ def redirect_settings_path(tmp_path, monkeypatch):
# ---------------------------------------------------------------------------
def test_load_returns_defaults_when_no_file():
"""load_settings() returns DEFAULT_SETTINGS when no file exists."""
def test_load_returns_defaults_when_no_file(monkeypatch):
"""load_settings() returns DEFAULT_SETTINGS (with hostname fill-in) when no file exists."""
import socket
monkeypatch.setattr(socket, "gethostname", lambda: "test-host")
result = load_settings()
assert result == DEFAULT_SETTINGS
expected = {**DEFAULT_SETTINGS, "device_name": "test-host"}
assert result == expected
def test_load_returns_saved_values(tmp_path, monkeypatch):
@@ -82,13 +86,17 @@ def test_save_merges_with_defaults(tmp_path, monkeypatch):
def test_load_handles_corrupt_json(tmp_path, monkeypatch):
"""load_settings() returns defaults gracefully on corrupt JSON."""
import socket
fake_path = tmp_path / "settings.json"
monkeypatch.setattr(settings_mod, "SETTINGS_PATH", fake_path)
monkeypatch.setattr(socket, "gethostname", lambda: "test-host")
fake_path.write_text("NOT VALID JSON {{{{")
result = load_settings()
assert result == DEFAULT_SETTINGS
expected = {**DEFAULT_SETTINGS, "device_name": "test-host"}
assert result == expected
def test_patch_settings_merges_single_field(tmp_path, monkeypatch):
@@ -142,3 +150,81 @@ def test_load_propagates_non_json_errors(monkeypatch):
with pytest.raises(PermissionError):
load_settings()
# ---------------------------------------------------------------------------
# Federation fields tests (task-3-extend-settings-federation-fields)
# ---------------------------------------------------------------------------
def test_defaults_include_remote_instances():
"""DEFAULT_SETTINGS must have 'remote_instances' key initialised to []."""
assert "remote_instances" in DEFAULT_SETTINGS
assert DEFAULT_SETTINGS["remote_instances"] == []
def test_defaults_include_device_name():
"""DEFAULT_SETTINGS must have 'device_name' key initialised to empty string."""
assert "device_name" in DEFAULT_SETTINGS
assert DEFAULT_SETTINGS["device_name"] == ""
def test_load_returns_hostname_when_device_name_empty(monkeypatch):
"""load_settings() fills empty device_name with socket.gethostname()."""
import socket
monkeypatch.setattr(socket, "gethostname", lambda: "my-laptop")
result = load_settings()
assert result["device_name"] == "my-laptop"
def test_load_preserves_explicit_device_name(tmp_path, monkeypatch):
"""load_settings() keeps an explicitly saved device_name (does not overwrite with hostname)."""
import socket
fake_path = tmp_path / "settings.json"
monkeypatch.setattr(settings_mod, "SETTINGS_PATH", fake_path)
fake_path.write_text(json.dumps({"device_name": "Work PC"}))
monkeypatch.setattr(socket, "gethostname", lambda: "should-not-appear")
result = load_settings()
assert result["device_name"] == "Work PC"
def test_remote_instances_round_trip(tmp_path, monkeypatch):
"""remote_instances survive a save/load cycle unchanged."""
fake_path = tmp_path / "settings.json"
monkeypatch.setattr(settings_mod, "SETTINGS_PATH", fake_path)
instances = [
{"url": "http://host1:7681", "name": "Host 1"},
{"url": "http://host2:7681", "name": "Host 2"},
]
save_settings({"remote_instances": instances})
result = load_settings()
assert result["remote_instances"] == instances
def test_device_name_round_trip(tmp_path, monkeypatch):
"""An explicit device_name survives a save/load cycle unchanged."""
fake_path = tmp_path / "settings.json"
monkeypatch.setattr(settings_mod, "SETTINGS_PATH", fake_path)
save_settings({"device_name": "My Server"})
result = load_settings()
assert result["device_name"] == "My Server"
def test_load_does_not_mutate_default_remote_instances():
"""Mutating the list returned by load_settings() must not corrupt DEFAULT_SETTINGS."""
result = load_settings()
result["remote_instances"].append({"url": "http://leaked", "name": "leaked"})
# DEFAULT_SETTINGS must be unchanged
assert DEFAULT_SETTINGS["remote_instances"] == []
# A second load must still return the clean default
result2 = load_settings()
assert result2["remote_instances"] == []