diff options
author | Ruy Adorno <ruyadorno@hotmail.com> | 2022-02-03 06:10:22 +0300 |
---|---|---|
committer | Ruy Adorno <ruyadorno@hotmail.com> | 2022-02-03 22:23:46 +0300 |
commit | 457e0ae61bbc55846f5af44afa4066921923490f (patch) | |
tree | 43a3bf35bc6a357c82b2c19874e022169e31fb41 /test/lib | |
parent | 0b0a7cc767947ea738da50caa832d8a922e20ac6 (diff) |
fix(ci): lock file validation
Make sure to validate any lock file (either package-lock.json or
npm-shrinkwrap.json) against the current install. This will properly
throw an error in case any of the dependencies being installed don't
match the dependencies that are currently listed in the lock file.
Fixes: https://github.com/npm/cli/issues/2701
Fixes: https://github.com/npm/cli/issues/3947
Diffstat (limited to 'test/lib')
-rw-r--r-- | test/lib/commands/ci.js | 82 | ||||
-rw-r--r-- | test/lib/utils/validate-lockfile.js | 82 |
2 files changed, 164 insertions, 0 deletions
diff --git a/test/lib/commands/ci.js b/test/lib/commands/ci.js index 537d0784f..e077c99ee 100644 --- a/test/lib/commands/ci.js +++ b/test/lib/commands/ci.js @@ -19,6 +19,17 @@ t.test('should ignore scripts with --ignore-scripts', async t => { this.reify = () => { REIFY_CALLED = true } + this.buildIdealTree = () => {} + this.virtualTree = { + inventory: new Map([ + ['foo', { name: 'foo', version: '1.0.0' }], + ]), + } + this.idealTree = { + inventory: new Map([ + ['foo', { name: 'foo', version: '1.0.0' }], + ]), + } }, }) @@ -99,6 +110,17 @@ t.test('should use Arborist and run-script', async t => { this.reify = () => { t.ok(true, 'reify is called') } + this.buildIdealTree = () => {} + this.virtualTree = { + inventory: new Map([ + ['foo', { name: 'foo', version: '1.0.0' }], + ]), + } + this.idealTree = { + inventory: new Map([ + ['foo', { name: 'foo', version: '1.0.0' }], + ]), + } }, rimraf: (path, ...args) => { actualRimrafs++ @@ -138,6 +160,17 @@ t.test('should pass flatOptions to Arborist.reify', async t => { this.reify = async (options) => { t.equal(options.production, true, 'should pass flatOptions to Arborist.reify') } + this.buildIdealTree = () => {} + this.virtualTree = { + inventory: new Map([ + ['foo', { name: 'foo', version: '1.0.0' }], + ]), + } + this.idealTree = { + inventory: new Map([ + ['foo', { name: 'foo', version: '1.0.0' }], + ]), + } }, }) const npm = mockNpm({ @@ -218,6 +251,17 @@ t.test('should remove existing node_modules before installing', async t => { const nodeModules = contents.filter((path) => path.startsWith('node_modules')) t.same(nodeModules, ['node_modules'], 'should only have the node_modules directory') } + this.buildIdealTree = () => {} + this.virtualTree = { + inventory: new Map([ + ['foo', { name: 'foo', version: '1.0.0' }], + ]), + } + this.idealTree = { + inventory: new Map([ + ['foo', { name: 'foo', version: '1.0.0' }], + ]), + } }, }) @@ -231,3 +275,41 @@ t.test('should remove existing node_modules before installing', async t => { await ci.exec(null) }) + +t.test('should throw error when ideal inventory mismatches virtual', async t => { + const CI = t.mock('../../../lib/commands/ci.js', { + '../../../lib/utils/reify-finish.js': async () => {}, + '@npmcli/run-script': ({ event }) => {}, + '@npmcli/arborist': function () { + this.loadVirtual = async () => {} + this.reify = () => {} + this.buildIdealTree = () => {} + this.virtualTree = { + inventory: new Map([ + ['foo', { name: 'foo', version: '1.0.0' }], + ]), + } + this.idealTree = { + inventory: new Map([ + ['foo', { name: 'foo', version: '2.0.0' }], + ]), + } + }, + }) + + const npm = mockNpm({ + globalDir: 'path/to/node_modules/', + prefix: 'foo', + config: { + global: false, + 'ignore-scripts': true, + }, + }) + const ci = new CI(npm) + + try { + await ci.exec([]) + } catch (err) { + t.matchSnapshot(err.message) + } +}) diff --git a/test/lib/utils/validate-lockfile.js b/test/lib/utils/validate-lockfile.js new file mode 100644 index 000000000..25939c5f8 --- /dev/null +++ b/test/lib/utils/validate-lockfile.js @@ -0,0 +1,82 @@ +const t = require('tap') +const validateLockfile = require('../../../lib/utils/validate-lockfile.js') + +t.test('identical inventory for both idealTree and virtualTree', async t => { + t.matchSnapshot( + validateLockfile( + new Map([ + ['foo', { name: 'foo', version: '1.0.0' }], + ['bar', { name: 'bar', version: '2.0.0' }], + ]), + new Map([ + ['foo', { name: 'foo', version: '1.0.0' }], + ['bar', { name: 'bar', version: '2.0.0' }], + ]) + ), + 'should have no errors on identical inventories' + ) +}) + +t.test('extra inventory items on idealTree', async t => { + t.matchSnapshot( + validateLockfile( + new Map([ + ['foo', { name: 'foo', version: '1.0.0' }], + ['bar', { name: 'bar', version: '2.0.0' }], + ]), + new Map([ + ['foo', { name: 'foo', version: '1.0.0' }], + ['bar', { name: 'bar', version: '2.0.0' }], + ['baz', { name: 'baz', version: '3.0.0' }], + ]) + ), + 'should have missing entries error' + ) +}) + +t.test('extra inventory items on virtualTree', async t => { + t.matchSnapshot( + validateLockfile( + new Map([ + ['foo', { name: 'foo', version: '1.0.0' }], + ['bar', { name: 'bar', version: '2.0.0' }], + ['baz', { name: 'baz', version: '3.0.0' }], + ]), + new Map([ + ['foo', { name: 'foo', version: '1.0.0' }], + ['bar', { name: 'bar', version: '2.0.0' }], + ]) + ), + 'should have no errors if finding virtualTree extra items' + ) +}) + +t.test('mismatching versions on inventory', async t => { + t.matchSnapshot( + validateLockfile( + new Map([ + ['foo', { name: 'foo', version: '1.0.0' }], + ['bar', { name: 'bar', version: '2.0.0' }], + ]), + new Map([ + ['foo', { name: 'foo', version: '2.0.0' }], + ['bar', { name: 'bar', version: '3.0.0' }], + ]) + ), + 'should have errors for each mismatching version' + ) +}) + +t.test('missing virtualTree inventory', async t => { + t.matchSnapshot( + validateLockfile( + new Map([]), + new Map([ + ['foo', { name: 'foo', version: '1.0.0' }], + ['bar', { name: 'bar', version: '2.0.0' }], + ['baz', { name: 'baz', version: '3.0.0' }], + ]) + ), + 'should have errors for each mismatching version' + ) +}) |