Skip to content

Commit 983f6e8

Browse files
authored
test: added unit tests for chainsync filter plugin (#385)
Signed-off-by: Jenita <[email protected]>
1 parent 7ee66a2 commit 983f6e8

File tree

2 files changed

+356
-1
lines changed

2 files changed

+356
-1
lines changed

filter/chainsync/chainsync_test.go

Lines changed: 265 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,116 @@
1414

1515
package chainsync
1616

17-
import "testing"
17+
import (
18+
"encoding/hex"
19+
"testing"
20+
"time"
21+
22+
"github.com/blinklabs-io/adder/event"
23+
"github.com/blinklabs-io/adder/input/chainsync"
24+
"github.com/blinklabs-io/gouroboros/cbor"
25+
"github.com/blinklabs-io/gouroboros/ledger"
26+
"github.com/blinklabs-io/gouroboros/ledger/common"
27+
"github.com/stretchr/testify/assert"
28+
"github.com/utxorpc/go-codegen/utxorpc/v1alpha/cardano"
29+
)
1830

1931
// MockLogger is a mock implementation of the plugin.Logger interface
2032
type MockLogger struct{}
2133

34+
// MockAddress is a mock implementation of the ledger.Address interface
35+
type MockAddress struct {
36+
common.Address // Embed the common.Address struct
37+
}
38+
39+
func (m MockAddress) ByronAttr() common.ByronAddressAttributes {
40+
return common.ByronAddressAttributes{}
41+
}
42+
43+
func (m MockAddress) ByronType() uint64 {
44+
return 0
45+
}
46+
47+
func (m MockAddress) Bytes() []byte {
48+
return []byte("mockAddressBytes")
49+
}
50+
51+
func (m *MockAddress) MarshalCBOR() ([]byte, error) {
52+
return []byte{}, nil
53+
}
54+
55+
func (m MockAddress) MarshalJSON() ([]byte, error) {
56+
return []byte("{}"), nil
57+
}
58+
59+
func (m MockAddress) NetworkId() uint {
60+
return 1
61+
}
62+
63+
func (m MockAddress) PaymentAddress() *common.Address {
64+
return &common.Address{}
65+
}
66+
67+
func (m *MockAddress) PaymentKeyHash() common.Blake2b224 {
68+
return common.Blake2b224Hash([]byte("paymentKeyHash"))
69+
}
70+
71+
func (m MockAddress) StakeAddress() *common.Address {
72+
return &common.Address{}
73+
}
74+
75+
func (m *MockAddress) StakeKeyHash() common.Blake2b224 {
76+
return common.Blake2b224Hash([]byte("stakeKeyHash"))
77+
}
78+
79+
func (m MockAddress) String() string {
80+
return hex.EncodeToString(m.Bytes())
81+
}
82+
83+
func (m MockAddress) Type() uint8 {
84+
return 0
85+
}
86+
87+
func (m *MockAddress) UnmarshalCBOR(data []byte) error {
88+
return nil
89+
}
90+
91+
// MockOutput is a mock implementation of the TransactionOutput interface
92+
type MockOutput struct {
93+
address ledger.Address
94+
amount uint64
95+
assets *common.MultiAsset[common.MultiAssetTypeOutput]
96+
datum *cbor.LazyValue
97+
}
98+
99+
func (m MockOutput) Address() ledger.Address {
100+
return m.address
101+
}
102+
103+
func (m MockOutput) Amount() uint64 {
104+
return m.amount
105+
}
106+
107+
func (m MockOutput) Assets() *common.MultiAsset[common.MultiAssetTypeOutput] {
108+
return m.assets
109+
}
110+
111+
func (m MockOutput) Datum() *cbor.LazyValue {
112+
return m.datum
113+
}
114+
115+
func (m MockOutput) DatumHash() *common.Blake2b256 {
116+
return nil
117+
}
118+
119+
func (m MockOutput) Cbor() []byte {
120+
return []byte{}
121+
}
122+
123+
func (m MockOutput) Utxorpc() *cardano.TxOutput {
124+
return nil
125+
}
126+
22127
func (l *MockLogger) Info(msg string, args ...interface{}) {}
23128
func (l *MockLogger) Error(msg string, args ...interface{}) {}
24129
func (l *MockLogger) Debug(msg string, args ...interface{}) {}
@@ -85,3 +190,162 @@ func TestChainSync_OutputChan(t *testing.T) {
85190
t.Fatalf("expected non-nil outputChan")
86191
}
87192
}
193+
194+
func TestFilterByAddress(t *testing.T) {
195+
// Setup ChainSync with address filter
196+
cs := New(WithAddresses([]string{"addr_test1qqjwq357"}))
197+
198+
// Create a mock address using the methods
199+
mockAddr := common.Address{}
200+
201+
// Mock transaction event
202+
output := MockOutput{
203+
address: mockAddr,
204+
amount: 1000000,
205+
assets: nil,
206+
datum: nil,
207+
}
208+
evt := event.Event{
209+
Payload: chainsync.TransactionEvent{
210+
Outputs: []ledger.TransactionOutput{output},
211+
ResolvedInputs: []ledger.TransactionOutput{output},
212+
},
213+
}
214+
215+
// Start the filter
216+
err := cs.Start()
217+
assert.NoError(t, err, "ChainSync filter should start without error")
218+
defer cs.Stop()
219+
220+
// Send event to input channel
221+
cs.InputChan() <- evt
222+
223+
// Wait for the event to be processed
224+
select {
225+
case filteredEvt := <-cs.OutputChan():
226+
assert.Equal(t, evt, filteredEvt, "Filtered event should match the input event")
227+
case <-time.After(10 * time.Second):
228+
t.Fatal("Test timed out waiting for filtered event")
229+
}
230+
}
231+
232+
func TestFilterByPolicyId(t *testing.T) {
233+
// Setup ChainSync with policy ID filter
234+
filterPolicyId := "random_policy_id"
235+
policyIdHash := common.Blake2b224Hash([]byte(filterPolicyId))
236+
cs := New(WithPolicies([]string{policyIdHash.String()}))
237+
238+
// Mock transaction event
239+
policyId := policyIdHash // Use the same hash as the filter
240+
241+
// Create a new MultiAsset with pre-populated data
242+
assetsData := make(map[common.Blake2b224]map[cbor.ByteString]common.MultiAssetTypeOutput)
243+
assetName := cbor.NewByteString([]byte("asset1"))
244+
assetsData[policyId] = map[cbor.ByteString]common.MultiAssetTypeOutput{
245+
assetName: 1, // Add asset with quantity 1
246+
}
247+
assets := common.NewMultiAsset(assetsData)
248+
249+
output := MockOutput{
250+
address: ledger.Address{},
251+
amount: 1000000,
252+
assets: &assets,
253+
datum: nil,
254+
}
255+
evt := event.Event{
256+
Payload: chainsync.TransactionEvent{
257+
Outputs: []ledger.TransactionOutput{output},
258+
ResolvedInputs: []ledger.TransactionOutput{output},
259+
},
260+
}
261+
262+
// Start the filter
263+
err := cs.Start()
264+
assert.NoError(t, err, "ChainSync filter should start without error")
265+
defer cs.Stop()
266+
267+
// Send event to input channel
268+
cs.InputChan() <- evt
269+
270+
// Wait for the event to be processed
271+
select {
272+
case filteredEvt := <-cs.OutputChan():
273+
assert.Equal(t, evt, filteredEvt, "Filtered event should match the input event")
274+
case <-time.After(5 * time.Second):
275+
t.Fatal("Test timed out waiting for filtered event")
276+
}
277+
}
278+
279+
func TestFilterByAssetFingerprint(t *testing.T) {
280+
// Setup ChainSync with asset fingerprint filter
281+
filterAssetFingerprint := "asset1e58wmplshqdkkq97tz02chq980456wgt35tfjr"
282+
cs := New(WithAssetFingerprints([]string{filterAssetFingerprint}))
283+
284+
// Mock transaction event
285+
policyId := common.Blake2b224Hash([]byte("policy1"))
286+
287+
// Create a new MultiAsset with pre-populated data
288+
assetsData := make(map[common.Blake2b224]map[cbor.ByteString]common.MultiAssetTypeOutput)
289+
assetName := cbor.NewByteString([]byte("asset1"))
290+
assetsData[policyId] = map[cbor.ByteString]common.MultiAssetTypeOutput{
291+
assetName: 1, // Add asset with quantity 1
292+
}
293+
assets := common.NewMultiAsset(assetsData)
294+
295+
output := MockOutput{
296+
address: ledger.Address{},
297+
amount: 1000000,
298+
assets: &assets,
299+
datum: nil,
300+
}
301+
evt := event.Event{
302+
Payload: chainsync.TransactionEvent{
303+
Outputs: []ledger.TransactionOutput{output},
304+
ResolvedInputs: []ledger.TransactionOutput{output},
305+
},
306+
}
307+
308+
// Start the filter
309+
err := cs.Start()
310+
assert.NoError(t, err, "ChainSync filter should start without error")
311+
defer cs.Stop()
312+
313+
// Send event to input channel
314+
cs.InputChan() <- evt
315+
316+
// Wait for the event to be processed
317+
select {
318+
case filteredEvt := <-cs.OutputChan():
319+
assert.Equal(t, evt, filteredEvt, "Filtered event should match the input event")
320+
case <-time.After(5 * time.Second):
321+
t.Fatal("Test timed out waiting for filtered event")
322+
}
323+
}
324+
325+
func TestFilterByPoolId(t *testing.T) {
326+
// Setup ChainSync with pool ID filter
327+
cs := New(WithPoolIds([]string{"pool1"}))
328+
329+
// Mock block event
330+
evt := event.Event{
331+
Payload: chainsync.BlockEvent{
332+
IssuerVkey: "pool1", // Match the filterPoolIds
333+
},
334+
}
335+
336+
// Start the filter
337+
err := cs.Start()
338+
assert.NoError(t, err, "ChainSync filter should start without error")
339+
defer cs.Stop()
340+
341+
// Send event to input channel
342+
cs.InputChan() <- evt
343+
344+
// Wait for the event to be processed
345+
select {
346+
case filteredEvt := <-cs.OutputChan():
347+
assert.Equal(t, evt, filteredEvt, "Filtered event should match the input event")
348+
case <-time.After(5 * time.Second):
349+
t.Fatal("Test timed out waiting for filtered event")
350+
}
351+
}

filter/chainsync/plugin_test.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package chainsync
2+
3+
import (
4+
"testing"
5+
"time"
6+
7+
"github.com/blinklabs-io/adder/event"
8+
"github.com/blinklabs-io/adder/input/chainsync"
9+
"github.com/blinklabs-io/adder/plugin"
10+
"github.com/stretchr/testify/assert"
11+
)
12+
13+
func TestPluginRegistration(t *testing.T) {
14+
// Retrieve the plugin entries
15+
plugins := plugin.GetPlugins(plugin.PluginTypeFilter) // Get all registered plugins
16+
17+
// Find the "chainsync" plugin
18+
var p plugin.Plugin
19+
for _, entry := range plugins {
20+
if entry.Name == "chainsync" {
21+
// Create a new instance of the plugin
22+
p = entry.NewFromOptionsFunc()
23+
break
24+
}
25+
}
26+
27+
// Verify that the plugin was found
28+
assert.NotNil(t, p, "Plugin should be registered")
29+
30+
// Verify that the plugin implements the Plugin interface
31+
_, ok := p.(plugin.Plugin)
32+
assert.True(t, ok, "Plugin should implement the Plugin interface")
33+
}
34+
35+
func TestPluginStartStop(t *testing.T) {
36+
// Create a new plugin instance
37+
p := NewFromCmdlineOptions()
38+
39+
// Start the plugin
40+
err := p.Start()
41+
assert.NoError(t, err, "Plugin should start without errors")
42+
43+
// Stop the plugin
44+
err = p.Stop()
45+
assert.NoError(t, err, "Plugin should stop without errors")
46+
}
47+
48+
func TestPluginChannels(t *testing.T) {
49+
// Create a new plugin instance
50+
p := NewFromCmdlineOptions()
51+
52+
// Verify that the error channel is not nil
53+
assert.NotNil(t, p.ErrorChan(), "Error channel should not be nil")
54+
55+
// Verify that the input channel is not nil
56+
assert.NotNil(t, p.InputChan(), "Input channel should not be nil")
57+
58+
// Verify that the output channel is not nil
59+
assert.NotNil(t, p.OutputChan(), "Output channel should not be nil")
60+
}
61+
62+
func TestPluginEventProcessing(t *testing.T) {
63+
// Create a new plugin instance
64+
p := NewFromCmdlineOptions()
65+
66+
// Start the plugin
67+
err := p.Start()
68+
assert.NoError(t, err, "Plugin should start without errors")
69+
70+
// Create a test event with a TransactionEvent payload
71+
testEvent := event.Event{
72+
Type: "transaction",
73+
Timestamp: time.Now(),
74+
Payload: chainsync.TransactionEvent{},
75+
}
76+
77+
// Send the event to the input channel
78+
p.InputChan() <- testEvent
79+
80+
// Read the event from the output channel
81+
select {
82+
case outputEvent := <-p.OutputChan():
83+
assert.Equal(t, testEvent, outputEvent, "Output event should match input event")
84+
case <-time.After(1 * time.Second):
85+
t.Fatal("Timeout waiting for output event")
86+
}
87+
88+
// Stop the plugin
89+
err = p.Stop()
90+
assert.NoError(t, err, "Plugin should stop without errors")
91+
}

0 commit comments

Comments
 (0)