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:
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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"] == []
|
||||
|
||||
Reference in New Issue
Block a user