cosmos-sdk/baseapp/txnrunner/default_test.go
Eric Warehime a991f69d05
feat!: block stm integration (#25334)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: yihuang <huang@crypto.com>
Co-authored-by: mmsqe <mavis@crypto.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <dependabot[bot]@users.noreply.github.com>
Co-authored-by: Alex | Interchain Labs <alex@interchainlabs.io>
Co-authored-by: Tyler <48813565+technicallyty@users.noreply.github.com>
Co-authored-by: FT <140458077+zeevick10@users.noreply.github.com>
Co-authored-by: VolodymyrBg <aqdrgg19@gmail.com>
Co-authored-by: GarmashAlex <garmasholeksii@gmail.com>
Co-authored-by: kilavvy <140459108+kilavvy@users.noreply.github.com>
Co-authored-by: Maxim Evtush <154841002+maximevtush@users.noreply.github.com>
Co-authored-by: fuder.eth <139509124+vtjl10@users.noreply.github.com>
Co-authored-by: leopardracer <136604165+leopardracer@users.noreply.github.com>
Co-authored-by: MozirDmitriy <dmitriymozir@gmail.com>
Co-authored-by: Avory <avorycorelli@gmail.com>
Co-authored-by: mmsqe <tqd0800210105@gmail.com>
Co-authored-by: julienrbrt <julien@rbrt.fr>
Co-authored-by: Aaron <aaronc@users.noreply.github.com>
Co-authored-by: Vlad J <vladjdk@gmail.com>
Co-authored-by: Thomas Nguy <thomas.nguy@crypto.com>
2025-10-24 16:35:37 +00:00

169 lines
4.6 KiB
Go

package txnrunner
import (
"context"
"errors"
"sync/atomic"
"testing"
abci "github.com/cometbft/cometbft/abci/types"
"github.com/stretchr/testify/require"
storetypes "cosmossdk.io/store/types"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)
// Mock TxDecoder for testing
func mockTxDecoder(txBytes []byte) (sdk.Tx, error) {
if len(txBytes) == 0 {
return nil, errors.New("empty tx")
}
// Valid transaction if first byte is not 0xFF
if txBytes[0] == 0xFF {
return nil, errors.New("invalid tx")
}
return nil, nil
}
// TestNewDefaultRunner tests the constructor
func TestNewDefaultRunner(t *testing.T) {
runner := NewDefaultRunner(nil)
require.NotNil(t, runner)
require.Nil(t, runner.txDecoder)
}
// TestDefaultRunner_Run_Success tests successful execution of transactions
func TestDefaultRunner_Run_Success(t *testing.T) {
decoder := mockTxDecoder
runner := NewDefaultRunner(decoder)
txs := [][]byte{
{0x01, 0x02, 0x03},
{0x04, 0x05, 0x06},
{0x07, 0x08, 0x09},
}
executionCount := atomic.Int32{}
deliverTx := func(tx []byte, ms storetypes.MultiStore, txIndex int, cache map[string]any) *abci.ExecTxResult {
executionCount.Add(1)
return &abci.ExecTxResult{
Code: 0,
Data: tx,
}
}
ctx := context.Background()
results, err := runner.Run(ctx, nil, txs, deliverTx)
require.NoError(t, err)
require.Len(t, results, len(txs))
require.Equal(t, int32(len(txs)), executionCount.Load())
for i, result := range results {
require.Equal(t, uint32(0), result.Code)
require.Equal(t, txs[i], result.Data)
}
}
// TestDefaultRunner_Run_EmptyTxs tests execution with no transactions
func TestDefaultRunner_Run_EmptyTxs(t *testing.T) {
decoder := mockTxDecoder
runner := NewDefaultRunner(decoder)
deliverTx := func(tx []byte, ms storetypes.MultiStore, txIndex int, cache map[string]any) *abci.ExecTxResult {
t.Fatal("deliverTx should not be called for empty txs")
return nil
}
ctx := context.Background()
results, err := runner.Run(ctx, nil, [][]byte{}, deliverTx)
require.NoError(t, err)
require.Empty(t, results)
}
// TestDefaultRunner_Run_InvalidTx tests handling of invalid transactions
func TestDefaultRunner_Run_InvalidTx(t *testing.T) {
decoder := mockTxDecoder
runner := NewDefaultRunner(decoder)
txs := [][]byte{
{0x01, 0x02, 0x03}, // valid
{0xFF, 0xFF, 0xFF}, // invalid (0xFF marker)
{0x07, 0x08, 0x09}, // valid
}
validTxCount := atomic.Int32{}
deliverTx := func(tx []byte, ms storetypes.MultiStore, txIndex int, cache map[string]any) *abci.ExecTxResult {
validTxCount.Add(1)
return &abci.ExecTxResult{Code: 0}
}
ctx := context.Background()
results, err := runner.Run(ctx, nil, txs, deliverTx)
require.NoError(t, err)
require.Len(t, results, len(txs))
// Only 2 valid transactions should be executed
require.Equal(t, int32(2), validTxCount.Load())
// The invalid tx should get an error response
require.Equal(t, sdkerrors.ErrTxDecode.ABCICode(), results[1].Code)
}
// TestDefaultRunner_Run_ContextCancellation tests that execution stops on context cancellation
func TestDefaultRunner_Run_ContextCancellation(t *testing.T) {
decoder := mockTxDecoder
runner := NewDefaultRunner(decoder)
txs := [][]byte{
{0x01, 0x02, 0x03},
{0x04, 0x05, 0x06},
{0x07, 0x08, 0x09},
{0x0A, 0x0B, 0x0C},
}
ctx, cancel := context.WithCancel(context.Background())
executionCount := atomic.Int32{}
deliverTx := func(tx []byte, ms storetypes.MultiStore, txIndex int, cache map[string]any) *abci.ExecTxResult {
count := executionCount.Add(1)
// Cancel after second transaction
if count == 2 {
cancel()
}
return &abci.ExecTxResult{Code: 0}
}
_, err := runner.Run(ctx, nil, txs, deliverTx)
require.Error(t, err)
require.Equal(t, context.Canceled, err)
// Results may be nil or partial depending on when cancellation occurs
// The key assertion is that execution was stopped
require.LessOrEqual(t, executionCount.Load(), int32(len(txs)))
}
// TestDefaultRunner_Run_MultiStoreIsNil tests that nil multistore is handled correctly
func TestDefaultRunner_Run_MultiStoreIsNil(t *testing.T) {
decoder := mockTxDecoder
runner := NewDefaultRunner(decoder)
txs := [][]byte{{0x01}}
deliverTx := func(tx []byte, ms storetypes.MultiStore, txIndex int, cache map[string]any) *abci.ExecTxResult {
require.Nil(t, ms, "multistore should be nil for DefaultRunner")
require.Nil(t, cache, "cache should be nil for DefaultRunner")
return &abci.ExecTxResult{Code: 0}
}
ctx := context.Background()
results, err := runner.Run(ctx, nil, txs, deliverTx)
require.NoError(t, err)
require.Len(t, results, 1)
}