Skip to content

Commit e04a54d

Browse files
authored
feat: add keepalive test (#521)
Signed-off-by: Ales Verbic <[email protected]>
1 parent e0f46d7 commit e04a54d

File tree

2 files changed

+236
-1
lines changed

2 files changed

+236
-1
lines changed

connection_manager_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ import (
2020
"time"
2121

2222
ouroboros "github.com/blinklabs-io/gouroboros"
23-
"github.com/blinklabs-io/ouroboros-mock"
2423
"github.com/blinklabs-io/gouroboros/protocol/keepalive"
24+
ouroboros_mock "github.com/blinklabs-io/ouroboros-mock"
2525
"go.uber.org/goleak"
2626
)
2727

protocol/keepalive/client_test.go

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
// Copyright 2023 Blink Labs Software
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package keepalive_test
16+
17+
import (
18+
"fmt"
19+
"testing"
20+
"time"
21+
22+
ouroboros "github.com/blinklabs-io/gouroboros"
23+
"github.com/blinklabs-io/gouroboros/protocol"
24+
"github.com/blinklabs-io/gouroboros/protocol/keepalive"
25+
ouroboros_mock "github.com/blinklabs-io/ouroboros-mock"
26+
"go.uber.org/goleak"
27+
)
28+
29+
const (
30+
// MockKeepAliveWrongCookie is the wrong cookie for a keep-alive response
31+
MockKeepAliveWrongCookie uint16 = 0x3e8
32+
MockKeepAliveDifferentCookie uint16 = 0xADA
33+
)
34+
35+
// ConversationKeepAlive is a pre-defined conversation with a NtN handshake and repeated keep-alive requests
36+
// and responses
37+
var ConversationKeepAliveWrongResponse = []ouroboros_mock.ConversationEntry{
38+
ouroboros_mock.ConversationEntryHandshakeRequestGeneric,
39+
ouroboros_mock.ConversationEntryHandshakeNtNResponse,
40+
ouroboros_mock.ConversationEntryInput{
41+
ProtocolId: keepalive.ProtocolId,
42+
Message: keepalive.NewMsgKeepAlive(MockKeepAliveWrongCookie),
43+
MsgFromCborFunc: keepalive.NewMsgFromCbor,
44+
},
45+
ouroboros_mock.ConversationEntryOutput{
46+
ProtocolId: keepalive.ProtocolId,
47+
IsResponse: true,
48+
Messages: []protocol.Message{
49+
keepalive.NewMsgKeepAliveResponse(MockKeepAliveWrongCookie), // Incorrect Cookie value
50+
},
51+
},
52+
}
53+
54+
var ConversationKeepAliveDifferentCookie = []ouroboros_mock.ConversationEntry{
55+
ouroboros_mock.ConversationEntryHandshakeRequestGeneric,
56+
ouroboros_mock.ConversationEntryHandshakeNtNResponse,
57+
ouroboros_mock.ConversationEntryInput{
58+
ProtocolId: keepalive.ProtocolId,
59+
Message: keepalive.NewMsgKeepAlive(MockKeepAliveDifferentCookie),
60+
MsgFromCborFunc: keepalive.NewMsgFromCbor,
61+
},
62+
ouroboros_mock.ConversationEntryOutput{
63+
ProtocolId: keepalive.ProtocolId,
64+
IsResponse: true,
65+
Messages: []protocol.Message{
66+
keepalive.NewMsgKeepAliveResponse(MockKeepAliveDifferentCookie),
67+
},
68+
},
69+
}
70+
71+
func TestServerKeepaliveHandling(t *testing.T) {
72+
defer goleak.VerifyNone(t)
73+
mockConn := ouroboros_mock.NewConnection(
74+
ouroboros_mock.ProtocolRoleClient,
75+
ouroboros_mock.ConversationKeepAlive,
76+
).(*ouroboros_mock.Connection)
77+
78+
// Async mock connection error handler
79+
asyncErrChan := make(chan error, 1)
80+
go func() {
81+
err := <-mockConn.ErrorChan()
82+
if err != nil {
83+
asyncErrChan <- fmt.Errorf("received unexpected error\n got: %v\n wanted: no error", err)
84+
}
85+
close(asyncErrChan)
86+
}()
87+
88+
oConn, err := ouroboros.New(
89+
ouroboros.WithConnection(mockConn),
90+
ouroboros.WithNetworkMagic(ouroboros_mock.MockNetworkMagic),
91+
ouroboros.WithNodeToNode(true),
92+
ouroboros.WithKeepAlive(true),
93+
ouroboros.WithKeepAliveConfig(keepalive.NewConfig(
94+
keepalive.WithPeriod(time.Millisecond*100),
95+
// Set correct cookie
96+
keepalive.WithCookie(ouroboros_mock.MockKeepAliveCookie),
97+
)),
98+
)
99+
if err != nil {
100+
t.Fatalf("unexpected error when creating Connection object: %s", err)
101+
}
102+
103+
// Wait for mock connection shutdown
104+
select {
105+
case err, ok := <-asyncErrChan:
106+
if ok {
107+
t.Fatal(err.Error())
108+
}
109+
case <-time.After(2 * time.Second):
110+
t.Fatalf("did not complete within timeout")
111+
}
112+
113+
// Close connection
114+
if err := oConn.Close(); err != nil {
115+
t.Fatalf("unexpected error when closing Connection object: %s", err)
116+
}
117+
// Wait for connection shutdown
118+
select {
119+
case <-oConn.ErrorChan():
120+
case <-time.After(10 * time.Second):
121+
t.Errorf("did not shutdown within timeout")
122+
}
123+
}
124+
125+
func TestServerKeepaliveHandlingWithWrongResponse(t *testing.T) {
126+
defer goleak.VerifyNone(t)
127+
mockConn := ouroboros_mock.NewConnection(
128+
ouroboros_mock.ProtocolRoleClient,
129+
ConversationKeepAliveWrongResponse,
130+
).(*ouroboros_mock.Connection)
131+
132+
// Expected cookie is 0x3e8 instead of 0x3e7 based on the mock connection
133+
expectedErr := "input error: parsed message does not match expected value: " +
134+
"got &keepalive.MsgKeepAlive{MessageBase:protocol.MessageBase{_:struct {}{}, rawCbor:[]uint8(nil), MessageType:0x0}, Cookie:0x3e7}, " +
135+
"expected &keepalive.MsgKeepAlive{MessageBase:protocol.MessageBase{_:struct {}{}, rawCbor:[]uint8(nil), MessageType:0x0}, Cookie:0x3e8}"
136+
// Async mock connection error handler
137+
asyncErrChan := make(chan error, 1)
138+
go func() {
139+
err := <-mockConn.ErrorChan()
140+
if err == nil {
141+
asyncErrChan <- fmt.Errorf("did not receive expected error")
142+
} else {
143+
if err.Error() != expectedErr {
144+
asyncErrChan <- fmt.Errorf("did not receive expected error\n got: %s\n wanted: %s", err, expectedErr)
145+
}
146+
}
147+
close(asyncErrChan)
148+
}()
149+
150+
oConn, err := ouroboros.New(
151+
ouroboros.WithConnection(mockConn),
152+
ouroboros.WithNetworkMagic(ouroboros_mock.MockNetworkMagic),
153+
ouroboros.WithNodeToNode(true),
154+
ouroboros.WithKeepAlive(true),
155+
ouroboros.WithKeepAliveConfig(keepalive.NewConfig(
156+
keepalive.WithPeriod(time.Millisecond*10),
157+
keepalive.WithCookie(ouroboros_mock.MockKeepAliveCookie),
158+
)),
159+
)
160+
if err != nil {
161+
t.Fatalf("unexpected error when creating Connection object: %s", err)
162+
}
163+
164+
// Wait for mock connection shutdown
165+
select {
166+
case err, ok := <-asyncErrChan:
167+
if ok {
168+
t.Fatal(err.Error())
169+
}
170+
case <-time.After(2 * time.Second):
171+
t.Fatalf("did not complete within timeout")
172+
}
173+
// Close connection
174+
if err := oConn.Close(); err != nil {
175+
t.Fatalf("unexpected error when closing Connection object: %s", err)
176+
}
177+
// Wait for connection shutdown
178+
select {
179+
case <-oConn.ErrorChan():
180+
case <-time.After(10 * time.Second):
181+
t.Errorf("did not shutdown within timeout")
182+
}
183+
}
184+
185+
func TestServerKeepaliveHandlingWithDifferentCookie(t *testing.T) {
186+
defer goleak.VerifyNone(t)
187+
mockConn := ouroboros_mock.NewConnection(
188+
ouroboros_mock.ProtocolRoleClient,
189+
ConversationKeepAliveDifferentCookie,
190+
).(*ouroboros_mock.Connection)
191+
192+
// Async mock connection error handler
193+
asyncErrChan := make(chan error, 1)
194+
go func() {
195+
err := <-mockConn.ErrorChan()
196+
if err != nil {
197+
asyncErrChan <- fmt.Errorf("received unexpected error\n got: %v\n wanted: no error", err)
198+
}
199+
close(asyncErrChan)
200+
}()
201+
202+
oConn, err := ouroboros.New(
203+
ouroboros.WithConnection(mockConn),
204+
ouroboros.WithNetworkMagic(ouroboros_mock.MockNetworkMagic),
205+
ouroboros.WithNodeToNode(true),
206+
ouroboros.WithKeepAlive(true),
207+
ouroboros.WithKeepAliveConfig(keepalive.NewConfig(
208+
keepalive.WithPeriod(time.Millisecond*10),
209+
keepalive.WithCookie(MockKeepAliveDifferentCookie),
210+
)),
211+
)
212+
if err != nil {
213+
t.Fatalf("unexpected error when creating Connection object: %s", err)
214+
}
215+
216+
// Wait for mock connection shutdown
217+
select {
218+
case err, ok := <-asyncErrChan:
219+
if ok {
220+
t.Fatal(err.Error())
221+
}
222+
case <-time.After(2 * time.Second):
223+
t.Fatalf("did not complete within timeout")
224+
}
225+
// Close connection
226+
if err := oConn.Close(); err != nil {
227+
t.Fatalf("unexpected error when closing Connection object: %s", err)
228+
}
229+
// Wait for connection shutdown
230+
select {
231+
case <-oConn.ErrorChan():
232+
case <-time.After(10 * time.Second):
233+
t.Errorf("did not shutdown within timeout")
234+
}
235+
}

0 commit comments

Comments
 (0)