mirror of
https://github.com/ershisan99/go-npm.git
synced 2026-02-02 12:35:14 +00:00
ADD unit tests, split and refactor source code
This commit is contained in:
91
__test__/actions/install.spec.js
Normal file
91
__test__/actions/install.spec.js
Normal file
@@ -0,0 +1,91 @@
|
||||
const { EventEmitter } = require('events');
|
||||
const request = require('request');
|
||||
const common = require('../../src/common');
|
||||
const install = require('../../src/actions/install');
|
||||
const move = require('../../src/assets/move');
|
||||
const untar = require('../../src/assets/untar');
|
||||
const verifyAndPlaceCallback = require('../../src/assets/binary');
|
||||
|
||||
jest.mock('fs');
|
||||
jest.mock('mkdirp');
|
||||
jest.mock('request');
|
||||
jest.mock('../../src/common');
|
||||
jest.mock('../../src/assets/move');
|
||||
jest.mock('../../src/assets/untar');
|
||||
jest.mock('../../src/assets/binary');
|
||||
|
||||
describe('install()', () => {
|
||||
|
||||
let callback, requestEvents;
|
||||
|
||||
beforeEach(() => {
|
||||
|
||||
callback = jest.fn();
|
||||
|
||||
requestEvents = new EventEmitter();
|
||||
});
|
||||
|
||||
it('should call callback with error if package.json did not return value' , () => {
|
||||
common.parsePackageJson.mockReturnValueOnce(undefined);
|
||||
|
||||
install(callback);
|
||||
|
||||
expect(callback).toHaveBeenCalledWith('Invalid inputs');
|
||||
});
|
||||
|
||||
it('should call callback with error on request error', () => {
|
||||
request.mockReturnValueOnce(requestEvents);
|
||||
common.parsePackageJson.mockReturnValueOnce({ url: 'http://url' });
|
||||
|
||||
install(callback);
|
||||
|
||||
requestEvents.emit('error');
|
||||
|
||||
expect(callback).toHaveBeenCalledWith('Error downloading from URL: http://url');
|
||||
});
|
||||
|
||||
it('should call callback with error on response with status code different than 200', () => {
|
||||
request.mockReturnValueOnce(requestEvents);
|
||||
common.parsePackageJson.mockReturnValueOnce({ url: 'http://url' });
|
||||
|
||||
install(callback);
|
||||
|
||||
requestEvents.emit('response', { statusCode: 404 });
|
||||
|
||||
expect(callback).toHaveBeenCalledWith('Error downloading binary. HTTP Status Code: 404');
|
||||
});
|
||||
|
||||
it('should pick move strategy if url is an uncompressed binary', () => {
|
||||
request.mockReturnValueOnce(requestEvents);
|
||||
common.parsePackageJson.mockReturnValueOnce({ url: 'http://url' });
|
||||
|
||||
install(callback);
|
||||
|
||||
requestEvents.emit('response', { statusCode: 200 });
|
||||
|
||||
expect(move).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should pick untar strategy if url ends with .tar.gz', () => {
|
||||
request.mockReturnValueOnce(requestEvents);
|
||||
common.parsePackageJson.mockReturnValueOnce({ url: 'http://url.tar.gz' });
|
||||
|
||||
install(callback);
|
||||
|
||||
requestEvents.emit('response', { statusCode: 200 });
|
||||
|
||||
expect(untar).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call verifyAndPlaceCallback on success', () => {
|
||||
request.mockReturnValueOnce(requestEvents);
|
||||
common.parsePackageJson.mockReturnValueOnce({ url: 'http://url', binName: 'command', binPath: './bin' });
|
||||
move.mockImplementationOnce(({ onSuccess }) => onSuccess());
|
||||
|
||||
install(callback);
|
||||
|
||||
requestEvents.emit('response', { statusCode: 200 });
|
||||
|
||||
expect(verifyAndPlaceCallback).toHaveBeenCalledWith('command', './bin', callback);
|
||||
});
|
||||
});
|
||||
59
__test__/actions/uninstall.spec.js
Normal file
59
__test__/actions/uninstall.spec.js
Normal file
@@ -0,0 +1,59 @@
|
||||
const fs = require('fs');
|
||||
const common = require('../../src/common');
|
||||
const uninstall = require('../../src/actions/uninstall');
|
||||
|
||||
jest.mock('fs');
|
||||
jest.mock('../../src/common');
|
||||
|
||||
describe('uninstall()', () => {
|
||||
|
||||
let callback;
|
||||
|
||||
beforeEach(() => {
|
||||
|
||||
callback = jest.fn();
|
||||
|
||||
common.parsePackageJson.mockReturnValueOnce({ binName: 'command' });
|
||||
});
|
||||
|
||||
it('should call callback with error if binary not found', () => {
|
||||
const error = new Error();
|
||||
|
||||
common.getInstallationPath.mockImplementationOnce((cb) => cb(error));
|
||||
|
||||
uninstall(callback);
|
||||
|
||||
expect(callback).toHaveBeenCalledWith(error);
|
||||
});
|
||||
|
||||
it('should call unlinkSync with binary and installation path', () => {
|
||||
|
||||
common.getInstallationPath.mockImplementationOnce((cb) => cb(null, './bin'));
|
||||
|
||||
uninstall(callback);
|
||||
|
||||
expect(fs.unlinkSync).toHaveBeenCalledWith('bin/command');
|
||||
});
|
||||
|
||||
it('should call callback on success', () => {
|
||||
|
||||
common.getInstallationPath.mockImplementationOnce((cb) => cb(null, './bin'));
|
||||
|
||||
uninstall(callback);
|
||||
|
||||
expect(callback).toHaveBeenCalledWith(null);
|
||||
});
|
||||
|
||||
it('should call callback regardless of errors on unlink', () => {
|
||||
|
||||
common.getInstallationPath.mockImplementationOnce((cb) => cb(null, './bin'));
|
||||
|
||||
fs.unlinkSync.mockImplementationOnce(() => {
|
||||
throw new Error();
|
||||
});
|
||||
|
||||
uninstall(callback);
|
||||
|
||||
expect(callback).toHaveBeenCalledWith(null);
|
||||
});
|
||||
});
|
||||
51
__test__/assets/binary.spec.js
Normal file
51
__test__/assets/binary.spec.js
Normal file
@@ -0,0 +1,51 @@
|
||||
const fs = require('fs');
|
||||
const common = require('../../src/common');
|
||||
const verifyAndPlaceBinary = require('../../src/assets/binary');
|
||||
|
||||
jest.mock('fs');
|
||||
jest.mock('../../src/common');
|
||||
|
||||
describe('verifyAndPlaceBinary()', () => {
|
||||
let callback;
|
||||
|
||||
beforeEach(() => {
|
||||
callback = jest.fn();
|
||||
});
|
||||
|
||||
it('should call callback with error if binary downloaded differs from config', () => {
|
||||
fs.existsSync.mockReturnValueOnce(false);
|
||||
|
||||
verifyAndPlaceBinary('command', './bin', callback);
|
||||
|
||||
expect(callback).toHaveBeenCalledWith('Downloaded binary does not contain the binary specified in configuration - command');
|
||||
});
|
||||
|
||||
it('should call callback with error if installation path cannot be found', () => {
|
||||
const error = new Error();
|
||||
|
||||
fs.existsSync.mockReturnValueOnce(true);
|
||||
common.getInstallationPath.mockImplementationOnce((cb) => cb(error));
|
||||
|
||||
verifyAndPlaceBinary('command', './bin', callback);
|
||||
|
||||
expect(callback).toHaveBeenCalledWith(error);
|
||||
});
|
||||
|
||||
it('should call callback with null on success', () => {
|
||||
fs.existsSync.mockReturnValueOnce(true);
|
||||
common.getInstallationPath.mockImplementationOnce((cb) => cb(null, '/usr/local/bin'));
|
||||
|
||||
verifyAndPlaceBinary('command', './bin', callback);
|
||||
|
||||
expect(callback).toHaveBeenCalledWith(null);
|
||||
});
|
||||
|
||||
it('should move the binary to installation directory', () => {
|
||||
fs.existsSync.mockReturnValueOnce(true);
|
||||
common.getInstallationPath.mockImplementationOnce((cb) => cb(null, '/usr/local/bin'));
|
||||
|
||||
verifyAndPlaceBinary('command', './bin', callback);
|
||||
|
||||
expect(fs.renameSync).toHaveBeenCalledWith('bin/command', '/usr/local/bin/command');
|
||||
});
|
||||
});
|
||||
49
__test__/assets/move.spec.js
Normal file
49
__test__/assets/move.spec.js
Normal file
@@ -0,0 +1,49 @@
|
||||
const { EventEmitter } = require('events');
|
||||
const fs = require('fs');
|
||||
const move = require('../../src/assets/move');
|
||||
|
||||
jest.mock('fs');
|
||||
|
||||
describe('move()', () => {
|
||||
|
||||
let streamEvents, pipe, onSuccess, onError;
|
||||
|
||||
beforeEach(() => {
|
||||
|
||||
streamEvents = new EventEmitter();
|
||||
|
||||
pipe = jest.fn();
|
||||
onSuccess = jest.fn();
|
||||
onError = jest.fn();
|
||||
createWriteStream = jest.fn();
|
||||
|
||||
fs.createWriteStream.mockReturnValueOnce(streamEvents);
|
||||
});
|
||||
|
||||
it('should download resource to given binPath', () => {
|
||||
|
||||
move({ opts: { binPath: './bin', binName: 'command' }, req: { pipe }, onSuccess, onError });
|
||||
|
||||
expect(fs.createWriteStream).toHaveBeenCalledWith('bin/command');
|
||||
});
|
||||
|
||||
it('should call onSuccess on stream closed', () => {
|
||||
|
||||
move({ opts: { binPath: './bin', binName: 'command' }, req: { pipe }, onSuccess, onError });
|
||||
|
||||
streamEvents.emit('close');
|
||||
|
||||
expect(onSuccess).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call onError with error on write stream error', () => {
|
||||
|
||||
const error = new Error();
|
||||
|
||||
move({ opts: { binPath: './bin', binName: 'command' }, req: { pipe }, onSuccess, onError });
|
||||
|
||||
streamEvents.emit('error', error);
|
||||
|
||||
expect(onError).toHaveBeenCalledWith(error);
|
||||
});
|
||||
});
|
||||
65
__test__/assets/untar.spec.js
Normal file
65
__test__/assets/untar.spec.js
Normal file
@@ -0,0 +1,65 @@
|
||||
const { EventEmitter } = require('events');
|
||||
const zlib = require('zlib');
|
||||
const tar = require('tar');
|
||||
const untar = require('../../src/assets/untar');
|
||||
|
||||
jest.mock('zlib');
|
||||
jest.mock('tar', () => ({
|
||||
Extract: jest.fn()
|
||||
}));
|
||||
|
||||
describe('untar()', () => {
|
||||
|
||||
let ungzEvents, untarEvents, pipe, onSuccess, onError;
|
||||
|
||||
beforeEach(() => {
|
||||
ungzEvents = new EventEmitter();
|
||||
untarEvents = new EventEmitter();
|
||||
|
||||
pipe = jest.fn();
|
||||
onSuccess = jest.fn();
|
||||
onError = jest.fn();
|
||||
|
||||
pipe.mockReturnValueOnce({ pipe });
|
||||
tar.Extract.mockReturnValueOnce(untarEvents);
|
||||
zlib.createGunzip.mockReturnValueOnce(ungzEvents);
|
||||
});
|
||||
|
||||
it('should download resource and untar to given binPath', () => {
|
||||
|
||||
untar({ opts: { binPath: './bin', binName: 'command' }, req: { pipe }, onSuccess, onError });
|
||||
|
||||
expect(tar.Extract).toHaveBeenCalledWith({ path: './bin' });
|
||||
});
|
||||
|
||||
it('should call onSuccess on untar end', () => {
|
||||
|
||||
untar({ opts: { binPath: './bin', binName: 'command' }, req: { pipe }, onSuccess, onError });
|
||||
|
||||
untarEvents.emit('end');
|
||||
|
||||
expect(onSuccess).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call onError with error on ungz error', () => {
|
||||
|
||||
const error = new Error();
|
||||
|
||||
untar({ opts: { binPath: './bin', binName: 'command' }, req: { pipe }, onSuccess, onError });
|
||||
|
||||
ungzEvents.emit('error', error);
|
||||
|
||||
expect(onError).toHaveBeenCalledWith(error);
|
||||
});
|
||||
|
||||
it('should call onError with error on untar error', () => {
|
||||
|
||||
const error = new Error();
|
||||
|
||||
untar({ opts: { binPath: './bin', binName: 'command' }, req: { pipe }, onSuccess, onError });
|
||||
|
||||
untarEvents.emit('error', error);
|
||||
|
||||
expect(onError).toHaveBeenCalledWith(error);
|
||||
});
|
||||
});
|
||||
40
__test__/cli.spec.js
Normal file
40
__test__/cli.spec.js
Normal file
@@ -0,0 +1,40 @@
|
||||
const cli = require('../src/cli');
|
||||
const install = require('../src/actions/install');
|
||||
|
||||
jest.mock('../src/actions/install');
|
||||
|
||||
describe('cli()', () => {
|
||||
let exit;
|
||||
|
||||
beforeEach(() => {
|
||||
exit = jest.fn();
|
||||
});
|
||||
|
||||
it('should exit with error if not enough args are supplied', () => {
|
||||
cli({ argv: [], exit });
|
||||
|
||||
expect(exit).toHaveBeenCalledWith(1);
|
||||
});
|
||||
|
||||
it('should exit with error if command does not exist', () => {
|
||||
cli({ argv: [ '/usr/local/bin/node', 'index.js', 'command' ], exit });
|
||||
|
||||
expect(exit).toHaveBeenCalledWith(1);
|
||||
});
|
||||
|
||||
it('should exit with error if command returns error', () => {
|
||||
install.mockImplementationOnce((cb) => cb(new Error()));
|
||||
|
||||
cli({ argv: [ '/usr/local/bin/node', 'index.js', 'install' ], exit });
|
||||
|
||||
expect(exit).toHaveBeenCalledWith(1);
|
||||
});
|
||||
|
||||
it('should exit with success if command runs fine', () => {
|
||||
install.mockImplementationOnce((cb) => cb(null));
|
||||
|
||||
cli({ argv: [ '/usr/local/bin/node', 'index.js', 'install' ], exit });
|
||||
|
||||
expect(exit).toHaveBeenCalledWith(0);
|
||||
});
|
||||
});
|
||||
123
__test__/common.spec.js
Normal file
123
__test__/common.spec.js
Normal file
@@ -0,0 +1,123 @@
|
||||
const fs = require('fs');
|
||||
const childProcess = require('child_process');
|
||||
const common = require('../src/common');
|
||||
|
||||
jest.mock('fs');
|
||||
jest.mock('child_process');
|
||||
jest.mock('mkdirp');
|
||||
|
||||
describe('common', () => {
|
||||
describe('getInstallationPath()', () => {
|
||||
let callback, env;
|
||||
|
||||
beforeEach(() => {
|
||||
callback = jest.fn();
|
||||
|
||||
env = { ...process.env };
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
process.env = env;
|
||||
});
|
||||
|
||||
it('should get binaries path from `npm bin`', () => {
|
||||
childProcess.exec.mockImplementationOnce((_cmd, cb) => cb(null, '/usr/local/bin'));
|
||||
|
||||
common.getInstallationPath(callback);
|
||||
|
||||
expect(callback).toHaveBeenCalledWith(null, '/usr/local/bin');
|
||||
});
|
||||
|
||||
it('should get binaries path from env', () => {
|
||||
childProcess.exec.mockImplementationOnce((_cmd, cb) => cb(new Error()));
|
||||
|
||||
process.env.npm_config_prefix = '/usr/local';
|
||||
|
||||
common.getInstallationPath(callback);
|
||||
|
||||
expect(callback).toHaveBeenCalledWith(null, '/usr/local/bin');
|
||||
});
|
||||
|
||||
it('should call callback with error if binaries path is not found', () => {
|
||||
childProcess.exec.mockImplementationOnce((_cmd, cb) => cb(new Error()));
|
||||
|
||||
process.env.npm_config_prefix = undefined;
|
||||
|
||||
common.getInstallationPath(callback);
|
||||
|
||||
expect(callback).toHaveBeenCalledWith(new Error('Error finding binary installation directory'));
|
||||
});
|
||||
});
|
||||
|
||||
describe('parsePackageJson()', () => {
|
||||
let _process;
|
||||
|
||||
beforeEach(() => {
|
||||
_process = { ...global.process };
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
global.process = _process;
|
||||
});
|
||||
|
||||
describe('validation', () => {
|
||||
it('should return if architecture is unsupported', () => {
|
||||
process.arch = 'mips';
|
||||
|
||||
expect(common.parsePackageJson()).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should return if platform is unsupported', () => {
|
||||
process.platform = 'amiga';
|
||||
|
||||
expect(common.parsePackageJson()).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should return if package.json does not exist', () => {
|
||||
fs.existsSync.mockReturnValueOnce(false);
|
||||
|
||||
expect(common.parsePackageJson()).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('variable replacement', () => {
|
||||
it('should append .exe extension on windows platform', () => {
|
||||
fs.existsSync.mockReturnValueOnce(true);
|
||||
fs.readFileSync.mockReturnValueOnce(JSON.stringify({
|
||||
version: '1.0.0',
|
||||
goBinary: {
|
||||
name: 'command',
|
||||
path: './bin',
|
||||
url: 'https://github.com/foo/bar/releases/v{{version}}/assets/command{{win_ext}}'
|
||||
}
|
||||
}));
|
||||
|
||||
process.platform = 'win32';
|
||||
|
||||
expect(common.parsePackageJson()).toMatchObject({
|
||||
binName: 'command.exe',
|
||||
url: 'https://github.com/foo/bar/releases/v1.0.0/assets/command.exe'
|
||||
});
|
||||
});
|
||||
|
||||
it('should not append .exe extension on platform different than windows', () => {
|
||||
fs.existsSync.mockReturnValueOnce(true);
|
||||
fs.readFileSync.mockReturnValueOnce(JSON.stringify({
|
||||
version: '1.0.0',
|
||||
goBinary: {
|
||||
name: 'command',
|
||||
path: './bin',
|
||||
url: 'https://github.com/foo/bar/releases/v{{version}}/assets/command{{win_ext}}'
|
||||
}
|
||||
}));
|
||||
|
||||
process.platform = 'darwin';
|
||||
|
||||
expect(common.parsePackageJson()).toMatchObject({
|
||||
binName: 'command',
|
||||
url: 'https://github.com/foo/bar/releases/v1.0.0/assets/command'
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,127 +0,0 @@
|
||||
const rewire = require('rewire');
|
||||
const EventEmitter = require('events').EventEmitter;
|
||||
|
||||
describe('go-npm', function() {
|
||||
let mod;
|
||||
|
||||
beforeEach(function() {
|
||||
mod = rewire('../src/index.js');
|
||||
});
|
||||
|
||||
describe('Resource handling strategies', function() {
|
||||
|
||||
describe('untarStrategy()', function() {
|
||||
|
||||
let untarStrategy, ungzEvents, untarEvents, pipe, callback, zlib, createGunzip, tar;
|
||||
|
||||
beforeEach(function() {
|
||||
untarStrategy = mod.__get__('untarStrategy');
|
||||
zlib = mod.__get__('zlib');
|
||||
tar = mod.__get__('tar');
|
||||
ungzEvents = new EventEmitter();
|
||||
untarEvents = new EventEmitter();
|
||||
|
||||
createGunzip = jest.fn();
|
||||
pipe = jest.fn();
|
||||
callback = jest.fn();
|
||||
|
||||
pipe.mockReturnValueOnce({ pipe });
|
||||
createGunzip.mockReturnValueOnce(ungzEvents);
|
||||
jest.spyOn(tar, 'Extract').mockReturnValueOnce(untarEvents);
|
||||
|
||||
// jest.spyOn not working on read-only properties
|
||||
Object.defineProperty(zlib, 'createGunzip', { value: createGunzip });
|
||||
});
|
||||
|
||||
it('should download resource and untar to given binPath', function() {
|
||||
|
||||
untarStrategy({ binPath: './bin', binName: 'command' }, { pipe }, callback);
|
||||
|
||||
expect(tar.Extract).toHaveBeenCalledWith({ path: './bin' });
|
||||
});
|
||||
|
||||
it('should call verifyAndPlaceBinary on untar end', function() {
|
||||
|
||||
const verifyAndPlaceBinary = jest.fn();
|
||||
|
||||
mod.__set__('verifyAndPlaceBinary', verifyAndPlaceBinary);
|
||||
|
||||
untarStrategy({ binPath: './bin', binName: 'command' }, { pipe }, callback);
|
||||
|
||||
untarEvents.emit('end');
|
||||
|
||||
expect(verifyAndPlaceBinary).toHaveBeenCalledWith('command', './bin', callback);
|
||||
});
|
||||
|
||||
it('should call callback with error on ungz error', function() {
|
||||
|
||||
const error = new Error();
|
||||
|
||||
untarStrategy({ binPath: './bin', binName: 'command' }, { pipe }, callback);
|
||||
|
||||
ungzEvents.emit('error', error);
|
||||
|
||||
expect(callback).toHaveBeenCalledWith(error);
|
||||
});
|
||||
|
||||
it('should call callback with error on untar error', function() {
|
||||
|
||||
const error = new Error();
|
||||
|
||||
untarStrategy({ binPath: './bin', binName: 'command' }, { pipe }, callback);
|
||||
|
||||
untarEvents.emit('error', error);
|
||||
|
||||
expect(callback).toHaveBeenCalledWith(error);
|
||||
});
|
||||
});
|
||||
|
||||
describe('moveStrategy()', function() {
|
||||
|
||||
let moveStrategy, streamEvents, pipe, callback, fs;
|
||||
|
||||
beforeEach(function() {
|
||||
|
||||
moveStrategy = mod.__get__('moveStrategy');
|
||||
fs = mod.__get__('fs');
|
||||
streamEvents = new EventEmitter();
|
||||
|
||||
pipe = jest.fn();
|
||||
callback = jest.fn();
|
||||
|
||||
jest.spyOn(fs, 'createWriteStream').mockReturnValueOnce(streamEvents);
|
||||
});
|
||||
|
||||
it('should download resource to given binPath', function() {
|
||||
|
||||
moveStrategy({ binPath: './bin', binName: 'command' }, { pipe }, callback);
|
||||
|
||||
expect(fs.createWriteStream).toHaveBeenCalledWith('bin/command');
|
||||
});
|
||||
|
||||
it('should call verifyAndPlaceBinary on stream closed', function() {
|
||||
|
||||
const verifyAndPlaceBinary = jest.fn();
|
||||
|
||||
mod.__set__('verifyAndPlaceBinary', verifyAndPlaceBinary);
|
||||
|
||||
moveStrategy({ binPath: './bin', binName: 'command' }, { pipe }, callback);
|
||||
|
||||
streamEvents.emit('close');
|
||||
|
||||
expect(verifyAndPlaceBinary).toHaveBeenCalledWith('command', './bin', callback);
|
||||
});
|
||||
|
||||
it('should call callback with error on write stream error', function() {
|
||||
|
||||
const error = new Error();
|
||||
|
||||
moveStrategy({ binPath: './bin', binName: 'command' }, { pipe }, callback);
|
||||
|
||||
streamEvents.emit('error', error);
|
||||
|
||||
expect(callback).toHaveBeenCalledWith(error);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user