test(handler/router): add conn_test.go (packetConn, lockWriter)
This commit is contained in:
@@ -0,0 +1,254 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"math"
|
||||
"sync"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// packetConn tests
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
func TestPacketConn_ReadWrite(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
pc := &packetConn{&fakeConn{writeBuf: buf}}
|
||||
|
||||
// Write via packetConn's Write method
|
||||
n, err := pc.Write([]byte("hello"))
|
||||
if err != nil {
|
||||
t.Fatalf("Write: %v", err)
|
||||
}
|
||||
// packetConn.Write writes 2 bytes length-prefix + data, so n is the
|
||||
// total bytes written to the underlying conn.
|
||||
if n != 7 {
|
||||
t.Errorf("n = %d, want 7 (2-byte header + 5-byte payload)", n)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPacketConn_Write_ExceedsMaxUint16(t *testing.T) {
|
||||
pc := &packetConn{&fakeConn{}}
|
||||
large := make([]byte, math.MaxUint16+1)
|
||||
_, err := pc.Write(large)
|
||||
if err == nil {
|
||||
t.Error("Write: expected error for data exceeding MaxUint16")
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// lockWriter tests
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
func TestLockWriter_Write(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
lw := LockWriter(&buf)
|
||||
|
||||
n, err := lw.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 TestLockWriter_Concurrent(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
lw := LockWriter(&buf)
|
||||
|
||||
var wg sync.WaitGroup
|
||||
for i := 0; i < 10; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
lw.Write([]byte("x"))
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
if buf.Len() != 10 {
|
||||
t.Errorf("buf.Len = %d, want 10", buf.Len())
|
||||
}
|
||||
}
|
||||
|
||||
func TestLockWriter_Close_Closer(t *testing.T) {
|
||||
closed := false
|
||||
lw := LockWriter(&closeWriter{
|
||||
closeFn: func() error { closed = true; return nil },
|
||||
})
|
||||
|
||||
if err := lw.(io.Closer).Close(); err != nil {
|
||||
t.Fatalf("Close: %v", err)
|
||||
}
|
||||
if !closed {
|
||||
t.Error("underlying writer was not closed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLockWriter_Close_NonCloser(t *testing.T) {
|
||||
// bytes.Buffer does NOT implement io.Closer
|
||||
var buf bytes.Buffer
|
||||
lw := LockWriter(&buf)
|
||||
|
||||
if err := lw.(io.Closer).Close(); err != nil {
|
||||
t.Fatalf("Close: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLockWriter_Close_Multiple(t *testing.T) {
|
||||
callCount := 0
|
||||
lw := LockWriter(&closeWriter{
|
||||
closeFn: func() error { callCount++; return nil },
|
||||
})
|
||||
|
||||
lw.(io.Closer).Close()
|
||||
lw.(io.Closer).Close()
|
||||
|
||||
// Close should be called each time since lockWriter.Close delegates
|
||||
// to the underlying closer without tracking.
|
||||
if callCount != 2 {
|
||||
t.Errorf("close called %d times, want 2", callCount)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLockWriter_WriteAfterClose(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
lw := LockWriter(&buf)
|
||||
lw.(io.Closer).Close()
|
||||
|
||||
// Write should still work after close (lockWriter doesn't track closed state)
|
||||
n, err := lw.Write([]byte("data"))
|
||||
if err != nil {
|
||||
t.Fatalf("Write after close: %v", err)
|
||||
}
|
||||
if n != 4 {
|
||||
t.Errorf("n = %d, want 4", n)
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// LockWriter function tests
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
func TestLockWriter_NilWriter(t *testing.T) {
|
||||
lw := LockWriter(nil)
|
||||
if lw == nil {
|
||||
t.Fatal("LockWriter(nil) returned nil")
|
||||
}
|
||||
|
||||
// Writing through a lockWriter with a nil underlying writer will panic.
|
||||
defer func() {
|
||||
if r := recover(); r == nil {
|
||||
t.Error("Write to nil writer should panic")
|
||||
}
|
||||
}()
|
||||
lw.Write([]byte("data"))
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// packetConn Round-trip test using pipeConn
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
func TestPacketConn_RoundTrip(t *testing.T) {
|
||||
a, b := newPipePair()
|
||||
defer a.Close()
|
||||
defer b.Close()
|
||||
|
||||
pcA := &packetConn{a}
|
||||
pcB := &packetConn{b}
|
||||
|
||||
// Write through pcA
|
||||
data := []byte("hello world")
|
||||
n, err := pcA.Write(data)
|
||||
if err != nil {
|
||||
t.Fatalf("Write: %v", err)
|
||||
}
|
||||
if n != len(data) {
|
||||
t.Errorf("n = %d, want %d", n, len(data))
|
||||
}
|
||||
|
||||
// Read through pcB
|
||||
readBuf := make([]byte, 1024)
|
||||
nr, err := pcB.Read(readBuf)
|
||||
if err != nil {
|
||||
t.Fatalf("Read: %v", err)
|
||||
}
|
||||
if nr != len(data) {
|
||||
t.Errorf("nr = %d, want %d", nr, len(data))
|
||||
}
|
||||
if string(readBuf[:nr]) != string(data) {
|
||||
t.Errorf("Read = %q, want %q", string(readBuf[:nr]), string(data))
|
||||
}
|
||||
}
|
||||
|
||||
func TestPacketConn_Read_Empty(t *testing.T) {
|
||||
a, b := newPipePair()
|
||||
defer a.Close()
|
||||
defer b.Close()
|
||||
|
||||
pcA := &packetConn{a}
|
||||
pcB := &packetConn{b}
|
||||
|
||||
// Write empty data
|
||||
n, err := pcA.Write([]byte{})
|
||||
if err != nil {
|
||||
t.Fatalf("Write empty: %v", err)
|
||||
}
|
||||
if n != 0 {
|
||||
t.Errorf("n = %d, want 0", n)
|
||||
}
|
||||
|
||||
// Read should get 0 bytes
|
||||
readBuf := make([]byte, 1024)
|
||||
nr, err := pcB.Read(readBuf)
|
||||
if err != nil {
|
||||
t.Fatalf("Read: %v", err)
|
||||
}
|
||||
if nr != 0 {
|
||||
t.Errorf("nr = %d, want 0", nr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPacketConn_Read_BufferSmaller(t *testing.T) {
|
||||
a, b := newPipePair()
|
||||
defer a.Close()
|
||||
defer b.Close()
|
||||
|
||||
pcA := &packetConn{a}
|
||||
pcB := &packetConn{b}
|
||||
|
||||
// Write data larger than read buffer
|
||||
data := []byte("this is a long message for testing")
|
||||
pcA.Write(data)
|
||||
|
||||
// Read with small buffer
|
||||
readBuf := make([]byte, 5)
|
||||
nr, err := pcB.Read(readBuf)
|
||||
if err != nil {
|
||||
t.Fatalf("Read: %v", err)
|
||||
}
|
||||
if nr != 5 {
|
||||
t.Errorf("nr = %d, want 5", nr)
|
||||
}
|
||||
if string(readBuf[:nr]) != "this " {
|
||||
t.Errorf("Read = %q, want %q", string(readBuf[:nr]), "this ")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPacketConn_Read_EOF(t *testing.T) {
|
||||
a, b := newPipePair()
|
||||
a.Close() // Close write end so b gets EOF
|
||||
|
||||
pcB := &packetConn{b}
|
||||
readBuf := make([]byte, 1024)
|
||||
_, err := pcB.Read(readBuf)
|
||||
if err != io.EOF {
|
||||
t.Errorf("Read = %v, want EOF", err)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user