@hashgraph/solo
Version:
An opinionated CLI tool to deploy and manage private Hedera Networks.
151 lines • 8.63 kB
JavaScript
// SPDX-License-Identifier: Apache-2.0
import { expect } from 'chai';
import { after, before, describe, it } from 'mocha';
import each from 'mocha-each';
import fs from 'node:fs';
import path from 'node:path';
import { KindDependencyManager } from '../../../../../src/core/dependency-managers/index.js';
import { getTestCacheDirectory, getTemporaryDirectory } from '../../../../test-utility.js';
import * as version from '../../../../../version.js';
import { PathEx } from '../../../../../src/business/utils/path-ex.js';
import sinon from 'sinon';
import { OperatingSystem } from '../../../../../src/business/utils/operating-system.js';
import { InjectTokens } from '../../../../../src/core/dependency-injection/inject-tokens.js';
import { container } from 'tsyringe-neo';
import { platform } from 'node:process';
import * as constants from '../../../../../src/core/constants.js';
describe('KindDependencyManager', () => {
const installationDirectory = PathEx.join(getTemporaryDirectory(), 'bin');
const originalPlatform = platform;
const originalInstallationDirectory = container.resolve(InjectTokens.KindInstallationDirectory);
let sandbox;
before(() => {
fs.mkdirSync(installationDirectory);
sandbox = sinon.createSandbox();
});
after(() => {
if (fs.existsSync(installationDirectory)) {
fs.rmSync(installationDirectory, { recursive: true });
}
});
afterEach(() => {
container.register(InjectTokens.OsPlatform, { useValue: originalPlatform });
container.register(InjectTokens.KindInstallationDirectory, { useValue: originalInstallationDirectory });
sandbox.restore();
});
it('should return kind version', () => {
const kindDependencyManager = new KindDependencyManager(undefined, installationDirectory, undefined, undefined);
expect(kindDependencyManager.getRequiredVersion()).to.equal(version.KIND_VERSION);
});
it('should be able to check when kind not installed', () => {
const kindDependencyManager = new KindDependencyManager(undefined, installationDirectory, undefined, undefined);
expect(kindDependencyManager.isInstalledLocally()).not.to.be.ok;
});
it('should be able to check when kind is installed', async () => {
const kindDependencyManager = new KindDependencyManager(undefined, installationDirectory, undefined, undefined);
// Create the local executable file for testing
const localPath = PathEx.join(installationDirectory, constants.KIND);
fs.writeFileSync(localPath, '');
expect(kindDependencyManager.isInstalledLocally()).to.be.ok;
});
describe('when kind is installed globally', () => {
let kindDependencyManager;
let runStub;
let existsSyncStub;
beforeEach(() => {
kindDependencyManager = new KindDependencyManager(undefined, installationDirectory, process.arch, undefined);
kindDependencyManager.uninstallLocal();
});
afterEach(() => {
sandbox.restore();
container.register(InjectTokens.OsPlatform, { useValue: originalPlatform });
container.register(InjectTokens.KindInstallationDirectory, { useValue: originalInstallationDirectory });
});
it('should prefer the global installation if it meets the requirements', async () => {
const fakeGlobalBinDirectory = '/test-solo-global-bin';
const fakeGlobalKindPath = `${fakeGlobalBinDirectory}/kind`;
const originalPath = process.env.PATH ?? '';
process.env.PATH = `${fakeGlobalBinDirectory}${path.delimiter}${originalPath}`;
sandbox.stub(fs, 'accessSync').callsFake((filePath) => {
if (String(filePath) === fakeGlobalKindPath) {
return;
}
throw Object.assign(new Error('ENOENT'), { code: 'ENOENT' });
});
runStub = sandbox.stub(kindDependencyManager, 'run');
runStub.withArgs(`"${fakeGlobalKindPath}" --version`).resolves([`kind version ${version.KIND_VERSION}`]);
existsSyncStub = sandbox.stub(fs, 'existsSync').returns(true);
existsSyncStub.withArgs(`${installationDirectory}/kind`).returns(false);
try {
// @ts-expect-error TS2341: Property isInstalledGloballyAndMeetsRequirements is private
const result = await kindDependencyManager.isInstalledGloballyAndMeetsRequirements();
expect(result).to.be.true;
expect(await kindDependencyManager.install(getTestCacheDirectory())).to.be.true;
// Should return global path since it meets requirements
expect(await kindDependencyManager.getExecutable()).to.equal(constants.KIND);
}
finally {
process.env.PATH = originalPath;
}
});
it('should install kind locally if the global installation does not meet the requirements', async () => {
const temporaryDirectory = getTemporaryDirectory();
container.register(InjectTokens.KindInstallationDirectory, { useValue: temporaryDirectory });
// Stub accessSync so the native PATH scan finds no global kind installation.
sandbox.stub(fs, 'accessSync').throws(Object.assign(new Error('ENOENT'), { code: 'ENOENT' }));
expect(await kindDependencyManager.install(temporaryDirectory)).to.be.true;
expect(fs.existsSync(PathEx.join(temporaryDirectory, constants.KIND))).to.be.ok;
expect(await kindDependencyManager.getExecutable()).to.equal(constants.KIND);
});
});
describe('Kind Installation Tests', () => {
afterEach(() => {
container.register(InjectTokens.OsPlatform, { useValue: originalPlatform });
container.register(InjectTokens.KindInstallationDirectory, { useValue: originalInstallationDirectory });
sandbox.restore();
});
each([
[OperatingSystem.OS_LINUX, 'x64'],
[OperatingSystem.OS_LINUX, 'amd64'],
[OperatingSystem.OS_WIN32, 'amd64'],
]).it('should be able to install kind base on %s and %s', async (osPlatform, osArch) => {
container.register(InjectTokens.OsPlatform, { useValue: osPlatform });
container.register(InjectTokens.KindInstallationDirectory, { useValue: installationDirectory });
const kindDependencyManager = new KindDependencyManager(undefined, installationDirectory, osArch, undefined);
kindDependencyManager.uninstallLocal();
expect(kindDependencyManager.isInstalledLocally()).not.to.be.ok;
// Stub accessSync so the native PATH scan finds no global kind installation.
sandbox.stub(fs, 'accessSync').throws(Object.assign(new Error('ENOENT'), { code: 'ENOENT' }));
expect(await kindDependencyManager.install(getTestCacheDirectory())).to.be.true;
expect(kindDependencyManager.isInstalledLocally()).to.be.ok;
fs.rmSync(installationDirectory, { recursive: true });
});
});
describe('KindDependencyManager system methods', () => {
let kindDependencyManager;
let runStub;
beforeEach(() => {
kindDependencyManager = new KindDependencyManager(undefined, installationDirectory, process.arch, undefined);
runStub = sandbox.stub(kindDependencyManager, 'run');
});
afterEach(() => {
sandbox.restore();
});
it('installationMeetsRequirements returns false on error', async () => {
runStub.rejects(new Error('fail'));
const path = await kindDependencyManager.getExecutable();
await expect(kindDependencyManager.installationMeetsRequirements(path)).to.eventually.be.false;
});
it('installationMeetsRequirements returns false on invalid version', async () => {
runStub.resolves(['not a version']);
const path = await kindDependencyManager.getExecutable();
await expect(kindDependencyManager.installationMeetsRequirements(path)).to.eventually.be.false;
});
it('installationMeetsRequirements returns false on lower than required version', async () => {
runStub.resolves(['v0.0.5']);
const path = await kindDependencyManager.getExecutable();
expect(await kindDependencyManager.installationMeetsRequirements(path)).to.be.false;
});
});
});
//# sourceMappingURL=kind-dependency-manager.test.js.map