From 2a43a1c42c2ac6b3b6618f6793ee656f66a1b0e2 Mon Sep 17 00:00:00 2001 From: Pavel Zbitskiy Date: Fri, 3 Jan 2025 21:38:39 -0500 Subject: [PATCH] tests: cross-sdk heartbeat tests --- test/algodclientv2_test.go | 46 +++++++++++++++++++++++++++++++++++++- test/indexer_unit_test.go | 20 +++++++++++++++++ test/unit.tags | 3 +++ 3 files changed, 68 insertions(+), 1 deletion(-) diff --git a/test/algodclientv2_test.go b/test/algodclientv2_test.go index e49bc5e9..b4d1b093 100644 --- a/test/algodclientv2_test.go +++ b/test/algodclientv2_test.go @@ -7,6 +7,8 @@ import ( "fmt" "github.com/algorand/go-algorand-sdk/v2/client/v2/algod" + "github.com/algorand/go-algorand-sdk/v2/client/v2/common" + "github.com/algorand/go-algorand-sdk/v2/client/v2/common/models" modelsV2 "github.com/algorand/go-algorand-sdk/v2/client/v2/common/models" "github.com/algorand/go-algorand-sdk/v2/types" @@ -65,6 +67,7 @@ func AlgodClientV2Context(s *godog.ScenarioContext) { s.Step(`^we make a LedgerStateDeltaForTransactionGroupResponse call for ID "([^"]*)"$`, weMakeALedgerStateDeltaForTransactionGroupResponseCallForID) s.Step(`^we make a TransactionGroupLedgerStateDeltaForRoundResponse call for round (\d+)$`, weMakeATransactionGroupLedgerStateDeltaForRoundResponseCallForRound) s.Step(`^we make a GetBlockTxids call against block number (\d+)$`, weMakeAGetBlockTxidsCallAgainstBlockNumber) + s.Step(`^the parsed Get Block response should have heartbeat address "([^"]*)"$`, theParsedGetBlockResponseShouldHaveHeartbeatAddress) s.Before(func(ctx context.Context, sc *godog.Scenario) (context.Context, error) { globalErrForExamination = nil @@ -104,6 +107,34 @@ func weMakeAnyAccountInformationCall() error { return weMakeAnyCallTo("algod", "AccountInformation") } +type mockBlock struct { + c *algod.Client + round uint64 + p algod.BlockParams +} + +// This is a mock implementation of the do method in the Block struct in the algod package. +// in order to support both JSON and MSGPACK formats depending on the mock file. +func (s *mockBlock) do(ctx context.Context, headers ...*common.Header) (result types.Block, err error) { + var response models.BlockResponse + + val := responseRing.Value.([]byte) + if len(val) > 0 && val[0] == '{' { + s.p.Format = "json" + err = (*common.Client)(s.c).Get(ctx, &response, fmt.Sprintf("/v2/blocks/%d", s.round), s.p, headers) + } else { + s.p.Format = "msgpack" + err = (*common.Client)(s.c).GetRawMsgpack(ctx, &response, fmt.Sprintf("/v2/blocks/%d", s.round), s.p, headers) + } + + if err != nil { + return + } + + result = response.Block + return +} + var blockResponse types.Block func weMakeAnyGetBlockCall() error { @@ -113,7 +144,9 @@ func weMakeAnyGetBlockCall() error { if err != nil { return err } - blockResponse, globalErrForExamination = algodClient.Block(0).Do(context.Background()) + req := &mockBlock{c: algodClient, round: 0} + blockResponse, globalErrForExamination = req.do(context.Background()) + return nil } @@ -129,6 +162,17 @@ func theParsedGetBlockResponseShouldHaveRewardsPool(pool string) error { return nil } +func theParsedGetBlockResponseShouldHaveHeartbeatAddress(hbAddress string) error { + if len(blockResponse.Payset) == 0 { + return fmt.Errorf("response has no payset") + } + addr := blockResponse.Payset[0].Txn.HeartbeatTxnFields.HbAddress.String() + if addr != hbAddress { + return fmt.Errorf("response heartbeat address %s mismatched expected address %s", addr, hbAddress) + } + return nil +} + func weMakeAnySuggestedTransactionParametersCall() error { return weMakeAnyCallTo("algod", "TransactionParams") } diff --git a/test/indexer_unit_test.go b/test/indexer_unit_test.go index ca678bae..05e412f9 100644 --- a/test/indexer_unit_test.go +++ b/test/indexer_unit_test.go @@ -6,6 +6,7 @@ import ( "fmt" "strings" + "github.com/algorand/go-algorand-sdk/v2/client/v2/common/models" "github.com/algorand/go-algorand-sdk/v2/client/v2/indexer" "github.com/cucumber/godog" ) @@ -56,6 +57,7 @@ func IndexerUnitTestContext(s *godog.ScenarioContext) { s.Step(`^we make a Lookup Block call against round (\d+) and header "([^"]*)"$`, weMakeALookupBlockCallAgainstRoundAndHeader) s.Step(`^we make a LookupApplicationBoxByIDandName call with applicationID (\d+) with encoded box name "([^"]*)"$`, weMakeALookupApplicationBoxByIDandName) s.Step(`^we make a SearchForApplicationBoxes call with applicationID (\d+) with max (\d+) nextToken "([^"]*)"$`, weMakeASearchForApplicationBoxes) + s.Step(`^the parsed SearchForTransactions response should be valid on round (\d+) and the array should be of len (\d+) and the element at index (\d+) should have hbaddress "([^"]*)"$`, theParsedSearchForTransactionsResponseHasHeartbeatAddress) s.Before(func(ctx context.Context, sc *godog.Scenario) (context.Context, error) { globalErrForExamination = nil return ctx, nil @@ -394,3 +396,21 @@ func weMakeASearchForApplicationBoxes(appId int, limit int, next string) error { indexerClient.SearchForApplicationBoxes(uint64(appId)).Limit(uint64(limit)).Next(next).Do(context.Background()) return nil } + +func theParsedSearchForTransactionsResponseHasHeartbeatAddress(round int, length int, index int, hbAddress string) error { + resp := response.(models.TransactionsResponse) + + if resp.CurrentRound != uint64(round) { + return fmt.Errorf("Expected round %d, got %d", round, resp.CurrentRound) + } + if len(resp.Transactions) != length { + return fmt.Errorf("Expected %d transactions, got %d", length, len(resp.Transactions)) + } + + hbTxn := resp.Transactions[index] + if hbTxn.HeartbeatTransaction.HbAddress != hbAddress { + return fmt.Errorf("Expected heartbeat address %s, got %s", hbAddress, hbTxn.HeartbeatTransaction.HbAddress) + } + + return nil +} diff --git a/test/unit.tags b/test/unit.tags index bda065cd..89cc0d32 100644 --- a/test/unit.tags +++ b/test/unit.tags @@ -2,6 +2,8 @@ @unit.abijson.byname @unit.algod @unit.algod.ledger_refactoring +@unit.algod.heartbeat +@unit.algod.heartbeat.msgp @unit.applications @unit.applications.boxes @unit.atomic_transaction_composer @@ -14,6 +16,7 @@ @unit.indexer.ledger_refactoring @unit.indexer.logs @unit.indexer.rekey +@unit.indexer.heartbeat @unit.offline @unit.program_sanity_check @unit.ready