Skip to content

Commit f594bdc

Browse files
authored
test: blockfetch client tests (#568)
Fixes #319
1 parent ea934b5 commit f594bdc

File tree

1 file changed

+193
-0
lines changed

1 file changed

+193
-0
lines changed

protocol/blockfetch/client_test.go

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
// Copyright 2024 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 blockfetch_test
16+
17+
import (
18+
"fmt"
19+
"testing"
20+
"time"
21+
22+
ouroboros "github.com/blinklabs-io/gouroboros"
23+
"github.com/blinklabs-io/gouroboros/cbor"
24+
"github.com/blinklabs-io/gouroboros/internal/test"
25+
"github.com/blinklabs-io/gouroboros/ledger"
26+
"github.com/blinklabs-io/gouroboros/protocol"
27+
"github.com/blinklabs-io/gouroboros/protocol/blockfetch"
28+
ocommon "github.com/blinklabs-io/gouroboros/protocol/common"
29+
ouroboros_mock "github.com/blinklabs-io/ouroboros-mock"
30+
"go.uber.org/goleak"
31+
)
32+
33+
var conversationHandshakeRequestRange = []ouroboros_mock.ConversationEntry{
34+
ouroboros_mock.ConversationEntryHandshakeRequestGeneric,
35+
ouroboros_mock.ConversationEntryHandshakeNtNResponse,
36+
ouroboros_mock.ConversationEntryInput{
37+
ProtocolId: blockfetch.ProtocolId,
38+
MessageType: blockfetch.MessageTypeRequestRange,
39+
},
40+
}
41+
42+
type testInnerFunc func(*testing.T, *ouroboros.Connection)
43+
44+
func runTest(t *testing.T, conversation []ouroboros_mock.ConversationEntry, innerFunc testInnerFunc) {
45+
defer goleak.VerifyNone(t)
46+
mockConn := ouroboros_mock.NewConnection(
47+
ouroboros_mock.ProtocolRoleClient,
48+
conversation,
49+
)
50+
// Async mock connection error handler
51+
asyncErrChan := make(chan error, 1)
52+
go func() {
53+
err := <-mockConn.(*ouroboros_mock.Connection).ErrorChan()
54+
if err != nil {
55+
asyncErrChan <- fmt.Errorf("received unexpected error: %s", err)
56+
}
57+
close(asyncErrChan)
58+
}()
59+
oConn, err := ouroboros.New(
60+
ouroboros.WithConnection(mockConn),
61+
ouroboros.WithNetworkMagic(ouroboros_mock.MockNetworkMagic),
62+
ouroboros.WithNodeToNode(true),
63+
)
64+
if err != nil {
65+
t.Fatalf("unexpected error when creating Ouroboros object: %s", err)
66+
}
67+
// Async error handler
68+
go func() {
69+
err, ok := <-oConn.ErrorChan()
70+
if !ok {
71+
return
72+
}
73+
// We can't call t.Fatalf() from a different Goroutine, so we panic instead
74+
panic(fmt.Sprintf("unexpected Ouroboros error: %s", err))
75+
}()
76+
// Run test inner function
77+
innerFunc(t, oConn)
78+
// Wait for mock connection shutdown
79+
select {
80+
case err, ok := <-asyncErrChan:
81+
if ok {
82+
t.Fatal(err.Error())
83+
}
84+
case <-time.After(2 * time.Second):
85+
t.Fatalf("did not complete within timeout")
86+
}
87+
// Close Ouroboros connection
88+
if err := oConn.Close(); err != nil {
89+
t.Fatalf("unexpected error when closing Ouroboros object: %s", err)
90+
}
91+
// Wait for connection shutdown
92+
select {
93+
case <-oConn.ErrorChan():
94+
case <-time.After(10 * time.Second):
95+
t.Errorf("did not shutdown within timeout")
96+
}
97+
}
98+
99+
func TestGetBlock(t *testing.T) {
100+
var testBlockSlot uint64 = 23456
101+
var testBlockNumber uint64 = 12345
102+
// Create basic block and round-trip it through the CBOR encoder to get the hash populated
103+
// The slot value is one higher than our intersect point and the block height is less than
104+
// our expected tip
105+
testBlock := ledger.BabbageBlock{
106+
Header: &ledger.BabbageBlockHeader{},
107+
}
108+
testBlock.Header.Body.BlockNumber = testBlockNumber
109+
testBlock.Header.Body.Slot = testBlockSlot
110+
blockCbor, err := cbor.Encode(testBlock)
111+
if err != nil {
112+
t.Fatalf("received unexpected error: %s", err)
113+
}
114+
if _, err := cbor.Decode(blockCbor, &testBlock); err != nil {
115+
t.Fatalf("received unexpected error: %s", err)
116+
}
117+
testBlockHash := test.DecodeHexString(testBlock.Hash())
118+
wrappedBlock := blockfetch.WrappedBlock{
119+
Type: ledger.BlockTypeBabbage,
120+
RawBlock: cbor.RawMessage(blockCbor),
121+
}
122+
wrappedBlockCbor, err := cbor.Encode(wrappedBlock)
123+
if err != nil {
124+
t.Fatalf("received unexpected error: %s", err)
125+
}
126+
conversation := append(
127+
conversationHandshakeRequestRange,
128+
ouroboros_mock.ConversationEntryOutput{
129+
ProtocolId: blockfetch.ProtocolId,
130+
IsResponse: true,
131+
Messages: []protocol.Message{
132+
blockfetch.NewMsgStartBatch(),
133+
blockfetch.NewMsgBlock(
134+
wrappedBlockCbor,
135+
),
136+
blockfetch.NewMsgBatchDone(),
137+
},
138+
},
139+
)
140+
runTest(
141+
t,
142+
conversation,
143+
func(t *testing.T, oConn *ouroboros.Connection) {
144+
blk, err := oConn.BlockFetch().Client.GetBlock(
145+
ocommon.NewPoint(
146+
testBlockSlot,
147+
testBlockHash,
148+
),
149+
)
150+
if err != nil {
151+
t.Fatalf("received unexpected error: %s", err)
152+
}
153+
if blk.Hash() != testBlock.Hash() {
154+
t.Fatalf("did not receive expected block hash: got %s, wanted %s", blk.Hash(), testBlock.Hash())
155+
}
156+
if blk.SlotNumber() != testBlockSlot {
157+
t.Fatalf("did not receive expected block slot: got %d, wanted %d", blk.SlotNumber(), testBlockSlot)
158+
}
159+
},
160+
)
161+
}
162+
163+
func TestGetBlockNoBlocks(t *testing.T) {
164+
conversation := append(
165+
conversationHandshakeRequestRange,
166+
ouroboros_mock.ConversationEntryOutput{
167+
ProtocolId: blockfetch.ProtocolId,
168+
IsResponse: true,
169+
Messages: []protocol.Message{
170+
blockfetch.NewMsgNoBlocks(),
171+
},
172+
},
173+
)
174+
expectedErr := `block(s) not found`
175+
runTest(
176+
t,
177+
conversation,
178+
func(t *testing.T, oConn *ouroboros.Connection) {
179+
_, err := oConn.BlockFetch().Client.GetBlock(
180+
ocommon.NewPoint(
181+
12345,
182+
test.DecodeHexString("abcdef0123456789"),
183+
),
184+
)
185+
if err == nil {
186+
t.Fatalf("did not receive expected error")
187+
}
188+
if err.Error() != expectedErr {
189+
t.Fatalf("did not receive expected error\n got: %s\n wanted: %s", err, expectedErr)
190+
}
191+
},
192+
)
193+
}

0 commit comments

Comments
 (0)