Skip to content
Merged
Show file tree
Hide file tree
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
266 changes: 265 additions & 1 deletion filter/chainsync/chainsync_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,116 @@

package chainsync

import "testing"
import (
"encoding/hex"
"testing"
"time"

"github.com/blinklabs-io/adder/event"
"github.com/blinklabs-io/adder/input/chainsync"
"github.com/blinklabs-io/gouroboros/cbor"
"github.com/blinklabs-io/gouroboros/ledger"
"github.com/blinklabs-io/gouroboros/ledger/common"
"github.com/stretchr/testify/assert"
"github.com/utxorpc/go-codegen/utxorpc/v1alpha/cardano"
)

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

// MockAddress is a mock implementation of the ledger.Address interface
type MockAddress struct {
common.Address // Embed the common.Address struct
}

func (m MockAddress) ByronAttr() common.ByronAddressAttributes {
return common.ByronAddressAttributes{}
}

func (m MockAddress) ByronType() uint64 {
return 0
}

func (m MockAddress) Bytes() []byte {
return []byte("mockAddressBytes")
}

func (m *MockAddress) MarshalCBOR() ([]byte, error) {
return []byte{}, nil
}

func (m MockAddress) MarshalJSON() ([]byte, error) {
return []byte("{}"), nil
}

func (m MockAddress) NetworkId() uint {
return 1
}

func (m MockAddress) PaymentAddress() *common.Address {
return &common.Address{}
}

func (m *MockAddress) PaymentKeyHash() common.Blake2b224 {
return common.Blake2b224Hash([]byte("paymentKeyHash"))
}

func (m MockAddress) StakeAddress() *common.Address {
return &common.Address{}
}

func (m *MockAddress) StakeKeyHash() common.Blake2b224 {
return common.Blake2b224Hash([]byte("stakeKeyHash"))
}

func (m MockAddress) String() string {
return hex.EncodeToString(m.Bytes())
}

func (m MockAddress) Type() uint8 {
return 0
}

func (m *MockAddress) UnmarshalCBOR(data []byte) error {
return nil
}

// MockOutput is a mock implementation of the TransactionOutput interface
type MockOutput struct {
address ledger.Address
amount uint64
assets *common.MultiAsset[common.MultiAssetTypeOutput]
datum *cbor.LazyValue
}

func (m MockOutput) Address() ledger.Address {
return m.address
}

func (m MockOutput) Amount() uint64 {
return m.amount
}

func (m MockOutput) Assets() *common.MultiAsset[common.MultiAssetTypeOutput] {
return m.assets
}

func (m MockOutput) Datum() *cbor.LazyValue {
return m.datum
}

func (m MockOutput) DatumHash() *common.Blake2b256 {
return nil
}

func (m MockOutput) Cbor() []byte {
return []byte{}
}

func (m MockOutput) Utxorpc() *cardano.TxOutput {
return nil
}

func (l *MockLogger) Info(msg string, args ...interface{}) {}
func (l *MockLogger) Error(msg string, args ...interface{}) {}
func (l *MockLogger) Debug(msg string, args ...interface{}) {}
Expand Down Expand Up @@ -85,3 +190,162 @@ func TestChainSync_OutputChan(t *testing.T) {
t.Fatalf("expected non-nil outputChan")
}
}

func TestFilterByAddress(t *testing.T) {
// Setup ChainSync with address filter
cs := New(WithAddresses([]string{"addr_test1qqjwq357"}))

// Create a mock address using the methods
mockAddr := common.Address{}

// Mock transaction event
output := MockOutput{
address: mockAddr,
amount: 1000000,
assets: nil,
datum: nil,
}
evt := event.Event{
Payload: chainsync.TransactionEvent{
Outputs: []ledger.TransactionOutput{output},
ResolvedInputs: []ledger.TransactionOutput{output},
},
}

// Start the filter
err := cs.Start()
assert.NoError(t, err, "ChainSync filter should start without error")
defer cs.Stop()

// Send event to input channel
cs.InputChan() <- evt

// Wait for the event to be processed
select {
case filteredEvt := <-cs.OutputChan():
assert.Equal(t, evt, filteredEvt, "Filtered event should match the input event")
case <-time.After(10 * time.Second):
t.Fatal("Test timed out waiting for filtered event")
}
}

func TestFilterByPolicyId(t *testing.T) {
// Setup ChainSync with policy ID filter
filterPolicyId := "random_policy_id"
policyIdHash := common.Blake2b224Hash([]byte(filterPolicyId))
cs := New(WithPolicies([]string{policyIdHash.String()}))

// Mock transaction event
policyId := policyIdHash // Use the same hash as the filter

// Create a new MultiAsset with pre-populated data
assetsData := make(map[common.Blake2b224]map[cbor.ByteString]common.MultiAssetTypeOutput)
assetName := cbor.NewByteString([]byte("asset1"))
assetsData[policyId] = map[cbor.ByteString]common.MultiAssetTypeOutput{
assetName: 1, // Add asset with quantity 1
}
assets := common.NewMultiAsset(assetsData)

output := MockOutput{
address: ledger.Address{},
amount: 1000000,
assets: &assets,
datum: nil,
}
evt := event.Event{
Payload: chainsync.TransactionEvent{
Outputs: []ledger.TransactionOutput{output},
ResolvedInputs: []ledger.TransactionOutput{output},
},
}

// Start the filter
err := cs.Start()
assert.NoError(t, err, "ChainSync filter should start without error")
defer cs.Stop()

// Send event to input channel
cs.InputChan() <- evt

// Wait for the event to be processed
select {
case filteredEvt := <-cs.OutputChan():
assert.Equal(t, evt, filteredEvt, "Filtered event should match the input event")
case <-time.After(5 * time.Second):
t.Fatal("Test timed out waiting for filtered event")
}
}

func TestFilterByAssetFingerprint(t *testing.T) {
// Setup ChainSync with asset fingerprint filter
filterAssetFingerprint := "asset1e58wmplshqdkkq97tz02chq980456wgt35tfjr"
cs := New(WithAssetFingerprints([]string{filterAssetFingerprint}))

// Mock transaction event
policyId := common.Blake2b224Hash([]byte("policy1"))

// Create a new MultiAsset with pre-populated data
assetsData := make(map[common.Blake2b224]map[cbor.ByteString]common.MultiAssetTypeOutput)
assetName := cbor.NewByteString([]byte("asset1"))
assetsData[policyId] = map[cbor.ByteString]common.MultiAssetTypeOutput{
assetName: 1, // Add asset with quantity 1
}
assets := common.NewMultiAsset(assetsData)

output := MockOutput{
address: ledger.Address{},
amount: 1000000,
assets: &assets,
datum: nil,
}
evt := event.Event{
Payload: chainsync.TransactionEvent{
Outputs: []ledger.TransactionOutput{output},
ResolvedInputs: []ledger.TransactionOutput{output},
},
}

// Start the filter
err := cs.Start()
assert.NoError(t, err, "ChainSync filter should start without error")
defer cs.Stop()

// Send event to input channel
cs.InputChan() <- evt

// Wait for the event to be processed
select {
case filteredEvt := <-cs.OutputChan():
assert.Equal(t, evt, filteredEvt, "Filtered event should match the input event")
case <-time.After(5 * time.Second):
t.Fatal("Test timed out waiting for filtered event")
}
}

func TestFilterByPoolId(t *testing.T) {
// Setup ChainSync with pool ID filter
cs := New(WithPoolIds([]string{"pool1"}))

// Mock block event
evt := event.Event{
Payload: chainsync.BlockEvent{
IssuerVkey: "pool1", // Match the filterPoolIds
},
}

// Start the filter
err := cs.Start()
assert.NoError(t, err, "ChainSync filter should start without error")
defer cs.Stop()

// Send event to input channel
cs.InputChan() <- evt

// Wait for the event to be processed
select {
case filteredEvt := <-cs.OutputChan():
assert.Equal(t, evt, filteredEvt, "Filtered event should match the input event")
case <-time.After(5 * time.Second):
t.Fatal("Test timed out waiting for filtered event")
}
}
91 changes: 91 additions & 0 deletions filter/chainsync/plugin_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package chainsync

import (
"testing"
"time"

"github.com/blinklabs-io/adder/event"
"github.com/blinklabs-io/adder/input/chainsync"
"github.com/blinklabs-io/adder/plugin"
"github.com/stretchr/testify/assert"
)

func TestPluginRegistration(t *testing.T) {
// Retrieve the plugin entries
plugins := plugin.GetPlugins(plugin.PluginTypeFilter) // Get all registered plugins

// Find the "chainsync" plugin
var p plugin.Plugin
for _, entry := range plugins {
if entry.Name == "chainsync" {
// Create a new instance of the plugin
p = entry.NewFromOptionsFunc()
break
}
}

// Verify that the plugin was found
assert.NotNil(t, p, "Plugin should be registered")

// Verify that the plugin implements the Plugin interface
_, ok := p.(plugin.Plugin)
assert.True(t, ok, "Plugin should implement the Plugin interface")
}

func TestPluginStartStop(t *testing.T) {
// Create a new plugin instance
p := NewFromCmdlineOptions()

// Start the plugin
err := p.Start()
assert.NoError(t, err, "Plugin should start without errors")

// Stop the plugin
err = p.Stop()
assert.NoError(t, err, "Plugin should stop without errors")
}

func TestPluginChannels(t *testing.T) {
// Create a new plugin instance
p := NewFromCmdlineOptions()

// Verify that the error channel is not nil
assert.NotNil(t, p.ErrorChan(), "Error channel should not be nil")

// Verify that the input channel is not nil
assert.NotNil(t, p.InputChan(), "Input channel should not be nil")

// Verify that the output channel is not nil
assert.NotNil(t, p.OutputChan(), "Output channel should not be nil")
}

func TestPluginEventProcessing(t *testing.T) {
// Create a new plugin instance
p := NewFromCmdlineOptions()

// Start the plugin
err := p.Start()
assert.NoError(t, err, "Plugin should start without errors")

// Create a test event with a TransactionEvent payload
testEvent := event.Event{
Type: "transaction",
Timestamp: time.Now(),
Payload: chainsync.TransactionEvent{},
}

// Send the event to the input channel
p.InputChan() <- testEvent

// Read the event from the output channel
select {
case outputEvent := <-p.OutputChan():
assert.Equal(t, testEvent, outputEvent, "Output event should match input event")
case <-time.After(1 * time.Second):
t.Fatal("Timeout waiting for output event")
}

// Stop the plugin
err = p.Stop()
assert.NoError(t, err, "Plugin should stop without errors")
}