refactor(handler/tunnel): split 794-line entrypoint.go into 6 files, fix 3 bugs, add 48 tests

Breaking Change: moves Connector, ConnectorPool, parseTunnelID from
tunnel.go into dedicated connector.go and id.go.

Bug Fixes:
- dialer.go: clear stale retry err so SD fallback is not masked (nil err
  from pool.Get() left previous iteration's error live)
- connect.go: check WriteTo errors in both mux and non-mux paths
- eprelay.go: use fresh relay.Response for features sent over mux
  (previously reused same resp struct after resp.WriteTo(muxConn))

Refactor:
- entrypoint.go: split ~790 lines into 6 files (ephttp, eptls, eprelay,
  epwebsocket, eplistener, entrypointsvc), moved to dedicated package
- handler.go: moved initEntrypoints/createEntrypointService to
  entrypointsvc.go
- tunnel.go: moved Connector/ConnectorPool/parseTunnelID to
  connector.go and id.go
- connect.go: moved entrypoint-originated handleConnect to eprelay.go
  (handler-side handleConnect kept in connect.go)

Tests (48 new, 88 total):
- id_test.go: parseTunnelID variants (empty, UUID, private '$', invalid)
- tunnel_test.go: Tunnel lifecycle (AddConnector, GetConnector
  weighted/closed filtering, CloseOnIdle, clean),
  Connector (NewConnector nil-opts, GetConn nil/closed session, Close,
  IsClosed), ConnectorPool (Add/Get/Close, idle cleanup, concurrency)
- dialer_test.go: SD error paths (sd.Get error, empty address)
- handler_test.go: observeStats (nil observer, cancelled context loop),
  initEntrypoints (no entrypoints configured)
- helpers_test.go: testLogger, fakeConn for handler tests
- eprelay_test.go: handleConnect ingress/dial/SD round-trip
This commit is contained in:
ginuerzh
2026-06-01 22:20:43 +08:00
parent 382d47ea12
commit e791ba47e2
18 changed files with 2417 additions and 950 deletions
+97
View File
@@ -0,0 +1,97 @@
package tunnel
import (
"testing"
"github.com/go-gost/relay"
"github.com/google/uuid"
)
func TestParseTunnelID(t *testing.T) {
t.Run("empty string", func(t *testing.T) {
tid := parseTunnelID("")
if !tid.IsZero() {
t.Error("expected zero tunnel ID for empty string")
}
})
t.Run("valid uuid", func(t *testing.T) {
tid := parseTunnelID("6ba7b810-9dad-11d1-80b4-00c04fd430c8")
if tid.IsZero() {
t.Error("expected non-zero tunnel ID")
}
if tid.IsPrivate() {
t.Error("expected non-private tunnel ID")
}
})
t.Run("valid uuid with $ prefix", func(t *testing.T) {
tid := parseTunnelID("$6ba7b810-9dad-11d1-80b4-00c04fd430c8")
if tid.IsZero() {
t.Error("expected non-zero tunnel ID")
}
if !tid.IsPrivate() {
t.Error("expected private tunnel ID")
}
})
t.Run("invalid uuid", func(t *testing.T) {
tid := parseTunnelID("not-a-uuid")
if !tid.IsZero() {
t.Error("expected zero tunnel ID for invalid input")
}
})
t.Run("invalid uuid with $ prefix", func(t *testing.T) {
tid := parseTunnelID("$not-a-uuid")
if !tid.IsZero() {
t.Error("expected zero tunnel ID for invalid input")
}
})
}
func TestParseTunnelID_PrivateMarker(t *testing.T) {
t.Run("dollar not at start", func(t *testing.T) {
tid := parseTunnelID("6ba7b810-9dad-11d1-80b4-00c04fd430c8")
if tid.IsPrivate() {
t.Error("expected non-private when $ is not at start")
}
})
}
func TestParseTunnelID_RoundTrip(t *testing.T) {
original := parseTunnelID("6ba7b810-9dad-11d1-80b4-00c04fd430c8")
t.Logf("original string: %s", original.String())
reparsed := parseTunnelID(original.String())
if !original.Equal(reparsed) {
t.Errorf("round-trip failed: original=%v reparsed=%v", original, reparsed)
}
}
func TestParseTunnelID_PrivateRoundTrip(t *testing.T) {
original := parseTunnelID("$6ba7b810-9dad-11d1-80b4-00c04fd430c8")
if !original.IsPrivate() {
t.Fatal("expected private tunnel ID")
}
t.Logf("original string: %s", original.String())
// The private flag is carried in the struct, not in the string representation.
// Reparsing from String() gives a non-private ID because the $ prefix is not
// part of the relay.TunnelID.String() output.
reparsed := parseTunnelID(original.String())
if !original.Equal(reparsed) {
t.Errorf("round-trip failed: original=%v reparsed=%v", original, reparsed)
}
}
func TestParseTunnelID_RelayCompatibility(t *testing.T) {
id := parseTunnelID("6ba7b810-9dad-11d1-80b4-00c04fd430c8")
u, _ := uuid.Parse("6ba7b810-9dad-11d1-80b4-00c04fd430c8")
var raw [16]byte
copy(raw[:], u[:])
expected := relay.NewTunnelID(raw[:])
if !id.Equal(expected) {
t.Error("parseTunnelID result should match relay.NewTunnelID")
}
}