diff --git a/Makefile b/Makefile index 09615018d59a..b5c18dd21632 100644 --- a/Makefile +++ b/Makefile @@ -362,7 +362,7 @@ benchmark: ### Linting ### ############################################################################### -golangci_version=v1.54.2 +golangci_version=v1.55.0 lint-install: @echo "--> Installing golangci-lint $(golangci_version)" diff --git a/baseapp/abci_test.go b/baseapp/abci_test.go index b12d1ac747f1..b60bc1b42321 100644 --- a/baseapp/abci_test.go +++ b/baseapp/abci_test.go @@ -43,6 +43,12 @@ import ( "github.com/cosmos/cosmos-sdk/x/auth/signing" ) +const ( + failStr = "&failOnAnte=false" + fooStr = "foo" + counterStr = "counter=" +) + func TestABCI_Info(t *testing.T) { suite := NewBaseAppSuite(t) ctx := suite.baseApp.NewContext(true) @@ -242,7 +248,7 @@ func TestABCI_FinalizeBlock_WithBeginAndEndBlocker(t *testing.T) { Type: "sometype", Attributes: []abci.EventAttribute{ { - Key: "foo", + Key: fooStr, Value: "bar", }, }, @@ -258,7 +264,7 @@ func TestABCI_FinalizeBlock_WithBeginAndEndBlocker(t *testing.T) { Type: "anothertype", Attributes: []abci.EventAttribute{ { - Key: "foo", + Key: fooStr, Value: "bar", }, }, @@ -280,13 +286,13 @@ func TestABCI_FinalizeBlock_WithBeginAndEndBlocker(t *testing.T) { require.Len(t, res.Events, 2) require.Equal(t, "sometype", res.Events[0].Type) - require.Equal(t, "foo", res.Events[0].Attributes[0].Key) + require.Equal(t, fooStr, res.Events[0].Attributes[0].Key) require.Equal(t, "bar", res.Events[0].Attributes[0].Value) require.Equal(t, "mode", res.Events[0].Attributes[1].Key) require.Equal(t, "BeginBlock", res.Events[0].Attributes[1].Value) require.Equal(t, "anothertype", res.Events[1].Type) - require.Equal(t, "foo", res.Events[1].Attributes[0].Key) + require.Equal(t, fooStr, res.Events[1].Attributes[0].Key) require.Equal(t, "bar", res.Events[1].Attributes[0].Value) require.Equal(t, "mode", res.Events[1].Attributes[1].Key) require.Equal(t, "EndBlock", res.Events[1].Attributes[1].Value) @@ -303,13 +309,13 @@ func TestABCI_ExtendVote(t *testing.T) { app := baseapp.NewBaseApp(name, log.NewTestLogger(t), db, nil) app.SetExtendVoteHandler(func(ctx sdk.Context, req *abci.RequestExtendVote) (*abci.ResponseExtendVote, error) { - voteExt := "foo" + hex.EncodeToString(req.Hash) + strconv.FormatInt(req.Height, 10) + voteExt := fooStr + hex.EncodeToString(req.Hash) + strconv.FormatInt(req.Height, 10) return &abci.ResponseExtendVote{VoteExtension: []byte(voteExt)}, nil }) app.SetVerifyVoteExtensionHandler(func(ctx sdk.Context, req *abci.RequestVerifyVoteExtension) (*abci.ResponseVerifyVoteExtension, error) { // do some kind of verification here - expectedVoteExt := "foo" + hex.EncodeToString(req.Hash) + strconv.FormatInt(req.Height, 10) + expectedVoteExt := fooStr + hex.EncodeToString(req.Hash) + strconv.FormatInt(req.Height, 10) if !bytes.Equal(req.VoteExtension, []byte(expectedVoteExt)) { return &abci.ResponseVerifyVoteExtension{Status: abci.ResponseVerifyVoteExtension_REJECT}, nil } @@ -387,7 +393,7 @@ func TestABCI_OnlyVerifyVoteExtension(t *testing.T) { app.SetVerifyVoteExtensionHandler(func(ctx sdk.Context, req *abci.RequestVerifyVoteExtension) (*abci.ResponseVerifyVoteExtension, error) { // do some kind of verification here - expectedVoteExt := "foo" + hex.EncodeToString(req.Hash) + strconv.FormatInt(req.Height, 10) + expectedVoteExt := fooStr + hex.EncodeToString(req.Hash) + strconv.FormatInt(req.Height, 10) if !bytes.Equal(req.VoteExtension, []byte(expectedVoteExt)) { return &abci.ResponseVerifyVoteExtension{Status: abci.ResponseVerifyVoteExtension_REJECT}, nil } @@ -450,7 +456,7 @@ func TestABCI_GRPCQuery(t *testing.T) { }) require.NoError(t, err) - req := testdata.SayHelloRequest{Name: "foo"} + req := testdata.SayHelloRequest{Name: fooStr} reqBz, err := req.Marshal() require.NoError(t, err) @@ -1482,7 +1488,7 @@ func TestABCI_Proposal_Read_State_PrepareProposal(t *testing.T) { setInitChainerOpt := func(bapp *baseapp.BaseApp) { bapp.SetInitChainer(func(ctx sdk.Context, req *abci.RequestInitChain) (*abci.ResponseInitChain, error) { - ctx.KVStore(capKey1).Set(someKey, []byte("foo")) + ctx.KVStore(capKey1).Set(someKey, []byte(fooStr)) return &abci.ResponseInitChain{}, nil }) } @@ -1491,7 +1497,7 @@ func TestABCI_Proposal_Read_State_PrepareProposal(t *testing.T) { bapp.SetPrepareProposal(func(ctx sdk.Context, req *abci.RequestPrepareProposal) (*abci.ResponsePrepareProposal, error) { value := ctx.KVStore(capKey1).Get(someKey) // We should be able to access any state written in InitChain - require.Equal(t, "foo", string(value)) + require.Equal(t, fooStr, string(value)) return &abci.ResponsePrepareProposal{Txs: req.Txs}, nil }) } @@ -1666,7 +1672,7 @@ func TestABCI_PrepareProposal_OverGasUnderBytes(t *testing.T) { builder := suite.txConfig.NewTxBuilder() err = builder.SetMsgs(msgs...) require.NoError(t, err) - builder.SetMemo("counter=" + strconv.FormatInt(i, 10) + "&failOnAnte=false") + builder.SetMemo(counterStr + strconv.FormatInt(i, 10) + failStr) builder.SetGasLimit(10) setTxSignature(t, builder, uint64(i)) @@ -1706,7 +1712,7 @@ func TestABCI_PrepareProposal_MaxGas(t *testing.T) { builder := suite.txConfig.NewTxBuilder() err = builder.SetMsgs(msgs...) require.NoError(t, err) - builder.SetMemo("counter=" + strconv.FormatInt(i, 10) + "&failOnAnte=false") + builder.SetMemo(counterStr + strconv.FormatInt(i, 10) + failStr) builder.SetGasLimit(10) setTxSignature(t, builder, uint64(i)) diff --git a/go.work.example b/go.work.example index df76c1f2d083..2b0084be8402 100644 --- a/go.work.example +++ b/go.work.example @@ -15,18 +15,19 @@ use ( ./orm ./simapp ./tests + ./tests/starship/tests ./store ./tools/cosmovisor ./tools/confix ./tools/hubl ./x/accounts - ./x/group - ./x/tx - ./x/nft ./x/circuit - ./x/feegrant ./x/evidence - ./x/upgrade - ./x/protocolpool + ./x/feegrant + ./x/group + ./x/nft ./x/params + ./x/protocolpool + ./x/tx + ./x/upgrade ) diff --git a/math/CHANGELOG.md b/math/CHANGELOG.md index 19c6be5cbbf2..68a0cffed10b 100644 --- a/math/CHANGELOG.md +++ b/math/CHANGELOG.md @@ -43,6 +43,7 @@ Ref: https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.j ### Bug Fixes * [#18214](https://github.com/cosmos/cosmos-sdk/pull/18214) Ensure that modifying the argument to `NewUIntFromBigInt` doesn't mutate the returned value. +* [#18211](https://github.com/cosmos/cosmos-sdk/pull/18211) RelativePow now returns 1 when 0^0, before it was returning the scale factor. * [#17725](https://github.com/cosmos/cosmos-sdk/pull/17725) Fix state break in ApproxRoot. This has been present since math/v1.0.1. It changed the rounding behavior at precision end in an intermediary division from banker's to truncation. The truncation occurs from binary right shift in the case of square roots. The change is now reverted back to banker's rounding universally for any root. ## [math/v1.1.2](https://github.com/cosmos/cosmos-sdk/releases/tag/math/v1.1.2) - 2023-08-21 diff --git a/math/uint.go b/math/uint.go index 6542dd8f20ea..85906047bff1 100644 --- a/math/uint.go +++ b/math/uint.go @@ -243,7 +243,7 @@ func checkNewUint(i *big.Int) (Uint, error) { func RelativePow(x, n, b Uint) (z Uint) { if x.IsZero() { if n.IsZero() { - z = b // 0^0 = 1 + z = OneUint() // 0^0 = 1 return z } z = ZeroUint() // otherwise 0^a = 0 diff --git a/math/uint_test.go b/math/uint_test.go index 1cfb123d1d16..3ae815e0e662 100644 --- a/math/uint_test.go +++ b/math/uint_test.go @@ -281,7 +281,7 @@ func (s *uintTestSuite) TestRelativePow() { want sdkmath.Uint }{ {[]sdkmath.Uint{sdkmath.ZeroUint(), sdkmath.ZeroUint(), sdkmath.OneUint()}, sdkmath.OneUint()}, - {[]sdkmath.Uint{sdkmath.ZeroUint(), sdkmath.ZeroUint(), sdkmath.NewUint(10)}, sdkmath.NewUint(10)}, + {[]sdkmath.Uint{sdkmath.ZeroUint(), sdkmath.ZeroUint(), sdkmath.NewUint(10)}, sdkmath.NewUint(1)}, {[]sdkmath.Uint{sdkmath.ZeroUint(), sdkmath.OneUint(), sdkmath.NewUint(10)}, sdkmath.ZeroUint()}, {[]sdkmath.Uint{sdkmath.NewUint(10), sdkmath.NewUint(2), sdkmath.OneUint()}, sdkmath.NewUint(100)}, {[]sdkmath.Uint{sdkmath.NewUint(210), sdkmath.NewUint(2), sdkmath.NewUint(100)}, sdkmath.NewUint(441)}, diff --git a/orm/internal/codegen/index.go b/orm/internal/codegen/index.go index 7bcdf994f387..797616adb815 100644 --- a/orm/internal/codegen/index.go +++ b/orm/internal/codegen/index.go @@ -9,6 +9,8 @@ import ( "google.golang.org/protobuf/reflect/protoreflect" ) +const indexKey = "IndexKey" + func (t tableGen) genIndexKeys() { // interface that all keys must adhere to t.P("type ", t.indexKeyInterfaceName(), " interface {") @@ -58,7 +60,7 @@ func (t tableGen) genIndexInterfaceGuard(idxKeyName string) { } func (t tableGen) indexKeyInterfaceName() string { - return t.msg.GoIdent.GoName + "IndexKey" + return t.msg.GoIdent.GoName + indexKey } func (t tableGen) genIndexKey(idxKeyName string) { @@ -82,7 +84,7 @@ func (t tableGen) indexKeyName(names []protoreflect.Name) string { cnames[i] = strcase.ToCamel(string(name)) } joinedNames := strings.Join(cnames, "") - return t.msg.GoIdent.GoName + joinedNames + "IndexKey" + return t.msg.GoIdent.GoName + joinedNames + indexKey } func (t tableGen) indexStructName(fields []string) string { @@ -91,7 +93,7 @@ func (t tableGen) indexStructName(fields []string) string { names[i] = strcase.ToCamel(field) } joinedNames := strings.Join(names, "") - return t.msg.GoIdent.GoName + joinedNames + "IndexKey" + return t.msg.GoIdent.GoName + joinedNames + indexKey } func (t tableGen) genIndex(fields string, id uint32, isPrimaryKey bool) { diff --git a/tools/cosmovisor/cmd/cosmovisor/init_test.go b/tools/cosmovisor/cmd/cosmovisor/init_test.go index 1ec40e8eb7e8..c872675e280d 100644 --- a/tools/cosmovisor/cmd/cosmovisor/init_test.go +++ b/tools/cosmovisor/cmd/cosmovisor/init_test.go @@ -17,6 +17,10 @@ import ( "cosmossdk.io/tools/cosmovisor" ) +const ( + notset = " is not set" +) + type InitTestSuite struct { suite.Suite } @@ -302,13 +306,13 @@ func (s *InitTestSuite) TestInitializeCosmovisorNegativeValidation() { name: "no name", env: cosmovisorInitEnv{Home: "/example", Name: ""}, args: []string{tmpExe}, - inErr: []string{cosmovisor.EnvName + " is not set"}, + inErr: []string{cosmovisor.EnvName + notset}, }, { name: "no home", env: cosmovisorInitEnv{Home: "", Name: "foo"}, args: []string{tmpExe}, - inErr: []string{cosmovisor.EnvHome + " is not set"}, + inErr: []string{cosmovisor.EnvHome + notset}, }, { name: "home is relative", @@ -320,7 +324,7 @@ func (s *InitTestSuite) TestInitializeCosmovisorNegativeValidation() { name: "no name and no home", env: cosmovisorInitEnv{Home: "", Name: ""}, args: []string{tmpExe}, - inErr: []string{cosmovisor.EnvName + " is not set", cosmovisor.EnvHome + " is not set"}, + inErr: []string{cosmovisor.EnvName + notset, cosmovisor.EnvHome + notset}, }, } diff --git a/types/address_test.go b/types/address_test.go index 8473047c173a..db0c932f472f 100644 --- a/types/address_test.go +++ b/types/address_test.go @@ -20,6 +20,14 @@ import ( "github.com/cosmos/cosmos-sdk/types/bech32/legacybech32" //nolint:staticcheck // we're using this to support the legacy way of dealing with bech32 ) +const ( + pubStr = "pub" + valoper = "valoper" + valoperpub = "valoperpub" + valcons = "valcons" + valconspub = "valconspub" +) + type addressTestSuite struct { suite.Suite } @@ -138,18 +146,18 @@ func (s *addressTestSuite) TestAddrCache() { // Set SDK bech32 prefixes to 'osmo' prefix := "osmo" conf := types.GetConfig() - conf.SetBech32PrefixForAccount(prefix, prefix+"pub") - conf.SetBech32PrefixForValidator(prefix+"valoper", prefix+"valoperpub") - conf.SetBech32PrefixForConsensusNode(prefix+"valcons", prefix+"valconspub") + conf.SetBech32PrefixForAccount(prefix, prefix+pubStr) + conf.SetBech32PrefixForValidator(prefix+valoper, prefix+valoperpub) + conf.SetBech32PrefixForConsensusNode(prefix+valcons, prefix+valconspub) acc := types.AccAddress(pub.Address()) osmoAddrBech32 := acc.String() // Set SDK bech32 to 'cosmos' prefix = "cosmos" - conf.SetBech32PrefixForAccount(prefix, prefix+"pub") - conf.SetBech32PrefixForValidator(prefix+"valoper", prefix+"valoperpub") - conf.SetBech32PrefixForConsensusNode(prefix+"valcons", prefix+"valconspub") + conf.SetBech32PrefixForAccount(prefix, prefix+pubStr) + conf.SetBech32PrefixForValidator(prefix+valoper, prefix+valoperpub) + conf.SetBech32PrefixForConsensusNode(prefix+valcons, prefix+valconspub) // We name this 'addrCosmos' to prove a point, but the bech32 address will still begin with 'osmo' due to the cache behavior. addrCosmos := types.AccAddress(pub.Address()) @@ -175,18 +183,18 @@ func (s *addressTestSuite) TestAddrCacheDisabled() { // Set SDK bech32 prefixes to 'osmo' prefix := "osmo" conf := types.GetConfig() - conf.SetBech32PrefixForAccount(prefix, prefix+"pub") - conf.SetBech32PrefixForValidator(prefix+"valoper", prefix+"valoperpub") - conf.SetBech32PrefixForConsensusNode(prefix+"valcons", prefix+"valconspub") + conf.SetBech32PrefixForAccount(prefix, prefix+pubStr) + conf.SetBech32PrefixForValidator(prefix+valoper, prefix+valoperpub) + conf.SetBech32PrefixForConsensusNode(prefix+valcons, prefix+valconspub) acc := types.AccAddress(pub.Address()) osmoAddrBech32 := acc.String() // Set SDK bech32 to 'cosmos' prefix = "cosmos" - conf.SetBech32PrefixForAccount(prefix, prefix+"pub") - conf.SetBech32PrefixForValidator(prefix+"valoper", prefix+"valoperpub") - conf.SetBech32PrefixForConsensusNode(prefix+"valcons", prefix+"valconspub") + conf.SetBech32PrefixForAccount(prefix, prefix+pubStr) + conf.SetBech32PrefixForValidator(prefix+valoper, prefix+valoperpub) + conf.SetBech32PrefixForConsensusNode(prefix+valcons, prefix+valconspub) addrCosmos := types.AccAddress(pub.Address()) cosmosAddrBech32 := addrCosmos.String() diff --git a/types/context_bench_test.go b/types/context_bench_test.go index 2c936a8ab6a9..4d75d2b7a25a 100644 --- a/types/context_bench_test.go +++ b/types/context_bench_test.go @@ -8,10 +8,15 @@ import ( "github.com/cosmos/cosmos-sdk/testutil" ) +const ( + tcc = "_TestCacheContext" + transient = "transient_" +) + func BenchmarkContext_KVStore(b *testing.B) { - key := types.NewKVStoreKey(b.Name() + "_TestCacheContext") + key := types.NewKVStoreKey(b.Name() + tcc) - ctx := testutil.DefaultContext(key, types.NewTransientStoreKey("transient_"+b.Name())) + ctx := testutil.DefaultContext(key, types.NewTransientStoreKey(transient+b.Name())) b.ResetTimer() for i := 0; i < b.N; i++ { @@ -20,9 +25,9 @@ func BenchmarkContext_KVStore(b *testing.B) { } func BenchmarkContext_TransientStore(b *testing.B) { - key := types.NewKVStoreKey(b.Name() + "_TestCacheContext") + key := types.NewKVStoreKey(b.Name() + tcc) - ctx := testutil.DefaultContext(key, types.NewTransientStoreKey("transient_"+b.Name())) + ctx := testutil.DefaultContext(key, types.NewTransientStoreKey(transient+b.Name())) b.ResetTimer() for i := 0; i < b.N; i++ { @@ -31,9 +36,9 @@ func BenchmarkContext_TransientStore(b *testing.B) { } func BenchmarkContext_CacheContext(b *testing.B) { - key := types.NewKVStoreKey(b.Name() + "_TestCacheContext") + key := types.NewKVStoreKey(b.Name() + tcc) - ctx := testutil.DefaultContext(key, types.NewTransientStoreKey("transient_"+b.Name())) + ctx := testutil.DefaultContext(key, types.NewTransientStoreKey(transient+b.Name())) b.ResetTimer() for i := 0; i < b.N; i++ { diff --git a/x/gov/client/cli/util_test.go b/x/gov/client/cli/util_test.go index fa5ed1e13bd9..b97c26a91457 100644 --- a/x/gov/client/cli/util_test.go +++ b/x/gov/client/cli/util_test.go @@ -27,6 +27,13 @@ import ( stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) +const ( + strPlus = ` string\s+` + startStr = `(?m:^\s+--` + strDollar = `$)` + strHelp = "help output" +) + func TestParseSubmitLegacyProposal(t *testing.T) { okJSON := testutil.WriteToNewTempFile(t, ` { @@ -280,10 +287,10 @@ func TestAddGovPropFlagsToCmd(t *testing.T) { expSummaryDesc := "The summary to include with the governance proposal" // Regexp notes: (?m:...) = multi-line mode so ^ and $ match the beginning and end of each line. // Each regexp assertion checks for a line containing only a specific flag and its description. - assert.Regexp(t, `(?m:^\s+--`+FlagDeposit+` string\s+`+expDepositDesc+`$)`, help, "help output") - assert.Regexp(t, `(?m:^\s+--`+FlagMetadata+` string\s+`+expMetadataDesc+`$)`, help, "help output") - assert.Regexp(t, `(?m:^\s+--`+FlagTitle+` string\s+`+expTitleDesc+`$)`, help, "help output") - assert.Regexp(t, `(?m:^\s+--`+FlagSummary+` string\s+`+expSummaryDesc+`$)`, help, "help output") + assert.Regexp(t, startStr+FlagDeposit+strPlus+expDepositDesc+strDollar, help, strHelp) + assert.Regexp(t, startStr+FlagMetadata+strPlus+expMetadataDesc+strDollar, help, strHelp) + assert.Regexp(t, startStr+FlagTitle+strPlus+expTitleDesc+strDollar, help, strHelp) + assert.Regexp(t, startStr+FlagSummary+strPlus+expSummaryDesc+strDollar, help, strHelp) } func TestReadGovPropFlags(t *testing.T) { diff --git a/x/upgrade/plan/downloader_test.go b/x/upgrade/plan/downloader_test.go index 16ef29585d7f..60706b4ac6d1 100644 --- a/x/upgrade/plan/downloader_test.go +++ b/x/upgrade/plan/downloader_test.go @@ -14,6 +14,12 @@ import ( "github.com/stretchr/testify/suite" ) +const ( + zipStr = ".zip" + checksumStr = "?checksum=sha256:" + fileStr = "file://" +) + type DownloaderTestSuite struct { suite.Suite @@ -145,9 +151,9 @@ func (s *DownloaderTestSuite) TestDownloadUpgrade() { someFileInBin := NewTestFile("bin"+someFileName, "#!/usr/bin\necho 'I am some file in bin'\n") anotherFile := NewTestFile("another-file", "#!/usr/bin\necho 'I am just another file'\n") justAFilePath := s.saveSrcTestFile(justAFile) - justAFileZip := s.saveSrcTestZip(justAFile.Name+".zip", NewTestZip(justAFile)) - someFileInBinZip := s.saveSrcTestZip(someFileInBin.Name+".zip", NewTestZip(someFileInBin)) - allFilesZip := s.saveSrcTestZip(anotherFile.Name+".zip", NewTestZip(justAFile, someFileInBin, anotherFile)) + justAFileZip := s.saveSrcTestZip(justAFile.Name+zipStr, NewTestZip(justAFile)) + someFileInBinZip := s.saveSrcTestZip(someFileInBin.Name+zipStr, NewTestZip(someFileInBin)) + allFilesZip := s.saveSrcTestZip(anotherFile.Name+zipStr, NewTestZip(justAFile, someFileInBin, anotherFile)) getDstDir := func(testName string) string { _, tName := filepath.Split(testName) return s.Home + "/dst/" + tName @@ -164,7 +170,7 @@ func (s *DownloaderTestSuite) TestDownloadUpgrade() { s.T().Run("url has incorrect checksum", func(t *testing.T) { dstRoot := getDstDir(t.Name()) badChecksum := "2c22e34510bd1d4ad2343cdc54f7165bccf30caef73f39af7dd1db2795a3da48" - url := "file://" + justAFilePath + "?checksum=sha256:" + badChecksum + url := fileStr + justAFilePath + checksumStr + badChecksum err := DownloadUpgrade(dstRoot, url, justAFile.Name) require.Error(t, err) assert.Contains(t, err.Error(), "Checksums did not match") @@ -260,14 +266,14 @@ func (s *DownloaderTestSuite) TestDownloadURL() { }) s.T().Run("without checksum", func(t *testing.T) { - url := "file://" + planPath + url := fileStr + planPath actual, err := DownloadURL(url) require.NoError(t, err) require.Equal(t, planContents, actual) }) s.T().Run("with correct checksum", func(t *testing.T) { - url := "file://" + planPath + "?checksum=sha256:" + planChecksum + url := fileStr + planPath + checksumStr + planChecksum actual, err := DownloadURL(url) require.NoError(t, err) require.Equal(t, planContents, actual) @@ -275,7 +281,7 @@ func (s *DownloaderTestSuite) TestDownloadURL() { s.T().Run("with incorrect checksum", func(t *testing.T) { badChecksum := "2c22e34510bd1d4ad2343cdc54f7165bccf30caef73f39af7dd1db2795a3da48" - url := "file://" + planPath + "?checksum=sha256:" + badChecksum + url := fileStr + planPath + checksumStr + badChecksum _, err := DownloadURL(url) require.Error(t, err) assert.Contains(t, err.Error(), "Checksums did not match") @@ -284,7 +290,7 @@ func (s *DownloaderTestSuite) TestDownloadURL() { }) s.T().Run("plan is empty", func(t *testing.T) { - url := "file://" + emptyPlanPath + "?checksum=sha256:" + emptyChecksum + url := fileStr + emptyPlanPath + checksumStr + emptyChecksum _, err := DownloadURL(url) require.Error(t, err) assert.Contains(t, err.Error(), "no content returned")