Skip to content

Commit 2acf839

Browse files
deelawnharry-hov
andauthored
feat: new txtar command adduser (#1471)
<!-- please provide a detailed description of the changes made in this pull request. --> As I've started to think about creating txtar tests for contracts with logic depending on contract calls from multiple users and durations since previous contract calls, the addition of the `adduser` command seems to be necessary. <details><summary>Contributors' checklist...</summary> - [x] Added new tests, or not needed, or not feasible - [x] Provided an example (e.g. screenshot) to aid review or the PR is self-explanatory - [x] Updated the official documentation or not needed - [x] No breaking changes were made, or a `BREAKING CHANGE: xxx` message was included in the description - [ ] Added references to related issues and PRs - [ ] Provided any useful hints for running manual tests - [ ] Added new benchmarks to [generated graphs](https://gnoland.github.io/benchmarks), if any. More info [here](https://github.com/gnolang/gno/blob/master/.benchmarks/README.md). </details> --------- Co-authored-by: Hariom Verma <hariom18599@gmail.com>
1 parent 26ad8dd commit 2acf839

File tree

3 files changed

+139
-4
lines changed

3 files changed

+139
-4
lines changed

gno.land/pkg/integration/doc.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@
1717
// - `--remote`, `--insecure-password-stdin`, and `--home` flags are set automatically to
1818
// communicate with the gnoland node.
1919
//
20+
// 3. `adduser`:
21+
// - Creates a new user in the default keybase directory.
22+
// - Must be run before `gnoland start`.
23+
//
2024
// Logging:
2125
//
2226
// Gnoland logs aren't forwarded to stdout to avoid overwhelming the tests with too much
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
adduser test8
2+
3+
## start a new node
4+
gnoland start
5+
6+
## add bar.gno package located in $WORK directory as gno.land/r/foobar/bar
7+
gnokey maketx addpkg -pkgdir $WORK/bar -pkgpath gno.land/r/foobar/bar -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test8
8+
9+
## execute Render
10+
gnokey maketx run -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test8 $WORK/script/script.gno
11+
12+
## compare render
13+
stdout 'main: --- hello from foo ---'
14+
stdout 'OK!'
15+
stdout 'GAS WANTED: 200000'
16+
stdout 'GAS USED: '
17+
18+
# should fail if user is added after node is started
19+
! adduser test5
20+
stderr '"adduser" error: adduser must be used before starting node'
21+
22+
-- bar/bar.gno --
23+
package bar
24+
25+
func Render(path string) string {
26+
return "hello from foo"
27+
}
28+
29+
-- script/script.gno --
30+
package main
31+
import "gno.land/r/foobar/bar"
32+
func main() {
33+
println("main: ---", bar.Render(""), "---")
34+
}

gno.land/pkg/integration/testing_integration.go

Lines changed: 101 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package integration
22

33
import (
44
"context"
5+
"errors"
56
"fmt"
67
"hash/crc32"
78
"os"
@@ -10,15 +11,20 @@ import (
1011
"strings"
1112
"testing"
1213

14+
"github.com/gnolang/gno/gno.land/pkg/gnoland"
1315
"github.com/gnolang/gno/gnovm/pkg/gnoenv"
1416
"github.com/gnolang/gno/tm2/pkg/bft/node"
1517
"github.com/gnolang/gno/tm2/pkg/commands"
18+
"github.com/gnolang/gno/tm2/pkg/crypto/bip39"
1619
"github.com/gnolang/gno/tm2/pkg/crypto/keys"
1720
"github.com/gnolang/gno/tm2/pkg/crypto/keys/client"
1821
"github.com/gnolang/gno/tm2/pkg/log"
22+
"github.com/gnolang/gno/tm2/pkg/std"
1923
"github.com/rogpeppe/go-internal/testscript"
2024
)
2125

26+
const numTestAccounts int = 4
27+
2228
type tSeqShim struct{ *testing.T }
2329

2430
// noop Parallel method allow us to run test sequentially
@@ -71,6 +77,10 @@ func setupGnolandTestScript(t *testing.T, txtarDir string) testscript.Params {
7177
// Testscripts run concurrently by default, so we need to be prepared for that.
7278
nodes := map[string]*testNode{}
7379

80+
// Track new user balances added via the `adduser` command. These are added to the genesis
81+
// state when the node is started.
82+
var newUserBalances []gnoland.Balance
83+
7484
updateScripts, _ := strconv.ParseBool(os.Getenv("UPDATE_SCRIPTS"))
7585
persistWorkDir, _ := strconv.ParseBool(os.Getenv("TESTWORK"))
7686
return testscript.Params{
@@ -107,12 +117,25 @@ func setupGnolandTestScript(t *testing.T, txtarDir string) testscript.Params {
107117
env.Values["_logger"] = logger
108118
}
109119

110-
// Setup "test1" default account
120+
// test1 must be created outside of the loop below because it is already included in genesis so
121+
// attempting to recreate results in it getting overwritten and breaking existing tests that
122+
// rely on its address being static.
111123
kb.CreateAccount(DefaultAccount_Name, DefaultAccount_Seed, "", "", 0, 0)
112-
113124
env.Setenv("USER_SEED_"+DefaultAccount_Name, DefaultAccount_Seed)
114125
env.Setenv("USER_ADDR_"+DefaultAccount_Name, DefaultAccount_Address)
115126

127+
// Create test accounts starting from test2.
128+
for i := 1; i < numTestAccounts; i++ {
129+
accountName := "test" + strconv.Itoa(i+1)
130+
131+
balance, err := createAccount(env, kb, accountName)
132+
if err != nil {
133+
return fmt.Errorf("error creating account %s: %w", accountName, err)
134+
}
135+
136+
newUserBalances = append(newUserBalances, balance)
137+
}
138+
116139
env.Setenv("GNOROOT", gnoRootDir)
117140
env.Setenv("GNOHOME", gnoHomeDir)
118141

@@ -126,15 +149,15 @@ func setupGnolandTestScript(t *testing.T, txtarDir string) testscript.Params {
126149
}
127150

128151
logger := ts.Value("_logger").(log.Logger) // grab logger
129-
sid := ts.Getenv("SID") // grab session id
152+
sid := getNodeSID(ts) // grab session id
130153

131154
var cmd string
132155
cmd, args = args[0], args[1:]
133156

134157
var err error
135158
switch cmd {
136159
case "start":
137-
if _, ok := nodes[sid]; ok {
160+
if nodeIsRunning(nodes, sid) {
138161
err = fmt.Errorf("node already started")
139162
break
140163
}
@@ -144,6 +167,16 @@ func setupGnolandTestScript(t *testing.T, txtarDir string) testscript.Params {
144167

145168
// Generate config and node
146169
cfg, _ := TestingNodeConfig(t, gnoRootDir)
170+
171+
// Add balances for users added via the `adduser` command.
172+
genesis := cfg.Genesis
173+
genesisState := gnoland.GnoGenesisState{
174+
Balances: genesis.AppState.(gnoland.GnoGenesisState).Balances,
175+
Txs: genesis.AppState.(gnoland.GnoGenesisState).Txs,
176+
}
177+
genesisState.Balances = append(genesisState.Balances, newUserBalances...)
178+
cfg.Genesis.AppState = genesisState
179+
147180
n, remoteAddr := TestingInMemoryNode(t, logger, cfg)
148181

149182
// Register cleanup
@@ -211,10 +244,42 @@ func setupGnolandTestScript(t *testing.T, txtarDir string) testscript.Params {
211244

212245
tsValidateError(ts, "gnokey", neg, err)
213246
},
247+
// adduser commands must be executed before starting the node; it errors out otherwise.
248+
"adduser": func(ts *testscript.TestScript, neg bool, args []string) {
249+
if nodeIsRunning(nodes, getNodeSID(ts)) {
250+
tsValidateError(ts, "adduser", neg, errors.New("adduser must be used before starting node"))
251+
return
252+
}
253+
254+
if len(args) == 0 {
255+
ts.Fatalf("new user name required")
256+
}
257+
258+
kb, err := keys.NewKeyBaseFromDir(gnoHomeDir)
259+
if err != nil {
260+
ts.Fatalf("unable to get keybase")
261+
}
262+
263+
balance, err := createAccount(ts, kb, args[0])
264+
if err != nil {
265+
ts.Fatalf("error creating account %s: %s", args[0], err)
266+
}
267+
268+
newUserBalances = append(newUserBalances, balance)
269+
},
214270
},
215271
}
216272
}
217273

274+
func getNodeSID(ts *testscript.TestScript) string {
275+
return ts.Getenv("SID")
276+
}
277+
278+
func nodeIsRunning(nodes map[string]*testNode, sid string) bool {
279+
_, ok := nodes[sid]
280+
return ok
281+
}
282+
218283
func getTestingLogger(env *testscript.Env, logname string) (log.Logger, error) {
219284
var path string
220285

@@ -273,3 +338,35 @@ func tsValidateError(ts *testscript.TestScript, cmd string, neg bool, err error)
273338
}
274339
}
275340
}
341+
342+
type envSetter interface {
343+
Setenv(key, value string)
344+
}
345+
346+
// createAccount creates a new account with the given name and adds it to the keybase.
347+
func createAccount(env envSetter, kb keys.Keybase, accountName string) (gnoland.Balance, error) {
348+
var balance gnoland.Balance
349+
entropy, err := bip39.NewEntropy(256)
350+
if err != nil {
351+
return balance, fmt.Errorf("error creating entropy: %w", err)
352+
}
353+
354+
mnemonic, err := bip39.NewMnemonic(entropy)
355+
if err != nil {
356+
return balance, fmt.Errorf("error generating mnemonic: %w", err)
357+
}
358+
359+
var keyInfo keys.Info
360+
if keyInfo, err = kb.CreateAccount(accountName, mnemonic, "", "", 0, 0); err != nil {
361+
return balance, fmt.Errorf("unable to create account: %w", err)
362+
}
363+
364+
address := keyInfo.GetAddress()
365+
env.Setenv("USER_SEED_"+accountName, mnemonic)
366+
env.Setenv("USER_ADDR_"+accountName, address.String())
367+
368+
return gnoland.Balance{
369+
Address: address,
370+
Amount: std.Coins{std.NewCoin("ugnot", 1000000000000000000)},
371+
}, nil
372+
}

0 commit comments

Comments
 (0)