fix: turn VPN off on sleep/resume and unload; harden teardown
- Detect resume (boottime-monotonic jump) and tear down the tunnel: on wake NetworkManager drops Mudfish's relay route and all traffic black-holes. - Turn the tunnel off on plugin unload (Deck shutdown/restart). - One verified teardown path for disconnect, connect failure/timeout and node-change failure; keep the route snapshot if tap0 stays up. - Toggle can always turn off a stuck tunnel; status failure with tap0 up is reported as an error, not off. - Re-sign in once when the cloud rejects an expired jwt. - Expire cached pings after 10 minutes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
f957489866
commit
8c595389a4
9 files changed
+372
-58
No files matched your search
+39
-1
@@ -1,7 +1,7 @@
|
||||
import json
|
||||
import unittest
|
||||
|
||||
from mudfish_decky.client import MudfishClient, MudfishError, SigninError
|
||||
from mudfish_decky.client import AuthError, MudfishClient, MudfishError, SigninError
|
||||
|
||||
BASE = "http://127.0.0.1:8282"
|
||||
SIGNED_OUT = {"status": 200, "isSignedIn": False, "username": "u", "token_v3": "123",
|
||||
@@ -74,6 +74,44 @@ class ClientTest(unittest.TestCase):
|
||||
with self.assertRaises(SigninError):
|
||||
MudfishClient(BASE, t).ensure_signed_in()
|
||||
|
||||
def test_ensure_signed_in_force_skips_shortcut(self):
|
||||
t = FakeTransport({
|
||||
("GET", BASE + "/do/mudrun/status"): [dict(SIGNED_OUT, isSignedIn=True)],
|
||||
("POST", "https://api.mudfish.net/signin_ng_v3"): [SIGNIN_OK],
|
||||
("POST", BASE + "/do/auth"): [{"status": 200, "msg": "OK"}],
|
||||
})
|
||||
MudfishClient(BASE, t).ensure_signed_in(force=True)
|
||||
self.assertEqual(len(t.calls_to("POST", "https://api.mudfish.net/signin_ng_v3")), 1)
|
||||
|
||||
def test_ensure_signed_in_http_401_is_signin_error(self):
|
||||
t = FakeTransport({("GET", BASE + "/do/mudrun/status"): [SIGNED_OUT]})
|
||||
real = t.__call__
|
||||
|
||||
def transport(method, url, body, headers):
|
||||
if "signin_ng_v3" in url:
|
||||
return 401, b"{}"
|
||||
return real(method, url, body, headers)
|
||||
|
||||
with self.assertRaises(SigninError):
|
||||
MudfishClient(BASE, transport).ensure_signed_in()
|
||||
|
||||
def test_ensure_signed_in_without_token_is_signin_error(self):
|
||||
t = FakeTransport({("GET", BASE + "/do/mudrun/status"): [dict(SIGNED_OUT, autologin=True, token_v3="")]})
|
||||
with self.assertRaises(SigninError):
|
||||
MudfishClient(BASE, t).ensure_signed_in()
|
||||
|
||||
def test_graphql_without_user_is_auth_error(self):
|
||||
t = FakeTransport({("POST", "https://api.mudfish.net/graphql"): [{"errors": ["unauthorized"]}]})
|
||||
with self.assertRaises(AuthError):
|
||||
MudfishClient(BASE, t).prog_conf("JWT", "api.mudfish.net")
|
||||
|
||||
def test_prog_http_401_is_auth_error(self):
|
||||
def transport(method, url, body, headers):
|
||||
return 401, b"{}"
|
||||
|
||||
with self.assertRaises(AuthError):
|
||||
MudfishClient(BASE, transport).save_prog_conf("JWT", "api.mudfish.net", {})
|
||||
|
||||
def test_start_accepts_201(self):
|
||||
t = FakeTransport({("POST", BASE + "/do/mudfish/start"): [{"status": 201}]})
|
||||
MudfishClient(BASE, t).start()
|
||||
|
||||
Reference in new issue
Block a user