package buffer import ( "bufio" "bytes" "io" "testing" "time" "git.quad4.io/Networks/Reticulum-Go/pkg/channel" "git.quad4.io/Networks/Reticulum-Go/pkg/common" "git.quad4.io/Networks/Reticulum-Go/pkg/packet" "git.quad4.io/Networks/Reticulum-Go/pkg/transport" ) func TestStreamDataMessage_Pack(t *testing.T) { tests := []struct { name string streamID uint16 data []byte eof bool compressed bool }{ { name: "NormalMessage", streamID: 123, data: []byte("test data"), eof: false, compressed: false, }, { name: "EOFMessage", streamID: 456, data: []byte("final data"), eof: true, compressed: false, }, { name: "CompressedMessage", streamID: 789, data: []byte("compressed data"), eof: false, compressed: true, }, { name: "EmptyData", streamID: 0, data: []byte{}, eof: false, compressed: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { msg := &StreamDataMessage{ StreamID: tt.streamID, Data: tt.data, EOF: tt.eof, Compressed: tt.compressed, } packed, err := msg.Pack() if err != nil { t.Fatalf("Pack() failed: %v", err) } if len(packed) < 2 { t.Error("Packed message too short") } unpacked := &StreamDataMessage{} if err := unpacked.Unpack(packed); err != nil { t.Fatalf("Unpack() failed: %v", err) } if unpacked.StreamID != tt.streamID { t.Errorf("StreamID = %d, want %d", unpacked.StreamID, tt.streamID) } if unpacked.EOF != tt.eof { t.Errorf("EOF = %v, want %v", unpacked.EOF, tt.eof) } if unpacked.Compressed != tt.compressed { t.Errorf("Compressed = %v, want %v", unpacked.Compressed, tt.compressed) } if !bytes.Equal(unpacked.Data, tt.data) { t.Errorf("Data = %v, want %v", unpacked.Data, tt.data) } }) } } func TestStreamDataMessage_Unpack(t *testing.T) { tests := []struct { name string data []byte wantError bool }{ { name: "ValidMessage", data: []byte{0x00, 0x7B, 'h', 'e', 'l', 'l', 'o'}, wantError: false, }, { name: "TooShort", data: []byte{0x00}, wantError: true, }, { name: "Empty", data: []byte{}, wantError: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { msg := &StreamDataMessage{} err := msg.Unpack(tt.data) if (err != nil) != tt.wantError { t.Errorf("Unpack() error = %v, wantError %v", err, tt.wantError) } }) } } func TestStreamDataMessage_GetType(t *testing.T) { msg := &StreamDataMessage{} if msg.GetType() != 0x01 { t.Errorf("GetType() = %d, want 0x01", msg.GetType()) } } func TestRawChannelReader_AddCallback(t *testing.T) { reader := &RawChannelReader{ streamID: 1, buffer: bytes.NewBuffer(nil), callbacks: make(map[int]func(int)), } cb := func(int) {} reader.AddReadyCallback(cb) if len(reader.callbacks) != 1 { t.Error("Callback should be added") } } func TestBuffer_Write(t *testing.T) { buf := &Buffer{ ReadWriter: bufio.NewReadWriter(bufio.NewReader(bytes.NewBuffer(nil)), bufio.NewWriter(bytes.NewBuffer(nil))), } data := []byte("test") n, err := buf.Write(data) if err != nil { t.Errorf("Write() error = %v", err) } if n != len(data) { t.Errorf("Write() = %d bytes, want %d", n, len(data)) } } func TestBuffer_Read(t *testing.T) { buf := &Buffer{ ReadWriter: bufio.NewReadWriter(bufio.NewReader(bytes.NewBuffer([]byte("test data"))), bufio.NewWriter(bytes.NewBuffer(nil))), } data := make([]byte, 10) n, err := buf.Read(data) if err != nil && err != io.EOF { t.Errorf("Read() error = %v", err) } if n <= 0 { t.Errorf("Read() = %d bytes, want > 0", n) } } func TestBuffer_Close(t *testing.T) { buf := &Buffer{ ReadWriter: bufio.NewReadWriter(bufio.NewReader(bytes.NewBuffer(nil)), bufio.NewWriter(bytes.NewBuffer(nil))), } if err := buf.Close(); err != nil { t.Errorf("Close() error = %v", err) } } func TestStreamIDMax(t *testing.T) { if StreamIDMax != 0x3fff { t.Errorf("StreamIDMax = %d, want %d", StreamIDMax, 0x3fff) } } func TestMaxChunkLen(t *testing.T) { if MaxChunkLen != 16*1024 { t.Errorf("MaxChunkLen = %d, want %d", MaxChunkLen, 16*1024) } } func TestMaxDataLen(t *testing.T) { if MaxDataLen != 457 { t.Errorf("MaxDataLen = %d, want %d", MaxDataLen, 457) } } type mockLink struct { status byte rtt float64 } func (m *mockLink) GetStatus() byte { return m.status } func (m *mockLink) GetRTT() float64 { return m.rtt } func (m *mockLink) RTT() float64 { return m.rtt } func (m *mockLink) GetLinkID() []byte { return []byte("testlink") } func (m *mockLink) Send(data []byte) interface{} { return &packet.Packet{Raw: data} } func (m *mockLink) Resend(p interface{}) error { return nil } func (m *mockLink) SetPacketTimeout(p interface{}, cb func(interface{}), t time.Duration) {} func (m *mockLink) SetPacketDelivered(p interface{}, cb func(interface{})) {} func (m *mockLink) HandleInbound(pkt *packet.Packet) error { return nil } func (m *mockLink) ValidateLinkProof(pkt *packet.Packet, networkIface common.NetworkInterface) error { return nil } func TestNewRawChannelReader(t *testing.T) { link := &mockLink{status: transport.STATUS_ACTIVE} ch := channel.NewChannel(link) reader := NewRawChannelReader(123, ch) if reader.streamID != 123 { t.Errorf("streamID = %d, want %d", reader.streamID, 123) } if reader.channel != ch { t.Error("channel not set correctly") } if reader.buffer == nil { t.Error("buffer is nil") } if reader.callbacks == nil { t.Error("callbacks is nil") } } func TestRawChannelReader_RemoveReadyCallback(t *testing.T) { reader := &RawChannelReader{ streamID: 1, buffer: bytes.NewBuffer(nil), callbacks: make(map[int]func(int)), } cb1 := func(int) {} cb2 := func(int) {} id1 := reader.AddReadyCallback(cb1) reader.AddReadyCallback(cb2) if len(reader.callbacks) != 2 { t.Errorf("callbacks length = %d, want 2", len(reader.callbacks)) } reader.RemoveReadyCallback(id1) if len(reader.callbacks) != 1 { t.Errorf("RemoveReadyCallback did not remove callback, length = %d", len(reader.callbacks)) } } func TestRawChannelReader_Read(t *testing.T) { reader := &RawChannelReader{ streamID: 1, buffer: bytes.NewBuffer([]byte("test data")), eof: false, } data := make([]byte, 10) n, err := reader.Read(data) if err != nil { t.Errorf("Read() error = %v", err) } if n == 0 { t.Error("Read() returned 0 bytes") } reader.eof = true reader.buffer = bytes.NewBuffer(nil) n, err = reader.Read(data) if err != io.EOF { t.Errorf("Read() error = %v, want io.EOF", err) } if n != 0 { t.Errorf("Read() = %d bytes, want 0", n) } } func TestRawChannelReader_HandleMessage(t *testing.T) { reader := &RawChannelReader{ streamID: 1, buffer: bytes.NewBuffer(nil), callbacks: make(map[int]func(int)), } msg := &StreamDataMessage{ StreamID: 1, Data: []byte("test"), EOF: false, Compressed: false, } called := false reader.AddReadyCallback(func(int) { called = true }) result := reader.HandleMessage(msg) if !result { t.Error("HandleMessage() = false, want true") } if !called { t.Error("callback was not called") } if reader.buffer.Len() == 0 { t.Error("buffer is empty after HandleMessage") } msg.StreamID = 2 result = reader.HandleMessage(msg) if result { t.Error("HandleMessage() = true, want false for different streamID") } msg.StreamID = 1 msg.EOF = true reader.HandleMessage(msg) if !reader.eof { t.Error("EOF not set after HandleMessage with EOF flag") } } func TestNewRawChannelWriter(t *testing.T) { link := &mockLink{status: transport.STATUS_ACTIVE} ch := channel.NewChannel(link) writer := NewRawChannelWriter(456, ch) if writer.streamID != 456 { t.Errorf("streamID = %d, want %d", writer.streamID, 456) } if writer.channel != ch { t.Error("channel not set correctly") } if writer.eof { t.Error("eof should be false initially") } } func TestRawChannelWriter_Write(t *testing.T) { link := &mockLink{status: transport.STATUS_ACTIVE} ch := channel.NewChannel(link) writer := NewRawChannelWriter(1, ch) data := []byte("test data") n, err := writer.Write(data) if err != nil { t.Errorf("Write() error = %v", err) } if n != len(data) { t.Errorf("Write() = %d bytes, want %d", n, len(data)) } largeData := make([]byte, MaxChunkLen+100) n, err = writer.Write(largeData) if err != nil { t.Errorf("Write() error = %v", err) } if n != MaxChunkLen { t.Errorf("Write() = %d bytes, want %d", n, MaxChunkLen) } } func TestRawChannelWriter_Close(t *testing.T) { link := &mockLink{status: transport.STATUS_ACTIVE} ch := channel.NewChannel(link) writer := NewRawChannelWriter(1, ch) if writer.eof { t.Error("EOF should be false before Close()") } err := writer.Close() if err != nil { t.Errorf("Close() error = %v", err) } if !writer.eof { t.Error("EOF should be true after Close()") } } func TestCreateReader(t *testing.T) { link := &mockLink{status: transport.STATUS_ACTIVE} ch := channel.NewChannel(link) callback := func(int) {} reader := CreateReader(789, ch, callback) if reader == nil { t.Error("CreateReader() returned nil") } } func TestCreateWriter(t *testing.T) { link := &mockLink{status: transport.STATUS_ACTIVE} ch := channel.NewChannel(link) writer := CreateWriter(101, ch) if writer == nil { t.Error("CreateWriter() returned nil") } } func TestCreateBidirectionalBuffer(t *testing.T) { link := &mockLink{status: transport.STATUS_ACTIVE} ch := channel.NewChannel(link) callback := func(int) {} buf := CreateBidirectionalBuffer(1, 2, ch, callback) if buf == nil { t.Error("CreateBidirectionalBuffer() returned nil") } } func TestCompressData(t *testing.T) { data := []byte("test data for compression") compressed := compressData(data) if compressed == nil { t.Skip("compressData() returned nil (compression implementation may be incomplete)") } } func TestDecompressData(t *testing.T) { data := []byte("test data") compressed := compressData(data) if compressed == nil { t.Skip("compression not working, skipping decompression test") } decompressed := decompressData(compressed) if decompressed == nil { t.Error("decompressData() returned nil") } }