test_gateway_linger.py
1 """Tests for gateway linger auto-enable behavior on headless Linux installs.""" 2 3 from types import SimpleNamespace 4 5 import hermes_cli.gateway as gateway 6 7 8 class TestEnsureLingerEnabled: 9 def test_linger_already_enabled_via_file(self, monkeypatch, capsys): 10 monkeypatch.setattr(gateway, "is_linux", lambda: True) 11 monkeypatch.setattr(gateway, "is_termux", lambda: False) 12 monkeypatch.setattr("getpass.getuser", lambda: "testuser") 13 monkeypatch.setattr(gateway, "Path", lambda _path: SimpleNamespace(exists=lambda: True)) 14 15 calls = [] 16 monkeypatch.setattr(gateway.subprocess, "run", lambda *args, **kwargs: calls.append((args, kwargs))) 17 18 gateway._ensure_linger_enabled() 19 20 out = capsys.readouterr().out 21 assert "Systemd linger is enabled" in out 22 assert calls == [] 23 24 def test_status_enabled_skips_enable(self, monkeypatch, capsys): 25 monkeypatch.setattr(gateway, "is_linux", lambda: True) 26 monkeypatch.setattr(gateway, "is_termux", lambda: False) 27 monkeypatch.setattr("getpass.getuser", lambda: "testuser") 28 monkeypatch.setattr(gateway, "Path", lambda _path: SimpleNamespace(exists=lambda: False)) 29 monkeypatch.setattr(gateway, "get_systemd_linger_status", lambda: (True, "")) 30 31 calls = [] 32 monkeypatch.setattr(gateway.subprocess, "run", lambda *args, **kwargs: calls.append((args, kwargs))) 33 34 gateway._ensure_linger_enabled() 35 36 out = capsys.readouterr().out 37 assert "Systemd linger is enabled" in out 38 assert calls == [] 39 40 def test_loginctl_success_enables_linger(self, monkeypatch, capsys): 41 monkeypatch.setattr(gateway, "is_linux", lambda: True) 42 monkeypatch.setattr(gateway, "is_termux", lambda: False) 43 monkeypatch.setattr("getpass.getuser", lambda: "testuser") 44 monkeypatch.setattr(gateway, "Path", lambda _path: SimpleNamespace(exists=lambda: False)) 45 monkeypatch.setattr(gateway, "get_systemd_linger_status", lambda: (False, "")) 46 monkeypatch.setattr("shutil.which", lambda name: "/usr/bin/loginctl") 47 48 run_calls = [] 49 50 def fake_run(cmd, capture_output=False, text=False, check=False, **kwargs): 51 run_calls.append((cmd, capture_output, text, check)) 52 return SimpleNamespace(returncode=0, stdout="", stderr="") 53 54 monkeypatch.setattr(gateway.subprocess, "run", fake_run) 55 56 gateway._ensure_linger_enabled() 57 58 out = capsys.readouterr().out 59 assert "Enabling linger" in out 60 assert "Linger enabled" in out 61 assert run_calls == [(["loginctl", "enable-linger", "testuser"], True, True, False)] 62 63 def test_missing_loginctl_shows_manual_guidance(self, monkeypatch, capsys): 64 monkeypatch.setattr(gateway, "is_linux", lambda: True) 65 monkeypatch.setattr(gateway, "is_termux", lambda: False) 66 monkeypatch.setattr("getpass.getuser", lambda: "testuser") 67 monkeypatch.setattr(gateway, "Path", lambda _path: SimpleNamespace(exists=lambda: False)) 68 monkeypatch.setattr(gateway, "get_systemd_linger_status", lambda: (None, "loginctl not found")) 69 monkeypatch.setattr("shutil.which", lambda name: None) 70 71 calls = [] 72 monkeypatch.setattr(gateway.subprocess, "run", lambda *args, **kwargs: calls.append((args, kwargs))) 73 74 gateway._ensure_linger_enabled() 75 76 out = capsys.readouterr().out 77 assert "sudo loginctl enable-linger testuser" in out 78 assert "loginctl not found" in out 79 assert calls == [] 80 81 def test_loginctl_failure_shows_manual_guidance(self, monkeypatch, capsys): 82 monkeypatch.setattr(gateway, "is_linux", lambda: True) 83 monkeypatch.setattr(gateway, "is_termux", lambda: False) 84 monkeypatch.setattr("getpass.getuser", lambda: "testuser") 85 monkeypatch.setattr(gateway, "Path", lambda _path: SimpleNamespace(exists=lambda: False)) 86 monkeypatch.setattr(gateway, "get_systemd_linger_status", lambda: (False, "")) 87 monkeypatch.setattr("shutil.which", lambda name: "/usr/bin/loginctl") 88 monkeypatch.setattr( 89 gateway.subprocess, 90 "run", 91 lambda *args, **kwargs: SimpleNamespace(returncode=1, stdout="", stderr="Permission denied"), 92 ) 93 94 gateway._ensure_linger_enabled() 95 96 out = capsys.readouterr().out 97 assert "sudo loginctl enable-linger testuser" in out 98 assert "Permission denied" in out 99 100 101 def test_systemd_install_calls_linger_helper(monkeypatch, tmp_path, capsys): 102 unit_path = tmp_path / "systemd" / "user" / "hermes-gateway.service" 103 104 monkeypatch.setattr(gateway, "get_systemd_unit_path", lambda system=False: unit_path) 105 106 calls = [] 107 108 def fake_run(cmd, check=False, **kwargs): 109 calls.append((cmd, check)) 110 return SimpleNamespace(returncode=0, stdout="", stderr="") 111 112 helper_calls = [] 113 monkeypatch.setattr(gateway.subprocess, "run", fake_run) 114 monkeypatch.setattr(gateway, "_ensure_linger_enabled", lambda: helper_calls.append(True)) 115 116 gateway.systemd_install(force=False) 117 118 out = capsys.readouterr().out 119 assert unit_path.exists() 120 assert [cmd for cmd, _ in calls] == [ 121 ["systemctl", "--user", "daemon-reload"], 122 ["systemctl", "--user", "enable", gateway.get_service_name()], 123 ] 124 assert helper_calls == [True] 125 assert "User service installed and enabled" in out