diff --git a/.github/workflows/ci-pr.yml b/.github/workflows/ci-pr.yml index 0a97bfab28..c4f9d8d5e1 100644 --- a/.github/workflows/ci-pr.yml +++ b/.github/workflows/ci-pr.yml @@ -49,7 +49,7 @@ jobs: - name: Run tests run: | PACKAGES="$(go list ./... | grep -v /go-algorand/test/)" - export PACKAGE_NAMES=$(echo $PACKAGES | tr -d '\n') + export PACKAGE_NAMES=$(echo $PACKAGES | tr -d '\n') mkdir -p test_results/${{ matrix.platform }}_test/${PARTITION_ID} go tool -modfile=tool.mod gotestsum --format standard-quiet \ --junitfile ~/test_results/${{ matrix.platform }}_test/${PARTITION_ID}/results.xml \ @@ -57,6 +57,11 @@ jobs: -- --tags "sqlite_unlock_notify sqlite_omit_load_extension" $SHORTTEST \ -race -timeout 1h -coverprofile=coverage.txt -covermode=atomic -p 4 \ $PACKAGE_NAMES + - name: Collect errorcontains capture files + if: ${{ !cancelled() }} + run: | + mkdir -p ~/test_results/${{ matrix.platform }}_test/${PARTITION_ID}/errorcontains + cp /tmp/error_capture.*.jsonl ~/test_results/${{ matrix.platform }}_test/${PARTITION_ID}/errorcontains/ 2>/dev/null || true - name: Notify Slack on failure if: failure() uses: ./.github/actions/slack-notify @@ -130,6 +135,11 @@ jobs: mkdir -p ~/test_results/${{ matrix.platform }}_integration/${PARTITION_ID} NO_BUILD=true TEST_RESULTS=~/test_results/${{ matrix.platform }}_integration/${PARTITION_ID} \ test/scripts/e2e.sh + - name: Collect errorcontains capture files + if: ${{ !cancelled() }} + run: | + mkdir -p ~/test_results/${{ matrix.platform }}_integration/${PARTITION_ID}/errorcontains + cp /tmp/error_capture.*.jsonl ~/test_results/${{ matrix.platform }}_integration/${PARTITION_ID}/errorcontains/ 2>/dev/null || true - name: Notify Slack on failure if: failure() uses: ./.github/actions/slack-notify @@ -186,6 +196,11 @@ jobs: mkdir -p ~/test_results/${{ matrix.platform }}_e2e_expect/${PARTITION_ID} NO_BUILD=true TEST_RESULTS=~/test_results/${{ matrix.platform }}_e2e_expect/${PARTITION_ID} \ test/scripts/e2e.sh + - name: Collect errorcontains capture files + if: ${{ !cancelled() }} + run: | + mkdir -p ~/test_results/${{ matrix.platform }}_e2e_expect/${PARTITION_ID}/errorcontains + cp /tmp/error_capture.*.jsonl ~/test_results/${{ matrix.platform }}_e2e_expect/${PARTITION_ID}/errorcontains/ 2>/dev/null || true - name: Notify Slack on failure if: failure() uses: ./.github/actions/slack-notify @@ -239,6 +254,11 @@ jobs: mkdir -p ~/test_results/${{ matrix.platform }}_e2e_subs NO_BUILD=true TEST_RESULTS=~/test_results/${{ matrix.platform }}_e2e_subs \ test/scripts/e2e.sh + - name: Collect errorcontains capture files + if: ${{ !cancelled() }} + run: | + mkdir -p ~/test_results/${{ matrix.platform }}_e2e_subs/errorcontains + cp /tmp/error_capture.*.jsonl ~/test_results/${{ matrix.platform }}_e2e_subs/errorcontains/ 2>/dev/null || true - name: Notify Slack on failure if: failure() uses: ./.github/actions/slack-notify diff --git a/agreement/bundle_test.go b/agreement/bundle_test.go index 2eac91fb41..23cbd3429c 100644 --- a/agreement/bundle_test.go +++ b/agreement/bundle_test.go @@ -90,7 +90,7 @@ func TestBundleCreationWithZeroVotes(t *testing.T) { makeBundlePanicWrapper(t, "makeBundle: no votes present in bundle (len(equivocationVotes) =", proposal, votes, nil) bundle, err := ub.verify(context.Background(), ledger, avv) - require.Error(t, err) + require.ErrorContains(t, err, `unauthenticatedBundle.verify: b.Step = 0`) bundles = append(bundles, bundle) } @@ -248,38 +248,38 @@ func TestBundleCreationWithEquivocationVotes(t *testing.T) { voteBadCredBundle := unauthenticatedBundles[0] voteBadCredBundle.Votes[0].Cred = committee.UnauthenticatedCredential{} _, err := voteBadCredBundle.verify(context.Background(), ledger, avv) - require.Error(t, err) + require.ErrorContains(t, err, `sender was not selected`) voteBadSenderBundle := unauthenticatedBundles[1] voteBadSenderBundle.Votes[0].Sender = basics.Address{} _, err = voteBadSenderBundle.verify(context.Background(), ledger, avv) - require.Error(t, err) + require.ErrorContains(t, err, `could not verify FS signature`) voteNoQuorumBundle := unauthenticatedBundles[2] voteNoQuorumBundle.Votes = voteNoQuorumBundle.Votes[:2] voteNoQuorumBundle.EquivocationVotes = voteNoQuorumBundle.EquivocationVotes[:2] _, err = voteNoQuorumBundle.verify(context.Background(), ledger, avv) - require.Error(t, err) + require.ErrorContains(t, err, `bundle: did not see enough votes: 1`) evBadCredBundle := unauthenticatedBundles[3] evBadCredBundle.EquivocationVotes[0].Cred = committee.UnauthenticatedCredential{} _, err = evBadCredBundle.verify(context.Background(), ledger, avv) - require.Error(t, err) + require.ErrorContains(t, err, `sender was not selected`) evBadEVBundle := unauthenticatedBundles[4] evBadEVBundle.EquivocationVotes[0].Sigs = [2]crypto.OneTimeSignature{{}, {}} _, err = evBadEVBundle.verify(context.Background(), ledger, avv) - require.Error(t, err) + require.ErrorContains(t, err, `could not verify FS signature`) duplicateVoteBundle := unauthenticatedBundles[5] duplicateVoteBundle.Votes = append(duplicateVoteBundle.Votes, duplicateVoteBundle.Votes[0]) _, err = duplicateVoteBundle.verify(context.Background(), ledger, avv) - require.Error(t, err) + require.ErrorContains(t, err, `was duplicated in bundle`) duplicateEquivocationVoteBundle := unauthenticatedBundles[6] duplicateEquivocationVoteBundle.EquivocationVotes = append(duplicateEquivocationVoteBundle.EquivocationVotes, duplicateEquivocationVoteBundle.EquivocationVotes[0]) _, err = duplicateEquivocationVoteBundle.verify(context.Background(), ledger, avv) - require.Error(t, err) + require.ErrorContains(t, err, `was duplicated in bundle`) } diff --git a/agreement/certificate_test.go b/agreement/certificate_test.go index 99f40cb35c..bd09f6825a 100644 --- a/agreement/certificate_test.go +++ b/agreement/certificate_test.go @@ -178,7 +178,7 @@ func TestCertificateBadCertificateWithFakeDoubleVote(t *testing.T) { avv := MakeAsyncVoteVerifier(nil) defer avv.Quit() - require.Error(t, cert.Authenticate(block, ledger, avv)) + require.ErrorContains(t, cert.Authenticate(block, ledger, avv), `not an equivocation pair`) } func TestCertificateDifferentBlock(t *testing.T) { @@ -204,12 +204,12 @@ func TestCertificateDifferentBlock(t *testing.T) { bundle := unauthenticatedBundle(cert) require.NotEqual(t, Certificate{}, cert) - require.Error(t, cert.claimsToAuthenticate(block)) + require.ErrorContains(t, cert.claimsToAuthenticate(block), `certificate claims to validate the wrong hash`) avv := MakeAsyncVoteVerifier(nil) defer avv.Quit() require.NoError(t, verifyBundleAgainstLedger(bundle, ledger, avv)) - require.Error(t, cert.Authenticate(block, ledger, avv)) + require.ErrorContains(t, cert.Authenticate(block, ledger, avv), `certificate claims to validate the wrong hash`) } func TestCertificateNoCertStep(t *testing.T) { @@ -293,12 +293,12 @@ func TestCertificateCertWrongRound(t *testing.T) { cert := makeCertTesting(block.Digest(), votes, equiVotes) bundle := unauthenticatedBundle(cert) require.NotEqual(t, Certificate{}, cert) - require.Error(t, cert.claimsToAuthenticate(block)) + require.ErrorContains(t, cert.claimsToAuthenticate(block), `certificate claims to validate the wrong round: 1 != 0`) avv := MakeAsyncVoteVerifier(nil) defer avv.Quit() require.NoError(t, verifyBundleAgainstLedger(bundle, ledger, avv)) - require.Error(t, cert.Authenticate(block, ledger, avv)) + require.ErrorContains(t, cert.Authenticate(block, ledger, avv), `certificate claims to validate the wrong round: 1 != 0`) } func TestCertificateCertWithTooFewVotes(t *testing.T) { @@ -363,6 +363,6 @@ func TestCertificateDupVote(t *testing.T) { avv := MakeAsyncVoteVerifier(nil) defer avv.Quit() - require.Error(t, verifyBundleAgainstLedger(bundle, ledger, avv)) - require.Error(t, cert.Authenticate(block, ledger, avv)) + require.ErrorContains(t, verifyBundleAgainstLedger(bundle, ledger, avv), `was duplicated in bundle`) + require.ErrorContains(t, cert.Authenticate(block, ledger, avv), `was duplicated in bundle`) } diff --git a/agreement/fuzzer/dropMessageFilter_test.go b/agreement/fuzzer/dropMessageFilter_test.go index e1d4bb6f78..df42f55645 100644 --- a/agreement/fuzzer/dropMessageFilter_test.go +++ b/agreement/fuzzer/dropMessageFilter_test.go @@ -18,6 +18,7 @@ package fuzzer import ( "encoding/json" + "github.com/algorand/go-algorand/protocol" ) diff --git a/agreement/fuzzer/messageDelayFilter_test.go b/agreement/fuzzer/messageDelayFilter_test.go index 5e5757c4a3..bd971ec1a0 100644 --- a/agreement/fuzzer/messageDelayFilter_test.go +++ b/agreement/fuzzer/messageDelayFilter_test.go @@ -19,6 +19,7 @@ package fuzzer import ( "container/heap" "encoding/json" + "github.com/algorand/go-deadlock" "github.com/algorand/go-algorand/protocol" diff --git a/agreement/fuzzer/messagePriorityQueue_test.go b/agreement/fuzzer/messagePriorityQueue_test.go index c503398076..7308e8f945 100644 --- a/agreement/fuzzer/messagePriorityQueue_test.go +++ b/agreement/fuzzer/messagePriorityQueue_test.go @@ -18,6 +18,7 @@ package fuzzer import ( "container/heap" + "github.com/algorand/go-algorand/protocol" ) diff --git a/agreement/proposalTracker_test.go b/agreement/proposalTracker_test.go index 703bee8775..0e4059e6e5 100644 --- a/agreement/proposalTracker_test.go +++ b/agreement/proposalTracker_test.go @@ -77,7 +77,7 @@ func TestProposalTrackerProposalSeeker(t *testing.T) { assert.Equal(t, s.Lowest, s.lowestIncludingLate) s, effect, err = s.accept(votes[3]) - assert.Error(t, err) + assert.ErrorContains(t, err, `proposalSeeker.accept: credential from`) assert.Equal(t, effect, NoLateCredentialTrackingImpact) assert.False(t, s.Frozen) assert.True(t, s.Filled) @@ -103,7 +103,7 @@ func TestProposalTrackerProposalSeeker(t *testing.T) { assert.Equal(t, s.Lowest, s.lowestIncludingLate) s, effect, err = s.accept(votes[0]) - assert.Error(t, err) + assert.ErrorContains(t, err, `proposalSeeker.accept: seeker is already frozen`) assert.Equal(t, effect, VerifiedBetterLateCredentialForTracking) assert.Equal(t, s.Lowest, lowestBeforeFreeze) assert.True(t, s.Frozen) diff --git a/agreement/proposal_test.go b/agreement/proposal_test.go index ac1463b569..fabb720b9f 100644 --- a/agreement/proposal_test.go +++ b/agreement/proposal_test.go @@ -147,10 +147,10 @@ func TestProposalFunctions(t *testing.T) { require.NoError(t, err) err = proposalValue.matches(encDigest, encDigest) - require.Error(t, err) + require.ErrorContains(t, err, `proposal block digest mismatches payload`) err = proposalValue.matches(digest, digest) - require.Error(t, err) + require.ErrorContains(t, err, `proposal encoding digest mismatches payload`) } } @@ -182,7 +182,7 @@ func TestProposalUnauthenticated(t *testing.T) { // test bad round number proposal, err = unauthenticatedProposal.validate(context.Background(), round+1, ledger, validator) - require.Error(t, err) + require.ErrorContains(t, err, `proposed entry from wrong round: entry.Round() != current: 1 != 2`) proposal, err = unauthenticatedProposal.validate(context.Background(), round, ledger, validator) require.NotNil(t, proposal) require.NoError(t, err) @@ -200,7 +200,7 @@ func TestProposalUnauthenticated(t *testing.T) { unauthenticatedProposal3 := proposal3.u() unauthenticatedProposal3.SeedProof = unauthenticatedProposal.SeedProof _, err = unauthenticatedProposal3.validate(context.Background(), round, ledger, validator) - require.Error(t, err) + require.ErrorContains(t, err, `unable to verify header: seed proof malformed (`) // validate mismatch proposer address between block and unauthenticatedProposal proposal4, _, _ := proposalForBlock(accounts.addresses[accountIndex], accounts.vrfs[accountIndex], testBlockFactory, period, ledger) diff --git a/agreement/pseudonode_test.go b/agreement/pseudonode_test.go index f8ae6548e7..337d6f2147 100644 --- a/agreement/pseudonode_test.go +++ b/agreement/pseudonode_test.go @@ -174,7 +174,7 @@ func TestPseudonode(t *testing.T) { } channels = append(channels, ch) } - assert.Error(t, err, "MakeProposals did not returned an error when being overflowed with requests") + assert.ErrorContains(t, err, `pseudonode input channel is full`, "MakeProposals did not returned an error when being overflowed with requests") persist := make(chan error) close(persist) @@ -186,7 +186,7 @@ func TestPseudonode(t *testing.T) { } channels = append(channels, ch) } - assert.Error(t, err, "MakeVotes did not returned an error when being overflowed with requests") + assert.ErrorContains(t, err, `pseudonode input channel is full`, "MakeVotes did not returned an error when being overflowed with requests") // drain output channels. for _, ch := range channels { @@ -525,7 +525,6 @@ func TestPseudonodeNonEnqueuedTasks(t *testing.T) { channels = append(channels, ch) } enqueuedProposals := len(channels) - require.Error(t, err, "MakeProposals did not returned an error when being overflowed with requests") require.ErrorIs(t, err, errPseudonodeBacklogFull) persist := make(chan error) @@ -538,7 +537,7 @@ func TestPseudonodeNonEnqueuedTasks(t *testing.T) { } channels = append(channels, ch) } - require.Error(t, err, "MakeVotes did not returned an error when being overflowed with requests") + require.ErrorContains(t, err, `pseudonode input channel is full`, "MakeVotes did not returned an error when being overflowed with requests") enqueuedVotes := len(channels) - enqueuedProposals // drain output channels. for _, ch := range channels { diff --git a/agreement/vote_test.go b/agreement/vote_test.go index 9c5f74c8c7..bccb8bd01c 100644 --- a/agreement/vote_test.go +++ b/agreement/vote_test.go @@ -113,37 +113,37 @@ func TestVoteValidation(t *testing.T) { noSig := unauthenticatedVote noSig.Sig = crypto.OneTimeSignature{} _, err = noSig.verify(ledger) - require.Error(t, err) + require.ErrorContains(t, err, `could not verify FS signature`) noCred := unauthenticatedVote noCred.Cred = committee.UnauthenticatedCredential{} _, err = noCred.verify(ledger) - require.Error(t, err) + require.ErrorContains(t, err, `sender was not selected`) badRound := unauthenticatedVote badRound.R.Round++ _, err = badRound.verify(ledger) - require.Error(t, err) + require.ErrorContains(t, err, `could not verify FS signature`) badPeriod := unauthenticatedVote badPeriod.R.Period++ _, err = badPeriod.verify(ledger) - require.Error(t, err) + require.ErrorContains(t, err, `could not verify FS signature`) badStep := unauthenticatedVote badStep.R.Step++ _, err = badStep.verify(ledger) - require.Error(t, err) + require.ErrorContains(t, err, `could not verify FS signature`) badBlockHash := unauthenticatedVote badBlockHash.R.Proposal.BlockDigest = randomBlockHash() _, err = badBlockHash.verify(ledger) - require.Error(t, err) + require.ErrorContains(t, err, `could not verify FS signature`) badProposer := unauthenticatedVote badProposer.R.Proposal.OriginalProposer = basics.Address(randomBlockHash()) _, err = badProposer.verify(ledger) - require.Error(t, err) + require.ErrorContains(t, err, `could not verify FS signature`) } } require.True(t, processedVote, "No votes were processed") @@ -194,7 +194,7 @@ func TestVoteReproposalValidation(t *testing.T) { badReproposalVote, err := makeVote(rv, otSecrets[i], vrfSecrets[i], ledger) require.NoError(t, err) _, err = badReproposalVote.verify(ledger) - require.Error(t, err) + require.ErrorContains(t, err, `unauthenticatedVote.verify: proposal-vote sender mismatches with proposal-value`) // bad period-1 reproposal for a period 2 original proposal rv = rawVote{Sender: address, Round: round, Period: per, Step: step(0), Proposal: proposal} @@ -203,7 +203,7 @@ func TestVoteReproposalValidation(t *testing.T) { badReproposalVote, err = makeVote(rv, otSecrets[i], vrfSecrets[i], ledger) require.NoError(t, err) _, err = badReproposalVote.verify(ledger) - require.Error(t, err) + require.ErrorContains(t, err, `unauthenticatedVote.verify: proposal-vote in period 1 claims to repropose block from future period 2`) } } require.True(t, processedVote, "No votes were processed") @@ -274,7 +274,7 @@ func TestVoteValidationStepCertAndProposalBottom(t *testing.T) { unauthenticatedVote.R.Step = cert unauthenticatedVote.R.Proposal = bottom _, err = unauthenticatedVote.verify(ledger) - require.Error(t, err) + require.ErrorContains(t, err, `unauthenticatedVote.verify: votes from step 2 cannot validate bottom`) } } @@ -343,7 +343,7 @@ func TestEquivocationVoteValidation(t *testing.T) { // check for same vote _, err = evSameVote.verify(ledger) - require.Error(t, err) + require.ErrorContains(t, err, `isEquivocationPair: not an equivocation pair: identical vote (block hash`) // test vote accessors v0 := aev.v0() @@ -357,42 +357,42 @@ func TestEquivocationVoteValidation(t *testing.T) { noSig := ev noSig.Sigs = [2]crypto.OneTimeSignature{{}, {}} _, err = noSig.verify(ledger) - require.Error(t, err) + require.ErrorContains(t, err, `could not verify FS signature`) noCred := ev noCred.Cred = committee.UnauthenticatedCredential{} _, err = noCred.verify(ledger) - require.Error(t, err) + require.ErrorContains(t, err, `sender was not selected`) badRound := ev badRound.Round++ _, err = badRound.verify(ledger) - require.Error(t, err) + require.ErrorContains(t, err, `could not verify FS signature`) badPeriod := ev badPeriod.Period++ _, err = badPeriod.verify(ledger) - require.Error(t, err) + require.ErrorContains(t, err, `could not verify FS signature`) badStep := ev badStep.Step++ _, err = badStep.verify(ledger) - require.Error(t, err) + require.ErrorContains(t, err, `could not verify FS signature`) badBlockHash1 := ev badBlockHash1.Proposals[0].BlockDigest = randomBlockHash() _, err = badBlockHash1.verify(ledger) - require.Error(t, err) + require.ErrorContains(t, err, `could not verify FS signature`) badBlockHash2 := ev badBlockHash2.Proposals[1].BlockDigest = randomBlockHash() _, err = badBlockHash2.verify(ledger) - require.Error(t, err) + require.ErrorContains(t, err, `could not verify FS signature`) badSender := ev badSender.Sender = basics.Address{} _, err = badSender.verify(ledger) - require.Error(t, err) + require.ErrorContains(t, err, `unauthenticatedEquivocationVote.verify: failed to verify pair 0: unauthenticatedVote.verify: could not verify FS signature on vote by AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY5HFKQ given`) } } require.True(t, processedVote, "No votes were processed") diff --git a/catchup/ledgerFetcher_test.go b/catchup/ledgerFetcher_test.go index 521b435413..7c6fa3cff0 100644 --- a/catchup/ledgerFetcher_test.go +++ b/catchup/ledgerFetcher_test.go @@ -57,7 +57,7 @@ func TestNonParsableAddress(t *testing.T) { lf := makeLedgerFetcher(&mocks.MockNetwork{}, &mocks.MockCatchpointCatchupAccessor{}, logging.TestingLog(t), &dummyLedgerFetcherReporter{}, config.GetDefaultLocal()) peer := testHTTPPeer(":def") err := lf.getPeerLedger(context.Background(), &peer, basics.Round(0)) - require.Error(t, err) + require.ErrorContains(t, err, `Get "/v1/mocknet/ledger/0": parse ":def": missing protocol scheme`) } func TestLedgerFetcherErrorResponseHandling(t *testing.T) { diff --git a/catchup/universalFetcher_test.go b/catchup/universalFetcher_test.go index 9d7c4d3e7e..955aa06a5b 100644 --- a/catchup/universalFetcher_test.go +++ b/catchup/universalFetcher_test.go @@ -73,7 +73,7 @@ func TestUGetBlockWs(t *testing.T) { block, cert, duration, err = fetcher.fetchBlock(context.Background(), next+1, up) - require.Error(t, err) + require.ErrorContains(t, err, `no block available for given round`) var noBlockErr noBlockForRoundError require.ErrorAs(t, err, &noBlockErr) require.Equal(t, next+1, err.(noBlockForRoundError).round) @@ -138,8 +138,7 @@ func TestUGetBlockUnsupported(t *testing.T) { fetcher := universalBlockFetcher{} peer := "" block, cert, duration, err := fetcher.fetchBlock(context.Background(), 1, peer) - require.Error(t, err) - require.Contains(t, err.Error(), "fetchBlock: UniversalFetcher only supports HTTPPeer and UnicastPeer") + require.ErrorContains(t, err, "fetchBlock: UniversalFetcher only supports HTTPPeer and UnicastPeer") require.Nil(t, block) require.Nil(t, cert) require.Equal(t, int64(duration), int64(0)) diff --git a/cmd/goal/node_test.go b/cmd/goal/node_test.go index b743d8a640..2c81977482 100644 --- a/cmd/goal/node_test.go +++ b/cmd/goal/node_test.go @@ -99,8 +99,7 @@ func TestGetMissingCatchpointLabel(t *testing.T) { label, err := getMissingCatchpointLabel(test.URL) if test.expectedErr != "" { - require.Error(t, err) - require.Contains(t, err.Error(), test.expectedErr) + require.ErrorContains(t, err, test.expectedErr) } else { _, _, err = ledgercore.ParseCatchpointLabel(label) assert.Equal(t, err, nil) diff --git a/cmd/tealdbg/cdtSession_test.go b/cmd/tealdbg/cdtSession_test.go index 83b9ca815e..8980921d78 100644 --- a/cmd/tealdbg/cdtSession_test.go +++ b/cmd/tealdbg/cdtSession_test.go @@ -91,7 +91,7 @@ func TestCdtSessionProto11Common(t *testing.T) { req.Method = "Debugger.getScriptSource" req.Params = map[string]interface{}{} resp, events, err = s.handleCdtRequest(&req, &state) - require.Error(t, err) + require.ErrorContains(t, err, `getScriptSource failed: no scriptId`) require.Equal(t, 0, len(events)) require.Empty(t, resp.Result) require.Empty(t, resp.ID) @@ -139,7 +139,7 @@ func TestCdtSessionProto11Breakpoints(t *testing.T) { req.Method = "Debugger.removeBreakpoint" req.Params = map[string]interface{}{"breakpointId": "test"} resp, events, err = s.handleCdtRequest(&req, &state) - require.Error(t, err) + require.ErrorContains(t, err, `strconv.Atoi: parsing "test": invalid syntax`) require.Equal(t, 0, len(events)) require.Empty(t, resp.ID) require.Empty(t, resp.Result) @@ -301,7 +301,7 @@ func TestCdtSessionProto11Evaluate(t *testing.T) { req.Params = map[string]interface{}{} resp, events, err = s.handleCdtRequest(&req, &state) - require.Error(t, err) + require.ErrorContains(t, err, `evaluate failed: no expression`) require.Equal(t, 0, len(events)) require.Empty(t, resp.ID) require.Empty(t, resp.Result) @@ -323,15 +323,15 @@ func TestCdtSessionProto11CallOnFunc(t *testing.T) { req.Method = "Runtime.callFunctionOn" req.Params = map[string]interface{}{} resp, events, err := s.handleCdtRequest(&req, &state) - require.Error(t, err) + require.ErrorContains(t, err, `callFunctionOn failed: no objectId`) req.Params = map[string]interface{}{"objectId": ""} resp, events, err = s.handleCdtRequest(&req, &state) - require.Error(t, err) + require.ErrorContains(t, err, `callFunctionOn failed: no functionDeclaration`) req.Params = map[string]interface{}{"objectId": "", "functionDeclaration": ""} resp, events, err = s.handleCdtRequest(&req, &state) - require.Error(t, err) + require.ErrorContains(t, err, `callFunctionOn failed: no arguments`) req.Params = map[string]interface{}{"objectId": "", "functionDeclaration": "", "arguments": []interface{}{}} resp, events, err = s.handleCdtRequest(&req, &state) @@ -414,11 +414,11 @@ func TestCdtSessionProto11GetProps(t *testing.T) { req.Method = "Runtime.getProperties" req.Params = map[string]interface{}{} resp, events, err := s.handleCdtRequest(&req, &state) - require.Error(t, err) + require.ErrorContains(t, err, `getProperties failed: no objectId`) req.Params = map[string]interface{}{"objectId": "", "generatePreview": true} resp, events, err = s.handleCdtRequest(&req, &state) - require.Error(t, err) + require.ErrorContains(t, err, `getObjectDescriptor error: unk object id`) req.Params = map[string]interface{}{"objectId": globalScopeObjID, "generatePreview": true} s.verbose = true @@ -586,7 +586,7 @@ func TestCdtSessionGetObjects(t *testing.T) { req.Method = "Runtime.getProperties" req.Params = map[string]interface{}{} resp, events, err := s.handleCdtRequest(&req, &state) - require.Error(t, err) + require.ErrorContains(t, err, `getProperties failed: no objectId`) for k := range objectDescMap { req.Params = map[string]interface{}{"objectId": k, "generatePreview": true} diff --git a/cmd/tealdbg/local_test.go b/cmd/tealdbg/local_test.go index f789253768..d02b263ae5 100644 --- a/cmd/tealdbg/local_test.go +++ b/cmd/tealdbg/local_test.go @@ -595,7 +595,7 @@ func TestDebugFromPrograms(t *testing.T) { } err := l.Setup(&dp) - a.Error(err) + require.ErrorContains(t, err, `invalid group index 1 for a single transaction`) a.Contains(err.Error(), "invalid group index 1 for a single transaction") dp = DebugParams{ @@ -606,7 +606,7 @@ func TestDebugFromPrograms(t *testing.T) { } err = l.Setup(&dp) - a.Error(err) + require.ErrorContains(t, err, `invalid group index 3 for a txn in a transaction group of 2`) a.Contains(err.Error(), "invalid group index 3 for a txn in a transaction group of 2") dp = DebugParams{ @@ -618,7 +618,7 @@ func TestDebugFromPrograms(t *testing.T) { } err = l.Setup(&dp) - a.Error(err) + require.ErrorContains(t, err, `unknown run mode`) a.Contains(err.Error(), "unknown run mode") dp = DebugParams{ @@ -787,7 +787,7 @@ func TestDebugFromTxn(t *testing.T) { } err = l.Setup(&dp) - a.Error(err) + require.ErrorContains(t, err, `no programs found in transactions`) a.Contains(err.Error(), "no programs found in transactions") a.Equal(2, len(l.txnGroup)) @@ -897,7 +897,7 @@ func TestDebugFromTxn(t *testing.T) { } err = l.Setup(&dp) - a.Error(err) + require.ErrorContains(t, err, `no programs found in transactions`) a.Equal(2, len(l.txnGroup)) } diff --git a/config/config_test.go b/config/config_test.go index c6058199af..03308034eb 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -271,7 +271,7 @@ func TestLocal_ConfigMigrate(t *testing.T) { cLatest.Version = getLatestConfigVersion() + 1 _, _, err = migrate(cLatest) - a.Error(err) + require.ErrorContains(t, err, `unexpected config version: 38`) // Ensure we don't migrate values that aren't the default old version c0Modified := GetVersionedDefaultLocalConfig(0) @@ -355,7 +355,7 @@ func TestLocal_ConfigMigrateFromDisk(t *testing.T) { cNext := Local{Version: getLatestConfigVersion() + 1} _, _, err = migrate(cNext) - a.Error(err) + require.ErrorContains(t, err, `unexpected config version: 38`) } // Verify that nobody is changing the shipping default configurations @@ -999,7 +999,7 @@ func TestEnsureAndResolveGenesisDirs_migrateCrashErr(t *testing.T) { require.NoError(t, err) // Resolve paths, err := cfg.EnsureAndResolveGenesisDirs(testDirectory, "myGenesisID", tLogger{t: t}) - require.Error(t, err) + require.ErrorContains(t, err, `error moving crash DB files from ColdDataDir`) require.Empty(t, paths) // Confirm that crash.sqlite was not moved to HotDataDir require.FileExists(t, filepath.Join(coldDir, "crash.sqlite")) @@ -1031,7 +1031,7 @@ func TestEnsureAndResolveGenesisDirs_migrateSPErr(t *testing.T) { require.NoError(t, err) // Resolve paths, err := cfg.EnsureAndResolveGenesisDirs(testDirectory, "myGenesisID", tLogger{t: t}) - require.Error(t, err) + require.ErrorContains(t, err, `error moving stateproof DB files from ColdDataDir`) require.Empty(t, paths) // Confirm that stateproof.sqlite was not moved to HotDataDir require.FileExists(t, filepath.Join(coldDir, "stateproof.sqlite")) @@ -1057,16 +1057,14 @@ func TestEnsureAndResolveGenesisDirsError(t *testing.T) { // first try an error with an empty root dir paths, err := cfg.EnsureAndResolveGenesisDirs("", "myGenesisID", tLogger{t: t}) require.Empty(t, paths) - require.Error(t, err) - require.Contains(t, err.Error(), "rootDir is required") + require.ErrorContains(t, err, "rootDir is required") require.NoError(t, os.Chmod(testDirectory, 0200)) // now try an error with a root dir that can't be written to paths, err = cfg.EnsureAndResolveGenesisDirs(testDirectory, "myGenesisID", tLogger{t: t}) require.Empty(t, paths) - require.Error(t, err) - require.Contains(t, err.Error(), "permission denied") + require.ErrorContains(t, err, "permission denied") } // TestResolveLogPaths confirms that log paths are resolved to the most appropriate data directory of the supplied config diff --git a/config/dnsbootstrap_test.go b/config/dnsbootstrap_test.go index 1e12b64f0d..a629760266 100644 --- a/config/dnsbootstrap_test.go +++ b/config/dnsbootstrap_test.go @@ -17,11 +17,12 @@ package config import ( - "github.com/algorand/go-algorand/protocol" - "pgregory.net/rapid" "strings" "testing" + "github.com/algorand/go-algorand/protocol" + "pgregory.net/rapid" + "github.com/algorand/go-algorand/internal/rapidgen" "github.com/algorand/go-algorand/test/partitiontest" "github.com/stretchr/testify/assert" diff --git a/crypto/batchverifier_test.go b/crypto/batchverifier_test.go index ac91c19023..944b78b8b4 100644 --- a/crypto/batchverifier_test.go +++ b/crypto/batchverifier_test.go @@ -80,7 +80,7 @@ func testBatchVerifierSingle(t *testing.T, makeBV func(int) BatchVerifier) { // break the signature: sig[0] = sig[0] + 1 bv.EnqueueSignature(sigSecrets.SignatureVerifier, msg, sig) - require.Error(t, bv.Verify()) + require.ErrorContains(t, bv.Verify(), `At least one signature didn't pass verification`) } func TestBatchVerifierBulk(t *testing.T) { @@ -147,7 +147,7 @@ func testBatchVerifierWithInvalidSignature(t *testing.T, makeBV func(int) BatchV sig[0] = sig[0] + 1 bv.EnqueueSignature(sigSecrets.SignatureVerifier, msg, sig) - require.Error(t, bv.Verify()) + require.ErrorContains(t, bv.Verify(), `At least one signature didn't pass verification`) } func BenchmarkBatchVerifier(b *testing.B) { diff --git a/crypto/falconWrapper_test.go b/crypto/falconWrapper_test.go index 4d8720e9f9..66432c01e4 100644 --- a/crypto/falconWrapper_test.go +++ b/crypto/falconWrapper_test.go @@ -70,7 +70,7 @@ func TestFalconCanHandleNilSignature(t *testing.T) { a.NoError(err) err = key.GetVerifyingKey().VerifyBytes([]byte("Test"), nil) - a.Error(err) + require.ErrorContains(t, err, `empty signature: falcon verify failed`) } func TestVerificationBytes(t *testing.T) { diff --git a/crypto/gobatchverifier_test.go b/crypto/gobatchverifier_test.go index 884e65eb44..48ff504361 100644 --- a/crypto/gobatchverifier_test.go +++ b/crypto/gobatchverifier_test.go @@ -214,7 +214,7 @@ func testBatchVectors(t *testing.T, makeBV func(int) BatchVerifier, testVectors } failed, err := bv.VerifyWithFeedback() if slices.Contains(expFail, true) { // some failures expected - require.Error(t, err) + require.ErrorContains(t, err, `At least one signature didn't pass verification`) require.NotNil(t, failed) require.Len(t, failed, len(vecs)) for i := range expFail { diff --git a/crypto/hashes_test.go b/crypto/hashes_test.go index 8999834423..15bc815bdd 100644 --- a/crypto/hashes_test.go +++ b/crypto/hashes_test.go @@ -68,7 +68,7 @@ func TestEmptyHash(t *testing.T) { var msg [4]byte len, err := hash.Write(msg[:]) a.Equal(0, len) - a.Error(err) + require.ErrorContains(t, err, `unknown hash type`) a.Equal(0, hash.BlockSize()) var emptySlice []byte diff --git a/crypto/merklearray/merkle_test.go b/crypto/merklearray/merkle_test.go index b84beef624..8766297f1b 100644 --- a/crypto/merklearray/merkle_test.go +++ b/crypto/merklearray/merkle_test.go @@ -185,7 +185,7 @@ func TestErrorInMarshal(t *testing.T) { a := nonmarshalable{1} _, err := Build(&a, crypto.HashFactory{}) - require.Error(t, err) + require.ErrorContains(t, err, `can't be marshaled`) } func TestMerkleBuildEdgeCases(t *testing.T) { @@ -235,7 +235,6 @@ func TestMerkleProveEdgeCases(t *testing.T) { a.NoError(err) _, err = tree.Prove([]uint64{4}) - a.Error(err) require.ErrorIs(t, err, ErrPosOutOfBound) // prove on nothing @@ -249,7 +248,6 @@ func TestMerkleProveEdgeCases(t *testing.T) { a.NoError(err) _, err = tree.Prove([]uint64{0}) - a.Error(err) require.ErrorIs(t, err, ErrProvingZeroCommitment) // prove on nothing - now the tree is empty as well @@ -272,12 +270,10 @@ func TestMerkleVCProveEdgeCases(t *testing.T) { // element in the out of the inner array _, err = tree.Prove([]uint64{5}) - a.Error(err) require.ErrorIs(t, err, ErrPosOutOfBound) // element in the padded array - bottom leaf _, err = tree.Prove([]uint64{8}) - a.Error(err) require.ErrorIs(t, err, ErrPosOutOfBound) // prove on nothing @@ -291,7 +287,6 @@ func TestMerkleVCProveEdgeCases(t *testing.T) { a.NoError(err) _, err = tree.Prove([]uint64{0}) - a.Error(err) require.ErrorIs(t, err, ErrProvingZeroCommitment) // prove on nothing - now the tree is empty as well @@ -318,15 +313,12 @@ func TestMerkleVerifyEdgeCases(t *testing.T) { root := tree.Root() err = Verify(root, map[uint64]crypto.Hashable{4: arr[3]}, proof) - a.Error(err) require.ErrorIs(t, err, ErrPosOutOfBound) err = Verify(root, map[uint64]crypto.Hashable{3: arr[3], 4: arr[3]}, proof) - a.Error(err) require.ErrorIs(t, err, ErrPosOutOfBound) err = Verify(root, nil, nil) - a.Error(err) a.ErrorIs(ErrProofIsNil, err) trivialProof := Proof{TreeDepth: 2, HashFactory: crypto.HashFactory{HashType: crypto.Sha512_256}} @@ -334,7 +326,6 @@ func TestMerkleVerifyEdgeCases(t *testing.T) { a.NoError(err) err = Verify(root, nil, proof) - a.Error(err) a.ErrorIs(ErrNonEmptyProofForEmptyElements, err) err = Verify(root, nil, &trivialProof) @@ -402,15 +393,12 @@ func TestMerkleVCVerifyEdgeCases(t *testing.T) { root := tree.Root() err = VerifyVectorCommitment(root, map[uint64]crypto.Hashable{4: arr[3]}, proof) - a.Error(err) require.ErrorIs(t, err, ErrPosOutOfBound) err = VerifyVectorCommitment(root, map[uint64]crypto.Hashable{3: arr[3], 4: arr[3]}, proof) - a.Error(err) require.ErrorIs(t, err, ErrPosOutOfBound) err = VerifyVectorCommitment(root, nil, nil) - a.Error(err) a.ErrorIs(ErrProofIsNil, err) trivialProof := Proof{TreeDepth: 2, HashFactory: crypto.HashFactory{HashType: crypto.Sha512_256}} @@ -418,7 +406,6 @@ func TestMerkleVCVerifyEdgeCases(t *testing.T) { a.NoError(err) err = VerifyVectorCommitment(root, nil, proof) - a.Error(err) a.ErrorIs(ErrNonEmptyProofForEmptyElements, err) err = VerifyVectorCommitment(root, nil, &trivialProof) @@ -447,7 +434,7 @@ func TestGenericDigest(t *testing.T) { require.NoError(t, err) err = testWithSize(t, crypto.MaxHashDigestSize+1) - require.Error(t, err) + require.ErrorContains(t, err, `msgp: length overflow: 65 > 64 at Path/0`) } func testWithSize(t *testing.T, size int) error { diff --git a/crypto/merklearray/vectorCommitmentArray_test.go b/crypto/merklearray/vectorCommitmentArray_test.go index 136609df78..1adb14ee3a 100644 --- a/crypto/merklearray/vectorCommitmentArray_test.go +++ b/crypto/merklearray/vectorCommitmentArray_test.go @@ -106,7 +106,6 @@ func TestIndexOutOfBounds(t *testing.T) { require.Equal(t, uint64(1), lsbIndex) lsbIndex, err = merkleTreeToVectorCommitmentIndex(2, pathLen) - require.Error(t, err) require.ErrorIs(t, err, ErrPosOutOfBound) pathLen = 4 @@ -115,7 +114,6 @@ func TestIndexOutOfBounds(t *testing.T) { require.Equal(t, uint64(15), lsbIndex) lsbIndex, err = merkleTreeToVectorCommitmentIndex(16, pathLen) - require.Error(t, err) require.ErrorIs(t, err, ErrPosOutOfBound) } diff --git a/crypto/merklesignature/merkleSignatureScheme_test.go b/crypto/merklesignature/merkleSignatureScheme_test.go index 83515099df..d6d3d1579e 100644 --- a/crypto/merklesignature/merkleSignatureScheme_test.go +++ b/crypto/merklesignature/merkleSignatureScheme_test.go @@ -66,13 +66,11 @@ func TestSignerCreation(t *testing.T) { signer = generateTestSigner(2, 2, 3, a) a.Equal(0, length(signer, a)) _, err = signer.GetSigner(2).SignBytes(genMsgForTest()) - a.Error(err) a.ErrorIs(err, ErrNoStateProofKeyForRound) signer = generateTestSigner(11, 19, 10, a) a.Equal(0, length(signer, a)) _, err = signer.GetSigner(2).SignBytes(genMsgForTest()) - a.Error(err) a.ErrorIs(err, ErrNoStateProofKeyForRound) // Make sure both rounds 10 and 11 can be signed (as key for round 10 is valid for both) @@ -100,11 +98,9 @@ func TestSignerCreationOutOfBounds(t *testing.T) { partitiontest.PartitionTest(t) a := require.New(t) _, err := New(8, 4, 1) - a.Error(err) a.ErrorIs(err, ErrStartBiggerThanEndRound) _, err = New(1, 8, 0) - a.Error(err) a.ErrorIs(err, ErrKeyLifetimeIsZero) } @@ -148,11 +144,9 @@ func TestEmptySigner(t *testing.T) { a.Equal(0, length(signer, a)) _, err = signer.GetSigner(8).SignBytes(h) - a.Error(err) a.ErrorIs(err, ErrNoStateProofKeyForRound) _, err = signer.GetSigner(9).SignBytes(h) - a.Error(err) a.ErrorIs(err, ErrNoStateProofKeyForRound) } @@ -263,11 +257,9 @@ func TestSigning(t *testing.T) { a.NoError(signer.GetVerifier().VerifyBytes(start, msg, &sig)) _, err = signer.GetSigner(start - 1).SignBytes(msg) - a.Error(err) a.ErrorIs(err, ErrNoStateProofKeyForRound) _, err = signer.GetSigner(end + 1).SignBytes(msg) - a.Error(err) a.ErrorIs(err, ErrNoStateProofKeyForRound) signer = generateTestSigner(start, end, 10, a) @@ -290,7 +282,6 @@ func TestSigning(t *testing.T) { i := uint64(50) for ; i < 60; i++ { // no key for these rounds (key for round 48 was not generated) _, err = signer.GetSigner(i).SignBytes(msg) - a.Error(err) a.ErrorIs(err, ErrNoStateProofKeyForRound) } for ; i < 100; i++ { @@ -317,16 +308,13 @@ func TestBadRound(t *testing.T) { msg, sig := makeSig(signer, start, a) err := signer.GetVerifier().VerifyBytes(start+1, msg, &sig) - a.Error(err) a.ErrorIs(err, ErrSignatureSchemeVerificationFailed) msg, sig = makeSig(signer, start+1, a) err = signer.GetVerifier().VerifyBytes(start, msg, &sig) - a.Error(err) a.ErrorIs(err, ErrSignatureSchemeVerificationFailed) err = signer.GetVerifier().VerifyBytes(start+2, msg, &sig) - a.Error(err) a.ErrorIs(err, ErrSignatureSchemeVerificationFailed) a.ErrorIs(err, ErrSignatureSchemeVerificationFailed) } @@ -342,7 +330,6 @@ func TestBadMerkleProofInSignature(t *testing.T) { sig2 := copySig(sig) sig2.Proof.Path = sig2.Proof.Path[:len(sig2.Proof.Path)-1] err := signer.GetVerifier().VerifyBytes(start, msg, &sig2) - a.Error(err) a.ErrorIs(err, ErrSignatureSchemeVerificationFailed) sig3 := copySig(sig) @@ -350,7 +337,6 @@ func TestBadMerkleProofInSignature(t *testing.T) { rand.Read(someDigest[:]) sig3.Proof.Path[0] = someDigest[:] err = signer.GetVerifier().VerifyBytes(start, msg, &sig3) - a.Error(err) a.ErrorIs(err, ErrSignatureSchemeVerificationFailed) } @@ -380,7 +366,6 @@ func TestIncorrectByteSignature(t *testing.T) { sig2.Signature = bs err := signer.GetVerifier().VerifyBytes(start, msg, &sig2) - a.Error(err) a.ErrorIs(err, ErrSignatureSchemeVerificationFailed) } @@ -399,16 +384,13 @@ func TestIncorrectMerkleIndex(t *testing.T) { sig.VectorCommitmentIndex = 0 err = signer.GetVerifier().VerifyBytes(20, h, &sig) - a.Error(err) a.ErrorIs(err, ErrSignatureSchemeVerificationFailed) sig.VectorCommitmentIndex = math.MaxUint64 err = signer.GetVerifier().VerifyBytes(20, h, &sig) - a.Error(err) a.ErrorIs(err, ErrSignatureSchemeVerificationFailed) err = signer.GetVerifier().VerifyBytes(20, h, &sig) - a.Error(err) a.ErrorIs(err, ErrSignatureSchemeVerificationFailed) } @@ -429,7 +411,6 @@ func TestAttemptToUseDifferentKey(t *testing.T) { sig2.VerifyingKey = *(key.GetVerifyingKey()) err := signer.GetVerifier().VerifyBytes(start+1, msg, &sig2) - a.Error(err) a.ErrorIs(err, ErrSignatureSchemeVerificationFailed) } diff --git a/crypto/multisig_test.go b/crypto/multisig_test.go index 2527fda740..9310426dc4 100644 --- a/crypto/multisig_test.go +++ b/crypto/multisig_test.go @@ -63,7 +63,7 @@ func TestMultisigAddr(t *testing.T) { // test if invalid threshold can be detected // #keys= 2 < threshold = 3 _, err = MultisigAddrGen(version, threshold, pks) - require.Error(t, err, "MultisigAddr: unable to detect invalid threshold (keys == %d, threshold == %d)", len(pks), threshold) + require.ErrorContains(t, err, `Invalid threshold`, "MultisigAddr: unable to detect invalid threshold (keys == %d, threshold == %d)", len(pks), threshold) // #keys = 3 == threshold = 3 pks = append(pks, secrets[2].SignatureVerifier) _, err = MultisigAddrGen(version, threshold, pks) @@ -111,10 +111,10 @@ func TestMultisig(t *testing.T) { // now testing signing functions // check if invalid version can be detected _, err = MultisigSign(txid, addr, version+1, threshold, pks, *secrets[0]) - require.Error(t, err, "should be able to detect invalid version number") + require.ErrorContains(t, err, `unknown version`, "should be able to detect invalid version number") // check if invalid secret key can be detected _, err = MultisigSign(txid, addr, version, threshold, pks, *secrets[4]) - require.Error(t, err, "should be able to detect invalid secret key used") + require.ErrorContains(t, err, `Key does not exist`, "should be able to detect invalid secret key used") // test assembling // test1: assemble a single signature -- should return failure @@ -122,7 +122,7 @@ func TestMultisig(t *testing.T) { sigs[0], err = MultisigSign(txid, addr, version, threshold, pks, *secrets[3]) require.NoError(t, err, "Multisig: unexpected failure in multisig signing") _, err = MultisigAssemble(sigs) - require.Error(t, err, "should be able to detect insufficient signatures for assembling") + require.ErrorContains(t, err, `invalid number of signatures to assemble`, "should be able to detect insufficient signatures for assembling") // test2: assemble 3 signatures // signing three signatures with pk0, pk1 and pk2 @@ -249,10 +249,10 @@ func TestEmptyMultisig(t *testing.T) { require.NoError(t, err, "Multisig: unexpected failure generating message digest") emptyMutliSig := MultisigSig{Version: version, Threshold: threshold, Subsigs: make([]MultisigSubsig, 0)} err = MultisigVerify(txid, addr, emptyMutliSig) - require.Error(t, err, "Multisig: did not return error as expected") + require.ErrorContains(t, err, `Invalid number of signatures`, "Multisig: did not return error as expected") br := MakeBatchVerifier() err = MultisigBatchPrep(txid, addr, emptyMutliSig, br) - require.Error(t, err, "Multisig: did not return error as expected") + require.ErrorContains(t, err, `Invalid number of signatures`, "Multisig: did not return error as expected") } func TestIncorrectAddrresInMultisig(t *testing.T) { @@ -275,10 +275,10 @@ func TestIncorrectAddrresInMultisig(t *testing.T) { require.NoError(t, err, "Multisig: could not create mutlisig") addr[0] = addr[0] + 1 err = MultisigVerify(txid, addr, MutliSig) - require.Error(t, err, "Multisig: did not return error as expected") + require.ErrorContains(t, err, `Invalid address`, "Multisig: did not return error as expected") br := MakeBatchVerifier() err = MultisigBatchPrep(txid, addr, MutliSig, br) - require.Error(t, err, "Multisig: did not return error as expected") + require.ErrorContains(t, err, `Invalid address`, "Multisig: did not return error as expected") } @@ -312,10 +312,10 @@ func TestMoreThanMaxSigsInMultisig(t *testing.T) { msig, err := MultisigAssemble(sigs) require.NoError(t, err, "Multisig: error assemble multisig") err = MultisigVerify(txid, addr, msig) - require.Error(t, err, "Multisig: did not return error as expected") + require.ErrorContains(t, err, `Invalid number of signatures`, "Multisig: did not return error as expected") br := MakeBatchVerifier() err = MultisigBatchPrep(txid, addr, msig, br) - require.Error(t, err, "Multisig: did not return error as expected") + require.ErrorContains(t, err, `Invalid number of signatures`, "Multisig: did not return error as expected") } func TestOneSignatureIsEmpty(t *testing.T) { @@ -349,10 +349,10 @@ func TestOneSignatureIsEmpty(t *testing.T) { require.NoError(t, err, "Multisig: error assemble multisig") msig.Subsigs[0].Sig = Signature{} err = MultisigVerify(txid, addr, msig) - require.Error(t, err, "Multisig: did not return error as expected") + require.ErrorContains(t, err, `Invalid number of signatures`, "Multisig: did not return error as expected") br := MakeBatchVerifier() err = MultisigBatchPrep(txid, addr, msig, br) - require.Error(t, err, "Multisig: did not return error as expected") + require.ErrorContains(t, err, `Invalid number of signatures`, "Multisig: did not return error as expected") } // in this test we want to test what happen if one of the signatures are not valid. @@ -388,12 +388,12 @@ func TestOneSignatureIsInvalid(t *testing.T) { msig, err := MultisigAssemble(sigs) require.NoError(t, err, "Multisig: error assemble multisig") err = MultisigVerify(txid, addr, msig) - require.Error(t, err, "Multisig: did not return error as expected") + require.ErrorContains(t, err, `At least one signature didn't pass verification`, "Multisig: did not return error as expected") br := MakeBatchVerifier() err = MultisigBatchPrep(txid, addr, msig, br) require.NoError(t, err, "Multisig: did not return error as expected") res := br.Verify() - require.Error(t, res, "Multisig: batch verification passed on broken signature") + require.ErrorContains(t, res, `At least one signature didn't pass verification`, "Multisig: batch verification passed on broken signature") } @@ -440,12 +440,12 @@ func TestMultisigLessThanTrashold(t *testing.T) { require.NoError(t, err, "should be able to detect insufficient signatures for assembling") msig.Subsigs[1].Sig = BlankSignature err = MultisigVerify(txid, addr, msig) - require.Error(t, err, "Multisig: expected verification failure with err") + require.ErrorContains(t, err, `Invalid number of signatures`, "Multisig: expected verification failure with err") msig, err = MultisigAssemble(sigs) require.NoError(t, err, "should be able to detect insufficient signatures for assembling") msig.Subsigs = msig.Subsigs[:len(msig.Subsigs)-1] err = MultisigVerify(txid, addr, msig) - require.Error(t, err, "Multisig: expected verification failure with err") + require.ErrorContains(t, err, `Invalid address`, "Multisig: expected verification failure with err") } diff --git a/crypto/passphrase/passphrase_test.go b/crypto/passphrase/passphrase_test.go index 66a9ae51fd..e7c218d128 100644 --- a/crypto/passphrase/passphrase_test.go +++ b/crypto/passphrase/passphrase_test.go @@ -61,7 +61,7 @@ func TestWordNotInList(t *testing.T) { mn := "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon zzz invest" _, err := MnemonicToKey(mn) - require.Error(t, err) + require.ErrorContains(t, err, `zzz is not in the words list`) return } @@ -77,7 +77,7 @@ func TestCorruptedChecksum(t *testing.T) { // Shuffle the last word (last 11 bits of checksum) wl[len(wl)-1] = wordlist[(indexOf(wordlist, lastWord)+1)%len(wordlist)] recovered, err := MnemonicToKey(strings.Join(wl, sepStr)) - require.Error(t, err) + require.ErrorContains(t, err, `checksum failed to validate`) require.Empty(t, recovered) } @@ -90,7 +90,7 @@ func TestInvalidKeyLen(t *testing.T) { _, err := rand.Read(key) require.NoError(t, err) m, err := KeyToMnemonic(key) - require.Error(t, err) + require.ErrorContains(t, err, `key length must be 32 bytes`) require.Empty(t, m) } } diff --git a/crypto/statetrie/nibbles/nibbles_test.go b/crypto/statetrie/nibbles/nibbles_test.go index 0e81bef5e9..974c213d29 100644 --- a/crypto/statetrie/nibbles/nibbles_test.go +++ b/crypto/statetrie/nibbles/nibbles_test.go @@ -77,7 +77,7 @@ func TestNibblesDeserialize(t *testing.T) { t.Parallel() enc := []byte{0x01} _, err := Deserialize(enc) - require.Error(t, err, "should return invalid encoding error") + require.ErrorContains(t, err, `invalid encoding`, "should return invalid encoding error") } func TestNibbles(t *testing.T) { @@ -212,7 +212,7 @@ func TestNibbles(t *testing.T) { } _, e := Deserialize([]byte{}) - require.Error(t, e) + require.ErrorContains(t, e, `invalid encoding`) _, e = Deserialize([]byte{0x02}) - require.Error(t, e) + require.ErrorContains(t, e, `invalid encoding`) } diff --git a/data/account/participationRegistry_test.go b/data/account/participationRegistry_test.go index e2ed12ba82..aabec71f33 100644 --- a/data/account/participationRegistry_test.go +++ b/data/account/participationRegistry_test.go @@ -598,7 +598,7 @@ func TestParticipation_MultipleInsertError(t *testing.T) { _, err := registry.Insert(p) a.NoError(err) _, err = registry.Insert(p) - a.Error(err, ErrAlreadyInserted.Error()) + require.ErrorContains(t, err, ErrAlreadyInserted.Error(), `these participation keys are already inserted`) } // This is a contrived test on every level. To workaround errors we setup the @@ -687,7 +687,7 @@ func TestParticipation_RecordMultipleUpdates_DB(t *testing.T) { err = registry.Register(id, 1) a.NoError(err) err = registry.Flush(defaultTimeout) - a.Error(err) + require.ErrorContains(t, err, `: multiple valid keys found for the same participationID`) a.Contains(err.Error(), "unable to disable old key") a.EqualError(errors.Unwrap(err), ErrMultipleKeysForID.Error()) @@ -934,9 +934,9 @@ func TestAddStateProofKeys(t *testing.T) { a.NoError(err) _, err = registry.GetStateProofSecretsForRound(id, basics.Round(1)) - a.Error(err) + require.ErrorContains(t, err, `failed to fetch state proof for round 1: the participation ID did not have secrets for the requested round`) _, err = registry.GetStateProofSecretsForRound(id, basics.Round(2)) - a.Error(err) + require.ErrorContains(t, err, `failed to fetch state proof for round 2: the participation ID did not have secrets for the requested round`) // Make sure we're able to fetch the same data that was put in. for i := uint64(3); i < max; i++ { @@ -1039,7 +1039,6 @@ func TestAddingSecretTwice(t *testing.T) { a.NoError(err) err = registry.Flush(10 * time.Second) - a.Error(err) a.EqualError(err, "unable to execute append keys: UNIQUE constraint failed: StateProofKeys.pk, StateProofKeys.round") } @@ -1065,10 +1064,10 @@ func TestGetRoundSecretsWithoutStateProof(t *testing.T) { a.NoError(registry.Flush(defaultTimeout)) partPerRound, err := registry.GetStateProofSecretsForRound(id, 1) - a.Error(err) + a.ErrorContains(err, `did not have secrets for the requested round`) partPerRound, err = registry.GetStateProofSecretsForRound(id, basics.Round(stateProofIntervalForTests)) - a.Error(err) + a.ErrorContains(err, `did not have secrets for the requested round`) // Append key keys := make(StateProofKeys, 1) @@ -1080,7 +1079,7 @@ func TestGetRoundSecretsWithoutStateProof(t *testing.T) { a.NoError(registry.Flush(defaultTimeout)) partPerRound, err = registry.GetStateProofSecretsForRound(id, basics.Round(stateProofIntervalForTests)-1) - a.Error(err) + a.ErrorContains(err, `did not have secrets for the requested round`) partPerRound, err = registry.GetStateProofSecretsForRound(id, basics.Round(stateProofIntervalForTests)) a.NoError(err) @@ -1202,7 +1201,7 @@ func TestFlushResetsLastError(t *testing.T) { err = registry.AppendKeys(id, keys) a.NoError(err) - a.Error(registry.Flush(10 * time.Second)) + a.ErrorContains(registry.Flush(10*time.Second), `UNIQUE constraint failed`) a.NoError(registry.Flush(10 * time.Second)) } diff --git a/data/account/participation_test.go b/data/account/participation_test.go index d2d967d8c8..953d2faf66 100644 --- a/data/account/participation_test.go +++ b/data/account/participation_test.go @@ -511,7 +511,7 @@ func TestKeyregValidityOverLimit(t *testing.T) { firstValid := basics.Round(0) lastValid := maxValidPeriod + 1 _, err := FillDBWithParticipationKeys(*store, address, firstValid, lastValid, dilution) - a.Error(err) + require.ErrorContains(t, err, `the validity period for mss is too large: the limit is 16777215`) } func TestFillDBWithParticipationKeys(t *testing.T) { @@ -564,7 +564,7 @@ func TestKeyregValidityPeriod(t *testing.T) { //nolint:paralleltest // Not paral firstValid = basics.Round(0) lastValid = maxValidPeriod + 1 _, err = FillDBWithParticipationKeys(*store, address, firstValid, lastValid, dilution) - a.Error(err) + require.ErrorContains(t, err, `the validity period for mss is too large: the limit is 4095`) } func BenchmarkParticipationSign(b *testing.B) { diff --git a/data/basics/address_test.go b/data/basics/address_test.go index ca07315611..ff1e112a56 100644 --- a/data/basics/address_test.go +++ b/data/basics/address_test.go @@ -105,7 +105,7 @@ func TestAddressChecksumCanonical(t *testing.T) { require.NoError(t, err) _, err = UnmarshalChecksumAddress(nonCanonical) - require.Error(t, err) + require.ErrorContains(t, err, `address J5YDZLPOHWB5O6MVRHNFGY4JXIQAYYM6NUJWPBSYBBIXH5ENQ4Z5LTJELV is non-canonical`) } type TestOb struct { diff --git a/data/bookkeeping/block_test.go b/data/bookkeeping/block_test.go index 367e27b157..2e4311e02c 100644 --- a/data/bookkeeping/block_test.go +++ b/data/bookkeeping/block_test.go @@ -151,10 +151,10 @@ func TestUpgradeVariableDelay(t *testing.T) { } _, err := s.applyUpgradeVote(basics.Round(10), UpgradeVote{UpgradePropose: proto1, UpgradeDelay: 2}) - require.Error(t, err, "accepted upgrade vote with delay less than MinUpgradeWaitRounds") + require.ErrorContains(t, err, `applyUpgradeVote: proposed upgrade wait rounds 2 out of permissible range [3, 7`, "accepted upgrade vote with delay less than MinUpgradeWaitRounds") _, err = s.applyUpgradeVote(basics.Round(10), UpgradeVote{UpgradePropose: proto1, UpgradeDelay: 8}) - require.Error(t, err, "accepted upgrade vote with delay more than MaxUpgradeWaitRounds") + require.ErrorContains(t, err, `applyUpgradeVote: proposed upgrade wait rounds 8 out of permissible range [3, 7`, "accepted upgrade vote with delay more than MaxUpgradeWaitRounds") _, err = s.applyUpgradeVote(basics.Round(10), UpgradeVote{UpgradePropose: proto1, UpgradeDelay: 5}) require.NoError(t, err, "did not accept upgrade vote with in-bounds delay") @@ -166,7 +166,7 @@ func TestUpgradeVariableDelay(t *testing.T) { require.NoError(t, err, "did not accept upgrade vote with maximal delay") _, err = s.applyUpgradeVote(basics.Round(10), UpgradeVote{UpgradePropose: proto1, UpgradeDelay: 0}) - require.Error(t, err, "accepted upgrade vote with zero (below minimal) delay") + require.ErrorContains(t, err, `applyUpgradeVote: proposed upgrade wait rounds 0 out of permissible range [3, 7`, "accepted upgrade vote with zero (below minimal) delay") } func TestMakeBlockUpgrades(t *testing.T) { @@ -479,12 +479,12 @@ func TestEncodeMalformedSignedTxn(t *testing.T) { tx.Txn.GenesisID = "bar" _, err = b.EncodeSignedTxn(tx, transactions.ApplyData{}) - require.Error(t, err) + require.ErrorContains(t, err, `GenesisID mismatch: bar != foo`) tx.Txn.GenesisID = b.BlockHeader.GenesisID crypto.RandBytes(tx.Txn.GenesisHash[:]) _, err = b.EncodeSignedTxn(tx, transactions.ApplyData{}) - require.Error(t, err) + require.ErrorContains(t, err, `GenesisHash mismatch`) } func TestDecodeMalformedSignedTxn(t *testing.T) { @@ -499,12 +499,12 @@ func TestDecodeMalformedSignedTxn(t *testing.T) { var txib1 transactions.SignedTxnInBlock txib1.SignedTxn.Txn.GenesisID = b.BlockHeader.GenesisID _, _, err := b.DecodeSignedTxn(txib1) - require.Error(t, err) + require.ErrorContains(t, err, `GenesisID not empty`) var txib2 transactions.SignedTxnInBlock txib2.SignedTxn.Txn.GenesisHash = b.BlockHeader.GenesisHash _, _, err = b.DecodeSignedTxn(txib2) - require.Error(t, err) + require.ErrorContains(t, err, `GenesisHash <`) } // TestInitialRewardsRateCalculation perform positive and negative testing for the InitialRewardsRateCalculation fix by diff --git a/data/committee/credential_test.go b/data/committee/credential_test.go index 57ce97cd38..fb2c87dd02 100644 --- a/data/committee/credential_test.go +++ b/data/committee/credential_test.go @@ -273,7 +273,7 @@ func TestNoMoneyAccountNotSelected(t *testing.T) { } u := MakeCredential(zeroVRFSecret, sel) _, err := u.Verify(proto, m) - require.Error(t, err, "account should not have been selected") + require.ErrorContains(t, err, `UnauthenticatedCredential.Verify: credential has weight 0`, "account should not have been selected") } } diff --git a/data/ledger_test.go b/data/ledger_test.go index 93a18dec65..960df21262 100644 --- a/data/ledger_test.go +++ b/data/ledger_test.go @@ -236,24 +236,24 @@ func TestLedgerCirculation(t *testing.T) { } else if rnd < basics.Round(520) { // test one round in the future ( expected error ) data, _, _, err = realLedger.LookupAccount(rnd+1, destAccount) - require.Error(t, err) + require.ErrorContains(t, err, `too high: dbRound`) require.Equal(t, uint64(0), data.MicroAlgos.Raw) data, _, _, err = l.LookupAccount(rnd+1, destAccount) - require.Error(t, err) + require.ErrorContains(t, err, `too high: dbRound`) require.Equal(t, uint64(0), data.MicroAlgos.Raw) _, err = realLedger.OnlineCirculation(rnd+1, rnd+1+voteRoundOffset) - require.Error(t, err) + require.ErrorContains(t, err, `too high: dbRound`) _, err = l.OnlineCirculation(rnd+1, rnd+1+voteRoundOffset) - require.Error(t, err) + require.ErrorContains(t, err, `too high: dbRound`) } else if rnd < basics.Round(520) { // test expired round ( expected error ) _, err = realLedger.OnlineCirculation(rnd-500, rnd-500+voteRoundOffset) - require.Error(t, err) + require.ErrorContains(t, err, `too high: dbRound`) _, err = l.OnlineCirculation(rnd-500, rnd-500+voteRoundOffset) - require.Error(t, err) + require.ErrorContains(t, err, `too high: dbRound`) } } } diff --git a/data/pools/transactionPool_test.go b/data/pools/transactionPool_test.go index 16534f9ca1..2859cf2426 100644 --- a/data/pools/transactionPool_test.go +++ b/data/pools/transactionPool_test.go @@ -231,7 +231,7 @@ func TestSenderGoesBelowMinBalance(t *testing.T) { }, } signedTx := tx.Sign(secrets[0]) - require.Error(t, transactionPool.RememberOne(signedTx)) + require.ErrorContains(t, transactionPool.RememberOne(signedTx), `balance 99999 below min 100000 (0 assets)`) } func TestSenderGoesBelowMinBalanceDueToAssets(t *testing.T) { @@ -297,7 +297,7 @@ func TestSenderGoesBelowMinBalanceDueToAssets(t *testing.T) { } signedTx := tx.Sign(secrets[0]) err := transactionPool.RememberOne(signedTx) - require.Error(t, err) + require.ErrorContains(t, err, `balance 199999 below min 200000 (1 assets)`) var returnedTxid, returnedAcct string var returnedBal, returnedMin, numAssets uint64 _, err = fmt.Sscanf(err.Error(), "TransactionPool.Remember: transaction %s account %s balance %d below min %d (%d assets)", @@ -366,7 +366,7 @@ func TestCloseAccount(t *testing.T) { }, } signedTx2 := tx.Sign(secrets[0]) - require.Error(t, transactionPool.RememberOne(signedTx2)) + require.ErrorContains(t, transactionPool.RememberOne(signedTx2), `overspend`) } func TestCloseAccountWhileTxIsPending(t *testing.T) { @@ -429,7 +429,7 @@ func TestCloseAccountWhileTxIsPending(t *testing.T) { }, } signedCloseTx := closeTx.Sign(secrets[0]) - require.Error(t, transactionPool.RememberOne(signedCloseTx)) + require.ErrorContains(t, transactionPool.RememberOne(signedCloseTx), `overspend`) } func TestClosingAccountBelowMinBalance(t *testing.T) { @@ -474,7 +474,7 @@ func TestClosingAccountBelowMinBalance(t *testing.T) { }, } signedTx := closeTx.Sign(secrets[0]) - require.Error(t, transactionPool.RememberOne(signedTx)) + require.ErrorContains(t, transactionPool.RememberOne(signedTx), `balance 99999 below min 100000 (0 assets)`) } func TestRecipientGoesBelowMinBalance(t *testing.T) { @@ -517,7 +517,7 @@ func TestRecipientGoesBelowMinBalance(t *testing.T) { }, } signedTx := tx.Sign(secrets[0]) - require.Error(t, transactionPool.RememberOne(signedTx)) + require.ErrorContains(t, transactionPool.RememberOne(signedTx), `overspend`) } func TestRememberForget(t *testing.T) { @@ -792,7 +792,7 @@ func TestOverspender(t *testing.T) { signedTx := tx.Sign(secrets[0]) // consume the transaction of allowed limit - require.Error(t, transactionPool.RememberOne(signedTx)) + require.ErrorContains(t, transactionPool.RememberOne(signedTx), `overspend`) // min transaction minTx := transactions.Transaction{ @@ -811,7 +811,7 @@ func TestOverspender(t *testing.T) { }, } signedMinTx := minTx.Sign(secrets[0]) - require.Error(t, transactionPool.RememberOne(signedMinTx)) + require.ErrorContains(t, transactionPool.RememberOne(signedMinTx), `overspend`) } func TestRemove(t *testing.T) { @@ -1369,7 +1369,7 @@ func TestTxPoolSizeLimits(t *testing.T) { } // ensure that we would fail adding this. - require.Error(t, transactionPool.Remember(txgroup)) + require.ErrorContains(t, transactionPool.Remember(txgroup), `TransactionPool.checkPendingQueueSize: transaction pool have reached capacity`) if groupSize > 1 { // add a single transaction and ensure we succeed diff --git a/data/transactions/logic/assembler_test.go b/data/transactions/logic/assembler_test.go index 455a287836..494ad6995d 100644 --- a/data/transactions/logic/assembler_test.go +++ b/data/transactions/logic/assembler_test.go @@ -1908,36 +1908,31 @@ func TestAssembleDisassembleErrors(t *testing.T) { ops := testProg(t, source, v) ops.Program[len(ops.Program)-1] = 0x50 // txn field dis, err := Disassemble(ops.Program) - require.Error(t, err, dis) - require.Contains(t, err.Error(), "invalid immediate f for txn") + require.ErrorContains(t, err, "invalid immediate f for txn", dis) source = `txna Accounts 0` ops = testProg(t, source, v) ops.Program[len(ops.Program)-2] = 0x50 // txn field dis, err = Disassemble(ops.Program) - require.Error(t, err, dis) - require.Contains(t, err.Error(), "invalid immediate f for txna") + require.ErrorContains(t, err, "invalid immediate f for txna", dis) source = `gtxn 0 Sender` ops = testProg(t, source, v) ops.Program[len(ops.Program)-1] = 0x50 // txn field dis, err = Disassemble(ops.Program) - require.Error(t, err, dis) - require.Contains(t, err.Error(), "invalid immediate f for gtxn") + require.ErrorContains(t, err, "invalid immediate f for gtxn", dis) source = `gtxna 0 Accounts 0` ops = testProg(t, source, v) ops.Program[len(ops.Program)-2] = 0x50 // txn field dis, err = Disassemble(ops.Program) - require.Error(t, err, dis) - require.Contains(t, err.Error(), "invalid immediate f for gtxna") + require.ErrorContains(t, err, "invalid immediate f for gtxna", dis) source = `global MinTxnFee` ops = testProg(t, source, v) ops.Program[len(ops.Program)-1] = 0x50 // txn field _, err = Disassemble(ops.Program) - require.Error(t, err) - require.Contains(t, err.Error(), "invalid immediate f for global") + require.ErrorContains(t, err, "invalid immediate f for global") ops.Program[0] = 0x11 // version out, err := Disassemble(ops.Program) @@ -1947,54 +1942,45 @@ func TestAssembleDisassembleErrors(t *testing.T) { ops.Program[0] = 0x01 // version ops.Program[1] = 0xFF // first opcode dis, err = Disassemble(ops.Program) - require.Error(t, err, dis) - require.Contains(t, err.Error(), "invalid opcode") + require.ErrorContains(t, err, "invalid opcode", dis) source = "int 0; int 0\nasset_holding_get AssetFrozen" ops = testProg(t, source, v) ops.Program[len(ops.Program)-1] = 0x50 // holding field dis, err = Disassemble(ops.Program) - require.Error(t, err, dis) - require.Contains(t, err.Error(), "invalid immediate f for") + require.ErrorContains(t, err, "invalid immediate f for", dis) source = "int 0\nasset_params_get AssetTotal" ops = testProg(t, source, v) ops.Program[len(ops.Program)-1] = 0x50 // params field dis, err = Disassemble(ops.Program) - require.Error(t, err, dis) - require.Contains(t, err.Error(), "invalid immediate f for") + require.ErrorContains(t, err, "invalid immediate f for", dis) source = "int 0\nasset_params_get AssetTotal" ops = testProg(t, source, v) ops.Program = ops.Program[0 : len(ops.Program)-1] dis, err = Disassemble(ops.Program) - require.Error(t, err, dis) - require.Contains(t, err.Error(), "program end while reading immediate f for") + require.ErrorContains(t, err, "program end while reading immediate f for", dis) source = "gtxna 0 Accounts 0" ops = testProg(t, source, v) dis, err = Disassemble(ops.Program[0 : len(ops.Program)-1]) - require.Error(t, err, dis) - require.Contains(t, err.Error(), "program end while reading immediate i for gtxna") + require.ErrorContains(t, err, "program end while reading immediate i for gtxna", dis) dis, err = Disassemble(ops.Program[0 : len(ops.Program)-2]) - require.Error(t, err, dis) - require.Contains(t, err.Error(), "program end while reading immediate f for gtxna") + require.ErrorContains(t, err, "program end while reading immediate f for gtxna", dis) dis, err = Disassemble(ops.Program[0 : len(ops.Program)-3]) - require.Error(t, err, dis) - require.Contains(t, err.Error(), "program end while reading immediate t for gtxna") + require.ErrorContains(t, err, "program end while reading immediate t for gtxna", dis) source = "txna Accounts 0" ops = testProg(t, source, v) ops.Program = ops.Program[0 : len(ops.Program)-1] dis, err = Disassemble(ops.Program) - require.Error(t, err, dis) - require.Contains(t, err.Error(), "program end while reading immediate i for txna") + require.ErrorContains(t, err, "program end while reading immediate i for txna", dis) source = "byte 0x4141\nsubstring 0 1" ops = testProg(t, source, v) dis, err = Disassemble(ops.Program[0 : len(ops.Program)-1]) - require.Error(t, err, dis) - require.Contains(t, err.Error(), "program end while reading immediate e for substring") + require.ErrorContains(t, err, "program end while reading immediate e for substring", dis) }) } } @@ -3826,9 +3812,9 @@ func TestDisassembleBadBranch(t *testing.T) { for _, br := range []byte{0x40, 0x41, 0x42} { dis, err := Disassemble([]byte{2, br}) - require.Error(t, err, dis) + require.ErrorContains(t, err, `program end while reading label for b`, dis) dis, err = Disassemble([]byte{2, br, 0x01}) - require.Error(t, err, dis) + require.ErrorContains(t, err, `program end while reading label for b`, dis) // It would be reasonable to error here, since it's a jump past the end. dis, err = Disassemble([]byte{2, br, 0x00, 0x05}) diff --git a/data/transactions/logic/backwardCompat_test.go b/data/transactions/logic/backwardCompat_test.go index 9d3ca14d2b..cc1737b777 100644 --- a/data/transactions/logic/backwardCompat_test.go +++ b/data/transactions/logic/backwardCompat_test.go @@ -413,7 +413,7 @@ func TestBackwardCompatTxnFields(t *testing.T) { ops, err := AssembleStringWithVersion(text, AssemblerMaxVersion) if fs.array { // "txn Accounts" is invalid, so skip evaluation - require.Error(t, err) + require.ErrorContains(t, err, `txn can only be used with`) continue } else { require.NoError(t, err) diff --git a/data/transactions/logic/crypto_test.go b/data/transactions/logic/crypto_test.go index 6d9607ac2e..966b7d70ed 100644 --- a/data/transactions/logic/crypto_test.go +++ b/data/transactions/logic/crypto_test.go @@ -449,7 +449,7 @@ func TestLeadingZeros(t *testing.T) { b := big.NewInt(0x100) r, err := leadingZeros(1, b) - require.Error(t, err) + require.ErrorContains(t, err, `insufficient buffer size: 1 < 2`) require.Nil(t, r) b = big.NewInt(100) diff --git a/data/transactions/logic/evalStateful_test.go b/data/transactions/logic/evalStateful_test.go index 3a0de42d8e..f7fe7ba7eb 100644 --- a/data/transactions/logic/evalStateful_test.go +++ b/data/transactions/logic/evalStateful_test.go @@ -1832,16 +1832,14 @@ intc_1 ep.SigLedger = ledger _, err = EvalApp(ops.Program, 0, 100, ep) - require.Error(t, err) - require.Contains(t, err.Error(), "is not opted into") + require.ErrorContains(t, err, "is not opted into") ledger.NewApp(txn.Txn.Sender, 100, basics.AppParams{}) ledger.NewLocals(txn.Txn.Sender, 100) if name == "read" { _, err = EvalApp(ops.Program, 0, 100, ep) - require.Error(t, err) - require.Contains(t, err.Error(), "err opcode") // no such key + require.ErrorContains(t, err, "err opcode") } ledger.NewLocal(txn.Txn.Sender, 100, "ALGO", 0x77) diff --git a/data/transactions/logic/eval_test.go b/data/transactions/logic/eval_test.go index 96caae9f2e..98ab0682b2 100644 --- a/data/transactions/logic/eval_test.go +++ b/data/transactions/logic/eval_test.go @@ -257,7 +257,7 @@ func TestTooManyArgs(t *testing.T) { args := [transactions.EvalMaxArgs + 1][]byte{} txn.Lsig.Args = args[:] pass, err := EvalSignature(0, defaultSigParams(txn)) - require.Error(t, err) + require.ErrorContains(t, err, `rejected by logic err=LogicSig has too many arguments. Details: pc=1`) require.False(t, pass) }) } @@ -274,7 +274,7 @@ func TestArgTooLarge(t *testing.T) { txn.Lsig.Logic = ops.Program txn.Lsig.Args = [][]byte{make([]byte, transactions.MaxLogicSigArgSize+1)} pass, err := EvalSignature(0, defaultSigParams(txn)) - require.Error(t, err) + require.ErrorContains(t, err, `rejected by logic err=LogicSig argument too large. Details: pc=1`) require.False(t, pass) }) } @@ -345,7 +345,7 @@ func TestTxnFieldToTealValue(t *testing.T) { require.Equal(t, string(addr[:]), tealValue.Bytes) tealValue, err = TxnFieldToTealValue(&txn, groupIndex, field, 100, false) - require.Error(t, err) + require.ErrorContains(t, err, `invalid Accounts index 100`) require.Equal(t, basics.TealUintType, tealValue.Type) require.Equal(t, uint64(0), tealValue.Uint) require.Equal(t, "", tealValue.Bytes) @@ -4523,7 +4523,7 @@ func TestAllowedOpcodesV2(t *testing.T) { _, err = EvalApp(ops.Program, 0, 0, aep) if spec.Name != "return" { // "return" opcode always succeeds so ignore it - require.Error(t, err, source) + require.ErrorContains(t, err, `0 appId in contract eval`, source) require.NotContains(t, err.Error(), "illegal opcode") } @@ -5260,7 +5260,7 @@ func TestPcDetails(t *testing.T) { ep.Trace = &strings.Builder{} pass, cx, err := EvalContract(ops.Program, 0, 888, ep) - require.Error(t, err) + require.ErrorContains(t, err, `. Details: app=888, pc=`) require.False(t, pass) require.NotNil(t, cx) // cx comes back nil if we couldn't even run @@ -5482,7 +5482,7 @@ func TestProtocolParseDuplicateErrMsg(t *testing.T) { var parsed map[string]json.RawMessage err := protocol.DecodeJSON([]byte(text), &parsed) require.Contains(t, err.Error(), "cannot decode into a non-pointer value") - require.Error(t, err) + require.ErrorContains(t, err, `json decode error [pos 26]: cannot decode into a non-pointer value`) } func TestOpJSONRef(t *testing.T) { @@ -6231,7 +6231,7 @@ func TestNoHeaderLedger(t *testing.T) { nhl := NoHeaderLedger{} _, err := nhl.BlockHdr(1) - require.Error(t, err) + require.ErrorContains(t, err, `no block header access`) require.Equal(t, err, fmt.Errorf("no block header access")) } diff --git a/data/transactions/logic/fields_test.go b/data/transactions/logic/fields_test.go index 67cf826dee..3cdcaa0925 100644 --- a/data/transactions/logic/fields_test.go +++ b/data/transactions/logic/fields_test.go @@ -310,11 +310,11 @@ func TestTxnEffectsAvailable(t *testing.T) { ep, _, _ := makeSampleEnv() ep.TxnGroup[1].Lsig.Logic = ops.Program _, err := EvalSignature(1, ep) - require.Error(t, err) + require.ErrorContains(t, err, `attempt to evaluate a signature with Application mode EvalParams`) ep.Ledger = NewLedger(nil) _, err = EvalApp(ops.Program, 1, 888, ep) if v < txnEffectsVersion { - require.Error(t, err, source) + require.ErrorContains(t, err, `logic eval error: Unable to obtain effects from top-level transactions. Details: app=888, pc=1`, source) } else { if fs.array { continue // Array (Logs) will be 0 length, so will fail anyway diff --git a/data/transactions/logic/jsonspec_test.go b/data/transactions/logic/jsonspec_test.go index f3e54b7b40..75fe6b9e11 100644 --- a/data/transactions/logic/jsonspec_test.go +++ b/data/transactions/logic/jsonspec_test.go @@ -48,10 +48,10 @@ func TestParseTrailingCommas(t *testing.T) { commas := strings.Repeat(",", i) intScalar := `{"key0": 4160` + commas + `}` _, err := parseJSON([]byte(intScalar)) - require.Error(t, err) + require.ErrorContains(t, err, `invalid json text`) strScalar := `{"key0": "algo"` + commas + `}` _, err = parseJSON([]byte(strScalar)) - require.Error(t, err) + require.ErrorContains(t, err, `invalid json text`) } } @@ -60,10 +60,10 @@ func TestParseComments(t *testing.T) { t.Parallel() text := `{"key0": /*comment*/"algo"}` _, err := parseJSON([]byte(text)) - require.Error(t, err) + require.ErrorContains(t, err, `invalid json text`) text = `{"key0": [1,/*comment*/,3]}` _, err = parseJSON([]byte(text)) - require.Error(t, err) + require.ErrorContains(t, err, `invalid json text`) } func TestParseUnclosed(t *testing.T) { @@ -71,19 +71,19 @@ func TestParseUnclosed(t *testing.T) { t.Parallel() text := `{"key0": ["algo"}` _, err := parseJSON([]byte(text)) - require.Error(t, err) + require.ErrorContains(t, err, `invalid json text`) text = `{"key0": ["algo"]]}` _, err = parseJSON([]byte(text)) - require.Error(t, err) + require.ErrorContains(t, err, `invalid json text`) text = `{"key0": ["algo"],"key1":{}` _, err = parseJSON([]byte(text)) - require.Error(t, err) + require.ErrorContains(t, err, `invalid json text`) text = `{"key0": ["algo"],"key1":{{}` _, err = parseJSON([]byte(text)) - require.Error(t, err) + require.ErrorContains(t, err, `invalid json text`) text = `{"key0": [1,}]}` _, err = parseJSON([]byte(text)) - require.Error(t, err) + require.ErrorContains(t, err, `invalid json text`) } func TestParseNested(t *testing.T) { @@ -100,7 +100,7 @@ func TestParseWhiteSpace(t *testing.T) { //empty text text := "" _, err := parseJSON([]byte(text)) - require.Error(t, err) + require.ErrorContains(t, err, `invalid json text`) //space, tab, new line and carriage return are allowed text = "{\"key0\": [\t]\n\r}" _, err = parseJSON([]byte(text)) @@ -108,7 +108,7 @@ func TestParseWhiteSpace(t *testing.T) { //form feed is not allowed text = "{\"key0\": [\f]}" _, err = parseJSON([]byte(text)) - require.Error(t, err) + require.ErrorContains(t, err, `invalid json text`) } func TestParseSpecialValues(t *testing.T) { @@ -116,13 +116,13 @@ func TestParseSpecialValues(t *testing.T) { t.Parallel() text := `{"key0": NaN}` _, err := parseJSON([]byte(text)) - require.Error(t, err) + require.ErrorContains(t, err, `invalid json text`) text = `{"key0": +Inf}` _, err = parseJSON([]byte(text)) - require.Error(t, err) + require.ErrorContains(t, err, `invalid json text`) text = `{"key0": -Inf}` _, err = parseJSON([]byte(text)) - require.Error(t, err) + require.ErrorContains(t, err, `invalid json text`) text = `{"key0": null}` _, err = parseJSON([]byte(text)) require.NoError(t, err) @@ -139,10 +139,10 @@ func TestParseHexValue(t *testing.T) { t.Parallel() text := `{"key0": 0x1}` _, err := parseJSON([]byte(text)) - require.Error(t, err) + require.ErrorContains(t, err, `invalid json text`) text = `{"key0": 0xFF}` _, err = parseJSON([]byte(text)) - require.Error(t, err) + require.ErrorContains(t, err, `invalid json text`) } func TestParseBigNum(t *testing.T) { @@ -174,7 +174,7 @@ func TestParseArrays(t *testing.T) { t.Parallel() text := `{"key0": [,1,]}` _, err := parseJSON([]byte(text)) - require.Error(t, err) + require.ErrorContains(t, err, `invalid json text`) text = "{\"key0\":[1\n]}" _, err = parseJSON([]byte(text)) require.NoError(t, err) @@ -199,7 +199,7 @@ func TestParseKeys(t *testing.T) { require.Equal(t, "1", string(parsed["a"])) text = `{"key0": 1,"key0": 2}` _, err = parseJSON([]byte(text)) - require.Error(t, err) + require.ErrorContains(t, err, `invalid json text, duplicate keys not allowed`) text = `{"key0": 1,"key1": {"key2":2,"key2":"10"}}` _, err = parseJSON([]byte(text)) require.NoError(t, err) @@ -208,16 +208,16 @@ func TestParseKeys(t *testing.T) { require.NoError(t, err) text = `{"key0":: 1}` _, err = parseJSON([]byte(text)) - require.Error(t, err) + require.ErrorContains(t, err, `invalid json text`) text = `{"key0":: "1"}` _, err = parseJSON([]byte(text)) - require.Error(t, err) + require.ErrorContains(t, err, `invalid json text`) text = `{"key0": 'algo'}` _, err = parseJSON([]byte(text)) - require.Error(t, err) + require.ErrorContains(t, err, `invalid json text`) text = `{1: 1}` _, err = parseJSON([]byte(text)) - require.Error(t, err) + require.ErrorContains(t, err, `invalid json text`) } func TestParseFileEncoding(t *testing.T) { @@ -237,13 +237,13 @@ func TestParseFileEncoding(t *testing.T) { encoded, err := enc.String(text) require.NoError(t, err) _, err = parseJSON([]byte(encoded)) - require.Error(t, err) + require.ErrorContains(t, err, `invalid json text`) // utf-16BE enc = unicode.UTF16(unicode.BigEndian, unicode.IgnoreBOM).NewEncoder() encoded, err = enc.String(text) require.NoError(t, err) _, err = parseJSON([]byte(encoded)) - require.Error(t, err) + require.ErrorContains(t, err, `invalid json text`) // json fails to parse utf-32 encoded text // utf-32LE @@ -251,13 +251,13 @@ func TestParseFileEncoding(t *testing.T) { encoded, err = enc.String(text) require.NoError(t, err) _, err = parseJSON([]byte(encoded)) - require.Error(t, err) + require.ErrorContains(t, err, `invalid json text`) // utf-32BE enc = utf32.UTF32(utf32.BigEndian, utf32.IgnoreBOM).NewEncoder() encoded, err = enc.String(text) require.NoError(t, err) _, err = parseJSON([]byte(encoded)) - require.Error(t, err) + require.ErrorContains(t, err, `invalid json text, only json object is allowed`) } func TestParseByteOrderMark(t *testing.T) { @@ -267,7 +267,7 @@ func TestParseByteOrderMark(t *testing.T) { // it is treated as an error text := "\uFEFF{\"key0\": 1}" _, err := parseJSON([]byte(text)) - require.Error(t, err) + require.ErrorContains(t, err, `invalid json text, only json object is allowed`) } func TestParseControlChar(t *testing.T) { @@ -291,13 +291,13 @@ func TestParseEscapeChar(t *testing.T) { // incomplete escaped chars text = `{"key0": ["\u00A"]}` _, err = parseJSON([]byte(text)) - require.Error(t, err) + require.ErrorContains(t, err, `invalid json text`) text = `{"key0": "\"}` _, err = parseJSON([]byte(text)) - require.Error(t, err) + require.ErrorContains(t, err, `invalid json text`) text = `{"key0": """}` _, err = parseJSON([]byte(text)) - require.Error(t, err) + require.ErrorContains(t, err, `invalid json text`) } func TestParseEscapedInvalidChar(t *testing.T) { @@ -329,8 +329,8 @@ func TestParseRawNonUnicodeChar(t *testing.T) { require.NoError(t, err) text = `{"key0": "\uFF"}` _, err = parseJSON([]byte(text)) - require.Error(t, err) + require.ErrorContains(t, err, `invalid json text`) text = `{"key0": FF}` _, err = parseJSON([]byte(text)) - require.Error(t, err) + require.ErrorContains(t, err, `invalid json text`) } diff --git a/data/transactions/stateproof_test.go b/data/transactions/stateproof_test.go index 9090c90352..24192dd8dd 100644 --- a/data/transactions/stateproof_test.go +++ b/data/transactions/stateproof_test.go @@ -121,25 +121,25 @@ func TestStateProofTxnShouldBeZero(t *testing.T) { const erroMsg = "type pay has non-zero fields for type stpf" txn.StateProofType = 1 err = txn.WellFormed(SpecialAddresses{}, curProto) - require.Error(t, err) + require.ErrorContains(t, err, `transaction of type pay has non-zero fields for type stpf`) require.Contains(t, err.Error(), erroMsg) txn.StateProofType = 0 txn.Message = stateproofmsg.Message{FirstAttestedRound: 1} err = txn.WellFormed(SpecialAddresses{}, curProto) - require.Error(t, err) + require.ErrorContains(t, err, `transaction of type pay has non-zero fields for type stpf`) require.Contains(t, err.Error(), erroMsg) txn.Message = stateproofmsg.Message{} txn.StateProof = stateproof.StateProof{SignedWeight: 100} err = txn.WellFormed(SpecialAddresses{}, curProto) - require.Error(t, err) + require.ErrorContains(t, err, `transaction of type pay has non-zero fields for type stpf`) require.Contains(t, err.Error(), erroMsg) txn.StateProof = stateproof.StateProof{} txn.Message.LastAttestedRound = 512 err = txn.WellFormed(SpecialAddresses{}, curProto) - require.Error(t, err) + require.ErrorContains(t, err, `transaction of type pay has non-zero fields for type stpf`) require.Contains(t, err.Error(), erroMsg) txn.Message.LastAttestedRound = 0 diff --git a/data/transactions/teal_test.go b/data/transactions/teal_test.go index 56edec8d13..314eac7179 100644 --- a/data/transactions/teal_test.go +++ b/data/transactions/teal_test.go @@ -212,7 +212,7 @@ func TestUnchangedAllocBounds(t *testing.T) { delta.InnerTxns = append(delta.InnerTxns, SignedTxnWithAD{}) msg := delta.MarshalMsg(nil) _, err := delta.UnmarshalMsg(msg) - require.Error(t, err) + require.ErrorContains(t, err, `msgp: length overflow: 257 > 256 at InnerTxns`) delta = &EvalDelta{} max = 2048 // Hardcodes bounds.MaxLogCalls, currently MaxAppProgramLen @@ -225,7 +225,7 @@ func TestUnchangedAllocBounds(t *testing.T) { delta.Logs = append(delta.Logs, "junk") msg = delta.MarshalMsg(nil) _, err = delta.UnmarshalMsg(msg) - require.Error(t, err) + require.ErrorContains(t, err, `msgp: length overflow: 2049 > 2048 at Logs`) delta = &EvalDelta{} max = 256 // Hardcodes bounds.MaxInnerTransactionsPerDelta @@ -238,7 +238,7 @@ func TestUnchangedAllocBounds(t *testing.T) { delta.InnerTxns = append(delta.InnerTxns, SignedTxnWithAD{}) msg = delta.MarshalMsg(nil) _, err = delta.UnmarshalMsg(msg) - require.Error(t, err) + require.ErrorContains(t, err, `msgp: length overflow: 257 > 256 at InnerTxns`) // This one appears wildly conservative. The real max is something like // MaxAppTxnAccounts (4) + 1, since the key must be an index in the static @@ -254,7 +254,7 @@ func TestUnchangedAllocBounds(t *testing.T) { delta.LocalDeltas[uint64(max)] = basics.StateDelta{} msg = delta.MarshalMsg(nil) _, err = delta.UnmarshalMsg(msg) - require.Error(t, err) + require.ErrorContains(t, err, `msgp: length overflow: 2049 > 2048 at LocalDeltas`) // This one *might* be wildly conservative. Only 64 keys can be set in // globals, but I don't know what happens if you set and delete 65 (or way @@ -270,6 +270,6 @@ func TestUnchangedAllocBounds(t *testing.T) { delta.GlobalDelta[fmt.Sprintf("%d", max)] = basics.ValueDelta{} msg = delta.MarshalMsg(nil) _, err = delta.UnmarshalMsg(msg) - require.Error(t, err) + require.ErrorContains(t, err, `msgp: length overflow: 2049 > 2048 at GlobalDelta`) } diff --git a/data/transactions/verify/txnBatch_test.go b/data/transactions/verify/txnBatch_test.go index 036b1fc0b8..3dc805762f 100644 --- a/data/transactions/verify/txnBatch_test.go +++ b/data/transactions/verify/txnBatch_test.go @@ -402,7 +402,7 @@ func TestStreamToBatchPoolShutdown(t *testing.T) { //nolint:paralleltest // Not for x := 0; x < 10; x++ { err := verificationPool.EnqueueBacklog(context.Background(), func(arg interface{}) interface{} { return nil }, nil, nil) - require.Error(t, err, fmt.Sprintf("x = %d", x)) + require.ErrorContains(t, err, `context canceled`, "x = %d", x) } ctx, cancel := context.WithCancel(context.Background()) @@ -800,10 +800,10 @@ func TestStreamToBatchPostVBlocked(t *testing.T) { func TestStreamToBatchMakeStreamToBatchErr(t *testing.T) { partitiontest.PartitionTest(t) _, err := MakeSigVerifier(&DummyLedgerForSignature{badHdr: true}, nil) - require.Error(t, err) + require.ErrorContains(t, err, `MakeSigVerifier: Could not get header for previous block: test error block hdr`) _, err = MakeSigVerifyJobProcessor(&DummyLedgerForSignature{badHdr: true}, nil, nil, nil) - require.Error(t, err) + require.ErrorContains(t, err, `MakeSigVerifier: Could not get header for previous block: test error block hdr`) } // TestStreamToBatchCancelWhenPooled tests the case where the ctx is cancled after the verification @@ -902,5 +902,5 @@ func TestSigVerifier(t *testing.T) { txnGroup = txnGroups[0] err = verifier.Verify(txnGroup) - require.Error(t, err) + require.ErrorContains(t, err, `At least one signature didn't pass verification`) } diff --git a/data/transactions/verify/txn_test.go b/data/transactions/verify/txn_test.go index 152ffb9009..d51653ed4a 100644 --- a/data/transactions/verify/txn_test.go +++ b/data/transactions/verify/txn_test.go @@ -255,7 +255,7 @@ func TestSignedPayment(t *testing.T) { stxn2.MessUpSigForTesting() require.Equal(t, stxn.ID(), stxn2.ID(), "changing sig caused txid to change") groupCtx.signedGroupTxns[0] = stxn2 - require.Error(t, verifyTxn(0, groupCtx), "verify succeeded with bad sig") + require.ErrorContains(t, verifyTxn(0, groupCtx), `At least one signature didn't pass verification`, "verify succeeded with bad sig") require.True(t, crypto.SignatureVerifier(addr).Verify(payment, stxn.Sig), "signature on the transaction is not the signature of the hash of the transaction under the spender's key") } @@ -339,7 +339,7 @@ func TestTxnValidationStateProof(t *testing.T) { stxn2.Txn.Header.Fee = basics.MicroAlgos{Raw: proto.MinTxnFee} groupCtx.signedGroupTxns[0] = stxn2 err = verifyTxn(0, groupCtx) - require.Error(t, err, "payment txn %#v verified from StateProofSender", stxn2) + require.ErrorContains(t, err, `signedtxn has no sig`, "payment txn %#v verified from StateProofSender", stxn2) secret := keypair() stxn2 = stxn @@ -348,32 +348,32 @@ func TestTxnValidationStateProof(t *testing.T) { stxn2 = stxn2.Txn.Sign(secret) groupCtx.signedGroupTxns[0] = stxn2 err = verifyTxn(0, groupCtx) - require.Error(t, err, "state proof txn %#v verified from non-StateProofSender", stxn2) + require.ErrorContains(t, err, `sender must be the state-proof sender`, "state proof txn %#v verified from non-StateProofSender", stxn2) // state proof txns are not allowed to have non-zero values for many fields stxn2 = stxn stxn2.Txn.Header.Fee = basics.MicroAlgos{Raw: proto.MinTxnFee} groupCtx.signedGroupTxns[0] = stxn2 err = verifyTxn(0, groupCtx) - require.Error(t, err, "state proof txn %#v verified", stxn2) + require.ErrorContains(t, err, `fee must be zero in state-proof transaction`, "state proof txn %#v verified", stxn2) stxn2 = stxn stxn2.Txn.Header.Note = []byte{'A'} groupCtx.signedGroupTxns[0] = stxn2 err = verifyTxn(0, groupCtx) - require.Error(t, err, "state proof txn %#v verified", stxn2) + require.ErrorContains(t, err, `note must be empty in state-proof transaction`, "state proof txn %#v verified", stxn2) stxn2 = stxn stxn2.Txn.Lease[0] = 1 groupCtx.signedGroupTxns[0] = stxn2 err = verifyTxn(0, groupCtx) - require.Error(t, err, "state proof txn %#v verified", stxn2) + require.ErrorContains(t, err, `lease must be zero in state-proof transaction`, "state proof txn %#v verified", stxn2) stxn2 = stxn stxn2.Txn.RekeyTo = basics.Address(secret.SignatureVerifier) groupCtx.signedGroupTxns[0] = stxn2 err = verifyTxn(0, groupCtx) - require.Error(t, err, "state proof txn %#v verified", stxn2) + require.ErrorContains(t, err, `rekey must be zero in state-proof transaction`, "state proof txn %#v verified", stxn2) } func TestDecodeNil(t *testing.T) { @@ -615,7 +615,7 @@ func TestPaysetGroups(t *testing.T) { // break the signature and see if it fails. txnGroups[0][0].Sig[0] = txnGroups[0][0].Sig[0] + 1 err = PaysetGroups(context.Background(), txnGroups, blkHdr, verificationPool, MakeVerifiedTransactionCache(50000), nil) - require.Error(t, err) + require.ErrorContains(t, err, `At least one signature didn't pass verification`) // ensure the rest are fine err = PaysetGroups(context.Background(), txnGroups[1:], blkHdr, verificationPool, MakeVerifiedTransactionCache(50000), nil) @@ -743,7 +743,7 @@ func TestLsigSize(t *testing.T) { if test.success { require.NoError(t, err) } else { - require.Error(t, err) + require.ErrorContains(t, err, `LogicSig`) } } } @@ -772,8 +772,7 @@ byte base64 5rZMNsevs5sULO+54aN+OvU6lQ503z2X+SSYUABIx7E= tmpSig := txnGroups[0][0].Sig txnGroups[0][0].Sig = crypto.Signature{} _, err = TxnGroup(txnGroups[0], &blkHdr, nil, &dummyLedger) - require.Error(t, err) - require.Contains(t, err.Error(), "has no sig") + require.ErrorContains(t, err, "has no sig") txnGroups[0][0].Sig = tmpSig ///// Sig + multiSig @@ -783,15 +782,13 @@ byte base64 5rZMNsevs5sULO+54aN+OvU6lQ503z2X+SSYUABIx7E= Sig: crypto.Signature{0x2}, } _, err = TxnGroup(txnGroups[0], &blkHdr, nil, &dummyLedger) - require.Error(t, err) - require.Contains(t, err.Error(), "should only have one of Sig or Msig or LogicSig") + require.ErrorContains(t, err, "should only have one of Sig or Msig or LogicSig") txnGroups[0][0].Msig.Subsigs = nil ///// Sig + logic txnGroups[0][0].Lsig.Logic = op.Program _, err = TxnGroup(txnGroups[0], &blkHdr, nil, &dummyLedger) - require.Error(t, err) - require.Contains(t, err.Error(), "should only have one of Sig or Msig or LogicSig") + require.ErrorContains(t, err, "should only have one of Sig or Msig or LogicSig") txnGroups[0][0].Lsig.Logic = []byte{} ///// MultiSig + logic @@ -803,8 +800,7 @@ byte base64 5rZMNsevs5sULO+54aN+OvU6lQ503z2X+SSYUABIx7E= Sig: crypto.Signature{0x2}, } _, err = TxnGroup(txnGroups[0], &blkHdr, nil, &dummyLedger) - require.Error(t, err) - require.Contains(t, err.Error(), "should only have one of Sig or Msig or LogicSig") + require.ErrorContains(t, err, "should only have one of Sig or Msig or LogicSig") txnGroups[0][0].Lsig.Logic = []byte{} txnGroups[0][0].Sig = tmpSig txnGroups[0][0].Msig.Subsigs = nil @@ -819,8 +815,7 @@ byte base64 5rZMNsevs5sULO+54aN+OvU6lQ503z2X+SSYUABIx7E= Sig: crypto.Signature{0x2}, } _, err = TxnGroup(txnGroups[0], &blkHdr, nil, &dummyLedger) - require.Error(t, err) - require.Contains(t, err.Error(), "should only have one of Sig, Msig, or LMsig") + require.ErrorContains(t, err, "should only have one of Sig, Msig, or LMsig") txnGroups[0][0].Lsig.Msig.Subsigs = nil ///// logic with sig and LMsig @@ -830,8 +825,7 @@ byte base64 5rZMNsevs5sULO+54aN+OvU6lQ503z2X+SSYUABIx7E= Sig: crypto.Signature{0x2}, } _, err = TxnGroup(txnGroups[0], &blkHdr, nil, &dummyLedger) - require.Error(t, err) - require.Contains(t, err.Error(), "should only have one of Sig, Msig, or LMsig") + require.ErrorContains(t, err, "should only have one of Sig, Msig, or LMsig") txnGroups[0][0].Lsig.Sig = crypto.Signature{} txnGroups[0][0].Lsig.LMsig.Subsigs = nil @@ -847,8 +841,7 @@ byte base64 5rZMNsevs5sULO+54aN+OvU6lQ503z2X+SSYUABIx7E= Sig: crypto.Signature{0x4}, } _, err = TxnGroup(txnGroups[0], &blkHdr, nil, &dummyLedger) - require.Error(t, err) - require.Contains(t, err.Error(), "should only have one of Sig, Msig, or LMsig") + require.ErrorContains(t, err, "should only have one of Sig, Msig, or LMsig") } @@ -1194,8 +1187,7 @@ func verifyGroup(t *testing.T, txnGroups [][]transactions.SignedTxn, blkHdr *boo dummyLedger := DummyLedgerForSignature{} _, err := TxnGroup(txnGroups[0], blkHdr, cache, &dummyLedger) - require.Error(t, err) - require.Contains(t, err.Error(), errorString) + require.ErrorContains(t, err, errorString) // The txns should not be in the cache unverifiedGroups := cache.GetUnverifiedTransactionGroups(txnGroups[:1], spec, blkHdr.CurrentProtocol) @@ -1232,8 +1224,7 @@ func verifyGroup(t *testing.T, txnGroups [][]transactions.SignedTxn, blkHdr *boo for _, txng := range txnGroups { _, err = TxnGroup(txng, blkHdr, cache, &dummyLedger) if err != nil { - require.Error(t, err) - require.Contains(t, err.Error(), errorString) + require.ErrorContains(t, err, errorString) numFailed++ } } diff --git a/data/transactions/verify/verifiedTxnCache_test.go b/data/transactions/verify/verifiedTxnCache_test.go index 6fde7135c1..e64552bc9a 100644 --- a/data/transactions/verify/verifiedTxnCache_test.go +++ b/data/transactions/verify/verifiedTxnCache_test.go @@ -186,5 +186,5 @@ func TestPinningTransactions(t *testing.T) { require.NoError(t, impl.Pin(txnGroups[0])) // try to pin an entry that was not added. - require.Error(t, impl.Pin(txnGroups[len(txnGroups)-1])) + require.ErrorContains(t, impl.Pin(txnGroups[len(txnGroups)-1]), `Missing pinned entry`) } diff --git a/go.mod b/go.mod index 398c42b4a6..7b5d1ac2ad 100644 --- a/go.mod +++ b/go.mod @@ -60,6 +60,7 @@ require ( golang.org/x/sys v0.38.0 golang.org/x/text v0.31.0 gopkg.in/sohlich/elogrus.v3 v3.0.0-20180410122755-1fa29e2f2009 + gopkg.in/vmarkovtsev/go-lcss.v1 v1.0.0-20181020221121-dfc501d07ea0 pgregory.net/rapid v1.2.0 ) diff --git a/go.sum b/go.sum index ce0354137b..853574d133 100644 --- a/go.sum +++ b/go.sum @@ -957,6 +957,8 @@ gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3M gopkg.in/sohlich/elogrus.v3 v3.0.0-20180410122755-1fa29e2f2009 h1:q/fZgS8MMadqFFGa8WL4Oyz+TmjiZfi8UrzWhTl8d5w= gopkg.in/sohlich/elogrus.v3 v3.0.0-20180410122755-1fa29e2f2009/go.mod h1:O0bY1e/dSoxMYZYTHP0SWKxG5EWLEvKR9/cOjWPPMKU= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/vmarkovtsev/go-lcss.v1 v1.0.0-20181020221121-dfc501d07ea0 h1:YY+ZVPsg2oJnV1rpzwIWtuCtQk71YFwuk47mMtjraN4= +gopkg.in/vmarkovtsev/go-lcss.v1 v1.0.0-20181020221121-dfc501d07ea0/go.mod h1:6LhSPGi1OSJsWUQZridpjQXWEnDzw7EZAXSjc5SyF8A= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/internal/rapidgen/rapidgenerators.go b/internal/rapidgen/rapidgenerators.go index aa7345dcb9..2e17dfb934 100644 --- a/internal/rapidgen/rapidgenerators.go +++ b/internal/rapidgen/rapidgenerators.go @@ -20,8 +20,9 @@ package rapidgen import ( "fmt" - "pgregory.net/rapid" "strings" + + "pgregory.net/rapid" ) // DomainWithPort generates an RFC 1035 compliant domain name with a port. diff --git a/ledger/acctonline_test.go b/ledger/acctonline_test.go index 6654e8718c..647db9a0ff 100644 --- a/ledger/acctonline_test.go +++ b/ledger/acctonline_test.go @@ -686,7 +686,7 @@ func TestAcctOnlineRoundParamsOffset(t *testing.T) { ao.deltas = make([]ledgercore.AccountDeltas, 10) ao.onlineRoundParamsData = make([]ledgercore.OnlineRoundParamsData, 331) offset, err = ao.roundParamsOffset(basics.Round(6)) - require.Error(t, err) + require.ErrorContains(t, err, `round 6 before dbRound 80`) require.Zero(t, offset) ao.cachedDBRoundOnline = 400 @@ -700,7 +700,7 @@ func TestAcctOnlineRoundParamsOffset(t *testing.T) { ao.deltas = nil ao.onlineRoundParamsData = nil offset, err = ao.roundParamsOffset(basics.Round(400)) - require.Error(t, err) + require.ErrorContains(t, err, `round 400 before dbRound 401`) require.Zero(t, offset) } @@ -1074,7 +1074,7 @@ func TestAcctOnlineCacheDBSync(t *testing.T) { require.Empty(t, cachedData.VoteLastValid) // round 1 is out of max history data, err = oa.lookupOnlineAccountData(1, addrA) - require.Error(t, err) + require.ErrorContains(t, err, `round 1 before dbRound 2`) data, err = oa.lookupOnlineAccountData(2, addrA) require.NoError(t, err) require.Empty(t, data.VotingData.VoteLastValid) @@ -1082,7 +1082,7 @@ func TestAcctOnlineCacheDBSync(t *testing.T) { _, has = oa.onlineAccountsCache.read(addrB, 1) require.True(t, has) // full history loaded when looked up addrB prev time _, err = oa.lookupOnlineAccountData(1, addrB) - require.Error(t, err) + require.ErrorContains(t, err, `round 1 before dbRound 2`) pad, err = oa.accountsq.LookupOnline(addrB, 1) require.NoError(t, err) require.Equal(t, addrB, pad.Addr) @@ -1799,7 +1799,7 @@ func TestAcctOnlineTopDBBehindMemRound(t *testing.T) { }() _, _, err = oa.TopOnlineAccounts(2, 2, 5, &proto, 0) - a.Error(err) + require.ErrorContains(t, err, `database round 1 is behind in-memory round 6`) a.Contains(err.Error(), "is behind in-memory round") case <-time.After(1 * time.Minute): @@ -2198,7 +2198,7 @@ func TestAcctOnline_ExpiredOnlineCirculation(t *testing.T) { } a.Equal(targetVoteRnd, rnd+basics.Round(params.MaxBalLookback)) _, err := oa.expiredOnlineCirculation(rnd, targetVoteRnd) - a.Error(err) + require.ErrorContains(t, err, `too high: dbRound`) a.Contains(err.Error(), fmt.Sprintf("round %d too high", rnd)) expiredStake, err := oa.expiredOnlineCirculation(rnd-1, targetVoteRnd) a.NoError(err) @@ -2310,13 +2310,13 @@ func TestAcctOnline_OnlineAcctsExpiredByRound(t *testing.T) { // check the stateproof interval 2 not in deltas offset, err := oa.roundOffset(targetRound) - require.Error(t, err) + require.ErrorContains(t, err, `round 32 before dbRound 69`) var roundOffsetError *RoundOffsetError require.ErrorAs(t, err, &roundOffsetError) require.Zero(t, offset) offset, err = oa.roundParamsOffset(targetRound) - require.Error(t, err) + require.ErrorContains(t, err, `round 32 before dbRound 58`) require.ErrorAs(t, err, &roundOffsetError) require.Zero(t, offset) diff --git a/ledger/acctupdates_test.go b/ledger/acctupdates_test.go index 71cf03d981..30ad51109c 100644 --- a/ledger/acctupdates_test.go +++ b/ledger/acctupdates_test.go @@ -349,20 +349,20 @@ func checkAcctUpdates(t *testing.T, au *accountUpdates, ao *onlineAccounts, base // the log has "onlineAccounts failed to fetch online totals for rnd" warning that is expected _, err := ao.onlineCirculation(latest+1, latest+1+basics.Round(ao.maxBalLookback())) - require.Error(t, err) + require.ErrorContains(t, err, `too high: dbRound`) var validThrough basics.Round _, validThrough, err = au.LookupWithoutRewards(latest+1, ledgertesting.RandomAddress()) - require.Error(t, err) + require.ErrorContains(t, err, `too high: dbRound`) require.Zero(t, validThrough) if base > 0 && base >= basics.Round(ao.maxBalLookback()) { rnd := base - basics.Round(ao.maxBalLookback()) _, err := ao.onlineCirculation(rnd, base) - require.Error(t, err) + require.ErrorContains(t, err, `too high: dbRound`) _, validThrough, err = au.LookupWithoutRewards(base-1, ledgertesting.RandomAddress()) - require.Error(t, err) + require.ErrorContains(t, err, `too high: dbRound`) require.Zero(t, validThrough) } diff --git a/ledger/apply/application_test.go b/ledger/apply/application_test.go index 10e9a9f4ba..419a2364a9 100644 --- a/ledger/apply/application_test.go +++ b/ledger/apply/application_test.go @@ -276,7 +276,7 @@ func TestAppCallGetParam(t *testing.T) { b := newTestBalances() _, _, _, err := getAppParams(b, appIdxError) - a.Error(err) + require.ErrorContains(t, err, `mock synthetic error`) _, _, exist, err := getAppParams(b, appIdxOk) a.NoError(err) @@ -287,14 +287,14 @@ func TestAppCallGetParam(t *testing.T) { b.appCreators = map[basics.AppIndex]basics.Address{appIdxOk: creator} b.balances = map[basics.Address]basics.AccountData{addr: {}} _, _, exist, err = getAppParams(b, appIdxOk) - a.Error(err) + require.ErrorContains(t, err, `mock balance not found`) a.True(exist) b.balances[creator] = basics.AccountData{ AppParams: map[basics.AppIndex]basics.AppParams{}, } _, _, exist, err = getAppParams(b, appIdxOk) - a.Error(err) + require.ErrorContains(t, err, `app 1 not found in account`) a.True(exist) b.balances[creator] = basics.AccountData{ @@ -421,7 +421,7 @@ func TestAppCallCreate(t *testing.T) { creator := getRandomAddress(a) // no balance record appIdx, err := createApplication(&ac, b, creator, txnCounter) - a.Error(err) + require.ErrorContains(t, err, `mock balance not found`) a.Zero(appIdx) b.balances = make(map[basics.Address]basics.AccountData) @@ -848,7 +848,7 @@ func TestAppCallClearState(t *testing.T) { b.delta = transactions.EvalDelta{GlobalDelta: nil} b.err = fmt.Errorf("test error") err = ApplicationCall(ac, h, b, ad, 0, ep, txnCounter) - a.Error(err) + require.ErrorContains(t, err, `test error`) br = b.putBalances[sender] a.Zero(len(br.AppLocalStates)) a.Equal(basics.StateSchema{}, br.TotalAppSchema) diff --git a/ledger/apply/payment_test.go b/ledger/apply/payment_test.go index 82c8f28f72..80c2e4e859 100644 --- a/ledger/apply/payment_test.go +++ b/ledger/apply/payment_test.go @@ -147,7 +147,7 @@ func TestPaymentSelfClose(t *testing.T) { CloseRemainderTo: self, }, } - require.Error(t, tx.WellFormed(spec, config.Consensus[protocol.ConsensusCurrentVersion])) + require.ErrorContains(t, tx.WellFormed(spec, config.Consensus[protocol.ConsensusCurrentVersion]), `transaction cannot close account to its sender`) } func generateTestPays(numTxs int) []transactions.Transaction { diff --git a/ledger/apply/stateproof_test.go b/ledger/apply/stateproof_test.go index a09ef8a61c..0dd15a62b8 100644 --- a/ledger/apply/stateproof_test.go +++ b/ledger/apply/stateproof_test.go @@ -263,7 +263,7 @@ func TestApplyStateProof(t *testing.T) { // crypto verification should fail since it is not a valid stateproof err = StateProof(stateProofTx, atRound, applier, validate) - a.Error(err) + require.ErrorContains(t, err, `the number of reveals is not large enough to prove that the desired weight signed, with the desired security level: state proof crypto error`) a.Contains(err.Error(), "crypto error") a.Equal(basics.Round(512), applier.GetStateProofNextRound()) diff --git a/ledger/boxtxn_test.go b/ledger/boxtxn_test.go index e48082a332..4ec0478af0 100644 --- a/ledger/boxtxn_test.go +++ b/ledger/boxtxn_test.go @@ -427,7 +427,7 @@ func TestBoxRW(t *testing.T) { dl.txn(call.Args("create", "yy"), fmt.Sprintf("invalid Box reference %#x", "yy")) withBr := call.Args("create", "yy") withBr.Boxes = append(withBr.Boxes, transactions.BoxRef{Index: 1, Name: []byte("yy")}) - require.Error(dl.t, withBr.Txn().WellFormed(transactions.SpecialAddresses{}, dl.generator.GenesisProto())) + require.ErrorContains(dl.t, withBr.Txn().WellFormed(transactions.SpecialAddresses{}, dl.generator.GenesisProto()), `tx.Boxes[1].Index is 1. Exceeds len(tx.ForeignApps)`) withBr.Boxes[1].Index = 0 dl.txn(withBr) }) @@ -650,7 +650,7 @@ func TestBoxInners(t *testing.T) { // This isn't right: Index should be index into ForeignApps call.Boxes = []transactions.BoxRef{{Index: uint64(boxID), Name: []byte("x")}} - require.Error(t, call.Txn().WellFormed(transactions.SpecialAddresses{}, dl.generator.genesisProto)) + require.ErrorContains(t, call.Txn().WellFormed(transactions.SpecialAddresses{}, dl.generator.genesisProto), `5. Exceeds len(tx.ForeignApps)`) call.Boxes = []transactions.BoxRef{{Index: 1, Name: []byte("x")}} if ver < boxQuotaBumpVersion { diff --git a/ledger/catchupaccessor_test.go b/ledger/catchupaccessor_test.go index 9e9971f83b..b4f01665f8 100644 --- a/ledger/catchupaccessor_test.go +++ b/ledger/catchupaccessor_test.go @@ -254,7 +254,7 @@ func TestCatchupAccessorFoo(t *testing.T) { err = catchpointAccessor.SetState(context.Background(), CatchpointCatchupStateSwitch) require.NoError(t, err, "catchpointAccessor.SetState") err = catchpointAccessor.SetState(context.Background(), catchpointCatchupStateLast+1) - require.Error(t, err, "catchpointAccessor.SetState") + require.ErrorContains(t, err, `invalid catchpoint catchup state provided : 5`, "catchpointAccessor.SetState") state, err := catchpointAccessor.GetState(context.Background()) require.NoError(t, err, "catchpointAccessor.GetState") @@ -263,7 +263,7 @@ func TestCatchupAccessorFoo(t *testing.T) { // invalid label err = catchpointAccessor.SetLabel(context.Background(), "wat") - require.Error(t, err, "catchpointAccessor.SetLabel") + require.ErrorContains(t, err, `catchpoint parsing failed`, "catchpointAccessor.SetLabel") // ok calabel := "98#QGMCMMUPV74AXXVKSNPRN73XMJG44ZJTZHU25HDG7JH5OHMM6N3Q" @@ -305,7 +305,7 @@ func TestBuildMerkleTrie(t *testing.T) { // actual testing... // insufficient setup, it should fail: err = catchpointAccessor.BuildMerkleTrie(ctx, progressNop) - require.Error(t, err) + require.ErrorContains(t, err, `no such table: main.catchpointpendinghashes`) // from reset it's okay, but it doesn't do anything err = catchpointAccessor.ResetStagingBalances(ctx, true) @@ -325,10 +325,10 @@ func TestBuildMerkleTrie(t *testing.T) { // this shouldn't work yet balancesFileName := fmt.Sprintf("%s%s%s", catchpointBalancesFileNamePrefix, "FAKE", catchpointBalancesFileNameSuffix) err = catchpointAccessor.ProcessStagingBalances(ctx, balancesFileName, blob, &progress) - require.Error(t, err) + require.ErrorContains(t, err, `CatchpointCatchupAccessorImpl::processStagingBalances: content chunk was missing`) // this needs content err = catchpointAccessor.ProcessStagingBalances(ctx, CatchpointContentFileName, blob, &progress) - require.Error(t, err) + require.ErrorContains(t, err, `msgp: too few bytes left to read object`) // content.msgpack from this: accountsCount := uint64(len(initKeys)) @@ -347,12 +347,12 @@ func TestBuildMerkleTrie(t *testing.T) { require.NoError(t, err) // shouldn't work a second time err = catchpointAccessor.ProcessStagingBalances(ctx, CatchpointContentFileName, encodedFileHeader, &progress) - require.Error(t, err) + require.ErrorContains(t, err, `CatchpointCatchupAccessorImpl::processStagingContent: content chunk already seen`) // This should still fail, but slightly different coverage path balancesFileName = fmt.Sprintf("%s%s%s", catchpointBalancesFileNamePrefix, "FAKE", catchpointBalancesFileNameSuffix) err = catchpointAccessor.ProcessStagingBalances(ctx, balancesFileName, blob, &progress) - require.Error(t, err) + require.ErrorContains(t, err, `msgp: too few bytes left to read object`) // create some catchpoint data encodedAccountChunks, _ := createTestingEncodedChunks(accountsCount) @@ -416,7 +416,7 @@ func TestCatchupAccessorBlockdb(t *testing.T) { // actual testing... err = catchpointAccessor.BuildMerkleTrie(ctx, func(uint64, uint64) {}) - require.Error(t, err) + require.ErrorContains(t, err, `no such table: main.catchpointpendinghashes`) } func TestVerifyCatchpoint(t *testing.T) { @@ -441,13 +441,13 @@ func TestVerifyCatchpoint(t *testing.T) { var blk bookkeeping.Block var cert agreement.Certificate err = catchpointAccessor.VerifyCatchpoint(ctx, &blk) - require.Error(t, err) + require.ErrorContains(t, err, `unable to make MerkleCommitter: no such table: catchpointaccounthashes`) err = catchpointAccessor.ResetStagingBalances(ctx, true) require.NoError(t, err, "ResetStagingBalances") err = catchpointAccessor.VerifyCatchpoint(ctx, &blk) - require.Error(t, err) + require.ErrorContains(t, err, `unable to get accounts totals: sql: no rows in result set`) // TODO: verify a catchpoint block that is valid // StoreBalancesRound assumes things are valid, so just does the db put @@ -466,7 +466,7 @@ func TestVerifyCatchpoint(t *testing.T) { // TODO: write a case with working no-err err = catchpointAccessor.CompleteCatchup(ctx) - require.Error(t, err) + require.ErrorContains(t, err, `sql: no rows in result set`) //require.NoError(t, err) } @@ -518,7 +518,7 @@ func TestCatchupAccessorResourceCountMismatch(t *testing.T) { // expect error since there is a resource count mismatch balancesFileName := fmt.Sprintf("%s%s%s", catchpointBalancesFileNamePrefix, "XX", catchpointBalancesFileNameSuffix) err = catchpointAccessor.ProcessStagingBalances(ctx, balancesFileName, encodedAccounts, &progress) - require.Error(t, err) + require.ErrorContains(t, err, `processStagingBalances received 0 appParams for account AAAAAAAAAAAA`) } type testStagingWriter struct { diff --git a/ledger/double_test.go b/ledger/double_test.go index 749f3a809e..c3938adc79 100644 --- a/ledger/double_test.go +++ b/ledger/double_test.go @@ -130,8 +130,7 @@ func (dl *DoubleLedger) txgroup(problem string, txns ...*txntest.Txn) (payset [] if problem == "" { require.NoError(dl.t, err) } else { - require.Error(dl.t, err) - require.Contains(dl.t, err.Error(), problem) + require.ErrorContains(dl.t, err, problem) } return nil } diff --git a/ledger/eval_simple_test.go b/ledger/eval_simple_test.go index a485e025e0..cfbad27319 100644 --- a/ledger/eval_simple_test.go +++ b/ledger/eval_simple_test.go @@ -87,14 +87,14 @@ func TestBlockEvaluator(t *testing.T) { st.Sig[2] ^= 8 txgroup := []transactions.SignedTxn{stbad} err = eval.TestTransactionGroup(txgroup) - require.Error(t, err) + require.ErrorContains(t, err, `transaction already in ledger`) // Repeat should fail txgroup = []transactions.SignedTxn{st} err = eval.TestTransactionGroup(txgroup) - require.Error(t, err) + require.ErrorContains(t, err, `transaction already in ledger`) err = eval.TransactionGroup(st.WithAD()) - require.Error(t, err) + require.ErrorContains(t, err, `transaction already in ledger`) // out of range should fail btxn := txn @@ -103,9 +103,9 @@ func TestBlockEvaluator(t *testing.T) { st = btxn.Sign(keys[0]) txgroup = []transactions.SignedTxn{st} err = eval.TestTransactionGroup(txgroup) - require.Error(t, err) + require.ErrorContains(t, err, `txn dead: round 1 outside of 2--3`) err = eval.TransactionGroup(st.WithAD()) - require.Error(t, err) + require.ErrorContains(t, err, `txn dead: round 1 outside of 2--3`) // bogus group should fail btxn = txn @@ -113,9 +113,9 @@ func TestBlockEvaluator(t *testing.T) { st = btxn.Sign(keys[0]) txgroup = []transactions.SignedTxn{st} err = eval.TestTransactionGroup(txgroup) - require.Error(t, err) + require.ErrorContains(t, err, `transactionGroup: incomplete group: AAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA !=`) err = eval.TransactionGroup(st.WithAD()) - require.Error(t, err) + require.ErrorContains(t, err, `transactionGroup: incomplete group: AAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA !=`) // mixed fields should fail btxn = txn @@ -123,7 +123,7 @@ func TestBlockEvaluator(t *testing.T) { st = btxn.Sign(keys[0]) txgroup = []transactions.SignedTxn{st} err = eval.TestTransactionGroup(txgroup) - require.Error(t, err) + require.ErrorContains(t, err, `: malformed: transaction of type pay has non-zero fields for type axfer`) // We don't test eval.Transaction() here because it doesn't check txn.WellFormed(), instead relying on that to have already been checked by the transaction pool. // err = eval.Transaction(st, transactions.ApplyData{}) // require.Error(t, err) @@ -162,10 +162,10 @@ func TestBlockEvaluator(t *testing.T) { s4 := t4.Sign(keys[2]) txgroup = []transactions.SignedTxn{s3, s4} err = eval.TestTransactionGroup(txgroup) - require.Error(t, err) + require.ErrorContains(t, err, `transactionGroup: [0] had zero Group but was submitted in a group of 2`) txgroupad := transactions.WrapSignedTxnsWithAD(txgroup) err = eval.TransactionGroup(txgroupad...) - require.Error(t, err) + require.ErrorContains(t, err, `transactionGroup: [0] had zero Group but was submitted in a group of 2`) // Test a group that should work var group transactions.TxGroup @@ -184,15 +184,15 @@ func TestBlockEvaluator(t *testing.T) { s4bad := t4bad.Sign(keys[2]) txgroup = []transactions.SignedTxn{s3, s4bad} err = eval.TestTransactionGroup(txgroup) - require.Error(t, err) + require.ErrorContains(t, err, `transactionGroup: inconsistent group values`) txgroupad = transactions.WrapSignedTxnsWithAD(txgroup) err = eval.TransactionGroup(txgroupad...) - require.Error(t, err) + require.ErrorContains(t, err, `transactionGroup: inconsistent group values`) // missing part of the group should fail txgroup = []transactions.SignedTxn{s3} err = eval.TestTransactionGroup(txgroup) - require.Error(t, err) + require.ErrorContains(t, err, `transactionGroup: incomplete group`) unfinishedBlock, err := eval.GenerateBlock(nil) require.NoError(t, err) @@ -1335,7 +1335,7 @@ func TestRekeying(t *testing.T) { makeTxn(addrs[0], addrs[0], basics.Address{}, keys[0], 2), // [A -> A][0,A] } err = tryBlock(test2txns) - require.Error(t, err) + require.ErrorContains(t, err, `: should have been authorized by`) // TODO: More tests } @@ -1412,10 +1412,10 @@ func TestEvalAppPooledBudgetWithTxnGroup(t *testing.T) { t.Run(fmt.Sprintf("i=%d,j=%d", i, j), func(t *testing.T) { err := testEvalAppPoolingGroup(t, basics.StateSchema{NumByteSlice: 3}, testCase.prog, param) if !testCase.isSuccessV29 && reflect.DeepEqual(param, protocol.ConsensusV29) { - require.Error(t, err) + require.ErrorContains(t, err, `dynamic cost budget exceeded, executing`) require.Contains(t, err.Error(), testCase.expectedErrorV29) } else if !testCase.isSuccessVFuture && reflect.DeepEqual(param, protocol.ConsensusFuture) { - require.Error(t, err) + require.ErrorContains(t, err, `: logic eval error: pc= 78 dynamic cost budget exceeded, executing pushint: local program cost was 2100. Details: app=1001, pc=78, opcodes=substring 0 4; pop; pushint 1`) require.Contains(t, err.Error(), testCase.expectedErrorVFuture) } }) diff --git a/ledger/ledger_test.go b/ledger/ledger_test.go index 3249efc50a..fffe0eedf7 100644 --- a/ledger/ledger_test.go +++ b/ledger/ledger_test.go @@ -520,99 +520,99 @@ func TestLedgerSingleTx(t *testing.T) { badTx = correctPay badTx.GenesisID = "invalid" - a.Error(l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), "added tx with invalid genesis ID") + require.ErrorContains(t, l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), `could not sign txn: GenesisID mismatch: invalid != TestLedgerSingleTx`, "added tx with invalid genesis ID") badTx = correctPay badTx.Type = "invalid" - a.Error(l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), "added tx with invalid tx type") + require.ErrorContains(t, l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), `appendUnvalidated error in Validate: transaction`, "added tx with invalid tx type") badTx = correctPay badTx.KeyregTxnFields = correctKeyregFields - a.Error(l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), "added pay tx with keyreg fields set") + require.ErrorContains(t, l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), `: transaction of type pay has non-zero fields for type keyreg`, "added pay tx with keyreg fields set") badTx = correctKeyreg badTx.PaymentTxnFields = correctPayFields - a.Error(l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), "added keyreg tx with pay fields set") + require.ErrorContains(t, l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), `: transaction of type keyreg has non-zero fields for type pay`, "added keyreg tx with pay fields set") badTx = correctKeyreg badTx.PaymentTxnFields = correctCloseFields - a.Error(l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), "added keyreg tx with pay (close) fields set") + require.ErrorContains(t, l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), `: transaction of type keyreg has non-zero fields for type pay`, "added keyreg tx with pay (close) fields set") badTx = correctPay badTx.FirstValid = badTx.LastValid + 1 - a.Error(l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), "added tx with FirstValid > LastValid") + require.ErrorContains(t, l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), `appendUnvalidated error in Validate: txn dead: round 1 outside of 11--10`, "added tx with FirstValid > LastValid") badTx = correctPay badTx.LastValid += basics.Round(proto.MaxTxnLife) - a.Error(l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), "added tx with overly long validity") + require.ErrorContains(t, l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), `appendUnvalidated error in Validate: transaction`, "added tx with overly long validity") badTx = correctPay badTx.LastValid = l.Latest() - a.Error(l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), "added expired tx") + require.ErrorContains(t, l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), `appendUnvalidated error in Validate: txn dead: round 1 outside of 1--0`, "added expired tx") badTx = correctPay badTx.FirstValid = l.Latest() + 2 - a.Error(l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), "added tx which is not valid yet") + require.ErrorContains(t, l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), `appendUnvalidated error in Validate: txn dead: round 1 outside of 2--10`, "added tx which is not valid yet") badTx = correctPay badTx.Note = make([]byte, proto.MaxTxnNoteBytes+1) - a.Error(l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), "added tx with overly large note field") + require.ErrorContains(t, l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), `transaction note too big: 1025 > 1024`, "added tx with overly large note field") badTx = correctPay badTx.Sender = poolAddr - a.Error(l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), "added tx send from tx pool") + require.ErrorContains(t, l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), `appendUnvalidated error in Validate: transaction`, "added tx send from tx pool") badTx = correctPay badTx.Sender = basics.Address{} - a.Error(l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), "added tx send from zero address") + require.ErrorContains(t, l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), `: transaction cannot close account to its sender AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY5HFKQ`, "added tx send from zero address") badTx = correctPay badTx.Fee = basics.MicroAlgos{} - a.Error(l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), "added tx with zero fee") + require.ErrorContains(t, l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), `: transaction had fee 0, which is less than the minimum 1000`, "added tx with zero fee") badTx = correctPay badTx.Fee = basics.MicroAlgos{Raw: proto.MinTxnFee - 1} - a.Error(l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), "added tx with fee below minimum") + require.ErrorContains(t, l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), `: transaction had fee 999, which is less than the minimum 1000`, "added tx with fee below minimum") badTx = correctKeyreg fee, overflow := basics.OAddA(initAccounts[badTx.Sender].MicroAlgos, basics.MicroAlgos{Raw: 1}) a.False(overflow) badTx.Fee = fee - a.Error(l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), "added keyreg tx with fee above user balance") + require.ErrorContains(t, l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), `tried to spend`, "added keyreg tx with fee above user balance") // TODO try excessive spending given distribution of some number of rewards badTx = correctPay sbadTx := sign(initSecrets, badTx) sbadTx.Sig = crypto.Signature{} - a.Error(l.appendUnvalidatedSignedTx(t, initAccounts, sbadTx, ad), "added tx with no signature") + require.ErrorContains(t, l.appendUnvalidatedSignedTx(t, initAccounts, sbadTx, ad), `signedtxn has no sig`, "added tx with no signature") badTx = correctPay sbadTx = sign(initSecrets, badTx) sbadTx.Sig[5]++ - a.Error(l.appendUnvalidatedSignedTx(t, initAccounts, sbadTx, ad), "added tx with corrupt signature") + require.ErrorContains(t, l.appendUnvalidatedSignedTx(t, initAccounts, sbadTx, ad), `appendUnvalidated error in Validate: At least one signature didn't pass verification`, "added tx with corrupt signature") // TODO set multisig and test badTx = correctPay badTx.Sender = sinkAddr - a.Error(l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), "sink spent to non-sink address") + require.ErrorContains(t, l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), `: cannot spend from fee sink's address FQVGZ2NJU7BIYIUV7UZE655FASFUFQVXVBKIJNUAWHQT2WM35M3BKL5JPY to non incentive pool address`, "sink spent to non-sink address") badTx = correctPay badTx.Sender = sinkAddr badTx.CloseRemainderTo = addrList[0] - a.Error(l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), "sink closed to non-sink address") + require.ErrorContains(t, l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), `: cannot spend from fee sink's address FQVGZ2NJU7BIYIUV7UZE655FASFUFQVXVBKIJNUAWHQT2WM35M3BKL5JPY to non incentive pool address`, "sink closed to non-sink address") badTx = correctPay badTx.Sender = sinkAddr badTx.Receiver = poolAddr badTx.CloseRemainderTo = addrList[0] - a.Error(l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), "sink closed to non-sink address") + require.ErrorContains(t, l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), `: cannot close fee sink FQVGZ2NJU7BIYIUV7UZE655FASFUFQVXVBKIJNUAWHQT2WM35M3BKL5JPY to`, "sink closed to non-sink address") badTx = correctPay badTx.Sender = sinkAddr badTx.CloseRemainderTo = poolAddr - a.Error(l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), "sink closed to pool address") + require.ErrorContains(t, l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), `: cannot spend from fee sink's address FQVGZ2NJU7BIYIUV7UZE655FASFUFQVXVBKIJNUAWHQT2WM35M3BKL5JPY to non incentive pool address`, "sink closed to pool address") badTx = correctPay remainder, overflow := basics.OSubA(initAccounts[badTx.Sender].MicroAlgos, badTx.Amount) @@ -620,7 +620,7 @@ func TestLedgerSingleTx(t *testing.T) { fee, overflow = basics.OAddA(remainder, basics.MicroAlgos{Raw: 1}) a.False(overflow) badTx.Fee = fee - a.Error(l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), "overspent with (amount + fee)") + require.ErrorContains(t, l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), `tried to spend`, "overspent with (amount + fee)") adClose := ad adClose.ClosingAmount = initAccounts[correctClose.Sender].MicroAlgos @@ -637,7 +637,7 @@ func TestLedgerSingleTx(t *testing.T) { correctPay.Receiver = poolAddr a.NoError(l.appendUnvalidatedTx(t, initAccounts, initSecrets, correctPay, ad), "could not spend from sink to pool") - a.Error(l.appendUnvalidatedTx(t, initAccounts, initSecrets, correctKeyreg, ad), "added duplicate tx") + require.ErrorContains(t, l.appendUnvalidatedTx(t, initAccounts, initSecrets, correctKeyreg, ad), `appendUnvalidated error in Validate: transaction already in ledger`, "added duplicate tx") } func TestLedgerSingleTxV24(t *testing.T) { @@ -1195,68 +1195,68 @@ func testLedgerSingleTxApplyData(t *testing.T, version protocol.ConsensusVersion badTx = correctPay badTx.GenesisID = "invalid" - a.Error(l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), "added tx with invalid genesis ID") + require.ErrorContains(t, l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), `could not sign txn: GenesisID mismatch: invalid != TestLedgerSingleTxApplyData`, "added tx with invalid genesis ID") badTx = correctPay badTx.Type = "invalid" - a.Error(l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), "added tx with invalid tx type") + require.ErrorContains(t, l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), `unknown tx type invalid`, "added tx with invalid tx type") badTx = correctPay badTx.KeyregTxnFields = correctKeyregFields - a.Error(l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), "added pay tx with keyreg fields set") + require.ErrorContains(t, l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), `: transaction of type pay has non-zero fields for type keyreg`, "added pay tx with keyreg fields set") badTx = correctKeyreg badTx.PaymentTxnFields = correctPayFields - a.Error(l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), "added keyreg tx with pay fields set") + require.ErrorContains(t, l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), `: transaction of type keyreg has non-zero fields for type pay`, "added keyreg tx with pay fields set") badTx = correctKeyreg badTx.PaymentTxnFields = correctCloseFields - a.Error(l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), "added keyreg tx with pay (close) fields set") + require.ErrorContains(t, l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), `: transaction of type keyreg has non-zero fields for type pay`, "added keyreg tx with pay (close) fields set") badTx = correctPay badTx.FirstValid = badTx.LastValid + 1 - a.Error(l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), "added tx with FirstValid > LastValid") + require.ErrorContains(t, l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), `appendUnvalidated error in Validate: txn dead: round 1 outside of 11--10`, "added tx with FirstValid > LastValid") badTx = correctPay badTx.LastValid += basics.Round(proto.MaxTxnLife) - a.Error(l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), "added tx with overly long validity") + require.ErrorContains(t, l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), `appendUnvalidated error in Validate: transaction`, "added tx with overly long validity") badTx = correctPay badTx.LastValid = l.Latest() - a.Error(l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), "added expired tx") + require.ErrorContains(t, l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), `appendUnvalidated error in Validate: txn dead: round 1 outside of 1--0`, "added expired tx") badTx = correctPay badTx.FirstValid = l.Latest() + 2 - a.Error(l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), "added tx which is not valid yet") + require.ErrorContains(t, l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), `appendUnvalidated error in Validate: txn dead: round 1 outside of 2--10`, "added tx which is not valid yet") badTx = correctPay badTx.Note = make([]byte, proto.MaxTxnNoteBytes+1) - a.Error(l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), "added tx with overly large note field") + require.ErrorContains(t, l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), `transaction note too big: 1025 > 1024`, "added tx with overly large note field") badTx = correctPay badTx.Sender = basics.Address{} - a.Error(l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), "added tx send from zero address") + require.ErrorContains(t, l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), `: transaction cannot close account to its sender AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY5HFKQ`, "added tx send from zero address") badTx = correctPay badTx.Fee = basics.MicroAlgos{} - a.Error(l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), "added tx with zero fee") + require.ErrorContains(t, l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), `appendUnvalidated error in Validate: t`, "added tx with zero fee") badTx = correctPay badTx.Fee = basics.MicroAlgos{Raw: proto.MinTxnFee - 1} - a.Error(l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), "added tx with fee below minimum") + require.ErrorContains(t, l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), `appendUnvalidated error in Validate: t`, "added tx with fee below minimum") badTx = correctKeyreg fee, overflow := basics.OAddA(initAccounts[badTx.Sender].MicroAlgos, basics.MicroAlgos{Raw: 1}) a.False(overflow) badTx.Fee = fee - a.Error(l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), "added keyreg tx with fee above user balance") + require.ErrorContains(t, l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), `tried to spend`, "added keyreg tx with fee above user balance") // TODO try excessive spending given distribution of some number of rewards badTx = correctPay sbadTx := sign(initSecrets, badTx) sbadTx.Sig = crypto.Signature{} - a.Error(l.appendUnvalidatedSignedTx(t, initAccounts, sbadTx, ad), "added tx with no signature") + require.ErrorContains(t, l.appendUnvalidatedSignedTx(t, initAccounts, sbadTx, ad), `signedtxn has no sig`, "added tx with no signature") badTx = correctPay remainder, overflow := basics.OSubA(initAccounts[badTx.Sender].MicroAlgos, badTx.Amount) @@ -1264,7 +1264,7 @@ func testLedgerSingleTxApplyData(t *testing.T, version protocol.ConsensusVersion fee, overflow = basics.OAddA(remainder, basics.MicroAlgos{Raw: 1}) a.False(overflow) badTx.Fee = fee - a.Error(l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), "overspent with (amount + fee)") + require.ErrorContains(t, l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad), `tried to spend`, "overspent with (amount + fee)") adClose := ad adClose.ClosingAmount = initAccounts[correctClose.Sender].MicroAlgos @@ -1277,11 +1277,11 @@ func testLedgerSingleTxApplyData(t *testing.T, version protocol.ConsensusVersion adCloseWrong.ClosingAmount.Raw++ a.NoError(l.appendUnvalidatedTx(t, initAccounts, initSecrets, correctPay, ad), "could not add payment transaction") - a.Error(l.appendUnvalidatedTx(t, initAccounts, initSecrets, correctClose, adCloseWrong), "closed transaction with wrong ApplyData") + require.ErrorContains(t, l.appendUnvalidatedTx(t, initAccounts, initSecrets, correctClose, adCloseWrong), `applyData mismatch`, "closed transaction with wrong ApplyData") a.NoError(l.appendUnvalidatedTx(t, initAccounts, initSecrets, correctClose, adClose), "could not add close transaction") a.NoError(l.appendUnvalidatedTx(t, initAccounts, initSecrets, correctKeyreg, ad), "could not add key registration") - a.Error(l.appendUnvalidatedTx(t, initAccounts, initSecrets, correctKeyreg, ad), "added duplicate tx") + require.ErrorContains(t, l.appendUnvalidatedTx(t, initAccounts, initSecrets, correctKeyreg, ad), `appendUnvalidated error in Validate: transaction already in ledger`, "added duplicate tx") leaseReleaseRound := l.Latest() + 10 correctPayLease := correctPay @@ -1294,7 +1294,7 @@ func testLedgerSingleTxApplyData(t *testing.T, version protocol.ConsensusVersion correctPayLease.Note = make([]byte, 1) correctPayLease.Note[0] = 1 correctPayLease.LastValid += 10 - a.Error(l.appendUnvalidatedTx(t, initAccounts, initSecrets, correctPayLease, ad), "added payment transaction with matching transaction lease") + require.ErrorContains(t, l.appendUnvalidatedTx(t, initAccounts, initSecrets, correctPayLease, ad), `overlapping lease`, "added payment transaction with matching transaction lease") correctPayLeaseOther := correctPayLease correctPayLeaseOther.Sender = addrList[4] a.NoError(l.appendUnvalidatedTx(t, initAccounts, initSecrets, correctPayLeaseOther, ad), "could not add payment transaction with matching lease but different sender") @@ -1303,7 +1303,7 @@ func testLedgerSingleTxApplyData(t *testing.T, version protocol.ConsensusVersion a.NoError(l.appendUnvalidatedTx(t, initAccounts, initSecrets, correctPayLeaseOther, ad), "could not add payment transaction with matching sender but different lease") for l.Latest() < leaseReleaseRound { - a.Error(l.appendUnvalidatedTx(t, initAccounts, initSecrets, correctPayLease, ad), "added payment transaction with matching transaction lease") + require.ErrorContains(t, l.appendUnvalidatedTx(t, initAccounts, initSecrets, correctPayLease, ad), `overlapping lease`, "added payment transaction with matching transaction lease") var totalRewardUnits uint64 for _, acctdata := range initAccounts { @@ -1345,7 +1345,7 @@ func testLedgerSingleTxApplyData(t *testing.T, version protocol.ConsensusVersion a.NoError(l.appendUnvalidatedTx(t, initAccounts, initSecrets, correctPayLease, ad), "could not add payment transaction after lease was dropped") } else { - a.Error(l.appendUnvalidatedTx(t, initAccounts, initSecrets, correctPayLease, ad), "added payment transaction with transaction lease unsupported by protocol version") + require.ErrorContains(t, l.appendUnvalidatedTx(t, initAccounts, initSecrets, correctPayLease, ad), `does not support transaction leases`) } } @@ -1448,7 +1448,7 @@ func testLedgerRegressionFaultyLeaseFirstValidCheck2f3880f7(t *testing.T, versio correctPayLease.LastValid = l.Latest() + 10 if proto.FixTransactionLeases { - a.Error(l.appendUnvalidatedTx(t, initAccounts, initSecrets, correctPayLease, ad), "added payment transaction with overlapping lease") + require.ErrorContains(t, l.appendUnvalidatedTx(t, initAccounts, initSecrets, correctPayLease, ad), `, AEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKE3PRHE)`, "added payment transaction with overlapping lease") } else { a.NoError(l.appendUnvalidatedTx(t, initAccounts, initSecrets, correctPayLease, ad), "should allow leasing payment transaction with newer FirstValid") } @@ -1758,7 +1758,7 @@ func TestLedgerVerifiesOldStateProofs(t *testing.T) { // we make sure that the voters header does not exist and that the voters tracker // lost tracking of the top voters. _, err = l.BlockHdr(basics.Round(proto.StateProofInterval)) - require.Error(t, err) + require.ErrorContains(t, err, `ledger does not have entry 256 (latest 3586, committed 3586)`) expectedErr := &ledgercore.ErrNoEntry{} require.ErrorAs(t, err, expectedErr, fmt.Sprintf("got error %s", err)) @@ -2214,7 +2214,7 @@ func TestLedgerReloadShrinkDeltas(t *testing.T) { // check an error latest-1 for txid := range txnIDs[latest-1] { - require.Error(t, l.CheckDup(proto, nextRound, latest-maxValidity, latest-1, txid, ledgercore.Txlease{})) + require.ErrorContains(t, l.CheckDup(proto, nextRound, latest-maxValidity, latest-1, txid, ledgercore.Txlease{}), `txTail: tried to check for dup in missing round 639`) } shorterLookback := config.GetDefaultLocal().MaxAcctLookback @@ -2225,7 +2225,7 @@ func TestLedgerReloadShrinkDeltas(t *testing.T) { rnd := basics.Round(proto.MaxBalLookback - shorterLookback) _, err = l.OnlineCirculation(rnd, rnd+basics.Round(proto.MaxBalLookback)) - require.Error(t, err) + require.ErrorContains(t, err, `round 316 before dbRound 317`) for i := basics.Round(proto.MaxBalLookback - shorterLookback + 1); i <= l.Latest(); i++ { online, err := l.OnlineCirculation(i, i+basics.Round(proto.MaxBalLookback)) require.NoError(t, err) @@ -2262,7 +2262,7 @@ func TestLedgerReloadShrinkDeltas(t *testing.T) { // check an error latest-1 for txid := range txnIDs[latest-1] { - require.Error(t, l.CheckDup(proto, nextRound, latest-maxValidity, latest-1, txid, ledgercore.Txlease{})) + require.ErrorContains(t, l.CheckDup(proto, nextRound, latest-maxValidity, latest-1, txid, ledgercore.Txlease{}), `txTail: tried to check for dup in missing round 639`) } } @@ -2670,7 +2670,7 @@ func TestLedgerMigrateV6ShrinkDeltas(t *testing.T) { // check an error latest-1 for txid := range txnIDs[latest-1] { - require.Error(t, l.CheckDup(proto, nextRound, latest-maxValidity, latest-1, txid, ledgercore.Txlease{})) + require.ErrorContains(t, l.CheckDup(proto, nextRound, latest-maxValidity, latest-1, txid, ledgercore.Txlease{}), `txTail: tried to check for dup in missing round 999`) } shorterLookback := config.GetDefaultLocal().MaxAcctLookback @@ -2691,7 +2691,7 @@ func TestLedgerMigrateV6ShrinkDeltas(t *testing.T) { rnd := basics.Round(proto.MaxBalLookback - shorterLookback) _, err = l2.OnlineCirculation(rnd, rnd+basics.Round(proto.MaxBalLookback)) - require.Error(t, err) + require.ErrorContains(t, err, `round 316 before dbRound 6`) for i := l2.Latest() - basics.Round(proto.MaxBalLookback-1); i <= l2.Latest(); i++ { online, err := l2.OnlineCirculation(i, i+basics.Round(proto.MaxBalLookback)) require.NoError(t, err) @@ -2724,7 +2724,7 @@ func TestLedgerMigrateV6ShrinkDeltas(t *testing.T) { // check an error latest-1 for txid := range txnIDs[latest-1] { - require.Error(t, l2.CheckDup(proto, nextRound, latest-maxValidity, latest-1, txid, ledgercore.Txlease{})) + require.ErrorContains(t, l2.CheckDup(proto, nextRound, latest-maxValidity, latest-1, txid, ledgercore.Txlease{}), `txTail: tried to check for dup in missing round 999`) } } @@ -3458,7 +3458,7 @@ func TestLedgerMaxBlockHistoryLookback(t *testing.T) { // make sure we can't get a block before the max lookback blk, err = l.Block(90) - require.Error(t, err) + require.ErrorContains(t, err, `ledger does not have entry 90 (latest 1500, committed 1500)`) require.Empty(t, blk) } diff --git a/ledger/ledgercore/catchpointlabel_test.go b/ledger/ledgercore/catchpointlabel_test.go index 5ac7e75d84..ccec1fde25 100644 --- a/ledger/ledgercore/catchpointlabel_test.go +++ b/ledger/ledgercore/catchpointlabel_test.go @@ -101,15 +101,15 @@ func TestCatchpointLabelParsing2(t *testing.T) { partitiontest.PartitionTest(t) _, _, err := ParseCatchpointLabel("5893060#KURJLS6EWBEVXTMLC7NP3NABTUMQP32QUJOBBW2TT23376L6RWJAB") - require.Error(t, err) + require.ErrorContains(t, err, `catchpoint parsing failed`) _, _, err = ParseCatchpointLabel("5893060KURJLS6EWBEVXTMLC7NP3NABTUMQP32QUJOBBW2TT23376L6RWJA") - require.Error(t, err) + require.ErrorContains(t, err, `catchpoint parsing failed`) _, _, err = ParseCatchpointLabel("5893060##KURJLS6EWBEVXTMLC7NP3NABTUMQP32QUJOBBW2TT23376L6RWJA") - require.Error(t, err) + require.ErrorContains(t, err, `catchpoint parsing failed`) _, _, err = ParseCatchpointLabel("5x893060#KURJLS6EWBEVXTMLC7NP3NABTUMQP32QUJOBBW2TT23376L6RWJA") - require.Error(t, err) + require.ErrorContains(t, err, `strconv.ParseUint: parsing "5x893060": invalid syntax`) _, _, err = ParseCatchpointLabel("-5893060#KURJLS6EWBEVXTMLC7NP3NABTUMQP32QUJOBBW2TT23376L6RWJA") - require.Error(t, err) + require.ErrorContains(t, err, `strconv.ParseUint: parsing "-5893060": invalid syntax`) _, _, err = ParseCatchpointLabel("5893060#aURJLS6EWBEVXTMLC7NP3NABTUMQP32QUJOBBW2TT23376L6RWJA") - require.Error(t, err) + require.ErrorContains(t, err, `illegal base32 data at input byte 0`) } diff --git a/ledger/store/blockdb/blockdb_test.go b/ledger/store/blockdb/blockdb_test.go index e9cd0742fe..2131ef5e93 100644 --- a/ledger/store/blockdb/blockdb_test.go +++ b/ledger/store/blockdb/blockdb_test.go @@ -73,7 +73,7 @@ func checkBlockDB(t *testing.T, tx *sql.Tx, blocks []testBlockEntry) { latest, err := BlockLatest(tx) if len(blocks) == 0 { - require.Error(t, err) + require.ErrorContains(t, err, `no blocks present`) } else { require.NoError(t, err) require.Equal(t, latest, basics.Round(len(blocks))-1) @@ -81,7 +81,7 @@ func checkBlockDB(t *testing.T, tx *sql.Tx, blocks []testBlockEntry) { earliest, err := BlockEarliest(tx) if len(blocks) == 0 { - require.Error(t, err) + require.ErrorContains(t, err, `no blocks present`) } else { require.NoError(t, err) require.Equal(t, earliest, blocks[0].block.BlockHeader.Round) @@ -99,7 +99,7 @@ func checkBlockDB(t *testing.T, tx *sql.Tx, blocks []testBlockEntry) { } _, err = BlockGet(tx, basics.Round(len(blocks))) - require.Error(t, err) + require.ErrorContains(t, err, `ledger does not have entry`) } func blockChainBlocks(be []testBlockEntry) []bookkeeping.Block { diff --git a/ledger/store/trackerdb/data_test.go b/ledger/store/trackerdb/data_test.go index 4524169b89..8cdde8be9b 100644 --- a/ledger/store/trackerdb/data_test.go +++ b/ledger/store/trackerdb/data_test.go @@ -1269,10 +1269,10 @@ func TestBaseAccountDataDecodeEmpty(t *testing.T) { var b BaseAccountData err := protocol.Decode([]byte{}, &b) - require.Error(t, err) + require.ErrorContains(t, err, `msgp: too few bytes left to read object`) err = protocol.Decode(nil, &b) - require.Error(t, err) + require.ErrorContains(t, err, `msgp: too few bytes left to read object`) err = protocol.Decode([]byte{0x80}, &b) require.NoError(t, err) diff --git a/ledger/store/trackerdb/dualdriver/accounts_writer_ext.go b/ledger/store/trackerdb/dualdriver/accounts_writer_ext.go index 73d5eb8918..ecfa1153e5 100644 --- a/ledger/store/trackerdb/dualdriver/accounts_writer_ext.go +++ b/ledger/store/trackerdb/dualdriver/accounts_writer_ext.go @@ -18,6 +18,7 @@ package dualdriver import ( "context" + "github.com/algorand/go-algorand/data/basics" "github.com/algorand/go-algorand/ledger/ledgercore" "github.com/algorand/go-algorand/ledger/store/trackerdb" diff --git a/ledger/store/trackerdb/dualdriver/stateproof_writer.go b/ledger/store/trackerdb/dualdriver/stateproof_writer.go index 188e86e83d..ffb85cbbce 100644 --- a/ledger/store/trackerdb/dualdriver/stateproof_writer.go +++ b/ledger/store/trackerdb/dualdriver/stateproof_writer.go @@ -18,6 +18,7 @@ package dualdriver import ( "context" + "github.com/algorand/go-algorand/data/basics" "github.com/algorand/go-algorand/ledger/ledgercore" "github.com/algorand/go-algorand/ledger/store/trackerdb" diff --git a/ledger/store/trackerdb/sqlitedriver/sql.go b/ledger/store/trackerdb/sqlitedriver/sql.go index f69363536b..f10ef4301c 100644 --- a/ledger/store/trackerdb/sqlitedriver/sql.go +++ b/ledger/store/trackerdb/sqlitedriver/sql.go @@ -19,6 +19,7 @@ package sqlitedriver import ( "database/sql" "fmt" + "github.com/algorand/go-algorand/data/basics" "github.com/algorand/go-algorand/ledger/ledgercore" "github.com/algorand/go-algorand/ledger/store/trackerdb" diff --git a/ledger/store/trackerdb/testsuite/accounts_ext_kv_test.go b/ledger/store/trackerdb/testsuite/accounts_ext_kv_test.go index f6d6852b26..8c0cd61d31 100644 --- a/ledger/store/trackerdb/testsuite/accounts_ext_kv_test.go +++ b/ledger/store/trackerdb/testsuite/accounts_ext_kv_test.go @@ -135,7 +135,7 @@ func CustomTestTxTail(t *customT) { // load TxTail's (error, must be the latest round) _, _, _, err = ar.LoadTxTail(context.Background(), basics.Round(1)) - require.Error(t, err) + require.ErrorContains(t, err, `txtail table contain unexpected round 2; round 1 was expected`) // load TxTail's txtails, hashes, readBaseRound, err := ar.LoadTxTail(context.Background(), basics.Round(2)) @@ -204,8 +204,7 @@ func CustomTestOnlineAccountParams(t *customT) { // lookup single round params (not found) _, err = aor.LookupOnlineRoundParams(basics.Round(9000)) - require.Error(t, err) - require.Equal(t, trackerdb.ErrNotFound, err) + require.ErrorIs(t, err, trackerdb.ErrNotFound) // read all round params readParams, endRound, err := ar.AccountsOnlineRoundParams() @@ -248,8 +247,7 @@ func CustomTestAccountLookupByRowID(t *customT) { // non-existing account _, err = ar.LookupAccountRowID(RandomAddress()) - require.Error(t, err) - require.Equal(t, err, trackerdb.ErrNotFound) + require.ErrorIs(t, err, trackerdb.ErrNotFound) // read account ref, err := ar.LookupAccountRowID(addrA) @@ -293,8 +291,7 @@ func CustomTestResourceLookupByRowID(t *customT) { // non-existing resource _, err = ar.LookupResourceDataByAddrID(refAccA, basics.CreatableIndex(100)) - require.Error(t, err) - require.Equal(t, err, trackerdb.ErrNotFound) + require.ErrorIs(t, err, trackerdb.ErrNotFound) // read resource data, err := ar.LookupResourceDataByAddrID(refAccA, aidxResA0) @@ -308,6 +305,5 @@ func CustomTestResourceLookupByRowID(t *customT) { // read resource on nil account _, err = ar.LookupResourceDataByAddrID(nil, basics.CreatableIndex(100)) - require.Error(t, err) - require.Equal(t, err, trackerdb.ErrNotFound) + require.ErrorIs(t, err, trackerdb.ErrNotFound) } diff --git a/ledger/store/trackerdb/testsuite/onlineaccounts_kv_test.go b/ledger/store/trackerdb/testsuite/onlineaccounts_kv_test.go index 9c0be20a60..ca9bf8fd26 100644 --- a/ledger/store/trackerdb/testsuite/onlineaccounts_kv_test.go +++ b/ledger/store/trackerdb/testsuite/onlineaccounts_kv_test.go @@ -308,8 +308,7 @@ func CustomTestLookupOnlineAccountDataByAddress(t *customT) { // check non-existing account nonExistingAddr := RandomAddress() _, _, err = ar.LookupOnlineAccountDataByAddress(nonExistingAddr) - require.Error(t, err) - require.Equal(t, trackerdb.ErrNotFound, err) // check the error type + require.ErrorIs(t, err, trackerdb.ErrNotFound) // read existing addr readRef, readData, err := ar.LookupOnlineAccountDataByAddress(addrA) diff --git a/ledger/store/trackerdb/testsuite/stateproofs_kv_test.go b/ledger/store/trackerdb/testsuite/stateproofs_kv_test.go index e474f5a8c4..4692132d1c 100644 --- a/ledger/store/trackerdb/testsuite/stateproofs_kv_test.go +++ b/ledger/store/trackerdb/testsuite/stateproofs_kv_test.go @@ -63,8 +63,7 @@ func CustomTestStateproofsReadWrite(t *customT) { // read non-existing item vc, err := spr.LookupSPContext(basics.Round(9000)) - require.Error(t, err) - require.Equal(t, trackerdb.ErrNotFound, err) + require.ErrorIs(t, err, trackerdb.ErrNotFound) // read back a single item vc, err = spr.LookupSPContext(basics.Round(0)) @@ -78,8 +77,7 @@ func CustomTestStateproofsReadWrite(t *customT) { // read delete items vc, err = spr.LookupSPContext(basics.Round(0)) - require.Error(t, err) - require.Equal(t, trackerdb.ErrNotFound, err) + require.ErrorIs(t, err, trackerdb.ErrNotFound) // read back remaining items vc, err = spr.LookupSPContext(basics.Round(1)) diff --git a/ledger/tracker_test.go b/ledger/tracker_test.go index 06563b6e88..cd083480ec 100644 --- a/ledger/tracker_test.go +++ b/ledger/tracker_test.go @@ -516,7 +516,6 @@ func TestTrackers_AccountUpdatesLedgerEvaluatorNoBlockHdr(t *testing.T) { tail: &txTail{}, } hdr, err := aul.BlockHdr(99) - require.Error(t, err) - require.Equal(t, ledgercore.ErrNoEntry{}, err) + require.ErrorIs(t, err, ledgercore.ErrNoEntry{}) require.Equal(t, bookkeeping.BlockHeader{}, hdr) } diff --git a/libgoal/libgoal_test.go b/libgoal/libgoal_test.go index eb4fb4f766..e4ff9c2d9e 100644 --- a/libgoal/libgoal_test.go +++ b/libgoal/libgoal_test.go @@ -58,28 +58,28 @@ func TestValidRounds(t *testing.T) { lastValid = 0 validRounds = maxTxnLife + 2 _, _, err = computeValidityRounds(firstValid, lastValid, validRounds, lastRound, maxTxnLife) - a.Error(err) + require.ErrorContains(t, err, `cannot construct transaction: txn validity period 1001 is greater than protocol max txn lifetime 1000`) a.Equal("cannot construct transaction: txn validity period 1001 is greater than protocol max txn lifetime 1000", err.Error()) firstValid = 0 lastValid = 1 validRounds = 2 _, _, err = computeValidityRounds(firstValid, lastValid, validRounds, lastRound, maxTxnLife) - a.Error(err) + require.ErrorContains(t, err, `cannot construct transaction: ambiguous input: lastValid = 1, validRounds = 2`) a.Equal("cannot construct transaction: ambiguous input: lastValid = 1, validRounds = 2", err.Error()) firstValid = 2 lastValid = 1 validRounds = 0 _, _, err = computeValidityRounds(firstValid, lastValid, validRounds, lastRound, maxTxnLife) - a.Error(err) + require.ErrorContains(t, err, `cannot construct transaction: txn would first be valid on round 2 which is after last valid round 1`) a.Equal("cannot construct transaction: txn would first be valid on round 2 which is after last valid round 1", err.Error()) firstValid = 1 lastValid = maxTxnLife + 2 validRounds = 0 _, _, err = computeValidityRounds(firstValid, lastValid, validRounds, lastRound, maxTxnLife) - a.Error(err) + require.ErrorContains(t, err, `cannot construct transaction: txn validity period ( 1 to 1002 ) is greater than protocol max txn lifetime 1000`) a.Equal("cannot construct transaction: txn validity period ( 1 to 1002 ) is greater than protocol max txn lifetime 1000", err.Error()) firstValid = 1 diff --git a/netdeploy/networkTemplates_test.go b/netdeploy/networkTemplates_test.go index 13aff8d842..5691b12ab4 100644 --- a/netdeploy/networkTemplates_test.go +++ b/netdeploy/networkTemplates_test.go @@ -54,7 +54,7 @@ func TestLoadMissingConfig(t *testing.T) { templateDir, err := filepath.Abs("../test/testdata/nettemplates") a.NoError(err) template, err := loadTemplate(filepath.Join(templateDir, ".json")) - a.Error(err) + require.ErrorContains(t, err, `.json: no such file or directory`) a.Equal(template.Genesis.NetworkName, "") } @@ -96,7 +96,7 @@ func TestValidate(t *testing.T) { templateDir, _ = filepath.Abs("../test/testdata/nettemplates") template, _ = loadTemplate(filepath.Join(templateDir, "NegativeStake.json")) err = template.Validate() - a.Error(err) + require.ErrorContains(t, err, `invalid template: negative stake on Genesis account W2`) templateDir, _ = filepath.Abs("../test/testdata/nettemplates") template, _ = loadTemplate(filepath.Join(templateDir, "TwoNodesOneRelay1000Accounts.json")) diff --git a/network/addr/addr_test.go b/network/addr/addr_test.go index d19889654a..918685e0cd 100644 --- a/network/addr/addr_test.go +++ b/network/addr/addr_test.go @@ -136,7 +136,7 @@ func TestParseHostURLOrMultiaddr(t *testing.T) { for _, addr := range badMultiAddrs { t.Run(addr, func(t *testing.T) { _, err := ParseHostOrURLOrMultiaddr(addr) - require.Error(t, err) + require.ErrorContains(t, err, `parse`) require.False(t, IsMultiaddr(addr)) }) } diff --git a/network/limited_reader_slurper_test.go b/network/limited_reader_slurper_test.go index c8c05ebd13..ba1d64e18c 100644 --- a/network/limited_reader_slurper_test.go +++ b/network/limited_reader_slurper_test.go @@ -190,7 +190,7 @@ func TestLimitedReaderSlurperPerMessageMaxSize(t *testing.T) { b = make([]byte, dataSize) crypto.RandBytes(b[:]) err := slurper.Read(bytes.NewBuffer(b)) - require.Error(t, err) + require.ErrorContains(t, err, `read limit exceeded`) } } } diff --git a/network/msgCompressor_test.go b/network/msgCompressor_test.go index 333c0b0baa..3a1a08ff3f 100644 --- a/network/msgCompressor_test.go +++ b/network/msgCompressor_test.go @@ -79,7 +79,7 @@ func TestZstdDecompress(t *testing.T) { compressed, err = zstd.Compress(nil, msg) require.NoError(t, err) decompressed, err = d.convert(compressed) - require.Error(t, err) + require.ErrorContains(t, err, `proposal data is too large: 20971530`) require.Nil(t, decompressed) } diff --git a/network/netidentity_test.go b/network/netidentity_test.go index 03c345fe79..c2ab5955d2 100644 --- a/network/netidentity_test.go +++ b/network/netidentity_test.go @@ -88,7 +88,7 @@ func TestIdentityChallengeSchemeVerifyRequestAndAttachResponse(t *testing.T) { require.Empty(t, r.Get(IdentityChallengeHeader)) require.Empty(t, chal) require.Empty(t, key) - require.Error(t, err) + require.ErrorContains(t, err, `illegal base64 data at input byte 4`) // happy path: response should be attached here h = http.Header{} @@ -198,7 +198,7 @@ func TestIdentityChallengeSchemeBadSignature(t *testing.T) { require.Empty(t, r.Get(IdentityChallengeHeader)) require.Empty(t, respChal) require.Empty(t, key) - require.Error(t, err) + require.ErrorContains(t, err, `identity challenge incorrectly signed`) } // TestIdentityChallengeSchemeBadPayload tests that the scheme will @@ -216,7 +216,7 @@ func TestIdentityChallengeSchemeBadPayload(t *testing.T) { require.Empty(t, r.Get(IdentityChallengeHeader)) require.Empty(t, respChal) require.Empty(t, key) - require.Error(t, err) + require.ErrorContains(t, err, `illegal base64 data at input byte 3`) } // TestIdentityChallengeSchemeBadResponseSignature tests that the scheme will @@ -247,7 +247,7 @@ func TestIdentityChallengeSchemeBadResponseSignature(t *testing.T) { key2, verificationMsg, err := i.VerifyResponse(r, origChal) require.Empty(t, key2) require.Empty(t, verificationMsg) - require.Error(t, err) + require.ErrorContains(t, err, `challenge response incorrectly signed`) } // TestIdentityChallengeSchemeBadResponsePayload tests that the scheme will @@ -269,7 +269,7 @@ func TestIdentityChallengeSchemeBadResponsePayload(t *testing.T) { key2, verificationMsg, err := i.VerifyResponse(r, origChal) require.Empty(t, key2) require.Empty(t, verificationMsg) - require.Error(t, err) + require.ErrorContains(t, err, `illegal base64 data at input byte 3`) } // TestIdentityChallengeSchemeWrongChallenge the scheme will @@ -295,7 +295,7 @@ func TestIdentityChallengeSchemeWrongChallenge(t *testing.T) { key2, verificationMsg, err := i.VerifyResponse(r, newIdentityChallengeValue()) require.Empty(t, key2) require.Empty(t, verificationMsg) - require.Error(t, err) + require.ErrorContains(t, err, `challenge response did not contain originally issued challenge value`) } func TestNewIdentityTracker(t *testing.T) { diff --git a/network/p2p/p2p_test.go b/network/p2p/p2p_test.go index 86e8e299de..a07262fd5e 100644 --- a/network/p2p/p2p_test.go +++ b/network/p2p/p2p_test.go @@ -76,7 +76,7 @@ func TestNetAddressToListenAddress(t *testing.T) { t.Run(fmt.Sprintf("input: %s", test.input), func(t *testing.T) { res, err := netAddressToListenAddress(test.input) if test.err { - require.Error(t, err) + require.ErrorContains(t, err, `invalid netAddress`) } else { require.NoError(t, err) require.Equal(t, test.output, res) diff --git a/network/p2pMetainfo_test.go b/network/p2pMetainfo_test.go index 08e8ada943..4add35f098 100644 --- a/network/p2pMetainfo_test.go +++ b/network/p2pMetainfo_test.go @@ -84,16 +84,14 @@ func TestReadPeerMetaHeaders(t *testing.T) { mockStream = new(MockStream) mockStream.On("Read", mock.Anything).Return([]byte{1}, nil).Once() _, err = readPeerMetaHeaders(mockStream, p2pPeer, n.supportedProtocolVersions) - assert.Error(t, err) - assert.Contains(t, err.Error(), "error reading response message length") + assert.ErrorContains(t, err, "error reading response message length") mockStream.AssertExpectations(t) // Error case: error reading length mockStream = new(MockStream) mockStream.On("Read", mock.Anything).Return([]byte{}, fmt.Errorf("read error")).Once() _, err = readPeerMetaHeaders(mockStream, p2pPeer, n.supportedProtocolVersions) - assert.Error(t, err) - assert.Contains(t, err.Error(), "error reading response message length") + assert.ErrorContains(t, err, "error reading response message length") mockStream.AssertExpectations(t) // Error case: incomplete message read @@ -101,8 +99,7 @@ func TestReadPeerMetaHeaders(t *testing.T) { mockStream.On("Read", mock.Anything).Return(lengthBytes, nil).Once() mockStream.On("Read", mock.Anything).Return(data[:len(data)/2], nil).Once() // Return only half the data _, err = readPeerMetaHeaders(mockStream, p2pPeer, n.supportedProtocolVersions) - assert.Error(t, err) - assert.Contains(t, err.Error(), "error reading response message") + assert.ErrorContains(t, err, "error reading response message") mockStream.AssertExpectations(t) // Error case: error reading message @@ -110,8 +107,7 @@ func TestReadPeerMetaHeaders(t *testing.T) { mockStream.On("Read", mock.Anything).Return(lengthBytes, nil).Once() mockStream.On("Read", mock.Anything).Return([]byte{}, fmt.Errorf("read error")).Once() _, err = readPeerMetaHeaders(mockStream, p2pPeer, n.supportedProtocolVersions) - assert.Error(t, err) - assert.Contains(t, err.Error(), "error reading response message") + assert.ErrorContains(t, err, "error reading response message") mockStream.AssertExpectations(t) // Error case: invalid messagepack (unmarshaling error) @@ -121,8 +117,7 @@ func TestReadPeerMetaHeaders(t *testing.T) { mockStream.On("Read", mock.Anything).Return(corruptedMsgpLength, nil).Once() mockStream.On("Read", mock.Anything).Return([]byte{0x99, 0x01, 0x02}, nil).Once() _, err = readPeerMetaHeaders(mockStream, p2pPeer, n.supportedProtocolVersions) - assert.Error(t, err) - assert.Contains(t, err.Error(), "error unmarshaling response message") + assert.ErrorContains(t, err, "error unmarshaling response message") mockStream.AssertExpectations(t) // Error case: no matching protocol version @@ -138,8 +133,7 @@ func TestReadPeerMetaHeaders(t *testing.T) { mockStream.On("Read", mock.Anything).Return(incompatibleLengthBytes, nil).Once() mockStream.On("Read", mock.Anything).Return(incompatibleData, nil).Once() _, err = readPeerMetaHeaders(mockStream, p2pPeer, n.supportedProtocolVersions) - assert.Error(t, err) - assert.Contains(t, err.Error(), "does not support any of the supported protocol versions") + assert.ErrorContains(t, err, "does not support any of the supported protocol versions") mockStream.AssertExpectations(t) } @@ -171,7 +165,6 @@ func TestWritePeerMetaHeaders(t *testing.T) { mockStream = new(MockStream) mockStream.On("Write", mock.Anything).Return(0, fmt.Errorf("write error")).Once() err = writePeerMetaHeaders(mockStream, p2pPeer, "1.0", n) - assert.Error(t, err) - assert.Contains(t, err.Error(), "error sending initial message") + assert.ErrorContains(t, err, "error sending initial message") mockStream.AssertExpectations(t) } diff --git a/network/vpack/lru_table_test.go b/network/vpack/lru_table_test.go index 8abf267a3b..d5aa7316d7 100644 --- a/network/vpack/lru_table_test.go +++ b/network/vpack/lru_table_test.go @@ -32,13 +32,11 @@ func TestLRUTableSizeValidation(t *testing.T) { // Test invalid size (not power of 2) _, err := NewStatefulEncoder(100) - require.Error(t, err) - require.Contains(t, err.Error(), "must be a power of 2") + require.ErrorContains(t, err, "must be a power of 2") // Test invalid size (too small) _, err = NewStatefulEncoder(8) - require.Error(t, err) - require.Contains(t, err.Error(), "at least 16") + require.ErrorContains(t, err, "at least 16") // Test valid sizes for _, size := range []uint{16, 32, 64, 128, 256, 512, 1024, 2048} { diff --git a/network/wsNetwork_test.go b/network/wsNetwork_test.go index 28c73b133d..ae3df0cec4 100644 --- a/network/wsNetwork_test.go +++ b/network/wsNetwork_test.go @@ -4735,7 +4735,7 @@ func TestWebsocketNetworkHTTPClient(t *testing.T) { require.Equal(t, http.StatusPreconditionFailed, resp.StatusCode) // not enough ws peer headers _, err = netB.GetHTTPClient("invalid") - require.Error(t, err) + require.ErrorContains(t, err, `could not parse a host from url`) } // TestPeerComparisonInBroadcast tests that the peer comparison in the broadcast function works as expected diff --git a/network/wsPeer_test.go b/network/wsPeer_test.go index 3fc21e9d09..eb3f05d097 100644 --- a/network/wsPeer_test.go +++ b/network/wsPeer_test.go @@ -133,17 +133,17 @@ func TestVersionToMajorMinor(t *testing.T) { require.Equal(t, int64(2), mi) ma, mi, err = versionToMajorMinor("1.2.3") - require.Error(t, err) + require.ErrorContains(t, err, `version 1.2.3 does not have two components`) require.Zero(t, ma) require.Zero(t, mi) ma, mi, err = versionToMajorMinor("1") - require.Error(t, err) + require.ErrorContains(t, err, `version 1 does not have two components`) require.Zero(t, ma) require.Zero(t, mi) ma, mi, err = versionToMajorMinor("a.b") - require.Error(t, err) + require.ErrorContains(t, err, `strconv.ParseInt: parsing "a": invalid syntax`) require.Zero(t, ma) require.Zero(t, mi) } diff --git a/node/follower_node_test.go b/node/follower_node_test.go index bd18dc3595..87789586c0 100644 --- a/node/follower_node_test.go +++ b/node/follower_node_test.go @@ -110,16 +110,16 @@ func TestErrors(t *testing.T) { t.Parallel() // Validates that expected functions are disabled node := setupFollowNode(t) - require.Error(t, node.BroadcastSignedTxGroup([]transactions.SignedTxn{})) - require.Error(t, node.BroadcastInternalSignedTxGroup([]transactions.SignedTxn{})) + require.ErrorContains(t, node.BroadcastSignedTxGroup([]transactions.SignedTxn{}), `cannot broadcast txns in follower mode`) + require.ErrorContains(t, node.BroadcastInternalSignedTxGroup([]transactions.SignedTxn{}), `cannot broadcast internal signed txn group in follower mode`) _, err := node.Simulate(simulation.Request{}) - require.Error(t, err) + require.ErrorContains(t, err, `expected 1 transaction group, got 0`) _, err = node.GetParticipationKey(account.ParticipationID{}) - require.Error(t, err) - require.Error(t, node.RemoveParticipationKey(account.ParticipationID{})) - require.Error(t, node.AppendParticipationKeys(account.ParticipationID{}, account.StateProofKeys{})) + require.ErrorContains(t, err, `cannot get participation key in follower mode`) + require.ErrorContains(t, node.RemoveParticipationKey(account.ParticipationID{}), `cannot remove participation key in follower mode`) + require.ErrorContains(t, node.AppendParticipationKeys(account.ParticipationID{}, account.StateProofKeys{}), `cannot append participation keys in follower mode`) _, err = node.InstallParticipationKey([]byte{}) - require.Error(t, err) + require.ErrorContains(t, err, `cannot install participation key in follower mode`) } func TestDevModeWarning(t *testing.T) { diff --git a/node/node_test.go b/node/node_test.go index aa7206844b..fc31d945f7 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -626,8 +626,7 @@ func TestMismatchingGenesisDirectoryPermissions(t *testing.T) { node, err := MakeFull(log, testDirectroy, config.GetDefaultLocal(), []string{}, genesis) require.Nil(t, node) - require.Error(t, err) - require.Contains(t, err.Error(), "permission denied") + require.ErrorContains(t, err, "permission denied") require.NoError(t, os.Chmod(testDirectroy, 1700)) require.NoError(t, os.RemoveAll(testDirectroy)) diff --git a/protocol/codec_test.go b/protocol/codec_test.go index 1971a79f9d..4e01450377 100644 --- a/protocol/codec_test.go +++ b/protocol/codec_test.go @@ -209,7 +209,7 @@ func TestMsgpDecode(t *testing.T) { var tag Tag = "test" dec := NewMsgpDecoderBytes([]byte{1, 2, 3}) err := dec.Decode(&tag) - require.Error(t, err) + require.ErrorContains(t, err, `msgp: attempted to decode type "int" with method for "str"`) data := EncodeMsgp(tag) dec = NewMsgpDecoderBytes(data) @@ -233,7 +233,6 @@ func TestMsgpDecode(t *testing.T) { require.Equal(t, tags[i], tag2) } err = dec.Decode(&tag2) - require.Error(t, err) require.ErrorIs(t, err, io.EOF) } diff --git a/rpcs/blockService_test.go b/rpcs/blockService_test.go index 494b4772ae..cbf8fd4730 100644 --- a/rpcs/blockService_test.go +++ b/rpcs/blockService_test.go @@ -501,8 +501,7 @@ func TestRedirectExceptions(t *testing.T) { require.NoError(t, err) _, err = client.Do(requestNodeB) - require.Error(t, err) - require.Contains(t, err.Error(), "stopped after 10 redirects") + require.ErrorContains(t, err, "stopped after 10 redirects") } var poolAddr = basics.Address{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff} diff --git a/rpcs/txSyncer_test.go b/rpcs/txSyncer_test.go index ac3be12335..eb6152544e 100644 --- a/rpcs/txSyncer_test.go +++ b/rpcs/txSyncer_test.go @@ -210,7 +210,7 @@ func TestSyncFromUnsupportedClient(t *testing.T) { syncer.ctx, syncer.cancel = context.WithCancel(context.Background()) syncer.log = logging.TestingLog(t) - require.Error(t, syncer.syncFromClient(&client)) + require.ErrorContains(t, syncer.syncFromClient(&client), `TxSyncer.Sync: peer 'mock.address.' error 'old failWithNil'`) require.Zero(t, handler.messageCounter.Load()) } @@ -227,7 +227,7 @@ func TestSyncFromClientAndQuit(t *testing.T) { syncer.ctx, syncer.cancel = context.WithCancel(context.Background()) syncer.log = logging.TestingLog(t) syncer.cancel() - require.Error(t, syncer.syncFromClient(&client)) + require.ErrorContains(t, syncer.syncFromClient(&client), `TxSyncer.Sync: peer 'mock.address.' error 'cancelled'`) require.Zero(t, handler.messageCounter.Load()) } @@ -243,7 +243,7 @@ func TestSyncFromClientAndError(t *testing.T) { // Since syncer is not Started, set the context here syncer.ctx, syncer.cancel = context.WithCancel(context.Background()) syncer.log = logging.TestingLog(t) - require.Error(t, syncer.syncFromClient(&client)) + require.ErrorContains(t, syncer.syncFromClient(&client), `TxSyncer.Sync: peer 'mock.address.' error 'failing call'`) require.Zero(t, handler.messageCounter.Load()) } @@ -260,7 +260,7 @@ func TestSyncFromClientAndTimeout(t *testing.T) { // Since syncer is not Started, set the context here syncer.ctx, syncer.cancel = context.WithCancel(context.Background()) syncer.log = logging.TestingLog(t) - require.Error(t, syncer.syncFromClient(&client)) + require.ErrorContains(t, syncer.syncFromClient(&client), `TxSyncer.Sync: peer 'mock.address.' error 'cancelled'`) require.Zero(t, handler.messageCounter.Load()) } diff --git a/stateproof/verify/stateproof_test.go b/stateproof/verify/stateproof_test.go index 8654531b3e..db964c2ca5 100644 --- a/stateproof/verify/stateproof_test.go +++ b/stateproof/verify/stateproof_test.go @@ -17,6 +17,8 @@ package verify import ( + "testing" + "github.com/algorand/go-algorand/config" "github.com/algorand/go-algorand/crypto/stateproof" "github.com/algorand/go-algorand/data/basics" @@ -27,7 +29,6 @@ import ( "github.com/algorand/go-algorand/protocol" "github.com/algorand/go-algorand/test/partitiontest" "github.com/stretchr/testify/require" - "testing" ) func invokeValidateStateProof(latestRoundInIntervalHdr *bookkeeping.BlockHeader, @@ -159,7 +160,7 @@ func TestStateProofParams(t *testing.T) { var hdr bookkeeping.BlockHeader _, err := GetProvenWeight(&votersHdr, &hdr) - require.Error(t, err) // not enabled + require.ErrorContains(t, err, `state proofs are not enabled`) votersHdr.CurrentProtocol = "TestStateProofParams" proto := config.Consensus[votersHdr.CurrentProtocol] @@ -167,12 +168,12 @@ func TestStateProofParams(t *testing.T) { config.Consensus[votersHdr.CurrentProtocol] = proto votersHdr.Round = 1 _, err = GetProvenWeight(&votersHdr, &hdr) - require.Error(t, err) // wrong round + require.ErrorContains(t, err, `not a multiple of`) votersHdr.Round = 2 hdr.Round = 3 _, err = GetProvenWeight(&votersHdr, &hdr) - require.Error(t, err) // wrong round + require.ErrorContains(t, err, `not 2 ahead of voters`) // Covers all cases except overflow } diff --git a/test/e2e-go/features/catchup/catchpointCatchup_test.go b/test/e2e-go/features/catchup/catchpointCatchup_test.go index c4ff7a3040..d216e21e06 100644 --- a/test/e2e-go/features/catchup/catchpointCatchup_test.go +++ b/test/e2e-go/features/catchup/catchpointCatchup_test.go @@ -45,6 +45,7 @@ import ( "github.com/algorand/go-algorand/node" "github.com/algorand/go-algorand/nodecontrol" "github.com/algorand/go-algorand/protocol" + "github.com/algorand/go-algorand/test/errorcontains" "github.com/algorand/go-algorand/test/framework/fixtures" "github.com/algorand/go-algorand/test/partitiontest" ) @@ -814,7 +815,7 @@ func TestReadyEndpoint(t *testing.T) { // The primary node is catching up with its previous catchpoint // Its status contain a catchpoint it is catching-up against, // so it should not be ready, and ready-ness endpoint should 503 err. - a.Error(fixture.GetAlgodClientForController(primaryNode).ReadyCheck()) + errorcontains.CaptureError(t, fixture.GetAlgodClientForController(primaryNode).ReadyCheck()) status1, err := client1.Status() a.NoError(err) @@ -957,7 +958,7 @@ func TestNodeTxSyncRestart(t *testing.T) { // the transaction should not be confirmed yet _, err = fixture.WaitForConfirmedTxn(0, tx.ID().String()) - a.Error(err) + errorcontains.CaptureError(t, err) // Wait for the catchup for t := 0; t < 10; t++ { diff --git a/test/e2e-go/features/participation/onlineOfflineParticipation_test.go b/test/e2e-go/features/participation/onlineOfflineParticipation_test.go index 21193d120f..6a8f0ff4eb 100644 --- a/test/e2e-go/features/participation/onlineOfflineParticipation_test.go +++ b/test/e2e-go/features/participation/onlineOfflineParticipation_test.go @@ -28,6 +28,7 @@ import ( "github.com/algorand/go-algorand/data/basics" "github.com/algorand/go-algorand/protocol" "github.com/algorand/go-algorand/test/e2e-go/globals" + "github.com/algorand/go-algorand/test/errorcontains" "github.com/algorand/go-algorand/test/framework/fixtures" "github.com/algorand/go-algorand/test/partitiontest" ) @@ -68,14 +69,14 @@ func TestParticipationKeyOnlyAccountParticipatesCorrectly(t *testing.T) { wh, err := client.GetUnencryptedWalletHandle() a.NoError(err, "should get unencrypted wallet handle") _, err = client.SendPaymentFromWallet(wh, nil, partkeyOnlyAccount, richAccount, amountToSend, transactionFee, nil, "", basics.Round(0), basics.Round(0)) - a.Error(err, "attempt to send money from partkey-only account should be treated as though wallet is not controlled") + errorcontains.CaptureError(t, err, "attempt to send money from partkey-only account should be treated as though wallet is not controlled") // partkeyonly_account attempts to go offline, should fail (no rootkey to sign txn with) goOfflineUTx, err := client.MakeUnsignedGoOfflineTx(partkeyOnlyAccount, 0, 0, transactionFee, [32]byte{}) a.NoError(err, "should be able to make go offline tx") wh, err = client.GetUnencryptedWalletHandle() a.NoError(err, "should get unencrypted wallet handle") _, err = client.SignAndBroadcastTransaction(wh, nil, goOfflineUTx) - a.Error(err, "partkey only account should fail to go offline") + errorcontains.CaptureError(t, err, "partkey only account should fail to go offline") } func waitForAccountToProposeBlock(a *require.Assertions, fixture *fixtures.RestClientFixture, account string, window int) bool { diff --git a/test/e2e-go/features/privatenet/privatenet_test.go b/test/e2e-go/features/privatenet/privatenet_test.go index 36232faf98..ee005a6e56 100644 --- a/test/e2e-go/features/privatenet/privatenet_test.go +++ b/test/e2e-go/features/privatenet/privatenet_test.go @@ -20,6 +20,7 @@ package privatenet import ( "testing" + "github.com/algorand/go-algorand/test/errorcontains" "github.com/algorand/go-algorand/test/framework/fixtures" "github.com/algorand/go-algorand/test/partitiontest" "github.com/stretchr/testify/require" @@ -47,7 +48,7 @@ func TestPrivateNetworkImportKeys(t *testing.T) { // Check that if there is an existing directory with same name, test fails. errStr, err := goalFixture.NetworkPregen(defaultTemplate, tmpGenDir) - require.Error(t, err) + errorcontains.CaptureError(t, err) require.Contains(t, errStr, "already exists and is not empty") // Then try importing files from same template. diff --git a/test/e2e-go/features/stateproofs/stateproofs_test.go b/test/e2e-go/features/stateproofs/stateproofs_test.go index e914d5e80e..2ff3bbfd9d 100644 --- a/test/e2e-go/features/stateproofs/stateproofs_test.go +++ b/test/e2e-go/features/stateproofs/stateproofs_test.go @@ -45,6 +45,7 @@ import ( "github.com/algorand/go-algorand/libgoal/participation" "github.com/algorand/go-algorand/nodecontrol" "github.com/algorand/go-algorand/protocol" + "github.com/algorand/go-algorand/test/errorcontains" "github.com/algorand/go-algorand/test/framework/fixtures" "github.com/algorand/go-algorand/test/partitiontest" ) @@ -1087,7 +1088,7 @@ func TestAtMostOneSPFullPoolWithLoad(t *testing.T) { // The pool is full, and only one SP transaction will be admitted in per round. Otherwise, pool is full error will be returned // However, if this is the lucky SP transaction to get into the pool, it will eventually be rejected by ValidateStateProof and a different // error will be returned - require.Error(t, err) + errorcontains.CaptureError(t, err) time.Sleep(25 * time.Millisecond) } }() @@ -1113,7 +1114,7 @@ func TestAtMostOneSPFullPoolWithLoad(t *testing.T) { cntr = cntr + 1 // ignore the returned error (most of the time will be error) _, err := relay.SendPaymentFromUnencryptedWallet(account0, account0, params.MinFee, ps.amount, []byte{byte(params.LastRound)}) - require.Error(t, err) + errorcontains.CaptureError(t, err) require.Equal(t, "HTTP 400 Bad Request: TransactionPool.checkPendingQueueSize: transaction pool have reached capacity", err.Error()) time.Sleep(25 * time.Millisecond) } diff --git a/test/e2e-go/features/teal/compile_test.go b/test/e2e-go/features/teal/compile_test.go index 7cd71d191d..73757a3f73 100644 --- a/test/e2e-go/features/teal/compile_test.go +++ b/test/e2e-go/features/teal/compile_test.go @@ -25,6 +25,7 @@ import ( "github.com/algorand/go-algorand/config" "github.com/algorand/go-algorand/crypto" "github.com/algorand/go-algorand/data/transactions/logic" + "github.com/algorand/go-algorand/test/errorcontains" "github.com/algorand/go-algorand/test/framework/fixtures" "github.com/algorand/go-algorand/test/partitiontest" ) @@ -82,7 +83,7 @@ func TestTealCompile(t *testing.T) { a.Equal("YOE6C22GHCTKAN3HU4SE5PGIPN5UKXAJTXCQUPJ3KKF5HOAH646A", hash.String()) compiledProgram, hash, _, err = libGoalClient.Compile([]byte("bad program"), false) - a.Error(err, "An invalid program should result in a compilation failure") + errorcontains.CaptureError(t, err, "An invalid program should result in a compilation failure") a.Nil(compiledProgram) a.Equal(crypto.Digest{}, hash) } diff --git a/test/e2e-go/features/transactions/asset_test.go b/test/e2e-go/features/transactions/asset_test.go index 16f7787113..30e7f90443 100644 --- a/test/e2e-go/features/transactions/asset_test.go +++ b/test/e2e-go/features/transactions/asset_test.go @@ -31,6 +31,7 @@ import ( "github.com/algorand/go-algorand/data/transactions" "github.com/algorand/go-algorand/libgoal" "github.com/algorand/go-algorand/protocol" + "github.com/algorand/go-algorand/test/errorcontains" "github.com/algorand/go-algorand/test/framework/fixtures" "github.com/algorand/go-algorand/test/partitiontest" ) @@ -264,7 +265,7 @@ func TestAssetConfig(t *testing.T) { // Creating more assets should return an error tx, err = client.MakeUnsignedAssetCreateTx(1, false, manager, reserve, freeze, clawback, "toomany", "toomany", assetURL, assetMetadataHash, 0) _, err = helperFillSignBroadcast(client, wh, account0, tx, err) - a.Error(err) + errorcontains.CaptureError(t, err) a.True(strings.Contains(err.Error(), "too many assets in account:")) } @@ -399,7 +400,7 @@ func TestAssetConfig(t *testing.T) { // Should not be able to close account before destroying assets _, err = client.SendPaymentFromWallet(wh, nil, account0, "", 0, 0, nil, reserve, 0, 0) - a.Error(err) + errorcontains.CaptureError(t, err) a.True(strings.Contains(err.Error(), "cannot close:")) a.True(strings.Contains(err.Error(), "outstanding assets")) @@ -676,11 +677,11 @@ func TestAssetGroupCreateSendDestroy(t *testing.T) { // asset 3 (create + destroy) not available _, err = client1.AssetInformation(assetID3) - a.Error(err) + errorcontains.CaptureError(t, err) // sending it should fail txSend, err = client1.MakeUnsignedAssetSendTx(assetID3, 0, account1, "", "") _, err = helperFillSignBroadcast(client1, wh1, account1, txSend, err) - a.Error(err) + errorcontains.CaptureError(t, err) } func TestAssetSend(t *testing.T) { @@ -765,7 +766,7 @@ func TestAssetSend(t *testing.T) { tx, err = client.MakeUnsignedAssetSendTx(nonFrozenIdx, 0, extra, "", "") _, err = helperFillSignBroadcast(client, wh, extra, tx, err) - a.Error(err) + errorcontains.CaptureError(t, err) a.True(strings.Contains(err.Error(), "overspend")) a.True(strings.Contains(err.Error(), "tried to spend")) @@ -779,14 +780,14 @@ func TestAssetSend(t *testing.T) { // after opting in, should succeed for non-frozen asset. tx, err = client.MakeUnsignedAssetSendTx(nonFrozenIdx, 1, extra, "", "") _, err = helperFillSignBroadcast(client, wh, account0, tx, err) - a.Error(err) + errorcontains.CaptureError(t, err) a.True(strings.Contains(err.Error(), "asset")) a.True(strings.Contains(err.Error(), "missing from")) // Clawback assets to an account that hasn't opted in should fail tx, err = client.MakeUnsignedAssetSendTx(nonFrozenIdx, 1, extra, "", account0) _, err = helperFillSignBroadcast(client, wh, clawback, tx, err) - a.Error(err) + errorcontains.CaptureError(t, err) a.True(strings.Contains(err.Error(), "asset")) a.True(strings.Contains(err.Error(), "missing from")) @@ -798,14 +799,14 @@ func TestAssetSend(t *testing.T) { // Account hasn't opted in yet. sending will fail tx, err = client.MakeUnsignedAssetSendTx(nonFrozenIdx, 1, extra, "", "") _, err = helperFillSignBroadcast(client, wh, account0, tx, err) - a.Error(err) + errorcontains.CaptureError(t, err) a.True(strings.Contains(err.Error(), "asset")) a.True(strings.Contains(err.Error(), "missing from")) // Account hasn't opted in yet. clawback to will fail tx, err = client.MakeUnsignedAssetSendTx(nonFrozenIdx, 1, extra, "", account0) _, err = helperFillSignBroadcast(client, wh, clawback, tx, err) - a.Error(err) + errorcontains.CaptureError(t, err) a.True(strings.Contains(err.Error(), "asset")) a.True(strings.Contains(err.Error(), "missing from")) @@ -822,7 +823,7 @@ func TestAssetSend(t *testing.T) { tx, err = client.MakeUnsignedAssetSendTx(frozenIdx, 1, extra, "", "") _, err = helperFillSignBroadcast(client, wh, account0, tx, err) - a.Error(err) + errorcontains.CaptureError(t, err) a.True(strings.Contains(err.Error(), "asset frozen in recipient")) tx, err = client.MakeUnsignedAssetSendTx(nonFrozenIdx, 10, extra, "", "") @@ -851,13 +852,13 @@ func TestAssetSend(t *testing.T) { // Should not be able to send more than is available tx, err = client.MakeUnsignedAssetSendTx(nonFrozenIdx, 11, extra, "", "") _, err = helperFillSignBroadcast(client, wh, extra, tx, err) - a.Error(err) + errorcontains.CaptureError(t, err) a.True(strings.Contains(err.Error(), "underflow on subtracting 11 from sender amount 10")) // Should not be able to clawback more than is available tx, err = client.MakeUnsignedAssetSendTx(nonFrozenIdx, 11, account0, "", extra) _, err = helperFillSignBroadcast(client, wh, clawback, tx, err) - a.Error(err) + errorcontains.CaptureError(t, err) a.True(strings.Contains(err.Error(), "underflow on subtracting 11 from sender amount 10")) tx, err = client.MakeUnsignedAssetSendTx(nonFrozenIdx, 10, extra, "", "") @@ -868,7 +869,7 @@ func TestAssetSend(t *testing.T) { // be able to change frozen status) tx, err = client.MakeUnsignedAssetFreezeTx(nonFrozenIdx, extra, true) _, err = helperFillSignBroadcast(client, wh, account0, tx, err) - a.Error(err) + errorcontains.CaptureError(t, err) a.True(strings.Contains(err.Error(), "freeze not allowed: sender")) tx, err = client.MakeUnsignedAssetFreezeTx(nonFrozenIdx, extra, true) @@ -887,7 +888,7 @@ func TestAssetSend(t *testing.T) { tx, err = client.MakeUnsignedAssetSendTx(nonFrozenIdx, 1, extra, "", "") _, err = helperFillSignBroadcast(client, wh, extra, tx, err) - a.Error(err) + errorcontains.CaptureError(t, err) a.True(strings.Contains(err.Error(), "frozen in")) // Clawback should be able to claim money out of both frozen and non-frozen accounts, @@ -900,7 +901,7 @@ func TestAssetSend(t *testing.T) { tx, err = client.MakeUnsignedAssetSendTx(nonFrozenIdx, 5, account0, "", extra) _, err = helperFillSignBroadcast(client, wh, account0, tx, err) - a.Error(err) + errorcontains.CaptureError(t, err) a.True(strings.Contains(err.Error(), "clawback not allowed: sender")) tx, err = client.MakeUnsignedAssetSendTx(nonFrozenIdx, 5, account0, "", extra) diff --git a/test/e2e-go/features/transactions/group_test.go b/test/e2e-go/features/transactions/group_test.go index 0e580fda39..de68398cf8 100644 --- a/test/e2e-go/features/transactions/group_test.go +++ b/test/e2e-go/features/transactions/group_test.go @@ -26,6 +26,7 @@ import ( "github.com/algorand/go-algorand/config" "github.com/algorand/go-algorand/data/transactions" "github.com/algorand/go-algorand/protocol" + "github.com/algorand/go-algorand/test/errorcontains" "github.com/algorand/go-algorand/test/framework/fixtures" "github.com/algorand/go-algorand/test/partitiontest" ) @@ -75,14 +76,14 @@ func TestGroupTransactions(t *testing.T) { // submitting the transactions individually should fail _, err = client.BroadcastTransaction(stx1) - a.Error(err) + errorcontains.CaptureError(t, err) _, err = client.BroadcastTransaction(stx2) - a.Error(err) + errorcontains.CaptureError(t, err) // wrong order should fail err = client.BroadcastTransactionGroup([]transactions.SignedTxn{stx2, stx1}) - a.Error(err) + errorcontains.CaptureError(t, err) // correct order should succeed err = client.BroadcastTransactionGroup([]transactions.SignedTxn{stx1, stx2}) @@ -208,7 +209,7 @@ func TestGroupTransactionsDifferentSizes(t *testing.T) { // broadcasting group should now fail err = client.BroadcastTransactionGroup(stxns) - a.Error(err) + errorcontains.CaptureError(t, err) } } @@ -272,7 +273,7 @@ func TestGroupTransactionsSubmission(t *testing.T) { // send gs-1 reduced := stxns[:len(stxns)-1] err = client.BroadcastTransactionGroup(reduced) - a.Error(err) + errorcontains.CaptureError(t, err) if len(reduced) == 0 { a.Contains(err.Error(), "empty txgroup") } else { @@ -282,7 +283,7 @@ func TestGroupTransactionsSubmission(t *testing.T) { // send gs+1 expanded := append(stxns, sampleStxn) err = client.BroadcastTransactionGroup(expanded) - a.Error(err) + errorcontains.CaptureError(t, err) if len(expanded) >= exceedGroupSize { a.Contains(err.Error(), "group size") a.Contains(err.Error(), fmt.Sprintf("%d", maxTxGroupSize)) diff --git a/test/e2e-go/features/transactions/lease_test.go b/test/e2e-go/features/transactions/lease_test.go index 135dea6073..bcce8da2d6 100644 --- a/test/e2e-go/features/transactions/lease_test.go +++ b/test/e2e-go/features/transactions/lease_test.go @@ -23,6 +23,7 @@ import ( "github.com/stretchr/testify/require" "github.com/algorand/go-algorand/data/basics" + "github.com/algorand/go-algorand/test/errorcontains" "github.com/algorand/go-algorand/test/framework/fixtures" "github.com/algorand/go-algorand/test/partitiontest" ) @@ -73,7 +74,7 @@ func TestLeaseTransactionsSameSender(t *testing.T) { // submitting the second transaction should fail _, err = client.BroadcastTransaction(stx2) - a.Error(err) + errorcontains.CaptureError(t, err) // wait for the txids and check balance txids := make(map[string]string) @@ -223,7 +224,7 @@ func TestLeaseRegressionFaultyFirstValidCheckNew_2f3880f7(t *testing.T) { // submitting the second transaction should fail _, err = client.BroadcastTransaction(stx2) - a.Error(err) + errorcontains.CaptureError(t, err) } func TestLeaseTransactionsSameSenderDifferentLease(t *testing.T) { @@ -428,7 +429,7 @@ func TestOverlappingLeases(t *testing.T) { // submitting the second transaction should fail right away _, err = client.BroadcastTransaction(stx2) - a.Error(err) + errorcontains.CaptureError(t, err) // wait for the first tx to confirm txids := make(map[string]string) @@ -439,7 +440,7 @@ func TestOverlappingLeases(t *testing.T) { // submitting the second transaction should still fail _, err = client.BroadcastTransaction(stx2) - a.Error(err) + errorcontains.CaptureError(t, err) // wait for a round after the first txn was confirmed, but before its // lease has expired @@ -447,7 +448,7 @@ func TestOverlappingLeases(t *testing.T) { // submitting the second transaction should still fail _, err = client.BroadcastTransaction(stx2) - a.Error(err) + errorcontains.CaptureError(t, err) // wait for us to be building leaseStart + firstTxLeaseLife + 1, where // the first txn's lease should have expired diff --git a/test/e2e-go/features/transactions/onlineStatusChange_test.go b/test/e2e-go/features/transactions/onlineStatusChange_test.go index 98b1f76708..504612495a 100644 --- a/test/e2e-go/features/transactions/onlineStatusChange_test.go +++ b/test/e2e-go/features/transactions/onlineStatusChange_test.go @@ -26,6 +26,7 @@ import ( "github.com/algorand/go-algorand/data/basics" "github.com/algorand/go-algorand/libgoal/participation" + "github.com/algorand/go-algorand/test/errorcontains" "github.com/algorand/go-algorand/test/framework/fixtures" "github.com/algorand/go-algorand/test/partitiontest" ) @@ -184,7 +185,7 @@ func TestCloseOnError(t *testing.T) { a.NoError(err) // check duplicate keys does not crash _, err = client.AddParticipationKey(partkeyFile) - a.Error(err) + errorcontains.CaptureError(t, err) a.Contains(err.Error(), "cannot register duplicate participation key") // check lastValid < firstValid does not crash _, _, err = client.GenParticipationKeys(initiallyOffline, curRound+1001, curRound+1000, 0) diff --git a/test/e2e-go/kmd/e2e_kmd_server_client_test.go b/test/e2e-go/kmd/e2e_kmd_server_client_test.go index fe326f3ea9..cfd0d09e66 100644 --- a/test/e2e-go/kmd/e2e_kmd_server_client_test.go +++ b/test/e2e-go/kmd/e2e_kmd_server_client_test.go @@ -24,6 +24,7 @@ import ( "github.com/algorand/go-algorand/daemon/kmd/client" "github.com/algorand/go-algorand/daemon/kmd/lib/kmdapi" + "github.com/algorand/go-algorand/test/errorcontains" "github.com/algorand/go-algorand/test/framework/fixtures" "github.com/algorand/go-algorand/test/partitiontest" ) @@ -64,7 +65,7 @@ func TestBadAuthErrs(t *testing.T) { req := kmdapi.APIV1GETWalletsRequest{} resp := kmdapi.APIV1GETWalletsResponse{} err = client.DoV1Request(req, &resp) - a.Error(err) + errorcontains.CaptureError(t, err) } func TestGoodAuthSucceeds(t *testing.T) { diff --git a/test/e2e-go/kmd/e2e_kmd_wallet_keyops_test.go b/test/e2e-go/kmd/e2e_kmd_wallet_keyops_test.go index 142aad39aa..a2e8dc9f6f 100644 --- a/test/e2e-go/kmd/e2e_kmd_wallet_keyops_test.go +++ b/test/e2e-go/kmd/e2e_kmd_wallet_keyops_test.go @@ -28,6 +28,7 @@ import ( "github.com/algorand/go-algorand/data/transactions" "github.com/algorand/go-algorand/data/transactions/logic" "github.com/algorand/go-algorand/protocol" + "github.com/algorand/go-algorand/test/errorcontains" "github.com/algorand/go-algorand/test/framework/fixtures" "github.com/algorand/go-algorand/test/partitiontest" ) @@ -123,7 +124,7 @@ func TestImportKey(t *testing.T) { err = f.Client.DoV1Request(req1, &resp1) // Should fail (duplicate key) - a.Error(err) + errorcontains.CaptureError(t, err) // List public keys req2 := kmdapi.APIV1POSTKeyListRequest{ @@ -202,7 +203,7 @@ func TestExportKey(t *testing.T) { } resp3 := kmdapi.APIV1POSTKeyExportResponse{} err = f.Client.DoV1Request(req3, &resp3) - a.Error(err) + errorcontains.CaptureError(t, err) } func TestDeleteKey(t *testing.T) { @@ -248,7 +249,7 @@ func TestDeleteKey(t *testing.T) { } resp2 := kmdapi.APIV1DELETEKeyResponse{} err = f.Client.DoV1Request(req2, &resp2) - a.Error(err) + errorcontains.CaptureError(t, err) // Try to delete the key req3 := kmdapi.APIV1DELETEKeyRequest{ @@ -476,7 +477,7 @@ func TestMasterKeyImportExport(t *testing.T) { } resp2 := kmdapi.APIV1POSTMasterKeyExportResponse{} err = f.Client.DoV1Request(req2, &resp2) - a.Error(err) + errorcontains.CaptureError(t, err) // Export master key with correct password should succeed req3 := kmdapi.APIV1POSTMasterKeyExportRequest{ diff --git a/test/e2e-go/kmd/e2e_kmd_wallet_multisig_test.go b/test/e2e-go/kmd/e2e_kmd_wallet_multisig_test.go index b69ffa76b7..0dbb46fdf5 100644 --- a/test/e2e-go/kmd/e2e_kmd_wallet_multisig_test.go +++ b/test/e2e-go/kmd/e2e_kmd_wallet_multisig_test.go @@ -28,6 +28,7 @@ import ( "github.com/algorand/go-algorand/data/transactions" "github.com/algorand/go-algorand/data/transactions/logic" "github.com/algorand/go-algorand/protocol" + "github.com/algorand/go-algorand/test/errorcontains" "github.com/algorand/go-algorand/test/framework/fixtures" "github.com/algorand/go-algorand/test/partitiontest" ) @@ -377,7 +378,7 @@ func TestMultisigSignWithWrongSigner(t *testing.T) { resp2 := kmdapi.APIV1POSTMultisigTransactionSignResponse{} err = f.Client.DoV1Request(req2, &resp2) - a.Error(err) + errorcontains.CaptureError(t, err) } diff --git a/test/e2e-go/kmd/e2e_kmd_wallet_test.go b/test/e2e-go/kmd/e2e_kmd_wallet_test.go index 5105e0caae..e845b2d5c2 100644 --- a/test/e2e-go/kmd/e2e_kmd_wallet_test.go +++ b/test/e2e-go/kmd/e2e_kmd_wallet_test.go @@ -23,6 +23,7 @@ import ( "github.com/stretchr/testify/require" "github.com/algorand/go-algorand/daemon/kmd/lib/kmdapi" + "github.com/algorand/go-algorand/test/errorcontains" "github.com/algorand/go-algorand/test/framework/fixtures" "github.com/algorand/go-algorand/test/partitiontest" ) @@ -77,7 +78,7 @@ func TestWalletCreation(t *testing.T) { err = f.Client.DoV1Request(req3, &resp3) // Should be an error - a.Error(err) + errorcontains.CaptureError(t, err) } func TestBlankWalletCreation(t *testing.T) { @@ -161,7 +162,7 @@ func TestWalletRename(t *testing.T) { err = f.Client.DoV1Request(req2, &resp2) // Should be an error - a.Error(err) + errorcontains.CaptureError(t, err) // Try to rename the wallet with the correct password req3 := kmdapi.APIV1POSTWalletRenameRequest{ @@ -234,7 +235,7 @@ func TestWalletSessionRelease(t *testing.T) { err = f.Client.DoV1Request(req2, &resp2) // Error response - a.Error(err) + errorcontains.CaptureError(t, err) // Should not return the wallet we created a.NotEqual(resp2.WalletHandle.Wallet.Name, f.WalletName) @@ -322,5 +323,5 @@ func TestWalletSessionExpiry(t *testing.T) { err = f.Client.DoV1Request(req1, &resp1) // Token should have expired - a.Error(err) + errorcontains.CaptureError(t, err) } diff --git a/test/e2e-go/restAPI/other/appsRestAPI_test.go b/test/e2e-go/restAPI/other/appsRestAPI_test.go index 17454a9de5..f917752c77 100644 --- a/test/e2e-go/restAPI/other/appsRestAPI_test.go +++ b/test/e2e-go/restAPI/other/appsRestAPI_test.go @@ -33,6 +33,7 @@ import ( "github.com/algorand/go-algorand/data/transactions" "github.com/algorand/go-algorand/data/transactions/logic" "github.com/algorand/go-algorand/libgoal" + "github.com/algorand/go-algorand/test/errorcontains" "github.com/algorand/go-algorand/test/framework/fixtures" "github.com/algorand/go-algorand/test/partitiontest" "github.com/stretchr/testify/require" @@ -310,7 +311,7 @@ end: // `assertErrorResponse` confirms the _Result limit exceeded_ error response provides expected fields and values. assertErrorResponse := func(err error, expectedCount, requestedMax uint64) { - a.Error(err) + errorcontains.CaptureError(t, err) e := err.(client.HTTPError) a.Equal(400, e.StatusCode) diff --git a/test/e2e-go/restAPI/other/misc_test.go b/test/e2e-go/restAPI/other/misc_test.go index 287aef0abd..cb3be55097 100644 --- a/test/e2e-go/restAPI/other/misc_test.go +++ b/test/e2e-go/restAPI/other/misc_test.go @@ -23,6 +23,7 @@ import ( "testing" "github.com/algorand/go-algorand/daemon/algod/api/client" + "github.com/algorand/go-algorand/test/errorcontains" "github.com/algorand/go-algorand/test/framework/fixtures" "github.com/algorand/go-algorand/test/partitiontest" "github.com/algorand/go-algorand/util/tokens" @@ -127,7 +128,7 @@ func TestSendingNotClosingAccountErrs(t *testing.T) { a.NoError(err) amt := someBal - params.MinFee - 1 _, err = testClient.SendPaymentFromWallet(wh, nil, someAddress, emptyAddress, params.MinFee, amt, nil, "", 0, 0) - a.Error(err) + errorcontains.CaptureError(t, err) } func TestClientCanGetPendingTransactions(t *testing.T) { diff --git a/test/e2e-go/restAPI/restClient_test.go b/test/e2e-go/restAPI/restClient_test.go index b6a4fc96cb..2927181be4 100644 --- a/test/e2e-go/restAPI/restClient_test.go +++ b/test/e2e-go/restAPI/restClient_test.go @@ -33,6 +33,7 @@ import ( "github.com/algorand/go-algorand/data/basics" "github.com/algorand/go-algorand/data/transactions" "github.com/algorand/go-algorand/protocol" + "github.com/algorand/go-algorand/test/errorcontains" "github.com/algorand/go-algorand/test/framework/fixtures" "github.com/algorand/go-algorand/test/partitiontest" ) @@ -154,7 +155,7 @@ func TestClientRejectsBadFromAddressWhenSending(t *testing.T) { badAccountAddress := "This is absolutely not a valid account address." goodAccountAddress := addresses[0] _, err = testClient.SendPaymentFromWallet(wh, nil, badAccountAddress, goodAccountAddress, params.MinFee, 100000, nil, "", 0, 0) - a.Error(err) + errorcontains.CaptureError(t, err) } func TestClientRejectsBadToAddressWhenSending(t *testing.T) { @@ -173,7 +174,7 @@ func TestClientRejectsBadToAddressWhenSending(t *testing.T) { badAccountAddress := "This is absolutely not a valid account address." goodAccountAddress := addresses[0] _, err = testClient.SendPaymentFromWallet(wh, nil, goodAccountAddress, badAccountAddress, params.MinFee, 100000, nil, "", 0, 0) - a.Error(err) + errorcontains.CaptureError(t, err) } func TestClientRejectsMutatedFromAddressWhenSending(t *testing.T) { @@ -199,7 +200,7 @@ func TestClientRejectsMutatedFromAddressWhenSending(t *testing.T) { } mutatedAccountAddress := mutateStringAtIndex(unmutatedAccountAddress, 0) _, err = testClient.SendPaymentFromWallet(wh, nil, mutatedAccountAddress, goodAccountAddress, params.MinFee, 100000, nil, "", 0, 0) - a.Error(err) + errorcontains.CaptureError(t, err) } func TestClientRejectsMutatedToAddressWhenSending(t *testing.T) { @@ -225,7 +226,7 @@ func TestClientRejectsMutatedToAddressWhenSending(t *testing.T) { } mutatedAccountAddress := mutateStringAtIndex(unmutatedAccountAddress, 0) _, err = testClient.SendPaymentFromWallet(wh, nil, goodAccountAddress, mutatedAccountAddress, params.MinFee, 100000, nil, "", 0, 0) - a.Error(err) + errorcontains.CaptureError(t, err) } func TestClientRejectsSendingMoneyFromAccountForWhichItHasNoKey(t *testing.T) { @@ -244,7 +245,7 @@ func TestClientRejectsSendingMoneyFromAccountForWhichItHasNoKey(t *testing.T) { goodAccountAddress := addresses[0] nodeDoesNotHaveKeyForThisAddress := "NJY27OQ2ZXK6OWBN44LE4K43TA2AV3DPILPYTHAJAMKIVZDWTEJKZJKO4A" _, err = testClient.SendPaymentFromWallet(wh, nil, nodeDoesNotHaveKeyForThisAddress, goodAccountAddress, params.MinFee, 100000, nil, "", 0, 0) - a.Error(err) + errorcontains.CaptureError(t, err) } func TestClientOversizedNote(t *testing.T) { @@ -272,7 +273,7 @@ func TestClientOversizedNote(t *testing.T) { maxTxnNoteBytes := config.Consensus[protocol.ConsensusCurrentVersion].MaxTxnNoteBytes note := make([]byte, maxTxnNoteBytes+1) _, err = testClient.SendPaymentFromWallet(wh, nil, fromAddress, toAddress, params.MinFee, 100000, note, "", 0, 0) - a.Error(err) + errorcontains.CaptureError(t, err) } func TestClientCanSendAndGetNote(t *testing.T) { @@ -487,22 +488,22 @@ func TestSendingTooMuchErrs(t *testing.T) { // too much amount _, err = testClient.SendPaymentFromWallet(wh, nil, fromAddress, toAddress, params.MinFee, fromBalance+100, nil, "", 0, 0) t.Log(err) - a.Error(err) + errorcontains.CaptureError(t, err) // waaaay too much amount _, err = testClient.SendPaymentFromWallet(wh, nil, fromAddress, toAddress, params.MinFee, math.MaxUint64, nil, "", 0, 0) t.Log(err) - a.Error(err) + errorcontains.CaptureError(t, err) // too much fee _, err = testClient.SendPaymentFromWallet(wh, nil, fromAddress, toAddress, fromBalance+100, params.MinFee, nil, "", 0, 0) t.Log(err) - a.Error(err) + errorcontains.CaptureError(t, err) // waaaay too much fee _, err = testClient.SendPaymentFromWallet(wh, nil, fromAddress, toAddress, math.MaxUint64, params.MinFee, nil, "", 0, 0) t.Log(err) - a.Error(err) + errorcontains.CaptureError(t, err) } func TestSendingFromEmptyAccountErrs(t *testing.T) { @@ -544,7 +545,7 @@ func TestSendingFromEmptyAccountErrs(t *testing.T) { params, err := testClient.SuggestedParams() a.NoError(err) _, err = testClient.SendPaymentFromWallet(wh, nil, fromAddress, toAddress, params.MinFee, 100000, nil, "", 0, 0) - a.Error(err) + errorcontains.CaptureError(t, err) } func TestSendingTooLittleToEmptyAccountErrs(t *testing.T) { @@ -579,7 +580,7 @@ func TestSendingTooLittleToEmptyAccountErrs(t *testing.T) { params, err := testClient.SuggestedParams() a.NoError(err) _, err = testClient.SendPaymentFromWallet(wh, nil, someAddress, emptyAddress, params.MinFee, 1, nil, "", 0, 0) - a.Error(err) + errorcontains.CaptureError(t, err) } func TestSendingLowFeeErrs(t *testing.T) { @@ -609,11 +610,11 @@ func TestSendingLowFeeErrs(t *testing.T) { a.NoError(err) _, err = testClient.BroadcastTransaction(stx) t.Log(err) - a.Error(err) + errorcontains.CaptureError(t, err) utx.Fee.Raw = 0 stx, err = testClient.SignTransactionWithWallet(wh, nil, utx) a.NoError(err) _, err = testClient.BroadcastTransaction(stx) t.Log(err) - a.Error(err) + errorcontains.CaptureError(t, err) } diff --git a/test/e2e-go/upgrades/stateproof_participation_test.go b/test/e2e-go/upgrades/stateproof_participation_test.go index db33661d25..90ad73705c 100644 --- a/test/e2e-go/upgrades/stateproof_participation_test.go +++ b/test/e2e-go/upgrades/stateproof_participation_test.go @@ -26,6 +26,7 @@ import ( "github.com/algorand/go-algorand/data/basics" "github.com/algorand/go-algorand/libgoal" "github.com/algorand/go-algorand/protocol" + "github.com/algorand/go-algorand/test/errorcontains" "github.com/algorand/go-algorand/test/framework/fixtures" "github.com/algorand/go-algorand/test/partitiontest" "github.com/stretchr/testify/require" @@ -76,7 +77,7 @@ func TestKeysWithoutStateProofKeyCannotRegister(t *testing.T) { waitUntilProtocolUpgrades(a, &fixture, nodeClient) - a.Error(registerKeyInto(&nodeClient, a, lastValid+2, protocol.ConsensusV30)) + errorcontains.CaptureError(t, registerKeyInto(&nodeClient, a, lastValid+2, protocol.ConsensusV30)) a.NoError(registerKeyInto(&nodeClient, a, lastValid+3, protocol.ConsensusV31)) } @@ -94,7 +95,7 @@ func TestKeysWithoutStateProofKeyCanRegister(t *testing.T) { nodeClient := fixture.GetLibGoalClientForNamedNode("Node") a.NoError(registerKeyInto(&nodeClient, a, lastValid, protocol.ConsensusV30)) - a.Error(registerKeyInto(&nodeClient, a, lastValid+1, protocol.ConsensusV31)) + errorcontains.CaptureError(t, registerKeyInto(&nodeClient, a, lastValid+1, protocol.ConsensusV31)) } func registerKeyInto(client *libgoal.Client, a *require.Assertions, lastValid basics.Round, ver protocol.ConsensusVersion) error { diff --git a/test/errorcontains/capture.go b/test/errorcontains/capture.go new file mode 100644 index 0000000000..22131849ed --- /dev/null +++ b/test/errorcontains/capture.go @@ -0,0 +1,237 @@ +// Copyright (C) 2019-2025 Algorand, Inc. +// This file is part of go-algorand +// +// go-algorand is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// go-algorand is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with go-algorand. If not, see . + +// Package errorcontains provides helpers to capture error details during test +// runs for migration from require.Error to more specific assertions like +// ErrorContains, ErrorIs, or ErrorAs. +package errorcontains + +import ( + "errors" + "fmt" + "os" + "path/filepath" + "reflect" + "runtime" + "strings" + "sync" + "testing" + + "github.com/algorand/go-deadlock" +) + +var ( + outputFile *os.File + outputMu deadlock.Mutex + initOnce sync.Once +) + +func initOutput() { + initOnce.Do(func() { + path := os.Getenv("ERROR_CAPTURE_FILE") + if path == "" { + // Use PID in filename to avoid contention between parallel test processes + path = fmt.Sprintf("/tmp/error_capture.%d.jsonl", os.Getpid()) + } + var err error + outputFile, err = os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644) + if err != nil { + fmt.Fprintf(os.Stderr, "errorcontains: failed to open output file %s: %v\n", path, err) + } + }) +} + +// CaptureError is a drop-in replacement for require.Error that captures error +// details to a file for later analysis. It calls t.FailNow() if err is nil. +// The msgAndArgs parameter is passed through like in testify assertions. +func CaptureError(t testing.TB, err error, msgAndArgs ...interface{}) { + t.Helper() + captureAndCheck(t, err, true, msgAndArgs...) +} + +// CaptureErrorf is like CaptureError but with a formatted message. +func CaptureErrorf(t testing.TB, err error, format string, args ...interface{}) { + t.Helper() + msg := fmt.Sprintf(format, args...) + captureAndCheck(t, err, true, msg) +} + +// CaptureErrorAssert is like CaptureError but uses t.Fail() instead of +// t.FailNow(), matching assert.Error behavior. +func CaptureErrorAssert(t testing.TB, err error, msgAndArgs ...interface{}) { + t.Helper() + captureAndCheck(t, err, false, msgAndArgs...) +} + +func captureAndCheck(t testing.TB, err error, failNow bool, msgAndArgs ...interface{}) { + t.Helper() + initOutput() + + // Get caller info (skip 2: captureAndCheck -> CaptureError -> actual test) + _, file, line, ok := runtime.Caller(2) + if !ok { + file = "unknown" + line = 0 + } + + // Make file path relative to repo root for readability + if idx := strings.Index(file, "go-algorand/"); idx != -1 { + file = file[idx+len("go-algorand/"):] + } + + var userMsg string + if len(msgAndArgs) > 0 { + if s, ok := msgAndArgs[0].(string); ok { + if len(msgAndArgs) > 1 { + userMsg = fmt.Sprintf(s, msgAndArgs[1:]...) + } else { + userMsg = s + } + } else { + userMsg = fmt.Sprintf("%v", msgAndArgs[0]) + } + } + + if err == nil { + // Log that we expected an error but got nil + writeEntry(t.Name(), file, line, "", "", userMsg, "ERROR_WAS_NIL") + if failNow { + t.Fatalf("expected an error but got nil: %s", userMsg) + } else { + t.Errorf("expected an error but got nil: %s", userMsg) + } + return + } + + // Capture error details + errType := reflect.TypeOf(err).String() + errMsg := err.Error() + + // Check for wrapped errors + var unwrapped []string + current := err + for { + inner := errors.Unwrap(current) + if inner == nil { + break + } + unwrapped = append(unwrapped, reflect.TypeOf(inner).String()) + current = inner + } + + writeEntry(t.Name(), file, line, errType, errMsg, userMsg, strings.Join(unwrapped, " -> ")) +} + +func writeEntry(testName, file string, line int, errType, errMsg, userMsg, unwrapped string) { + if outputFile == nil { + return + } + + // Escape strings for JSON + escape := func(s string) string { + s = strings.ReplaceAll(s, "\\", "\\\\") + s = strings.ReplaceAll(s, "\"", "\\\"") + s = strings.ReplaceAll(s, "\n", "\\n") + s = strings.ReplaceAll(s, "\r", "\\r") + s = strings.ReplaceAll(s, "\t", "\\t") + return s + } + + entry := fmt.Sprintf(`{"test":%q,"file":%q,"line":%d,"error_type":%q,"error_msg":%q,"user_msg":%q,"unwrapped":%q}`+"\n", + escape(testName), escape(file), line, + escape(errType), escape(errMsg), escape(userMsg), escape(unwrapped)) + + outputMu.Lock() + defer outputMu.Unlock() + _, _ = outputFile.WriteString(entry) +} + +// ErrorCapture provides an assertions-style interface like require.New(t). +type ErrorCapture struct { + t testing.TB + failNow bool +} + +// New returns an ErrorCapture for require-style usage (fails immediately). +func New(t testing.TB) *ErrorCapture { + return &ErrorCapture{t: t, failNow: true} +} + +// NewAssert returns an ErrorCapture for assert-style usage (continues on failure). +func NewAssert(t testing.TB) *ErrorCapture { + return &ErrorCapture{t: t, failNow: false} +} + +// Error captures error details and fails if err is nil. +func (e *ErrorCapture) Error(err error, msgAndArgs ...interface{}) { + e.t.Helper() + captureAndCheck(e.t, err, e.failNow, msgAndArgs...) +} + +// Errorf captures error details with a formatted message. +func (e *ErrorCapture) Errorf(err error, format string, args ...interface{}) { + e.t.Helper() + msg := fmt.Sprintf(format, args...) + captureAndCheck(e.t, err, e.failNow, msg) +} + +// Close should be called at the end of test runs to ensure all data is flushed. +func Close() { + outputMu.Lock() + defer outputMu.Unlock() + if outputFile != nil { + outputFile.Close() + outputFile = nil + } +} + +// Summary generates a summary report from the captured error data. +// This is useful for reviewing the captured errors before migration. +func Summary(inputPath string) (map[string]int, error) { + data, err := os.ReadFile(inputPath) + if err != nil { + return nil, err + } + + // Count occurrences of each error type + counts := make(map[string]int) + for line := range strings.SplitSeq(string(data), "\n") { + if line == "" { + continue + } + // Extract error_type from JSON line (simple parsing) + if idx := strings.Index(line, `"error_type":"`); idx != -1 { + start := idx + len(`"error_type":"`) + end := strings.Index(line[start:], `"`) + if end != -1 { + errType := line[start : start+end] + counts[errType]++ + } + } + } + return counts, nil +} + +// OutputPath returns the path where error captures are being written. +// Note: when ERROR_CAPTURE_FILE is not set, each test process writes to its +// own file with PID suffix. Use "cat /tmp/error_capture.*.jsonl" to combine. +func OutputPath() string { + path := os.Getenv("ERROR_CAPTURE_FILE") + if path == "" { + path = fmt.Sprintf("/tmp/error_capture.%d.jsonl", os.Getpid()) + } + return filepath.Clean(path) +} diff --git a/test/errorcontains/capture_test.go b/test/errorcontains/capture_test.go new file mode 100644 index 0000000000..d8548720cc --- /dev/null +++ b/test/errorcontains/capture_test.go @@ -0,0 +1,205 @@ +// Copyright (C) 2019-2025 Algorand, Inc. +// This file is part of go-algorand +// +// go-algorand is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// go-algorand is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with go-algorand. If not, see . + +package errorcontains + +import ( + "encoding/json" + "errors" + "fmt" + "os" + "strings" + stdsync "sync" + "testing" + + "github.com/algorand/go-algorand/test/partitiontest" +) + +// customError is a custom error type for testing error type capture. +type customError struct { + code int + msg string +} + +func (e *customError) Error() string { + return fmt.Sprintf("code %d: %s", e.code, e.msg) +} + +// wrappedError wraps another error for testing unwrap chain capture. +type wrappedError struct { + msg string + inner error +} + +func (e *wrappedError) Error() string { + return fmt.Sprintf("%s: %v", e.msg, e.inner) +} + +func (e *wrappedError) Unwrap() error { + return e.inner +} + +// resetSingleton resets the package-level singleton for testing. +func resetSingleton(path string) { + outputMu.Lock() + defer outputMu.Unlock() + if outputFile != nil { + outputFile.Close() + } + outputFile = nil + initOnce = stdsync.Once{} + os.Setenv("ERROR_CAPTURE_FILE", path) +} + +func TestCaptureError_BasicError(t *testing.T) { + partitiontest.PartitionTest(t) + + tmpFile, err := os.CreateTemp("", "error_capture_test_*.jsonl") + if err != nil { + t.Fatal(err) + } + tmpPath := tmpFile.Name() + tmpFile.Close() + defer os.Remove(tmpPath) + + resetSingleton(tmpPath) + + testErr := errors.New("test error message") + CaptureError(t, testErr, "this is a test") + + Close() + + data, err := os.ReadFile(tmpPath) + if err != nil { + t.Fatal(err) + } + + var entry map[string]interface{} + if err := json.Unmarshal([]byte(strings.TrimSpace(string(data))), &entry); err != nil { + t.Fatalf("failed to parse JSON: %v\ndata: %s", err, data) + } + + if entry["error_type"] != "*errors.errorString" { + t.Errorf("expected error_type *errors.errorString, got %v", entry["error_type"]) + } + if entry["error_msg"] != "test error message" { + t.Errorf("expected error_msg 'test error message', got %v", entry["error_msg"]) + } + if entry["user_msg"] != "this is a test" { + t.Errorf("expected user_msg 'this is a test', got %v", entry["user_msg"]) + } +} + +func TestCaptureError_CustomError(t *testing.T) { + partitiontest.PartitionTest(t) + + tmpFile, err := os.CreateTemp("", "error_capture_test_*.jsonl") + if err != nil { + t.Fatal(err) + } + tmpPath := tmpFile.Name() + tmpFile.Close() + defer os.Remove(tmpPath) + + resetSingleton(tmpPath) + + testErr := &customError{code: 42, msg: "something went wrong"} + CaptureError(t, testErr) + + Close() + + data, err := os.ReadFile(tmpPath) + if err != nil { + t.Fatal(err) + } + + var entry map[string]interface{} + if err := json.Unmarshal([]byte(strings.TrimSpace(string(data))), &entry); err != nil { + t.Fatalf("failed to parse JSON: %v", err) + } + + if entry["error_type"] != "*errorcontains.customError" { + t.Errorf("expected error_type *errorcontains.customError, got %v", entry["error_type"]) + } + if entry["error_msg"] != "code 42: something went wrong" { + t.Errorf("expected error message, got %v", entry["error_msg"]) + } +} + +func TestCaptureError_WrappedError(t *testing.T) { + partitiontest.PartitionTest(t) + + tmpFile, err := os.CreateTemp("", "error_capture_test_*.jsonl") + if err != nil { + t.Fatal(err) + } + tmpPath := tmpFile.Name() + tmpFile.Close() + defer os.Remove(tmpPath) + + resetSingleton(tmpPath) + + inner := errors.New("inner error") + testErr := &wrappedError{msg: "outer", inner: inner} + CaptureError(t, testErr) + + Close() + + data, err := os.ReadFile(tmpPath) + if err != nil { + t.Fatal(err) + } + + var entry map[string]interface{} + if err := json.Unmarshal([]byte(strings.TrimSpace(string(data))), &entry); err != nil { + t.Fatalf("failed to parse JSON: %v", err) + } + + if entry["error_type"] != "*errorcontains.wrappedError" { + t.Errorf("expected error_type *errorcontains.wrappedError, got %v", entry["error_type"]) + } + if entry["unwrapped"] != "*errors.errorString" { + t.Errorf("expected unwrapped chain, got %v", entry["unwrapped"]) + } +} + +func TestErrorCapture_Interface(t *testing.T) { + partitiontest.PartitionTest(t) + + tmpFile, err := os.CreateTemp("", "error_capture_test_*.jsonl") + if err != nil { + t.Fatal(err) + } + tmpPath := tmpFile.Name() + tmpFile.Close() + defer os.Remove(tmpPath) + + resetSingleton(tmpPath) + + ec := New(t) + ec.Error(errors.New("interface test")) + + Close() + + data, err := os.ReadFile(tmpPath) + if err != nil { + t.Fatal(err) + } + + if !strings.Contains(string(data), "interface test") { + t.Errorf("expected to find 'interface test' in output: %s", data) + } +} diff --git a/test/errorcontains/cmd/aggregate/main.go b/test/errorcontains/cmd/aggregate/main.go new file mode 100644 index 0000000000..20ec5fec9c --- /dev/null +++ b/test/errorcontains/cmd/aggregate/main.go @@ -0,0 +1,291 @@ +// Copyright (C) 2019-2025 Algorand, Inc. +// This file is part of go-algorand +// +// go-algorand is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// go-algorand is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with go-algorand. If not, see . + +// Command aggregate reads error capture JSONL files and produces an aggregated +// report with longest common substrings for each call site. +package main + +import ( + "bufio" + "encoding/json" + "fmt" + "os" + "path/filepath" + "slices" + "sort" + "strings" + + lcss "gopkg.in/vmarkovtsev/go-lcss.v1" +) + +// CapturedError represents one entry from the JSONL capture file. +type CapturedError struct { + Test string `json:"test"` + File string `json:"file"` + Line int `json:"line"` + ErrorType string `json:"error_type"` + ErrorMsg string `json:"error_msg"` + UserMsg string `json:"user_msg"` + Unwrapped string `json:"unwrapped"` +} + +// CallSite represents aggregated data for one file:line location. +type CallSite struct { + File string `json:"file"` + Line int `json:"line"` + Count int `json:"count"` + ErrorTypes map[string]int `json:"error_types"` + LCS string `json:"lcs"` + LCSLength int `json:"lcs_length"` + Samples []string `json:"samples"` + UserMsgs map[string]int `json:"user_msgs,omitempty"` + NeedsReview bool `json:"needs_review,omitempty"` +} + +func main() { + if len(os.Args) < 2 { + fmt.Println("Usage: aggregate [output.json]") + fmt.Println(" aggregate /tmp/all_errors.jsonl /tmp/error_sites.json") + os.Exit(1) + } + + inputFile := os.Args[1] + outputFile := "/tmp/error_sites.json" + if len(os.Args) > 2 { + outputFile = os.Args[2] + } + + // Read and parse input + sites, err := readAndAggregate(inputFile) + if err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + os.Exit(1) + } + + // Compute LCS for each site + for key, site := range sites { + if len(site.Samples) > 0 { + site.LCS = longestCommonSubstring(site.Samples) + site.LCSLength = len(site.LCS) + if site.LCSLength < 5 { + site.NeedsReview = true + } + } + // Keep only first 3 samples for output + if len(site.Samples) > 3 { + site.Samples = site.Samples[:3] + } + sites[key] = site + } + + // Convert to sorted slice for output + var results []CallSite + for _, site := range sites { + results = append(results, *site) + } + sort.Slice(results, func(i, j int) bool { + if results[i].File != results[j].File { + return results[i].File < results[j].File + } + return results[i].Line < results[j].Line + }) + + // Write output + output, err := json.MarshalIndent(results, "", " ") + if err != nil { + fmt.Fprintf(os.Stderr, "Error marshaling output: %v\n", err) + os.Exit(1) + } + + if err := os.WriteFile(outputFile, output, 0644); err != nil { + fmt.Fprintf(os.Stderr, "Error writing output: %v\n", err) + os.Exit(1) + } + + // Print summary + needsReview := 0 + for _, r := range results { + if r.NeedsReview { + needsReview++ + } + } + fmt.Printf("Aggregated %d call sites from %s\n", len(results), inputFile) + fmt.Printf("Output written to %s\n", outputFile) + fmt.Printf("Sites needing review (LCS < 5 chars): %d\n", needsReview) +} + +func readAndAggregate(filename string) (map[string]*CallSite, error) { + f, err := os.Open(filename) + if err != nil { + return nil, err + } + defer f.Close() + + sites := make(map[string]*CallSite) + scanner := bufio.NewScanner(f) + // Increase buffer size for long lines + buf := make([]byte, 0, 64*1024) + scanner.Buffer(buf, 1024*1024) + + lineNum := 0 + for scanner.Scan() { + lineNum++ + line := scanner.Text() + if line == "" { + continue + } + + var entry CapturedError + if err := json.Unmarshal([]byte(line), &entry); err != nil { + fmt.Fprintf(os.Stderr, "Warning: failed to parse line %d: %v\n", lineNum, err) + continue + } + + // Normalize file path + entry.File = normalizePath(entry.File) + + key := fmt.Sprintf("%s:%d", entry.File, entry.Line) + site, ok := sites[key] + if !ok { + site = &CallSite{ + File: entry.File, + Line: entry.Line, + ErrorTypes: make(map[string]int), + UserMsgs: make(map[string]int), + Samples: []string{}, + } + sites[key] = site + } + + site.Count++ + site.ErrorTypes[entry.ErrorType]++ + if entry.UserMsg != "" { + site.UserMsgs[entry.UserMsg]++ + } + + // Add unique samples (up to 10 for LCS computation) + if len(site.Samples) < 10 && !slices.Contains(site.Samples, entry.ErrorMsg) { + site.Samples = append(site.Samples, entry.ErrorMsg) + } + } + + return sites, scanner.Err() +} + +func normalizePath(path string) string { + // Remove the absolute path prefix, keep relative from repo root + // Handle both "go-algorand/" and "errorcontains/" repo names + for _, repoName := range []string{"go-algorand/", "errorcontains/"} { + if idx := strings.Index(path, repoName); idx != -1 { + return path[idx+len(repoName):] + } + } + return filepath.Base(path) +} + +// longestCommonSubstring finds the longest substring common to all strings. +func longestCommonSubstring(strs []string) string { + if len(strs) == 0 { + return "" + } + if len(strs) == 1 { + return extractPhrase(strs[0]) + } + + // Convert to [][]byte for the library + byteStrs := make([][]byte, len(strs)) + for i, s := range strs { + byteStrs[i] = []byte(s) + } + + // Use the efficient library implementation + result := lcss.LongestCommonSubstring(byteStrs...) + lcsStr := string(result) + + // If too short, try common prefix + if len(lcsStr) < 5 { + prefix := commonPrefix(strs) + if len(prefix) > len(lcsStr) { + lcsStr = prefix + } + } + + return cleanupLCS(strings.TrimSpace(lcsStr)) +} + +// commonPrefix finds the longest common prefix of all strings. +func commonPrefix(strs []string) string { + if len(strs) == 0 { + return "" + } + + prefix := strs[0] + for _, s := range strs[1:] { + for !strings.HasPrefix(s, prefix) { + prefix = prefix[:len(prefix)-1] + if prefix == "" { + return "" + } + } + } + return prefix +} + +// extractPhrase extracts a meaningful phrase from a single error message. +// We want a short, meaningful substring - not serialized struct data. +func extractPhrase(msg string) string { + return cleanupLCS(msg) +} + +// cleanupLCS trims an LCS to remove serialized struct data and keep +// only the meaningful error prefix. +func cleanupLCS(lcs string) string { + lcs = strings.TrimSpace(lcs) + + // If LCS starts with garbage characters, it's not useful + if strings.HasPrefix(lcs, "]") || strings.HasPrefix(lcs, "}") || + strings.HasPrefix(lcs, "[0") || strings.HasPrefix(lcs, "0x") { + return "" + } + + // Chop at '{' which typically starts serialized struct data + if idx := strings.Index(lcs, "{"); idx > 5 { + lcs = strings.TrimSpace(lcs[:idx]) + } + + // Also chop at '[' followed by numbers (byte arrays) + if idx := strings.Index(lcs, "[0 "); idx > 5 { + lcs = strings.TrimSpace(lcs[:idx]) + } + if idx := strings.Index(lcs, "[0x"); idx > 5 { + lcs = strings.TrimSpace(lcs[:idx]) + } + + // Chop at serialized struct markers + if idx := strings.Index(lcs, "_struct:"); idx > 0 { + lcs = strings.TrimSpace(lcs[:idx]) + } + + // Remove trailing colons, punctuation, or partial struct syntax + lcs = strings.TrimRight(lcs, ":, ]}") + + // If what's left is too short or looks like garbage, return empty + if len(lcs) < 5 { + return "" + } + + return lcs +} diff --git a/test/errorcontains/cmd/phase3/main.go b/test/errorcontains/cmd/phase3/main.go new file mode 100644 index 0000000000..4875c5ca77 --- /dev/null +++ b/test/errorcontains/cmd/phase3/main.go @@ -0,0 +1,346 @@ +// Copyright (C) 2019-2025 Algorand, Inc. +// This file is part of go-algorand +// +// go-algorand is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// go-algorand is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with go-algorand. If not, see . + +// Command phase3 converts errorcontains.CaptureError calls to specific error assertions. +package main + +import ( + "encoding/json" + "fmt" + "os" + "os/exec" + "path/filepath" + "regexp" + "sort" + "strconv" + "strings" +) + +// CallSite represents aggregated data from the aggregate tool. +type CallSite struct { + File string `json:"file"` + Line int `json:"line"` + Count int `json:"count"` + ErrorTypes map[string]int `json:"error_types"` + LCS string `json:"lcs"` + LCSLength int `json:"lcs_length"` + Samples []string `json:"samples"` + UserMsgs map[string]int `json:"user_msgs,omitempty"` + NeedsReview bool `json:"needs_review,omitempty"` +} + +var ( + // Pattern to match CaptureError calls - handles nested function calls + // Group 1: indent, Group 2: t var, Group 3: error expression, Group 4: optional user msg + captureErrorRe = regexp.MustCompile(`(?m)^(\s*)errorcontains\.CaptureError\(([^,]+),\s*(.+?)(?:,\s*("[^"]*"[^)]*))?\)\s*$`) + + // Pattern to match the next line after CaptureError + containsRe = regexp.MustCompile(`(?m)^(\s*)require\.Contains\(([^,]+),\s*([^.]+)\.Error\(\),\s*("[^"]+"|` + "`[^`]+`" + `)\)`) + equalRe = regexp.MustCompile(`(?m)^(\s*)require\.Equal\(([^,]+),\s*([^,]+),\s*([^)]+)\)`) +) + +func main() { + if len(os.Args) < 3 { + fmt.Println("Usage: phase3 [--dry-run]") + fmt.Println(" phase3 /tmp/error_sites.json /path/to/go-algorand") + os.Exit(1) + } + + sitesFile := os.Args[1] + rootDir := os.Args[2] + dryRun := len(os.Args) > 3 && os.Args[3] == "--dry-run" + + // Load aggregated error data + sites, err := loadSites(sitesFile) + if err != nil { + fmt.Fprintf(os.Stderr, "Error loading sites: %v\n", err) + os.Exit(1) + } + fmt.Printf("Loaded %d call sites\n", len(sites)) + + // Build lookup map by file:line + siteLookup := make(map[string]*CallSite) + for i := range sites { + key := fmt.Sprintf("%s:%d", sites[i].File, sites[i].Line) + siteLookup[key] = &sites[i] + } + + // Find all Go test files + var files []string + err = filepath.Walk(rootDir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if !info.IsDir() && strings.HasSuffix(path, "_test.go") { + files = append(files, path) + } + return nil + }) + if err != nil { + fmt.Fprintf(os.Stderr, "Error walking directory: %v\n", err) + os.Exit(1) + } + + stats := struct { + filesModified int + captureRemoved int + toErrorIs int + toErrorContains int + standalone int + skipped int + }{} + + for _, file := range files { + content, err := os.ReadFile(file) + if err != nil { + fmt.Fprintf(os.Stderr, "Error reading %s: %v\n", file, err) + continue + } + + original := string(content) + modified := processFile(original, file, rootDir, siteLookup, &stats) + + if modified != original { + stats.filesModified++ + if dryRun { + fmt.Printf("Would modify: %s\n", file) + } else { + if err := os.WriteFile(file, []byte(modified), 0644); err != nil { + fmt.Fprintf(os.Stderr, "Error writing %s: %v\n", file, err) + continue + } + fmt.Printf("Modified: %s\n", file) + } + } + } + + // Run goimports if not dry run + if !dryRun && stats.filesModified > 0 { + fmt.Println("\nRunning goimports...") + cmd := exec.Command("goimports", "-w", rootDir) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + fmt.Fprintf(os.Stderr, "Warning: goimports failed: %v\n", err) + } + } + + fmt.Printf("\nSummary:\n") + fmt.Printf(" Files modified: %d\n", stats.filesModified) + fmt.Printf(" CaptureError removed (kept ErrorIs): %d\n", stats.captureRemoved) + fmt.Printf(" Converted to ErrorIs: %d\n", stats.toErrorIs) + fmt.Printf(" Converted to ErrorContains: %d\n", stats.toErrorContains) + fmt.Printf(" Standalone converted: %d\n", stats.standalone) + fmt.Printf(" Skipped (needs review): %d\n", stats.skipped) +} + +func loadSites(filename string) ([]CallSite, error) { + data, err := os.ReadFile(filename) + if err != nil { + return nil, err + } + var sites []CallSite + if err := json.Unmarshal(data, &sites); err != nil { + return nil, err + } + return sites, nil +} + +func processFile(content, filePath, rootDir string, siteLookup map[string]*CallSite, stats *struct { + filesModified int + captureRemoved int + toErrorIs int + toErrorContains int + standalone int + skipped int +}) string { + lines := strings.Split(content, "\n") + + // Find CaptureError calls and their line numbers + type replacement struct { + lineIdx int + newLine string + deleteNext bool + nextLineIdx int + } + var replacements []replacement + + for i := 0; i < len(lines); i++ { + line := lines[i] + + // Check for CaptureError call + if !strings.Contains(line, "errorcontains.CaptureError") { + continue + } + + // Parse the CaptureError line + match := captureErrorRe.FindStringSubmatch(line) + if match == nil { + continue + } + + indent := match[1] + tVar := strings.TrimSpace(match[2]) + errVar := strings.TrimSpace(match[3]) + userMsg := "" + if len(match) > 4 && match[4] != "" { + userMsg = strings.TrimSpace(match[4]) + } + + // Get relative path for lookup + relPath := filePath + for _, repoName := range []string{"go-algorand/", "errorcontains/"} { + if idx := strings.Index(filePath, repoName); idx != -1 { + relPath = filePath[idx+len(repoName):] + break + } + } + + // Line numbers in the file are 1-indexed, array is 0-indexed + lineNum := i + 1 + siteKey := fmt.Sprintf("%s:%d", relPath, lineNum) + + // Check next line for pattern + nextLine := "" + if i+1 < len(lines) { + nextLine = lines[i+1] + } + + // Pattern 1: CaptureError + ErrorIs -> just remove CaptureError + if strings.Contains(nextLine, "require.ErrorIs") || strings.Contains(nextLine, "a.ErrorIs") { + replacements = append(replacements, replacement{ + lineIdx: i, + newLine: "", // delete the line + }) + stats.captureRemoved++ + continue + } + + // Pattern 2: CaptureError + Contains -> ErrorContains + if containsMatch := containsRe.FindStringSubmatch(nextLine); containsMatch != nil { + substring := containsMatch[4] + var newLine string + if userMsg != "" { + newLine = fmt.Sprintf("%srequire.ErrorContains(%s, %s, %s, %s)", indent, tVar, errVar, substring, userMsg) + } else { + newLine = fmt.Sprintf("%srequire.ErrorContains(%s, %s, %s)", indent, tVar, errVar, substring) + } + replacements = append(replacements, replacement{ + lineIdx: i, + newLine: newLine, + deleteNext: true, + nextLineIdx: i + 1, + }) + stats.toErrorContains++ + continue + } + + // Pattern 3: CaptureError + Equal(sentinel, err) -> ErrorIs + // Only convert if sentinel is a variable (not a function call like fmt.Errorf) + if equalMatch := equalRe.FindStringSubmatch(nextLine); equalMatch != nil { + arg1 := strings.TrimSpace(equalMatch[3]) + arg2 := strings.TrimSpace(equalMatch[4]) + + // Check if this is comparing error to sentinel + var sentinel string + if arg2 == errVar || strings.HasSuffix(arg2, errVar) { + sentinel = arg1 + } else if arg1 == errVar || strings.HasSuffix(arg1, errVar) { + sentinel = arg2 + } + + // Only use ErrorIs if sentinel looks like a variable (not a function call) + // Function calls contain "(" which sentinels don't + if sentinel != "" && !strings.Contains(sentinel, "(") && !strings.Contains(sentinel, ".Error()") { + var newLine string + if userMsg != "" { + newLine = fmt.Sprintf("%srequire.ErrorIs(%s, %s, %s, %s)", indent, tVar, errVar, sentinel, userMsg) + } else { + newLine = fmt.Sprintf("%srequire.ErrorIs(%s, %s, %s)", indent, tVar, errVar, sentinel) + } + replacements = append(replacements, replacement{ + lineIdx: i, + newLine: newLine, + deleteNext: true, + nextLineIdx: i + 1, + }) + stats.toErrorIs++ + continue + } + } + + // Pattern 4: Standalone CaptureError -> ErrorContains with LCS + site := siteLookup[siteKey] + if site == nil { + // Try with just the file basename + baseName := filepath.Base(relPath) + for k, s := range siteLookup { + if strings.HasSuffix(k, baseName+":"+strconv.Itoa(lineNum)) { + site = s + break + } + } + } + + if site != nil && site.LCS != "" && !site.NeedsReview { + lcs := escapeString(site.LCS) + var newLine string + if userMsg != "" { + newLine = fmt.Sprintf("%srequire.ErrorContains(%s, %s, %s, %s)", indent, tVar, errVar, lcs, userMsg) + } else { + newLine = fmt.Sprintf("%srequire.ErrorContains(%s, %s, %s)", indent, tVar, errVar, lcs) + } + replacements = append(replacements, replacement{ + lineIdx: i, + newLine: newLine, + }) + stats.standalone++ + } else { + stats.skipped++ + } + } + + // Apply replacements in reverse order to preserve line numbers + sort.Slice(replacements, func(i, j int) bool { + return replacements[i].lineIdx > replacements[j].lineIdx + }) + + for _, r := range replacements { + if r.deleteNext && r.nextLineIdx < len(lines) { + lines = append(lines[:r.nextLineIdx], lines[r.nextLineIdx+1:]...) + } + if r.newLine == "" { + lines = append(lines[:r.lineIdx], lines[r.lineIdx+1:]...) + } else { + lines[r.lineIdx] = r.newLine + } + } + + return strings.Join(lines, "\n") +} + +func escapeString(s string) string { + // Use backtick if possible (no backticks in string) + if !strings.Contains(s, "`") && !strings.Contains(s, "\n") { + return "`" + s + "`" + } + // Otherwise use double quotes with escaping + escaped := strings.ReplaceAll(s, `\`, `\\`) + escaped = strings.ReplaceAll(escaped, `"`, `\"`) + escaped = strings.ReplaceAll(escaped, "\n", `\n`) + escaped = strings.ReplaceAll(escaped, "\t", `\t`) + return `"` + escaped + `"` +} diff --git a/test/errorcontains/cmd/replace/main.go b/test/errorcontains/cmd/replace/main.go new file mode 100644 index 0000000000..5990a65beb --- /dev/null +++ b/test/errorcontains/cmd/replace/main.go @@ -0,0 +1,283 @@ +// Copyright (C) 2019-2025 Algorand, Inc. +// This file is part of go-algorand +// +// go-algorand is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// go-algorand is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with go-algorand. If not, see . + +// Command replace rewrites require.Error/assert.Error/a.Error calls to use +// errorcontains.CaptureError for error capture during migration. +package main + +import ( + "fmt" + "go/ast" + "go/parser" + "go/token" + "os" + "os/exec" + "strings" +) + +func main() { + if len(os.Args) < 2 { + fmt.Println("Usage: replace [file2.go ...]") + os.Exit(1) + } + + for _, filename := range os.Args[1:] { + if err := processFile(filename); err != nil { + fmt.Fprintf(os.Stderr, "Error processing %s: %v\n", filename, err) + } + } +} + +func processFile(filename string) error { + content, err := os.ReadFile(filename) + if err != nil { + return err + } + + // Set global for exprToString + sourceContent = content + + fset := token.NewFileSet() + file, err := parser.ParseFile(fset, filename, content, parser.ParseComments) + if err != nil { + return fmt.Errorf("parse error: %w", err) + } + + var replacements []replacement + + // Find all function declarations and their testing parameter name + ast.Inspect(file, func(n ast.Node) bool { + switch node := n.(type) { + case *ast.FuncDecl: + // Find the testing.T or testing.TB parameter name + tVar := findTestingParam(node) + if tVar == "" { + return true + } + + // Find assertions object variable (a := require.New(t)) + assertVar, assertStyle := findAssertionsVar(node) + + // Walk the function body for Error calls + ast.Inspect(node.Body, func(inner ast.Node) bool { + call, ok := inner.(*ast.CallExpr) + if !ok { + return true + } + + r := analyzeErrorCall(call, tVar, assertVar, assertStyle, fset) + if r != nil { + replacements = append(replacements, *r) + } + return true + }) + } + return true + }) + + if len(replacements) == 0 { + return nil + } + + // Apply replacements from end to start to preserve positions + result := string(content) + for i := len(replacements) - 1; i >= 0; i-- { + r := replacements[i] + result = result[:r.start] + r.newText + result[r.end:] + } + + // Write the result + if err := os.WriteFile(filename, []byte(result), 0644); err != nil { + return err + } + + // Run goimports to fix imports and format + cmd := exec.Command("goimports", "-w", filename) + if output, err := cmd.CombinedOutput(); err != nil { + return fmt.Errorf("goimports failed: %v\n%s", err, output) + } + + fmt.Printf("Processed %s (%d replacements)\n", filename, len(replacements)) + return nil +} + +type replacement struct { + start int + end int + newText string +} + +func findTestingParam(fn *ast.FuncDecl) string { + if fn.Type.Params == nil { + return "" + } + for _, param := range fn.Type.Params.List { + if isTestingType(param.Type) { + if len(param.Names) > 0 { + return param.Names[0].Name + } + } + } + return "" +} + +func isTestingType(expr ast.Expr) bool { + switch t := expr.(type) { + case *ast.StarExpr: + sel, ok := t.X.(*ast.SelectorExpr) + if !ok { + return false + } + ident, ok := sel.X.(*ast.Ident) + if !ok { + return false + } + return ident.Name == "testing" && (sel.Sel.Name == "T" || sel.Sel.Name == "B" || sel.Sel.Name == "F") + case *ast.SelectorExpr: + ident, ok := t.X.(*ast.Ident) + if !ok { + return false + } + return ident.Name == "testing" && t.Sel.Name == "TB" + } + return false +} + +func findAssertionsVar(fn *ast.FuncDecl) (varName string, isRequire bool) { + if fn.Body == nil { + return "", false + } + + ast.Inspect(fn.Body, func(n ast.Node) bool { + assign, ok := n.(*ast.AssignStmt) + if !ok { + return true + } + if len(assign.Lhs) != 1 || len(assign.Rhs) != 1 { + return true + } + + // Check for require.New(t) or assert.New(t) + call, ok := assign.Rhs[0].(*ast.CallExpr) + if !ok { + return true + } + sel, ok := call.Fun.(*ast.SelectorExpr) + if !ok { + return true + } + ident, ok := sel.X.(*ast.Ident) + if !ok { + return true + } + if sel.Sel.Name != "New" { + return true + } + + if ident.Name == "require" { + if lhs, ok := assign.Lhs[0].(*ast.Ident); ok { + varName = lhs.Name + isRequire = true + } + } else if ident.Name == "assert" { + if lhs, ok := assign.Lhs[0].(*ast.Ident); ok { + varName = lhs.Name + isRequire = false + } + } + return true + }) + return +} + +func analyzeErrorCall(call *ast.CallExpr, tVar, assertVar string, assertIsRequire bool, fset *token.FileSet) *replacement { + sel, ok := call.Fun.(*ast.SelectorExpr) + if !ok { + return nil + } + + if sel.Sel.Name != "Error" { + return nil + } + + // Check what kind of call this is + switch x := sel.X.(type) { + case *ast.Ident: + // Could be: require.Error, assert.Error, or a.Error + switch x.Name { + case "require": + // require.Error(t, err, ...) -> errorcontains.CaptureError(t, err, ...) + return makeReplacement(call, fset, "errorcontains.CaptureError", nil) + + case "assert": + // assert.Error(t, err, ...) -> errorcontains.CaptureErrorAssert(t, err, ...) + return makeReplacement(call, fset, "errorcontains.CaptureErrorAssert", nil) + + default: + // Check if this is the assertions variable (a.Error) + if x.Name == assertVar { + // a.Error(err, ...) -> errorcontains.CaptureError(t, err, ...) + funcName := "errorcontains.CaptureError" + if !assertIsRequire { + funcName = "errorcontains.CaptureErrorAssert" + } + return makeReplacementWithT(call, fset, funcName, tVar) + } + } + } + + return nil +} + +func makeReplacement(call *ast.CallExpr, fset *token.FileSet, newFunc string, extraArgs []string) *replacement { + start := fset.Position(call.Pos()).Offset + end := fset.Position(call.End()).Offset + + // Build new call: newFunc(args...) + var args []string + for _, arg := range call.Args { + args = append(args, exprToString(arg, fset)) + } + + newText := fmt.Sprintf("%s(%s)", newFunc, strings.Join(args, ", ")) + return &replacement{start: start, end: end, newText: newText} +} + +func makeReplacementWithT(call *ast.CallExpr, fset *token.FileSet, newFunc, tVar string) *replacement { + start := fset.Position(call.Pos()).Offset + end := fset.Position(call.End()).Offset + + // Build new call: newFunc(t, args...) + args := []string{tVar} + for _, arg := range call.Args { + args = append(args, exprToString(arg, fset)) + } + + newText := fmt.Sprintf("%s(%s)", newFunc, strings.Join(args, ", ")) + return &replacement{start: start, end: end, newText: newText} +} + +// sourceContent holds the original file content for extracting expressions +var sourceContent []byte + +func exprToString(expr ast.Expr, fset *token.FileSet) string { + start := fset.Position(expr.Pos()).Offset + end := fset.Position(expr.End()).Offset + if start >= 0 && end <= len(sourceContent) && start < end { + return string(sourceContent[start:end]) + } + return "" +} diff --git a/tools/block-generator/generator/config_test.go b/tools/block-generator/generator/config_test.go index 1f86aff09b..8c1ee1b022 100644 --- a/tools/block-generator/generator/config_test.go +++ b/tools/block-generator/generator/config_test.go @@ -232,7 +232,7 @@ func TestTxTypeParse(t *testing.T) { isApp, kind, txType, err := parseAppTxType(test.txType) if test.err != "" { - require.Error(t, err, test.err) + require.ErrorContains(t, err, test.err) } else { require.NoError(t, err) require.Equal(t, test.IsApp, isApp, "Mismatch in isApp for %s", test.txType) diff --git a/tools/block-generator/generator/server_test.go b/tools/block-generator/generator/server_test.go index 562d81f8ff..559cd42960 100644 --- a/tools/block-generator/generator/server_test.go +++ b/tools/block-generator/generator/server_test.go @@ -18,7 +18,6 @@ package generator import ( "fmt" - "strings" "testing" "github.com/algorand/go-algorand/test/partitiontest" @@ -104,8 +103,7 @@ func TestParseURL(t *testing.T) { require.NoError(t, err, msg) assert.Equal(t, tc.expectedParam, round) } else { - require.Error(t, err, fmt.Sprintf("Expected an error containing: %s", tc.err)) - require.True(t, strings.Contains(err.Error(), tc.err)) + require.ErrorContains(t, err, tc.err) } }) } diff --git a/tools/block-generator/runner/runner.go b/tools/block-generator/runner/runner.go index e3c857c3ce..9109831875 100644 --- a/tools/block-generator/runner/runner.go +++ b/tools/block-generator/runner/runner.go @@ -35,7 +35,7 @@ func init() { Use: "runner", Short: "Run test suite and collect results.", Long: "Run an automated test suite using the block-generator daemon and a provided conduit binary. Results are captured to a specified output directory.", - RunE: func(cmd *cobra.Command, args []string) error{ + RunE: func(cmd *cobra.Command, args []string) error { fmt.Printf("starting block-generator runner with args: %+v\n", runnerArgs) if runnerArgs.Template == "postgres-exporter" && runnerArgs.PostgresConnectionString == "" { diff --git a/tools/network/bootstrap_test.go b/tools/network/bootstrap_test.go index a20e493bc3..b43a490e19 100644 --- a/tools/network/bootstrap_test.go +++ b/tools/network/bootstrap_test.go @@ -35,7 +35,7 @@ func TestReadFromSRVPriority(t *testing.T) { secure := true prioAddrs, err := ReadFromSRVPriority("", protocol, name, fallback, secure) - require.Error(t, err) + require.ErrorContains(t, err, `ReadFromBootstrap: DNS LookupSRV failed when using system resolver(no signature in DNS response for _._tls.devnet.algodev.network), fallback resolver(), as well as using default resolver due to no signature in DNS response for _._tls.devnet.algodev.network`) prioAddrs, err = ReadFromSRVPriority(service, protocol, name, fallback, secure) require.NoError(t, err) @@ -57,7 +57,7 @@ func TestReadFromSRV(t *testing.T) { secure := true addrs, err := ReadFromSRV(context.Background(), "", protocol, name, fallback, secure) - require.Error(t, err) + require.ErrorContains(t, err, `ReadFromBootstrap: DNS LookupSRV failed when using system resolver(no signature in DNS response for _._tls.devnet.algodev.network), fallback resolver(), as well as using default resolver due to no signature in DNS response for _._tls.devnet.algodev.network`) addrs, err = ReadFromSRV(context.Background(), service, protocol, name, fallback, secure) require.NoError(t, err) diff --git a/tools/network/dnssec/anchor_test.go b/tools/network/dnssec/anchor_test.go index 1d3502d732..6c01614390 100644 --- a/tools/network/dnssec/anchor_test.go +++ b/tools/network/dnssec/anchor_test.go @@ -42,5 +42,5 @@ func TestParseRootTrustAnchor(t *testing.T) { a.Equal(uint8(2), currentDS.DigestType) _, err = makeRootTrustAnchor("not xml") - a.Error(err) + require.ErrorContains(t, err, `EOF`) } diff --git a/tools/network/dnssec/client_test.go b/tools/network/dnssec/client_test.go index 82df54c2fd..89b1438767 100644 --- a/tools/network/dnssec/client_test.go +++ b/tools/network/dnssec/client_test.go @@ -33,19 +33,19 @@ func TestEmptyClient(t *testing.T) { c := MakeDNSClient(nil, time.Second) rr, rsig, err := c.QueryRRSet(context.Background(), "test", 0) - a.Error(err) + require.ErrorContains(t, err, `no answer for (test., 0) from DNS servers [`) a.Empty(rr) a.Empty(rsig) c = MakeDNSClient([]ResolverAddress{}, time.Second) rr, rsig, err = c.QueryRRSet(context.Background(), "test", 0) - a.Error(err) + require.ErrorContains(t, err, `no answer for (test., 0) from DNS servers [`) a.Empty(rr) a.Empty(rsig) c = MakeDNSClient([]ResolverAddress{"example.com"}, time.Millisecond) rr, rsig, err = c.QueryRRSet(context.Background(), "test", 0) - a.Error(err) + require.ErrorContains(t, err, `no answer for (test., 0) from DNS servers [example.com`) a.Empty(rr) a.Empty(rsig) } @@ -66,7 +66,7 @@ func TestMockedClient(t *testing.T) { qs := ttr{} c := dnsClient{[]ResolverAddress{"test"}, time.Second, qs} rr, rsig, err := c.QueryRRSet(context.Background(), "test", 0) - a.Error(err) + require.ErrorContains(t, err, `no signature in DNS response for test`) a.Empty(rr) a.Empty(rsig) @@ -74,7 +74,7 @@ func TestMockedClient(t *testing.T) { qs = ttr{msg: dns.Msg{MsgHdr: dns.MsgHdr{Rcode: dns.RcodeServerFailure}, Answer: answer}} c = dnsClient{[]ResolverAddress{"test"}, time.Second, qs} rr, rsig, err = c.QueryRRSet(context.Background(), "test", 0) - a.Error(err) + require.ErrorContains(t, err, `DNS error: SERVFAIL`) a.Contains(err.Error(), "SERVFAIL") a.Empty(rr) a.Empty(rsig) diff --git a/tools/network/dnssec/config_unix_test.go b/tools/network/dnssec/config_unix_test.go index 9d9b82f9e5..fa607f78fa 100644 --- a/tools/network/dnssec/config_unix_test.go +++ b/tools/network/dnssec/config_unix_test.go @@ -33,7 +33,7 @@ func TestConfigEmpty(t *testing.T) { a := require.New(t) s, tm, err := systemConfig(nil) - a.Error(err) + require.ErrorContains(t, err, `empty config reader`) a.Empty(s) a.Empty(tm) diff --git a/tools/network/dnssec/dnssec_test.go b/tools/network/dnssec/dnssec_test.go index 590c2e49ad..e25ce1f73b 100644 --- a/tools/network/dnssec/dnssec_test.go +++ b/tools/network/dnssec/dnssec_test.go @@ -111,7 +111,7 @@ func TestLookup(t *testing.T) { a.Equal("target1.test.", res[2].Target) name, res, err = dnssec.LookupSRV(context.Background(), "", "", "my-srv-1.test.") - a.Error(err) + require.ErrorContains(t, err, `my-srv-1.test. not found`) // check CNAME cname := dns.CNAME{ @@ -122,11 +122,11 @@ func TestLookup(t *testing.T) { a.NoError(err) _, err = dnssec.LookupCNAME(context.Background(), "algo.test.") - a.Error(err) + require.ErrorContains(t, err, `my-algo.test. not found`) a.Contains(err.Error(), "my-algo.test. not found") _, err = dnssec.LookupCNAME(context.Background(), "algo-1.test.") - a.Error(err) + require.ErrorContains(t, err, `algo-1.test. not found`) err = r.updateARecord("my-algo.test.", net.IPv4(11, 12, 13, 14), time.Time{}) a.NoError(err) @@ -135,7 +135,7 @@ func TestLookup(t *testing.T) { a.Equal(net.IPv4(11, 12, 13, 14), addrs[0].IP) addrs, err = dnssec.LookupIPAddr(context.Background(), "algo-1.test.") - a.Error(err) + require.ErrorContains(t, err, `algo-1.test. not found`) a.Empty(addrs) // test double redirection @@ -163,12 +163,12 @@ func TestLookup(t *testing.T) { dnssec.maxHops = 2 addrs, err = dnssec.LookupIPAddr(context.Background(), "main.test.") - a.Error(err) + require.ErrorContains(t, err, `exceed max attempts 2`) a.Empty(addrs) // check non-existing addrs, err = dnssec.LookupIPAddr(context.Background(), "main-12.test.") - a.Error(err) + require.ErrorContains(t, err, `main-12.test. not found`) a.Empty(addrs) // create a loop and expect failure @@ -182,7 +182,7 @@ func TestLookup(t *testing.T) { dnssec.maxHops = DefaultMaxHops addrs, err = dnssec.LookupIPAddr(context.Background(), "main.test.") - a.Error(err) + require.ErrorContains(t, err, `loop detected: main.test. already seen`) a.Contains(err.Error(), "loop detected: main.test. already seen") a.Empty(addrs) @@ -198,7 +198,7 @@ func TestLookup(t *testing.T) { a.NoError(err) addrs, err = dnssec.LookupIPAddr(context.Background(), "follower2.test.") - a.Error(err) + require.ErrorContains(t, err, `multiple CNAME RR detected`) a.Contains(err.Error(), "multiple CNAME RR detected") a.Empty(addrs) @@ -211,7 +211,7 @@ func TestLookup(t *testing.T) { maxHops: DefaultMaxHops, } addrs, err = dnssec.LookupIPAddr(context.Background(), "www.test.") - a.Error(err) + require.ErrorContains(t, err, `. not found`) a.Contains(err.Error(), ". not found") } @@ -324,8 +324,7 @@ func TestDeadNS(t *testing.T) { r := MakeDnssecResolver([]ResolverAddress{"192.168.12.34:5678", "10.12.34.56:890"}, time.Microsecond) addrs, err := r.LookupIPAddr(context.Background(), "example.com") - a.Error(err) - a.Contains(err.Error(), "no answer for") + require.ErrorContains(t, err, "no answer for") a.Empty(addrs) // possible race :( with 100ms timeout @@ -353,7 +352,7 @@ func TestRealRequests(t *testing.T) { a.NoError(err) a.Equal(2, len(addrs)) _, err = r.LookupIPAddr(context.Background(), "dnssec-failed.org") - a.Error(err) + require.Error(t, err) // DNSSEC validation error varies by DNS server response // SRV srvFullName := "_algobootstrap._tcp.mainnet.algorand.network." @@ -382,8 +381,7 @@ func TestRealRequests(t *testing.T) { // but it is two-level aliasing r.(*Resolver).maxHops = 1 addrs, err = r.LookupIPAddr(context.Background(), "relay-montreal-mainnet-algorand.algorand-mainnet.network.") - a.Error(err) - a.Contains(err.Error(), "exceed max attempts") + require.ErrorContains(t, err, "exceed max attempts") } func TestDefaultResolver(t *testing.T) { diff --git a/tools/network/dnssec/trustedchain_test.go b/tools/network/dnssec/trustedchain_test.go index 8b459669b7..2f9566bdf7 100644 --- a/tools/network/dnssec/trustedchain_test.go +++ b/tools/network/dnssec/trustedchain_test.go @@ -185,10 +185,10 @@ func TestEnsureTrustChain(t *testing.T) { // this was removed during last cache re-validation // triggers the cache update and the chain is broken err = tch.ensure(context.Background(), "algo.test.", []uint16{algoTestZSK.KeyTag()}) - a.Error(err) + require.ErrorContains(t, err, `no KSK to verify DNSKEY RRSet of test.`) // this is a new, triggers the cache update and the chain is broken err = tch.ensure(context.Background(), "cow.test.", []uint16{cowTestZSK.KeyTag()}) - a.Error(err) + require.ErrorContains(t, err, `no KSK to verify DNSKEY RRSet of test.`) // DNSKEY expiration test // DNSKEY is expired when its RRSIG expired @@ -270,7 +270,7 @@ func TestEnsureTrustChain(t *testing.T) { a.NoError(err) newCh = makeTrustChain(r) err = newCh.ensure(context.Background(), "algo.test.", []uint16{algoTestZSK.KeyTag()}) - a.Error(err) + require.ErrorContains(t, err, `failed to verify test. KSK against digest in parent DS`) a.Contains(err.Error(), "failed to verify test. KSK against digest in parent DS") } @@ -297,10 +297,10 @@ func TestEnsureTrustChainError(t *testing.T) { // test error on empty zone newCh := makeTrustChain(r) err = newCh.ensure(context.Background(), "", []uint16{}) - a.Error(err) + require.ErrorContains(t, err, `is not FQDN`) // test non-existing ZSK err = newCh.ensure(context.Background(), ".", []uint16{}) - a.Error(err) + require.ErrorContains(t, err, `ZSK [] not found in zone .`) a.Contains(err.Error(), "ZSK [] not found in zone .") testKSK, testKSKsk := getKey("test.", dns.ZONE|dns.SEP) @@ -321,7 +321,7 @@ func TestEnsureTrustChainError(t *testing.T) { tch := makeTrustChain(r) err = tch.ensure(context.Background(), "test.", []uint16{testZSK.KeyTag()}) - a.Error(err) + require.ErrorContains(t, err, `cache outdated for already updated zone .`) a.Contains(err.Error(), "cache outdated for already updated zone .") } @@ -353,7 +353,7 @@ func TestAuthenticate(t *testing.T) { rrsig = append(rrsig, sig) tch := makeTrustChain(r) err = tch.Authenticate(context.Background(), rrset, rrsig) - a.Error(err) + require.ErrorContains(t, err, `signer name mismatch: . vs test`) a.Contains(err.Error(), "signer name mismatch") testKSK, testKSKsk := getKey("test.", dns.ZONE|dns.SEP) @@ -376,12 +376,12 @@ func TestAuthenticate(t *testing.T) { // DNSKEY is signed with KSK so authenticate will fail looking up KSK in ZSK rrset, rrsig, err = r.QueryRRSet(context.Background(), "test.", dns.TypeDNSKEY) err = tch.Authenticate(context.Background(), rrset, rrsig) - a.Error(err) + require.ErrorContains(t, err, `not found in zone`) err = tch.Authenticate(context.Background(), rrset, nil) - a.Error(err) + require.ErrorContains(t, err, `empty RRSIG`) err = tch.Authenticate(context.Background(), rrset, []dns.RRSIG{}) - a.Error(err) + require.ErrorContains(t, err, `empty RRSIG`) } func TestQueryWrapper(t *testing.T) { diff --git a/tools/network/dnssec/trustedzone_test.go b/tools/network/dnssec/trustedzone_test.go index 32d91a4b83..c510b3bb67 100644 --- a/tools/network/dnssec/trustedzone_test.go +++ b/tools/network/dnssec/trustedzone_test.go @@ -96,7 +96,7 @@ func TestTrustedZone(t *testing.T) { zk.PublicKey = ksks[0].PublicKey tzRoot.zsk[zsks[0].KeyTag()] = zk cacheOutdated, err = tzRoot.verifyDS(rrsDS, rrsigsDS, tt) - a.Error(err) + require.ErrorContains(t, err, `DS signature verification failed for 'com.'`) a.False(cacheOutdated) } @@ -146,7 +146,7 @@ func TestMakeTrustedZone(t *testing.T) { } tzRoot.zsk[zk.KeyTag()] = ks tzCom, cacheOutdated, err = makeTrustedZone(context.Background(), "com.", tzRoot, r, tt) - a.Error(err) + require.ErrorContains(t, err, `DS signature verification failed for 'com.'`) a.Contains(err.Error(), "DS signature verification failed") a.False(cacheOutdated) a.Empty(tzCom) @@ -155,7 +155,7 @@ func TestMakeTrustedZone(t *testing.T) { // test non-existing zone tzOrg, cacheOutdated, err := makeTrustedZone(context.Background(), "ttt.", tzRoot, r, tt) - a.Error(err) + require.ErrorContains(t, err, `ttt. not found`) a.False(cacheOutdated) a.Empty(tzOrg) @@ -171,8 +171,7 @@ func TestMakeTrustedZone(t *testing.T) { a.NoError(err) tzRoot, cacheOutdated, err = makeTrustedZone(context.Background(), ".", trustedZone{}, re, tt) - a.Error(err) - a.Contains(err.Error(), "EOF") + require.ErrorContains(t, err, "EOF") a.False(cacheOutdated) a.Empty(tzRoot) @@ -187,7 +186,7 @@ func TestMakeTrustedZone(t *testing.T) { a.NoError(err) tzTest, cacheOutdated, err := makeTrustedZone(context.Background(), "test.", trustedZone{}, re, tt) - a.Error(err) + require.ErrorContains(t, err, `test. not found`) a.Contains(err.Error(), "test. not found") a.False(cacheOutdated) a.Empty(tzTest) diff --git a/tools/network/dnssec/util_test.go b/tools/network/dnssec/util_test.go index 22ac880d7a..9191090d56 100644 --- a/tools/network/dnssec/util_test.go +++ b/tools/network/dnssec/util_test.go @@ -31,13 +31,13 @@ func TestSplitZone(t *testing.T) { var err error res, err = splitToZones("") - a.Error(err) + require.ErrorContains(t, err, `is not FQDN`) res, err = splitToZones("com") - a.Error(err) + require.ErrorContains(t, err, `com is not FQDN`) res, err = splitToZones("example.com") - a.Error(err) + require.ErrorContains(t, err, `example.com is not FQDN`) res, err = splitToZones(".") a.NoError(err) @@ -64,13 +64,13 @@ func TestParentZone(t *testing.T) { var err error res, err = getParentZone("") - a.Error(err) + require.ErrorContains(t, err, `is not FQDN`) res, err = getParentZone("com") - a.Error(err) + require.ErrorContains(t, err, `com is not FQDN`) res, err = getParentZone(".") - a.Error(err) + require.ErrorContains(t, err, `No parent zone for .`) res, err = getParentZone("com.") a.NoError(err) diff --git a/tools/network/resolveController_test.go b/tools/network/resolveController_test.go index 8dd1496c9f..4ebdedf179 100644 --- a/tools/network/resolveController_test.go +++ b/tools/network/resolveController_test.go @@ -118,6 +118,6 @@ func TestRealNamesWithResolver(t *testing.T) { timeoutCtx, cancel := context.WithTimeout(context.Background(), time.Second) _, err = r.LookupIPAddr(timeoutCtx, example) cancel() - a.Error(err) + require.Error(t, err) // timeout or connection refused - error message varies by platform } } diff --git a/tools/network/resolver_test.go b/tools/network/resolver_test.go index a414f649f2..d833865bd9 100644 --- a/tools/network/resolver_test.go +++ b/tools/network/resolver_test.go @@ -75,7 +75,7 @@ func TestResolverWithInvalidDNSResolution(t *testing.T) { timingOutContext, timingOutContextFunc := context.WithTimeout(context.Background(), time.Duration(100)*time.Millisecond) defer timingOutContextFunc() cname, addrs, err := resolver.LookupSRV(timingOutContext, "telemetry", "tls", "devnet.algodev.network") - require.Error(t, err) + require.ErrorContains(t, err, `dial udp 255.255.128.1:53: i/o timeout`) require.Equal(t, "", cname) require.True(t, len(addrs) == 0) require.Equal(t, "255.255.128.1", resolver.EffectiveResolverDNS()) diff --git a/tools/x-repo-types/typeAnalyzer/typeAnalyzer_test.go b/tools/x-repo-types/typeAnalyzer/typeAnalyzer_test.go index 778bdccf53..3fe463d26e 100644 --- a/tools/x-repo-types/typeAnalyzer/typeAnalyzer_test.go +++ b/tools/x-repo-types/typeAnalyzer/typeAnalyzer_test.go @@ -75,7 +75,7 @@ func TestEdgeFromLabel(t *testing.T) { t.Parallel() edge, err := ChildNameFromLabel(tc.label) if tc.expectError { - require.Error(t, err) + require.ErrorContains(t, err, `invalid label`) require.Equal(t, ChildName{}, edge) } else { require.NoError(t, err) diff --git a/util/execpool/stream_test.go b/util/execpool/stream_test.go index b311acddd5..5863ec3888 100644 --- a/util/execpool/stream_test.go +++ b/util/execpool/stream_test.go @@ -430,7 +430,7 @@ func TestPendingJobOnRestart(t *testing.T) { // wait for the notifiation from cleanup before checking the TestPendingJobOnRestart <-mbp.notify - require.Error(t, mj.returnError) + require.ErrorContains(t, mj.returnError, `not processed, execpool service is shutting down`) require.False(t, mj.processed) <-callbackFeedback diff --git a/util/rateLimit_test.go b/util/rateLimit_test.go index 5a62cac5b5..64f3d675c2 100644 --- a/util/rateLimit_test.go +++ b/util/rateLimit_test.go @@ -70,7 +70,7 @@ func TestElasticRateLimiterCongestionControlled(t *testing.T) { _, _, err = erl.ConsumeCapacity(client) assert.Equal(t, 0, len(erl.capacityByClient[client])) assert.Equal(t, 1, len(erl.sharedCapacity)) - assert.Error(t, err) + assert.ErrorContains(t, err, `congestionManager prevented client from consuming capacity`) erl.DisableCongestionControl() _, _, err = erl.ConsumeCapacity(client) @@ -161,7 +161,7 @@ func TestConsumeReleaseCapacity(t *testing.T) { _, _, err = erl.ConsumeCapacity(client) assert.Equal(t, 0, len(erl.capacityByClient[client])) assert.Equal(t, 0, len(erl.sharedCapacity)) - assert.Error(t, err) + assert.ErrorContains(t, err, `could not consume capacity from capacityQueue`) // now release the capacity and observe the items return to the correct places err = c1.Release() diff --git a/util/s3/s3Helper_test.go b/util/s3/s3Helper_test.go index 32e4c58f8a..639cc8fbb4 100644 --- a/util/s3/s3Helper_test.go +++ b/util/s3/s3Helper_test.go @@ -243,7 +243,7 @@ func TestGetPartsFromVersion(t *testing.T) { } _, _, _, err := GetVersionPartsFromVersion(1<<40 - 1) - require.Error(t, err, "Versions less than 1.0.0 should not be parsed.") + require.ErrorContains(t, err, `versions below 1.0.0 not supported`, "Versions less than 1.0.0 should not be parsed.") } func TestGetPartsFromVersionEndToEnd(t *testing.T) {