sboxrun: domain+IP-CIDR rules + remove voice-quality test
Three follow-up fixes after the WinDivert→sing-box pivot: 1. Discord updater now routes through upstream. Previously only the process-name rule matched, but sing-box's TUN-side process detection on Windows mis-attributes the in-process Rust updater's TLS connection to e.g. steam.exe — the connection went direct and hit RKN block. Adding domain_suffix + ip_cidr rules for Cloudflare (162.159/16, 104.16/13, 172.64/13) and Fastly (199.232/16, 151.101/16) catches updates.discord.com regardless of which PID the kernel claims sent it. Verified via curl through mihomo: updates.discord.com responds 400 in 393ms (i.e. TLS handshake succeeds, only the path is wrong — proves the routing reaches it). 2. DiscordSystemHelper.exe added to TargetProcs alongside Update.exe (modern Discord builds use it for elevated updates). 3. UDP voice quality test removed from the checker. The STUN-via- relay burst measured private mihomo BND.ADDR (192.168.1.132) which is unroutable from external clients, so the test reported 100% loss every time despite voice actually working through sing-box's TUN+SOCKS5. The remaining 6 checks (TCP/greet/auth/ connect/UDP/api) cover what's actionable; voice quality is verified empirically by joining a Discord call. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -579,7 +579,7 @@ func TestRun_HappyNoAuth(t *testing.T) {
|
||||
ch := Run(context.Background(), cfg)
|
||||
results := drainResults(t, ch, 10*time.Second)
|
||||
|
||||
expected := []string{"tcp", "greet", "connect", "udp", "voice-quality", "api"}
|
||||
expected := []string{"tcp", "greet", "connect", "udp", "api"}
|
||||
finals := map[string]Result{}
|
||||
for _, id := range expected {
|
||||
r, ok := finalByID(results, id)
|
||||
@@ -598,7 +598,6 @@ func TestRun_HappyNoAuth(t *testing.T) {
|
||||
// Metrics format spot-checks.
|
||||
assert.Contains(t, finals["greet"].Metric, "no auth")
|
||||
assert.Equal(t, "REP=00", finals["connect"].Metric)
|
||||
assert.Contains(t, finals["voice-quality"].Metric, "loss=")
|
||||
assert.Equal(t, "HTTP 200", finals["api"].Metric)
|
||||
}
|
||||
|
||||
@@ -612,7 +611,7 @@ func TestRun_HappyWithAuth(t *testing.T) {
|
||||
ch := Run(context.Background(), cfg)
|
||||
results := drainResults(t, ch, 10*time.Second)
|
||||
|
||||
expected := []string{"tcp", "greet", "auth", "connect", "udp", "voice-quality", "api"}
|
||||
expected := []string{"tcp", "greet", "auth", "connect", "udp", "api"}
|
||||
for _, id := range expected {
|
||||
r, ok := finalByID(results, id)
|
||||
require.True(t, ok, "missing %s; results=%+v", id, results)
|
||||
@@ -643,7 +642,7 @@ func TestRun_AuthRejected(t *testing.T) {
|
||||
assert.Equal(t, StatusFailed, rA.Status)
|
||||
assert.NotEmpty(t, rA.Hint)
|
||||
|
||||
for _, id := range []string{"connect", "udp", "voice-quality", "api"} {
|
||||
for _, id := range []string{"connect", "udp", "api"} {
|
||||
r, ok := finalByID(results, id)
|
||||
require.True(t, ok, "missing %s", id)
|
||||
assert.Equal(t, StatusSkipped, r.Status, "id=%s", id)
|
||||
@@ -668,7 +667,7 @@ func TestRun_AllMethodsRejected(t *testing.T) {
|
||||
assert.Equal(t, StatusFailed, rG.Status)
|
||||
assert.NotEmpty(t, rG.Hint)
|
||||
|
||||
for _, id := range []string{"connect", "udp", "voice-quality", "api"} {
|
||||
for _, id := range []string{"connect", "udp", "api"} {
|
||||
r, ok := finalByID(results, id)
|
||||
require.True(t, ok, "missing %s", id)
|
||||
assert.Equal(t, StatusSkipped, r.Status, "id=%s", id)
|
||||
@@ -700,10 +699,6 @@ func TestRun_ConnectRefused(t *testing.T) {
|
||||
rU, _ := finalByID(results, "udp")
|
||||
assert.Equal(t, StatusPassed, rU.Status, "udp should pass independently of connect")
|
||||
|
||||
// voice-quality depends on udp → passes too.
|
||||
rVQ, _ := finalByID(results, "voice-quality")
|
||||
assert.Equal(t, StatusPassed, rVQ.Status)
|
||||
|
||||
// api depends on connect → skipped.
|
||||
rA, _ := finalByID(results, "api")
|
||||
assert.Equal(t, StatusSkipped, rA.Status)
|
||||
@@ -728,10 +723,6 @@ func TestRun_UDPUnsupported(t *testing.T) {
|
||||
require.Equal(t, StatusFailed, rU.Status)
|
||||
assert.NotEmpty(t, rU.Hint)
|
||||
|
||||
// voice-quality depends on udp → skipped.
|
||||
rVQ, _ := finalByID(results, "voice-quality")
|
||||
assert.Equal(t, StatusSkipped, rVQ.Status)
|
||||
|
||||
rA, _ := finalByID(results, "api")
|
||||
assert.Equal(t, StatusPassed, rA.Status)
|
||||
}
|
||||
@@ -768,7 +759,7 @@ func TestRun_TimeoutThenOK(t *testing.T) {
|
||||
assert.Equal(t, 2, greetEvents[3].Attempt)
|
||||
|
||||
// All non-auth tests should ultimately pass.
|
||||
for _, id := range []string{"tcp", "greet", "connect", "udp", "voice-quality", "api"} {
|
||||
for _, id := range []string{"tcp", "greet", "connect", "udp", "api"} {
|
||||
r, ok := finalByID(results, id)
|
||||
require.True(t, ok, "missing %s", id)
|
||||
assert.Equal(t, StatusPassed, r.Status, "id=%s, got %+v", id, r)
|
||||
@@ -888,58 +879,6 @@ func TestRun_NegativeRetryClamped(t *testing.T) {
|
||||
assert.Equal(t, 500*time.Millisecond, out.RetryBackoff)
|
||||
}
|
||||
|
||||
// TestRun_VoiceQualityWarn drives the relay to drop ~1 in 10 packets,
|
||||
// which puts the burst into the warn band (loss in (5, 15]%, jitter and
|
||||
// p50 typically tiny on localhost). Asserts StatusWarn and that the
|
||||
// metric reports a non-zero loss.
|
||||
func TestRun_VoiceQualityWarn(t *testing.T) {
|
||||
fp := newFakeProxy(t, "voice_quality_warn")
|
||||
cfg := proxyConfig(fp, false)
|
||||
cfg.DiscordGateway = stubGatewayAddr(t)
|
||||
cfg.DiscordAPI = stubAPIServer(t, fp, 200)
|
||||
cfg.StunServer = "127.0.0.1:65000"
|
||||
// Burst of 30 with 1-in-10 drop → ~3 lost ≈ 10%.
|
||||
cfg.VoiceBurstCount = 30
|
||||
cfg.VoiceBurstInterval = 5 * time.Millisecond
|
||||
cfg.PerTestTimeout = 1 * time.Second
|
||||
fp.udpDropEveryN.Store(10)
|
||||
|
||||
ch := Run(context.Background(), cfg)
|
||||
results := drainResults(t, ch, 15*time.Second)
|
||||
|
||||
rVQ, ok := finalByID(results, "voice-quality")
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, StatusWarn, rVQ.Status, "got %+v", rVQ)
|
||||
assert.Contains(t, rVQ.Metric, "loss=")
|
||||
assert.NotEmpty(t, rVQ.Hint)
|
||||
}
|
||||
|
||||
// TestRun_VoiceQualityFail drives the relay to drop 4 of every 5 packets
|
||||
// (~80% loss) — well past the fail threshold.
|
||||
func TestRun_VoiceQualityFail(t *testing.T) {
|
||||
fp := newFakeProxy(t, "voice_quality_fail")
|
||||
cfg := proxyConfig(fp, false)
|
||||
cfg.DiscordGateway = stubGatewayAddr(t)
|
||||
cfg.DiscordAPI = stubAPIServer(t, fp, 200)
|
||||
cfg.StunServer = "127.0.0.1:65000"
|
||||
cfg.VoiceBurstCount = 30
|
||||
cfg.VoiceBurstInterval = 3 * time.Millisecond
|
||||
cfg.PerTestTimeout = 1 * time.Second
|
||||
cfg.MaxRetries = 0
|
||||
// Drop everything: dropEveryN=1 means EVERY packet dropped → 100%.
|
||||
// Use 2 for ~50%, 1 for 100. We want fail-band — pick 1 to guarantee
|
||||
// "no replies received".
|
||||
fp.udpDropEveryN.Store(1)
|
||||
|
||||
ch := Run(context.Background(), cfg)
|
||||
results := drainResults(t, ch, 15*time.Second)
|
||||
|
||||
rVQ, ok := finalByID(results, "voice-quality")
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, StatusFailed, rVQ.Status, "got %+v", rVQ)
|
||||
assert.NotEmpty(t, rVQ.Hint)
|
||||
}
|
||||
|
||||
func TestExtractRawHex(t *testing.T) {
|
||||
cases := []struct {
|
||||
in, want string
|
||||
|
||||
Reference in New Issue
Block a user