mirror of
https://github.com/hamburghammer/grcon.git
synced 2024-12-23 13:37:43 +01:00
425 lines
9.9 KiB
Go
425 lines
9.9 KiB
Go
|
package grcon_test
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"errors"
|
||
|
"net"
|
||
|
"testing"
|
||
|
"time"
|
||
|
|
||
|
"github.com/hamburghammer/grcon"
|
||
|
)
|
||
|
|
||
|
func TestRemoteConsole_Write(t *testing.T) {
|
||
|
t.Run("compare written bytes", func(t *testing.T) {
|
||
|
mockConn := &MockConn{}
|
||
|
remoteConsole := grcon.NewRemoteConsole(mockConn)
|
||
|
|
||
|
expect := []byte{
|
||
|
// size
|
||
|
13, 0, 0, 0,
|
||
|
// id
|
||
|
1, 0, 0, 0,
|
||
|
// type
|
||
|
2, 0, 0, 0,
|
||
|
// body with null termination
|
||
|
102, 111, 111, 0,
|
||
|
// termination
|
||
|
0,
|
||
|
}
|
||
|
|
||
|
// under test
|
||
|
err := remoteConsole.Write(grcon.Packet{
|
||
|
Id: grcon.PacketId(1),
|
||
|
Type: grcon.SERVERDATA_EXECCOMMAND,
|
||
|
Body: []byte("foo"),
|
||
|
})
|
||
|
if err != nil {
|
||
|
t.Errorf("an error occurred that was not expected: %s", err.Error())
|
||
|
t.FailNow()
|
||
|
}
|
||
|
got := mockConn.Send[0]
|
||
|
|
||
|
if !bytes.Equal(expect, got) {
|
||
|
t.Errorf("written bytes does not match:\nexpected:\n%b\ngot:\n%b\n", expect, got)
|
||
|
}
|
||
|
})
|
||
|
|
||
|
t.Run("too long body", func(t *testing.T) {
|
||
|
mockConn := &MockConn{}
|
||
|
remoteConsole := grcon.NewRemoteConsole(mockConn)
|
||
|
// under test
|
||
|
err := remoteConsole.Write(grcon.Packet{
|
||
|
Id: grcon.PacketId(1),
|
||
|
Type: grcon.SERVERDATA_EXECCOMMAND,
|
||
|
Body: make([]byte, grcon.MaxPacket),
|
||
|
})
|
||
|
|
||
|
if _, ok := err.(grcon.RequestTooLongError); !ok {
|
||
|
t.Errorf("error did not match:\nexpected:\n%T\ngot:\n%T", grcon.RequestTooLongError{}, err)
|
||
|
t.FailNow()
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
|
||
|
func TestRemoteConsole_Read(t *testing.T) {
|
||
|
t.Run("normal packet", func(t *testing.T) {
|
||
|
mockConn := &MockConn{}
|
||
|
mockConn.Receive = make([][]byte, 0, 1)
|
||
|
mockConn.Receive = append(mockConn.Receive, []byte{
|
||
|
// size
|
||
|
13, 0, 0, 0,
|
||
|
// id
|
||
|
1, 0, 0, 0,
|
||
|
// type
|
||
|
0, 0, 0, 0,
|
||
|
// body with null termination
|
||
|
102, 111, 111, 0,
|
||
|
// termination
|
||
|
0,
|
||
|
})
|
||
|
remoteConsole := grcon.NewRemoteConsole(mockConn)
|
||
|
|
||
|
expect := grcon.Packet{
|
||
|
Id: 1,
|
||
|
Type: grcon.SERVERDATA_RESPONSE_VALUE,
|
||
|
Body: []byte("foo"),
|
||
|
}
|
||
|
// under test
|
||
|
got, err := remoteConsole.Read()
|
||
|
if err != nil {
|
||
|
t.Errorf("an error occurred that was not expected: %s", err.Error())
|
||
|
t.FailNow()
|
||
|
}
|
||
|
|
||
|
if !EqualPacket(expect, got) {
|
||
|
t.Errorf("packet are not equal:\nexpected:\n%+v\ngot:\n%+v", expect, got)
|
||
|
}
|
||
|
})
|
||
|
|
||
|
t.Run("max packet", func(t *testing.T) {
|
||
|
mockConn := &MockConn{}
|
||
|
remoteConsole := grcon.NewRemoteConsole(mockConn)
|
||
|
|
||
|
// we use the write method to make it easier for us
|
||
|
// for this we have to make sure that the method works as expected!
|
||
|
remoteConsole.Write(grcon.Packet{Id: 1, Type: grcon.SERVERDATA_RESPONSE_VALUE, Body: make([]byte, grcon.MaxBody)})
|
||
|
mockConn.Receive = make([][]byte, 0, 1)
|
||
|
mockConn.Receive = append(mockConn.Receive, mockConn.Send...)
|
||
|
|
||
|
expect := grcon.Packet{
|
||
|
Id: 1,
|
||
|
Type: grcon.SERVERDATA_RESPONSE_VALUE,
|
||
|
Body: make([]byte, grcon.MaxBody),
|
||
|
}
|
||
|
// under test
|
||
|
got, err := remoteConsole.Read()
|
||
|
if err != nil {
|
||
|
t.Errorf("an error occurred that was not expected: %s", err.Error())
|
||
|
t.FailNow()
|
||
|
}
|
||
|
|
||
|
if len(got.Body) != len(expect.Body) {
|
||
|
t.Errorf("packet body has not the expected size:\nexpected:%d\ngot: %d", len(expect.Body), len(got.Body))
|
||
|
}
|
||
|
|
||
|
if !EqualPacket(expect, got) {
|
||
|
t.Errorf("packet are not equal:\nexpected:\n%+v\ngot:\n%+v", expect, got)
|
||
|
}
|
||
|
})
|
||
|
|
||
|
t.Run("min packet", func(t *testing.T) {
|
||
|
mockConn := &MockConn{}
|
||
|
remoteConsole := grcon.NewRemoteConsole(mockConn)
|
||
|
|
||
|
// we use the write method to make it easier for us
|
||
|
// for this we have to make sure that the method works as expected!
|
||
|
remoteConsole.Write(grcon.Packet{Id: 1, Type: grcon.SERVERDATA_RESPONSE_VALUE, Body: []byte{}})
|
||
|
mockConn.Receive = make([][]byte, 0, 1)
|
||
|
mockConn.Receive = append(mockConn.Receive, mockConn.Send...)
|
||
|
|
||
|
expect := grcon.Packet{
|
||
|
Id: 1,
|
||
|
Type: grcon.SERVERDATA_RESPONSE_VALUE,
|
||
|
Body: []byte{},
|
||
|
}
|
||
|
// under test
|
||
|
got, err := remoteConsole.Read()
|
||
|
if err != nil {
|
||
|
t.Errorf("an error occurred that was not expected: %s", err.Error())
|
||
|
t.FailNow()
|
||
|
}
|
||
|
|
||
|
if len(got.Body) != len(expect.Body) {
|
||
|
t.Errorf("packet body has not the expected size:\nexpected:%d\ngot: %d", len(expect.Body), len(got.Body))
|
||
|
}
|
||
|
|
||
|
if !EqualPacket(expect, got) {
|
||
|
t.Errorf("packet are not equal:\nexpected:\n%+v\ngot:\n%+v", expect, got)
|
||
|
}
|
||
|
})
|
||
|
|
||
|
t.Run("tow packets using the queue buffer.", func(t *testing.T) {
|
||
|
mockConn := &MockConn{}
|
||
|
mockConn.Receive = make([][]byte, 0, 1)
|
||
|
mockConn.Receive = append(mockConn.Receive, []byte{
|
||
|
// size
|
||
|
13, 0, 0, 0,
|
||
|
// id
|
||
|
1, 0, 0, 0,
|
||
|
// type
|
||
|
0, 0, 0, 0,
|
||
|
// body with null termination
|
||
|
102, 111, 111, 0,
|
||
|
// termination
|
||
|
0,
|
||
|
|
||
|
// size
|
||
|
13, 0, 0, 0,
|
||
|
// id
|
||
|
2, 0, 0, 0,
|
||
|
// type
|
||
|
0, 0, 0, 0,
|
||
|
// body with null termination
|
||
|
102, 111, 111, 0,
|
||
|
// termination
|
||
|
0,
|
||
|
})
|
||
|
remoteConsole := grcon.NewRemoteConsole(mockConn)
|
||
|
|
||
|
expect1 := grcon.Packet{
|
||
|
Id: 1,
|
||
|
Type: grcon.SERVERDATA_RESPONSE_VALUE,
|
||
|
Body: []byte("foo"),
|
||
|
}
|
||
|
expect2 := grcon.Packet{
|
||
|
Id: 2,
|
||
|
Type: grcon.SERVERDATA_RESPONSE_VALUE,
|
||
|
Body: []byte("foo"),
|
||
|
}
|
||
|
|
||
|
// under test
|
||
|
got, err := remoteConsole.Read()
|
||
|
if err != nil {
|
||
|
t.Errorf("an error occurred that was not expected: %s", err.Error())
|
||
|
t.FailNow()
|
||
|
}
|
||
|
|
||
|
if !EqualPacket(expect1, got) {
|
||
|
t.Errorf("packet are not equal:\nexpected:\n%+v\ngot:\n%+v", expect1, got)
|
||
|
}
|
||
|
|
||
|
// second read
|
||
|
got, err = remoteConsole.Read()
|
||
|
if err != nil {
|
||
|
t.Errorf("an error occurred that was not expected: %s", err.Error())
|
||
|
t.FailNow()
|
||
|
}
|
||
|
|
||
|
if !EqualPacket(expect2, got) {
|
||
|
t.Errorf("packet are not equal:\nexpected:\n%+v\ngot:\n%+v", expect2, got)
|
||
|
}
|
||
|
|
||
|
})
|
||
|
|
||
|
t.Run("receive size field over slow connection", func(t *testing.T) {
|
||
|
mockConn := &MockConn{}
|
||
|
mockConn.Receive = make([][]byte, 0, 5)
|
||
|
// size
|
||
|
mockConn.Receive = append(mockConn.Receive, []byte{13})
|
||
|
mockConn.Receive = append(mockConn.Receive, []byte{0})
|
||
|
mockConn.Receive = append(mockConn.Receive, []byte{0})
|
||
|
mockConn.Receive = append(mockConn.Receive, []byte{0})
|
||
|
|
||
|
mockConn.Receive = append(mockConn.Receive, []byte{
|
||
|
// id
|
||
|
1, 0, 0, 0,
|
||
|
// type
|
||
|
0, 0, 0, 0,
|
||
|
// body with null termination
|
||
|
102, 111, 111, 0,
|
||
|
// termination
|
||
|
0,
|
||
|
})
|
||
|
|
||
|
remoteConsole := grcon.NewRemoteConsole(mockConn)
|
||
|
|
||
|
expect := grcon.Packet{
|
||
|
Id: 1,
|
||
|
Type: grcon.SERVERDATA_RESPONSE_VALUE,
|
||
|
Body: []byte("foo"),
|
||
|
}
|
||
|
// under test
|
||
|
got, err := remoteConsole.Read()
|
||
|
if err != nil {
|
||
|
t.Errorf("an error occurred that was not expected: %s", err.Error())
|
||
|
t.FailNow()
|
||
|
}
|
||
|
|
||
|
if !EqualPacket(expect, got) {
|
||
|
t.Errorf("packet are not equal:\nexpected:\n%+v\ngot:\n%+v", expect, got)
|
||
|
}
|
||
|
})
|
||
|
|
||
|
t.Run("receive packet over slow connection execpt size field", func(t *testing.T) {
|
||
|
mockConn := &MockConn{}
|
||
|
mockConn.Receive = make([][]byte, 0, 5)
|
||
|
// size
|
||
|
mockConn.Receive = append(mockConn.Receive, []byte{13, 0, 0, 0})
|
||
|
// id
|
||
|
mockConn.Receive = append(mockConn.Receive, []byte{1, 0, 0, 0})
|
||
|
// type
|
||
|
mockConn.Receive = append(mockConn.Receive, []byte{0, 0, 0, 0})
|
||
|
// body with null termination
|
||
|
mockConn.Receive = append(mockConn.Receive, []byte{102, 111, 111, 0})
|
||
|
// termination
|
||
|
mockConn.Receive = append(mockConn.Receive, []byte{0})
|
||
|
|
||
|
remoteConsole := grcon.NewRemoteConsole(mockConn)
|
||
|
|
||
|
expect := grcon.Packet{
|
||
|
Id: 1,
|
||
|
Type: grcon.SERVERDATA_RESPONSE_VALUE,
|
||
|
Body: []byte("foo"),
|
||
|
}
|
||
|
// under test
|
||
|
got, err := remoteConsole.Read()
|
||
|
if err != nil {
|
||
|
t.Errorf("an error occurred that was not expected: %s", err.Error())
|
||
|
t.FailNow()
|
||
|
}
|
||
|
|
||
|
if !EqualPacket(expect, got) {
|
||
|
t.Errorf("packet are not equal:\nexpected:\n%+v\ngot:\n%+v", expect, got)
|
||
|
}
|
||
|
})
|
||
|
|
||
|
t.Run("too large packet", func(t *testing.T) {
|
||
|
mockConn := &MockConn{}
|
||
|
mockConn.Receive = make([][]byte, 0, 1)
|
||
|
mockConn.Receive = append(mockConn.Receive, []byte{
|
||
|
// size
|
||
|
// 4100
|
||
|
4, 16, 0, 0,
|
||
|
// id
|
||
|
1, 0, 0, 0,
|
||
|
// type
|
||
|
0, 0, 0, 0,
|
||
|
// body with null termination
|
||
|
102, 111, 111, 0,
|
||
|
// termination
|
||
|
0,
|
||
|
})
|
||
|
remoteConsole := grcon.NewRemoteConsole(mockConn)
|
||
|
|
||
|
// under test
|
||
|
_, err := remoteConsole.Read()
|
||
|
if _, ok := err.(grcon.ResponseTooLongError); !ok {
|
||
|
t.Errorf("error did not match:\nexpected:\n%T\ngot:\n%T", grcon.ResponseTooLongError{}, err)
|
||
|
t.FailNow()
|
||
|
}
|
||
|
})
|
||
|
|
||
|
t.Run("too small packet", func(t *testing.T) {
|
||
|
mockConn := &MockConn{}
|
||
|
mockConn.Receive = make([][]byte, 0, 1)
|
||
|
mockConn.Receive = append(mockConn.Receive, []byte{
|
||
|
// size
|
||
|
// 1
|
||
|
1, 0, 0, 0,
|
||
|
// id
|
||
|
1, 0, 0, 0,
|
||
|
// type
|
||
|
0, 0, 0, 0,
|
||
|
// body with null termination
|
||
|
102, 111, 111, 0,
|
||
|
// termination
|
||
|
0,
|
||
|
})
|
||
|
remoteConsole := grcon.NewRemoteConsole(mockConn)
|
||
|
|
||
|
// under test
|
||
|
_, err := remoteConsole.Read()
|
||
|
if _, ok := err.(grcon.UnexpectedFormatError); !ok {
|
||
|
t.Errorf("error did not match:\nexpected:\n%T\ngot:\n%T", grcon.UnexpectedFormatError{}, err)
|
||
|
t.FailNow()
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
|
||
|
// Helper functions
|
||
|
|
||
|
func EqualPacket(expected, got grcon.Packet) bool {
|
||
|
if expected.Id != got.Id {
|
||
|
return false
|
||
|
}
|
||
|
if expected.Type != got.Type {
|
||
|
return false
|
||
|
}
|
||
|
if !bytes.Equal(expected.Body, got.Body) {
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
// Mock implementations for tests
|
||
|
|
||
|
var ErrNotImplemented = errors.New("not implemented")
|
||
|
|
||
|
type MockConn struct {
|
||
|
Send [][]byte
|
||
|
Receive [][]byte
|
||
|
nextSend int
|
||
|
nextReceive int
|
||
|
IsClosed bool
|
||
|
}
|
||
|
|
||
|
func (c *MockConn) Read(b []byte) (n int, err error) {
|
||
|
toReceive := c.Receive[c.nextReceive]
|
||
|
n = copy(b, toReceive)
|
||
|
c.nextReceive++
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func (c *MockConn) Write(b []byte) (n int, err error) {
|
||
|
if c.Send == nil {
|
||
|
c.Send = make([][]byte, 0, 1)
|
||
|
}
|
||
|
toSend := make([]byte, len(b))
|
||
|
n = copy(toSend, b)
|
||
|
c.Send = append(c.Send, toSend)
|
||
|
c.nextSend++
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func (c *MockConn) Close() error {
|
||
|
c.IsClosed = true
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (c *MockConn) LocalAddr() net.Addr {
|
||
|
return mockAddr{}
|
||
|
}
|
||
|
|
||
|
func (c *MockConn) RemoteAddr() net.Addr {
|
||
|
return mockAddr{}
|
||
|
}
|
||
|
|
||
|
func (c *MockConn) SetDeadline(t time.Time) error {
|
||
|
return ErrNotImplemented
|
||
|
}
|
||
|
|
||
|
func (c *MockConn) SetReadDeadline(t time.Time) error {
|
||
|
return ErrNotImplemented
|
||
|
}
|
||
|
|
||
|
func (c *MockConn) SetWriteDeadline(t time.Time) error {
|
||
|
return ErrNotImplemented
|
||
|
}
|
||
|
|
||
|
type mockAddr struct{}
|
||
|
|
||
|
func (mockAddr) Network() string { return "mock addr" }
|
||
|
func (mockAddr) String() string { return "mock addr" }
|