UNPKG

@hashgraph/solo

Version:

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

153 lines (152 loc) 9.75 kB
// SPDX-License-Identifier: Apache-2.0 import { expect } from 'chai'; import { afterEach, beforeEach, describe, it } from 'mocha'; import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import AdmZip from 'adm-zip'; import sinon from 'sinon'; import { DiagnosticsAnalyzer } from '../../../../src/commands/util/diagnostics-analyzer.js'; describe('DiagnosticsAnalyzer', () => { let temporaryDirectory; let loggerStub; let userMessages; const swirldsLogSample = `2026-03-27 16:52:37.460 20 INFO STARTUP <main> EnhancedKeyStoreLoader: Finished key store migration. 2026-03-27 16:52:37.481 21 INFO STARTUP <main> EnhancedKeyStoreLoader: Generating agreement key pair for local nodeId 2 2026-03-27 16:52:37.539 22 WARN STARTUP <main> EnhancedKeyStoreLoader: No certificate found for nodeId 2 [purpose = AGREEMENT ] 2026-03-27 16:52:37.539 23 ERROR EXCEPTION <main> CryptoStatic: Exception while loading/generating keys com.swirlds.platform.crypto.KeyLoadingException: No certificate found for nodeId 2 [purpose = AGREEMENT ] \tat com.swirlds.platform.crypto.EnhancedKeyStoreLoader.verify(EnhancedKeyStoreLoader.java:341) \tat com.swirlds.platform.crypto.CryptoStatic.initNodeSecurity(CryptoStatic.java:186) \tat com.hedera.node.app.ServicesMain.main(ServicesMain.java:228) 2026-03-27 16:52:37.541 24 INFO STARTUP <main> SystemExitUtils: System exit requested (KEY_LOADING_FAILED) thread requesting exit: main com.swirlds.platform.system.SystemExitUtils.exitSystem(SystemExitUtils.java:37) \tat com.swirlds.platform.system.SystemExitUtils.exitSystem(SystemExitUtils.java:73) \tat com.swirlds.platform.crypto.CryptoStatic.initNodeSecurity(CryptoStatic.java:216) \tat com.hedera.node.app.ServicesMain.main(ServicesMain.java:228) 2026-03-27 16:52:37.544 25 ERROR EXCEPTION <main> SystemExitUtils: Exiting system {"reason":"KEY_LOADING_FAILED","code":204} [com.swirlds.logging.legacy.payload.SystemExitPayload] 2026-03-27 16:52:37.544 26 INFO STARTUP <<browser: shutdown-hook>> Log4jSetup: JVM is shutting down. `; beforeEach(() => { temporaryDirectory = fs.mkdtempSync(path.join(os.tmpdir(), 'solo-diagnostics-analyzer-')); userMessages = []; loggerStub = { setDevMode: sinon.stub(), isDevMode: sinon.stub().returns(false), nextTraceId: sinon.stub(), prepMeta: sinon.stub().callsFake((meta) => meta ?? {}), showUser: sinon.stub().callsFake((message) => { userMessages.push(String(message)); }), showUserError: sinon.stub(), error: sinon.stub(), warn: sinon.stub(), info: sinon.stub(), debug: sinon.stub(), showList: sinon.stub(), showJSON: sinon.stub(), addMessageGroup: sinon.stub(), getMessageGroup: sinon.stub().returns([]), addMessageGroupMessage: sinon.stub(), showMessageGroup: sinon.stub(), getMessageGroupKeys: sinon.stub().returns([]), showAllMessageGroups: sinon.stub(), flush: sinon.stub().callsFake((callback) => callback()), setLogBinding: sinon.stub(), addLogBindings: sinon.stub(), clearLogBindings: sinon.stub(), }; }); afterEach(() => { fs.rmSync(temporaryDirectory, { recursive: true, force: true }); sinon.restore(); }); it('extracts and reports exception stack details from swirlds.log', () => { const archivePath = path.join(temporaryDirectory, 'network-node3-0-log-config.zip'); const archive = new AdmZip(); archive.addFile('output/swirlds.log', Buffer.from(swirldsLogSample, 'utf8')); archive.writeZip(archivePath); new DiagnosticsAnalyzer(loggerStub).analyze(temporaryDirectory, ''); const reportPath = path.join(temporaryDirectory, 'diagnostics-analysis.txt'); expect(fs.existsSync(reportPath)).to.equal(true); const reportText = fs.readFileSync(reportPath, 'utf8'); expect(reportText).to.include('Consensus node may not have reached ACTIVE status'); expect(reportText).to.include('Exception detected in swirlds.log'); expect(reportText).to.include('com.swirlds.platform.crypto.KeyLoadingException: No certificate found for nodeId 2 [purpose = AGREEMENT ]'); expect(reportText).to.include('at com.swirlds.platform.crypto.EnhancedKeyStoreLoader.verify(EnhancedKeyStoreLoader.java:341)'); expect(reportText).to.include('No ACTIVE status marker found in swirlds.log'); const consoleSummary = userMessages.join('\n'); expect(consoleSummary).to.include('Exception detected in swirlds.log'); expect(consoleSummary).to.include('com.swirlds.platform.crypto.KeyLoadingException: No certificate found for nodeId 2 [purpose = AGREEMENT ]'); }); it('includes the preceding ERROR EXCEPTION line when exception block starts on throwable class line', () => { const logWithUppercaseExceptionMarker = `2026-03-27 16:52:37.539 23 ERROR EXCEPTION <main> CryptoStatic: key loading failed com.swirlds.platform.crypto.KeyLoadingException: No certificate found for nodeId 2 [purpose = AGREEMENT ] \tat com.swirlds.platform.crypto.EnhancedKeyStoreLoader.verify(EnhancedKeyStoreLoader.java:341) 2026-03-27 16:52:37.541 24 INFO STARTUP <main> SystemExitUtils: System exit requested (KEY_LOADING_FAILED) `; const archivePath = path.join(temporaryDirectory, 'network-node3-0-log-config.zip'); const archive = new AdmZip(); archive.addFile('output/swirlds.log', Buffer.from(logWithUppercaseExceptionMarker, 'utf8')); archive.writeZip(archivePath); new DiagnosticsAnalyzer(loggerStub).analyze(temporaryDirectory, ''); const reportPath = path.join(temporaryDirectory, 'diagnostics-analysis.txt'); const reportText = fs.readFileSync(reportPath, 'utf8'); expect(reportText).to.include('ERROR EXCEPTION <main> CryptoStatic: key loading failed'); expect(reportText).to.include('com.swirlds.platform.crypto.KeyLoadingException: No certificate found for nodeId 2 [purpose = AGREEMENT ]'); }); it('includes the preceding timestamped ERROR line for hgcaa.log exceptions', () => { const hgcaaSample = `2026-03-27 16:46:55.329 INFO 401 WrapsHistoryProver - Considering publication of WRAPS R1 output on construction #2 2026-03-27 16:46:55.330 ERROR 351 HandleWorkflow - Possibly CATASTROPHIC failure trying to reconcile TSS state java.lang.NullPointerException \tat java.base/java.util.Objects.requireNonNull(Objects.java:220) \tat com.hedera.node.app.history.impl.WrapsHistoryProver.publishIfNeeded(WrapsHistoryProver.java:407) 2026-03-27 16:46:55.390 INFO 401 WrapsHistoryProver - Considering publication of WRAPS R1 output on construction #2 `; const archivePath = path.join(temporaryDirectory, 'network-node2-0-log-config.zip'); const archive = new AdmZip(); archive.addFile('output/hgcaa.log', Buffer.from(hgcaaSample, 'utf8')); archive.writeZip(archivePath); new DiagnosticsAnalyzer(loggerStub).analyze(temporaryDirectory, ''); const reportPath = path.join(temporaryDirectory, 'diagnostics-analysis.txt'); const reportText = fs.readFileSync(reportPath, 'utf8'); expect(reportText).to.include('Exception detected in hgcaa.log'); expect(reportText).to.include('2026-03-27 16:46:55.330 ERROR 351 HandleWorkflow - Possibly CATASTROPHIC failure trying to reconcile TSS state'); expect(reportText).to.include('java.lang.NullPointerException'); }); it('detects image-pull failures from YAML pod describe content', () => { const describeSample = `pod: status: phase: Running events: - lastTimestamp: 2026-03-27T17:10:35.000Z message: 'Failed to pull image "curlimages/curl:8.9.1": failed to pull and unpack image "docker.io/curlimages/curl:8.9.1": failed to copy: httpReadSeeker: failed open: unexpected status code https://registry-1.docker.io/v2/curlimages/curl/manifests/sha256:78c8580bd9480f0d2527c0b781eeb9ffa00f3795f882e625f576aa51af8f4ad5: 429 Too Many Requests - Server message: toomanyrequests: You have reached your unauthenticated pull rate limit. https://www.docker.com/increase-rate-limit' reason: Failed - lastTimestamp: 2026-03-27T17:12:57.000Z message: "Error: ErrImagePull" reason: Failed - lastTimestamp: 2026-03-27T17:24:58.000Z message: "Error: ImagePullBackOff" reason: Failed `; const describeDirectory = path.join(temporaryDirectory, 'hiero-components-logs', 'kind-solo-e2e'); fs.mkdirSync(describeDirectory, { recursive: true }); fs.writeFileSync(path.join(describeDirectory, 'network-node1-0.describe.txt'), describeSample, 'utf8'); new DiagnosticsAnalyzer(loggerStub).analyze(temporaryDirectory, ''); const reportPath = path.join(temporaryDirectory, 'diagnostics-analysis.txt'); const reportText = fs.readFileSync(reportPath, 'utf8'); expect(reportText).to.include('Image pull failure detected for pod network-node1-0'); expect(reportText).to.include('line 10: 429 Too Many Requests - Server message: toomanyrequests: You have reached'); expect(reportText).to.include('message: "Error: ErrImagePull"'); expect(reportText).to.include('message: "Error: ImagePullBackOff"'); expect(reportText).to.not.include('Pod not ready/running: network-node1-0'); }); }); //# sourceMappingURL=diagnostics-analyzer.test.js.map