Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: additional handshake client tests #560

Merged
merged 1 commit into from
Mar 23, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
244 changes: 238 additions & 6 deletions protocol/handshake/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,85 @@ package handshake_test

import (
"fmt"
"reflect"
"testing"
"time"

ouroboros "github.com/blinklabs-io/gouroboros"
"github.com/blinklabs-io/gouroboros/internal/test/ouroboros_mock"
"github.com/blinklabs-io/gouroboros/protocol"
"github.com/blinklabs-io/gouroboros/protocol/handshake"
"go.uber.org/goleak"
)

func TestClientBasicHandshake(t *testing.T) {
const (
mockProtocolVersionNtC uint16 = (14 + protocol.ProtocolVersionNtCOffset)
mockProtocolVersionNtN uint16 = 13
mockProtocolVersionNtNV11 uint16 = 11
)

var conversationEntryNtCResponse = ouroboros_mock.ConversationEntry{
Type: ouroboros_mock.EntryTypeOutput,
ProtocolId: handshake.ProtocolId,
IsResponse: true,
OutputMessages: []protocol.Message{
handshake.NewMsgAcceptVersion(
mockProtocolVersionNtC,
mockNtCVersionData(),
),
},
}

var conversationEntryNtNResponse = ouroboros_mock.ConversationEntry{
Type: ouroboros_mock.EntryTypeOutput,
ProtocolId: handshake.ProtocolId,
IsResponse: true,
OutputMessages: []protocol.Message{
handshake.NewMsgAcceptVersion(
mockProtocolVersionNtN,
mockNtNVersionData(),
),
},
}

var conversationEntryNtNResponseV11 = ouroboros_mock.ConversationEntry{
Type: ouroboros_mock.EntryTypeOutput,
ProtocolId: handshake.ProtocolId,
IsResponse: true,
OutputMessages: []protocol.Message{
handshake.NewMsgAcceptVersion(
mockProtocolVersionNtNV11,
mockNtNVersionDataV11(),
),
},
}

func mockNtCVersionData() protocol.VersionData {
return protocol.VersionDataNtC9to14(ouroboros_mock.MockNetworkMagic)
}

func mockNtNVersionDataV11() protocol.VersionData {
return protocol.VersionDataNtN11to12{
CborNetworkMagic: ouroboros_mock.MockNetworkMagic,
CborInitiatorAndResponderDiffusionMode: protocol.DiffusionModeInitiatorOnly,
CborPeerSharing: protocol.PeerSharingModeNoPeerSharing,
CborQuery: protocol.QueryModeDisabled,
}
}

func mockNtNVersionData() protocol.VersionData {
return protocol.VersionDataNtN13andUp{
VersionDataNtN11to12: mockNtNVersionDataV11().(protocol.VersionDataNtN11to12),
}
}

func TestClientNtCAccept(t *testing.T) {
defer goleak.VerifyNone(t)
mockConn := ouroboros_mock.NewConnection(
ouroboros_mock.ProtocolRoleClient,
[]ouroboros_mock.ConversationEntry{
ouroboros_mock.ConversationEntryHandshakeRequestGeneric,
ouroboros_mock.ConversationEntryHandshakeNtCResponse,
conversationEntryNtCResponse,
},
)
oConn, err := ouroboros.New(
Expand All @@ -49,6 +113,14 @@ func TestClientBasicHandshake(t *testing.T) {
// We can't call t.Fatalf() from a different Goroutine, so we panic instead
panic(fmt.Sprintf("unexpected Ouroboros connection error: %s", err))
}()
// Check negotiated version and version data
protoVersion, protoVersionData := oConn.ProtocolVersion()
if protoVersion != mockProtocolVersionNtC {
t.Fatalf("did not get expected protocol version: got %d, wanted %d", protoVersion, mockProtocolVersionNtC)
}
if !reflect.DeepEqual(protoVersionData, mockNtCVersionData()) {
t.Fatalf("did not get expected protocol version data:\n got: %#v\n wanted: %#v", protoVersionData, mockNtCVersionData())
}
// Close Ouroboros connection
if err := oConn.Close(); err != nil {
t.Fatalf("unexpected error when closing Ouroboros object: %s", err)
Expand All @@ -61,18 +133,19 @@ func TestClientBasicHandshake(t *testing.T) {
}
}

func TestClientDoubleStart(t *testing.T) {
func TestClientNtNAccept(t *testing.T) {
defer goleak.VerifyNone(t)
mockConn := ouroboros_mock.NewConnection(
ouroboros_mock.ProtocolRoleClient,
[]ouroboros_mock.ConversationEntry{
ouroboros_mock.ConversationEntryHandshakeRequestGeneric,
ouroboros_mock.ConversationEntryHandshakeNtCResponse,
conversationEntryNtNResponse,
},
)
oConn, err := ouroboros.New(
ouroboros.WithConnection(mockConn),
ouroboros.WithNetworkMagic(ouroboros_mock.MockNetworkMagic),
ouroboros.WithNodeToNode(true),
)
if err != nil {
t.Fatalf("unexpected error when creating Ouroboros object: %s", err)
Expand All @@ -86,8 +159,14 @@ func TestClientDoubleStart(t *testing.T) {
// We can't call t.Fatalf() from a different Goroutine, so we panic instead
panic(fmt.Sprintf("unexpected Ouroboros connection error: %s", err))
}()
// Try to start the Handshake client again
oConn.Handshake().Client.Start()
// Check negotiated version and version data
protoVersion, protoVersionData := oConn.ProtocolVersion()
if protoVersion != mockProtocolVersionNtN {
t.Fatalf("did not get expected protocol version: got %d, wanted %d", protoVersion, mockProtocolVersionNtN)
}
if !reflect.DeepEqual(protoVersionData, mockNtNVersionData()) {
t.Fatalf("did not get expected protocol version data:\n got: %#v\n wanted: %#v", protoVersionData, mockNtNVersionData())
}
// Close Ouroboros connection
if err := oConn.Close(); err != nil {
t.Fatalf("unexpected error when closing Ouroboros object: %s", err)
Expand All @@ -99,3 +178,156 @@ func TestClientDoubleStart(t *testing.T) {
t.Errorf("did not shutdown within timeout")
}
}

func TestClientNtNAcceptV11(t *testing.T) {
defer goleak.VerifyNone(t)
mockConn := ouroboros_mock.NewConnection(
ouroboros_mock.ProtocolRoleClient,
[]ouroboros_mock.ConversationEntry{
ouroboros_mock.ConversationEntryHandshakeRequestGeneric,
conversationEntryNtNResponseV11,
},
)
oConn, err := ouroboros.New(
ouroboros.WithConnection(mockConn),
ouroboros.WithNetworkMagic(ouroboros_mock.MockNetworkMagic),
ouroboros.WithNodeToNode(true),
)
if err != nil {
t.Fatalf("unexpected error when creating Ouroboros object: %s", err)
}
// Async error handler
go func() {
err, ok := <-oConn.ErrorChan()
if !ok {
return
}
// We can't call t.Fatalf() from a different Goroutine, so we panic instead
panic(fmt.Sprintf("unexpected Ouroboros connection error: %s", err))
}()
// Check negotiated version and version data
protoVersion, protoVersionData := oConn.ProtocolVersion()
if protoVersion != mockProtocolVersionNtNV11 {
t.Fatalf("did not get expected protocol version: got %d, wanted %d", protoVersion, mockProtocolVersionNtNV11)
}
if !reflect.DeepEqual(protoVersionData, mockNtNVersionDataV11()) {
t.Fatalf("did not get expected protocol version data:\n got: %#v\n wanted: %#v", protoVersionData, mockNtNVersionDataV11())
}
// Close Ouroboros connection
if err := oConn.Close(); err != nil {
t.Fatalf("unexpected error when closing Ouroboros object: %s", err)
}
// Wait for connection shutdown
select {
case <-oConn.ErrorChan():
case <-time.After(10 * time.Second):
t.Errorf("did not shutdown within timeout")
}
}

func TestClientNtCRefuseVersionMismatch(t *testing.T) {
defer goleak.VerifyNone(t)
expectedErr := fmt.Sprintf("%s: version mismatch", handshake.ProtocolName)
mockConn := ouroboros_mock.NewConnection(
ouroboros_mock.ProtocolRoleClient,
[]ouroboros_mock.ConversationEntry{
ouroboros_mock.ConversationEntryHandshakeRequestGeneric,
ouroboros_mock.ConversationEntry{
Type: ouroboros_mock.EntryTypeOutput,
ProtocolId: handshake.ProtocolId,
IsResponse: true,
OutputMessages: []protocol.Message{
handshake.NewMsgRefuse(
[]any{
handshake.RefuseReasonVersionMismatch,
[]uint16{1, 2, 3},
},
),
},
},
},
)
_, err := ouroboros.New(
ouroboros.WithConnection(mockConn),
ouroboros.WithNetworkMagic(ouroboros_mock.MockNetworkMagic),
)
if err == nil {
t.Fatalf("did not receive expected error")
} else {
if err.Error() != expectedErr {
t.Fatalf("received unexpected error\n got: %v\n wanted: %v", err, expectedErr)
}
}
}

func TestClientNtCRefuseDecodeError(t *testing.T) {
defer goleak.VerifyNone(t)
expectedErr := fmt.Sprintf("%s: decode error: foo", handshake.ProtocolName)
mockConn := ouroboros_mock.NewConnection(
ouroboros_mock.ProtocolRoleClient,
[]ouroboros_mock.ConversationEntry{
ouroboros_mock.ConversationEntryHandshakeRequestGeneric,
ouroboros_mock.ConversationEntry{
Type: ouroboros_mock.EntryTypeOutput,
ProtocolId: handshake.ProtocolId,
IsResponse: true,
OutputMessages: []protocol.Message{
handshake.NewMsgRefuse(
[]any{
handshake.RefuseReasonDecodeError,
mockProtocolVersionNtC,
"foo",
},
),
},
},
},
)
_, err := ouroboros.New(
ouroboros.WithConnection(mockConn),
ouroboros.WithNetworkMagic(ouroboros_mock.MockNetworkMagic),
)
if err == nil {
t.Fatalf("did not receive expected error")
} else {
if err.Error() != expectedErr {
t.Fatalf("received unexpected error\n got: %v\n wanted: %v", err, expectedErr)
}
}
}

func TestClientNtCRefuseRefused(t *testing.T) {
defer goleak.VerifyNone(t)
expectedErr := fmt.Sprintf("%s: refused: foo", handshake.ProtocolName)
mockConn := ouroboros_mock.NewConnection(
ouroboros_mock.ProtocolRoleClient,
[]ouroboros_mock.ConversationEntry{
ouroboros_mock.ConversationEntryHandshakeRequestGeneric,
ouroboros_mock.ConversationEntry{
Type: ouroboros_mock.EntryTypeOutput,
ProtocolId: handshake.ProtocolId,
IsResponse: true,
OutputMessages: []protocol.Message{
handshake.NewMsgRefuse(
[]any{
handshake.RefuseReasonRefused,
mockProtocolVersionNtC,
"foo",
},
),
},
},
},
)
_, err := ouroboros.New(
ouroboros.WithConnection(mockConn),
ouroboros.WithNetworkMagic(ouroboros_mock.MockNetworkMagic),
)
if err == nil {
t.Fatalf("did not receive expected error")
} else {
if err.Error() != expectedErr {
t.Fatalf("received unexpected error\n got: %v\n wanted: %v", err, expectedErr)
}
}
}