Skip to content

Commit

Permalink
add update cli coverage
Browse files Browse the repository at this point in the history
  • Loading branch information
hugomontero committed May 20, 2024
1 parent 4783c05 commit fb2b788
Show file tree
Hide file tree
Showing 2 changed files with 285 additions and 6 deletions.
12 changes: 6 additions & 6 deletions src/cmd/update-cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,13 @@ class UpdateCliCommand {
logAndReject(error, reject, version) {
const baseMessage = 'We were unable to check for updates';
const message = version ? `${baseMessage}: Version ${version} not found` : `${baseMessage} Please try again later`;
log.error(error);
log.error(error.message || error);
reject(message);
}

async downloadCLI(manifest) {
async downloadCLI(manifest, _os = os) {
try {
const { url, sha256: expectedHash } = this.getBuildDetailsFromManifest(manifest);
const { url, sha256: expectedHash } = this.getBuildDetailsFromManifest(manifest, _os);
const fileName = url.split('/').pop();
const fileNameWithoutLastExtension = path.basename(fileName, path.extname(fileName));
const filePath = path.join(os.tmpdir(), fileNameWithoutLastExtension);
Expand Down Expand Up @@ -153,9 +153,9 @@ class UpdateCliCommand {
});
}

getBuildDetailsFromManifest(manifest) {
const platform = os.platform();
let arch = os.arch();
getBuildDetailsFromManifest(manifest, _os = os) {
const platform = _os.platform();
let arch = _os.arch();
const platformKey = platform;
const archKey = arch;
const platformManifest = manifest.builds && manifest.builds[platformKey];
Expand Down
279 changes: 279 additions & 0 deletions src/cmd/update-cli.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,279 @@
const { expect, sinon } = require('../../test/setup');
const UpdateCLI = require('./update-cli');
const settings = require('../../settings');
const fs = require('fs-extra');
const path = require('path');
const nock = require('nock');
const log = require('../lib/log');
const pkg = require('../../package');
const os = require('os');

describe('Update CLI Command', () => {
beforeEach(() => {
sinon.stub(settings, 'saveProfileData');
settings.profile_json = { enableUpdates: false };
});

afterEach(() => {
sinon.restore();
});

describe('updateCli', () => {
beforeEach(() => {
sinon.stub(log, 'info');
});
afterEach(() => {
sinon.restore();
});
it('skip update if the CLI is up to date', async () => {
const update = new UpdateCLI();
const manifest = { version: '1.2.3' };
sinon.stub(update, 'downloadManifest').resolves(manifest);
sinon.stub(pkg, 'version').value('1.2.3');
await update.updateCli();
expect(log.info).to.have.been.calledWith('CLI is already up to date');
});

it('update the CLI to the latest version', async () => {
const update = new UpdateCLI();
const manifest = { version: '1.2.3' };
sinon.stub(update, 'downloadManifest').resolves(manifest);
sinon.stub(pkg, 'version').value('1.2.2');
sinon.stub(update, 'downloadCLI').resolves('/path/to/cli/particle-cli-1.2.3-linux-x64.gz');
sinon.stub(update, 'replaceCLI').resolves();
await update.updateCli();
expect(log.info).to.have.been.calledWith('CLI updated successfully');
expect(settings.saveProfileData).to.have.property('callCount', 1);
expect(settings.profile_json.last_version_check).to.gt(0);
expect(update.downloadCLI).to.have.been.calledWith(manifest);
expect(update.replaceCLI).to.have.been.calledWith('/path/to/cli/particle-cli-1.2.3-linux-x64.gz');
});
});
describe('enable/disable updates', () => {
it('should enable updates', async () => {
const update = new UpdateCLI();
await update.update({ 'enable-updates': true });
expect(settings.saveProfileData).to.have.property('callCount', 1);
expect(settings.profile_json.enableUpdates).to.equal(true);
});

it('should disable updates', async () => {
settings.profile_json.enableUpdates = true;
const update = new UpdateCLI();
await update.update({ 'disable-updates': true });
expect(settings.saveProfileData).to.have.property('callCount', 1);
expect(settings.profile_json.enableUpdates).to.equal(false);
});
});

describe('downloadManifest', () => {
const manifestData = JSON.stringify({
'version': '4.1.0',
'builds': {
'linux': { 'x64': { 'url': 'https://binaries.particle.io/cli/manifest/4.1.0/particle-cli-4.1.0-linux-x64.tar.gz' }
}
}
});
beforeEach(() => {
sinon.stub(log, 'info');
sinon.stub(log, 'error');
});
afterEach(() => {
sinon.restore();
});
it('downloads the latest manifest version', async () => {
nock('https://binaries.particle.io')
.intercept('/particle-cli/manifest.json', 'GET')
.reply(200, manifestData);
const update = new UpdateCLI();
const manifest = await update.downloadManifest();
expect(manifest).to.deep.equal(JSON.parse(manifestData));
});

it('downloads a specific manifest version', async () => {
nock('https://binaries.particle.io')
.intercept('/particle-cli/manifest-4.1.0.json', 'GET')
.reply(200, manifestData);
const update = new UpdateCLI();
const manifest = await update.downloadManifest('4.1.0');
expect(manifest).to.deep.equal(JSON.parse(manifestData));
});
it('throws an error if the manifest download fails', async () => {
nock('https://binaries.particle.io')
.intercept('/particle-cli/manifest.json', 'GET')
.reply(404);
const update = new UpdateCLI();
await expect(update.downloadManifest()).to.be.rejectedWith('We were unable to check for updates Please try again later');
expect(log.error).to.have.been.calledWith('Failed to download manifest: Status Code 404');
});
it('throws an error if the manifest is invalid', async () => {
nock('https://binaries.particle.io')
.intercept('/particle-cli/manifest.json', 'GET')
.reply(200, 'invalid json');
const update = new UpdateCLI();
await expect(update.downloadManifest()).to.be.rejectedWith('We were unable to check for updates Please try again later');
expect(log.error).to.have.been.calledWith('Unexpected token i in JSON at position 0');
});
});

describe('downloadCLI', () => {
beforeEach(() => {
sinon.stub(log, 'error');
sinon.stub(log, 'debug');
});
afterEach(() => {
sinon.restore();
});
it('downloads the CLI binary', async () => {
const update = new UpdateCLI();
const buildDetails = {
url: 'https://binaries.particle.io/cli/manifest/1.2.3/particle-cli-1.2.3-linux-x64.gz',
sha256: '9cb63cb779e8c571db3199b783a36cc43cd9e7c076beeb496c39e9cc06196dc5'
};
sinon.stub(update, 'getBuildDetailsFromManifest').returns(buildDetails);
sinon.stub(update, 'unzipFile').resolves('/path/to/cli/particle-cli-1.2.3-linux-x64.gz');

nock('https://binaries.particle.io')
.intercept('/cli/manifest/1.2.3/particle-cli-1.2.3-linux-x64.gz', 'GET')
.reply(200, 'binary data');
const cliPath = await update.downloadCLI(buildDetails);
expect(cliPath).to.contain('particle-cli-1.2.3-linux-x64.gz');
expect(log.error).to.not.have.been.called;
});

it('throws an error if the CLI download fails', async () => {
const update = new UpdateCLI();
const buildDetails = {
url: 'https://binaries.particle.io/cli/manifest/1.2.3/particle-cli-1.2.3-linux-x64.gz',
sha256: '9cb63cb779e8c571db3199b783a36cc43cd9e7c076beeb496c39e9cc06196dc5'
};
sinon.stub(update, 'getBuildDetailsFromManifest').returns(buildDetails);
sinon.stub(update, 'unzipFile').resolves('/path/to/cli/particle-cli-1.2.3-linux-x64.gz');

nock('https://binaries.particle.io')
.intercept('/cli/manifest/1.2.3/particle-cli-1.2.3-linux-x64.gz', 'GET')
.reply(404);
await expect(update.downloadCLI(buildDetails)).to.be.rejectedWith('Failed to download or verify the CLI, please try again later');
expect(log.debug).to.have.been.calledWith('Failed during download or verification: Error: No file found to download');
});

it('throws an error if the CLI hash does not match', async () => {
const update = new UpdateCLI();
const buildDetails = {
url: 'https://binaries.particle.io/cli/manifest/1.2.3/particle-cli-1.2.3-linux-x64.gz',
sha256: 'invalid-hash'
};
sinon.stub(update, 'getBuildDetailsFromManifest').returns(buildDetails);
sinon.stub(update, 'unzipFile').resolves('/path/to/cli/particle-cli-1.2.3-linux-x64.gz');

nock('https://binaries.particle.io')
.intercept('/cli/manifest/1.2.3/particle-cli-1.2.3-linux-x64.gz', 'GET')
.reply(200, 'binary data');
await expect(update.downloadCLI(buildDetails)).to.be.rejectedWith('Failed to download or verify the CLI, please try again later');
expect(log.debug).to.have.been.calledWith('Failed during download or verification: Error: Hash mismatch');
});
});

describe('getBuildDetailsFromManifest', () => {
const manifest = {
builds: {
linux: {
x64: {
url: 'https://binaries.particle.io/cli/manifest/1.2.3/particle-cli-1.2.3-linux-x64.gz'
}
},
darwin: {
x64: {
url: 'https://binaries.particle.io/cli/manifest/1.2.3/particle-cli-1.2.3-darwin-x64.gz'
},
arm64: {
url: 'https://binaries.particle.io/cli/manifest/1.2.3/particle-cli-1.2.3-darwin-arm64.gz'
}
},
win32: {
x64: {
url: 'https://binaries.particle.io/cli/manifest/1.2.3/particle-cli-1.2.3-win32-x64.gz'
}
}
}
};
const _os = {
platform: sinon.stub().returns('darwin'),
arch: sinon.stub().returns('unknown')
};
afterEach(() => {
sinon.restore();
});
it('returns the manifest details from current platform/architecture', () => {
const update = new UpdateCLI();
const buildDetails = update.getBuildDetailsFromManifest(manifest);
expect(buildDetails).to.equal(manifest.builds[os.platform()][os.arch()]);
});

it('throws an error if the platform is not found in the manifest', () => {
const update = new UpdateCLI();
expect(() => update.getBuildDetailsFromManifest(manifest, _os)).to.throw('No CLI build found for darwin unknown');
});

it('throws an error if the platform is not found in the manifest', () => {
const update = new UpdateCLI();
expect(() => update.getBuildDetailsFromManifest({})).to.throw(`No CLI build found for ${os.platform()} ${os.arch()}`);
});

});

describe('replaceCLI', () => {
let processExecPathTmp;
beforeEach(() => {
processExecPathTmp = process.execPath;
process.execPath = path.join(path.sep,'usr','bin','particle');
});

afterEach(() => {
process.execPath = processExecPathTmp;
});


it('replace the cli for a new one', async () => {
const update = new UpdateCLI();
const newCliPath = path.join(path.sep, 'path', 'to', 'new', 'cli');
const execPath = path.join(path.sep, 'usr', 'bin', 'particle');
const binPath = path.join(path.sep, 'usr', 'bin');
const fileName = 'particle';


const getBinaryPathStub = sinon.spy(update, 'getBinaryPath');
const moveStub = sinon.stub(fs, 'move').resolves();
const chmodStub = sinon.stub(fs, 'chmod').resolves();
const cliPath = execPath;
const oldCliPath = path.join(binPath, `${fileName}.old`);
await update.replaceCLI(newCliPath);
expect(getBinaryPathStub).to.have.been.calledOnce;
expect(moveStub).to.have.been.calledWith(execPath, oldCliPath, { overwrite: true });
expect(moveStub).to.have.been.calledWith(newCliPath, cliPath);
expect(chmodStub).to.have.been.calledWith(cliPath, 0o755);
});
});

describe('configureProfileSettings', () => {
it ('disable updates if version is provided', async() => {
const update = new UpdateCLI();
settings.profile_json.last_version_check = 0;
settings.profile_json.enableUpdates = true;
await update.configureProfileSettings('1.2.3');
expect(settings.profile_json.last_version_check).to.gt(0);
expect(settings.profile_json.enableUpdates).to.equal(false);
expect(settings.saveProfileData).to.have.property('callCount', 2);
});

it ('update last_version_check but will not change enableUpdates data if version is undefined', async () => {
const update = new UpdateCLI();
settings.profile_json.last_version_check = 0;
settings.profile_json.enableUpdates = true;
await update.configureProfileSettings();
expect(settings.profile_json.last_version_check).to.gt(0);
expect(settings.profile_json.enableUpdates).to.equal(true);
expect(settings.saveProfileData).to.have.property('callCount', 1);
});
});
});

0 comments on commit fb2b788

Please sign in to comment.