Skip to content

Commit 8fbc05e

Browse files
test(mcms): optimise container startups (#112)
The original tests which include spinning up solana containers take up a good chunk of time ``` github.com/smartcontractkit/cld-changesets/legacy/mcms/changesets 112.753s coverage: 67.1% of statements github.com/smartcontractkit/cld-changesets/mcms/changesets/set-config 133.902s coverage: 91.2% of statements github.com/smartcontractkit/cld-changesets/mcms/solana/changesets/fund-mcm-pdas 60.253s coverage: 89.7% of statements ``` I have since optimised it by doing: - reduce container startups by having multple tests share the same container if there are no conflicts - reduce duplicate tests in the sequence or operation tests when the same flow has been covered by the integration test at changeset_test.go After optimisation ``` ❯ go test ./mcms/... -count=1 ? github.com/smartcontractkit/cld-changesets/mcms/changesets [no test files] ? github.com/smartcontractkit/cld-changesets/mcms/changesets/deploy [no test files] ok github.com/smartcontractkit/cld-changesets/mcms/changesets/deploy-custom-topology 1.897s ok github.com/smartcontractkit/cld-changesets/mcms/changesets/firedrill 1.154s ? github.com/smartcontractkit/cld-changesets/mcms/changesets/firedrill/all [no test files] ok github.com/smartcontractkit/cld-changesets/mcms/changesets/grant-role 2.702s ? github.com/smartcontractkit/cld-changesets/mcms/changesets/grant-role/all [no test files] ok github.com/smartcontractkit/cld-changesets/mcms/changesets/set-config 2.160s ? github.com/smartcontractkit/cld-changesets/mcms/changesets/set-config/all [no test files] ok github.com/smartcontractkit/cld-changesets/mcms/changesets/transfer-to-timelock 2.395s ? github.com/smartcontractkit/cld-changesets/mcms/changesets/transfer-to-timelock/all [no test files] ok github.com/smartcontractkit/cld-changesets/mcms/evm/deploy 3.165s ? github.com/smartcontractkit/cld-changesets/mcms/evm/deploy/v1_0_0/operations/call_proxy [no test files] ? github.com/smartcontractkit/cld-changesets/mcms/evm/deploy/v1_0_0/operations/many_chain_multi_sig [no test files] ? github.com/smartcontractkit/cld-changesets/mcms/evm/deploy/v1_0_0/operations/rbac_timelock [no test files] ok github.com/smartcontractkit/cld-changesets/mcms/evm/deploy-custom-topology 1.281s ok github.com/smartcontractkit/cld-changesets/mcms/evm/firedrill 2.177s ok github.com/smartcontractkit/cld-changesets/mcms/evm/grant-role 6.359s ok github.com/smartcontractkit/cld-changesets/mcms/evm/internal/gasboost 3.336s ok github.com/smartcontractkit/cld-changesets/mcms/evm/readers 10.430s ok github.com/smartcontractkit/cld-changesets/mcms/evm/set-config 7.381s ok github.com/smartcontractkit/cld-changesets/mcms/evm/transfer-to-timelock 4.490s ? github.com/smartcontractkit/cld-changesets/mcms/evm/transfer-to-timelock/v1_0_0/operations/burn_mint_erc677 [no test files] ok github.com/smartcontractkit/cld-changesets/mcms/operations 10.392s ? github.com/smartcontractkit/cld-changesets/mcms/solana/changesets [no test files] ok github.com/smartcontractkit/cld-changesets/mcms/solana/changesets/fund-mcm-pdas 37.022s ok github.com/smartcontractkit/cld-changesets/mcms/solana/deploy 56.093s ok github.com/smartcontractkit/cld-changesets/mcms/solana/firedrill 49.339s ok github.com/smartcontractkit/cld-changesets/mcms/solana/readers 11.145s ok github.com/smartcontractkit/cld-changesets/mcms/solana/set-config 55.496s ```
1 parent 06e4c39 commit 8fbc05e

11 files changed

Lines changed: 931 additions & 1322 deletions

File tree

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Package solanatest provides test utilities for Solana MCMS integration tests.
2+
package solanatest
3+
4+
import (
5+
"testing"
6+
7+
"github.com/stretchr/testify/require"
8+
9+
"github.com/smartcontractkit/chainlink-deployments-framework/datastore"
10+
mcmscontracts "github.com/smartcontractkit/chainlink-deployments-framework/engine/cld/contracts/mcms"
11+
12+
"github.com/smartcontractkit/cld-changesets/internal/semvers"
13+
"github.com/smartcontractkit/cld-changesets/legacy/pkg/family/solana/solutils"
14+
)
15+
16+
// NewDataStoreWithMCMSPrograms seeds a datastore with canonical MCMS program IDs.
17+
// The test validator preloads the same programs via WithSolanaContainer; program
18+
// deploy is skipped because artifacts lack -keypair.json files required by
19+
// solana program deploy for fixed program IDs.
20+
func NewDataStoreWithMCMSPrograms(t *testing.T, selector uint64) datastore.DataStore {
21+
t.Helper()
22+
23+
v := semvers.V1_0_0
24+
ds := datastore.NewMemoryDataStore()
25+
for _, entry := range []struct {
26+
addr string
27+
ct datastore.ContractType
28+
}{
29+
{solutils.GetProgramID(solutils.ProgAccessController), datastore.ContractType(mcmscontracts.AccessControllerProgram)},
30+
{solutils.GetProgramID(solutils.ProgMCM), datastore.ContractType(mcmscontracts.ManyChainMultisigProgram)},
31+
{solutils.GetProgramID(solutils.ProgTimelock), datastore.ContractType(mcmscontracts.RBACTimelockProgram)},
32+
} {
33+
require.NoError(t, ds.Addresses().Add(datastore.AddressRef{
34+
ChainSelector: selector,
35+
Address: entry.addr,
36+
Type: entry.ct,
37+
Version: &v,
38+
}))
39+
}
40+
41+
return ds.Seal()
42+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package solanatest
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/require"
7+
8+
mcmscontracts "github.com/smartcontractkit/chainlink-deployments-framework/engine/cld/contracts/mcms"
9+
mcmslib "github.com/smartcontractkit/mcms"
10+
mcmssolana "github.com/smartcontractkit/mcms/sdk/solana"
11+
mcmstypes "github.com/smartcontractkit/mcms/types"
12+
13+
solstate "github.com/smartcontractkit/cld-changesets/legacy/pkg/family/solana"
14+
)
15+
16+
// AssertMCMSSetConfigProposal verifies structure of a Solana set-config MCMS timelock proposal
17+
// without executing it on-chain. Batch ops may appear as one merged operation per chain (new
18+
// changeset output builder) or one operation per MCM role (legacy proposeutils path).
19+
func AssertMCMSSetConfigProposal(
20+
t *testing.T,
21+
selector uint64,
22+
mcmsState *solstate.MCMSWithTimelockState,
23+
proposal mcmslib.TimelockProposal,
24+
wantAction mcmstypes.TimelockAction,
25+
wantDelay mcmstypes.Duration,
26+
wantDescription string,
27+
) {
28+
t.Helper()
29+
30+
require.Equal(t, wantAction, proposal.Action)
31+
require.Equal(t, wantDelay, proposal.Delay)
32+
require.Equal(t, wantDescription, proposal.Description)
33+
34+
timelockAddr := mcmssolana.ContractAddress(mcmsState.TimelockProgram, mcmssolana.PDASeed(mcmsState.TimelockSeed))
35+
require.Equal(t, timelockAddr, proposal.TimelockAddresses[mcmstypes.ChainSelector(selector)])
36+
37+
wantContractTypes := []string{
38+
string(mcmscontracts.ProposerManyChainMultisig),
39+
string(mcmscontracts.CancellerManyChainMultisig),
40+
string(mcmscontracts.BypasserManyChainMultisig),
41+
}
42+
require.NotEmpty(t, proposal.Operations)
43+
44+
mcmProgram := mcmsState.McmProgram.String()
45+
gotContractTypes := make([]string, 0, len(wantContractTypes))
46+
47+
for _, op := range proposal.Operations {
48+
require.Equal(t, mcmstypes.ChainSelector(selector), op.ChainSelector)
49+
require.NotEmpty(t, op.Transactions)
50+
51+
for _, tx := range op.Transactions {
52+
require.Equal(t, mcmProgram, tx.To)
53+
require.NotEmpty(t, tx.Data)
54+
require.NotEmpty(t, tx.ContractType)
55+
gotContractTypes = append(gotContractTypes, tx.ContractType)
56+
}
57+
}
58+
59+
require.ElementsMatch(t, wantContractTypes, uniqueStrings(gotContractTypes))
60+
for _, contractType := range wantContractTypes {
61+
require.Contains(t, gotContractTypes, contractType)
62+
}
63+
}
64+
65+
func uniqueStrings(values []string) []string {
66+
seen := make(map[string]struct{}, len(values))
67+
unique := make([]string, 0, len(values))
68+
for _, v := range values {
69+
if _, ok := seen[v]; ok {
70+
continue
71+
}
72+
seen[v] = struct{}{}
73+
unique = append(unique, v)
74+
}
75+
76+
return unique
77+
}

legacy/mcms/changesets/set_config_mcms_test.go

Lines changed: 23 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package changesets
33
import (
44
"crypto/ecdsa"
55
"testing"
6-
"time"
76

87
"github.com/ethereum/go-ethereum/common"
98
"github.com/ethereum/go-ethereum/crypto"
@@ -22,9 +21,9 @@ import (
2221
"github.com/smartcontractkit/mcms/sdk/solana"
2322
mcmstypes "github.com/smartcontractkit/mcms/types"
2423

24+
"github.com/smartcontractkit/cld-changesets/internal/testutil/solanatest"
2525
evmstate "github.com/smartcontractkit/cld-changesets/legacy/pkg/family/evm"
2626
solstate "github.com/smartcontractkit/cld-changesets/legacy/pkg/family/solana"
27-
"github.com/smartcontractkit/cld-changesets/legacy/pkg/family/solana/changesets"
2827
soltestutils "github.com/smartcontractkit/cld-changesets/legacy/pkg/family/solana/testutils"
2928
linkchangesets "github.com/smartcontractkit/cld-changesets/tokens/link/changesets"
3029
)
@@ -202,8 +201,7 @@ func TestSetConfigMCMSV2Solana(t *testing.T) {
202201
inspector := solana.NewInspector(chain.Client)
203202

204203
// Create some signers to set into the config
205-
signer1Key, signer1Addr := createSolSigner(t)
206-
_, signer2Addr := createSolSigner(t)
204+
_, signer1Addr := createSolSigner(t)
207205

208206
newCfgProposer := cldftesthelpers.SingleGroupMCMS(t)
209207
newCfgProposer.Signers = append(newCfgProposer.Signers, signer1Addr)
@@ -233,44 +231,30 @@ func TestSetConfigMCMSV2Solana(t *testing.T) {
233231
assertSolConfigEquals(t, inspector, mcmsState.McmProgram, mcmsState.CancellerMcmSeed, newCfgCanceller)
234232
})
235233

236-
t.Run("MCMS enabled", func(t *testing.T) { //nolint:paralleltest
237-
// Now we transfer the MCMS contracts to the timelock for testing setConfig on MCMS owned contracts
238-
err = rt.Exec(
239-
runtime.ChangesetTask(changesets.TransferMCMSToTimelockSolana{}, changesets.TransferMCMSToTimelockSolanaConfig{
240-
Chains: []uint64{selector},
241-
MCMSCfg: cldfproposalutils.TimelockConfig{MinDelay: time.Second * 1},
242-
}),
243-
// We must sign with an additional signer since we changed the config quorum previously.
244-
runtime.SignAndExecuteProposalsTask([]*ecdsa.PrivateKey{cldftesthelpers.TestXXXMCMSSigner, signer1Key}),
245-
)
246-
require.NoError(t, err)
247-
248-
// Update the configs with yet another additional signer
249-
newCfgProposer.Signers = append(newCfgProposer.Signers, signer2Addr)
250-
newCfgProposer.Quorum = 3
251-
newCfgBypasser.Signers = append(newCfgBypasser.Signers, signer2Addr)
252-
newCfgBypasser.Quorum = 3
253-
254-
err = rt.Exec(
255-
runtime.ChangesetTask(cldf.CreateLegacyChangeSet(SetConfigMCMSV2), MCMSConfigV2{
256-
ProposalConfig: &cldfproposalutils.TimelockConfig{
257-
MinDelay: time.Second * 1,
258-
},
259-
ConfigsPerChain: map[uint64]ConfigPerRoleV2{
260-
selector: {
261-
Proposer: &newCfgProposer,
262-
Canceller: &newCfgCanceller,
263-
Bypasser: &newCfgBypasser,
264-
},
234+
t.Run("builds MCMS proposal without execute", func(t *testing.T) { //nolint:paralleltest
235+
cfg := cldftesthelpers.SingleGroupMCMS(t)
236+
taskID, err := runtime.ExecChangeset(rt, cldf.CreateLegacyChangeSet(SetConfigMCMSV2), MCMSConfigV2{
237+
ProposalConfig: &cldfproposalutils.TimelockConfig{
238+
MinDelay: 0,
239+
MCMSAction: mcmstypes.TimelockActionBypass,
240+
},
241+
ConfigsPerChain: map[uint64]ConfigPerRoleV2{
242+
selector: {
243+
Proposer: &cfg,
244+
Canceller: &cfg,
245+
Bypasser: &cfg,
265246
},
266-
}),
267-
runtime.SignAndExecuteProposalsTask([]*ecdsa.PrivateKey{cldftesthelpers.TestXXXMCMSSigner, signer1Key}),
268-
)
247+
},
248+
})
269249
require.NoError(t, err)
270250

271-
assertSolConfigEquals(t, inspector, mcmsState.McmProgram, mcmsState.ProposerMcmSeed, newCfgProposer)
272-
assertSolConfigEquals(t, inspector, mcmsState.McmProgram, mcmsState.BypasserMcmSeed, newCfgBypasser)
273-
assertSolConfigEquals(t, inspector, mcmsState.McmProgram, mcmsState.CancellerMcmSeed, newCfgCanceller)
251+
output, ok := rt.State().Outputs[taskID]
252+
require.True(t, ok)
253+
require.Len(t, output.MCMSTimelockProposals, 1)
254+
solanatest.AssertMCMSSetConfigProposal(
255+
t, selector, mcmsState, output.MCMSTimelockProposals[0],
256+
mcmstypes.TimelockActionBypass, mcmstypes.NewDuration(0), "Set config proposal",
257+
)
274258
})
275259
}
276260

0 commit comments

Comments
 (0)