Files
Vole/scripts/tests/test_protocol_integration.py
T
yiguodev 7b8ea15e74 feat: add ShadowTLS v3, UoT v2, TUIC v5, and HTTPUpgrade (#2)
* test: prepare protocol-completion validation

* feat: support ShadowTLS v3 for Shadowsocks 2022

* feat: add UoT v2 to Shadowsocks 2022

* feat: support TUIC v5 TCP and UDP

* feat: add HTTPUpgrade to VMess and Trojan

Reuse the existing raw upgrade stream with typed normal/fast-open options and strict early-data configuration. Reject an expired setup before ready IO can write any prefix. Add isolated official-peer data, UDP, authentication, fingerprint, close and legacy-transport gates.

* test: validate extended proxy chains and lifecycle

* build: freeze release dependencies for protocol completion

* fix: enforce HTTPUpgrade deadline on resumed IO

Recheck the absolute setup deadline before each handshake poll and before accepting success, without retaining a timer on established streams. Cover ready late responses, blocked prefix writes, and post-setup IO in both normal and fast-open modes.
2026-09-29 11:26:43 +08:00

421 lines
16 KiB
Python

from __future__ import annotations
import copy
import io
import json
import tempfile
import unittest
from contextlib import redirect_stdout
from pathlib import Path
from vcore_scripts import cli
class IntegrationAcceptanceTest(unittest.TestCase):
def test_integration_quality_separates_release_source_policy(self):
from vcore_scripts.protocol_integration_local import commands
rows = commands("INTEGRATION-QUALITY")
self.assertNotIn(["vcore-scripts", "check", "tls-dependencies"], rows)
self.assertIn(["vcore-scripts", "check", "c-header"], rows)
self.assertIn(
[
"cargo",
"test",
"--locked",
"--manifest-path",
"crates/vcore-netstack/Cargo.toml",
"--all-targets",
],
rows,
)
self.assertIn(["vcore-scripts", "build", "apple"], rows)
self.assertIn(["vcore-scripts", "build", "android"], rows)
def test_native_source_identity_and_cleanup_are_required(self):
from vcore_scripts.protocol_integration_checks import envelope
identity = dict(
version="official-test",
binary_sha256="a" * 64,
archive_sha256="b" * 64,
source_url="https://github.com/MetaCubeX/mihomo/releases/latest/download/test",
)
source = {
k: "c" * 64
for k in (
"parent_commit",
"source_tree_sha256",
"dirty_patch_sha256",
"lock_sha256",
)
}
digest = "sha256:" + "d" * 64
report = dict(
status="PASS",
cleanup=True,
source_unchanged=True,
source=source,
peers={"M": identity},
isolation=dict(
network_mode="hostOnly",
host_servers=False,
guest_mtu=1500,
image_digest=digest,
peers=[dict(started=True, joined=True)],
),
)
self.assertTrue(envelope(report, {"M": identity}, source, digest))
for mutate in (
lambda d: d.update(cleanup=False),
lambda d: d.update(source_unchanged=False),
lambda d: d["source"].update(source_tree_sha256="e" * 64),
lambda d: d["peers"]["M"].update(binary_sha256="e" * 64),
lambda d: d["isolation"].update(host_servers=True),
lambda d: d["isolation"].update(guest_mtu=1200),
lambda d: d["isolation"]["peers"][0].update(joined=False),
lambda d: d["isolation"].update(peers=[]),
):
broken = copy.deepcopy(report)
mutate(broken)
self.assertFalse(envelope(broken, {"M": identity}, source, digest))
def test_pressure_evidence_is_recomputed_not_inferred(self):
from vcore_scripts.protocol_evidence import RESOURCE_KINDS
from vcore_scripts.protocol_integration_metrics import (
QUEUES,
lifetimes,
rebuild,
soak,
)
from vcore_scripts.protocol_integration_profiles import (
TCP_GROUPS,
UDP_GROUPS,
expected_flows,
)
def snapshot(current):
return dict(
counts=[
dict(kind=k, current=current, peak=1)
for k in sorted(RESOURCE_KINDS)
]
)
def sample(active=True):
return dict(
heap_in_use=2_000_000,
rss_kib=50_000,
fd=100 if active else 6,
resources=snapshot(int(active)),
queues=[
dict(kind=k, capacity=v, peak=int(v > 0)) for k, v in QUEUES.items()
],
)
event = dict(resources=snapshot(0))
stop = dict(
stop_ms=4,
after_stop=sample(False),
quiet_seconds=5.1,
quiet=sample(False),
ports_rebound=True,
)
rows = [
dict(
generation=i,
tcp=20,
udp=20,
per_group_tcp=5,
per_group_udp=5,
flow_profiles=expected_flows(i),
active=sample(),
setup_us=[1000] * 40,
fault="owned-peer-connections-closed",
new_clients=True,
after_clients=sample(),
)
for i in range(100)
]
value = dict(
count=100,
same_running_session=True,
tcp_groups=list(TCP_GROUPS),
udp_groups=list(UDP_GROUPS),
cold_fd=4,
baseline_fd=6,
cycles=rows,
stopped=stop,
)
self.assertTrue(rebuild(value, event))
for mutate in (
lambda d: d["cycles"].pop(),
lambda d: d.update(same_running_session=False),
lambda d: d["cycles"][0].update(tcp=0),
lambda d: d["cycles"][0].update(setup_us=[]),
lambda d: d["cycles"][0]["flow_profiles"]["tcp"].pop(),
lambda d: d["cycles"][1].update(flow_profiles=expected_flows(0)),
lambda d: d["stopped"].update(stop_ms=5001),
lambda d: d["stopped"].update(quiet_seconds=0),
lambda d: d["stopped"]["after_stop"].update(fd=7),
):
broken = copy.deepcopy(value)
mutate(broken)
self.assertFalse(rebuild(broken, event))
self.assertFalse(lifetimes(dict(count=99, cycles=[]), []))
points = [
dict(sample(), seconds=300 + i * 60, waves=i + 1, faults=5 + i)
for i in range(25)
]
value = dict(
requested_seconds=1800,
seconds=1800.2,
tcp_groups=list(TCP_GROUPS),
udp_groups=list(UDP_GROUPS),
tcp=20,
udp=20,
per_group_tcp=5,
per_group_udp=5,
first_profiles=expected_flows(0),
last_profiles=expected_flows(29),
waves=1000,
normal_verified_bytes=1000 * 20 * 2 * 2 * 257,
normal_corruption=0,
normal_misdirection=0,
normal_unexpected_loss=0,
switches=list(range(1, 1800)),
faults=[
dict(
start=60 * i,
end=60 * i + 0.5,
old_tcp_closed=20,
new_tcp=20,
new_udp=20,
replay=False,
)
for i in range(1, 30)
],
samples=points,
cold_fd=4,
baseline_fd=6,
stopped=stop,
active_end=sample(),
first_setup_us=[1000] * 40,
last_setup_us=[1100] * 40,
first_setup_median_us=1000,
last_setup_median_us=1100,
heap=dict(
early_median=2_000_000, late_median=2_000_000, allowed_growth=1048576
),
)
self.assertTrue(soak(value, event, points))
for mutate in (
lambda d: d.update(requested_seconds=65),
lambda d: d.update(seconds=1799),
lambda d: d["faults"].pop(),
lambda d: d.update(switches=[]),
lambda d: d["samples"][0].update(heap_in_use=0),
lambda d: d["samples"][-1]["queues"][0].update(peak=10000),
lambda d: d["heap"].update(late_median=4_000_000),
lambda d: d.update(last_setup_us=[3000] * 40, last_setup_median_us=3000),
lambda d: d.update(normal_unexpected_loss=1),
):
broken = copy.deepcopy(value)
mutate(broken)
self.assertFalse(soak(broken, event, broken["samples"]))
self.assertFalse(soak(value, event, points[:-1]))
def test_integration_consumer_results_keep_the_manifest_scope(self):
from vcore_scripts.protocol_evidence import load_manifest, validate_results
from vcore_scripts.protocol_security_acceptance import result
case = next(
c for c in load_manifest() if c["case_id"] == "INTEGRATION-PAIR-SS-SS"
)
validate_results([case], [result(case, True, True)])
def test_pair_evidence_requires_each_real_path_and_original_assertions(self):
from vcore_scripts.protocol_integration_acceptance import pair_pass
from vcore_scripts.protocol_integration_native import rust_command
identifier = "INTEGRATION-PAIR-SOCKS5-VLESS"
record = dict(
case_id=identifier,
command=rust_command(),
exit_code=0,
command_cleanup=True,
)
paths = [
dict(
outer_ipv6=v6,
nested_select=group,
tcp_target_kinds=3,
tcp_bytes_each_direction=31457280,
udp_target_kinds=3,
udp_packets=1200,
udp_sizes=[1, 64, 512, 1200],
server_first=True,
client_first=True,
ports_rebound=True,
)
for v6 in (False, True)
for group in (False, True)
]
observation = dict(
first="socks5",
last="vless",
paths=paths,
domain_native=[],
budgets=[
dict(
transmit=tx,
receive=rx,
roundtrip_size=min(tx, rx),
packets=100,
oversize_rejected_before_origin=True,
)
for tx, rx in ((128, 128), (512, 256))
],
routed_udp=dict(
upstream_business_udp_disabled=True,
carrier_tcp=True,
carrier_udp=True,
leaf_business_udp_rejected=True,
),
carrier_capability=dict(
upstream_datagrams="rejected",
tcp_allowed=True,
udp_allowed=True,
no_bypass=True,
),
)
events = [
dict(schema_version=1, suite=suite, assertion=assertion, status=status)
for suite, assertion, count in (
("INTEGRATION-PAIR", "ordered_pair", 1),
("INTEGRATION-BASE", "tcp_10mib_both_directions", 12),
("INTEGRATION-PAIR", "directional_budget", 1),
("INTEGRATION-PAIR", "routed_udp_permission", 1),
("INTEGRATION-PAIR", "carrier_capability", 1),
)
for _ in range(count)
for status in ("BEGIN", "PASS")
]
with tempfile.TemporaryDirectory() as temp:
root = Path(temp)
event_path = root / (identifier + "-events.jsonl")
data_path = root / (identifier + "-observations.json")
event_path.write_text("".join(json.dumps(e) + "\n" for e in events))
data_path.write_text(json.dumps(observation))
self.assertTrue(pair_pass(identifier, record, root))
for mutate in (
lambda d: d["paths"].pop(),
lambda d: d["paths"].append(d["paths"][0]),
lambda d: d["paths"][0].update(udp_packets=799),
lambda d: d["paths"][0].update(ports_rebound=False),
lambda d: d["paths"][0].update(server_first=False),
lambda d: d["paths"][0].update(
server_first_limitation="official-ss2022-empty-first-write"
),
lambda d: d.update(last="vmess"),
lambda d: d["budgets"][0].update(packets=99),
lambda d: d["carrier_capability"].update(no_bypass=False),
):
broken = copy.deepcopy(observation)
mutate(broken)
data_path.write_text(json.dumps(broken))
self.assertFalse(pair_pass(identifier, record, root))
data_path.write_text(json.dumps(observation))
event_path.write_text("".join(json.dumps(e) + "\n" for e in events[:-1]))
self.assertFalse(pair_pass(identifier, record, root))
identifier = "INTEGRATION-CHAIN-TUIC-QUIC-SS-V3-AES-256-GCM"
record["case_id"] = identifier
observation.update(
first="tuic",
last="ss",
variant=dict(udp_mode="quic", cipher="2022-blake3-aes-256-gcm"),
)
observation["carrier_capability"]["udp_allowed"] = False
for path in observation["paths"]:
path.update(
server_first=False,
server_first_limitation="official-ss2022-empty-first-write",
)
event_path = root / (identifier + "-events.jsonl")
data_path = root / (identifier + "-observations.json")
event_path.write_text("".join(json.dumps(e) + "\n" for e in events))
data_path.write_text(json.dumps(observation))
self.assertTrue(pair_pass(identifier, record, root))
for mutate in (
lambda d: d.pop("variant"),
lambda d: d["variant"].update(udp_mode="native"),
lambda d: d["variant"].update(cipher="2022-blake3-aes-128-gcm"),
lambda d: d["paths"][0].update(server_first=True),
lambda d: d["paths"][0].pop("server_first_limitation"),
lambda d: d["paths"][0].update(client_first=False),
lambda d: d["paths"][0].update(tcp_bytes_each_direction=1),
):
broken = copy.deepcopy(observation)
mutate(broken)
data_path.write_text(json.dumps(broken))
self.assertFalse(pair_pass(identifier, record, root))
def test_list_contains_all_ordered_pairs_and_remaining_gates(self):
output = io.StringIO()
with redirect_stdout(output):
self.assertEqual(
cli.main(
["check", "protocol-interop", "--suite", "integration", "--list"]
),
0,
)
identifiers = [line.split("\t")[0] for line in output.getvalue().splitlines()]
protocols = {
"SOCKS5",
"ANYTLS",
"SS",
"TROJAN",
"VMESS",
"VLESS",
"HYSTERIA2",
"TUIC",
}
expected = {
f"INTEGRATION-PAIR-{first}-{last}"
for first in protocols
for last in protocols
}
self.assertEqual(len(expected), 64)
self.assertEqual({key for key in identifiers if "-PAIR-" in key}, expected)
self.assertEqual(
{key for key in identifiers if "-CHAIN-" in key},
{
f"INTEGRATION-CHAIN-TUIC-{mode}-SS-V3-{cipher}"
for mode in ("NATIVE", "QUIC")
for cipher in ("AES-128-GCM", "AES-256-GCM", "CHACHA20-POLY1305")
},
)
self.assertEqual(len(identifiers), len(set(identifiers)))
self.assertTrue(
{
"INTEGRATION-ENTRYPOINTS",
"INTEGRATION-GRAPH",
"INTEGRATION-DNS-MEASURE",
"INTEGRATION-SS-ALGORITHMS",
"INTEGRATION-SS-EIH",
"INTEGRATION-LIFECYCLE",
"INTEGRATION-REBUILD",
"INTEGRATION-FAILURES",
"INTEGRATION-SOAK",
"INTEGRATION-HY2-HOP",
"INTEGRATION-DEBUG",
"INTEGRATION-RELEASE",
"INTEGRATION-QUALITY",
"INTEGRATION-FEATURES",
"INTEGRATION-SCRIPTS",
"INTEGRATION-SHARED",
"INTEGRATION-COUPLED",
}
<= set(identifiers)
)