Files
x/metrics/wrapper/conn_test.go
T
ginuerzh bc3d12ec2c fix(metrics): add doc comments, fix labels mutation and nil returns, add idempotent Close, add nil guards, add tests
- Fix promMetrics.Gauge/Counter/Observer mutating caller's labels map by
  using maps.Copy instead of direct assignment
- Fix Gauge/Counter/Observer returning nil for unknown metric names
  (now return noop implementations)
- Fix metricService.Close() race with sync.Once for safe concurrent calls
- Fix SetDSCP returning nil instead of errUnsupport on unsupported ops
- Add nil guards to WrapConn, WrapPacketConn, WrapUDPConn, WrapListener
- Add doc comments to all exported symbols
- Add 45 unit tests (metrics, noop, prom, service, wrapper)
2026-05-24 22:50:41 +08:00

231 lines
5.6 KiB
Go

package wrapper
import (
"io"
"net"
"sync"
"testing"
"time"
xio "github.com/go-gost/x/internal/io"
)
func TestWrapConnNil(t *testing.T) {
if c := WrapConn("svc", nil); c != nil {
t.Error("WrapConn(nil) should return nil")
}
}
func TestWrapPacketConnNil(t *testing.T) {
if pc := WrapPacketConn("svc", nil); pc != nil {
t.Error("WrapPacketConn(nil) should return nil")
}
}
func TestWrapUDPConnNil(t *testing.T) {
if uc := WrapUDPConn("svc", nil); uc != nil {
t.Error("WrapUDPConn(nil) should return nil")
}
}
func TestWrapConnReadWrite(t *testing.T) {
client, server := net.Pipe()
defer client.Close()
defer server.Close()
wrapped := WrapConn("test-svc", server)
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
client.Write([]byte("hello"))
}()
buf := make([]byte, 1024)
n, err := wrapped.Read(buf)
wg.Wait()
if err != nil {
t.Fatalf("Read error: %v", err)
}
if n != 5 {
t.Errorf("expected 5 bytes, got %d", n)
}
if string(buf[:n]) != "hello" {
t.Errorf("expected 'hello', got %q", string(buf[:n]))
}
// Write through the wrapped conn.
go func() {
buf := make([]byte, 1024)
n, err := client.Read(buf)
if err != nil {
t.Errorf("client Read error: %v", err)
return
}
if n != 5 || string(buf[:n]) != "world" {
t.Errorf("expected 'world', got %q", string(buf[:n]))
}
}()
nw, err := wrapped.Write([]byte("world"))
if err != nil {
t.Fatalf("Write error: %v", err)
}
if nw != 5 {
t.Errorf("expected 5 bytes written, got %d", nw)
}
}
func TestWrapConnSyscallConn(t *testing.T) {
client, server := net.Pipe()
defer client.Close()
defer server.Close()
wrapped := WrapConn("test-svc", server)
// SyscallConn should not panic. The return values depend on the underlying
// connection type.
_, _ = wrapped.(*serverConn).SyscallConn()
}
func TestWrapConnCloseReadCloseWrite(t *testing.T) {
client, server := net.Pipe()
defer client.Close()
defer server.Close()
wrapped := WrapConn("test-svc", server)
// net.Pipe does not implement CloseRead/CloseWrite, so these should
// return ErrUnsupported.
err := wrapped.(*serverConn).CloseRead()
if err != xio.ErrUnsupported {
t.Errorf("CloseRead: expected ErrUnsupported, got %v", err)
}
err = wrapped.(*serverConn).CloseWrite()
if err != xio.ErrUnsupported {
t.Errorf("CloseWrite: expected ErrUnsupported, got %v", err)
}
}
func TestWrapConnContext(t *testing.T) {
client, server := net.Pipe()
defer client.Close()
defer server.Close()
wrapped := WrapConn("test-svc", server)
// net.Pipe does not implement ctx.Context, so Context() returns nil.
ctx := wrapped.(*serverConn).Context()
if ctx != nil {
t.Errorf("expected nil context, got %v", ctx)
}
}
// mockPacketConn implements net.PacketConn for testing.
type mockPacketConn struct {
readFrom func([]byte) (int, net.Addr, error)
writeTo func([]byte, net.Addr) (int, error)
}
func (m *mockPacketConn) ReadFrom(p []byte) (int, net.Addr, error) {
return m.readFrom(p)
}
func (m *mockPacketConn) WriteTo(p []byte, addr net.Addr) (int, error) {
return m.writeTo(p, addr)
}
func (m *mockPacketConn) Close() error { return nil }
func (m *mockPacketConn) LocalAddr() net.Addr { return &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0} }
func (m *mockPacketConn) SetDeadline(t time.Time) error { return nil }
func (m *mockPacketConn) SetReadDeadline(t time.Time) error { return nil }
func (m *mockPacketConn) SetWriteDeadline(t time.Time) error { return nil }
func TestWrapPacketConnReadFromWriteTo(t *testing.T) {
testAddr := &net.UDPAddr{IP: net.IPv4(10, 0, 0, 1), Port: 1234}
mock := &mockPacketConn{
readFrom: func(p []byte) (int, net.Addr, error) {
copy(p, []byte("data"))
return 4, testAddr, nil
},
writeTo: func(p []byte, addr net.Addr) (int, error) {
return len(p), nil
},
}
wrapped := WrapPacketConn("test-svc", mock)
// ReadFrom
buf := make([]byte, 1024)
n, addr, err := wrapped.ReadFrom(buf)
if err != nil {
t.Fatalf("ReadFrom error: %v", err)
}
if n != 4 {
t.Errorf("expected 4 bytes, got %d", n)
}
if addr.String() != testAddr.String() {
t.Errorf("expected addr %s, got %s", testAddr, addr)
}
// WriteTo
nw, err := wrapped.WriteTo([]byte("reply"), testAddr)
if err != nil {
t.Fatalf("WriteTo error: %v", err)
}
if nw != 5 {
t.Errorf("expected 5 bytes written, got %d", nw)
}
}
func TestWrapPacketConnContext(t *testing.T) {
mock := &mockPacketConn{
readFrom: func(p []byte) (int, net.Addr, error) { return 0, nil, io.EOF },
writeTo: func(p []byte, addr net.Addr) (int, error) { return len(p), nil },
}
wrapped := WrapPacketConn("test-svc", mock)
// mockPacketConn does not implement ctx.Context
ctx := wrapped.(*packetConn).Context()
if ctx != nil {
t.Errorf("expected nil context, got %v", ctx)
}
}
func TestWrapConnRemoteAddr(t *testing.T) {
client, server := net.Pipe()
defer client.Close()
defer server.Close()
wrapped := WrapConn("test-svc", server)
// The wrapped conn should expose the underlying remote address.
if wrapped.RemoteAddr() == nil {
t.Error("RemoteAddr should not be nil")
}
}
func TestWrapConnNilLabels(t *testing.T) {
// When the remote address has no port, SplitHostPort may fail.
// WrapConn should still succeed and the host may be empty.
client, server := net.Pipe()
defer client.Close()
defer server.Close()
wrapped := WrapConn("test-svc", server)
// Read should not panic even with potentially malformed addresses.
// net.Pipe addresses are well-formed so this is a smoke test.
buf := make([]byte, 1)
go client.Write([]byte{0x01})
_, err := wrapped.Read(buf)
if err != nil {
t.Fatalf("Read error: %v", err)
}
}