Merge pull request #140 from CosmWasm/real-contract-migrations-test
Add integration tests with updated wasm contracts
This commit is contained in:
@@ -2,6 +2,7 @@ package keeper
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
@@ -461,9 +462,8 @@ func TestMigrate(t *testing.T) {
|
||||
accKeeper, keeper := keepers.AccountKeeper, keepers.WasmKeeper
|
||||
|
||||
deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000))
|
||||
topUp := sdk.NewCoins(sdk.NewInt64Coin("denom", 5000))
|
||||
creator := createFakeFundedAccount(ctx, accKeeper, deposit.Add(deposit...))
|
||||
fred := createFakeFundedAccount(ctx, accKeeper, topUp)
|
||||
fred := createFakeFundedAccount(ctx, accKeeper, sdk.NewCoins(sdk.NewInt64Coin("denom", 5000)))
|
||||
|
||||
wasmCode, err := ioutil.ReadFile("./testdata/contract.wasm")
|
||||
require.NoError(t, err)
|
||||
@@ -475,12 +475,20 @@ func TestMigrate(t *testing.T) {
|
||||
require.NotEqual(t, originalContractID, newContractID)
|
||||
|
||||
_, _, anyAddr := keyPubAddr()
|
||||
_, _, newVerifierAddr := keyPubAddr()
|
||||
initMsg := InitMsg{
|
||||
Verifier: fred,
|
||||
Beneficiary: anyAddr,
|
||||
}
|
||||
initMsgBz, err := json.Marshal(initMsg)
|
||||
require.NoError(t, err)
|
||||
|
||||
migMsg := struct {
|
||||
Verifier sdk.AccAddress `json:"verifier"`
|
||||
}{Verifier: newVerifierAddr}
|
||||
migMsgBz, err := json.Marshal(migMsg)
|
||||
require.NoError(t, err)
|
||||
|
||||
specs := map[string]struct {
|
||||
admin sdk.AccAddress
|
||||
overrideContractAddr sdk.AccAddress
|
||||
@@ -488,28 +496,35 @@ func TestMigrate(t *testing.T) {
|
||||
codeID uint64
|
||||
migrateMsg []byte
|
||||
expErr *sdkerrors.Error
|
||||
expVerifier sdk.AccAddress
|
||||
}{
|
||||
"all good with same code id": {
|
||||
admin: creator,
|
||||
caller: creator,
|
||||
codeID: originalContractID,
|
||||
admin: creator,
|
||||
caller: creator,
|
||||
codeID: originalContractID,
|
||||
migrateMsg: migMsgBz,
|
||||
expVerifier: newVerifierAddr,
|
||||
},
|
||||
"all good with new code id": {
|
||||
admin: creator,
|
||||
caller: creator,
|
||||
codeID: newContractID,
|
||||
"all good with different code id": {
|
||||
admin: creator,
|
||||
caller: creator,
|
||||
codeID: newContractID,
|
||||
migrateMsg: migMsgBz,
|
||||
expVerifier: newVerifierAddr,
|
||||
},
|
||||
"all good with admin set": {
|
||||
admin: fred,
|
||||
caller: fred,
|
||||
codeID: newContractID,
|
||||
admin: fred,
|
||||
caller: fred,
|
||||
codeID: newContractID,
|
||||
migrateMsg: migMsgBz,
|
||||
expVerifier: newVerifierAddr,
|
||||
},
|
||||
"prevent migration when admin was not set on instantiate": {
|
||||
caller: creator,
|
||||
codeID: originalContractID,
|
||||
expErr: sdkerrors.ErrUnauthorized,
|
||||
},
|
||||
"prevent migration when not admin": {
|
||||
"prevent migration when not sent by admin": {
|
||||
caller: creator,
|
||||
admin: fred,
|
||||
codeID: originalContractID,
|
||||
@@ -528,18 +543,21 @@ func TestMigrate(t *testing.T) {
|
||||
codeID: originalContractID,
|
||||
expErr: sdkerrors.ErrInvalidRequest,
|
||||
},
|
||||
"fail when migration caused error": {
|
||||
"fail in contract with invalid migrate msg": {
|
||||
admin: creator,
|
||||
caller: creator,
|
||||
codeID: originalContractID,
|
||||
migrateMsg: bytes.Repeat([]byte{0x1}, 7), // condition hard coded in stub: >6 = error
|
||||
migrateMsg: bytes.Repeat([]byte{0x1}, 7),
|
||||
expErr: types.ErrMigrationFailed,
|
||||
},
|
||||
"fail in contract without migrate msg": {
|
||||
admin: creator,
|
||||
caller: creator,
|
||||
codeID: originalContractID,
|
||||
expErr: types.ErrMigrationFailed,
|
||||
},
|
||||
}
|
||||
var (
|
||||
builtIntoGoCosmWasmStubGas = sdk.Gas(10000)
|
||||
builtIntoGoCosmWasmStubData = []byte(("my-migration-response-data"))
|
||||
)
|
||||
|
||||
for msg, spec := range specs {
|
||||
t.Run(msg, func(t *testing.T) {
|
||||
ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1)
|
||||
@@ -548,26 +566,141 @@ func TestMigrate(t *testing.T) {
|
||||
if spec.overrideContractAddr != nil {
|
||||
addr = spec.overrideContractAddr
|
||||
}
|
||||
gasBefore := ctx.GasMeter().GasConsumed()
|
||||
res, err := keeper.Migrate(ctx, addr, spec.caller, spec.codeID, spec.migrateMsg)
|
||||
_, err = keeper.Migrate(ctx, addr, spec.caller, spec.codeID, spec.migrateMsg)
|
||||
require.True(t, spec.expErr.Is(err), "expected %v but got %+v", spec.expErr, err)
|
||||
if spec.expErr != nil {
|
||||
return
|
||||
}
|
||||
gasAfter := ctx.GasMeter().GasConsumed()
|
||||
assert.Greater(t, gasAfter-gasBefore, builtIntoGoCosmWasmStubGas/GasMultiplier)
|
||||
assert.Equal(t, builtIntoGoCosmWasmStubData, res.Data)
|
||||
cInfo := keeper.GetContractInfo(ctx, addr)
|
||||
assert.Equal(t, spec.codeID, cInfo.CodeID)
|
||||
assert.Equal(t, originalContractID, cInfo.PreviousCodeID)
|
||||
assert.Equal(t, types.NewCreatedAt(ctx), cInfo.LastUpdated)
|
||||
// TODO: check contract store was updated by migration code (impl also in contract)
|
||||
// TODO: check any messages dispatched proper
|
||||
// TODO: check events?
|
||||
|
||||
m := keeper.QueryRaw(ctx, addr, []byte("config"))
|
||||
require.Len(t, m, 1)
|
||||
var stored map[string][]byte
|
||||
require.NoError(t, json.Unmarshal(m[0].Value, &stored))
|
||||
require.Contains(t, stored, "verifier")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, spec.expVerifier, sdk.AccAddress(stored["verifier"]))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMigrateWithDispatchedMessage(t *testing.T) {
|
||||
tempDir, err := ioutil.TempDir("", "wasm")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tempDir)
|
||||
ctx, keepers := CreateTestInput(t, false, tempDir, SupportedFeatures, nil, nil)
|
||||
accKeeper, keeper := keepers.AccountKeeper, keepers.WasmKeeper
|
||||
|
||||
deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000))
|
||||
creator := createFakeFundedAccount(ctx, accKeeper, deposit.Add(deposit...))
|
||||
fred := createFakeFundedAccount(ctx, accKeeper, sdk.NewCoins(sdk.NewInt64Coin("denom", 5000)))
|
||||
|
||||
wasmCode, err := ioutil.ReadFile("./testdata/contract.wasm")
|
||||
require.NoError(t, err)
|
||||
burnerCode, err := ioutil.ReadFile("./testdata/burner.wasm")
|
||||
require.NoError(t, err)
|
||||
|
||||
originalContractID, err := keeper.Create(ctx, creator, wasmCode, "", "")
|
||||
require.NoError(t, err)
|
||||
burnerContractID, err := keeper.Create(ctx, creator, burnerCode, "", "")
|
||||
require.NoError(t, err)
|
||||
require.NotEqual(t, originalContractID, burnerContractID)
|
||||
|
||||
_, _, myPayoutAddr := keyPubAddr()
|
||||
initMsg := InitMsg{
|
||||
Verifier: fred,
|
||||
Beneficiary: fred,
|
||||
}
|
||||
initMsgBz, err := json.Marshal(initMsg)
|
||||
require.NoError(t, err)
|
||||
|
||||
ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1)
|
||||
contractAddr, err := keeper.Instantiate(ctx, originalContractID, creator, fred, initMsgBz, "demo contract", deposit)
|
||||
require.NoError(t, err)
|
||||
|
||||
migMsg := struct {
|
||||
Payout sdk.AccAddress `json:"payout"`
|
||||
}{Payout: myPayoutAddr}
|
||||
migMsgBz, err := json.Marshal(migMsg)
|
||||
require.NoError(t, err)
|
||||
ctx = ctx.WithEventManager(sdk.NewEventManager()).WithBlockHeight(ctx.BlockHeight() + 1)
|
||||
res, err := keeper.Migrate(ctx, contractAddr, fred, burnerContractID, migMsgBz)
|
||||
require.NoError(t, err)
|
||||
dataBz, err := base64.StdEncoding.DecodeString(string(res.Data))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "burnt", string(dataBz))
|
||||
assert.Equal(t, "", res.Log)
|
||||
type dict map[string]interface{}
|
||||
expEvents := []dict{
|
||||
{
|
||||
"Type": "wasm",
|
||||
"Attr": []dict{
|
||||
{"contract_address": contractAddr},
|
||||
{"action": "burn"},
|
||||
{"payout": myPayoutAddr},
|
||||
},
|
||||
},
|
||||
{
|
||||
"Type": "transfer",
|
||||
"Attr": []dict{
|
||||
{"recipient": myPayoutAddr},
|
||||
{"sender": contractAddr},
|
||||
{"amount": "100000denom"},
|
||||
},
|
||||
},
|
||||
{
|
||||
"Type": "message",
|
||||
"Attr": []dict{
|
||||
{"sender": contractAddr},
|
||||
},
|
||||
},
|
||||
{
|
||||
"Type": "message",
|
||||
"Attr": []dict{
|
||||
{"module": "bank"},
|
||||
},
|
||||
},
|
||||
}
|
||||
expJsonEvts := string(mustMarshal(t, expEvents))
|
||||
assert.JSONEq(t, expJsonEvts, prettyEvents(t, ctx.EventManager().Events()))
|
||||
|
||||
// all persistent data cleared
|
||||
m := keeper.QueryRaw(ctx, contractAddr, []byte("config"))
|
||||
require.Len(t, m, 0)
|
||||
|
||||
// and all deposit tokens sent to myPayoutAddr
|
||||
balance := accKeeper.GetAccount(ctx, myPayoutAddr).GetCoins()
|
||||
assert.Equal(t, deposit, balance)
|
||||
}
|
||||
|
||||
func prettyEvents(t *testing.T, events sdk.Events) string {
|
||||
t.Helper()
|
||||
type prettyEvent struct {
|
||||
Type string
|
||||
Attr []map[string]string
|
||||
}
|
||||
|
||||
r := make([]prettyEvent, len(events))
|
||||
for i, e := range events {
|
||||
attr := make([]map[string]string, len(e.Attributes))
|
||||
for j, a := range e.Attributes {
|
||||
attr[j] = map[string]string{string(a.Key): string(a.Value)}
|
||||
}
|
||||
r[i] = prettyEvent{Type: e.Type, Attr: attr}
|
||||
}
|
||||
return string(mustMarshal(t, r))
|
||||
}
|
||||
|
||||
func mustMarshal(t *testing.T, r interface{}) []byte {
|
||||
t.Helper()
|
||||
bz, err := json.Marshal(r)
|
||||
require.NoError(t, err)
|
||||
return bz
|
||||
}
|
||||
|
||||
func TestUpdateContractAdmin(t *testing.T) {
|
||||
tempDir, err := ioutil.TempDir("", "wasm")
|
||||
require.NoError(t, err)
|
||||
|
||||
Reference in New Issue
Block a user