UNPKG

veendor

Version:

a tool for stroing your npm dependencies in arbitraty storage

1,066 lines (819 loc) 38.7 kB
const {describe, it, beforeEach, afterEach} = require('mocha'); const chai = require('chai'); const chaiAsPromised = require('chai-as-promised'); const sinon = require('sinon'); const mockfs = require('mock-fs'); const fsExtra = require('fs-extra'); const path = require('path'); const _ = require('lodash'); const tracer = require('tracer'); const install = require('../../../lib/install'); const pkgJson = require('../../../lib/pkgjson'); const gitWrapper = require('../../../lib/commandWrappers/gitWrapper'); const npmWrapper = require('../../../lib/commandWrappers/npmWrapper'); const errors = require('../../../lib/errors'); const logger = require('../../../lib/logger'); const helpers = require('../helpers'); const assert = chai.assert; chai.use(chaiAsPromised); let PKGJSON; let LOCKFILE_CONTENTS; let fakeSha1; let sandbox; let fakeBackends; let config; let npmWrapperInstallAllStub; describe('install', () => { beforeEach(() => { sandbox = sinon.sandbox.create(); fakeBackends = [helpers.fakeBackendConfig('fakeBackends[0]'), helpers.fakeBackendConfig('fakeBackends[1]')]; fakeBackends[0].backend.pull = () => Promise.reject(new errors.BundleNotFoundError); npmWrapperInstallAllStub = sandbox.stub(npmWrapper, 'installAll').resolves(); PKGJSON = { dependencies: { foo: '2.2.8', c: '2.2.9' }, devDependencies: { baz: '6.6.6' } }; LOCKFILE_CONTENTS = '{"content": "lockfile contents"}'; mockfs({ 'package.json': JSON.stringify(PKGJSON) }); config = { backends: fakeBackends, fallbackToNpm: true, installDiff: true, packageHash: {} }; fakeSha1 = '1234567890deadbeef1234567890'; sandbox.stub(pkgJson, 'calcHash').returns(fakeSha1); logger.setLogger(tracer.console({level: 6})); }); afterEach(() => { mockfs.restore(); sandbox.restore(); }); it('should fail if node_modules already exist', done => { mockfs({ 'node_modules': {some: {stuff: 'inside'}}, 'package.json': JSON.stringify(PKGJSON) }); const result = install({config: {}}); assert.isRejected(result, install.NodeModulesAlreadyExistError).notify(done); }); it('should fail if pkgJson rejects with EmptyPkgJsonError', done => { sandbox.stub(pkgJson, 'parsePkgJson').rejects(new pkgJson.EmptyPkgJsonError); mockfs({ 'package.json': '{}' }); const result = install({config}); assert.isRejected(result, pkgJson.EmptyPkgJsonError).notify(done); }); it('should delete node_modules, if force option is used', done => { mockfs({ 'node_modules': {some: {stuff: 'inside'}}, 'package.json': JSON.stringify(PKGJSON) }); const nodeModules = path.join(process.cwd(), 'node_modules'); install({force: true, config}).then(() => { fsExtra.stat(nodeModules).then(() => { done(new Error('node_modules haven\'t been removed')); }, () => { done(); }); }, done); }); it('should fail if pkgJson is not supplied', done => { mockfs({}); const result = install({config}); assert.isRejected(result, install.PkgJsonNotFoundError).notify(done); }); it('should call pkgjson with package.json contents first', done => { pkgJson.calcHash.restore(); const pkgJsonMock = sandbox.mock(pkgJson).expects('calcHash').withArgs(PKGJSON); const checkResult = checkMockResult.bind(null, [pkgJsonMock], done); install({config}).then(checkResult, checkResult); }); it('should pass config.packageHash to pkgjson', done => { config.packageHash = {suffix: 'test'}; pkgJson.calcHash.restore(); const pkgJsonMock = sandbox.mock(pkgJson).expects('calcHash').withArgs(PKGJSON, null, config.packageHash); const checkResult = checkMockResult.bind(null, [pkgJsonMock], done); install({config}).then(checkResult, checkResult); }); it('should pass lockfile to pkgjson', done => { mockfs({ 'package.json': JSON.stringify(PKGJSON), 'package-lock.json': '{watwatwat}', }); pkgJson.calcHash.restore(); const pkgJsonMock = sandbox.mock(pkgJson) .expects('calcHash') .withArgs(PKGJSON, '{watwatwat}').atLeast(1); const checkResult = checkMockResult.bind(null, [pkgJsonMock], done); install({config, lockfile: 'package-lock.json'}).then(checkResult, checkResult); }); it('should call `pull` on all backends until any backend succedes', done => { const fakeBackends0Mock = sandbox.mock(fakeBackends[0].backend) .expects('pull') .withArgs(fakeSha1) .rejects(new errors.BundleNotFoundError); const fakeBackends1Mock = sandbox.mock(fakeBackends[1].backend) .expects('pull') .withArgs(fakeSha1) .resolves(); const checkResult = checkMockResult.bind(null, [fakeBackends0Mock, fakeBackends1Mock], done); install({ config: {backends: fakeBackends} }).then(checkResult, checkResult); }); it('should stop calling `pull` if backend fails with generic error', done => { mockfs({ 'package.json': JSON.stringify(PKGJSON) }); const fakeBackends0Mock = sandbox.mock(fakeBackends[0].backend) .expects('pull') .withArgs(fakeSha1) .rejects(new helpers.AnError('life sucks')); const fakeBackends1Mock = sandbox.mock(fakeBackends[1].backend) .expects('pull') .never(); const checkResult = checkMockResult.bind(null, [fakeBackends0Mock, fakeBackends1Mock], done); install({ config: {backends: fakeBackends} }).then(checkResult, checkResult); }); it('should not call `push` if `pull` succedes', done => { mockfs({ 'package.json': JSON.stringify(PKGJSON) }); const fakeBackends1Mock = sandbox.mock(fakeBackends[1].backend) .expects('push') .never(); config.backends = [fakeBackends[1]]; fakeBackends[1].push = true; const checkResult = checkMockResult.bind(null, [fakeBackends1Mock], done); install({config}).then(checkResult, checkResult); }); it('should pass options to `pull` on a backend', done => { const fakeBackends0Mock = sandbox.mock(fakeBackends[0].backend) .expects('pull') .withArgs(sinon.match.any, sinon.match.same(fakeBackends[0].options)) .rejects(new errors.BundleNotFoundError); const fakeBackends1Mock = sandbox.mock(fakeBackends[1].backend) .expects('pull') .withArgs(sinon.match.any, sinon.match.same(fakeBackends[1].options)) .resolves(); const checkResult = checkMockResult.bind(null, [fakeBackends0Mock, fakeBackends1Mock], done); install({config}).then(checkResult, checkResult); }); it('should create cache directory before pull', done => { const checkResult = () => { fsExtra.stat(path.resolve('.veendor', fakeBackends[0].alias)).then(() => done(), done); }; install({config}).then(checkResult, checkResult); }); it('should pass cache directory to pull', done => { const fakeBackends0Mock = sandbox.mock(fakeBackends[0].backend) .expects('pull') .withArgs(sinon.match.any, sinon.match.any, sinon.match(`.veendor/${fakeBackends[0].alias}`)) .rejects(new errors.BundleNotFoundError); const fakeBackends1Mock = sandbox.mock(fakeBackends[1].backend) .expects('pull') .withArgs(sinon.match.any, sinon.match.any, sinon.match(`.veendor/${fakeBackends[1].alias}`)) .resolves(); const checkResult = checkMockResult.bind(null, [fakeBackends0Mock, fakeBackends1Mock], done); install({config}).then(checkResult, checkResult); }); it('should clean cache directory before pull', done => { const mockfsConfig = { '.veendor': {}, 'package.json': JSON.stringify(PKGJSON) }; mockfsConfig['.veendor'][fakeBackends[0].alias] = {'shouldBeDeleted': 'true'}; mockfs(mockfsConfig); fakeBackends[0].backend.pull = () => { return new Promise((resolve, reject) => { fsExtra.stat(path.resolve('.veendor', fakeBackends[0].alias, 'shouldBeDeleted')).then( () => {done(new Error(`'.veendor/${fakeBackends[0].alias}/shouldBeDeleted' is not deleted`))}, (err) => { if (err.code === 'ENOENT') { done(); } else { done(err); } } ); return reject(new errors.BundleNotFoundError); }); }; install({config}); }); it('should not clean cache directory before pull if backend has keepCache === true property', done => { const mockfsConfig = { '.veendor': {}, 'package.json': JSON.stringify(PKGJSON) }; mockfsConfig['.veendor'][fakeBackends[0].alias] = {'shouldStay': 'true'}; mockfs(mockfsConfig); fakeBackends[0].backend.keepCache = true; fakeBackends[0].backend.pull = () => { return new Promise((resolve, reject) => { fsExtra.stat(path.resolve('.veendor', fakeBackends[0].alias, 'shouldStay')).then( () => {done()}, done ); return reject(new errors.BundleNotFoundError); }); }; install({config}); }); it('should call `npmWrapper.installAll` if no backend succeded', done => { mockfs({ 'package.json': JSON.stringify(PKGJSON) }); npmWrapper.installAll.restore(); const npmWrapperMock = sandbox.mock(npmWrapper, 'installAll') .expects('installAll'); const checkResults = checkMockResult.bind(null, [npmWrapperMock], done); config.backends = [fakeBackends[0], fakeBackends[0]]; install({config}).then(checkResults, checkResults); }); it('should not call `npmWrapper.installAll` if fallbackToNpm set to false', done => { config.fallbackToNpm = false; config.backends = [fakeBackends[0], fakeBackends[0]]; const result = install({config}); assert.isRejected(result, install.BundlesNotFoundError).notify(done); }); describe('_', () => { let fakePkgJson1; let fakePkgJson2; let pkgJsonStub; let gitWrapperOlderRevisionStub; let gitWrapperIsGitRepoStub; let npmWrapperInstallStub; let olderLockfiles = [ '{"content": "package-lock.json a year ago"}', '{"content": "package-lock.json two years ago"}', '{"content": "package-lock.json three years ago"}', ]; beforeEach(() => { mockfs({ 'package.json': JSON.stringify(PKGJSON) }); fakePkgJson1 = _.cloneDeep(PKGJSON); fakePkgJson1.dependencies.c = '1.0.0'; fakePkgJson2 = _.cloneDeep(PKGJSON); fakePkgJson2.dependencies.c = '2.1.8'; pkgJson.calcHash.restore(); pkgJsonStub = sandbox.stub(pkgJson, 'calcHash').callsFake((_pkgJson, lockfileContents) => { if (_.isEqual(_pkgJson, PKGJSON) && lockfileContents === olderLockfiles[0]) { return 'PKGJSONHash'; } else if (_.isEqual(_pkgJson, PKGJSON) && lockfileContents === LOCKFILE_CONTENTS) { return 'PKGJSONHashWithNewLockfile'; } else if (_.isEqual(_pkgJson, fakePkgJson1)) { return 'fakePkgJson1Hash'; } else if (_.isEqual(_pkgJson, fakePkgJson2)) { return 'fakePkgJson2Hash'; } else if (_.isEqual(_pkgJson, PKGJSON)) { return 'PKGJSONHash'; } throw new Error('Something is unmocked'); }); gitWrapperOlderRevisionStub = sandbox.stub(gitWrapper, 'olderRevision') .callsFake((gitDir, [filename1, filename2], age) => { if (filename2 === 'package-lock.json') { if (age === 1) { return Promise.resolve([JSON.stringify(PKGJSON), LOCKFILE_CONTENTS]); } else if (age === 2) { return Promise.resolve([JSON.stringify(fakePkgJson1), olderLockfiles[0]]); } else if (age === 3) { return Promise.resolve([JSON.stringify(fakePkgJson2), olderLockfiles[1]]); } } else { if (age === 1) { return Promise.resolve([JSON.stringify(PKGJSON), null]); } else if (age === 2) { return Promise.resolve([JSON.stringify(fakePkgJson1), null]); } else if (age === 3) { return Promise.resolve([JSON.stringify(fakePkgJson2), null]); } } return Promise.reject(new gitWrapper.TooOldRevisionError); }); gitWrapperIsGitRepoStub = sandbox.stub(gitWrapper, 'isGitRepo').callsFake(() => Promise.resolve()); npmWrapperInstallStub = sandbox.stub(npmWrapper, 'install').callsFake(() => Promise.resolve()); fakeBackends[0].push = true; fakeBackends[1].backend.pull = (hash) => { if (hash === 'PKGJSONHash' || hash === 'fakePkgJson1Hash') { return Promise.reject(new errors.BundleNotFoundError); } else if (hash === 'fakePkgJson2Hash') { return Promise.resolve(); } else { throw new Error('Something is unmocked'); } }; }); it('should look in useGitHistory.depth entries, starting at HEAD', (done) => { gitWrapperOlderRevisionStub.restore(); const gitWrapperMock = sandbox.mock(gitWrapper); gitWrapperMock.expects('olderRevision') .withArgs(process.cwd(), [sinon.match('package.json'), null], 1) .resolves([JSON.stringify(fakePkgJson1)]); gitWrapperMock.expects('olderRevision') .withArgs(process.cwd(), [sinon.match('package.json'), null], 2) .resolves([JSON.stringify(fakePkgJson1)]); gitWrapperMock.expects('olderRevision') .withArgs(process.cwd(), [sinon.match('package.json'), null], 3) .resolves([JSON.stringify(fakePkgJson2)]); config.useGitHistory = { depth: 2 }; const result = install({config}); const checkResult = checkMockResult.bind(null, [gitWrapperMock], done); result.then(checkResult, checkResult); }); it('should call pkgjson with older package.json revision', done => { pkgJsonStub.restore(); const pkgJsonMock = sandbox.mock(pkgJson); pkgJsonMock.expects('calcHash').withArgs(PKGJSON).returns('PKGJSONHash').atLeast(1); pkgJsonMock.expects('calcHash').withArgs(fakePkgJson2).returns('fakePkgJson2Hash').atLeast(1); pkgJsonMock.expects('calcHash').withArgs(fakePkgJson1).returns('fakePkgJson1Hash'); const checkResult = checkMockResult.bind(null, [pkgJsonMock], done); config.backends = [fakeBackends[0]]; config.useGitHistory = { depth: 1 }; install({config}).then(checkResult, checkResult); }); it('should pass options to pkgjson with older package.json revision', done => { pkgJsonStub.restore(); const pkgJsonMock = sandbox.mock(pkgJson); config.backends = [fakeBackends[0]]; config.useGitHistory = { depth: 1 }; config.packageHash = {suffix: 'test'}; pkgJsonMock .expects('calcHash') .withArgs(PKGJSON, null, config.packageHash) .returns('PKGJSONHash') .atLeast(1); pkgJsonMock .expects('calcHash') .withArgs(fakePkgJson2, null, config.packageHash) .returns('fakePkgJson2Hash') .atLeast(1); pkgJsonMock .expects('calcHash') .withArgs(fakePkgJson1, null, config.packageHash) .returns('fakePkgJson1Hash'); const checkResult = checkMockResult.bind(null, [pkgJsonMock], done); install({config}).then(checkResult, checkResult); }); it('should pass options to pkgjson with older package.json revision', done => { pkgJsonStub.restore(); const pkgJsonMock = sandbox.mock(pkgJson); config.backends = [fakeBackends[0]]; config.useGitHistory = { depth: 1 }; config.packageHash = {suffix: 'test'}; pkgJsonMock .expects('calcHash') .withArgs(PKGJSON, null, config.packageHash) .returns('PKGJSONHash') .atLeast(1); pkgJsonMock .expects('calcHash') .withArgs(fakePkgJson1, null, config.packageHash) .returns('fakePkgJson1Hash'); pkgJsonMock .expects('calcHash') .withArgs(fakePkgJson2, null, config.packageHash) .returns('fakePkgJson2Hash') .atLeast(1); const checkResult = checkMockResult.bind(null, [pkgJsonMock], done); install({config}).then(checkResult, checkResult); }); it('should pass lockfile to pkgjson with older package.json revision', done => { mockfs({ 'package.json': JSON.stringify(PKGJSON), 'package-lock.json': LOCKFILE_CONTENTS, }); pkgJsonStub.restore(); const pkgJsonMock = sandbox.mock(pkgJson); config.backends = [fakeBackends[0]]; config.useGitHistory = { depth: 1 }; pkgJsonMock .expects('calcHash') .withArgs(PKGJSON, LOCKFILE_CONTENTS, config.packageHash) .returns('PKGJSONHash') .atLeast(1); pkgJsonMock .expects('calcHash') .withArgs(fakePkgJson1, olderLockfiles[0], config.packageHash) .returns('fakePkgJson1Hash'); pkgJsonMock .expects('calcHash') .withArgs(fakePkgJson2, olderLockfiles[1], config.packageHash) .returns('fakePkgJson2Hash') .atLeast(1); const checkResult = checkMockResult.bind(null, [pkgJsonMock], done); install({config, lockfile: 'package-lock.json'}).then(checkResult, checkResult); }); it('should call `pull` on backends with gitWrapper.olderRevision\'s hash', done => { const backend0Mock = sandbox.mock(fakeBackends[0].backend); backend0Mock.expects('pull').withArgs('PKGJSONHash').rejects(new errors.BundleNotFoundError); backend0Mock.expects('pull').withArgs('fakePkgJson1Hash').resolves(); const backend1Mock = sandbox.mock(fakeBackends[1].backend); backend1Mock.expects('pull').withArgs('PKGJSONHash').rejects(new errors.BundleNotFoundError); backend1Mock.expects('pull').withArgs('fakePkgJson1Hash').never(); const checkResult = checkMockResult.bind(null, [backend0Mock, backend1Mock], done); config.backends = fakeBackends; config.useGitHistory = { depth: 1 }; install({config}).then(checkResult, checkResult); }); it('should reject if olderBundles not found', done => { config.backends = [fakeBackends[0]]; config.useGitHistory = { depth: 1 }; config.fallbackToNpm = false; assert.isRejected(install({config}), install.BundlesNotFoundError).notify(done); }); it('should not call gitWrapper.olderRevision if useGitHistory.depth is not defined', done => { gitWrapperOlderRevisionStub.restore(); const gitWrapperMock = sandbox.mock(gitWrapper); gitWrapperMock.expects('olderRevision').never(); const checkResult = checkMockResult.bind(null, [gitWrapperMock], done); config.backends = [fakeBackends[0]]; install({config}).then(checkResult, checkResult); }); it('should not call gitWrapper.olderRevision if not in git repo', done => { gitWrapperOlderRevisionStub.restore(); gitWrapperIsGitRepoStub.restore(); const gitWrapperMock = sandbox.mock(gitWrapper); gitWrapperMock.expects('isGitRepo').rejects(new gitWrapper.NotAGitRepoError); gitWrapperMock.expects('olderRevision').never(); const checkResult = checkMockResult.bind(null, [gitWrapperMock], done); config.backends = [fakeBackends[0]]; config.useGitHistory = { depth: 1 }; install({config}).then(checkResult, checkResult); }); it('should call `npmWrapper.install` with diff between package.json\'s ' + 'after successful pull of history bundle', done => { fakeBackends[0].backend.pull = (hash) => { if (hash === 'PKGJSONHash' || hash === 'fakePkgJson1Hash') { return Promise.reject(new errors.BundleNotFoundError); } else if (hash === 'fakePkgJson2Hash') { return Promise.resolve(); } else { throw new Error('Something is unmocked'); } }; npmWrapperInstallStub.restore(); const npmWrapperMock = sandbox.mock(npmWrapper); npmWrapperMock.expects('install').withArgs({c: '2.2.9'}).resolves(); const checkResult = checkMockResult.bind(null, [npmWrapperMock], done); config.backends = [fakeBackends[0]]; config.useGitHistory = { depth: 2 }; install({config}).then(checkResult, checkResult); }); it('should call `npmWrapper.uninstall` for deleted modules', done => { delete PKGJSON.dependencies.c; mockfs({ 'package.json': JSON.stringify(PKGJSON) }); fakeBackends[0].backend.pull = (hash) => { if (hash === 'PKGJSONHash' || hash === 'fakePkgJson1Hash') { return Promise.reject(new errors.BundleNotFoundError); } else if (hash === 'fakePkgJson2Hash') { return Promise.resolve(); } else { throw new Error('Something is unmocked'); } }; npmWrapperInstallStub.restore(); const npmWrapperMock = sandbox.mock(npmWrapper); npmWrapperMock.expects('uninstall').withArgs(['c']).resolves(); const checkResult = checkMockResult.bind(null, [npmWrapperMock], done); config.backends = [fakeBackends[0]]; config.useGitHistory = { depth: 2 }; install({config}).then(checkResult, checkResult); }); it('should not call `npmWrapper.uninstall` for modules moved from devdeps to deps', done => { PKGJSON.devDependencies.c = fakePkgJson2.dependencies.c; delete PKGJSON.dependencies.c; mockfs({ 'package.json': JSON.stringify(PKGJSON) }); fakeBackends[0].backend.pull = (hash) => { if (hash === 'PKGJSONHash' || hash === 'fakePkgJson1Hash') { return Promise.reject(new errors.BundleNotFoundError); } else if (hash === 'fakePkgJson2Hash') { return Promise.resolve(); } else { throw new Error('Something is unmocked'); } }; npmWrapperInstallStub.restore(); const npmWrapperMock = sandbox.mock(npmWrapper); npmWrapperMock.expects('uninstall').never(); npmWrapperMock.expects('install').never(); const checkResult = checkMockResult.bind(null, [npmWrapperMock], done); config.backends = [fakeBackends[0]]; config.useGitHistory = { depth: 2 }; install({config}).then(checkResult, checkResult); }); it('should call `push` on all backends with push: true option after partial npm install', done => { const backendMock0 = sandbox.mock(fakeBackends[0].backend); const backendMock1 = sandbox.mock(fakeBackends[1].backend); backendMock0.expects('push').withArgs('PKGJSONHash').resolves(); backendMock1.expects('push').never(); const checkResult = checkMockResult.bind(null, [backendMock0, backendMock1], done); config.useGitHistory = { depth: 2 }; install({config}).then(checkResult, checkResult); }); it('should pass options to `push`', done => { const backendMock0 = sandbox.mock(fakeBackends[0].backend); backendMock0 .expects('push') .withArgs(sinon.match.any, sinon.match.same(fakeBackends[0].options)) .resolves(); const checkResult = checkMockResult.bind(null, [backendMock0], done); config.useGitHistory = { depth: 2 }; install({config}).then(checkResult, checkResult); }); it('should create cache directory before push', done => { mockfs({ 'package.json': JSON.stringify(PKGJSON) }); const checkResult = () => { fsExtra.stat(path.resolve('.veendor', fakeBackends[0].alias)).then(() => done(), done); }; const old1Pull = fakeBackends[1].backend.pull; fakeBackends[1].backend.pull = (hash) => { mockfs.restore(); mockfs({ 'package.json': JSON.stringify(PKGJSON) }); return old1Pull(hash); }; config.useGitHistory = { depth: 2 }; install({config}).then(checkResult, checkResult); }); it('should pass cache directory to push', done => { mockfs({ 'package.json': JSON.stringify(PKGJSON) }); const backendMock0 = sandbox.mock(fakeBackends[0].backend); backendMock0 .expects('push') .withArgs(sinon.match.any, sinon.match.any, path.resolve('.veendor', fakeBackends[0].alias)) .resolves(); const checkResult = checkMockResult.bind(null, [backendMock0], done); config.useGitHistory = { depth: 2 }; install({config}).then(checkResult, checkResult); }); it('should clean cache directory before push', done => { mockfs({ 'package.json': JSON.stringify(PKGJSON) }); fakeBackends[0].backend.pull = () => { const mockfsConfig = { '.veendor': {}, 'package.json': JSON.stringify(PKGJSON) }; mockfsConfig['.veendor'][fakeBackends[0].alias] = {'shouldBe': 'deleted'}; mockfs(mockfsConfig); return Promise.reject(new errors.BundleNotFoundError); }; fakeBackends[0].backend.push = () => { return new Promise(resolve => { fsExtra.stat(path.resolve('.veendor', fakeBackends[0].alias, 'shouldBe')).then( () => {done(new Error(`'.veendor/${fakeBackends[0].alias}/shouldBe' should be deleted`))}, (err) => { if (err.code === 'ENOENT') { done(); } else { done(err); } } ); return resolve(); }); }; config.useGitHistory = { depth: 2 }; install({config}); }); it('should not clean cache directory before push if backend has keepCache === true property', done => { mockfs({ 'package.json': JSON.stringify(PKGJSON) }); fakeBackends[0].backend.keepCache = true; fakeBackends[0].backend.pull = () => { const mockfsConfig = { '.veendor': {}, 'package.json': JSON.stringify(PKGJSON) }; mockfsConfig['.veendor'][fakeBackends[0].alias] = {'shouldStay': 'true'}; mockfs(mockfsConfig); return Promise.reject(new errors.BundleNotFoundError); }; fakeBackends[0].backend.push = () => { return new Promise(resolve => { fsExtra.stat(path.resolve('.veendor', fakeBackends[0].alias, 'shouldStay')).then( () => {done()}, done ); return resolve(); }); }; config.useGitHistory = { depth: 2 }; install({config}); }); it('should call installAll if can not find old bundles', done => { config.useGitHistory = { depth: 2 }; npmWrapper.installAll.restore(); const npmWrapperMock = sandbox.mock(npmWrapper) .expects('installAll'); const checkResults = checkMockResult.bind(null, [npmWrapperMock], done); config.backends = [fakeBackends[0], fakeBackends[0]]; install({config}).then(checkResults, checkResults); }); it('should call `push` on all backends with push: true option after npm install', done => { fakeBackends[1].backend.pull = () => Promise.reject(new errors.BundleNotFoundError); const backendMock0 = sandbox.mock(fakeBackends[0].backend); const backendMock1 = sandbox.mock(fakeBackends[1].backend); backendMock0.expects('push').withArgs('PKGJSONHash').resolves(); backendMock1.expects('push').never(); const checkResult = checkMockResult.bind(null, [backendMock0, backendMock1], done); install({config}).then(checkResult, checkResult); }); it('should call `push` on all backends with push: true option after npm install (with history)', done => { fakeBackends[1].backend.pull = () => Promise.reject(new errors.BundleNotFoundError); const backendMock0 = sandbox.mock(fakeBackends[0].backend); const backendMock1 = sandbox.mock(fakeBackends[1].backend); backendMock0.expects('push').withArgs('PKGJSONHash').resolves(); backendMock1.expects('push').never(); const checkResult = checkMockResult.bind(null, [backendMock0, backendMock1], done); config.useGitHistory = { depth: 2 }; install({config}).then(checkResult, checkResult); }); it('should push bundle to backends, which don\'t have it, if got it from another backend', done => { const backendMock0 = sandbox.mock(fakeBackends[0].backend); const backendMock1 = sandbox.mock(fakeBackends[1].backend); backendMock0.expects('push').withArgs('PKGJSONHash').resolves(); backendMock1.expects('push').never(); backendMock1.expects('pull').resolves(); const checkResult = checkMockResult.bind(null, [backendMock0, backendMock1], done); install({config}).then(checkResult, checkResult); }); it('should increase history.depth if hash hasn\'t changed ' + '(changes in package.json were unrelated to deps)', done => { gitWrapper.olderRevision.restore(); const gitWrapperMock = sandbox.mock(gitWrapper); gitWrapperMock.expects('olderRevision').exactly(3).resolves([JSON.stringify(PKGJSON)]); gitWrapperMock.expects('olderRevision').once().resolves([JSON.stringify(fakePkgJson2)]); config.backends = [fakeBackends[1]]; config.useGitHistory = { depth: 1 }; const checkResult = checkMockResult.bind(null, [gitWrapperMock], done); install({config}).then(checkResult, checkResult); }); it('should not pull backends if hash hasn\'t changed ' + '(changes in package.json were unrelated to deps)', done => { pkgJson.calcHash.restore(); let hashCount = 0; pkgJson.calcHash = () => { if (hashCount < 4) { hashCount++; return 'fakePkgJson1Hash'; } return 'fakePkgJson2Hash'; }; const backendMock0 = sandbox.mock(fakeBackends[0].backend); config.backends = [fakeBackends[0]]; config.useGitHistory = { depth: 1 }; backendMock0.expects('pull').withArgs('fakePkgJson1Hash').once().resolves(); const checkResult = checkMockResult.bind(null, [backendMock0], done); install({config}).then(checkResult, checkResult); }); it('failing to push with BundleAlreadyExistsError should call backend.pull once again', done => { let turn = 0; fakeBackends[0].backend.push = () => Promise.reject(new errors.BundleAlreadyExistsError()); fakeBackends[0].backend.pull = () => { if (turn === 1) { return Promise.resolve(); } turn++; return Promise.reject(new errors.BundleNotFoundError); }; assert.isFulfilled(install({config})).notify(error => { if (error) { return done(error); } try { assert.equal(turn, 1); done(); } catch (e) { done(e); } }); }); it('failing to push with BundleAlreadyExistsError should call backend.pull only once', done => { let turn = 0; fakeBackends[0].backend.push = () => Promise.reject(new errors.BundleAlreadyExistsError()); fakeBackends[0].backend.pull = () => { turn++; return Promise.reject(new errors.BundleNotFoundError); }; assert.isRejected(install({config}), errors.BundleAlreadyExistsError).notify(done); }); it('should re-calc hash after npm install', done => { // because npm install with lockfile will change the lockfile and we need to push with new hash mockfs({ 'package.json': JSON.stringify(PKGJSON), 'package-lock.json': olderLockfiles[0], }); fakeBackends[1].backend.pull = () => Promise.reject(new errors.BundleNotFoundError); const backendMock0 = sandbox.mock(fakeBackends[0].backend); npmWrapper.installAll.restore(); sandbox.stub(npmWrapper, 'installAll').callsFake(() => { mockfs({ 'package.json': JSON.stringify(PKGJSON), 'package-lock.json': LOCKFILE_CONTENTS, }); return Promise.resolve(); }); backendMock0.expects('push').withArgs('PKGJSONHashWithNewLockfile').resolves(); const checkResult = checkMockResult.bind(null, [backendMock0], done); install({config, lockfile: 'package-lock.json'}).then(checkResult, checkResult); }); it('should locate lockfile and re-calc hash after npm install', done => { // because npm install with lockfile will change the lockfile and we need to push with new hash mockfs({ 'package.json': JSON.stringify(PKGJSON), }); fakeBackends[1].backend.pull = () => Promise.reject(new errors.BundleNotFoundError); const backendMock0 = sandbox.mock(fakeBackends[0].backend); npmWrapper.installAll.restore(); sandbox.stub(npmWrapper, 'installAll').callsFake(() => { mockfs({ 'package.json': JSON.stringify(PKGJSON), 'package-lock.json': LOCKFILE_CONTENTS, }); return Promise.resolve(); }); backendMock0.expects('push').withArgs('PKGJSONHashWithNewLockfile').resolves(); const checkResult = checkMockResult.bind(null, [backendMock0], done); install({config}).then(checkResult, checkResult); }); }); }); function checkMockResult(mocks, done, error) { if (error && error.name === 'ExpectationError') { return done(error); } try { mocks.map(mock => mock.verify()); } catch (error) { return done(error); } done(); }