Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
ignite
GitHub Repository: ignite/cli
Path: blob/main/integration/simulate.go
1007 views
1
package envtest
2
3
import (
4
"context"
5
"strings"
6
7
"github.com/buger/jsonparser"
8
"github.com/stretchr/testify/require"
9
10
sdktypes "github.com/cosmos/cosmos-sdk/types"
11
12
"github.com/ignite/cli/v29/ignite/pkg/multiformatname"
13
"github.com/ignite/cli/v29/ignite/templates/field"
14
"github.com/ignite/cli/v29/ignite/templates/field/datatype"
15
)
16
17
// testValue determines the default test value for a given datatype.
18
func testValue(name datatype.Name) string {
19
dt, _ := datatype.IsSupportedType(name)
20
return dt.DefaultTestValue
21
}
22
23
// txArgs generates transaction arguments as strings from a given set of fields.
24
func txArgs(fields field.Fields) []string {
25
args := make([]string, len(fields))
26
for i, f := range fields {
27
args[i] = testValue(f.DatatypeName)
28
}
29
return args
30
}
31
32
// assertJSONData verifies that the JSON data contains expected values for the given fields.
33
func (a *App) assertJSONData(data []byte, msgName string, fields field.Fields) {
34
for _, f := range fields {
35
dt := testValue(f.DatatypeName)
36
value, _, _, err := jsonparser.Get(data, msgName, f.Name.Snake)
37
require.NoError(a.env.T(), err)
38
if dt == "{}" {
39
continue
40
}
41
v := string(value)
42
switch {
43
case f.DatatypeName == datatype.Coin:
44
45
c, err := sdktypes.ParseCoinNormalized(dt)
46
require.NoError(a.env.T(), err)
47
amount, err := jsonparser.GetString(value, "amount")
48
require.NoError(a.env.T(), err)
49
require.EqualValues(a.env.T(), amount, c.Amount.String())
50
denom, err := jsonparser.GetString(value, "denom")
51
require.NoError(a.env.T(), err)
52
require.EqualValues(a.env.T(), denom, c.Denom)
53
54
case f.DatatypeName == datatype.Coins || f.DatatypeName == datatype.CoinSliceAlias:
55
56
c, err := sdktypes.ParseCoinsNormalized(dt)
57
require.NoError(a.env.T(), err)
58
cJSON, err := c.MarshalJSON()
59
require.NoError(a.env.T(), err)
60
dt = string(cJSON)
61
require.JSONEq(a.env.T(), dt, v)
62
63
case f.DatatypeName == datatype.DecCoin || f.DatatypeName == datatype.DecCoins || f.DatatypeName == datatype.DecCoinSliceAlias:
64
65
c, err := sdktypes.ParseCoinNormalized(dt)
66
require.NoError(a.env.T(), err)
67
// TODO find a better way to compare DecCoins as they have a different result pattern from CLI and Query
68
require.Contains(a.env.T(), v, c.Denom)
69
require.Contains(a.env.T(), v, c.Amount.String())
70
71
case f.IsSlice():
72
73
var slice []string
74
_, err = jsonparser.ArrayEach(value, func(value []byte, _ jsonparser.ValueType, _ int, _ error) {
75
slice = append(slice, string(value))
76
})
77
require.NoError(a.env.T(), err)
78
v = strings.Join(slice, ",")
79
require.EqualValues(a.env.T(), dt, v)
80
81
default:
82
require.EqualValues(a.env.T(), dt, v)
83
}
84
}
85
}
86
87
// assertJSONList verifies that a JSON array contains expected values for the given fields.
88
func (a *App) assertJSONList(data []byte, msgName string, fields field.Fields) {
89
value, _, _, err := jsonparser.Get(data, msgName)
90
require.NoError(a.env.t, err)
91
92
a.assertJSONData(value, "[0]", fields)
93
}
94
95
// createTx sends a transaction to create a resource and verifies the response from the chain.
96
func (a *App) createTx(
97
servers Hosts,
98
module string,
99
name multiformatname.Name,
100
args ...string,
101
) {
102
// Submit the transaction and verify it was accepted
103
txResponse := a.CLITx(
104
servers.RPC,
105
module,
106
"create-"+name.Kebab,
107
args...,
108
)
109
require.Equal(a.env.T(), 0, txResponse.Code,
110
"tx failed code=%d log=%s", txResponse.Code, txResponse.RawLog)
111
112
// Query the transaction using its hash
113
tx := a.CLIQueryTx(
114
servers.RPC,
115
txResponse.TxHash,
116
)
117
require.Equal(a.env.T(), 0, tx.Code,
118
"tx failed code=%d log=%s", txResponse.Code, txResponse.RawLog)
119
}
120
121
// RunChainAndSimulateTxs starts the blockchain network and runs transaction simulations.
122
func (a *App) RunChainAndSimulateTxs(servers Hosts) {
123
ctx, cancel := context.WithCancel(a.env.ctx)
124
defer cancel()
125
126
// Start serving the blockchain in a separate goroutine
127
go func() {
128
a.MustServe(ctx)
129
}()
130
131
// Wait until the chain is up and running
132
a.WaitChainUp(ctx, servers.API)
133
134
// Run the transaction simulations
135
a.RunSimulationTxs(ctx, servers)
136
}
137
138
// RunSimulationTxs runs different types of transactions for modules and queries the chain.
139
func (a *App) RunSimulationTxs(ctx context.Context, servers Hosts) {
140
for _, s := range a.scaffolded {
141
module := s.module
142
if module == "" {
143
module = a.name
144
}
145
name, err := multiformatname.NewName(s.name)
146
require.NoError(a.env.t, err)
147
148
// Handle different types of scaffolds
149
switch s.typeName {
150
case "module":
151
// No transactions for "module" type
152
case "list":
153
a.SendListTxsAndQueryFirst(ctx, servers, module, name, s.fields)
154
case "map":
155
a.SendMapTxsAndQuery(ctx, servers, module, name, s.fields, s.index)
156
case "single":
157
a.SendSingleTxsAndQuery(ctx, servers, module, name, s.fields)
158
case "params":
159
case "message":
160
case "query":
161
case "configs":
162
case "type":
163
case "packet":
164
}
165
}
166
}
167
168
// SendSingleTxsAndQuery submits a single transaction and queries the result from both CLI and API.
169
func (a *App) SendSingleTxsAndQuery(
170
ctx context.Context,
171
servers Hosts,
172
module string,
173
name multiformatname.Name,
174
fields field.Fields,
175
) {
176
// Generate transaction arguments and submit the transaction
177
args := txArgs(fields)
178
a.createTx(servers, module, name, args...)
179
180
// Query the state via CLI
181
queryResponse := a.CLIQuery(
182
servers.RPC,
183
module,
184
"get-"+name.Kebab,
185
)
186
a.assertJSONData(queryResponse, name.Snake, fields)
187
188
// Query the state via API
189
apiResponse := a.APIQuery(
190
ctx,
191
servers.API,
192
a.namespace,
193
module,
194
name.Snake,
195
)
196
a.assertJSONData(apiResponse, name.Snake, fields)
197
198
// Ensure CLI and API responses match
199
require.JSONEq(a.env.t, string(queryResponse), string(apiResponse))
200
}
201
202
// SendListTxsAndQueryFirst sends a list transaction and queries the first element using both CLI and API.
203
func (a *App) SendListTxsAndQueryFirst(
204
ctx context.Context,
205
servers Hosts,
206
module string,
207
name multiformatname.Name,
208
fields field.Fields,
209
) {
210
a.SendTxsAndQuery(ctx, servers, module, name, fields, "0")
211
}
212
213
// SendMapTxsAndQuery sends a map transaction and queries the element using both CLI and API.
214
func (a *App) SendMapTxsAndQuery(
215
ctx context.Context,
216
servers Hosts,
217
module string,
218
name multiformatname.Name,
219
fields field.Fields,
220
index field.Field,
221
) {
222
a.SendTxsAndQuery(
223
ctx,
224
servers,
225
module,
226
name,
227
append(field.Fields{index}, fields...),
228
testValue(index.DatatypeName),
229
)
230
}
231
232
// SendTxsAndQuery sends a transaction and queries the element using both CLI and API.
233
func (a *App) SendTxsAndQuery(
234
ctx context.Context,
235
servers Hosts,
236
module string,
237
name multiformatname.Name,
238
fields field.Fields,
239
index string,
240
) {
241
// Generate transaction arguments and submit the transaction
242
args := txArgs(fields)
243
a.createTx(servers, module, name, args...)
244
245
// Query the chain for the first element via CLI
246
queryResponse := a.CLIQuery(
247
servers.RPC,
248
module,
249
"get-"+name.Kebab,
250
index,
251
)
252
a.assertJSONData(queryResponse, name.Snake, fields)
253
254
// Query the chain for the first element via API
255
apiResponse := a.APIQuery(
256
ctx,
257
servers.API,
258
a.namespace,
259
module,
260
name.Snake,
261
index,
262
)
263
a.assertJSONData(apiResponse, name.Snake, fields)
264
265
// Query the full list via CLI
266
queryListResponse := a.CLIQuery(
267
servers.RPC,
268
module,
269
"list-"+name.Kebab,
270
)
271
a.assertJSONList(queryListResponse, name.Snake, fields)
272
273
// Query the full list via API
274
apiListResponse := a.APIQuery(
275
ctx,
276
servers.API,
277
a.namespace,
278
module,
279
name.Snake,
280
)
281
a.assertJSONList(apiListResponse, name.Snake, fields)
282
}
283
284