refactor!: extract simulation helpers out of simapp (#13402)
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
package testutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
)
|
||||
@@ -36,3 +38,41 @@ func GetRequestWithHeaders(url string, headers map[string]string) ([]byte, error
|
||||
|
||||
return body, nil
|
||||
}
|
||||
|
||||
// GetRequest defines a wrapper around an HTTP GET request with a provided URL.
|
||||
// An error is returned if the request or reading the body fails.
|
||||
func GetRequest(url string) ([]byte, error) {
|
||||
res, err := http.Get(url) //nolint:gosec
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
_ = res.Body.Close()
|
||||
}()
|
||||
|
||||
body, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return body, nil
|
||||
}
|
||||
|
||||
// PostRequest defines a wrapper around an HTTP POST request with a provided URL and data.
|
||||
// An error is returned if the request or reading the body fails.
|
||||
func PostRequest(url string, contentType string, data []byte) ([]byte, error) {
|
||||
res, err := http.Post(url, contentType, bytes.NewBuffer(data)) //nolint:gosec
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error while sending post request: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
_ = res.Body.Close()
|
||||
}()
|
||||
|
||||
bz, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading response body: %w", err)
|
||||
}
|
||||
|
||||
return bz, nil
|
||||
}
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
// Package rest provides HTTP types and primitives for REST
|
||||
// requests validation and responses handling.
|
||||
package rest
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// GetRequest defines a wrapper around an HTTP GET request with a provided URL.
|
||||
// An error is returned if the request or reading the body fails.
|
||||
func GetRequest(url string) ([]byte, error) {
|
||||
res, err := http.Get(url) //nolint:gosec
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
_ = res.Body.Close()
|
||||
}()
|
||||
|
||||
body, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return body, nil
|
||||
}
|
||||
|
||||
// PostRequest defines a wrapper around an HTTP POST request with a provided URL and data.
|
||||
// An error is returned if the request or reading the body fails.
|
||||
func PostRequest(url string, contentType string, data []byte) ([]byte, error) {
|
||||
res, err := http.Post(url, contentType, bytes.NewBuffer(data)) //nolint:gosec
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error while sending post request: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
_ = res.Body.Close()
|
||||
}()
|
||||
|
||||
bz, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading response body: %w", err)
|
||||
}
|
||||
|
||||
return bz, nil
|
||||
}
|
||||
@@ -29,11 +29,7 @@ import (
|
||||
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
|
||||
)
|
||||
|
||||
// SimAppChainID hardcoded chainID for simulation
|
||||
const (
|
||||
DefaultGenTxGas = 10000000
|
||||
SimAppChainID = "simulation-app"
|
||||
)
|
||||
const DefaultGenTxGas = 10000000
|
||||
|
||||
// DefaultConsensusParams defines the default Tendermint consensus params used in
|
||||
// SimApp testing.
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
package sims
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
"github.com/cosmos/cosmos-sdk/runtime"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/types/kv"
|
||||
"github.com/cosmos/cosmos-sdk/types/module"
|
||||
simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
dbm "github.com/tendermint/tm-db"
|
||||
)
|
||||
|
||||
// SetupSimulation creates the config, db (levelDB), temporary directory and logger for the simulation tests.
|
||||
// If `skip` is false it skips the current test. `skip` should be set using the `FlagEnabledValue` flag.
|
||||
// Returns error on an invalid db intantiation or temp dir creation.
|
||||
func SetupSimulation(config simtypes.Config, dirPrefix, dbName string, verbose, skip bool) (dbm.DB, string, log.Logger, bool, error) {
|
||||
if !skip {
|
||||
return nil, "", nil, true, nil
|
||||
}
|
||||
|
||||
var logger log.Logger
|
||||
if verbose {
|
||||
logger = log.TestingLogger()
|
||||
} else {
|
||||
logger = log.NewNopLogger()
|
||||
}
|
||||
|
||||
dir, err := os.MkdirTemp("", dirPrefix)
|
||||
if err != nil {
|
||||
return nil, "", nil, false, err
|
||||
}
|
||||
|
||||
db, err := dbm.NewDB(dbName, dbm.BackendType(config.DBBackend), dir)
|
||||
if err != nil {
|
||||
return nil, "", nil, false, err
|
||||
}
|
||||
|
||||
return db, dir, logger, false, nil
|
||||
}
|
||||
|
||||
// SimulationOperations retrieves the simulation params from the provided file path
|
||||
// and returns all the modules weighted operations
|
||||
func SimulationOperations(app runtime.AppI, cdc codec.JSONCodec, config simtypes.Config) []simtypes.WeightedOperation {
|
||||
simState := module.SimulationState{
|
||||
AppParams: make(simtypes.AppParams),
|
||||
Cdc: cdc,
|
||||
}
|
||||
|
||||
if config.ParamsFile != "" {
|
||||
bz, err := os.ReadFile(config.ParamsFile)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = json.Unmarshal(bz, &simState.AppParams)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
simState.Contents = app.SimulationManager().GetProposalContents(simState)
|
||||
return app.SimulationManager().WeightedOperations(simState)
|
||||
}
|
||||
|
||||
// CheckExportSimulation exports the app state and simulation parameters to JSON
|
||||
// if the export paths are defined.
|
||||
func CheckExportSimulation(app runtime.AppI, config simtypes.Config, params simtypes.Params) error {
|
||||
if config.ExportStatePath != "" {
|
||||
fmt.Println("exporting app state...")
|
||||
exported, err := app.ExportAppStateAndValidators(false, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := os.WriteFile(config.ExportStatePath, []byte(exported.AppState), 0o600); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if config.ExportParamsPath != "" {
|
||||
fmt.Println("exporting simulation params...")
|
||||
paramsBz, err := json.MarshalIndent(params, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := os.WriteFile(config.ExportParamsPath, paramsBz, 0o600); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// PrintStats prints the corresponding statistics from the app DB.
|
||||
func PrintStats(db dbm.DB) {
|
||||
fmt.Println("\nLevelDB Stats")
|
||||
fmt.Println(db.Stats()["leveldb.stats"])
|
||||
fmt.Println("LevelDB cached block size", db.Stats()["leveldb.cachedblock"])
|
||||
}
|
||||
|
||||
// GetSimulationLog unmarshals the KVPair's Value to the corresponding type based on the
|
||||
// each's module store key and the prefix bytes of the KVPair's key.
|
||||
func GetSimulationLog(storeName string, sdr sdk.StoreDecoderRegistry, kvAs, kvBs []kv.Pair) (log string) {
|
||||
for i := 0; i < len(kvAs); i++ {
|
||||
if len(kvAs[i].Value) == 0 && len(kvBs[i].Value) == 0 {
|
||||
// skip if the value doesn't have any bytes
|
||||
continue
|
||||
}
|
||||
|
||||
decoder, ok := sdr[storeName]
|
||||
if ok {
|
||||
log += decoder(kvAs[i], kvBs[i])
|
||||
} else {
|
||||
log += fmt.Sprintf("store A %X => %X\nstore B %X => %X\n", kvAs[i].Key, kvAs[i].Value, kvBs[i].Key, kvBs[i].Value)
|
||||
}
|
||||
}
|
||||
|
||||
return log
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package sims
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/types/kv"
|
||||
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
)
|
||||
|
||||
func TestGetSimulationLog(t *testing.T) {
|
||||
legacyAmino := codec.NewLegacyAmino()
|
||||
decoders := make(sdk.StoreDecoderRegistry)
|
||||
decoders[authtypes.StoreKey] = func(kvAs, kvBs kv.Pair) string { return "10" }
|
||||
|
||||
tests := []struct {
|
||||
store string
|
||||
kvPairs []kv.Pair
|
||||
expectedLog string
|
||||
}{
|
||||
{
|
||||
"Empty",
|
||||
[]kv.Pair{{}},
|
||||
"",
|
||||
},
|
||||
{
|
||||
authtypes.StoreKey,
|
||||
[]kv.Pair{{Key: authtypes.GlobalAccountNumberKey, Value: legacyAmino.MustMarshal(uint64(10))}},
|
||||
"10",
|
||||
},
|
||||
{
|
||||
"OtherStore",
|
||||
[]kv.Pair{{Key: []byte("key"), Value: []byte("value")}},
|
||||
fmt.Sprintf("store A %X => %X\nstore B %X => %X\n", []byte("key"), []byte("value"), []byte("key"), []byte("value")),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.store, func(t *testing.T) {
|
||||
require.Equal(t, tt.expectedLog, GetSimulationLog(tt.store, decoders, tt.kvPairs, tt.kvPairs), tt.store)
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user