-
Notifications
You must be signed in to change notification settings - Fork 129
add indexer client unit tests #2000
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
1015319
d728c86
325be3d
0706f55
093f7ba
b598b52
d504aaa
3e185f7
8e17b92
890c656
5d5edd8
c96fbae
95fb2b5
a7b8885
3e61cd7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,43 +9,54 @@ import ( | |
"testing" | ||
"time" | ||
|
||
"github.com/ava-labs/avalanchego/database" | ||
"github.com/ava-labs/avalanchego/ids" | ||
"github.com/ava-labs/avalanchego/trace" | ||
"github.com/stretchr/testify/require" | ||
|
||
"github.com/ava-labs/hypersdk/api" | ||
"github.com/ava-labs/hypersdk/chain" | ||
"github.com/ava-labs/hypersdk/chain/chaintest" | ||
"github.com/ava-labs/hypersdk/codec" | ||
) | ||
|
||
func TestIndexerClientBlocks(t *testing.T) { | ||
const ( | ||
numExecutedBlocks = 4 | ||
blockWindow = 2 | ||
numTxs = 3 | ||
blockWindow = 2 | ||
numTxs = 3 | ||
) | ||
|
||
testCases := []struct { | ||
name string | ||
blkHeight uint64 | ||
err error | ||
name string | ||
blkHeight uint64 | ||
blkHeightErr error | ||
numExecutedBlocks int | ||
}{ | ||
{ | ||
name: "success", | ||
blkHeight: numExecutedBlocks - 1, | ||
err: nil, | ||
name: "success", | ||
blkHeight: 3, | ||
blkHeightErr: nil, | ||
numExecutedBlocks: 4, | ||
}, | ||
{ | ||
name: "missing block", | ||
blkHeight: 0, | ||
blkHeightErr: errBlockNotFound, | ||
numExecutedBlocks: 4, | ||
}, | ||
{ | ||
name: "missing block", | ||
blkHeight: 0, | ||
err: errBlockNotFound, | ||
name: "no blocks", | ||
blkHeight: 0, | ||
blkHeightErr: errBlockNotFound, | ||
numExecutedBlocks: 0, | ||
}, | ||
} | ||
|
||
for _, tt := range testCases { | ||
t.Run(tt.name, func(t *testing.T) { | ||
r := require.New(t) | ||
ctx := context.Background() | ||
indexer, executedBlocks, _ := createTestIndexer(t, ctx, numExecutedBlocks, blockWindow, numTxs) | ||
indexer, executedBlocks, _ := createTestIndexer(t, ctx, tt.numExecutedBlocks, blockWindow, numTxs) | ||
|
||
jsonHandler, err := api.NewJSONRPCHandler(Name, NewServer(trace.Noop, indexer)) | ||
r.NoError(err) | ||
|
@@ -59,26 +70,36 @@ func TestIndexerClientBlocks(t *testing.T) { | |
|
||
client := NewClient(httpServer.URL) | ||
|
||
expectedBlock := executedBlocks[tt.blkHeight] | ||
executedBlock, err := client.GetBlockByHeight(ctx, expectedBlock.Block.Hght, parser) | ||
if tt.err == nil { | ||
executedBlock, err := client.GetBlockByHeight(ctx, tt.blkHeight, parser) | ||
if tt.blkHeightErr == nil { | ||
tsachiherman marked this conversation as resolved.
Show resolved
Hide resolved
|
||
r.NoError(err) | ||
expectedBlock := executedBlocks[tt.blkHeight-1] | ||
r.Equal(expectedBlock.Block, executedBlock.Block) | ||
} else { | ||
r.ErrorContains(err, tt.err.Error()) | ||
r.ErrorContains(err, tt.blkHeightErr.Error()) | ||
} | ||
|
||
expectedBlkID := ids.Empty | ||
if tt.blkHeight > 0 { | ||
expectedBlock := executedBlocks[tt.blkHeight-1] | ||
expectedBlkID = expectedBlock.Block.GetID() | ||
} | ||
|
||
executedBlock, err = client.GetBlock(ctx, expectedBlock.Block.GetID(), parser) | ||
if tt.err == nil { | ||
executedBlock, err = client.GetBlock(ctx, expectedBlkID, parser) | ||
if tt.blkHeightErr == nil { | ||
RodrigoVillar marked this conversation as resolved.
Show resolved
Hide resolved
|
||
r.NoError(err) | ||
r.Equal(expectedBlock.Block, executedBlock.Block) | ||
r.Equal(executedBlocks[tt.blkHeight-1].Block, executedBlock.Block) | ||
} else { | ||
r.ErrorContains(err, tt.err.Error()) | ||
r.ErrorContains(err, tt.blkHeightErr.Error()) | ||
} | ||
|
||
executedBlock, err = client.GetLatestBlock(ctx, parser) | ||
r.NoError(err) | ||
r.Equal(executedBlocks[numExecutedBlocks-1].Block, executedBlock.Block) | ||
if tt.numExecutedBlocks == 0 { | ||
r.ErrorContains(err, database.ErrNotFound.Error()) | ||
Comment on lines
+97
to
+98
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Handling the special case this way makes it seem like we default to not including the genesis. Could we include at least the genesis block (we should receive a notification of at least the genesis block on startup) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is not intended to cover the genesis block; it's really about covering the no-blocks-of-any-kind usecaswe. |
||
} else { | ||
r.NoError(err) | ||
r.Equal(executedBlocks[tt.numExecutedBlocks-1].Block, executedBlock.Block) | ||
} | ||
}) | ||
} | ||
} | ||
|
@@ -90,26 +111,61 @@ func TestIndexerClientTransactions(t *testing.T) { | |
numTxs = 3 | ||
) | ||
|
||
parser := chaintest.NewTestParser() | ||
badParser := &chain.TxTypeParser{ | ||
ActionRegistry: codec.NewTypeParser[chain.Action](), | ||
AuthRegistry: codec.NewTypeParser[chain.Auth](), | ||
} | ||
|
||
testCases := []struct { | ||
name string | ||
blockIndex int | ||
txIndex int | ||
err error | ||
found bool | ||
name string | ||
blockIndex int | ||
txIndex int | ||
getTxResultsErr error | ||
getTxErr error | ||
found bool | ||
parser *chain.TxTypeParser | ||
malformedBlock bool | ||
}{ | ||
{ | ||
name: "success", | ||
blockIndex: numExecutedBlocks - 1, | ||
txIndex: 0, | ||
found: true, | ||
err: nil, | ||
name: "success", | ||
blockIndex: numExecutedBlocks - 1, | ||
txIndex: 0, | ||
found: true, | ||
getTxResultsErr: nil, | ||
getTxErr: nil, | ||
parser: parser, | ||
malformedBlock: false, | ||
}, | ||
{ | ||
name: "missing transaction", | ||
blockIndex: 0, | ||
txIndex: 0, | ||
found: false, | ||
getTxResultsErr: nil, | ||
getTxErr: nil, | ||
parser: parser, | ||
malformedBlock: false, | ||
}, | ||
{ | ||
name: "badParser", | ||
blockIndex: numExecutedBlocks - 1, | ||
txIndex: numTxs - 1, | ||
found: true, | ||
getTxResultsErr: nil, | ||
getTxErr: errTxUnmarshalingFailed, | ||
parser: badParser, | ||
malformedBlock: false, | ||
}, | ||
{ | ||
name: "missing transaction", | ||
blockIndex: 0, | ||
txIndex: 0, | ||
found: false, | ||
err: nil, | ||
name: "malformed block", | ||
blockIndex: numExecutedBlocks - 1, | ||
txIndex: 0, | ||
found: false, | ||
getTxResultsErr: errTxResultNotFound, | ||
getTxErr: errTxResultNotFound, | ||
parser: parser, | ||
malformedBlock: true, | ||
}, | ||
} | ||
|
||
|
@@ -127,14 +183,26 @@ func TestIndexerClientTransactions(t *testing.T) { | |
httpServer.Close() | ||
}) | ||
|
||
parser := chaintest.NewTestParser() | ||
|
||
client := NewClient(httpServer.URL) | ||
|
||
executedBlock := executedBlocks[tt.blockIndex] | ||
executedTx := executedBlock.Block.Txs[tt.txIndex] | ||
|
||
if tt.malformedBlock { | ||
// create a malformed block and have the indexer add it. | ||
// clearing out the results ensure that we won't be able to find the result corresponding to any | ||
// of the transactions. | ||
executedBlock.ExecutionResults.Results = []*chain.Result{} | ||
r.NoError(indexer.Notify(ctx, executedBlock)) | ||
} | ||
|
||
txResponse, found, err := client.GetTxResults(ctx, executedTx.GetID()) | ||
r.Equal(tt.err, err) | ||
if tt.getTxResultsErr != nil { | ||
RodrigoVillar marked this conversation as resolved.
Show resolved
Hide resolved
|
||
r.Error(err) | ||
r.ErrorContains(err, tt.getTxResultsErr.Error()) | ||
err = nil | ||
} | ||
r.NoError(err) | ||
r.Equal(tt.found, found) | ||
if tt.found { | ||
r.Equal(GetTxResponse{ | ||
|
@@ -144,17 +212,24 @@ func TestIndexerClientTransactions(t *testing.T) { | |
}, txResponse) | ||
} | ||
|
||
txResponse, tx, found, err := client.GetTx(ctx, executedTx.GetID(), parser) | ||
r.Equal(tt.err, err) | ||
txResponse, tx, found, err := client.GetTx(ctx, executedTx.GetID(), tt.parser) | ||
if tt.getTxErr != nil { | ||
r.Error(err) | ||
r.ErrorContains(err, tt.getTxErr.Error()) | ||
return | ||
} | ||
r.NoError(err) | ||
r.Equal(tt.found, found) | ||
if tt.found { | ||
r.Equal(GetTxResponse{ | ||
TxBytes: executedTx.Bytes(), | ||
Timestamp: executedBlock.Block.Tmstmp, | ||
Result: executedBlock.ExecutionResults.Results[tt.txIndex], | ||
}, txResponse) | ||
r.Equal(executedTx, tx) | ||
if !tt.found { | ||
return | ||
} | ||
|
||
r.Equal(GetTxResponse{ | ||
TxBytes: executedTx.Bytes(), | ||
Timestamp: executedBlock.Block.Tmstmp, | ||
Result: executedBlock.ExecutionResults.Results[tt.txIndex], | ||
}, txResponse) | ||
r.Equal(executedTx, tx) | ||
}) | ||
} | ||
} | ||
|
@@ -167,7 +242,7 @@ func TestIndexerClientWaitForTransaction(t *testing.T) { | |
) | ||
r := require.New(t) | ||
ctx := context.Background() | ||
indexer, _, _ := createTestIndexer(t, ctx, numExecutedBlocks, blockWindow, numTxs) | ||
indexer, executedBlocks, _ := createTestIndexer(t, ctx, numExecutedBlocks, blockWindow, numTxs) | ||
|
||
jsonHandler, err := api.NewJSONRPCHandler(Name, NewServer(trace.Noop, indexer)) | ||
r.NoError(err) | ||
|
@@ -183,4 +258,13 @@ func TestIndexerClientWaitForTransaction(t *testing.T) { | |
defer ctxCancel() | ||
_, _, err = client.WaitForTransaction(timeoutCtx, 1*time.Millisecond, ids.GenerateTestID()) | ||
r.ErrorIs(err, context.DeadlineExceeded) | ||
|
||
// wait for a past transaction. | ||
lastExecutedBlock := executedBlocks[numExecutedBlocks-1] | ||
lastTx := lastExecutedBlock.Block.Txs[numTxs-1] | ||
lastTxResult := lastExecutedBlock.ExecutionResults.Results[numTxs-1] | ||
success, fee, err := client.WaitForTransaction(ctx, 1*time.Millisecond, lastTx.GetID()) | ||
r.NoError(err) | ||
r.Equal(lastTxResult.Success, success) | ||
r.Equal(lastTxResult.Fee, fee) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In production, we should be able to serve the genesis block, should we generate a sequence of blocks that includes a genesis block?