diff --git a/workspaces/libnpmexec/test/fixtures/setup.js b/workspaces/libnpmexec/test/fixtures/setup.js new file mode 100644 index 0000000000000..38b9d88400fd8 --- /dev/null +++ b/workspaces/libnpmexec/test/fixtures/setup.js @@ -0,0 +1,257 @@ + +const fs = require('fs/promises') +const { existsSync } = require('fs') +const { resolve, extname, join } = require('path') +const _binLinks = require('bin-links') +const MockRegistry = require('@npmcli/mock-registry') +const justExtend = require('just-extend') +const set = require('just-safe-set') + +const merge = (...args) => justExtend(true, ...args) + +const DEFAULT_BIN_FILE = 'bin-file.js' + +const createPkg = ({ + name = '@npmcli/create-index', + bin, + files, + versions = [], + version, + localVersion, +}) => { + if (localVersion && !versions.includes(localVersion)) { + versions = [...versions, localVersion] + } + if (version && !versions.includes(version)) { + versions = [...versions, version] + } + + const defaultBinName = name.includes('/') ? name.split('/')[1] : name + + if (!bin) { + bin = { + [defaultBinName]: `./${DEFAULT_BIN_FILE}`, + } + } + + const pkgsArr = versions.map((v) => ({ + name, + version: v, + bin, + })) + + const pkgs = {} + const tarballs = {} + const fixtures = {} + + for (const pkg of pkgsArr) { + pkgs[pkg.version] = pkg + } + + if (localVersion) { + set(fixtures, ['node_modules', ...name.split('/')], { + 'package.json': pkgs[localVersion], + ...files || { + [DEFAULT_BIN_FILE]: { key: name, value: `local-${localVersion}` }, + }, + }) + fixtures['package.json'] = { + name: 'pkg', + version: '9.9.9', + dependencies: { + [name]: `^${localVersion}`, + }, + } + } + + for (const pkg of pkgsArr) { + const fixturePath = `${pkg.name}-${pkg.version}`.replace('/', '-') + set(fixtures, ['packages', fixturePath], { + 'package.json': pkg, + ...files || { + [DEFAULT_BIN_FILE]: { key: pkg.name, value: `packages-${pkg.version}` }, + }, + }) + tarballs[pkg.version] = join('packages', fixturePath) + } + + return { + pkg: pkgsArr[0], + pkgs, + fixtures, + package: ({ registry, path, tarballs: tgz = versions, ...opts }) => registry.package({ + times: 2, + manifest: registry.manifest({ name: pkgsArr[0].name, packuments: pkgsArr }), + tarballs: tgz.reduce((acc, v) => { + acc[v] = resolve(path, tarballs[v]) + return acc + }, {}), + ...opts, + }), + } +} + +const createTestdir = (...objs) => { + const testdirHelper = (obj, ancestors = []) => { + for (const [key, value] of Object.entries(obj)) { + if (extname(key) === '.json') { + obj[key] = JSON.stringify(value, null, 2) + } else if (extname(key) === '.js' || ancestors.slice(-2).join('/') === 'node_modules/.bin') { + // a js or bin file is converted to a bin script that writes a file + obj[key] = `#!/usr/bin/env node\nrequire('fs').writeFileSync( + 'output-${value.key.replace('/', '-')}', + JSON.stringify({ + value: '${value.value}', + args: process.argv.slice(2), + created: '${[...ancestors, key].join('/')}', + }) + )` + } else if (value && typeof value === 'object') { + obj[key] = testdirHelper(value, [...ancestors, key]) + } else { + obj[key] = value + } + } + return obj + } + + return testdirHelper(merge(...objs)) +} + +const setup = (t, { + pkg, + testdir: _testdir = {}, + mocks, + global, + debug, + execPath, + defaults = true, +} = {}) => { + const registry = new MockRegistry({ + tap: t, + registry: 'http://smoke-test-registry.club/', + strict: true, + debug, + }) + + if (debug) { + process.on('log', console.error) + t.teardown(() => process.off('log', console.error)) + } + + const { node_modules: testdirNm, ...testdir } = _testdir + const fullTestdir = createTestdir({ + cache: {}, + npxCache: {}, + ...testdirNm ? + global ? { + global: { + node_modules: { + '.bin': {}, + ...testdirNm, + }, + }, + } : { + node_modules: { + '.bin': {}, + ...testdirNm, + }, + } + : {}, + }, testdir) + + // quick way to remove undefined and null values that we merged + // in to not write certain directories + const path = t.testdir(JSON.parse(JSON.stringify(fullTestdir, (_, v) => { + if (v === null) { + return + } + if (typeof v === 'string') { + return v.replace(/\{REGISTRY\}/g, registry.origin) + } + return v + })) + ) + + const cache = resolve(path, 'cache') + const npxCache = resolve(path, 'npxCache') + const nodeModules = resolve(path, global ? 'global/node_modules' : 'node_modules') + + const defaultOpts = { + call: '', + color: false, + localBin: '', + globalBin: '', + packages: [], + scriptShell: undefined, + yes: true, + path, + runPath: path, + } + + const baseOpts = { + audit: false, + registry: registry.origin + '/', + ...existsSync(cache) ? { cache } : {}, + ...existsSync(npxCache) ? { npxCache } : {}, + ...global ? { + globalBin: resolve(path, nodeModules, '.bin'), + globalPath: resolve(path, 'global'), + } : {}, + } + + return { + path, + registry, + chmod: async (chmodPath) => { + if (!chmodPath) { + for (const p of [].concat(pkg)) { + await fs.chmod(resolve(path, nodeModules, p.name, DEFAULT_BIN_FILE), 0o775) + } + return + } + return fs.chmod(resolve(path, chmodPath), 0o775) + }, + binLinks: async (binPkg) => { + if (!binPkg) { + for (const p of [].concat(pkg)) { + await _binLinks({ + pkg: p, + path: resolve(path, nodeModules, p.name), + }) + } + return + } + await _binLinks({ + pkg: binPkg, + path: resolve(path, nodeModules, binPkg.name), + }) + }, + readOutput: async (outputPath, { root = path } = {}) => { + if (!outputPath) { + outputPath = pkg.name.replace('/', '-') + } + return fs.readFile(resolve(root, `output-${outputPath}`), 'utf-8').then(r => JSON.parse(r)) + }, + rmOutput: (outputPath, { root = path } = {}) => { + if (!outputPath) { + outputPath = pkg.name.replace('/', '-') + } + return fs.rm(resolve(root, `output-${outputPath}`)) + }, + rimraf: (p) => fs.rm(resolve(path, p), { recursive: true, force: true }), + exec: (opts) => t.mock(execPath || '../../lib/index.js', mocks)({ + ...defaults ? { + ...defaultOpts, + path, + runPath: path, + } : {}, + ...baseOpts, + ...opts, + }), + } +} + +module.exports.setup = setup +module.exports.createPkg = createPkg +module.exports.merge = merge diff --git a/workspaces/libnpmexec/test/index.js b/workspaces/libnpmexec/test/index.js index 739a686aba8c3..008560efc7018 100644 --- a/workspaces/libnpmexec/test/index.js +++ b/workspaces/libnpmexec/test/index.js @@ -1,1269 +1,16 @@ -const fs = require('fs') -const { resolve } = require('path') const t = require('tap') -const binLinks = require('bin-links') - -const libexec = require('../lib/index.js') - -// setup server -const registryServer = require('./registry/server.js') -const { registry } = registryServer -t.test('setup server', { bail: true, buffered: false }, registryServer) - -const baseOpts = { - audit: false, - call: '', - color: false, - localBin: '', - globalBin: '', - packages: [], - path: '', - registry, - runPath: '', - scriptShell: undefined, - yes: true, -} - -t.test('bin in local pkg', async t => { - const pkg = { - name: '@npmcli/local-pkg-bin-test', - bin: { - a: 'local-bin-test.js', - }, - } - const path = t.testdir({ - cache: {}, - npxCache: {}, - 'local-bin-test.js': `#!/usr/bin/env node -require('fs').writeFileSync(process.argv.slice(2)[0], 'LOCAL PKG')`, - 'package.json': JSON.stringify(pkg), - }) - const localBin = resolve(path, 'node_modules/.bin') - const runPath = path - const npxCache = resolve(path, 'npxCache') - - const executable = resolve(path, 'local-bin-test.js') - fs.chmodSync(executable, 0o775) - - await libexec({ - ...baseOpts, - args: ['a', 'resfile'], - npxCache, - localBin, - path, - runPath, - }) - - const res = fs.readFileSync(resolve(path, 'resfile')).toString() - t.equal(res, 'LOCAL PKG', 'should run local pkg bin script') -}) - -t.test('locally available pkg - by scoped name only', async t => { - const pkg = { - name: '@npmcli/npx-local-test', - version: '2.0.0', - bin: { - 'npx-local-test': './index.js', - }, - } - const path = t.testdir({ - cache: {}, - npxCache: {}, - node_modules: { - '.bin': {}, - '@npmcli': { - 'npx-local-test': { - 'package.json': JSON.stringify(pkg), - 'index.js': `#!/usr/bin/env node - require('fs').writeFileSync(process.argv.slice(2)[0], 'LOCAL PKG')`, - }, - }, - }, - 'package.json': JSON.stringify({ - name: 'pkg', - dependencies: { - '@npmcli/npx-local-test': '^2.0.0', - }, - }), - }) - const runPath = path - const cache = resolve(path, 'cache') - const npxCache = resolve(path, 'npxCache') - - const executable = - resolve(path, 'node_modules/@npmcli/npx-local-test/index.js') - fs.chmodSync(executable, 0o775) - - await binLinks({ - path: resolve(path, 'node_modules/@npmcli/npx-local-test'), - pkg, - }) - - await libexec({ - ...baseOpts, - cache, - npxCache, - args: ['@npmcli/npx-local-test', 'resfile'], - path, - runPath, - }) - - const res = fs.readFileSync(resolve(path, 'resfile')).toString() - t.equal(res, 'LOCAL PKG', 'should run local pkg bin script') -}) - -t.test('locally available pkg - by name', async t => { - const pkg = { - name: '@ruyadorno/create-index', - version: '2.0.0', - bin: { - 'create-index': './index.js', - }, - } - const path = t.testdir({ - cache: {}, - npxCache: {}, - node_modules: { - '.bin': {}, - '@ruyadorno': { - 'create-index': { - 'package.json': JSON.stringify(pkg), - 'index.js': `#!/usr/bin/env node - require('fs').writeFileSync(process.argv.slice(2)[0], 'LOCAL PKG')`, - }, - }, - }, - 'package.json': JSON.stringify({ - name: 'pkg', - dependencies: { - '@ruyadorno/create-index': '^2.0.0', - }, - }), - }) - const runPath = path - const cache = resolve(path, 'cache') - const npxCache = resolve(path, 'npxCache') - - const executable = - resolve(path, 'node_modules/@ruyadorno/create-index/index.js') - fs.chmodSync(executable, 0o775) - - await binLinks({ - path: resolve(path, 'node_modules/@ruyadorno/create-index'), - pkg, - }) - - await libexec({ - ...baseOpts, - cache, - npxCache, - packages: ['@ruyadorno/create-index'], - call: 'create-index resfile', - path, - runPath, - }) - - const res = fs.readFileSync(resolve(path, 'resfile')).toString() - t.equal(res, 'LOCAL PKG', 'should run local pkg bin script') -}) - -t.test('locally available pkg - by version', async t => { - const pkg = { - name: '@ruyadorno/create-index', - version: '1.0.0', - bin: { - 'create-index': './index.js', - }, - } - const path = t.testdir({ - cache: {}, - npxCache: {}, - node_modules: { - '.bin': {}, - '@ruyadorno': { - 'create-index': { - 'package.json': JSON.stringify(pkg), - 'index.js': `#!/usr/bin/env node - require('fs').writeFileSync('resfile', 'LOCAL PKG')`, - }, - }, - }, - 'package.json': JSON.stringify({ - name: 'pkg', - dependencies: { - '@ruyadorno/create-index': '^1.0.0', - }, - }), - }) - const runPath = path - const cache = resolve(path, 'cache') - const npxCache = resolve(path, 'npxCache') - - const executable = - resolve(path, 'node_modules/@ruyadorno/create-index/index.js') - fs.chmodSync(executable, 0o775) - - await binLinks({ - path: resolve(path, 'node_modules/@ruyadorno/create-index'), - pkg, - }) - - await libexec({ - ...baseOpts, - cache, - npxCache, - args: ['@ruyadorno/create-index@1.0.0'], - path, - runPath, - }) - - const res = fs.readFileSync(resolve(path, 'resfile')).toString() - t.equal(res, 'LOCAL PKG', 'should run local pkg bin script') -}) - -t.test('locally available pkg - by range', async t => { - const pkg = { - name: '@ruyadorno/create-index', - version: '2.0.0', - bin: { - 'create-index': './index.js', - }, - } - const path = t.testdir({ - cache: {}, - npxCache: {}, - node_modules: { - '.bin': {}, - '@ruyadorno': { - 'create-index': { - 'package.json': JSON.stringify(pkg), - 'index.js': `#!/usr/bin/env node - require('fs').writeFileSync(process.argv.slice(2)[0], 'LOCAL PKG')`, - }, - }, - }, - 'package.json': JSON.stringify({ - name: 'pkg', - dependencies: { - '@ruyadorno/create-index': '^2.0.0', - }, - }), - }) - const runPath = path - const cache = resolve(path, 'cache') - const npxCache = resolve(path, 'npxCache') - - const executable = - resolve(path, 'node_modules/@ruyadorno/create-index/index.js') - fs.chmodSync(executable, 0o775) - - await binLinks({ - path: resolve(path, 'node_modules/@ruyadorno/create-index'), - pkg, - }) - - await libexec({ - ...baseOpts, - cache, - npxCache, - packages: ['@ruyadorno/create-index@^2.0.0'], - call: 'create-index resfile', - path, - runPath, - }) - - const res = fs.readFileSync(resolve(path, 'resfile')).toString() - t.equal(res, 'LOCAL PKG', 'should run local pkg bin script') -}) - -t.test('locally available pkg - by tag', async t => { - const pkg = { - name: '@ruyadorno/create-index', - version: '1.0.0', - bin: { - 'create-index': './index.js', - }, - } - - const path = t.testdir({ - cache: {}, - npxCache: {}, - node_modules: { - '.bin': {}, - '@ruyadorno': { - 'create-index': { - 'package.json': JSON.stringify(pkg), - 'index.js': `#!/usr/bin/env node - require('fs').writeFileSync(process.argv.slice(2)[0], 'LOCAL PKG')`, - }, - }, - '.package-lock.json': JSON.stringify({ - name: 'lock', - lockfileVersion: 3, - requires: true, - packages: { - 'node_modules/@ruyadorno/create-index': { - version: '1.0.0', - resolved: 'https://registry.npmjs.org/@ruyadorno/create-index/-/create-index-1.0.0.tgz', - bin: { - 'create-index': 'create-index.js', - }, - }, - }, - - }), - }, - 'package.json': JSON.stringify({ - name: 'pkg', - dependencies: { - '@ruyadorno/create-index': '^1.0.0', - }, - }), - }) - const runPath = path - const cache = resolve(path, 'cache') - const npxCache = resolve(path, 'npxCache') - - const executable = - resolve(path, 'node_modules/@ruyadorno/create-index/index.js') - fs.chmodSync(executable, 0o775) - - await binLinks({ - path: resolve(path, 'node_modules/@ruyadorno/create-index'), - pkg, - }) - - await libexec({ - ...baseOpts, - cache, - npxCache, - packages: ['@ruyadorno/create-index@latest'], - call: 'create-index resfile', - path, - runPath, - }) - - const res = fs.readFileSync(resolve(path, 'resfile')).toString() - t.equal(res, 'LOCAL PKG', 'should run local pkg bin script') -}) - -t.test('multiple local pkgs', async t => { - const foo = { - name: '@ruyadorno/create-foo', - version: '2.0.0', - bin: { - 'create-foo': './index.js', - }, - } - const bar = { - name: '@ruyadorno/create-bar', - version: '2.0.0', - bin: { - 'create-bar': './index.js', - }, - } - const path = t.testdir({ - cache: {}, - npxCache: {}, - node_modules: { - '.bin': {}, - '@ruyadorno': { - 'create-foo': { - 'package.json': JSON.stringify(foo), - 'index.js': `#!/usr/bin/env node - require('fs').writeFileSync(process.argv.slice(2)[0], 'foo')`, - }, - 'create-bar': { - 'package.json': JSON.stringify(bar), - 'index.js': `#!/usr/bin/env node - require('fs').writeFileSync(process.argv.slice(2)[0], 'bar')`, - }, - }, - }, - 'package.json': JSON.stringify({ - name: 'pkg', - dependencies: { - '@ruyadorno/create-foo': '^2.0.0', - '@ruyadorno/create-bar': '^2.0.0', - }, - }), - }) - const runPath = path - const cache = resolve(path, 'cache') - const npxCache = resolve(path, 'npxCache') - - const setupBins = async (pkg) => { - const executable = - resolve(path, `node_modules/${pkg.name}/index.js`) - fs.chmodSync(executable, 0o775) - - await binLinks({ - path: resolve(path, `node_modules/${pkg.name}`), - pkg, - }) - } - - await Promise.all([foo, bar] - .map(setupBins)) - - await libexec({ - ...baseOpts, - localBin: resolve(path, 'node_modules/.bin'), - cache, - npxCache, - packages: ['@ruyadorno/create-foo', '@ruyadorno/create-bar'], - call: 'create-foo resfile && create-bar bar', - path, - runPath, - }) - - const resFoo = fs.readFileSync(resolve(path, 'resfile')).toString() - t.equal(resFoo, 'foo', 'should run local pkg bin script') - const resBar = fs.readFileSync(resolve(path, 'bar')).toString() - t.equal(resBar, 'bar', 'should run local pkg bin script') -}) - -t.test('no npxCache', async t => { - const path = t.testdir({ - cache: {}, - a: { - 'package.json': JSON.stringify({ - name: 'a', - bin: { - a: './index.js', - }, - }), - 'index.js': `#!/usr/bin/env node -require('fs').writeFileSync(process.argv.slice(2)[0], 'LOCAL PKG')`, - }, - }) - const runPath = path - const cache = resolve(path, 'cache') - - const executable = resolve(path, 'a/index.js') - fs.chmodSync(executable, 0o775) - - await t.rejects(libexec({ - ...baseOpts, - args: [`file:${resolve(path, 'a')}`, 'resfile'], - cache, - path, - runPath, - }), /Must provide a valid npxCache path/) -}) - -t.test('local file system path', async t => { - const path = t.testdir({ - cache: {}, - npxCache: {}, - a: { - 'package.json': JSON.stringify({ - name: 'a', - bin: { - a: './index.js', - }, - }), - 'index.js': `#!/usr/bin/env node -require('fs').writeFileSync(process.argv.slice(2)[0], 'LOCAL PKG')`, - }, - }) - const runPath = path - const cache = resolve(path, 'cache') - const npxCache = resolve(path, 'npxCache') - - const executable = resolve(path, 'a/index.js') - fs.chmodSync(executable, 0o775) - - const mockexec = t.mock('../lib/index.js', { - '@npmcli/ci-detect': () => true, - 'proc-log': { - warn (title, msg) { - t.fail('should not warn about local file package install') - }, - }, - }) - - await mockexec({ - ...baseOpts, - args: [`file:${resolve(path, 'a')}`, 'resfile'], - cache, - npxCache, - path, - runPath, - }) - - const res = fs.readFileSync(resolve(path, 'resfile')).toString() - t.equal(res, 'LOCAL PKG', 'should run local pkg bin script') -}) - -t.test('global space pkg', async t => { - const pkg = { - name: 'a', - bin: { - a: 'index.js', - }, - } - const path = t.testdir({ - cache: {}, - npxCache: {}, - global: { - node_modules: { - '.bin': {}, - a: { - 'index.js': `#!/usr/bin/env node - require('fs').writeFileSync(process.argv.slice(2)[0], 'GLOBAL PKG')`, - 'package.json': JSON.stringify(pkg), - }, - }, - }, - }) - const globalBin = resolve(path, 'global/node_modules/.bin') - const globalPath = resolve(path, 'global') - const runPath = path - - const executable = resolve(path, 'global/node_modules/a') - fs.chmodSync(executable, 0o775) - - await binLinks({ - path: resolve(path, 'global/node_modules/a'), - pkg, - }) - - await libexec({ - ...baseOpts, - args: ['a', 'resfile'], - globalBin, - globalPath, - path, - runPath, - }) - - const res = fs.readFileSync(resolve(path, 'resfile')).toString() - t.equal(res, 'GLOBAL PKG', 'should run local pkg bin script') -}) - -t.test('global scoped pkg', async t => { - const pkg = { - name: '@ruyadorno/create-test', - bin: { - 'create-test': 'index.js', - }, - } - const path = t.testdir({ - cache: {}, - npxCache: {}, - global: { - node_modules: { - '.bin': {}, - '@ruyadorno': { - 'create-test': { - 'index.js': `#!/usr/bin/env node - require('fs').writeFileSync(process.argv.slice(2)[0], 'GLOBAL PKG')`, - 'package.json': JSON.stringify(pkg), - }, - }, - }, - }, - }) - const globalBin = resolve(path, 'global/node_modules/.bin') - const globalPath = resolve(path, 'global') - const runPath = path - - await binLinks({ - path: resolve(path, 'global/node_modules/@ruyadorno/create-test'), - pkg, - }) - - await libexec({ - ...baseOpts, - args: ['@ruyadorno/create-test', 'resfile'], - globalBin, - globalPath, - path, - runPath, - }) - - const res = fs.readFileSync(resolve(path, 'resfile')).toString() - t.equal(res, 'GLOBAL PKG', 'should run global pkg bin script') -}) - -t.test('run from registry - no local packages', async t => { - const testdir = t.testdir({ - cache: {}, - npxCache: {}, - global: { - lib: {}, - bin: {}, - }, - work: {}, - }) - const path = resolve(testdir, 'work') - - t.throws( - () => fs.statSync(resolve(path, 'index.js')), - { code: 'ENOENT' }, - 'should not have template file' - ) - - await libexec({ - ...baseOpts, - args: ['@ruyadorno/create-index'], - cache: resolve(testdir, 'cache'), - globalPath: resolve(testdir, 'global'), - npxCache: resolve(testdir, 'npxCache'), - path, - runPath: path, - }) - - t.ok(fs.statSync(resolve(path, 'index.js')).isFile(), 'ran create pkg') -}) - -t.test('run from registry - local version mismatch', async t => { - const path = t.testdir({ - cache: {}, - npxCache: {}, - node_modules: { - '@ruyadorno': { - 'create-index': { - 'package.json': JSON.stringify({ - name: '@ruyadorno/create-index', - version: '2.0.0', - }), - }, - }, - }, - 'package.json': JSON.stringify({ - name: 'pkg', - dependencies: { - '@ruyadorno/create-index': '^2.0.0', - }, - }), - }) - const runPath = path - const cache = resolve(path, 'cache') - const npxCache = resolve(path, 'npxCache') - - await libexec({ - ...baseOpts, - args: ['@ruyadorno/create-index@1.0.0'], - cache, - npxCache, - path, - runPath, - }) - - t.ok(fs.statSync(resolve(path, 'index.js')).isFile(), 'ran create pkg from registry') -}) - -t.test('run from registry - local range mismatch', async t => { - const path = t.testdir({ - cache: {}, - npxCache: {}, - node_modules: { - '@ruyadorno': { - 'create-index': { - 'package.json': JSON.stringify({ - name: '@ruyadorno/create-index', - version: '2.0.0', - }), - }, - }, - }, - 'package.json': JSON.stringify({ - name: 'pkg', - dependencies: { - '@ruyadorno/create-index': '^2.0.0', - }, - }), - }) - const runPath = path - const cache = resolve(path, 'cache') - const npxCache = resolve(path, 'npxCache') - - await libexec({ - ...baseOpts, - args: ['@ruyadorno/create-index@^1.0.0'], - cache, - npxCache, - path, - runPath, - }) - - t.ok(fs.statSync(resolve(path, 'index.js')).isFile(), 'ran create pkg from registry') -}) - -t.test('run from registry - local tag mismatch', async t => { - const path = t.testdir({ - cache: {}, - npxCache: {}, - node_modules: { - '@ruyadorno': { - 'create-index': { - 'package.json': JSON.stringify({ - name: '@ruyadorno/create-index', - version: '2.0.0', - }), - }, - }, - '.package-lock.json': JSON.stringify({ - name: 'lock', - lockfileVersion: 3, - requires: true, - packages: { - 'node_modules/@ruyadorno/create-index': { - version: '2.0.0', - resolved: 'https://registry.npmjs.org/@ruyadorno/create-index/-/create-index-2.0.0.tgz', - bin: { - 'create-index': 'create-index.js', - }, - }, - }, - - }), - }, - 'package.json': JSON.stringify({ - name: 'pkg', - dependencies: { - '@ruyadorno/create-index': '^2.0.0', - }, - }), - }) - const runPath = path - const cache = resolve(path, 'cache') - const npxCache = resolve(path, 'npxCache') - - await libexec({ - ...baseOpts, - args: ['@ruyadorno/create-index@latest'], - cache, - npxCache, - path, - runPath, - }) - - t.ok(fs.statSync(resolve(path, 'index.js')).isFile(), 'ran create pkg from registry') -}) - -t.test('avoid install when exec from registry an available pkg', async t => { - const testdir = t.testdir({ - cache: {}, - npxCache: {}, - work: {}, - }) - const path = resolve(testdir, 'work') - const runPath = path - const cache = resolve(testdir, 'cache') - const npxCache = resolve(testdir, 'npxCache') - - t.throws( - () => fs.statSync(resolve(path, 'index.js')), - { code: 'ENOENT' }, - 'should not have template file' - ) - - await libexec({ - ...baseOpts, - args: ['@ruyadorno/create-index'], - cache, - npxCache, - path, - runPath, - }) - - t.ok(fs.statSync(resolve(path, 'index.js')).isFile(), 'ran create pkg') - fs.unlinkSync(resolve(path, 'index.js')) - - await libexec({ - ...baseOpts, - args: ['@ruyadorno/create-index'], - cache, - npxCache, - path, - runPath, - }) - - t.ok(fs.statSync(resolve(path, 'index.js')).isFile(), 'ran create pkg again') -}) - -t.test('run multiple from registry', async t => { - const testdir = t.testdir({ - cache: {}, - npxCache: {}, - work: {}, - }) - const path = resolve(testdir, 'work') - const runPath = path - const cache = resolve(testdir, 'cache') - const npxCache = resolve(testdir, 'npxCache') - - t.throws( - () => fs.statSync(resolve(path, 'index.js')), - { code: 'ENOENT' }, - 'should not have index template file' - ) - - t.throws( - () => fs.statSync(resolve(path, 'test.js')), - { code: 'ENOENT' }, - 'should not have test template file' - ) - - await libexec({ - ...baseOpts, - packages: ['@ruyadorno/create-test', '@ruyadorno/create-index'], - call: 'create-test && create-index', - cache, - npxCache, - path, - runPath, - }) - - t.ok(fs.statSync(resolve(path, 'index.js')).isFile(), 'ran index pkg') - t.ok(fs.statSync(resolve(path, 'test.js')).isFile(), 'ran test pkg') -}) +const { setup } = require('./fixtures/setup.js') t.test('no args', async t => { - const path = t.testdir({}) - const runPath = path - const mockexec = t.mock('../lib/index.js', { - '../lib/run-script': ({ args }) => { - t.ok(args.length === 0, 'should call run-script with no args') - }, - }) - - await mockexec({ - ...baseOpts, - path, - runPath, - }) -}) - -t.test('prompt, accepts', async t => { - const testdir = t.testdir({ - cache: {}, - npxCache: {}, - work: {}, - }) - const path = resolve(testdir, 'work') - const runPath = path - const cache = resolve(testdir, 'cache') - const npxCache = resolve(testdir, 'npxCache') - t.test('with clearProgress function', async t => { - const mockexec = t.mock('../lib/index.js', { - '@npmcli/ci-detect': () => false, - npmlog: { - clearProgress () { - t.ok(true, 'should call clearProgress function') - }, - disableProgress () {}, - enableProgress () {}, - }, - read (opts, cb) { - cb(null, 'y') - }, - '../lib/no-tty.js': () => false, - }) - - await mockexec({ - ...baseOpts, - args: ['@ruyadorno/create-index'], - cache, - npxCache, - path, - runPath, - yes: undefined, - }) - - const installedDir = resolve(npxCache, - '0e8e15840a234288/node_modules/@ruyadorno/create-index/package.json') - t.ok(fs.statSync(installedDir).isFile(), 'installed required packages') - }) - - t.test('without clearProgress function', async t => { - const mockexec = t.mock('../lib/index.js', { - '@npmcli/ci-detect': () => false, - read (opts, cb) { - cb(null, 'y') - }, - '../lib/no-tty.js': () => false, - }) - - await mockexec({ - ...baseOpts, - args: ['@ruyadorno/create-index'], - cache, - npxCache, - path, - runPath, - yes: undefined, - }) - - const installedDir = resolve(npxCache, - '0e8e15840a234288/node_modules/@ruyadorno/create-index/package.json') - t.ok(fs.statSync(installedDir).isFile(), 'installed required packages') - }) -}) - -t.test('prompt, refuses', async t => { - const testdir = t.testdir({ - cache: {}, - npxCache: {}, - work: {}, - }) - const path = resolve(testdir, 'work') - const runPath = path - const cache = resolve(testdir, 'cache') - const npxCache = resolve(testdir, 'npxCache') - t.test('with clearProgress function', async t => { - const mockexec = t.mock('../lib/index.js', { - '@npmcli/ci-detect': () => false, - npmlog: { - clearProgress () { - t.ok(true, 'should call clearProgress function') - }, - disableProgess () {}, - }, - read (opts, cb) { - cb(null, 'n') - }, - '../lib/no-tty.js': () => false, - }) - - await t.rejects( - mockexec({ - ...baseOpts, - args: ['@ruyadorno/create-index'], - cache, - npxCache, - path, - runPath, - yes: undefined, - }), - /canceled/, - 'should throw with canceled error' - ) - - const installedDir = resolve(npxCache, - '0e8e15840a234288/node_modules/@ruyadorno/create-index/package.json') - - t.throws( - () => fs.statSync(installedDir), - { code: 'ENOENT' }, - 'should not have installed required packages' - ) - }) + t.plan(1) - t.test('without clearProgress function', async t => { - const mockexec = t.mock('../lib/index.js', { - '@npmcli/ci-detect': () => false, - read (opts, cb) { - cb(null, 'n') - }, - '../lib/no-tty.js': () => false, - }) - - await t.rejects( - mockexec({ - ...baseOpts, - args: ['@ruyadorno/create-index'], - cache, - npxCache, - path, - runPath, - yes: undefined, - }), - /canceled/, - 'should throw with canceled error' - ) - - const installedDir = resolve(npxCache, - '0e8e15840a234288/node_modules/@ruyadorno/create-index/package.json') - - t.throws( - () => fs.statSync(installedDir), - { code: 'ENOENT' }, - 'should not have installed required packages' - ) - }) -}) - -t.test('prompt, -n', async t => { - const testdir = t.testdir({ - cache: {}, - npxCache: {}, - work: {}, - }) - const path = resolve(testdir, 'work') - const runPath = path - const cache = resolve(testdir, 'cache') - const npxCache = resolve(testdir, 'npxCache') - - await t.rejects( - libexec({ - ...baseOpts, - args: ['@ruyadorno/create-index'], - cache, - npxCache, - path, - runPath, - yes: false, - }), - /canceled/, - 'should throw with canceled error' - ) - - const installedDir = resolve(npxCache, - '0e8e15840a234288/node_modules/@ruyadorno/create-index/package.json') - - t.throws( - () => fs.statSync(installedDir), - { code: 'ENOENT' }, - 'should not have installed required packages' - ) -}) - -t.test('no prompt if no tty', async t => { - const testdir = t.testdir({ - cache: {}, - npxCache: {}, - work: {}, - }) - const path = resolve(testdir, 'work') - const runPath = path - const cache = resolve(testdir, 'cache') - const npxCache = resolve(testdir, 'npxCache') - const mockexec = t.mock('../lib/index.js', { - '../lib/no-tty.js': () => true, - }) - - await mockexec({ - ...baseOpts, - args: ['@ruyadorno/create-index'], - cache, - npxCache, - path, - runPath, - yes: undefined, - }) - - const installedDir = resolve(npxCache, - '0e8e15840a234288/node_modules/@ruyadorno/create-index/package.json') - t.ok(fs.statSync(installedDir).isFile(), 'installed required packages') -}) - -t.test('no prompt if CI', async t => { - const testdir = t.testdir({ - cache: {}, - npxCache: {}, - work: {}, - }) - const path = resolve(testdir, 'work') - const runPath = path - const cache = resolve(testdir, 'cache') - const npxCache = resolve(testdir, 'npxCache') - const mockexec = t.mock('../lib/index.js', { - '@npmcli/ci-detect': () => true, - }) - - await mockexec({ - ...baseOpts, - args: ['@ruyadorno/create-index'], - cache, - npxCache, - path, - runPath, - yes: undefined, - }) - - const installedDir = resolve(npxCache, - '0e8e15840a234288/node_modules/@ruyadorno/create-index/package.json') - t.ok(fs.statSync(installedDir).isFile(), 'installed required packages') -}) - -t.test('no prompt if CI, multiple packages', async t => { - const testdir = t.testdir({ - cache: {}, - npxCache: {}, - work: {}, - }) - const path = resolve(testdir, 'work') - const runPath = path - const cache = resolve(testdir, 'cache') - const npxCache = resolve(testdir, 'npxCache') - const mockexec = t.mock('../lib/index.js', { - '@npmcli/ci-detect': () => true, - 'proc-log': { - warn (title, msg) { - t.equal(title, 'exec', 'should warn exec title') - // this message is nondeterministic as it queries manifests so we just - // test the constituent parts - t.match( - msg, - 'The following packages were not found and will be installed:', - 'should warn installing packages' - ) - t.match(msg, '@ruyadorno/create-index@1.0.0', 'includes package being installed') - t.match(msg, '@ruyadorno/create-test@1.0.0', 'includes package being installed') + const { exec } = setup(t, { + mocks: { + '../../lib/run-script': ({ args }) => { + t.ok(args.length === 0, 'should call run-script with no args') }, }, }) - await mockexec({ - ...baseOpts, - call: 'create-index', - packages: ['@ruyadorno/create-index', '@ruyadorno/create-test'], - cache, - npxCache, - path, - runPath, - yes: undefined, - }) -}) - -t.test('defaults', async t => { - const testdir = t.testdir({ - cache: {}, - npxCache: {}, - work: {}, - }) - const cache = resolve(testdir, 'cache') - const npxCache = resolve(testdir, 'npxCache') - const workdir = resolve(testdir, 'work') - - const cwd = process.cwd() - process.chdir(workdir) - t.teardown(() => { - process.chdir(cwd) - }) - - await libexec({ - args: ['@ruyadorno/create-index'], - cache, - npxCache, - yes: true, - }) - - t.ok(fs.statSync(resolve(workdir, 'index.js')).isFile(), - 'ran create-index pkg') -}) - -t.test('scriptShell default value', t => { - t.test('/bin/sh platforms', t => { - t.plan(1) - const mockexec = t.mock('../lib/index.js', { - '../lib/is-windows.js': false, - '../lib/run-script.js': (opt) => { - t.equal(opt.scriptShell, 'sh', 'should use expected shell value') - }, - }) - mockexec({ args: [], runPath: t.testDirName }) - }) - - t.test('win32 defined ComSpec env var', t => { - t.plan(1) - const comspec = process.env.ComSpec - process.env.ComSpec = 'CMD' - const mockexec = t.mock('../lib/index.js', { - '../lib/is-windows.js': true, - '../lib/run-script.js': ({ scriptShell }) => { - t.equal(scriptShell, 'CMD', 'should use expected ComSpec value') - process.env.ComSpec = comspec - }, - }) - mockexec({ args: [], runPath: t.testDirName }) - }) - - t.test('win32 cmd', t => { - t.plan(1) - const comspec = process.env.ComSpec - process.env.ComSpec = '' - const mockexec = t.mock('../lib/index.js', { - '../lib/is-windows.js': true, - '../lib/run-script.js': ({ scriptShell }) => { - t.equal(scriptShell, 'cmd', 'should use expected cmd default value') - process.env.ComSpec = comspec - }, - }) - mockexec({ args: [], runPath: t.testDirName }) - }) - - t.end() -}) - -t.test('workspaces', async t => { - const pkg = { - name: '@ruyadorno/create-index', - version: '2.0.0', - bin: { - 'create-index': './index.js', - }, - } - const path = t.testdir({ - cache: {}, - npxCache: {}, - node_modules: { - '.bin': {}, - '@ruyadorno': { - 'create-index': { - 'package.json': JSON.stringify(pkg), - 'index.js': `#!/usr/bin/env node - require('fs').writeFileSync('resfile', 'LOCAL PKG')`, - }, - }, - a: t.fixture('symlink', '../a'), - }, - 'package.json': JSON.stringify({ - name: 'project', - workspaces: ['a'], - }), - a: { - 'package.json': JSON.stringify({ - name: 'a', - version: '1.0.0', - dependencies: { - '@ruyadorno/create-index': '^2.0.0', - }, - }), - }, - }) - const runPath = path - const cache = resolve(path, 'cache') - const npxCache = resolve(path, 'npxCache') - - const executable = - resolve(path, 'node_modules/@ruyadorno/create-index/index.js') - fs.chmodSync(executable, 0o775) - - await binLinks({ - path: resolve(path, 'node_modules/@ruyadorno/create-index'), - pkg, - }) - - // runs at the project level - await libexec({ - ...baseOpts, - args: ['create-index'], - localBin: resolve(path, 'node_modules/.bin'), - cache, - npxCache, - path, - runPath, - }) - - const res = fs.readFileSync(resolve(path, 'resfile')).toString() - t.equal(res, 'LOCAL PKG', 'should run existing bin from project level') - - // runs at the child workspace level - await libexec({ - ...baseOpts, - args: ['create-index'], - cache, - npxCache, - localBin: resolve(path, 'a/node_modules/.bin'), - path: resolve(path, 'a'), - runPath: resolve(path, 'a'), - }) - - const wRes = fs.readFileSync(resolve(path, 'a/resfile')).toString() - t.equal(wRes, 'LOCAL PKG', 'should run existing bin from workspace level') + await exec() }) diff --git a/workspaces/libnpmexec/test/local.js b/workspaces/libnpmexec/test/local.js new file mode 100644 index 0000000000000..f1d98ff3f4ce5 --- /dev/null +++ b/workspaces/libnpmexec/test/local.js @@ -0,0 +1,350 @@ +const log = require('proc-log') +const { resolve } = require('path') +const t = require('tap') +const { setup, createPkg, merge } = require('./fixtures/setup.js') + +t.test('bin in local pkg', async t => { + const { pkg, fixtures } = createPkg({ + versions: ['1.0.0'], + name: '@npmcli/local-pkg-bin-test', + bin: { + b: 'does-not-exist.js', + a: 'local-bin-test.js', + 'a-nested': 'bin-dir/nested-bin-test.js', + 'conflicting-bin': 'conflicting-bin.js', + }, + files: { + 'local-bin-test.js': { key: 'local-bin', value: 'LOCAL PKG' }, + 'conflicting-bin.js': { key: 'conflicting-bin', value: 'LOCAL PKG' }, + 'bin-dir': { + 'nested-bin-test.js': { key: 'nested-bin', value: 'LOCAL PKG' }, + }, + }, + }) + + const { exec, chmod, readOutput, rimraf, registry, path } = setup(t, { + pkg, + testdir: { + ...fixtures.packages[`@npmcli-local-pkg-bin-test-1.0.0`], + node_modules: { + '.bin': { + 'conflicting-bin': { key: 'existing-bin', value: 'NODEMODULES PKG' }, + }, + '@npmcli': { + 'some-other-pkg-with-same-scope': {}, + }, + }, + }, + }) + + const localBin = resolve(path, 'node_modules', '.bin') + + await chmod('local-bin-test.js') + await chmod('bin-dir/nested-bin-test.js') + await chmod('conflicting-bin.js') + await chmod('node_modules/.bin/conflicting-bin') + + await exec({ localBin, args: ['a', 'argument-a'] }) + + t.match(await readOutput('local-bin'), { + value: 'LOCAL PKG', + args: ['argument-a'], + }) + + // remove the existing scope dir from node_modules so that the next run + // will have to create and cleanup that directory + await rimraf('node_modules/@npmcli') + + await exec({ localBin, args: ['a-nested', 'argument-a-nested'] }) + + t.match(await readOutput('nested-bin'), { + value: 'LOCAL PKG', + args: ['argument-a-nested'], + }) + + await exec({ localBin, args: ['conflicting-bin'] }) + + t.match(await readOutput('existing-bin'), { + value: 'NODEMODULES PKG', + }) + + // this will hit the registry because the file does not exist + registry.nock.get('/b').times(2).reply(404) + await t.rejects(() => exec({ localBin, args: ['b'] })) +}) + +t.test('locally available pkg - by scoped name only', async t => { + const { pkg, fixtures } = createPkg({ + name: '@npmcli/npx-local-test', + localVersion: '2.0.0', + }) + + const { exec, chmod, binLinks, readOutput } = setup(t, { + pkg, + testdir: fixtures, + }) + + await chmod() + await binLinks() + await exec({ args: ['@npmcli/npx-local-test', 'arg'] }) + + t.match(await readOutput(), { + value: 'local-2.0.0', + args: ['arg'], + }) +}) + +t.test('locally available pkg - by name', async t => { + const { pkg, fixtures } = createPkg({ + name: '@npmcli/create-index', + localVersion: '2.0.0', + }) + + const { chmod, binLinks, exec, readOutput } = setup(t, { + pkg, + testdir: fixtures, + }) + + await chmod() + await binLinks() + await exec({ + packages: ['@npmcli/create-index'], + call: 'create-index arg', + }) + + t.match(await readOutput(), { + value: 'local-2.0.0', + args: ['arg'], + }) +}) + +t.test('locally available pkg - by version', async t => { + const { pkg, fixtures } = createPkg({ + name: '@npmcli/create-index', + localVersion: '1.0.0', + }) + const { chmod, binLinks, exec, readOutput } = setup(t, { + pkg, + testdir: fixtures, + }) + + await chmod() + await binLinks() + await exec({ args: ['@npmcli/create-index@1.0.0'] }) + + t.match(await readOutput(), { + value: 'local-1.0.0', + args: [], + }) +}) + +t.test('locally available pkg - by range', async t => { + const { pkg, fixtures } = createPkg({ + name: '@npmcli/create-index', + localVersion: '2.0.0', + }) + const { chmod, binLinks, exec, readOutput } = setup(t, { + pkg, + testdir: fixtures, + }) + + await chmod() + await binLinks() + await exec({ + packages: ['@npmcli/create-index@^2.0.0'], + call: 'create-index resfile', + }) + + t.match(await readOutput(), { + value: 'local-2.0.0', + args: ['resfile'], + }) +}) + +t.test('locally available pkg - by latest tag', async t => { + const { pkg, fixtures, package } = createPkg({ + name: '@npmcli/create-index', + localVersion: '1.0.0', + }) + const { chmod, binLinks, exec, readOutput, registry, path } = setup(t, { + pkg, + testdir: merge(fixtures, { + node_modules: { + '.package-lock.json': { + name: 'lock', + lockfileVersion: 3, + requires: true, + packages: { + [`node_modules/${pkg.name}`]: { + ...pkg, + resolved: `{REGISTRY}/${pkg.name}/-/create-index-${pkg.version}.tgz`, + }, + }, + }, + }, + }), + }) + + // latest forces the manifest to be fetched + await package({ registry, path, times: 1, tarballs: [] }) + + await chmod() + await binLinks() + await exec({ + packages: ['@npmcli/create-index@latest'], + call: 'create-index resfile', + }) + + t.match(await readOutput(), { + value: 'local-1.0.0', + args: ['resfile'], + }) +}) + +t.test('multiple local pkgs', async t => { + const pkgFoo = createPkg({ + name: '@npmcli/create-foo', + localVersion: '2.0.0', + }) + + const pkgBar = createPkg({ + name: '@npmcli/create-bar', + localVersion: '1.0.0', + }) + + const { readOutput, chmod, exec, binLinks } = setup(t, { + pkg: [pkgFoo.pkg, pkgBar.pkg], + testdir: merge(pkgFoo.fixtures, pkgBar.fixtures), + }) + + await chmod() + await binLinks() + + await exec({ + packages: ['@npmcli/create-foo', '@npmcli/create-bar'], + call: 'create-foo resfile && create-bar bar', + }) + + t.match(await readOutput('@npmcli-create-foo'), { + value: 'local-2.0.0', + args: ['resfile'], + }) + t.match(await readOutput('@npmcli-create-bar'), { + value: 'local-1.0.0', + args: ['bar'], + }) +}) + +t.test('no npxCache', async t => { + const { chmod, exec, path } = setup(t, { + testdir: { + npxCache: null, + a: { + 'package.json': { + name: 'a', + bin: { + a: './index.js', + }, + }, + 'index.js': { key: 'a', value: 'LOCAL PKG' }, + }, + }, + }) + + await chmod('a/index.js') + + await t.rejects(() => exec({ + args: [`file:${resolve(path, 'a')}`, 'resfile'], + }), /Must provide a valid npxCache path/) +}) + +t.test('local file system path', async t => { + const { exec, chmod, readOutput, path } = setup(t, { + mocks: { + '@npmcli/ci-detect': () => true, + 'proc-log': { + ...log, + warn () { + t.fail('should not warn about local file package install') + }, + }, + }, + testdir: { + a: { + 'package.json': { + name: 'a', + bin: { + a: './index.js', + }, + }, + 'index.js': { key: 'a', value: 'LOCAL PKG' }, + }, + }, + }) + + await chmod('a/index.js') + + await exec({ + args: [`file:${resolve(path, 'a')}`, 'resfile'], + + }) + + t.match(await readOutput('a'), { + value: 'LOCAL PKG', + args: ['resfile'], + }) +}) + +t.test('global space pkg', async t => { + const { pkg, fixtures } = createPkg({ + name: 'a', + localVersion: '1.0.0', + }) + + const { exec, chmod, readOutput, binLinks } = setup(t, { + pkg, + global: true, + testdir: fixtures, + }) + + await chmod() + await binLinks() + + await exec({ + args: ['a', 'resfile'], + }) + + t.match(await readOutput(), { + value: 'local-1.0.0', + args: [], + created: 'global/node_modules/a/bin-file.js', + }) +}) + +t.test('global scoped pkg', async t => { + const { pkg, fixtures, package } = createPkg({ + localVersion: '1.0.0', + name: '@npmcli/create-test', + }) + + const { chmod, exec, readOutput, binLinks, registry, path } = setup(t, { + pkg, + global: true, + testdir: fixtures, + }) + + await chmod() + await binLinks() + + await package({ registry, path, times: 1, tarballs: [] }) + + await exec({ + args: ['@npmcli/create-test', 'resfile'], + }) + + t.match(await readOutput(), { + value: 'local-1.0.0', + args: ['resfile'], + created: 'global/node_modules/@npmcli/create-test/bin-file.js', + }) +}) diff --git a/workspaces/libnpmexec/test/prompt.js b/workspaces/libnpmexec/test/prompt.js new file mode 100644 index 0000000000000..f0d164cecaf5a --- /dev/null +++ b/workspaces/libnpmexec/test/prompt.js @@ -0,0 +1,384 @@ +const log = require('proc-log') +const { resolve } = require('path') +const t = require('tap') +const fs = require('fs/promises') +const { setup, createPkg, merge } = require('./fixtures/setup.js') + +t.test('prompt, accepts', async t => { + t.test('with clearProgress function', async t => { + const { pkg, package, fixtures } = createPkg({ + name: '@npmcli/create-index', + version: '1.0.0', + }) + const { exec, path, registry } = setup(t, { + testdir: fixtures, + mocks: { + '@npmcli/ci-detect': () => false, + '../../lib/no-tty.js': () => false, + npmlog: { + clearProgress () { + t.ok(true, 'should call clearProgress function') + }, + disableProgress () {}, + enableProgress () {}, + }, + read (_, cb) { + cb(null, 'y') + }, + }, + }) + + await package({ registry, path }) + + await exec({ + args: ['@npmcli/create-index'], + yes: undefined, + }) + + const installedDir = `npxCache/e7ce50d8d2d8ec11/node_modules/${pkg.name}/package.json` + t.ok(await fs.stat(resolve(path, installedDir)).then(f => f.isFile())) + }) + + t.test('without clearProgress function', async t => { + const { pkg, package, fixtures } = createPkg({ + name: '@npmcli/create-index', + version: '1.0.0', + }) + const { exec, path, registry } = setup(t, { + testdir: fixtures, + mocks: { + '@npmcli/ci-detect': () => false, + '../../lib/no-tty.js': () => false, + read (_, cb) { + cb(null, 'y') + }, + }, + }) + + await package({ registry, path }) + + await exec({ + args: ['@npmcli/create-index'], + yes: undefined, + }) + + const installedDir = `npxCache/e7ce50d8d2d8ec11/node_modules/${pkg.name}/package.json` + t.ok(await fs.stat(resolve(path, installedDir)).then(f => f.isFile())) + }) +}) + +t.test('prompt, refuses', async t => { + t.test('with clearProgress function', async t => { + t.plan(3) + + const { pkg, package, fixtures } = createPkg({ + name: '@npmcli/create-index', + version: '1.0.0', + }) + const { exec, path, registry } = setup(t, { + testdir: fixtures, + mocks: { + '@npmcli/ci-detect': () => false, + npmlog: { + clearProgress () { + t.ok(true, 'should call clearProgress function') + }, + disableProgess () {}, + }, + read (_, cb) { + cb(null, 'n') + }, + '../../lib/no-tty.js': () => false, + }, + }) + + await package({ registry, path, times: 1, tarballs: [] }) + + await t.rejects( + exec({ + args: ['@npmcli/create-index'], + yes: undefined, + }), + /canceled/, + 'should throw with canceled error' + ) + + const installedDir = `npxCache/e7ce50d8d2d8ec11/node_modules/${pkg.name}/package.json` + t.rejects( + () => fs.stat(resolve(path, installedDir)), + { code: 'ENOENT' } + ) + }) + + t.test('without clearProgress function', async t => { + const { pkg, package, fixtures } = createPkg({ + name: '@npmcli/create-index', + version: '1.0.0', + }) + const { exec, path, registry } = setup(t, { + testdir: fixtures, + mocks: { + '@npmcli/ci-detect': () => false, + read (_, cb) { + cb(null, 'n') + }, + '../../lib/no-tty.js': () => false, + }, + }) + + await package({ registry, path, times: 1, tarballs: [] }) + + await t.rejects( + exec({ + args: ['@npmcli/create-index'], + yes: undefined, + }), + /canceled/, + 'should throw with canceled error' + ) + + const installedDir = `npxCache/e7ce50d8d2d8ec11/node_modules/${pkg.name}/package.json` + t.rejects( + () => fs.stat(resolve(path, installedDir)), + { code: 'ENOENT' } + ) + }) +}) + +t.test('prompt, -n', async t => { + const { pkg, package, fixtures } = createPkg({ + name: '@npmcli/create-index', + version: '1.0.0', + }) + const { exec, path, registry } = setup(t, { + testdir: fixtures, + }) + + await package({ registry, path, times: 1, tarballs: [] }) + + await t.rejects( + exec({ + args: ['@npmcli/create-index'], + yes: false, + }), + /canceled/, + 'should throw with canceled error' + ) + + const installedDir = `npxCache/e7ce50d8d2d8ec11/node_modules/${pkg.name}/package.json` + t.rejects( + () => fs.stat(resolve(path, installedDir)), + { code: 'ENOENT' } + ) +}) + +t.test('no prompt if no tty', async t => { + const { pkg, package, fixtures } = createPkg({ + name: '@npmcli/create-index', + version: '1.0.0', + }) + const { exec, path, registry } = setup(t, { + testdir: fixtures, + mocks: { '../../lib/no-tty.js': () => true }, + }) + + await package({ registry, path }) + + await exec({ + args: ['@npmcli/create-index'], + yes: undefined, + }) + + const installedDir = `npxCache/e7ce50d8d2d8ec11/node_modules/${pkg.name}/package.json` + t.ok(await fs.stat(resolve(path, installedDir)).then(f => f.isFile())) +}) + +t.test('no prompt if CI', async t => { + const { pkg, package, fixtures } = createPkg({ + name: '@npmcli/create-index', + version: '1.0.0', + }) + const { exec, path, registry } = setup(t, { + testdir: fixtures, + mocks: { '@npmcli/ci-detect': () => true }, + }) + + await package({ registry, path }) + + await exec({ + args: ['@npmcli/create-index'], + yes: undefined, + }) + + const installedDir = `npxCache/e7ce50d8d2d8ec11/node_modules/${pkg.name}/package.json` + t.ok(await fs.stat(resolve(path, installedDir)).then(f => f.isFile())) +}) + +t.test('no prompt if CI, multiple packages', async t => { + t.plan(4) + + const pkgIndex = createPkg({ + name: '@npmcli/create-index', + version: '1.0.0', + }) + const pkgTest = createPkg({ + name: '@npmcli/create-test', + version: '1.0.0', + }) + const { exec, path, registry } = setup(t, { + testdir: merge(pkgIndex.fixtures, pkgTest.fixtures), + mocks: { + '@npmcli/ci-detect': () => true, + 'proc-log': { + ...log, + warn (title, msg) { + t.equal(title, 'exec', 'should warn exec title') + // this message is nondeterministic as it queries manifests so we just + // test the constituent parts + t.match( + msg, + 'The following packages were not found and will be installed:', + 'should warn installing packages' + ) + t.match(msg, '@npmcli/create-index@1.0.0', 'includes package being installed') + t.match(msg, '@npmcli/create-test@1.0.0', 'includes package being installed') + }, + }, + }, + }) + + await pkgIndex.package({ path, registry }) + await pkgTest.package({ path, registry }) + + await exec({ + call: 'create-index', + packages: ['@npmcli/create-index', '@npmcli/create-test'], + yes: undefined, + }) +}) + +t.test('defaults', async t => { + const { pkg, fixtures, package } = createPkg({ + name: '@npmcli/create-index', + version: '1.0.0', + }) + const { exec, path, registry, readOutput } = setup(t, { + pkg, + defaults: false, + execPath: '../../../lib/index.js', + testdir: { + ...fixtures, + work: {}, + }, + }) + + const workDir = resolve(path, 'work') + const cwd = process.cwd() + process.chdir(workDir) + t.teardown(() => process.chdir(cwd)) + + await package({ registry, path }) + + await exec({ + args: ['@npmcli/create-index'], + yes: true, + }) + + t.match(await readOutput('', { root: workDir }), { + value: 'packages-1.0.0', + args: [], + }) +}) + +t.test('scriptShell default value', async t => { + await t.test('/bin/sh platforms', async t => { + t.plan(1) + const mockexec = t.mock('../lib/index.js', { + '../lib/is-windows.js': false, + '../lib/run-script.js': (opt) => { + t.equal(opt.scriptShell, 'sh', 'should use expected shell value') + }, + }) + await mockexec({ args: [], runPath: t.testDirName }) + }) + + await t.test('win32 defined ComSpec env var', async t => { + t.plan(1) + const comspec = process.env.ComSpec + process.env.ComSpec = 'CMD' + const mockexec = t.mock('../lib/index.js', { + '../lib/is-windows.js': true, + '../lib/run-script.js': ({ scriptShell }) => { + t.equal(scriptShell, 'CMD', 'should use expected ComSpec value') + process.env.ComSpec = comspec + }, + }) + await mockexec({ args: [], runPath: t.testDirName }) + }) + + await t.test('win32 cmd', async t => { + t.plan(1) + const comspec = process.env.ComSpec + process.env.ComSpec = '' + const mockexec = t.mock('../lib/index.js', { + '../lib/is-windows.js': true, + '../lib/run-script.js': ({ scriptShell }) => { + t.equal(scriptShell, 'cmd', 'should use expected cmd default value') + process.env.ComSpec = comspec + }, + }) + await mockexec({ args: [], runPath: t.testDirName }) + }) +}) + +t.test('workspaces', async t => { + const { pkg, fixtures } = createPkg({ + name: '@npmcli/create-index', + localVersion: '2.0.0', + }) + + const { path, exec, chmod, binLinks, readOutput } = setup(t, { + pkg, + testdir: merge(fixtures, { + 'package.json': { + workspaces: ['a'], + dependencies: null, + }, + a: { + 'package.json': { + name: 'a', + version: '1.0.0', + dependencies: fixtures['package.json'].dependencies, + }, + }, + node_modules: { + a: t.fixture('symlink', '../a'), + }, + }), + }) + + await chmod() + await binLinks() + + // runs at the project level + await exec({ + args: ['create-index'], + localBin: resolve(path, 'node_modules/.bin'), + }) + + t.match(await readOutput(), { + value: 'local-2.0.0', + }) + + // runs at the child workspace level + await exec({ + args: ['create-index'], + localBin: resolve(path, 'a/node_modules/.bin'), + path: resolve(path, 'a'), + runPath: resolve(path, 'a'), + }) + + t.match(await readOutput('', { root: resolve(path, 'a') }), { + value: 'local-2.0.0', + }) +}) diff --git a/workspaces/libnpmexec/test/registry.js b/workspaces/libnpmexec/test/registry.js new file mode 100644 index 0000000000000..45d7cf4654541 --- /dev/null +++ b/workspaces/libnpmexec/test/registry.js @@ -0,0 +1,166 @@ +const { resolve } = require('path') +const t = require('tap') +const { setup, createPkg, merge } = require('./fixtures/setup.js') + +t.test('run from registry - no local packages', async t => { + const { fixtures, package } = createPkg({ versions: ['2.0.0'] }) + + const { exec, path, registry, readOutput } = setup(t, { + testdir: merge(fixtures, { + global: {}, + }), + }) + + await package({ registry, path }) + + await exec({ + args: ['@npmcli/create-index'], + globalPath: resolve(path, 'global'), + }) + + t.match(await readOutput('@npmcli-create-index'), { + value: 'packages-2.0.0', + }) +}) + +t.test('run from registry - local version mismatch', async t => { + const { fixtures, package } = createPkg({ + localVersion: '2.0.0', + versions: ['2.0.0', '1.0.0'], + }) + + const { exec, path, registry, readOutput } = setup(t, { + testdir: { + ...fixtures, + }, + }) + + await package({ registry, path, tarballs: ['1.0.0'] }) + + await exec({ args: ['@npmcli/create-index@1.0.0'] }) + + t.match(await readOutput('@npmcli-create-index'), { + value: 'packages-1.0.0', + }) +}) + +t.test('run from registry - local range mismatch', async t => { + const { fixtures, package } = createPkg({ + localVersion: '2.0.0', + versions: ['2.0.0', '1.0.0'], + }) + + const { exec, path, registry, readOutput } = setup(t, { + testdir: { + ...fixtures, + }, + }) + + await package({ registry, path, tarballs: ['1.0.0'] }) + + await exec({ + args: ['@npmcli/create-index@^1.0.0'], + }) + + t.match(await readOutput('@npmcli-create-index'), { + value: 'packages-1.0.0', + }) +}) + +t.test('run from registry - local tag mismatch', async t => { + const { fixtures, package } = createPkg({ + localVersion: '2.0.0', + versions: ['2.0.0'], + }) + + const { exec, path, registry, readOutput } = setup(t, { + testdir: { + ...fixtures, + }, + }) + + await package({ registry, path }) + + await exec({ + args: ['@npmcli/create-index@latest'], + }) + + t.match(await readOutput('@npmcli-create-index'), { + value: 'packages-2.0.0', + }) +}) + +t.test('avoid install when exec from registry an available pkg', async t => { + const { fixtures, package } = createPkg({ + versions: ['2.0.0'], + }) + + const { exec, path, registry, readOutput, rmOutput } = setup(t, { + testdir: { + ...fixtures, + }, + }) + + await package({ registry, path }) + + // no file + t.rejects(() => readOutput('@npmcli-create-index'), { code: 'ENOENT' }) + + // file is created + await exec({ args: ['@npmcli/create-index'] }) + t.match(await readOutput('@npmcli-create-index'), { + value: 'packages-2.0.0', + }) + + // remove file + await rmOutput('@npmcli-create-index') + t.rejects(() => readOutput('@npmcli-create-index'), { code: 'ENOENT' }) + + // create file again but mock manifest only once + await package({ registry, path, tarballs: [], times: 1 }) + await exec({ args: ['@npmcli/create-index'] }) + t.match(await readOutput('@npmcli-create-index'), { + value: 'packages-2.0.0', + }) +}) + +t.test('run multiple from registry', async t => { + const indexPkg = createPkg({ + versions: ['2.0.0'], + name: '@npmcli/create-index', + }) + const testPkg = createPkg({ + versions: ['2.0.0'], + name: '@npmcli/create-test', + }) + + const { exec, path, registry, readOutput } = setup(t, { + testdir: { + ...merge(indexPkg.fixtures, testPkg.fixtures), + }, + }) + + await indexPkg.package({ registry, path }) + await testPkg.package({ registry, path }) + + t.rejects( + () => readOutput(resolve('@npmcli-create-index')), + { code: 'ENOENT' } + ) + t.rejects( + () => readOutput(resolve('@npmcli-create-test')), + { code: 'ENOENT' } + ) + + await exec({ + packages: ['@npmcli/create-test', '@npmcli/create-index'], + call: 'create-test && create-index', + }) + + t.match(await readOutput('@npmcli-create-index'), { + value: 'packages-2.0.0', + }) + t.match(await readOutput('@npmcli-create-test'), { + value: 'packages-2.0.0', + }) +}) diff --git a/workspaces/libnpmexec/test/registry/content/ruyadorno/create-index.json b/workspaces/libnpmexec/test/registry/content/ruyadorno/create-index.json deleted file mode 100644 index 6118dec7dfd87..0000000000000 --- a/workspaces/libnpmexec/test/registry/content/ruyadorno/create-index.json +++ /dev/null @@ -1,81 +0,0 @@ -{ - "_id": "@ruyadorno/create-index", - "name": "@ruyadorno/create-index", - "dist-tags": { - "latest": "1.0.0" - }, - "versions": { - "1.0.0": { - "name": "@ruyadorno/create-index", - "version": "1.0.0", - "description": "Create an empty index.js file", - "bin": { - "create-index": "create-index.js" - }, - "keywords": [ - "init", - "create", - "index" - ], - "author": { - "name": "Ruy Adorno", - "url": "https://ruyadorno.com" - }, - "license": "MIT", - "gitHead": "0c1b6a4c503d8565439b2b194b4691824a1bc902", - "_id": "@ruyadorno/create-index@1.0.0", - "_nodeVersion": "15.13.0", - "_npmVersion": "7.9.0", - "dist": { - "integrity": "sha512-2T2JRYWtB9/wN8Vr/SRDcjIbKD5IjR5joO8iCCCYjXfDRZ2lYBSnZQ2kGp34F+T8OEavzJfj9sxNt9Y7QT7Oaw==", - "shasum": "a7d15d2ca78c496685b7b2bc24599d4e0983783c", - "tarball": "https://registry.npmjs.org/@ruyadorno/create-index/-/create-index-1.0.0.tgz", - "fileCount": 3, - "unpackedSize": 565, - "npm-signature": "-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgd0fRCRA9TVsSAnZWagAA9ysP/i22HySX0+RYcHUldWcv\neDgd24/wQqNEsQiTrGpIRSSorqbrC5+xoZfFzbvbUA24JaFChgQE1rRtYDab\ntjo5asfUqCspru1X05D3T3lmy3NyBCShqzwsZo88stj8L1w8DcnmU83als4h\n6DqxmwQbPMn+hd5gKtr6ZsUwHZRc/9dXWjn6GI3ztAla73RKXQ4D9Gs/ULyo\nNwS6a/CqThqu4atlA6ZGXum72XsFYSRB712N3Q1l0+8T9L3lAWuitGx/K8L/\n95gxU0e6ME+Wiin62SxH6QYWuVIKD04UNkz14dzfI2RIjT2NDbX6l308uSza\nbWz6aro4w9kUJviDX/hk/o469d+EQ87L+vpFrLDbSfZg8RtvSptHCDdM6mNw\n05xNFji33ujMX54HyGxplioAgnE5X2ZTQuBymsiINHq5gxCn8MSaUxiX45yB\n7Bhf1rWbp5KgiUa0kGXV4eoAutP6HWs1avzkHi9q2xS61wMBdPPHX5GsTTqe\nI+4mdgpNOdQLQjLyCp+ydvSqTHtVHkHDrBJzgkOjDWC7YzDcbzFQt6Fn6uc/\nA4kTlU1yTD2lPz9ICNI6BwqM7aOa9qCVkBL7vWaUUpxblRpzfbKmCtEi704h\nIJ6YZ3z6xwTl59aMXiInOLFsb7upEwtTXTAWqDlsJmTYS7hsVi3gY7wqYp1p\nMwwm\r\n=rrJ8\r\n-----END PGP SIGNATURE-----\r\n" - }, - "_npmUser": { - "name": "ruyadorno", - "email": "ruyadorno@hotmail.com" - }, - "directories": {}, - "maintainers": [ - { - "name": "ruyadorno", - "email": "ruyadorno@hotmail.com" - } - ], - "_npmOperationalInternal": { - "host": "s3://npm-registry-packages", - "tmp": "tmp/create-index_1.0.0_1618429905498_0.11104270815832784" - }, - "_hasShrinkwrap": false - } - }, - "time": { - "created": "2021-04-14T19:51:45.442Z", - "1.0.0": "2021-04-14T19:51:45.650Z", - "modified": "2021-04-14T19:51:47.833Z" - }, - "maintainers": [ - { - "name": "ruyadorno", - "email": "ruyadorno@hotmail.com" - } - ], - "description": "Create an empty index.js file", - "keywords": [ - "init", - "create", - "index" - ], - "author": { - "name": "Ruy Adorno", - "url": "https://ruyadorno.com" - }, - "license": "MIT", - "readme": "# create-index\n\nPuts an empty `index.js` into current working dir. Meant for testing only.\n\n## Usage\n\n`npm exec @ruyadorno/create-index`\n\n", - "readmeFilename": "README.md", - "_cached": false, - "_contentLength": 0 -} diff --git a/workspaces/libnpmexec/test/registry/content/ruyadorno/create-index.min.json b/workspaces/libnpmexec/test/registry/content/ruyadorno/create-index.min.json deleted file mode 100644 index e4e998317585c..0000000000000 --- a/workspaces/libnpmexec/test/registry/content/ruyadorno/create-index.min.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name": "@ruyadorno/create-index", - "dist-tags": { - "latest": "1.0.0" - }, - "versions": { - "1.0.0": { - "name": "@ruyadorno/create-index", - "version": "1.0.0", - "bin": { - "create-index": "create-index.js" - }, - "dist": { - "integrity": "sha512-2T2JRYWtB9/wN8Vr/SRDcjIbKD5IjR5joO8iCCCYjXfDRZ2lYBSnZQ2kGp34F+T8OEavzJfj9sxNt9Y7QT7Oaw==", - "shasum": "a7d15d2ca78c496685b7b2bc24599d4e0983783c", - "tarball": "https://registry.npmjs.org/@ruyadorno/create-index/-/create-index-1.0.0.tgz", - "fileCount": 3, - "unpackedSize": 565, - "npm-signature": "-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgd0fRCRA9TVsSAnZWagAA9ysP/i22HySX0+RYcHUldWcv\neDgd24/wQqNEsQiTrGpIRSSorqbrC5+xoZfFzbvbUA24JaFChgQE1rRtYDab\ntjo5asfUqCspru1X05D3T3lmy3NyBCShqzwsZo88stj8L1w8DcnmU83als4h\n6DqxmwQbPMn+hd5gKtr6ZsUwHZRc/9dXWjn6GI3ztAla73RKXQ4D9Gs/ULyo\nNwS6a/CqThqu4atlA6ZGXum72XsFYSRB712N3Q1l0+8T9L3lAWuitGx/K8L/\n95gxU0e6ME+Wiin62SxH6QYWuVIKD04UNkz14dzfI2RIjT2NDbX6l308uSza\nbWz6aro4w9kUJviDX/hk/o469d+EQ87L+vpFrLDbSfZg8RtvSptHCDdM6mNw\n05xNFji33ujMX54HyGxplioAgnE5X2ZTQuBymsiINHq5gxCn8MSaUxiX45yB\n7Bhf1rWbp5KgiUa0kGXV4eoAutP6HWs1avzkHi9q2xS61wMBdPPHX5GsTTqe\nI+4mdgpNOdQLQjLyCp+ydvSqTHtVHkHDrBJzgkOjDWC7YzDcbzFQt6Fn6uc/\nA4kTlU1yTD2lPz9ICNI6BwqM7aOa9qCVkBL7vWaUUpxblRpzfbKmCtEi704h\nIJ6YZ3z6xwTl59aMXiInOLFsb7upEwtTXTAWqDlsJmTYS7hsVi3gY7wqYp1p\nMwwm\r\n=rrJ8\r\n-----END PGP SIGNATURE-----\r\n" - } - } - }, - "modified": "2021-04-14T19:51:47.833Z", - "_cached": false, - "_contentLength": 1423 -} \ No newline at end of file diff --git a/workspaces/libnpmexec/test/registry/content/ruyadorno/create-index/-/create-index-1.0.0.tgz b/workspaces/libnpmexec/test/registry/content/ruyadorno/create-index/-/create-index-1.0.0.tgz deleted file mode 100644 index d6ddf7570e3fc..0000000000000 Binary files a/workspaces/libnpmexec/test/registry/content/ruyadorno/create-index/-/create-index-1.0.0.tgz and /dev/null differ diff --git a/workspaces/libnpmexec/test/registry/content/ruyadorno/create-test.json b/workspaces/libnpmexec/test/registry/content/ruyadorno/create-test.json deleted file mode 100644 index d193b83cf25df..0000000000000 --- a/workspaces/libnpmexec/test/registry/content/ruyadorno/create-test.json +++ /dev/null @@ -1,81 +0,0 @@ -{ - "_id": "@ruyadorno/create-test", - "name": "@ruyadorno/create-test", - "dist-tags": { - "latest": "1.0.0" - }, - "versions": { - "1.0.0": { - "name": "@ruyadorno/create-test", - "version": "1.0.0", - "description": "Creates an empty test.js file", - "bin": { - "create-test": "create-test.js" - }, - "keywords": [ - "init", - "create", - "test" - ], - "author": { - "name": "Ruy Adorno", - "url": "https://ruyadorno.com" - }, - "license": "MIT", - "gitHead": "707aa293e34f48dcf9cb6b4b452cb1fc8e484c8b", - "_id": "@ruyadorno/create-test@1.0.0", - "_nodeVersion": "15.13.0", - "_npmVersion": "7.9.0", - "dist": { - "integrity": "sha512-WOifELHCU8nmg0yHsPbSETPaNO1orDPhTSflJsomqGFNwVS44qvkWwMPbDE3L2aAglXLf5AxUznyFkxsXgDF2w==", - "shasum": "f0f393449fe5205c54a4ca2181d8355d2372da93", - "tarball": "https://registry.npmjs.org/@ruyadorno/create-test/-/create-test-1.0.0.tgz", - "fileCount": 3, - "unpackedSize": 557, - "npm-signature": "-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgd1SICRA9TVsSAnZWagAAOa4P/jknjgmFaBWz6PCng8qV\nsdfa23GbE4MdmvpB72v6DvNjGQ+51Vgd7PBAJRo+d3LmQ0c2DE/e6PZEgam/\nOtuVbgimxPy85V1MTA66bgML4nFtEOKS/R/Z5s7wMMCrhYqKdMp6ELMUEO07\n7cDIzAmc7WeSLyzhTBC661T0nKPPAf2nKKYXLI+6RisQoXnEgZmgNyNlIt6D\nNDNTRZjaR6s1QvHgWN9h/hLAgKvgaAnSy+JOzcB+SGaClLow3svbvW+klQpA\n8afOTLV4D/pgPDGXvvwDDInH6yccYSOSNiAZgd45hsmo82xIR3n+Cod2qk9Y\njCye36nXzdQTz9A7a3SgH++DV7fA5n87GoahkpGEnKu8gjgMuE6ncDEypbTi\nM4R8JikZrScR2wWXtO+jK4f/5XHVh19ZpqdOrlxzXutkUy0/bMoHFNxcGrXB\n5D1Qk/lOpNO4rd0NoURk6OkpueHOlBHlBNxqrEsltzY2IWs+JICcFaz385H0\nKYyNQrmltEqWVgW+LeFvm3B1sLL5wySqplX/396lC6kCHZyofeeqZFcC1G+m\ntkp0iho63tlm6WjIzw6ddHWu8olNohCk4xFpvNkkZ0u9GR4BaDBRXS60AcoD\nNIYwMuUlqmXAc7ey+xNZCqXokgbtjD7aI2uIDLNUMHELxrRzBccHe76sIQit\nBeBy\r\n=u7P4\r\n-----END PGP SIGNATURE-----\r\n" - }, - "_npmUser": { - "name": "ruyadorno", - "email": "ruyadorno@hotmail.com" - }, - "directories": {}, - "maintainers": [ - { - "name": "ruyadorno", - "email": "ruyadorno@hotmail.com" - } - ], - "_npmOperationalInternal": { - "host": "s3://npm-registry-packages", - "tmp": "tmp/create-test_1.0.0_1618433159830_0.5969279363908722" - }, - "_hasShrinkwrap": false - } - }, - "time": { - "created": "2021-04-14T20:45:59.789Z", - "1.0.0": "2021-04-14T20:45:59.974Z", - "modified": "2021-04-14T20:46:02.139Z" - }, - "maintainers": [ - { - "name": "ruyadorno", - "email": "ruyadorno@hotmail.com" - } - ], - "description": "Creates an empty test.js file", - "keywords": [ - "init", - "create", - "test" - ], - "author": { - "name": "Ruy Adorno", - "url": "https://ruyadorno.com" - }, - "license": "MIT", - "readme": "# create-test\n\nPuts an empty `test.js` into current working dir. Meant for testing only.\n\n## Usage\n\n`npm exec @ruyadorno/create-test`\n\n", - "readmeFilename": "README.md", - "_cached": false, - "_contentLength": 0 -} \ No newline at end of file diff --git a/workspaces/libnpmexec/test/registry/content/ruyadorno/create-test.min.json b/workspaces/libnpmexec/test/registry/content/ruyadorno/create-test.min.json deleted file mode 100644 index cc15084158717..0000000000000 --- a/workspaces/libnpmexec/test/registry/content/ruyadorno/create-test.min.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name": "@ruyadorno/create-test", - "dist-tags": { - "latest": "1.0.0" - }, - "versions": { - "1.0.0": { - "name": "@ruyadorno/create-test", - "version": "1.0.0", - "bin": { - "create-test": "create-test.js" - }, - "dist": { - "integrity": "sha512-WOifELHCU8nmg0yHsPbSETPaNO1orDPhTSflJsomqGFNwVS44qvkWwMPbDE3L2aAglXLf5AxUznyFkxsXgDF2w==", - "shasum": "f0f393449fe5205c54a4ca2181d8355d2372da93", - "tarball": "https://registry.npmjs.org/@ruyadorno/create-test/-/create-test-1.0.0.tgz", - "fileCount": 3, - "unpackedSize": 557, - "npm-signature": "-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgd1SICRA9TVsSAnZWagAAOa4P/jknjgmFaBWz6PCng8qV\nsdfa23GbE4MdmvpB72v6DvNjGQ+51Vgd7PBAJRo+d3LmQ0c2DE/e6PZEgam/\nOtuVbgimxPy85V1MTA66bgML4nFtEOKS/R/Z5s7wMMCrhYqKdMp6ELMUEO07\n7cDIzAmc7WeSLyzhTBC661T0nKPPAf2nKKYXLI+6RisQoXnEgZmgNyNlIt6D\nNDNTRZjaR6s1QvHgWN9h/hLAgKvgaAnSy+JOzcB+SGaClLow3svbvW+klQpA\n8afOTLV4D/pgPDGXvvwDDInH6yccYSOSNiAZgd45hsmo82xIR3n+Cod2qk9Y\njCye36nXzdQTz9A7a3SgH++DV7fA5n87GoahkpGEnKu8gjgMuE6ncDEypbTi\nM4R8JikZrScR2wWXtO+jK4f/5XHVh19ZpqdOrlxzXutkUy0/bMoHFNxcGrXB\n5D1Qk/lOpNO4rd0NoURk6OkpueHOlBHlBNxqrEsltzY2IWs+JICcFaz385H0\nKYyNQrmltEqWVgW+LeFvm3B1sLL5wySqplX/396lC6kCHZyofeeqZFcC1G+m\ntkp0iho63tlm6WjIzw6ddHWu8olNohCk4xFpvNkkZ0u9GR4BaDBRXS60AcoD\nNIYwMuUlqmXAc7ey+xNZCqXokgbtjD7aI2uIDLNUMHELxrRzBccHe76sIQit\nBeBy\r\n=u7P4\r\n-----END PGP SIGNATURE-----\r\n" - } - } - }, - "modified": "2021-04-14T20:46:02.139Z", - "_cached": false, - "_contentLength": 1417 -} \ No newline at end of file diff --git a/workspaces/libnpmexec/test/registry/content/ruyadorno/create-test/-/create-test-1.0.0.tgz b/workspaces/libnpmexec/test/registry/content/ruyadorno/create-test/-/create-test-1.0.0.tgz deleted file mode 100644 index 34a85702055f3..0000000000000 Binary files a/workspaces/libnpmexec/test/registry/content/ruyadorno/create-test/-/create-test-1.0.0.tgz and /dev/null differ diff --git a/workspaces/libnpmexec/test/registry/server.js b/workspaces/libnpmexec/test/registry/server.js deleted file mode 100644 index f35acfdeb2e5a..0000000000000 --- a/workspaces/libnpmexec/test/registry/server.js +++ /dev/null @@ -1,280 +0,0 @@ -const { join, dirname } = require('path') -const { existsSync, readFileSync, writeFileSync } = require('fs') -const PORT = 12345 + (+process.env.TAP_CHILD_ID || 0) -const http = require('http') -const https = require('https') - -const mkdirp = require('mkdirp') -const doProxy = process.env.ARBORIST_TEST_PROXY -const missing = /\/@isaacs(\/|%2[fF])(this-does-not-exist-at-all|testing-missing-tgz\/-\/)/ -const corgiDoc = 'application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8, */*' -const { gzipSync, unzipSync } = require('zlib') - -let advisoryBulkResponse = null -let failAdvisoryBulk = false -let auditResponse = null -let failAudit = false -const startServer = cb => { - const server = module.exports.server = http.createServer((req, res) => { - res.setHeader('connection', 'close') - - if (req.url === '/-/npm/v1/security/advisories/bulk') { - const body = [] - req.on('data', c => body.push(c)) - req.on('end', () => { - res.setHeader('connection', 'close') - if (failAdvisoryBulk) { - res.statusCode = 503 - return res.end('no advisory bulk for you') - } - if (!advisoryBulkResponse) { - if (auditResponse && !failAudit) { - // simulate what the registry does when quick audits are allowed, - // but advisory bulk requests are not - res.statusCode = 405 - return res.end(JSON.stringify({ - code: 'MethodNotAllowedError', - message: 'POST is not allowed', - })) - } else { - res.statusCode = 404 - return res.end('not found') - } - } - if (doProxy && !existsSync(advisoryBulkResponse)) { - // hit the main registry, then fall back to staging for now - // XXX: remove this when bulk advisory endpoint pushed to production! - const opts = { - host: 'registry.npmjs.org', - method: req.method, - path: req.url, - headers: { - ...req.headers, - accept: '*', - host: 'registry.npmjs.org', - connection: 'close', - 'if-none-match': '', - }, - } - const handleUpstream = upstream => { - res.statusCode = upstream.statusCode - if (upstream.statusCode >= 300 || upstream.statusCode < 200) { - console.error('UPSTREAM ERROR', upstream.statusCode) - return upstream.pipe(res) - } - res.setHeader('content-encoding', upstream.headers['content-encoding']) - const file = advisoryBulkResponse - console.error('PROXY', `${req.url} -> ${file} ${upstream.statusCode}`) - mkdirp.sync(dirname(file)) - const data = [] - upstream.on('end', () => { - const out = Buffer.concat(data) - const obj = JSON.parse(unzipSync(out).toString()) - writeFileSync(file, JSON.stringify(obj, 0, 2) + '\n') - res.end(out) - }) - upstream.on('data', c => data.push(c)) - } - return https.request(opts).on('response', upstream => { - if (upstream.statusCode !== 200) { - console.error('ATTEMPTING TO PROXY FROM STAGING') - console.error('NOTE: THIS WILL FAIL WHEN NOT ON VPN!') - opts.host = 'security-microservice-3-west.npm.red' - opts.headers.host = opts.host - opts.path = '/v1/advisories/bulk' - https.request(opts) - .on('response', r => handleUpstream(r)) - .end(Buffer.concat(body)) - } else { - handleUpstream(upstream) - } - }).end(Buffer.concat(body)) - } else { - res.setHeader('content-encoding', 'gzip') - res.end(gzipSync(readFileSync(advisoryBulkResponse))) - } - }) - return - } else if (req.url === '/-/npm/v1/security/audits/quick') { - const body = [] - req.on('data', c => body.push(c)) - req.on('end', () => { - res.setHeader('connection', 'close') - if (failAudit) { - res.statusCode = 503 - return res.end('no audit for you') - } - if (!auditResponse) { - res.statusCode = 404 - return res.end('not found') - } - if (doProxy && !existsSync(auditResponse)) { - return https.request({ - host: 'registry.npmjs.org', - method: req.method, - path: req.url, - headers: { - ...req.headers, - accept: '*', - host: 'registry.npmjs.org', - connection: 'close', - 'if-none-match': '', - }, - }).on('response', upstream => { - res.statusCode = upstream.statusCode - if (upstream.statusCode >= 300 || upstream.statusCode < 200) { - console.error('UPSTREAM ERROR', upstream.statusCode) - // don't save if it's not a valid response - return upstream.pipe(res) - } - res.setHeader('content-encoding', upstream.headers['content-encoding']) - const file = auditResponse - console.error('PROXY', `${req.url} -> ${file} ${upstream.statusCode}`) - mkdirp.sync(dirname(file)) - const data = [] - upstream.on('end', () => { - const out = Buffer.concat(data) - // make it a bit prettier to read later - const obj = JSON.parse(unzipSync(out).toString()) - writeFileSync(file, JSON.stringify(obj, 0, 2) + '\n') - res.end(out) - }) - upstream.on('data', c => data.push(c)) - }).end(Buffer.concat(body)) - } else { - res.setHeader('content-encoding', 'gzip') - res.end(gzipSync(readFileSync(auditResponse))) - } - }) - return - } - - const f = join(__dirname, 'content', join('/', req.url.replace(/@/, '').replace(/%2f/i, '/'))) - const isCorgi = req.headers.accept.includes('application/vnd.npm.install-v1+json') - const file = f + ( - isCorgi && existsSync(`${f}.min.json`) ? '.min.json' - : existsSync(`${f}.json`) ? '.json' - : existsSync(`${f}/index.json`) ? 'index.json' - : '' - ) - - try { - const body = readFileSync(file) - res.setHeader('content-length', body.length) - res.setHeader('content-type', /\.min\.json$/.test(file) ? corgiDoc - : /\.json$/.test(file) ? 'application/json' - : 'application/octet-stream') - res.end(body) - } catch (er) { - // testing things going missing from the registry somehow - if (missing.test(req.url)) { - res.statusCode = 404 - res.end('{"error": "not found"}') - return - } - - if (doProxy) { - return https.get({ - host: 'registry.npmjs.org', - path: req.url, - headers: { - ...req.headers, - accept: '*', - 'accept-encoding': 'identity', - host: 'registry.npmjs.org', - connection: 'close', - 'if-none-match': '', - }, - }).on('response', upstream => { - const errorStatus = - upstream.statusCode >= 300 || upstream.statusCode < 200 - - if (errorStatus) { - console.error('UPSTREAM ERROR', upstream.statusCode) - } - - const ct = upstream.headers['content-type'] - const isJson = ct.includes('application/json') - const proxyFile = isJson ? f + '.json' : f - console.error('PROXY', `${req.url} -> ${proxyFile} ${ct}`) - mkdirp.sync(dirname(proxyFile)) - const data = [] - res.statusCode = upstream.statusCode - res.setHeader('content-type', ct) - upstream.on('end', () => { - console.error('ENDING', req.url) - const out = Buffer.concat(data) - if (!errorStatus) { - if (isJson) { - const obj = JSON.parse(out.toString()) - writeFileSync(proxyFile, JSON.stringify(obj, 0, 2) + '\n') - const mrm = require('minify-registry-metadata') - const minFile = proxyFile.replace(/\.json$/, '.min.json') - writeFileSync(minFile, JSON.stringify(mrm(obj), 0, 2) + '\n') - console.error('WROTE JSONS', [proxyFile, minFile]) - } else { - writeFileSync(proxyFile, out) - } - } - res.end(out) - }) - upstream.on('data', c => data.push(c)) - }).end() - } - - res.statusCode = er.code === 'ENOENT' ? 404 : 500 - if (res.method === 'GET') { - console.error(er) - } - res.setHeader('content-type', 'text/plain') - res.end(er.stack) - } - }) - server.listen(PORT, cb) -} - -module.exports = t => startServer(() => { - t.parent.teardown(() => module.exports.server.close()) - t.end() -}) - -module.exports.auditResponse = value => { - if (auditResponse && auditResponse !== value) { - throw new Error('setting audit response, but already set\n' + - '(did you forget to call the returned function on teardown?)') - } - auditResponse = value - return () => auditResponse = null -} -module.exports.failAudit = () => { - failAudit = true - return () => failAudit = false -} - -module.exports.advisoryBulkResponse = value => { - if (advisoryBulkResponse && advisoryBulkResponse !== value) { - throw new Error('setting advisory bulk response, but already set\n' + - '(did you forget to call the returned function on teardown?)') - } - advisoryBulkResponse = value - return () => advisoryBulkResponse = null -} -module.exports.failAdvisoryBulk = () => { - failAdvisoryBulk = true - return () => failAdvisoryBulk = false -} - -module.exports.registry = `http://localhost:${PORT}/` - -module.exports.start = startServer -module.exports.stop = () => module.exports.server.close() - -if (require.main === module) { - startServer(() => { - console.log(`Mock registry live at: - ${module.exports.registry} -Press ^D to close gracefully.`) - }) - process.openStdin() - process.stdin.on('end', () => module.exports.server.close()) -}