491 lines
14 KiB
Go
491 lines
14 KiB
Go
package router
|
|
|
|
import (
|
|
"bytes"
|
|
"io"
|
|
"testing"
|
|
|
|
"github.com/go-gost/core/logger"
|
|
"github.com/go-gost/relay"
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
// init sets up the default logger to prevent nil panics in NewConnector.
|
|
func init() {
|
|
logger.SetDefault(&testLogger{})
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// CloseWriter — helper that tracks close calls
|
|
// ---------------------------------------------------------------------------
|
|
|
|
type closeWriter struct {
|
|
closeFn func() error
|
|
}
|
|
|
|
func (w *closeWriter) Write(p []byte) (int, error) { return len(p), nil }
|
|
func (w *closeWriter) Close() error {
|
|
if w.closeFn != nil {
|
|
return w.closeFn()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Connector tests
|
|
// ---------------------------------------------------------------------------
|
|
|
|
func TestNewConnector_Minimal(t *testing.T) {
|
|
rid := relay.NewTunnelID([]byte("0123456789abcdef"))
|
|
cid := relay.NewConnectorID([]byte("abcdef0123456789"))
|
|
c := NewConnector(rid, cid, "example.com", nil, nil)
|
|
if c == nil {
|
|
t.Fatal("connector is nil")
|
|
}
|
|
if !c.ID().Equal(cid) {
|
|
t.Errorf("ID = %v, want %v", c.ID(), cid)
|
|
}
|
|
if w := c.Writer(); w != nil {
|
|
t.Errorf("Writer = %v, want nil", w)
|
|
}
|
|
}
|
|
|
|
func TestNewConnector_WithOpts(t *testing.T) {
|
|
rid := relay.NewTunnelID([]byte("0123456789abcdef"))
|
|
cid := relay.NewConnectorID([]byte("abcdef0123456789"))
|
|
var buf bytes.Buffer
|
|
c := NewConnector(rid, cid, "example.com", &buf, &ConnectorOptions{})
|
|
if c == nil {
|
|
t.Fatal("connector is nil")
|
|
}
|
|
n, err := c.Writer().Write([]byte("hello"))
|
|
if err != nil {
|
|
t.Fatalf("write: %v", err)
|
|
}
|
|
if n != 5 {
|
|
t.Errorf("n = %d, want 5", n)
|
|
}
|
|
if buf.String() != "hello" {
|
|
t.Errorf("buf = %q, want hello", buf.String())
|
|
}
|
|
}
|
|
|
|
func TestConnector_Close_NilWriter(t *testing.T) {
|
|
rid := relay.NewTunnelID([]byte("0123456789abcdef"))
|
|
cid := relay.NewConnectorID([]byte("abcdef0123456789"))
|
|
c := NewConnector(rid, cid, "example.com", nil, nil)
|
|
if err := c.Close(); err != nil {
|
|
t.Fatalf("Close: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestConnector_Close_Writer(t *testing.T) {
|
|
rid := relay.NewTunnelID([]byte("0123456789abcdef"))
|
|
cid := relay.NewConnectorID([]byte("abcdef0123456789"))
|
|
closed := false
|
|
c := NewConnector(rid, cid, "example.com", &closeWriter{
|
|
closeFn: func() error { closed = true; return nil },
|
|
}, nil)
|
|
if err := c.Close(); err != nil {
|
|
t.Fatalf("Close: %v", err)
|
|
}
|
|
if !closed {
|
|
t.Error("writer was not closed")
|
|
}
|
|
}
|
|
|
|
func TestConnector_Close_Double(t *testing.T) {
|
|
rid := relay.NewTunnelID([]byte("0123456789abcdef"))
|
|
cid := relay.NewConnectorID([]byte("abcdef0123456789"))
|
|
c := NewConnector(rid, cid, "example.com", &closeWriter{}, nil)
|
|
c.Close()
|
|
c.Close() // second close should be safe
|
|
}
|
|
|
|
func TestConnector_Writer_Nil(t *testing.T) {
|
|
var c *Connector
|
|
if w := c.Writer(); w != nil {
|
|
t.Errorf("nil connector Writer = %v, want nil", w)
|
|
}
|
|
}
|
|
|
|
func TestConnector_Close_Nil(t *testing.T) {
|
|
var c *Connector
|
|
if err := c.Close(); err != nil {
|
|
t.Fatalf("nil connector Close: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestConnector_Close_NonCloser(t *testing.T) {
|
|
rid := relay.NewTunnelID([]byte("0123456789abcdef"))
|
|
cid := relay.NewConnectorID([]byte("abcdef0123456789"))
|
|
// bytes.Buffer does not implement io.Closer
|
|
var buf bytes.Buffer
|
|
c := NewConnector(rid, cid, "example.com", &buf, nil)
|
|
if err := c.Close(); err != nil {
|
|
t.Fatalf("Close: %v", err)
|
|
}
|
|
}
|
|
|
|
// xcloseWriter implements io.Closer but times out
|
|
type xcloseWriter struct {
|
|
data []byte
|
|
}
|
|
|
|
func (w *xcloseWriter) Write(p []byte) (int, error) {
|
|
w.data = append(w.data, p...)
|
|
return len(p), nil
|
|
}
|
|
func (w *xcloseWriter) Close() error { return io.ErrClosedPipe }
|
|
|
|
func TestConnector_Close_CloserError(t *testing.T) {
|
|
rid := relay.NewTunnelID([]byte("0123456789abcdef"))
|
|
cid := relay.NewConnectorID([]byte("abcdef0123456789"))
|
|
c := NewConnector(rid, cid, "example.com", &xcloseWriter{}, nil)
|
|
if err := c.Close(); err != io.ErrClosedPipe {
|
|
t.Errorf("Close = %v, want ErrClosedPipe", err)
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Router tests
|
|
// ---------------------------------------------------------------------------
|
|
|
|
func TestRouter_AddConnector_Nil(t *testing.T) {
|
|
rid := relay.NewTunnelID([]byte("0123456789abcdef"))
|
|
r := NewRouter("node1", rid)
|
|
r.AddConnector(nil) // should not panic
|
|
}
|
|
|
|
func TestRouter_AddGetConnector_Single(t *testing.T) {
|
|
rid := relay.NewTunnelID([]byte("0123456789abcdef"))
|
|
r := NewRouter("node1", rid)
|
|
if !r.ID().Equal(rid) {
|
|
t.Errorf("ID = %v, want %v", r.ID(), rid)
|
|
}
|
|
|
|
cid := relay.NewConnectorID([]byte("abcdef0123456789"))
|
|
c := NewConnector(rid, cid, "host1", nil, nil)
|
|
r.AddConnector(c)
|
|
|
|
got := r.GetConnector("host1")
|
|
if got == nil {
|
|
t.Fatal("GetConnector returned nil")
|
|
}
|
|
if !got.ID().Equal(cid) {
|
|
t.Errorf("ID = %v, want %v", got.ID(), cid)
|
|
}
|
|
}
|
|
|
|
func TestRouter_AddGetConnector_MultipleHosts(t *testing.T) {
|
|
rid := relay.NewTunnelID([]byte("0123456789abcdef"))
|
|
r := NewRouter("node1", rid)
|
|
|
|
cid1 := relay.NewConnectorID([]byte("aaaaaaaaaaaaaaaa"))
|
|
cid2 := relay.NewConnectorID([]byte("bbbbbbbbbbbbbbbb"))
|
|
r.AddConnector(NewConnector(rid, cid1, "host1", nil, nil))
|
|
r.AddConnector(NewConnector(rid, cid2, "host2", nil, nil))
|
|
|
|
got1 := r.GetConnector("host1")
|
|
if got1 == nil || !got1.ID().Equal(cid1) {
|
|
t.Errorf("host1 got ID = %v, want %v", got1, cid1)
|
|
}
|
|
got2 := r.GetConnector("host2")
|
|
if got2 == nil || !got2.ID().Equal(cid2) {
|
|
t.Errorf("host2 got ID = %v, want %v", got2, cid2)
|
|
}
|
|
}
|
|
|
|
func TestRouter_GetConnector_EmptyHost(t *testing.T) {
|
|
rid := relay.NewTunnelID([]byte("0123456789abcdef"))
|
|
r := NewRouter("node1", rid)
|
|
if got := r.GetConnector(""); got != nil {
|
|
t.Errorf("GetConnector('') = %v, want nil", got)
|
|
}
|
|
}
|
|
|
|
func TestRouter_GetConnector_MissingHost(t *testing.T) {
|
|
rid := relay.NewTunnelID([]byte("0123456789abcdef"))
|
|
r := NewRouter("node1", rid)
|
|
if got := r.GetConnector("nonexistent"); got != nil {
|
|
t.Errorf("GetConnector('nonexistent') = %v, want nil", got)
|
|
}
|
|
}
|
|
|
|
func TestRouter_GetConnector_Weighted(t *testing.T) {
|
|
rid := relay.NewTunnelID([]byte("0123456789abcdef"))
|
|
r := NewRouter("node1", rid)
|
|
|
|
cid1 := relay.NewConnectorID([]byte("aaaaaaaaaaaaaaaa"))
|
|
cid2 := relay.NewConnectorID([]byte("bbbbbbbbbbbbbbbb"))
|
|
// Weight 0 should be treated as 1
|
|
r.AddConnector(NewConnector(rid, cid1, "host1", nil, nil))
|
|
r.AddConnector(NewConnector(rid, cid2, "host1", nil, nil))
|
|
|
|
// With multiple connectors weighted selection should return non-nil
|
|
got := r.GetConnector("host1")
|
|
if got == nil {
|
|
t.Fatal("GetConnector returned nil for weighted selection")
|
|
}
|
|
}
|
|
|
|
func TestRouter_DelConnector(t *testing.T) {
|
|
rid := relay.NewTunnelID([]byte("0123456789abcdef"))
|
|
r := NewRouter("node1", rid)
|
|
|
|
cid1 := relay.NewConnectorID([]byte("aaaaaaaaaaaaaaaa"))
|
|
cid2 := relay.NewConnectorID([]byte("bbbbbbbbbbbbbbbb"))
|
|
r.AddConnector(NewConnector(rid, cid1, "host1", nil, nil))
|
|
r.AddConnector(NewConnector(rid, cid2, "host1", nil, nil))
|
|
|
|
r.DelConnector("host1", cid1)
|
|
got := r.GetConnector("host1")
|
|
if got == nil {
|
|
t.Fatal("GetConnector returned nil after delete")
|
|
}
|
|
if !got.ID().Equal(cid2) {
|
|
t.Errorf("remaining connector = %v, want %v", got.ID(), cid2)
|
|
}
|
|
}
|
|
|
|
func TestRouter_DelConnector_MultipleRemaining(t *testing.T) {
|
|
// Regression test: deleting one of three should leave two intact
|
|
rid := relay.NewTunnelID([]byte("0123456789abcdef"))
|
|
r := NewRouter("node1", rid)
|
|
|
|
ids := make([]relay.ConnectorID, 3)
|
|
for i := range ids {
|
|
uid := uuid.New()
|
|
ids[i] = relay.NewConnectorID(uid[:])
|
|
r.AddConnector(NewConnector(rid, ids[i], "host1", nil, nil))
|
|
}
|
|
|
|
r.DelConnector("host1", ids[0])
|
|
got := r.GetConnector("host1")
|
|
if got == nil {
|
|
t.Fatal("GetConnector returned nil after deleting one of three")
|
|
}
|
|
}
|
|
|
|
func TestRouter_DelConnector_NoMatch(t *testing.T) {
|
|
rid := relay.NewTunnelID([]byte("0123456789abcdef"))
|
|
r := NewRouter("node1", rid)
|
|
|
|
cid1 := relay.NewConnectorID([]byte("aaaaaaaaaaaaaaaa"))
|
|
cid2 := relay.NewConnectorID([]byte("cccccccccccccccc"))
|
|
r.AddConnector(NewConnector(rid, cid1, "host1", nil, nil))
|
|
|
|
// Delete with non-existent cid — should be no-op
|
|
r.DelConnector("host1", cid2)
|
|
got := r.GetConnector("host1")
|
|
if got == nil {
|
|
t.Fatal("GetConnector returned nil after deleting non-existent cid")
|
|
}
|
|
if !got.ID().Equal(cid1) {
|
|
t.Errorf("remaining connector = %v, want %v", got.ID(), cid1)
|
|
}
|
|
}
|
|
|
|
func TestRouter_DelConnector_WrongHost(t *testing.T) {
|
|
rid := relay.NewTunnelID([]byte("0123456789abcdef"))
|
|
r := NewRouter("node1", rid)
|
|
|
|
cid1 := relay.NewConnectorID([]byte("aaaaaaaaaaaaaaaa"))
|
|
r.AddConnector(NewConnector(rid, cid1, "host1", nil, nil))
|
|
|
|
// Delete with correct cid but wrong host — should be no-op
|
|
r.DelConnector("other-host", cid1)
|
|
got := r.GetConnector("host1")
|
|
if got == nil {
|
|
t.Fatal("GetConnector returned nil after delete with wrong host")
|
|
}
|
|
}
|
|
|
|
func TestRouter_Close(t *testing.T) {
|
|
rid := relay.NewTunnelID([]byte("0123456789abcdef"))
|
|
r := NewRouter("node1", rid)
|
|
r.AddConnector(NewConnector(rid, relay.NewConnectorID([]byte("aaaaaaaaaaaaaaaa")), "host1", nil, nil))
|
|
|
|
if err := r.Close(); err != nil {
|
|
t.Fatalf("Close: %v", err)
|
|
}
|
|
if got := r.GetConnector("host1"); got != nil {
|
|
t.Error("GetConnector returned non-nil after Close")
|
|
}
|
|
}
|
|
|
|
func TestRouter_Close_Double(t *testing.T) {
|
|
rid := relay.NewTunnelID([]byte("0123456789abcdef"))
|
|
r := NewRouter("node1", rid)
|
|
r.Close()
|
|
r.Close() // second close should not panic
|
|
}
|
|
|
|
func TestRouter_Close_ClosesConnectors(t *testing.T) {
|
|
rid := relay.NewTunnelID([]byte("0123456789abcdef"))
|
|
r := NewRouter("node1", rid)
|
|
|
|
closed := false
|
|
c := NewConnector(rid, relay.NewConnectorID([]byte("aaaaaaaaaaaaaaaa")), "host1", &closeWriter{
|
|
closeFn: func() error { closed = true; return nil },
|
|
}, nil)
|
|
r.AddConnector(c)
|
|
|
|
r.Close()
|
|
if !closed {
|
|
t.Error("connector was not closed by Router.Close")
|
|
}
|
|
}
|
|
|
|
func TestRouter_ID(t *testing.T) {
|
|
rid := relay.NewTunnelID([]byte("0123456789abcdef"))
|
|
r := NewRouter("node1", rid)
|
|
if !r.ID().Equal(rid) {
|
|
t.Errorf("ID = %v, want %v", r.ID(), rid)
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// ConnectorPool tests
|
|
// ---------------------------------------------------------------------------
|
|
|
|
func TestConnectorPool_NilSafe_Get(t *testing.T) {
|
|
var p *ConnectorPool
|
|
if got := p.Get(relay.TunnelID{}, "host"); got != nil {
|
|
t.Errorf("nil pool Get = %v, want nil", got)
|
|
}
|
|
}
|
|
|
|
func TestConnectorPool_NilSafe_Del(t *testing.T) {
|
|
var p *ConnectorPool
|
|
p.Del(relay.TunnelID{}, "host", relay.ConnectorID{}) // should not panic
|
|
}
|
|
|
|
func TestConnectorPool_NilSafe_Close(t *testing.T) {
|
|
var p *ConnectorPool
|
|
if err := p.Close(); err != nil {
|
|
t.Fatalf("nil pool Close: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestConnectorPool_AddGet(t *testing.T) {
|
|
p := NewConnectorPool("node1")
|
|
rid := relay.NewTunnelID([]byte("0123456789abcdef"))
|
|
cid := relay.NewConnectorID([]byte("aaaaaaaaaaaaaaaa"))
|
|
|
|
c := NewConnector(rid, cid, "host1", nil, nil)
|
|
p.Add(rid, c)
|
|
|
|
got := p.Get(rid, "host1")
|
|
if got == nil {
|
|
t.Fatal("Get returned nil")
|
|
}
|
|
if !got.ID().Equal(cid) {
|
|
t.Errorf("ID = %v, want %v", got.ID(), cid)
|
|
}
|
|
}
|
|
|
|
func TestConnectorPool_AddGet_NewRouter(t *testing.T) {
|
|
p := NewConnectorPool("node1")
|
|
rid1 := relay.NewTunnelID([]byte("aaaaaaaaaaaaaaaa"))
|
|
rid2 := relay.NewTunnelID([]byte("bbbbbbbbbbbbbbbb"))
|
|
|
|
p.Add(rid1, NewConnector(rid1, relay.NewConnectorID([]byte("1111111111111111")), "host1", nil, nil))
|
|
p.Add(rid2, NewConnector(rid2, relay.NewConnectorID([]byte("2222222222222222")), "host1", nil, nil))
|
|
|
|
got1 := p.Get(rid1, "host1")
|
|
got2 := p.Get(rid2, "host1")
|
|
if got1 == nil || got2 == nil {
|
|
t.Fatal("different routers should both return connectors")
|
|
}
|
|
}
|
|
|
|
func TestConnectorPool_Get_MissingRouter(t *testing.T) {
|
|
p := NewConnectorPool("node1")
|
|
rid := relay.NewTunnelID([]byte("0123456789abcdef"))
|
|
otherRid := relay.NewTunnelID([]byte("ffffffffffffffff"))
|
|
|
|
p.Add(rid, NewConnector(rid, relay.NewConnectorID([]byte("aaaaaaaaaaaaaaaa")), "host1", nil, nil))
|
|
|
|
if got := p.Get(otherRid, "host1"); got != nil {
|
|
t.Error("Get with unknown rid returned non-nil")
|
|
}
|
|
}
|
|
|
|
func TestConnectorPool_Get_MissingHost(t *testing.T) {
|
|
p := NewConnectorPool("node1")
|
|
rid := relay.NewTunnelID([]byte("0123456789abcdef"))
|
|
|
|
p.Add(rid, NewConnector(rid, relay.NewConnectorID([]byte("aaaaaaaaaaaaaaaa")), "host1", nil, nil))
|
|
|
|
if got := p.Get(rid, "other-host"); got != nil {
|
|
t.Error("Get with unknown host returned non-nil")
|
|
}
|
|
}
|
|
|
|
func TestConnectorPool_Del(t *testing.T) {
|
|
p := NewConnectorPool("node1")
|
|
rid := relay.NewTunnelID([]byte("0123456789abcdef"))
|
|
cid1 := relay.NewConnectorID([]byte("aaaaaaaaaaaaaaaa"))
|
|
cid2 := relay.NewConnectorID([]byte("bbbbbbbbbbbbbbbb"))
|
|
|
|
p.Add(rid, NewConnector(rid, cid1, "host1", nil, nil))
|
|
p.Add(rid, NewConnector(rid, cid2, "host1", nil, nil))
|
|
|
|
p.Del(rid, "host1", cid1)
|
|
got := p.Get(rid, "host1")
|
|
if got == nil || !got.ID().Equal(cid2) {
|
|
t.Errorf("after Del got ID = %v, want %v", got, cid2)
|
|
}
|
|
}
|
|
|
|
func TestConnectorPool_Del_UnknownRouter(t *testing.T) {
|
|
p := NewConnectorPool("node1")
|
|
p.Del(relay.NewTunnelID([]byte("aaaaaaaaaaaaaaaa")), "host1", relay.ConnectorID{}) // no-op, should not panic
|
|
}
|
|
|
|
func TestConnectorPool_Close(t *testing.T) {
|
|
p := NewConnectorPool("node1")
|
|
rid := relay.NewTunnelID([]byte("0123456789abcdef"))
|
|
p.Add(rid, NewConnector(rid, relay.NewConnectorID([]byte("aaaaaaaaaaaaaaaa")), "host1", nil, nil))
|
|
|
|
if err := p.Close(); err != nil {
|
|
t.Fatalf("Close: %v", err)
|
|
}
|
|
if got := p.Get(rid, "host1"); got != nil {
|
|
t.Error("Get returned non-nil after Close")
|
|
}
|
|
}
|
|
|
|
func TestConnectorPool_Close_Double(t *testing.T) {
|
|
p := NewConnectorPool("node1")
|
|
p.Close()
|
|
p.Close() // should not panic
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// parseRouterID tests
|
|
// ---------------------------------------------------------------------------
|
|
|
|
func TestParseRouterID_Empty(t *testing.T) {
|
|
rid := parseRouterID("")
|
|
if !rid.Equal(relay.TunnelID{}) {
|
|
t.Errorf("parseRouterID('') = %v, want zero value", rid)
|
|
}
|
|
}
|
|
|
|
func TestParseRouterID_ValidUUID(t *testing.T) {
|
|
uid := uuid.New().String()
|
|
rid := parseRouterID(uid)
|
|
if rid.Equal(relay.TunnelID{}) {
|
|
t.Error("parseRouterID(valid) returned zero value")
|
|
}
|
|
}
|
|
|
|
func TestParseRouterID_Invalid(t *testing.T) {
|
|
rid := parseRouterID("not-a-uuid")
|
|
// Invalid UUID returns zero value (no error returned from function)
|
|
if !rid.Equal(relay.TunnelID{}) {
|
|
t.Errorf("parseRouterID('invalid') = %v, want zero value", rid)
|
|
}
|
|
} |