UNPKG

@hashgraph/solo

Version:

An opinionated CLI tool to deploy and manage private Hedera Networks.

166 lines 8.83 kB
// SPDX-License-Identifier: Apache-2.0 import { describe } from 'mocha'; import { resetForTest } from '../../test-container.js'; import { container } from 'tsyringe-neo'; import { InjectTokens } from '../../../src/core/dependency-injection/inject-tokens.js'; import fs from 'node:fs'; import { DEFAULT_LOCAL_CONFIG_FILE, RESOURCES_DIR } from '../../../src/core/constants.js'; import { Duration } from '../../../src/core/time/duration.js'; import { PathEx } from '../../../src/business/utils/path-ex.js'; import { EndToEndTestSuiteBuilder } from '../end-to-end-test-suite-builder.js'; import { InitTest } from './tests/init-test.js'; import { ClusterReferenceTest } from './tests/cluster-reference-test.js'; import { DeploymentTest } from './tests/deployment-test.js'; import { ConsensusNodeTest } from './tests/consensus-node-test.js'; import { NetworkTest } from './tests/network-test.js'; import { MirrorNodeTest } from './tests/mirror-node-test.js'; import { ExplorerTest } from './tests/explorer-test.js'; import { RelayTest } from './tests/relay-test.js'; import { spawn } from 'node:child_process'; import { MetricsServerImpl } from '../../../src/business/runtime-state/services/metrics-server-impl.js'; import * as constants from '../../../src/core/constants.js'; import { BlockNodeTest } from './tests/block-node-test.js'; import { getTemporaryDirectory } from '../../test-utility.js'; const testName = 'external-database-test'; // Use dual-cluster specific values file with higher memory limits to prevent OOM const dualClusterValuesFile = PathEx.joinWithRealPath(RESOURCES_DIR, 'mirror-node-values-dual-cluster-minimal.yaml'); const configFiles = { 'api-permission.properties': 'api-permission.properties.txt', 'application.env': 'application.env.txt', [constants.APPLICATION_PROPERTIES]: 'application.properties.txt', 'bootstrap.properties': 'bootstrap.properties.txt', 'log4j2.xml': 'log4j2.xml.txt', 'settings.txt': 'settings.txt.txt', }; const endToEndTestSuite = new EndToEndTestSuiteBuilder() .withTestName(testName) .withTestSuiteName('External Database E2E Test Suite') .withNamespace(testName) .withDeployment(`${testName}-deployment`) .withClusterCount(2) .withConsensusNodesCount(2) .withLoadBalancerEnabled(true) .withPinger(true) .withShard(3) .withRealm(2) .withApiPermissionProperties(configFiles['api-permission.properties']) .withApplicationEnvironment(configFiles['application.env']) .withApplicationProperties(configFiles[constants.APPLICATION_PROPERTIES]) .withBootstrapProperties(configFiles['bootstrap.properties']) .withLog4j2Xml(configFiles['log4j2.xml']) .withSettingsTxt(configFiles['settings.txt']) .withTestSuiteCallback((options, preDestroy) => { describe('External Database E2E Test', () => { const { testCacheDirectory, testLogger, namespace, contexts } = options; const blockNodeEnabled = process.env.SOLO_E2E_EXTERNAL_DB_TEST_BLOCK_NODE === 'true'; const topicTestOnly = process.env.SOLO_E2E_EXTERNAL_DB_TEST_TOPIC_ONLY === 'true'; before(async () => { fs.rmSync(testCacheDirectory, { recursive: true, force: true }); try { fs.rmSync(PathEx.joinWithRealPath(testCacheDirectory, '..', DEFAULT_LOCAL_CONFIG_FILE), { force: true, }); } catch { // allowed to fail if the file doesn't exist } resetForTest(namespace.name, testCacheDirectory, false); for (const item of contexts) { const k8Client = container.resolve(InjectTokens.K8Factory).getK8(item); await k8Client.namespaces().delete(namespace); } testLogger.info(`${testName}: starting ${testName} e2e test`); // copy all consensus config files to a temporary directory with non-default names const templateDirectory = PathEx.joinWithRealPath(RESOURCES_DIR, 'templates'); const temporaryDirectory = getTemporaryDirectory(); for (const [sourceFileName, targetFileName] of Object.entries(configFiles)) { fs.cpSync(PathEx.join(templateDirectory, sourceFileName), PathEx.join(temporaryDirectory, targetFileName)); } }).timeout(Duration.ofMinutes(5).toMillis()); after(async () => { await preDestroy(endToEndTestSuite); }).timeout(Duration.ofMinutes(5).toMillis()); beforeEach(async () => { testLogger.info(`${testName}: resetting containers for each test`); resetForTest(namespace.name, testCacheDirectory, false); testLogger.info(`${testName}: finished resetting containers for each test`); }); InitTest.init(options); ClusterReferenceTest.connect(options); DeploymentTest.create(options); DeploymentTest.addCluster(options); ConsensusNodeTest.keys(options); if (blockNodeEnabled) { BlockNodeTest.add(options); } NetworkTest.deploy(options); ConsensusNodeTest.setup(options); ConsensusNodeTest.start(options); // Mirror node, explorer and relay node are deployed to the second cluster MirrorNodeTest.installPostgres(options); // Use dual-cluster specific values file with higher memory limits MirrorNodeTest.deployWithExternalDatabase({ ...options, valuesFile: dualClusterValuesFile }); ExplorerTest.add(options); RelayTest.add(options); DeploymentTest.info(options); DeploymentTest.verifyDeploymentConfigInfo(options); it('should run smoke tests', async () => { // Mirror node is deployed to the second cluster in the dual-cluster setup. // Pass its context so solo_smoke_test.sh can issue kubectl commands against // the right cluster (e.g. kubectl wait for mirror-grpc readiness). const mirrorClusterContext = contexts[1]; const scriptPath = `export SOLO_HOME=${testCacheDirectory}; \ export SHARD_NUM=3; \ export REALM_NUM=2; \ export NEW_NODE_ACCOUNT_ID=3.2.3; \ export SOLO_NAMESPACE=${namespace.name}; \ export SOLO_CLUSTER_CONTEXT=${mirrorClusterContext}; \ export SOLO_CACHE_DIR=${testCacheDirectory}; \ export SOLO_DEPLOYMENT=${testName}-deployment; \ ${topicTestOnly ? 'source .github/workflows/script/helper.sh && cd .. && ' + 'create_test_account ${SOLO_DEPLOYMENT} && cd solo && node scripts/create-topic.js' : '.github/workflows/script/solo_smoke_test.sh'}`; // running the script and show its output in real time for easy to debug // and check its progress return new Promise((resolve, reject) => { const process = spawn(scriptPath, { stdio: 'pipe', // Use pipe to capture output shell: true, // Run in shell to support bash features }); // Stream stdout in real-time process.stdout.on('data', (data) => { data.toString().replaceAll('::group::', '\r::group::').replaceAll('::endgroup::', '\r::endgroup::'); console.log(`${data}`.trim()); }); // Stream stderr in real-time process.stderr.on('data', (data) => { data.toString().replaceAll('::group::', '\r::group::').replaceAll('::endgroup::', '\r::endgroup::'); console.log(`${data}`.trim()); }); // Handle process completion process.on('close', (code) => { if (code) { const error = new Error(`Smoke test failed with exit code ${code}`); reject(error); } else { console.log('Smoke test execution succeeded'); resolve(); } }); // Handle process errors process.on('error', (error) => { console.error('Failed to start smoke test process:', error.message); reject(error); }); }); }).timeout(Duration.ofMinutes(30).toMillis()); it('Should write log metrics', async () => { await new MetricsServerImpl().logMetrics(testName, PathEx.join(constants.SOLO_LOGS_DIR, `${testName}`), undefined, undefined, contexts); }); }).timeout(Duration.ofMinutes(40).toMillis()); }) .build(); endToEndTestSuite.runTestSuite(); //# sourceMappingURL=external-database.test.js.map