115 lines
4.4 KiB
Python
115 lines
4.4 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""拆分后 rrpp_core 的独立回归测试(不依赖 Qt 或真实代理连接)。"""
|
|
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
|
|
LITE_DIR = Path(__file__).resolve().parent.parent
|
|
sys.path.insert(0, str(LITE_DIR))
|
|
|
|
from rrpp_core.forwarder_primitives import (
|
|
force_http_close,
|
|
inject_proxy_auth,
|
|
parse_connect_authority,
|
|
parse_http_absolute,
|
|
)
|
|
from rrpp_core.node_api import NodeAuthError, b64url
|
|
from rrpp_core.node_secrets import NodeSecretsStore
|
|
from rrpp_core.network_primitives import is_private_host, is_private_ipv4_int, ttl_cache
|
|
from rrpp_core.pac_data import atomic_write, read_version, validate_document
|
|
from rrpp_core.routing_primitives import (
|
|
build_suffix_trie,
|
|
merge_ip_ranges,
|
|
parse_domain_rules,
|
|
suffix_trie_matches,
|
|
)
|
|
from rrpp_core.settings_store import JsonSettingsStore
|
|
from rrpp_core.traffic import TrafficStats, format_bytes
|
|
|
|
|
|
class CoreModuleTests(unittest.TestCase):
|
|
def test_domain_rules_and_trie(self):
|
|
suffixes, exact, regexes, invalid = parse_domain_rules(
|
|
["example.com", "full:only.example", "regexp:^edge-[0-9]+\\.example$", "regexp:["]
|
|
)
|
|
self.assertEqual(invalid, 1)
|
|
self.assertIn("example.com", suffixes)
|
|
self.assertIn("only.example", exact)
|
|
self.assertTrue(regexes[0].search("edge-42.example"))
|
|
trie = build_suffix_trie(suffixes)
|
|
self.assertTrue(suffix_trie_matches("cdn.example.com", trie))
|
|
self.assertFalse(suffix_trie_matches("example.net", trie))
|
|
self.assertEqual(
|
|
merge_ip_ranges(["10.0.0.0/25", "10.0.0.128/25"], 4),
|
|
[(167772160, 167772415)],
|
|
)
|
|
|
|
def test_forwarder_primitives(self):
|
|
self.assertEqual(
|
|
parse_connect_authority("CONNECT [::1]:443 HTTP/1.1"), ("::1", 443)
|
|
)
|
|
self.assertEqual(
|
|
parse_http_absolute("GET https://example.com/path HTTP/1.1"),
|
|
("example.com", 443),
|
|
)
|
|
request = b"GET / HTTP/1.1\r\nHost: example.com\r\n\r\n"
|
|
injected = inject_proxy_auth(request, "token")
|
|
self.assertIn(b"Authorization: Basic token\r\n", injected)
|
|
self.assertEqual(inject_proxy_auth(injected, "different"), injected)
|
|
self.assertIn(b"Connection: close\r\n", force_http_close(request))
|
|
|
|
def test_settings_pac_and_node_secrets_storage(self):
|
|
with tempfile.TemporaryDirectory() as temporary:
|
|
directory = Path(temporary)
|
|
settings = JsonSettingsStore(lambda: directory / "settings.json")
|
|
self.assertEqual(settings.load(), {})
|
|
settings.save({"local_port": 1080})
|
|
self.assertEqual(settings.load(), {"local_port": 1080})
|
|
|
|
payload = {"version": "1", "domains": ["example.com"]}
|
|
atomic_write(directory, "pac_domains", payload)
|
|
self.assertEqual(read_version(directory / "pac_domains.json"), ("1", ""))
|
|
self.assertTrue(validate_document("pac_domains", payload))
|
|
|
|
secrets = NodeSecretsStore(lambda: directory)
|
|
self.assertEqual(secrets.decrypt_bytes(secrets.encrypt_text("round-trip")), "round-trip")
|
|
secrets.save_auth("test-user", "test-password")
|
|
self.assertEqual(
|
|
secrets.load_auth(), {"username": "test-user", "password": "test-password"}
|
|
)
|
|
|
|
def test_traffic_and_node_protocol_helpers(self):
|
|
times = iter((10.0, 12.0))
|
|
stats = TrafficStats(clock=lambda: next(times))
|
|
stats.reset()
|
|
stats.add(up=20, down=10)
|
|
self.assertEqual(stats.rate(), (10, 5, 20, 10))
|
|
self.assertEqual(format_bytes(1536), "1.5 KB")
|
|
self.assertEqual(b64url("a/b="), "YS9iPQ")
|
|
error = NodeAuthError("denied", 403)
|
|
self.assertEqual((str(error), error.code), ("denied", 403))
|
|
|
|
def test_network_primitives_and_ttl_cache(self):
|
|
self.assertTrue(is_private_ipv4_int(0x0A010203))
|
|
self.assertTrue(is_private_host("::1"))
|
|
self.assertFalse(is_private_host("8.8.8.8"))
|
|
calls = []
|
|
|
|
@ttl_cache(300)
|
|
def probe(value):
|
|
calls.append(value)
|
|
return value
|
|
|
|
self.assertEqual((probe("x"), probe("x")), ("x", "x"))
|
|
self.assertEqual(calls, ["x"])
|
|
probe.cache_clear()
|
|
probe("x")
|
|
self.assertEqual(calls, ["x", "x"])
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main(verbosity=2)
|