UNPKG

@hashgraph/solo

Version:

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

101 lines 4.98 kB
// SPDX-License-Identifier: Apache-2.0 import 'sinon-chai'; import sinon from 'sinon'; import { expect } from 'chai'; import { describe, it, beforeEach, afterEach } from 'mocha'; import { ShellRunner } from '../../../src/core/shell-runner.js'; import { ChildProcess } from 'node:child_process'; import { Readable } from 'node:stream'; import { Duration } from '../../../src/core/time/duration.js'; import { SoloPinoLogger } from '../../../src/core/logging/solo-pino-logger.js'; import { OperatingSystem } from '../../../src/business/utils/operating-system.js'; describe('ShellRunner', () => { let shellRunner, loggerDebugStub, loggerInfoStub, childProcessSpy, readableSpy; beforeEach(() => { shellRunner = new ShellRunner(); // Spy on methods loggerDebugStub = sinon.stub(SoloPinoLogger.prototype, 'debug'); loggerInfoStub = sinon.stub(SoloPinoLogger.prototype, 'info'); childProcessSpy = sinon.spy(ChildProcess.prototype, 'on'); readableSpy = sinon.spy(Readable.prototype, 'on'); }); afterEach(() => sinon.restore()); it('should run command', async () => { const commandToRun = OperatingSystem.isWin32() ? 'dir' : 'ls -l'; await shellRunner.run(commandToRun); loggerInfoStub.withArgs(`Executing command: '${commandToRun}'`).onFirstCall(); loggerDebugStub.withArgs(`Finished executing: '${commandToRun}'`, sinon.match.any).onFirstCall(); expect(loggerDebugStub).to.have.been.calledOnce; expect(loggerInfoStub).to.have.been.calledOnce; expect(readableSpy).to.have.been.calledWith('data', sinon.match.any); expect(childProcessSpy).to.have.been.calledWith('exit', sinon.match.any); }).timeout(Duration.ofSeconds(10).toMillis()); it('should complete successfully within timeout', async () => { const result = await shellRunner.run('echo hello', [], false, false, {}, 10_000); expect(result).to.include('hello'); }).timeout(Duration.ofSeconds(15).toMillis()); it('should reject with timeout error when command exceeds timeoutMs', async () => { // Use a node one-liner as a cross-platform long-running command: works on all platforms // (Windows `timeout /t N` exits immediately when stdin is non-interactive in CI environments) const commandToRun = 'node -e "setTimeout(()=>{}, 10000)"'; const timeoutMs = 500; await expect(shellRunner.run(commandToRun, [], false, false, {}, timeoutMs)).to.be.rejectedWith(`Command timed out after ${timeoutMs}ms`); }).timeout(Duration.ofSeconds(10).toMillis()); describe('redactArguments', () => { it('should redact --password and its value', () => { const arguments_ = ['--password', 'mySecret']; const redacted = ShellRunner.redactArguments(arguments_); expect(redacted).to.deep.equal(['--password', '******']); }); it('should redact -p and its value', () => { const arguments_ = ['-p', 'mySecret']; const redacted = ShellRunner.redactArguments(arguments_); expect(redacted).to.deep.equal(['-p', '******']); }); it('should redact sensitive key=value pairs', () => { const arguments_ = [ '--set', 'global.password=mySecret', 'some-token=abc', 'my_key=123', 'normal=value', ]; const redacted = ShellRunner.redactArguments(arguments_); expect(redacted).to.deep.equal([ '--set', 'global.password=******', 'some-token=******', 'my_key=******', 'normal=value', ]); }); it('should not modify unrelated arguments', () => { const arguments_ = ['--set', 'global.name=myApp', '--values', 'values.yaml']; const redacted = ShellRunner.redactArguments(arguments_); expect(redacted).to.deep.equal(['--set', 'global.name=myApp', '--values', 'values.yaml']); }); it('should redact composite arguments', () => { const arguments_ = [ 'helm', 'upgrade', '--values values.yaml --set foo.bar=false --set foo.privateKey=0x123456 --set foo.bar.ALL_CAPS_KEY=0x123456 --set foo.bar.foo.bar.password=123456', ]; const redacted = ShellRunner.redactArguments(arguments_); expect(redacted).to.deep.equal([ 'helm', 'upgrade', '--values', 'values.yaml', '--set', 'foo.bar=false', '--set', 'foo.privateKey=******', '--set', 'foo.bar.ALL_CAPS_KEY=******', '--set', 'foo.bar.foo.bar.password=******', ]); }); }); }); //# sourceMappingURL=shell-runner.test.js.map