5
5
package e2e
6
6
7
7
import (
8
- "errors"
9
- "fmt"
10
- "os"
11
- "sync"
12
8
"time"
13
9
14
10
ginkgo "github.com/onsi/ginkgo/v2"
15
- "github.com/onsi/gomega "
11
+ "github.com/stretchr/testify/require "
16
12
17
- "github.com/ava-labs/avalanchego/ids"
18
- "github.com/ava-labs/avalanchego/tests"
13
+ "github.com/ava-labs/avalanchego/tests/fixture"
19
14
"github.com/ava-labs/avalanchego/tests/fixture/testnet"
20
15
"github.com/ava-labs/avalanchego/tests/fixture/testnet/local"
21
16
"github.com/ava-labs/avalanchego/utils/crypto/secp256k1"
22
- "github.com/ava-labs/avalanchego/vms/secp256k1fx"
23
17
)
24
18
25
19
const (
@@ -29,256 +23,26 @@ const (
29
23
// Defines default tx confirmation timeout.
30
24
// Enough for test/custom networks.
31
25
DefaultConfirmTxTimeout = 20 * time .Second
32
-
33
- DefaultShutdownTimeout = 2 * time .Minute
34
26
)
35
27
36
- // Env is the global struct containing all we need to test
37
- var (
38
- Env = & TestEnvironment {}
39
-
40
- errNoKeyFile = errors .New ("test keys file not provided" )
41
- )
28
+ // Env is used to access shared test fixture. Intended to be
29
+ // initialized by SynchronizedBeforeSuite.
30
+ var Env TestEnvironment
42
31
43
32
type TestEnvironment struct {
44
- avalancheGoExecPath string
45
- isPersistentNetwork bool
46
-
47
- setupCalled bool
48
-
49
- urisMu sync.RWMutex
50
- uris []string
51
-
52
- networkDir string
53
-
54
- // isNetworkPristine is set to true after network creation and
55
- // false after a call to GetURIsRW to know when a test needs the
56
- // network to be recreated.
57
- isNetworkPristine bool
58
-
59
- testKeysMu sync.RWMutex
60
- testKeys []* secp256k1.PrivateKey
61
- }
62
-
63
- // Setup ensures the environment is configured with a network that can
64
- // be targeted for testing.
65
- func (te * TestEnvironment ) Setup (
66
- avalancheGoExecPath string ,
67
- testKeysFile string ,
68
- persistentNetworkDir string ,
69
- ) error {
70
- // TODO(marun) Use testify instead of returning errors
71
- if te .setupCalled {
72
- return errors .New ("setup has already been called and should only be called once" )
73
- } else {
74
- te .setupCalled = true
75
- }
76
-
77
- // Need to always configure avalanchego. Even if using a
78
- // persistent network, avalanchego is required for operations like
79
- // node addition.
80
- if avalancheGoExecPath != "" {
81
- if _ , err := os .Stat (avalancheGoExecPath ); err != nil {
82
- return fmt .Errorf ("could not find avalanchego binary: %w" , err )
83
- }
84
- }
85
- te .avalancheGoExecPath = avalancheGoExecPath
86
-
87
- // TODO(marun) Does this really have to be configurable? Maybe just embed.
88
- err := te .LoadKeys (testKeysFile )
89
- if err != nil {
90
- return err
91
- }
92
-
93
- te .networkDir = persistentNetworkDir
94
- if len (te .networkDir ) > 0 {
95
- tests .Outf ("{{yellow}}Using a pre-existing network{{/}}\n " )
96
- te .isPersistentNetwork = true
97
-
98
- network , err := local .LoadNetwork (te .networkDir )
99
- if err != nil {
100
- return fmt .Errorf ("failed to load network: %w" , err )
101
- }
102
- uris := network .GetURIs ()
103
- if len (uris ) == 0 {
104
- return fmt .Errorf ("network path contains no nodes: %s" , te .networkDir )
105
- }
106
- te .setURIs (uris )
107
- }
108
-
109
- // Network setup will be performed just-in-time by the first
110
- // test that needs it. This avoids the cost of starting the
111
- // default network when iterating on tests that won't use it.
112
-
113
- return nil
114
- }
115
-
116
- func (te * TestEnvironment ) LoadKeys (testKeysFile string ) error {
117
- // load test keys
118
- if len (testKeysFile ) == 0 {
119
- return errNoKeyFile
120
- }
121
- testKeys , err := tests .LoadHexTestKeys (testKeysFile )
122
- if err != nil {
123
- return fmt .Errorf ("failed loading test keys: %w" , err )
124
- }
125
- te .setTestKeys (testKeys )
126
- return nil
127
- }
128
-
129
- // EnsureNetwork starts a network if one is not already running.
130
- func (te * TestEnvironment ) EnsureNetwork () {
131
- if te .isPersistentNetwork {
132
- tests .Outf ("{{yellow}}using pre-existing network.{{/}}\n " )
133
- return
134
- }
135
-
136
- // TODO(marun) Add locking to support just-in-time network
137
- // provisioning among tests executing in parallel.
138
-
139
- // The absence of URIs is an indication that the network has not yet been started.
140
- if len (te .GetURIsRO ()) == 0 {
141
- err := te .startCluster ()
142
- gomega .Expect (err ).Should (gomega .BeNil ())
143
- }
144
- }
145
-
146
- // EnsurePristineNetwork attempts to ensure that the currently active
147
- // network is compatible with tests that require a pristine
148
- // (unmodified) state.
149
- func (te * TestEnvironment ) EnsurePristineNetwork () {
150
- if te .isPersistentNetwork {
151
- tests .Outf ("{{yellow}}unable to ensure pristine initial state with a pre-existing network.{{/}}\n " )
152
- return
153
- }
154
-
155
- networkRunning := len (te .networkDir ) != 0
156
- if networkRunning {
157
- if te .isNetworkPristine {
158
- tests .Outf ("{{green}}network is already pristine.{{/}}\n " )
159
- return
160
- }
161
-
162
- tests .Outf ("{{yellow}}network state is not pristine, the current network needs to be replaced.{{/}}\n " )
163
-
164
- tests .Outf ("{{magenta}}shutting down the current network.{{/}}\n " )
165
- err := local .StopNetwork (te .networkDir )
166
- gomega .Expect (err ).Should (gomega .BeNil ())
167
- tests .Outf ("{{green}}network shutdown successful.{{/}}\n " )
168
- }
169
-
170
- err := te .startCluster ()
171
- gomega .Expect (err ).Should (gomega .BeNil ())
172
- }
173
-
174
- // startCluster launches a new network
175
- func (te * TestEnvironment ) startCluster () error {
176
- tests .Outf ("{{magenta}}starting network with %q{{/}}\n " , te .avalancheGoExecPath )
177
-
178
- // TODO(marun) Ensure removal of tmp dir
179
- tmpDir , err := os .MkdirTemp ("" , "" )
180
- if err != nil {
181
- return fmt .Errorf ("failed to create tmp dir for network: %w" , err )
182
- }
183
- networkID := uint32 (0 ) // Network ID will be generated
184
- nodeCount := 5
185
- network , err := local .StartNetwork (
186
- ginkgo .GinkgoWriter ,
187
- tmpDir ,
188
- & local.LocalNetwork {
189
- LocalConfig : local.LocalConfig {
190
- ExecPath : te .avalancheGoExecPath ,
191
- UseStaticPorts : false , // Avoid conflicting with persistent local networks
192
- },
193
- },
194
- networkID ,
195
- nodeCount ,
196
- )
197
- if err != nil {
198
- return err
199
- }
200
-
201
- // TODO(marun) Remove need for locking
202
- te .networkDir = network .Dir
203
-
204
- uris := network .GetURIs ()
205
- if len (uris ) == 0 {
206
- return fmt .Errorf ("network %s has no running nodes" , te .networkDir )
207
- }
208
- te .setURIs (uris )
209
- tests .Outf ("{{green}}successfully started network: {{/}} %+v\n " , uris )
210
-
211
- te .isNetworkPristine = true
212
-
213
- return nil
214
- }
215
-
216
- func (te * TestEnvironment ) setURIs (us []string ) {
217
- te .urisMu .Lock ()
218
- te .uris = us
219
- te .urisMu .Unlock ()
220
- }
221
-
222
- // GetURIsRW returns the current network URIs for read and write
223
- // operations. The network is assumed to have been modified from its
224
- // initial state after this function has been called.
225
- //
226
- // TODO(marun) Find a better way of determining whether a network has
227
- // been modified from its initial state. Or maybe ensure most (if not
228
- // all) tests are capable of targeting any network in a
229
- // non-pathological state?
230
- func (te * TestEnvironment ) GetURIsRW () []string {
231
- te .urisMu .Lock ()
232
- us := te .uris
233
- te .isNetworkPristine = false
234
- te .urisMu .Unlock ()
235
- return us
236
- }
237
-
238
- // GetURIsRO returns the current network URIs for read-only operations.
239
- func (te * TestEnvironment ) GetURIsRO () []string {
240
- te .urisMu .RLock ()
241
- us := te .uris
242
- te .urisMu .RUnlock ()
243
- return us
244
- }
245
-
246
- func (te * TestEnvironment ) setTestKeys (ks []* secp256k1.PrivateKey ) {
247
- te .testKeysMu .Lock ()
248
- te .testKeys = ks
249
- te .testKeysMu .Unlock ()
250
- }
251
-
252
- func (te * TestEnvironment ) GetTestKeys () ([]* secp256k1.PrivateKey , []ids.ShortID , * secp256k1fx.Keychain ) {
253
- te .testKeysMu .RLock ()
254
- testKeys := te .testKeys
255
- te .testKeysMu .RUnlock ()
256
- testKeyAddrs := make ([]ids.ShortID , len (testKeys ))
257
- for i := range testKeyAddrs {
258
- testKeyAddrs [i ] = testKeys [i ].PublicKey ().Address ()
259
- }
260
- keyChain := secp256k1fx .NewKeychain (testKeys ... )
261
- return testKeys , testKeyAddrs , keyChain
33
+ NetworkDir string
34
+ URIs []string
35
+ KeyServerURI string
262
36
}
263
37
264
- func (te * TestEnvironment ) Teardown () error {
265
- gomega .Expect (te .setupCalled ).Should (gomega .BeTrue ())
266
-
267
- if te .isPersistentNetwork {
268
- tests .Outf ("{{yellow}}teardown not required for persistent network{{/}}\n " )
269
- return nil
270
- }
271
-
272
- networkRunning := len (te .networkDir ) != 0
273
- if networkRunning {
274
- // Teardown the active network.
275
- tests .Outf ("{{red}}shutting down network{{/}}\n " )
276
- return local .StopNetwork (te .networkDir )
277
- }
278
-
279
- return nil
38
+ func (te * TestEnvironment ) GetNetwork () testnet.Network {
39
+ network , err := local .LoadNetwork (te .NetworkDir )
40
+ require .NoError (ginkgo .GinkgoT (), err )
41
+ return network
280
42
}
281
43
282
- func (te * TestEnvironment ) GetNetwork () (testnet.Network , error ) {
283
- return local .LoadNetwork (te .networkDir )
44
+ func (te * TestEnvironment ) AllocateTestKeys (count int ) []* secp256k1.PrivateKey {
45
+ keys , err := fixture .AllocateTestKeys (te .KeyServerURI , count )
46
+ require .NoError (ginkgo .GinkgoT (), err )
47
+ return keys
284
48
}
0 commit comments