test(handler/router): add handler_test.go (NewHandler, Init, Handle, Close)
This commit is contained in:
@@ -0,0 +1,262 @@
|
|||||||
|
package router
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/go-gost/core/auth"
|
||||||
|
"github.com/go-gost/core/handler"
|
||||||
|
"github.com/go-gost/relay"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// newInitdHandler — convenience constructor with minimal Init
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func newInitdHandler(t *testing.T, opts ...handler.Option) *routerHandler {
|
||||||
|
t.Helper()
|
||||||
|
h := NewHandler(append([]handler.Option{
|
||||||
|
handler.LoggerOption(&testLogger{}),
|
||||||
|
}, opts...)...).(*routerHandler)
|
||||||
|
if err := h.parseMetadata(testMD(nil)); err != nil {
|
||||||
|
t.Fatalf("parseMetadata: %v", err)
|
||||||
|
}
|
||||||
|
h.id = uuid.New().String()
|
||||||
|
h.log = &testLogger{}
|
||||||
|
h.pool = NewConnectorPool(h.id)
|
||||||
|
_, cancel := context.WithCancel(context.Background())
|
||||||
|
h.cancel = cancel
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// NewHandler tests
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func TestNewHandler_Minimal(t *testing.T) {
|
||||||
|
h := NewHandler()
|
||||||
|
if h == nil {
|
||||||
|
t.Fatal("handler is nil")
|
||||||
|
}
|
||||||
|
rh := h.(*routerHandler)
|
||||||
|
if rh.options.Service != "" {
|
||||||
|
t.Errorf("Service = %q, want empty", rh.options.Service)
|
||||||
|
}
|
||||||
|
if rh.sdCache == nil {
|
||||||
|
t.Error("sdCache is nil")
|
||||||
|
}
|
||||||
|
if rh.routeCache == nil {
|
||||||
|
t.Error("routeCache is nil")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewHandler_WithOptions(t *testing.T) {
|
||||||
|
h := NewHandler(
|
||||||
|
handler.LoggerOption(&testLogger{}),
|
||||||
|
handler.ServiceOption("test-svc"),
|
||||||
|
)
|
||||||
|
rh := h.(*routerHandler)
|
||||||
|
if rh.options.Logger == nil {
|
||||||
|
t.Error("Logger not set")
|
||||||
|
}
|
||||||
|
if rh.options.Service != "test-svc" {
|
||||||
|
t.Errorf("Service = %q, want test-svc", rh.options.Service)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Init tests
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func TestInit_Minimal(t *testing.T) {
|
||||||
|
rh := NewHandler(handler.LoggerOption(&testLogger{})).(*routerHandler)
|
||||||
|
if err := rh.Init(testMD(nil)); err != nil {
|
||||||
|
t.Fatalf("Init: %v", err)
|
||||||
|
}
|
||||||
|
if rh.id == "" {
|
||||||
|
t.Error("id is empty")
|
||||||
|
}
|
||||||
|
if rh.cancel == nil {
|
||||||
|
t.Error("cancel is nil")
|
||||||
|
}
|
||||||
|
if rh.stats != nil {
|
||||||
|
t.Error("stats should be nil (no observer)")
|
||||||
|
}
|
||||||
|
if rh.limiter != nil {
|
||||||
|
t.Error("limiter should be nil (no limiter)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInit_WithObserver(t *testing.T) {
|
||||||
|
obs := newFakeObserver(10)
|
||||||
|
rh := NewHandler(
|
||||||
|
handler.LoggerOption(&testLogger{}),
|
||||||
|
handler.ObserverOption(obs),
|
||||||
|
handler.ServiceOption("test-svc"),
|
||||||
|
).(*routerHandler)
|
||||||
|
|
||||||
|
if err := rh.Init(testMD(nil)); err != nil {
|
||||||
|
t.Fatalf("Init: %v", err)
|
||||||
|
}
|
||||||
|
if rh.stats == nil {
|
||||||
|
t.Error("stats should be non-nil when observer is set")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInit_WithLimiter(t *testing.T) {
|
||||||
|
rh := NewHandler(
|
||||||
|
handler.LoggerOption(&testLogger{}),
|
||||||
|
handler.TrafficLimiterOption(&mockTrafficLimiter{}),
|
||||||
|
).(*routerHandler)
|
||||||
|
|
||||||
|
if err := rh.Init(testMD(nil)); err != nil {
|
||||||
|
t.Fatalf("Init: %v", err)
|
||||||
|
}
|
||||||
|
if rh.limiter == nil {
|
||||||
|
t.Error("limiter should be non-nil when limiter is set")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Handle tests
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func TestHandle_BadVersion(t *testing.T) {
|
||||||
|
h := newInitdHandler(t)
|
||||||
|
|
||||||
|
req := relay.Request{
|
||||||
|
Version: 0x02, // not Version1 — triggers error in ReadFrom
|
||||||
|
Cmd: relay.CmdAssociate,
|
||||||
|
}
|
||||||
|
var buf bytes.Buffer
|
||||||
|
req.WriteTo(&buf)
|
||||||
|
|
||||||
|
conn := &fakeConn{buf: buf.Bytes()}
|
||||||
|
err := h.Handle(context.Background(), conn)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected error for bad version")
|
||||||
|
}
|
||||||
|
|
||||||
|
// The response is not written when ReadFrom fails, so we only verify
|
||||||
|
// that the connection was closed (defer conn.Close in Handle).
|
||||||
|
if !conn.closed {
|
||||||
|
t.Error("connection was not closed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHandle_UnknownCmd(t *testing.T) {
|
||||||
|
h := newInitdHandler(t)
|
||||||
|
|
||||||
|
req := relay.Request{
|
||||||
|
Version: relay.Version1,
|
||||||
|
Cmd: 0xFF,
|
||||||
|
}
|
||||||
|
var buf bytes.Buffer
|
||||||
|
req.WriteTo(&buf)
|
||||||
|
|
||||||
|
conn := &fakeConn{buf: buf.Bytes()}
|
||||||
|
err := h.Handle(context.Background(), conn)
|
||||||
|
if err != ErrUnknownCmd {
|
||||||
|
t.Errorf("err = %v, want ErrUnknownCmd", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp := readRelayResponse(t, conn.writeBuf.Bytes())
|
||||||
|
if resp.Status != relay.StatusBadRequest {
|
||||||
|
t.Errorf("status = %d, want BadRequest", resp.Status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHandle_AuthSuccess(t *testing.T) {
|
||||||
|
h := newInitdHandler(t,
|
||||||
|
handler.AutherOption(&mockAuther{
|
||||||
|
authenticateFn: func(ctx context.Context, user, pass string, opts ...auth.Option) (string, bool) {
|
||||||
|
return "client-1", true
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
rid := relay.NewTunnelID([]byte("0123456789abcdef"))
|
||||||
|
req := relay.Request{
|
||||||
|
Version: relay.Version1,
|
||||||
|
Cmd: relay.CmdAssociate,
|
||||||
|
Features: []relay.Feature{
|
||||||
|
&relay.UserAuthFeature{Username: "user", Password: "pass"},
|
||||||
|
&relay.TunnelFeature{ID: rid},
|
||||||
|
&relay.NetworkFeature{Network: relay.NetworkIP},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
var buf bytes.Buffer
|
||||||
|
req.WriteTo(&buf)
|
||||||
|
|
||||||
|
conn := &fakeConn{buf: buf.Bytes()}
|
||||||
|
err := h.Handle(context.Background(), conn)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Handle: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHandle_AuthFailure(t *testing.T) {
|
||||||
|
h := newInitdHandler(t,
|
||||||
|
handler.AutherOption(&mockAuther{
|
||||||
|
authenticateFn: func(ctx context.Context, user, pass string, opts ...auth.Option) (string, bool) {
|
||||||
|
return "", false
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
rid := relay.NewTunnelID([]byte("0123456789abcdef"))
|
||||||
|
req := relay.Request{
|
||||||
|
Version: relay.Version1,
|
||||||
|
Cmd: relay.CmdAssociate,
|
||||||
|
Features: []relay.Feature{
|
||||||
|
&relay.UserAuthFeature{Username: "user", Password: "wrong"},
|
||||||
|
&relay.TunnelFeature{ID: rid},
|
||||||
|
&relay.NetworkFeature{Network: relay.NetworkIP},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
var buf bytes.Buffer
|
||||||
|
req.WriteTo(&buf)
|
||||||
|
|
||||||
|
conn := &fakeConn{buf: buf.Bytes()}
|
||||||
|
err := h.Handle(context.Background(), conn)
|
||||||
|
if err != ErrUnauthorized {
|
||||||
|
t.Errorf("err = %v, want ErrUnauthorized", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp := readRelayResponse(t, conn.writeBuf.Bytes())
|
||||||
|
if resp.Status != relay.StatusUnauthorized {
|
||||||
|
t.Errorf("status = %d, want Unauthorized", resp.Status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHandle_AssociateValid(t *testing.T) {
|
||||||
|
h := newInitdHandler(t)
|
||||||
|
h.md.ingress = nil
|
||||||
|
|
||||||
|
rid := relay.NewTunnelID([]byte("0123456789abcdef"))
|
||||||
|
reqData := buildRelayAssociateRequest(t, "10.0.0.1:0", rid, "ip")
|
||||||
|
|
||||||
|
conn := &fakeConn{buf: reqData}
|
||||||
|
err := h.Handle(context.Background(), conn)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Handle: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Close tests
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func TestClose(t *testing.T) {
|
||||||
|
h := newInitdHandler(t)
|
||||||
|
h.Close()
|
||||||
|
// should not panic
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClose_Double(t *testing.T) {
|
||||||
|
h := newInitdHandler(t)
|
||||||
|
h.Close()
|
||||||
|
h.Close() // should not panic
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user