Skip to content

Commit

Permalink
chore(test): Add argv wrapper class and use it in tests (#1438)
Browse files Browse the repository at this point in the history
Signed-off-by: instamenta <[email protected]>
Signed-off-by: Zhan Milenkov <[email protected]>
  • Loading branch information
instamenta authored Feb 20, 2025
1 parent 19db9d4 commit e18eeee
Show file tree
Hide file tree
Showing 31 changed files with 1,536 additions and 1,651 deletions.
12 changes: 11 additions & 1 deletion src/core/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {SoloError} from './errors.js';
import {Templates} from './templates.js';
import * as constants from './constants.js';
import {PrivateKey, ServiceEndpoint} from '@hashgraph/sdk';
import {type NodeAlias, type NodeAliases} from '../types/aliases.js';
import {type AnyObject, type NodeAlias, type NodeAliases} from '../types/aliases.js';
import {type CommandFlag} from '../types/flag_types.js';
import {type SoloLogger} from './logging.js';
import {type Duration} from './time/duration.js';
Expand Down Expand Up @@ -457,3 +457,13 @@ export function getSoloVersion(): Version {
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
return packageJson.version;
}

/**
* Helper for making deep-clones, works for objects and arrays, ideally with non-class data
*
* @param obj - object to be cloned
* @returns the cloned object
*/
export function deepClone<T = AnyObject>(obj: T): T {
return JSON.parse(JSON.stringify(obj));
}
109 changes: 55 additions & 54 deletions test/e2e/commands/account.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
} from '@hashgraph/sdk';
import * as constants from '../../../src/core/constants.js';
import * as version from '../../../version.js';
import {e2eTestSuite, getDefaultArgv, HEDERA_PLATFORM_VERSION_TAG, TEST_CLUSTER, testLogger} from '../../test_util.js';
import {e2eTestSuite, HEDERA_PLATFORM_VERSION_TAG, TEST_CLUSTER, testLogger} from '../../test_util.js';
import {AccountCommand} from '../../../src/commands/account.js';
import {Flags as flags} from '../../../src/commands/flags.js';
import {Duration} from '../../../src/core/time/duration.js';
Expand All @@ -34,28 +34,29 @@ import {InjectTokens} from '../../../src/core/dependency_injection/inject_tokens
import * as helpers from '../../../src/core/helpers.js';
import {Templates} from '../../../src/core/templates.js';
import * as Base64 from 'js-base64';
import {Argv} from '../../helpers/argv_wrapper.js';
import {type DeploymentName} from '../../../src/core/config/remote/types.js';

const defaultTimeout = Duration.ofSeconds(20).toMillis();

const testName = 'account-cmd-e2e';
const namespace: NamespaceName = NamespaceName.of(testName);
const testSystemAccounts = [[3, 5]];
const argv = getDefaultArgv(namespace);
argv[flags.forcePortForward.name] = true;
argv[flags.namespace.name] = namespace.name;
argv[flags.releaseTag.name] = HEDERA_PLATFORM_VERSION_TAG;
argv[flags.nodeAliasesUnparsed.name] = 'node1,node2';
argv[flags.generateGossipKeys.name] = true;
argv[flags.generateTlsKeys.name] = true;
argv[flags.clusterRef.name] = TEST_CLUSTER;
argv[flags.soloChartVersion.name] = version.SOLO_CHART_VERSION;
argv[flags.loadBalancerEnabled.name] = true;
// set the env variable SOLO_CHARTS_DIR if developer wants to use local Solo charts
argv[flags.chartDirectory.name] = process.env.SOLO_CHARTS_DIR ?? undefined;
const argv = Argv.getDefaultArgv(namespace);
argv.setArg(flags.forcePortForward, true);
argv.setArg(flags.namespace, namespace.name);
argv.setArg(flags.releaseTag, HEDERA_PLATFORM_VERSION_TAG);
argv.setArg(flags.nodeAliasesUnparsed, 'node1,node2');
argv.setArg(flags.generateGossipKeys, true);
argv.setArg(flags.generateTlsKeys, true);
argv.setArg(flags.clusterRef, TEST_CLUSTER);
argv.setArg(flags.soloChartVersion, version.SOLO_CHART_VERSION);
argv.setArg(flags.loadBalancerEnabled, true);
argv.setArg(flags.chartDirectory, process.env.SOLO_CHARTS_DIR ?? undefined);
// enable load balancer for e2e tests
argv[flags.loadBalancerEnabled.name] = true;
argv.setArg(flags.loadBalancerEnabled, true);

e2eTestSuite(testName, argv, undefined, undefined, undefined, undefined, undefined, undefined, true, bootstrapResp => {
e2eTestSuite(testName, argv, {}, bootstrapResp => {
describe('AccountCommand', async () => {
let accountCmd: AccountCommand;
let k8Factory: K8Factory;
Expand All @@ -82,7 +83,7 @@ e2eTestSuite(testName, argv, undefined, undefined, undefined, undefined, undefin
});

describe('node proxies should be UP', () => {
for (const nodeAlias of argv[flags.nodeAliasesUnparsed.name].split(',')) {
for (const nodeAlias of argv.getArg<string>(flags.nodeAliasesUnparsed).split(',')) {
it(`proxy should be UP: ${nodeAlias} `, async () => {
await k8Factory
.default()
Expand All @@ -99,7 +100,7 @@ e2eTestSuite(testName, argv, undefined, undefined, undefined, undefined, undefin

describe('account init command', () => {
it('should succeed with init command', async () => {
const status = await accountCmd.init(argv);
const status = await accountCmd.init(argv.build());
expect(status).to.be.ok;
}).timeout(Duration.ofMinutes(3).toMillis());

Expand All @@ -114,8 +115,8 @@ e2eTestSuite(testName, argv, undefined, undefined, undefined, undefined, undefin
await accountManager.loadNodeClient(
namespace,
clusterRefs,
argv[flags.deployment.name],
argv[flags.forcePortForward.name],
argv.getArg<DeploymentName>(flags.deployment),
argv.getArg<boolean>(flags.forcePortForward),
);
});

Expand All @@ -125,7 +126,7 @@ e2eTestSuite(testName, argv, undefined, undefined, undefined, undefined, undefin
});

it('Node admin key should have been updated, not eqaul to genesis key', async () => {
const nodeAliases = helpers.parseNodeAliases(argv[flags.nodeAliasesUnparsed.name]);
const nodeAliases = helpers.parseNodeAliases(argv.getArg<string>(flags.nodeAliasesUnparsed));
for (const nodeAlias of nodeAliases) {
const keyFromK8 = await k8Factory
.default()
Expand Down Expand Up @@ -160,8 +161,8 @@ e2eTestSuite(testName, argv, undefined, undefined, undefined, undefined, undefin

it('should create account with no options', async () => {
try {
argv[flags.amount.name] = 200;
expect(await accountCmd.create(argv)).to.be.true;
argv.setArg(flags.amount, 200);
expect(await accountCmd.create(argv.build())).to.be.true;

// @ts-ignore to access the private property
const accountInfo = accountCmd.accountInfo;
Expand All @@ -182,11 +183,11 @@ e2eTestSuite(testName, argv, undefined, undefined, undefined, undefined, undefin

it('should create account with private key and hbar amount options', async () => {
try {
argv[flags.ed25519PrivateKey.name] = constants.GENESIS_KEY;
argv[flags.amount.name] = 777;
configManager.update(argv);
argv.setArg(flags.ed25519PrivateKey, constants.GENESIS_KEY);
argv.setArg(flags.amount, 777);
configManager.update(argv.build());

expect(await accountCmd.create(argv)).to.be.true;
expect(await accountCmd.create(argv.build())).to.be.true;

// @ts-ignore to access the private property
const accountInfo = accountCmd.accountInfo;
Expand All @@ -204,16 +205,16 @@ e2eTestSuite(testName, argv, undefined, undefined, undefined, undefined, undefin

it('should update account-1', async () => {
try {
argv[flags.amount.name] = 0;
argv[flags.accountId.name] = accountId1;
configManager.update(argv);
argv.setArg(flags.amount, 0);
argv.setArg(flags.accountId, accountId1);
configManager.update(argv.build());

expect(await accountCmd.update(argv)).to.be.true;
expect(await accountCmd.update(argv.build())).to.be.true;

// @ts-ignore to access the private property
const accountInfo = accountCmd.accountInfo;
expect(accountInfo).not.to.be.null;
expect(accountInfo.accountId).to.equal(argv[flags.accountId.name]);
expect(accountInfo.accountId).to.equal(argv.getArg<string>(flags.accountId));
expect(accountInfo.privateKey).to.be.undefined;
expect(accountInfo.publicKey).not.to.be.null;
expect(accountInfo.balance).to.equal(200);
Expand All @@ -225,17 +226,17 @@ e2eTestSuite(testName, argv, undefined, undefined, undefined, undefined, undefin

it('should update account-2 with accountId, amount, new private key, and standard out options', async () => {
try {
argv[flags.accountId.name] = accountId2;
argv[flags.ed25519PrivateKey.name] = constants.GENESIS_KEY;
argv[flags.amount.name] = 333;
configManager.update(argv);
argv.setArg(flags.accountId, accountId2);
argv.setArg(flags.ed25519PrivateKey, constants.GENESIS_KEY);
argv.setArg(flags.amount, 333);
configManager.update(argv.build());

expect(await accountCmd.update(argv)).to.be.true;
expect(await accountCmd.update(argv.build())).to.be.true;

// @ts-ignore to access the private property
const accountInfo = accountCmd.accountInfo;
expect(accountInfo).not.to.be.null;
expect(accountInfo.accountId).to.equal(argv[flags.accountId.name]);
expect(accountInfo.accountId).to.equal(argv.getArg<string>(flags.accountId));
expect(accountInfo.privateKey).to.be.undefined;
expect(accountInfo.publicKey).not.to.be.null;
expect(accountInfo.balance).to.equal(1_110);
Expand All @@ -247,14 +248,14 @@ e2eTestSuite(testName, argv, undefined, undefined, undefined, undefined, undefin

it('should be able to get account-1', async () => {
try {
argv[flags.accountId.name] = accountId1;
configManager.update(argv);
argv.setArg(flags.accountId, accountId1);
configManager.update(argv.build());

expect(await accountCmd.get(argv)).to.be.true;
// @ts-ignore to access the private property
expect(await accountCmd.get(argv.build())).to.be.true;
// @ts-expect-error - TS2341: to access private property
const accountInfo = accountCmd.accountInfo;
expect(accountInfo).not.to.be.null;
expect(accountInfo.accountId).to.equal(argv[flags.accountId.name]);
expect(accountInfo.accountId).to.equal(argv.getArg<string>(flags.accountId));
expect(accountInfo.privateKey).to.be.undefined;
expect(accountInfo.publicKey).to.be.ok;
expect(accountInfo.balance).to.equal(200);
Expand All @@ -266,14 +267,14 @@ e2eTestSuite(testName, argv, undefined, undefined, undefined, undefined, undefin

it('should be able to get account-2', async () => {
try {
argv[flags.accountId.name] = accountId2;
configManager.update(argv);
argv.setArg(flags.accountId, accountId2);
configManager.update(argv.build());

expect(await accountCmd.get(argv)).to.be.true;
expect(await accountCmd.get(argv.build())).to.be.true;
// @ts-ignore to access the private property
const accountInfo = accountCmd.accountInfo;
expect(accountInfo).not.to.be.null;
expect(accountInfo.accountId).to.equal(argv[flags.accountId.name]);
expect(accountInfo.accountId).to.equal(argv.getArg<string>(flags.accountId));
expect(accountInfo.privateKey).to.be.undefined;
expect(accountInfo.publicKey).to.be.ok;
expect(accountInfo.balance).to.equal(1_110);
Expand All @@ -287,11 +288,11 @@ e2eTestSuite(testName, argv, undefined, undefined, undefined, undefined, undefin
const ecdsaPrivateKey = PrivateKey.generateECDSA();

try {
argv[flags.ecdsaPrivateKey.name] = ecdsaPrivateKey.toString();
argv[flags.setAlias.name] = true;
configManager.update(argv);
argv.setArg(flags.ecdsaPrivateKey, ecdsaPrivateKey.toString());
argv.setArg(flags.setAlias, true);
configManager.update(argv.build());

expect(await accountCmd.create(argv)).to.be.true;
expect(await accountCmd.create(argv.build())).to.be.true;

// @ts-ignore to access the private property
const newAccountInfo = accountCmd.accountInfo;
Expand All @@ -310,8 +311,8 @@ e2eTestSuite(testName, argv, undefined, undefined, undefined, undefined, undefin
await accountManager.loadNodeClient(
namespace,
clusterRefs,
argv[flags.deployment.name],
argv[flags.forcePortForward.name],
argv.getArg<DeploymentName>(flags.deployment),
argv.getArg<boolean>(flags.forcePortForward),
);
const accountAliasInfo = await accountManager.accountInfoQuery(newAccountInfo.accountAlias);
expect(accountAliasInfo).not.to.be.null;
Expand Down Expand Up @@ -342,8 +343,8 @@ e2eTestSuite(testName, argv, undefined, undefined, undefined, undefined, undefin
await accountManager.loadNodeClient(
namespace,
clusterRefs,
argv[flags.deployment.name],
argv[flags.forcePortForward.name],
argv.getArg(flags.deployment),
argv.getArg(flags.forcePortForward),
);
const privateKey = PrivateKey.generate();
const amount = 100;
Expand Down
60 changes: 29 additions & 31 deletions test/e2e/commands/cluster.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,14 @@ import {it, describe, after, before, afterEach, beforeEach} from 'mocha';
import {expect} from 'chai';

import {Flags as flags} from '../../../src/commands/flags.js';
import {bootstrapTestVariables, getDefaultArgv, HEDERA_PLATFORM_VERSION_TAG, TEST_CLUSTER} from '../../test_util.js';
import {bootstrapTestVariables, HEDERA_PLATFORM_VERSION_TAG, TEST_CLUSTER} from '../../test_util.js';
import * as constants from '../../../src/core/constants.js';
import * as logging from '../../../src/core/logging.js';
import {sleep} from '../../../src/core/helpers.js';
import * as version from '../../../version.js';
import {Duration} from '../../../src/core/time/duration.js';
import {NamespaceName} from '../../../src/core/kube/resources/namespace/namespace_name.js';

import {NamespaceNameInvalidError} from '../../../src/core/kube/errors/namespace_name_invalid_error.js';
import {Argv} from '../../helpers/argv_wrapper.js';

describe('ClusterCommand', () => {
// mock showUser and showJSON to silent logging during tests
Expand All @@ -32,20 +31,19 @@ describe('ClusterCommand', () => {

const testName = 'cluster-cmd-e2e';
const namespace = NamespaceName.of(testName);
const argv = getDefaultArgv(namespace);
argv[flags.namespace.name] = namespace.name;
argv[flags.clusterSetupNamespace.name] = constants.SOLO_SETUP_NAMESPACE.name;
argv[flags.releaseTag.name] = HEDERA_PLATFORM_VERSION_TAG;
argv[flags.nodeAliasesUnparsed.name] = 'node1';
argv[flags.generateGossipKeys.name] = true;
argv[flags.generateTlsKeys.name] = true;
argv[flags.clusterRef.name] = TEST_CLUSTER;
argv[flags.soloChartVersion.name] = version.SOLO_CHART_VERSION;
argv[flags.force.name] = true;
// set the env variable SOLO_CHARTS_DIR if developer wants to use local Solo charts
argv[flags.chartDirectory.name] = process.env.SOLO_CHARTS_DIR ?? undefined;

const bootstrapResp = bootstrapTestVariables(testName, argv);
const argv = Argv.getDefaultArgv(namespace);
argv.setArg(flags.namespace, namespace.name);
argv.setArg(flags.clusterSetupNamespace, constants.SOLO_SETUP_NAMESPACE.name);
argv.setArg(flags.releaseTag, HEDERA_PLATFORM_VERSION_TAG);
argv.setArg(flags.nodeAliasesUnparsed, 'node1');
argv.setArg(flags.generateGossipKeys, true);
argv.setArg(flags.generateTlsKeys, true);
argv.setArg(flags.clusterRef, TEST_CLUSTER);
argv.setArg(flags.soloChartVersion, version.SOLO_CHART_VERSION);
argv.setArg(flags.force, true);
argv.setArg(flags.chartDirectory, process.env.SOLO_CHARTS_DIR ?? undefined);

const bootstrapResp = bootstrapTestVariables(testName, argv, {});
const k8Factory = bootstrapResp.opts.k8Factory;
const configManager = bootstrapResp.opts.configManager;
const chartManager = bootstrapResp.opts.chartManager;
Expand All @@ -56,9 +54,9 @@ describe('ClusterCommand', () => {
this.timeout(Duration.ofMinutes(3).toMillis());

await k8Factory.default().namespaces().delete(namespace);
argv[flags.clusterSetupNamespace.name] = constants.SOLO_SETUP_NAMESPACE.name;
configManager.update(argv);
await clusterCmd.handlers.setup(argv); // restore solo-cluster-setup for other e2e tests to leverage
argv.setArg(flags.clusterSetupNamespace, constants.SOLO_SETUP_NAMESPACE.name);
configManager.update(argv.build());
await clusterCmd.handlers.setup(argv.build()); // restore solo-cluster-setup for other e2e tests to leverage
do {
await sleep(Duration.ofSeconds(5));
} while (
Expand All @@ -75,26 +73,26 @@ describe('ClusterCommand', () => {

it('should cleanup existing deployment', async () => {
if (await chartManager.isChartInstalled(constants.SOLO_SETUP_NAMESPACE, constants.SOLO_CLUSTER_SETUP_CHART)) {
expect(await clusterCmd.handlers.reset(argv)).to.be.true;
expect(await clusterCmd.handlers.reset(argv.build())).to.be.true;
}
}).timeout(Duration.ofMinutes(1).toMillis());

it('solo cluster setup should fail with invalid cluster name', async () => {
argv[flags.clusterSetupNamespace.name] = 'INVALID';
await expect(clusterCmd.handlers.setup(argv)).to.be.rejectedWith('Error on cluster setup');
argv.setArg(flags.clusterSetupNamespace, 'INVALID');
await expect(clusterCmd.handlers.setup(argv.build())).to.be.rejectedWith('Error on cluster setup');
}).timeout(Duration.ofMinutes(1).toMillis());

it('solo cluster setup should work with valid args', async () => {
argv[flags.clusterSetupNamespace.name] = namespace.name;
expect(await clusterCmd.handlers.setup(argv)).to.be.true;
argv.setArg(flags.clusterSetupNamespace, namespace.name);
expect(await clusterCmd.handlers.setup(argv.build())).to.be.true;
}).timeout(Duration.ofMinutes(1).toMillis());

it('solo cluster info should work', () => {
expect(clusterCmd.handlers.info(argv)).to.be.ok;
expect(clusterCmd.handlers.info(argv.build())).to.be.ok;
}).timeout(Duration.ofMinutes(1).toMillis());

it('solo cluster list', async () => {
expect(clusterCmd.handlers.list(argv)).to.be.ok;
expect(clusterCmd.handlers.list(argv.build())).to.be.ok;
}).timeout(Duration.ofMinutes(1).toMillis());

it('function showInstalledChartList should return right true', async () => {
Expand All @@ -104,18 +102,18 @@ describe('ClusterCommand', () => {

// helm list would return an empty list if given invalid namespace
it('solo cluster reset should fail with invalid cluster name', async () => {
argv[flags.clusterSetupNamespace.name] = 'INVALID';
argv.setArg(flags.clusterSetupNamespace, 'INVALID');

try {
await expect(clusterCmd.handlers.reset(argv)).to.be.rejectedWith('Error on cluster reset');
await expect(clusterCmd.handlers.reset(argv.build())).to.be.rejectedWith('Error on cluster reset');
} catch (e) {
clusterCmd.logger.showUserError(e);
expect.fail();
}
}).timeout(Duration.ofMinutes(1).toMillis());

it('solo cluster reset should work with valid args', async () => {
argv[flags.clusterSetupNamespace.name] = namespace.name;
expect(await clusterCmd.handlers.reset(argv)).to.be.true;
argv.setArg(flags.clusterSetupNamespace, namespace.name);
expect(await clusterCmd.handlers.reset(argv.build())).to.be.true;
}).timeout(Duration.ofMinutes(1).toMillis());
});
Loading

0 comments on commit e18eeee

Please sign in to comment.