139 lines
5.2 KiB
Python
139 lines
5.2 KiB
Python
import json
|
|
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
|
|
|
|
|
|
class ServerConfigTest(unittest.TestCase):
|
|
def setUp(self) -> None:
|
|
self.state = sample_state()
|
|
self.config = build_server_config(self.state)
|
|
|
|
def test_all_protocols_present(self) -> None:
|
|
tags = {inbound["tag"] for inbound in self.config["inbounds"]}
|
|
self.assertEqual(
|
|
tags,
|
|
{"hysteria2-in", "vless-reality-in", "trojan-in", "shadowsocks-in", "tuic-in", "anytls-in"},
|
|
)
|
|
|
|
def test_disabled_protocol_is_skipped(self) -> None:
|
|
self.state.protocols["trojan"].enabled = False
|
|
tags = {inbound["tag"] for inbound in build_server_config(self.state)["inbounds"]}
|
|
self.assertNotIn("trojan-in", tags)
|
|
|
|
def test_hysteria2_tls_and_obfs(self) -> None:
|
|
inbound = next(i for i in self.config["inbounds"] if i["tag"] == "hysteria2-in")
|
|
self.assertEqual(inbound["tls"]["alpn"], ["h3"])
|
|
self.assertEqual(inbound["obfs"]["type"], "salamander")
|
|
self.assertEqual(inbound["users"][0]["password"], "PASSWORD")
|
|
|
|
def test_reality_uses_private_key(self) -> None:
|
|
inbound = next(i for i in self.config["inbounds"] if i["tag"] == "vless-reality-in")
|
|
self.assertEqual(inbound["tls"]["reality"]["private_key"], "PRIVATE")
|
|
self.assertEqual(inbound["tls"]["reality"]["handshake"]["server"], "www.bing.com")
|
|
|
|
def test_shadowsocks_multi_user(self) -> None:
|
|
inbound = next(i for i in self.config["inbounds"] if i["tag"] == "shadowsocks-in")
|
|
self.assertEqual(inbound["password"], SERVER_SS_KEY)
|
|
self.assertEqual(inbound["users"][0]["password"], USER_SS_KEY)
|
|
|
|
def test_shadowsocks_client_sends_identity_and_user_key(self) -> None:
|
|
config = build_client_config(self.state, "shadowsocks", self.state.users[0])
|
|
self.assertEqual(
|
|
config["outbounds"][0]["password"], f"{SERVER_SS_KEY}:{USER_SS_KEY}"
|
|
)
|
|
|
|
def test_config_is_json_serializable(self) -> None:
|
|
json.dumps(self.config)
|
|
|
|
def test_reality_rejects_ip_server_name(self) -> None:
|
|
self.state.protocols["vless-reality"].options["server_name"] = "203.0.113.10"
|
|
with self.assertRaises(SbpError):
|
|
build_server_config(self.state)
|
|
|
|
def test_reality_rejects_odd_length_short_id(self) -> None:
|
|
self.state.protocols["vless-reality"].options["short_id"] = "abc"
|
|
with self.assertRaises(SbpError):
|
|
build_server_config(self.state)
|
|
|
|
def test_reality_accepts_even_short_id(self) -> None:
|
|
self.state.protocols["vless-reality"].options["short_id"] = "ab12"
|
|
build_server_config(self.state)
|
|
|
|
|
|
class ClientConfigTest(unittest.TestCase):
|
|
def setUp(self) -> None:
|
|
self.state = sample_state()
|
|
self.user = self.state.users[0]
|
|
|
|
def test_disabled_protocol_raises_sbp_error(self) -> None:
|
|
self.state.protocols["tuic"].enabled = False
|
|
with self.assertRaises(SbpError):
|
|
build_client_config(self.state, "tuic", self.user)
|
|
with self.assertRaises(SbpError):
|
|
share_uri(self.state, "tuic", self.user)
|
|
|
|
def test_each_client_config_routes_to_outbound(self) -> None:
|
|
for key in self.state.protocols:
|
|
config = build_client_config(self.state, key, self.user)
|
|
self.assertEqual(config["route"]["final"], config["outbounds"][0]["tag"])
|
|
json.dumps(config)
|
|
|
|
def test_self_signed_pins_public_key(self) -> None:
|
|
config = build_client_config(self.state, "trojan", self.user)
|
|
self.assertEqual(
|
|
config["outbounds"][0]["tls"]["certificate_public_key_sha256"], ["SPKI"]
|
|
)
|
|
|
|
def test_reality_does_not_pin(self) -> None:
|
|
config = build_client_config(self.state, "vless-reality", self.user)
|
|
tls = config["outbounds"][0]["tls"]
|
|
self.assertNotIn("certificate_public_key_sha256", tls)
|
|
self.assertEqual(tls["reality"]["public_key"], "PUBLIC")
|
|
|
|
|
|
class ShareUriTest(unittest.TestCase):
|
|
def setUp(self) -> None:
|
|
self.state = sample_state()
|
|
self.user = self.state.users[0]
|
|
|
|
def test_uri_prefixes(self) -> None:
|
|
prefixes = {
|
|
"hysteria2": "hysteria2://",
|
|
"vless-reality": "vless://",
|
|
"trojan": "trojan://",
|
|
"shadowsocks": "ss://",
|
|
"tuic": "tuic://",
|
|
"anytls": "anytls://",
|
|
}
|
|
for key, prefix in prefixes.items():
|
|
self.assertTrue(share_uri(self.state, key, self.user).startswith(prefix), key)
|
|
|
|
def test_hysteria2_pin_and_obfs(self) -> None:
|
|
uri = share_uri(self.state, "hysteria2", self.user)
|
|
self.assertIn("pinSHA256=SHA256", uri)
|
|
self.assertIn("obfs=salamander", uri)
|
|
|
|
def test_vless_reality_parameters(self) -> None:
|
|
uri = share_uri(self.state, "vless-reality", self.user)
|
|
self.assertIn("security=reality", uri)
|
|
self.assertIn("pbk=PUBLIC", uri)
|
|
self.assertIn("flow=xtls-rprx-vision", uri)
|
|
|
|
def test_shadowsocks_uses_plain_userinfo(self) -> None:
|
|
from urllib.parse import unquote
|
|
|
|
uri = share_uri(self.state, "shadowsocks", self.user)
|
|
userinfo = unquote(uri.removeprefix("ss://").split("@", 1)[0])
|
|
self.assertEqual(userinfo, f"2022-blake3-aes-128-gcm:{SERVER_SS_KEY}:{USER_SS_KEY}")
|
|
|
|
def test_all_uris(self) -> None:
|
|
self.assertEqual(len(all_uris(self.state, self.user)), 6)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|