-
Notifications
You must be signed in to change notification settings - Fork 126
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
add indexer client unit tests #2000
base: main
Are you sure you want to change the base?
Changes from 5 commits
1015319
d728c86
325be3d
0706f55
093f7ba
b598b52
d504aaa
3e185f7
8e17b92
890c656
5d5edd8
c96fbae
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 { | ||
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. Could we simplify this if-condition? I think it's sufficient to check that the errors are "equal" before invoking the if-condition rather than doing an if-else: r.ErrorIs(tt.blkHeightErr, err)
if tt.blkHeightErr == nil {
expectedBlock := executedBlocks[tt.blkHeight-1]
r.Equal(expectedBlock.Block, executedBlock.Block)
} 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. Unfortunately, that won't work ( as is.. ). The returned error is an rpc error that contains a string representing the internal server error. Hence, we need to compare it by string ( We do have a PR to update this and create RPC specific error codes, which would resolve this issue (#1942) 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. Ah understood, thanks for the clarification! |
||
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.GenerateTestID() | ||
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. Could we use |
||
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{ | ||
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. nit: |
||
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, | ||
err: nil, | ||
name: "missing transaction", | ||
blockIndex: 0, | ||
txIndex: 0, | ||
found: false, | ||
getTxResultsErr: nil, | ||
getTxErr: nil, | ||
parser: parser, | ||
malformedBlock: false, | ||
}, | ||
{ | ||
name: "badparser", | ||
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. nit: |
||
blockIndex: numExecutedBlocks - 1, | ||
txIndex: numTxs - 1, | ||
found: true, | ||
getTxResultsErr: nil, | ||
getTxErr: errTxUnmarshalingFailed, | ||
parser: badparser, | ||
malformedBlock: false, | ||
}, | ||
{ | ||
name: "malformed block", | ||
blockIndex: numExecutedBlocks - 1, | ||
txIndex: numTxs - 1, | ||
found: true, | ||
getTxResultsErr: errTxResultNotFound, | ||
getTxErr: errTxResultNotFound, | ||
parser: parser, | ||
malformedBlock: true, | ||
}, | ||
} | ||
|
||
|
@@ -127,33 +183,45 @@ 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 { | ||
// "damage" the last executed block by removing the last result. | ||
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. Could we fix this comment? This removes the first result not the last result |
||
executedBlock.ExecutionResults.Results = executedBlock.ExecutionResults.Results[1:] | ||
r.NoError(indexer.Notify(context.Background(), executedBlock)) | ||
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. Could we use the |
||
} | ||
|
||
txResponse, found, err := client.GetTxResults(ctx, executedTx.GetID()) | ||
r.Equal(tt.err, 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) | ||
if tt.getTxResultsErr != nil { | ||
RodrigoVillar marked this conversation as resolved.
Show resolved
Hide resolved
|
||
r.ErrorContains(err, tt.getTxResultsErr.Error()) | ||
} else { | ||
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) | ||
} | ||
} | ||
|
||
txResponse, tx, found, err := client.GetTx(ctx, executedTx.GetID(), parser) | ||
r.Equal(tt.err, 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) | ||
txResponse, tx, found, err := client.GetTx(ctx, executedTx.GetID(), tt.parser) | ||
if tt.getTxErr != nil { | ||
r.ErrorContains(err, tt.getTxErr.Error()) | ||
} else { | ||
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) | ||
} | ||
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. There's a lot of conditional nesting in these table tests. Could we try and simplify them so that they have narrow scope and reduce unnecessary nesting as much as possible? For example, this section could be re-written as: txResponse, tx, found, err := client.GetTx(ctx, executedTx.GetID(), tt.parser)
if tt.getTxErr != nil {
r.ErrorContains(err, tt.getTxErr.Error())
}
r.Equal(tt.found, found)
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) style guide refs: 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. Hmm putting this in place fails on the tests: it seems this is because |
||
} | ||
}) | ||
} | ||
|
@@ -167,7 +235,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 +251,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(context.Background(), 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?