@@ -35,23 +35,40 @@ import {PodRef} from '../../../src/integration/kube/resources/pod/pod-ref.js';
35
35
import { type SoloWinstonLogger } from '../../../src/core/logging/solo-winston-logger.js' ;
36
36
import { type NodeAlias } from '../../../src/types/aliases.js' ;
37
37
import * as constants from '../../../src/core/constants.js' ;
38
+ import { type ExtendedNetServer } from '../../../src/types/index.js' ;
39
+ import http from 'http' ;
40
+ import { sleep } from '../../../src/core/helpers.js' ;
41
+ import { type AccountManager } from '../../../src/core/account-manager.js' ;
42
+ import {
43
+ AccountCreateTransaction ,
44
+ Hbar ,
45
+ HbarUnit ,
46
+ PrivateKey ,
47
+ type TransactionReceipt ,
48
+ type TransactionResponse ,
49
+ } from '@hashgraph/sdk' ;
50
+ import { type PackageDownloader } from '../../../src/core/package-downloader.js' ;
38
51
39
52
const testName : string = 'dual-cluster-full' ;
40
53
41
54
describe ( 'Dual Cluster Full E2E Test' , async function dualClusterFullE2eTest ( ) : Promise < void > {
42
55
this . bail ( true ) ;
43
56
const namespace : NamespaceName = NamespaceName . of ( testName ) ;
44
- const deployment : string = `${ testName } -deployment` ;
45
- const testClusterRefs : ClusterRef [ ] = [ 'e2e-cluster-alpha' , 'e2e-cluster-beta' ] ;
57
+ const deployment : DeploymentName = `${ testName } -deployment` ;
58
+ const testClusterArray : ClusterRef [ ] = [ 'e2e-cluster-alpha' , 'e2e-cluster-beta' ] ;
46
59
const soloTestCluster : string = getTestCluster ( ) ;
47
60
const testCluster : string =
48
61
soloTestCluster . includes ( 'c1' ) || soloTestCluster . includes ( 'c2' ) ? soloTestCluster : `${ soloTestCluster } -c1` ;
49
62
const contexts : string [ ] = [
50
63
`${ testCluster } ` ,
51
64
`${ testCluster . replace ( soloTestCluster . includes ( '-c1' ) ? '-c1' : '-c2' , soloTestCluster . includes ( '-c1' ) ? '-c2' : '-c1' ) } ` ,
52
65
] ;
66
+ const testClusterRefs : ClusterRefs = { } ;
67
+ testClusterRefs [ testClusterArray [ 0 ] ] = contexts [ 0 ] ;
68
+ testClusterRefs [ testClusterArray [ 1 ] ] = contexts [ 1 ] ;
53
69
const testCacheDir : string = getTestCacheDir ( testName ) ;
54
70
let testLogger : SoloWinstonLogger ;
71
+ const createdAccountIds : string [ ] = [ ] ;
55
72
56
73
// TODO the kube config context causes issues if it isn't one of the selected clusters we are deploying to
57
74
before ( async ( ) : Promise < void > => {
@@ -86,13 +103,13 @@ describe('Dual Cluster Full E2E Test', async function dualClusterFullE2eTest():
86
103
87
104
it ( `${ testName } : solo cluster-ref connect` , async ( ) : Promise < void > => {
88
105
testLogger . info ( `${ testName } : beginning solo cluster-ref connect` ) ;
89
- for ( let index : number = 0 ; index < testClusterRefs . length ; index ++ ) {
90
- await main ( soloClusterRefConnectArgv ( testClusterRefs [ index ] , contexts [ index ] ) ) ;
106
+ for ( let index : number = 0 ; index < testClusterArray . length ; index ++ ) {
107
+ await main ( soloClusterRefConnectArgv ( testClusterArray [ index ] , contexts [ index ] ) ) ;
91
108
}
92
109
const localConfig : LocalConfig = container . resolve < LocalConfig > ( InjectTokens . LocalConfig ) ;
93
110
const clusterRefs : ClusterRefs = localConfig . clusterRefs ;
94
- expect ( clusterRefs [ testClusterRefs [ 0 ] ] ) . to . equal ( contexts [ 0 ] ) ;
95
- expect ( clusterRefs [ testClusterRefs [ 1 ] ] ) . to . equal ( contexts [ 1 ] ) ;
111
+ expect ( clusterRefs [ testClusterArray [ 0 ] ] ) . to . equal ( contexts [ 0 ] ) ;
112
+ expect ( clusterRefs [ testClusterArray [ 1 ] ] ) . to . equal ( contexts [ 1 ] ) ;
96
113
testLogger . info ( `${ testName } : finished solo cluster-ref connect` ) ;
97
114
} ) ;
98
115
@@ -104,22 +121,22 @@ describe('Dual Cluster Full E2E Test', async function dualClusterFullE2eTest():
104
121
105
122
it ( `${ testName } : solo deployment add-cluster` , async ( ) : Promise < void > => {
106
123
testLogger . info ( `${ testName } : beginning solo deployment add-cluster` ) ;
107
- for ( let index : number = 0 ; index < testClusterRefs . length ; index ++ ) {
108
- await main ( soloDeploymentAddClusterArgv ( deployment , testClusterRefs [ index ] , 1 ) ) ;
124
+ for ( let index : number = 0 ; index < testClusterArray . length ; index ++ ) {
125
+ await main ( soloDeploymentAddClusterArgv ( deployment , testClusterArray [ index ] , 1 ) ) ;
109
126
}
110
127
const remoteConfigManager : RemoteConfigManager = container . resolve ( InjectTokens . RemoteConfigManager ) ;
111
128
expect ( remoteConfigManager . isLoaded ( ) , 'remote config manager should be loaded' ) . to . be . true ;
112
129
const consensusNodes : Record < string , ConsensusNodeComponent > = remoteConfigManager . components . consensusNodes ;
113
130
expect ( Object . entries ( consensusNodes ) . length , 'consensus node count should be 2' ) . to . equal ( 2 ) ;
114
- expect ( consensusNodes [ 'node1' ] . cluster ) . to . equal ( testClusterRefs [ 0 ] ) ;
115
- expect ( consensusNodes [ 'node2' ] . cluster ) . to . equal ( testClusterRefs [ 1 ] ) ;
131
+ expect ( consensusNodes [ 'node1' ] . cluster ) . to . equal ( testClusterArray [ 0 ] ) ;
132
+ expect ( consensusNodes [ 'node2' ] . cluster ) . to . equal ( testClusterArray [ 1 ] ) ;
116
133
testLogger . info ( `${ testName } : finished solo deployment add-cluster` ) ;
117
134
} ) ;
118
135
119
136
it ( `${ testName } : solo cluster-ref setup` , async ( ) : Promise < void > => {
120
137
testLogger . info ( `${ testName } : beginning solo cluster-ref setup` ) ;
121
- for ( let index : number = 0 ; index < testClusterRefs . length ; index ++ ) {
122
- await main ( soloClusterRefSetup ( testClusterRefs [ index ] ) ) ;
138
+ for ( let index : number = 0 ; index < testClusterArray . length ; index ++ ) {
139
+ await main ( soloClusterRefSetup ( testClusterArray [ index ] ) ) ;
123
140
}
124
141
testLogger . info ( `${ testName } : finishing solo cluster-ref setup` ) ;
125
142
} ) ;
@@ -193,11 +210,45 @@ describe('Dual Cluster Full E2E Test', async function dualClusterFullE2eTest():
193
210
constants . NETWORK_PROXY_DELAY ,
194
211
) ;
195
212
expect ( haProxyPod ) . to . have . lengthOf ( 1 ) ;
213
+ createdAccountIds . push ( await verifyAccountCreateWasSuccessful ( namespace , testClusterRefs ) ) ;
214
+ createdAccountIds . push ( await verifyAccountCreateWasSuccessful ( namespace , testClusterRefs ) ) ;
196
215
}
197
216
} ) . timeout ( Duration . ofMinutes ( 5 ) . toMillis ( ) ) ;
198
217
199
- // TODO mirror node deploy
218
+ it ( `${ testName } : mirror node deploy` , async ( ) : Promise < void > => {
219
+ await main ( soloMirrorNodeDeployArgv ( deployment , testClusterArray [ 1 ] ) ) ;
220
+ await verifyMirrorNodeDeployWasSuccessful ( contexts , namespace , testLogger ) ;
221
+ // TODO validate the new accounts are showing up with the mirror node rest url
222
+ } ) . timeout ( Duration . ofMinutes ( 10 ) . toMillis ( ) ) ;
223
+
200
224
// TODO explorer deploy
225
+ xit ( `${ testName } : explorer deploy` , async ( ) : Promise < void > => {
226
+ await main ( soloExplorerDeployArgv ( deployment , testClusterArray [ 1 ] ) ) ;
227
+ const k8Factory : K8Factory = container . resolve < K8Factory > ( InjectTokens . K8Factory ) ;
228
+ const k8 : K8 = k8Factory . getK8 ( contexts [ 1 ] ) ;
229
+ const hederaExplorerPods : Pod [ ] = await k8
230
+ . pods ( )
231
+ . list ( namespace , [
232
+ 'app.kubernetes.io/instance=hedera-explorer' ,
233
+ 'app.kubernetes.io/name=hedera-explorer-chart' ,
234
+ 'app.kubernetes.io/component=hedera-explorer' ,
235
+ ] ) ;
236
+ expect ( hederaExplorerPods ) . to . have . lengthOf ( 1 ) ;
237
+ let portForwarder : ExtendedNetServer = null ;
238
+ try {
239
+ portForwarder = await k8 . pods ( ) . readByRef ( hederaExplorerPods [ 0 ] . podRef ) . portForward ( 8_080 , 8_080 ) ;
240
+ await sleep ( Duration . ofSeconds ( 2 ) ) ;
241
+ const guiUrl : string = 'http://127.0.0.1:8080/localnet/dashboard' ;
242
+ const packageDownloader : PackageDownloader = container . resolve < PackageDownloader > ( InjectTokens . PackageDownloader ) ;
243
+ expect ( await packageDownloader . urlExists ( guiUrl ) , 'the hedera explorer GUI URL should exist' ) . to . be . true ;
244
+ // TODO validate the new accounts are showing up with the hedera explorer url
245
+ } finally {
246
+ if ( portForwarder ) {
247
+ await k8 . pods ( ) . readByRef ( null ) . stopPortForward ( portForwarder ) ;
248
+ }
249
+ }
250
+ } ) ;
251
+
201
252
// TODO json rpc relay deploy
202
253
// TODO json rpc relay destroy
203
254
// TODO explorer destroy
@@ -207,6 +258,7 @@ describe('Dual Cluster Full E2E Test', async function dualClusterFullE2eTest():
207
258
await main ( soloNetworkDestroyArgv ( deployment ) ) ;
208
259
} ) ;
209
260
} ) ;
261
+
210
262
function newArgv ( ) : string [ ] {
211
263
return [ '${PATH}/node' , '${SOLO_ROOT}/solo.ts' ] ;
212
264
}
@@ -236,7 +288,7 @@ function soloClusterRefConnectArgv(clusterRef: ClusterRef, context: string): str
236
288
return argv ;
237
289
}
238
290
239
- function soloDeploymentCreateArgv ( deployment : string , namespace : NamespaceName ) : string [ ] {
291
+ function soloDeploymentCreateArgv ( deployment : DeploymentName , namespace : NamespaceName ) : string [ ] {
240
292
const argv : string [ ] = newArgv ( ) ;
241
293
argv . push ( 'deployment' ) ;
242
294
argv . push ( 'create' ) ;
@@ -248,7 +300,11 @@ function soloDeploymentCreateArgv(deployment: string, namespace: NamespaceName):
248
300
return argv ;
249
301
}
250
302
251
- function soloDeploymentAddClusterArgv ( deployment : string , clusterRef : ClusterRef , numberOfNodes : number ) : string [ ] {
303
+ function soloDeploymentAddClusterArgv (
304
+ deployment : DeploymentName ,
305
+ clusterRef : ClusterRef ,
306
+ numberOfNodes : number ,
307
+ ) : string [ ] {
252
308
const argv : string [ ] = newArgv ( ) ;
253
309
argv . push ( 'deployment' ) ;
254
310
argv . push ( 'add-cluster' ) ;
@@ -285,7 +341,7 @@ function soloNodeKeysArgv(deployment: DeploymentName): string[] {
285
341
return argv ;
286
342
}
287
343
288
- function soloNetworkDeployArgv ( deployment : string ) : string [ ] {
344
+ function soloNetworkDeployArgv ( deployment : DeploymentName ) : string [ ] {
289
345
const argv : string [ ] = newArgv ( ) ;
290
346
argv . push ( 'network' ) ;
291
347
argv . push ( 'deploy' ) ;
@@ -296,7 +352,7 @@ function soloNetworkDeployArgv(deployment: string): string[] {
296
352
return argv ;
297
353
}
298
354
299
- function soloNodeSetupArgv ( deployment : string ) : string [ ] {
355
+ function soloNodeSetupArgv ( deployment : DeploymentName ) : string [ ] {
300
356
const argv : string [ ] = newArgv ( ) ;
301
357
argv . push ( 'node' ) ;
302
358
argv . push ( 'setup' ) ;
@@ -306,7 +362,7 @@ function soloNodeSetupArgv(deployment: string): string[] {
306
362
return argv ;
307
363
}
308
364
309
- function soloNodeStartArgv ( deployment : string ) : string [ ] {
365
+ function soloNodeStartArgv ( deployment : DeploymentName ) : string [ ] {
310
366
const argv : string [ ] = newArgv ( ) ;
311
367
argv . push ( 'node' ) ;
312
368
argv . push ( 'start' ) ;
@@ -316,7 +372,125 @@ function soloNodeStartArgv(deployment: string): string[] {
316
372
return argv ;
317
373
}
318
374
319
- function soloNetworkDestroyArgv ( deployment : string ) : string [ ] {
375
+ async function verifyAccountCreateWasSuccessful ( namespace : NamespaceName , clusterRefs : ClusterRefs ) : Promise < string > {
376
+ const accountManager : AccountManager = container . resolve < AccountManager > ( InjectTokens . AccountManager ) ;
377
+ try {
378
+ await accountManager . refreshNodeClient ( namespace , clusterRefs ) ;
379
+ expect ( accountManager . _nodeClient ) . not . to . be . null ;
380
+ const privateKey : PrivateKey = PrivateKey . generate ( ) ;
381
+ const amount : number = 100 ;
382
+
383
+ const newAccount : TransactionResponse = await new AccountCreateTransaction ( )
384
+ . setKeyWithoutAlias ( privateKey )
385
+ . setInitialBalance ( Hbar . from ( amount , HbarUnit . Hbar ) )
386
+ . execute ( accountManager . _nodeClient ) ;
387
+
388
+ // Get the new account ID
389
+ const getReceipt : TransactionReceipt = await newAccount . getReceipt ( accountManager . _nodeClient ) ;
390
+ const accountInfo : { accountId : string ; privateKey : string ; balance : number ; publicKey : string } = {
391
+ accountId : getReceipt . accountId . toString ( ) ,
392
+ privateKey : privateKey . toString ( ) ,
393
+ publicKey : privateKey . publicKey . toString ( ) ,
394
+ balance : amount ,
395
+ } ;
396
+
397
+ expect ( accountInfo . accountId ) . not . to . be . null ;
398
+ expect ( accountInfo . balance ) . to . equal ( amount ) ;
399
+
400
+ return accountInfo . accountId ;
401
+ } finally {
402
+ await accountManager . close ( ) ;
403
+ // @ts -expect-error - TS2341: Property _portForwards is private and only accessible within class AccountManager
404
+ expect ( accountManager . _portForwards , 'port forwards should be empty after accountManager.close()' ) . to . have . lengthOf (
405
+ 0 ,
406
+ ) ;
407
+ }
408
+ }
409
+
410
+ function soloMirrorNodeDeployArgv ( deployment : DeploymentName , clusterRef : ClusterRef ) : string [ ] {
411
+ const argv : string [ ] = newArgv ( ) ;
412
+ argv . push ( 'mirror-node' ) ;
413
+ argv . push ( 'deploy' ) ;
414
+ argv . push ( optionFromFlag ( Flags . deployment ) ) ;
415
+ argv . push ( deployment ) ;
416
+ argv . push ( optionFromFlag ( Flags . clusterRef ) ) ;
417
+ argv . push ( clusterRef ) ;
418
+ argv . push ( optionFromFlag ( Flags . pinger ) ) ;
419
+ argvPushGlobalFlags ( argv , true , true ) ;
420
+ return argv ;
421
+ }
422
+
423
+ async function verifyMirrorNodeDeployWasSuccessful (
424
+ contexts : string [ ] ,
425
+ namespace : NamespaceName ,
426
+ testLogger : SoloWinstonLogger ,
427
+ ) : Promise < void > {
428
+ const k8Factory : K8Factory = container . resolve < K8Factory > ( InjectTokens . K8Factory ) ;
429
+ const k8 : K8 = k8Factory . getK8 ( contexts [ 1 ] ) ;
430
+ const mirrorNodeRestPods : Pod [ ] = await k8
431
+ . pods ( )
432
+ . list ( namespace , [
433
+ 'app.kubernetes.io/instance=mirror' ,
434
+ 'app.kubernetes.io/name=rest' ,
435
+ 'app.kubernetes.io/component=rest' ,
436
+ ] ) ;
437
+ expect ( mirrorNodeRestPods ) . to . have . lengthOf ( 1 ) ;
438
+ let portForwarder : ExtendedNetServer = null ;
439
+ try {
440
+ portForwarder = await k8 . pods ( ) . readByRef ( mirrorNodeRestPods [ 0 ] . podRef ) . portForward ( 5_551 , 5_551 ) ;
441
+ await sleep ( Duration . ofSeconds ( 2 ) ) ;
442
+ const queryUrl : string = 'http://localhost:5551/api/v1/network/nodes' ;
443
+ let received : boolean = false ;
444
+ // wait until the transaction reached consensus and retrievable from the mirror node API
445
+ while ( ! received ) {
446
+ const req : http . ClientRequest = http . request (
447
+ queryUrl ,
448
+ { method : 'GET' , timeout : 100 , headers : { Connection : 'close' } } ,
449
+ ( res : http . IncomingMessage ) : void => {
450
+ res . setEncoding ( 'utf8' ) ;
451
+ res . on ( 'data' , ( chunk ) : void => {
452
+ // convert chunk to json object
453
+ const obj : { nodes : unknown [ ] } = JSON . parse ( chunk ) ;
454
+ expect (
455
+ obj . nodes ?. length ,
456
+ "expect there to be two nodes in the mirror node's copy of the address book" ,
457
+ ) . to . equal ( 2 ) ;
458
+ // TODO need to enable this, but looks like mirror node currently is getting no service endpoints
459
+ // expect(
460
+ // obj.nodes[0].service_endpoints?.length,
461
+ // 'expect there to be at least one service endpoint',
462
+ // ).to.be.greaterThan(0);
463
+ received = true ;
464
+ } ) ;
465
+ } ,
466
+ ) ;
467
+ req . on ( 'error' , ( e : Error ) : void => {
468
+ testLogger . debug ( `problem with request: ${ e . message } ` , e ) ;
469
+ } ) ;
470
+ req . end ( ) ; // make the request
471
+ await sleep ( Duration . ofSeconds ( 2 ) ) ;
472
+ }
473
+ await sleep ( Duration . ofSeconds ( 1 ) ) ;
474
+ } finally {
475
+ if ( portForwarder ) {
476
+ await k8 . pods ( ) . readByRef ( null ) . stopPortForward ( portForwarder ) ;
477
+ }
478
+ }
479
+ }
480
+
481
+ function soloExplorerDeployArgv ( deployment : DeploymentName , clusterRef : ClusterRef ) : string [ ] {
482
+ const argv : string [ ] = newArgv ( ) ;
483
+ argv . push ( 'explorer' ) ;
484
+ argv . push ( 'deploy' ) ;
485
+ argv . push ( optionFromFlag ( Flags . deployment ) ) ;
486
+ argv . push ( deployment ) ;
487
+ argv . push ( optionFromFlag ( Flags . clusterRef ) ) ;
488
+ argv . push ( clusterRef ) ;
489
+ argvPushGlobalFlags ( argv , true , true ) ;
490
+ return argv ;
491
+ }
492
+
493
+ function soloNetworkDestroyArgv ( deployment : DeploymentName ) : string [ ] {
320
494
const argv : string [ ] = newArgv ( ) ;
321
495
argv . push ( 'network' ) ;
322
496
argv . push ( 'destroy' ) ;
0 commit comments