新增 Clash/mihomo 节点输出: sbp user show --clash
This commit is contained in:
1 parent
79fe594385
commit
f9874454f0
16 files changed
+318
-14
No files matched your search
@@ -0,0 +1,97 @@
|
||||
import unittest
|
||||
|
||||
from sbp.clash import cert_fingerprint, proxies_yaml
|
||||
from sbp.errors import SbpError
|
||||
from sbp.share import clash_proxies, clash_yaml
|
||||
from tests.test_state import CERT_SHA256, SERVER_SS_KEY, USER_SS_KEY, sample_state
|
||||
|
||||
|
||||
class FingerprintTest(unittest.TestCase):
|
||||
def test_colon_uppercase(self) -> None:
|
||||
self.assertEqual(cert_fingerprint("ab" * 32), ":".join(["AB"] * 32))
|
||||
|
||||
def test_rejects_short_value(self) -> None:
|
||||
with self.assertRaises(ValueError):
|
||||
cert_fingerprint("abc")
|
||||
|
||||
|
||||
class YamlTest(unittest.TestCase):
|
||||
def test_nested_mapping_and_list(self) -> None:
|
||||
text = proxies_yaml([{
|
||||
"name": "a",
|
||||
"port": 1,
|
||||
"udp": True,
|
||||
"alpn": ["h3"],
|
||||
"reality-opts": {"public-key": "P", "short-id": "s"},
|
||||
}])
|
||||
self.assertEqual(
|
||||
text,
|
||||
"proxies:\n"
|
||||
" - name: a\n"
|
||||
" port: 1\n"
|
||||
" udp: true\n"
|
||||
" alpn:\n"
|
||||
" - h3\n"
|
||||
" reality-opts:\n"
|
||||
" public-key: P\n"
|
||||
" short-id: s\n",
|
||||
)
|
||||
|
||||
def test_quotes_values_that_need_it(self) -> None:
|
||||
text = proxies_yaml([{"name": "a", "password": "abc:def", "server": "2602::1"}])
|
||||
self.assertIn('password: "abc:def"', text)
|
||||
self.assertIn('server: "2602::1"', text)
|
||||
|
||||
def test_quotes_reserved_words(self) -> None:
|
||||
self.assertIn('name: "no"', proxies_yaml([{"name": "no"}]))
|
||||
|
||||
|
||||
class ProxyTest(unittest.TestCase):
|
||||
def setUp(self) -> None:
|
||||
self.state = sample_state()
|
||||
self.user = self.state.users[0]
|
||||
self.proxies = {proxy["type"]: proxy for proxy in clash_proxies(self.state, self.user)}
|
||||
|
||||
def test_all_protocols_present(self) -> None:
|
||||
self.assertEqual(
|
||||
set(self.proxies), {"hysteria2", "vless", "trojan", "ss", "tuic", "anytls"}
|
||||
)
|
||||
|
||||
def test_hysteria2_pins_certificate(self) -> None:
|
||||
proxy = self.proxies["hysteria2"]
|
||||
self.assertEqual(proxy["fingerprint"], cert_fingerprint(CERT_SHA256))
|
||||
self.assertEqual(proxy["obfs"], "salamander")
|
||||
self.assertNotIn("skip-cert-verify", proxy)
|
||||
|
||||
def test_vless_reality_options(self) -> None:
|
||||
proxy = self.proxies["vless"]
|
||||
self.assertEqual(proxy["reality-opts"]["public-key"], "PUBLIC")
|
||||
self.assertEqual(proxy["servername"], "www.bing.com")
|
||||
self.assertTrue(proxy["tls"])
|
||||
|
||||
def test_shadowsocks_sends_identity_and_user_key(self) -> None:
|
||||
self.assertEqual(
|
||||
self.proxies["ss"]["password"], f"{SERVER_SS_KEY}:{USER_SS_KEY}"
|
||||
)
|
||||
|
||||
def test_self_signed_protocols_skip_verification(self) -> None:
|
||||
for key in ("trojan", "tuic", "anytls"):
|
||||
self.assertTrue(self.proxies[key]["skip-cert-verify"], key)
|
||||
|
||||
def test_disabled_protocol_is_rejected(self) -> None:
|
||||
self.state.protocols["tuic"].enabled = False
|
||||
with self.assertRaises(SbpError):
|
||||
clash_proxies(self.state, self.user, "tuic")
|
||||
|
||||
def test_single_protocol(self) -> None:
|
||||
proxies = clash_proxies(self.state, self.user, "trojan")
|
||||
self.assertEqual([proxy["type"] for proxy in proxies], ["trojan"])
|
||||
|
||||
def test_yaml_covers_every_enabled_protocol(self) -> None:
|
||||
text = clash_yaml(self.state, self.user)
|
||||
self.assertTrue(text.startswith("proxies:\n"))
|
||||
self.assertEqual(text.count("\n - name: "), 6)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -4,7 +4,7 @@ import unittest
|
||||
from sbp.config import build_client_config, build_server_config
|
||||
from sbp.errors import SbpError
|
||||
from sbp.share import all_uris, share_uri
|
||||
from tests.test_state import SERVER_SS_KEY, USER_SS_KEY, sample_state
|
||||
from tests.test_state import CERT_SHA256, SERVER_SS_KEY, USER_SS_KEY, sample_state
|
||||
|
||||
|
||||
class ServerConfigTest(unittest.TestCase):
|
||||
@@ -114,7 +114,7 @@ class ShareUriTest(unittest.TestCase):
|
||||
|
||||
def test_hysteria2_pin_and_obfs(self) -> None:
|
||||
uri = share_uri(self.state, "hysteria2", self.user)
|
||||
self.assertIn("pinSHA256=SHA256", uri)
|
||||
self.assertIn(f"pinSHA256={CERT_SHA256}", uri)
|
||||
self.assertIn("obfs=salamander", uri)
|
||||
|
||||
def test_vless_reality_parameters(self) -> None:
|
||||
|
||||
+2
-1
@@ -6,6 +6,7 @@ from sbp.state import ProtocolState, RealityState, State, TlsState, User, migrat
|
||||
USER_UUID = "11111111-2222-3333-4444-555555555555"
|
||||
USER_SS_KEY = base64.b64encode(b"0123456789abcdef").decode()
|
||||
SERVER_SS_KEY = base64.b64encode(b"abcdef0123456789").decode()
|
||||
CERT_SHA256 = "ab" * 32
|
||||
|
||||
|
||||
def sample_state() -> State:
|
||||
@@ -21,7 +22,7 @@ def sample_state() -> State:
|
||||
sni="www.bing.com",
|
||||
self_signed=True,
|
||||
spki_pin="SPKI",
|
||||
cert_sha256="SHA256",
|
||||
cert_sha256=CERT_SHA256,
|
||||
),
|
||||
reality=RealityState(private_key="PRIVATE", public_key="PUBLIC"),
|
||||
users=[User("u1", "PASSWORD", USER_UUID, USER_SS_KEY, "2026-01-01T00:00:00Z")],
|
||||
|
||||
Reference in new issue
Block a user