From 7a4f0c96c2ab9f264f7bda2caf7e72c881571270 Mon Sep 17 00:00:00 2001 From: nlf Date: Thu, 10 Dec 2020 09:45:46 -0800 Subject: [PATCH] tests for npm doctor PR-URL: https://github.com/npm/cli/pull/2323 Credit: @nlf Close: #2323 Reviewed-by: @isaacs --- lib/doctor.js | 4 +- test/lib/doctor.js | 943 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 945 insertions(+), 2 deletions(-) create mode 100644 test/lib/doctor.js diff --git a/lib/doctor.js b/lib/doctor.js index a596f1f4b50d3..0b29936bb4d1f 100644 --- a/lib/doctor.js +++ b/lib/doctor.js @@ -20,7 +20,7 @@ const checkPing = async () => { if (/^E\d{3}$/.test(er.code || '')) throw er.code.substr(1) + ' ' + er.message else - throw er + throw er.message } finally { tracker.finish() } @@ -92,7 +92,7 @@ const lstat = promisify(fs.lstat) const readdir = promisify(fs.readdir) const access = promisify(fs.access) const isWindows = require('./utils/is-windows.js') -const checkFilesPermission = async (root, shouldOwn = true, mask = null) => { +const checkFilesPermission = async (root, shouldOwn, mask = null) => { if (mask === null) mask = shouldOwn ? R_OK | W_OK : R_OK diff --git a/test/lib/doctor.js b/test/lib/doctor.js new file mode 100644 index 0000000000000..c2c3fdb9781f2 --- /dev/null +++ b/test/lib/doctor.js @@ -0,0 +1,943 @@ +const { test } = require('tap') +const requireInject = require('require-inject') + +const { join } = require('path') +const fs = require('fs') +const ansiTrim = require('../../lib/utils/ansi-trim.js') +const isWindows = require('../../lib/utils/is-windows.js') + +// getuid and getgid do not exist in windows, so we shim them +// to return 0, as that is the value that lstat will assign the +// gid and uid properties for fs.Stats objects +if (isWindows) { + process.getuid = () => 0 + process.getgid = () => 0 +} + +const output = [] + +let pingError +const ping = async () => { + if (pingError) + throw pingError +} + +const nodeVersions = [ + { version: 'v14.0.0', lts: false }, + { version: 'v13.0.0', lts: false }, + // it's necessary to allow tests in node 10.x to not mark 12.x as lts + { version: 'v12.0.0', lts: false }, + { version: 'v10.13.0', lts: 'Dubnium' }, +] + +const fetch = async () => { + return { + json: async () => { + return nodeVersions + }, + } +} + +const logs = { + info: [], +} + +const clearLogs = (obj = logs) => { + output.length = 0 + for (const key in obj) { + if (Array.isArray(obj[key])) + obj[key].length = 0 + else + delete obj[key] + } +} + +const npm = { + flatOptions: { + registry: 'https://registry.npmjs.org/', + }, + log: { + info: (msg) => { + logs.info.push(msg) + }, + newItem: (name) => { + logs[name] = {} + + return { + info: (_, msg) => { + if (!logs[name].info) + logs[name].info = [] + logs[name].info.push(msg) + }, + warn: (_, msg) => { + if (!logs[name].warn) + logs[name].warn = [] + logs[name].warn.push(msg) + }, + error: (_, msg) => { + if (!logs[name].error) + logs[name].error = [] + logs[name].error.push(msg) + }, + silly: (_, msg) => { + if (!logs[name].silly) + logs[name].silly = [] + logs[name].silly.push(msg) + }, + completeWork: () => {}, + finish: () => { + logs[name].finished = true + }, + } + }, + level: 'error', + levels: { + info: 1, + error: 0, + }, + }, + version: '7.1.0', +} + +let latestNpm = npm.version +const pacote = { + manifest: async () => { + return { version: latestNpm } + }, +} + +let whichError = null +const which = async () => { + if (whichError) + throw whichError + return '/path/to/git' +} + +let verifyResponse = { verifiedCount: 1, verifiedContent: 1 } +const cacache = { + verify: async () => { + return verifyResponse + }, +} + +const doctor = requireInject('../../lib/doctor.js', { + '../../lib/utils/is-windows.js': false, + '../../lib/utils/ping.js': ping, + '../../lib/utils/output.js': (data) => { + output.push(data) + }, + '../../lib/npm.js': npm, + cacache, + pacote, + 'make-fetch-happen': fetch, + which, +}) + +test('npm doctor checks ok', t => { + const dir = t.testdir() + npm.cache = npm.flatOptions.cache = dir + npm.localDir = dir + npm.globalDir = dir + npm.localBin = dir + npm.globalBin = dir + + t.teardown(() => { + delete npm.cache + delete npm.flatOptions.cache + delete npm.localDir + delete npm.globalDir + delete npm.localBin + delete npm.globalBin + clearLogs() + }) + + doctor([], (err) => { + if (err) { + t.fail(output) + return t.end() + } + + t.match(logs, { + checkPing: { finished: true }, + getLatestNpmVersion: { finished: true }, + getLatestNodejsVersion: { finished: true }, + getGitPath: { finished: true }, + [dir]: { finished: true }, + verifyCachedFiles: { finished: true }, + }, 'trackers all finished') + t.match(output, /npm ping\s*ok/, 'ping output is ok') + t.match(output, /npm -v\s*ok/, 'npm -v output is ok') + t.match(output, /node -v\s*ok/, 'node -v output is ok') + t.match(output, /npm config get registry\s*ok\s*using default/, 'npm config get registry output is ok') + t.match(output, /which git\s*ok/, 'which git output is ok') + t.match(output, /cached files\s*ok/, 'cached files are ok') + t.match(output, /local node_modules\s*ok/, 'local node_modules are ok') + t.match(output, /global node_modules\s*ok/, 'global node_modules are ok') + t.match(output, /local bin folder\s*ok/, 'local bin is ok') + t.match(output, /global bin folder\s*ok/, 'global bin is ok') + t.match(output, /cache contents\s*ok/, 'cache contents is ok') + t.end() + }) +}) + +test('npm doctor supports silent', t => { + const dir = t.testdir() + npm.cache = npm.flatOptions.cache = dir + npm.localDir = dir + npm.globalDir = dir + npm.localBin = dir + npm.globalBin = dir + npm.log.level = 'info' + + t.teardown(() => { + delete npm.cache + delete npm.flatOptions.cache + delete npm.localDir + delete npm.globalDir + delete npm.localBin + delete npm.globalBin + npm.log.level = 'error' + clearLogs() + }) + + doctor([], (err) => { + if (err) { + t.fail(err) + return t.end() + } + + t.match(logs, { + checkPing: { finished: true }, + getLatestNpmVersion: { finished: true }, + getLatestNodejsVersion: { finished: true }, + getGitPath: { finished: true }, + [dir]: { finished: true }, + verifyCachedFiles: { finished: true }, + }, 'trackers all finished') + t.strictSame(output, [], 'did not print output') + t.end() + }) +}) + +test('npm doctor supports color', t => { + const dir = t.testdir() + npm.cache = npm.flatOptions.cache = dir + npm.localDir = dir + npm.globalDir = dir + npm.localBin = dir + npm.globalBin = dir + npm.color = true + pingError = { message: 'generic error' } + const _consoleError = console.error + console.error = () => {} + + t.teardown(() => { + delete npm.cache + delete npm.flatOptions.cache + delete npm.localDir + delete npm.globalDir + delete npm.localBin + delete npm.globalBin + delete npm.color + pingError = null + console.error = _consoleError + clearLogs() + }) + + doctor([], (err) => { + t.match(err, /Some problems found/, 'detected the ping error') + t.match(logs, { + checkPing: { finished: true }, + getLatestNpmVersion: { finished: true }, + getLatestNodejsVersion: { finished: true }, + getGitPath: { finished: true }, + [dir]: { finished: true }, + verifyCachedFiles: { finished: true }, + }, 'trackers all finished') + t.match(output, /npm ping.*not ok/, 'ping output is ok') + t.match(output, /npm -v.*ok/, 'npm -v output is ok') + t.match(output, /node -v.*ok/, 'node -v output is ok') + t.match(output, /npm config get registry.*ok.*using default/, 'npm config get registry output is ok') + t.match(output, /which git.*ok/, 'which git output is ok') + t.match(output, /cached files.*ok/, 'cached files are ok') + t.match(output, /local node_modules.*ok/, 'local node_modules are ok') + t.match(output, /global node_modules.*ok/, 'global node_modules are ok') + t.match(output, /local bin folder.*ok/, 'local bin is ok') + t.match(output, /global bin folder.*ok/, 'global bin is ok') + t.match(output, /cache contents.*ok/, 'cache contents is ok') + t.notEqual(output[0], ansiTrim(output[0]), 'output should contain color codes') + t.end() + }) +}) + +test('npm doctor skips some tests in windows', t => { + const winDoctor = requireInject('../../lib/doctor.js', { + '../../lib/utils/is-windows.js': true, + '../../lib/utils/ping.js': ping, + '../../lib/utils/output.js': (data) => { + output.push(data) + }, + '../../lib/npm.js': npm, + cacache, + pacote, + 'make-fetch-happen': fetch, + which, + }) + + const dir = t.testdir() + npm.cache = npm.flatOptions.cache = dir + npm.localDir = dir + npm.globalDir = dir + npm.localBin = dir + npm.globalBin = dir + + t.teardown(() => { + delete npm.cache + delete npm.flatOptions.cache + delete npm.localDir + delete npm.globalDir + delete npm.localBin + delete npm.globalBin + clearLogs() + }) + + winDoctor([], (err) => { + if (err) { + t.fail(output) + return t.end() + } + + t.match(logs, { + checkPing: { finished: true }, + getLatestNpmVersion: { finished: true }, + getLatestNodejsVersion: { finished: true }, + getGitPath: { finished: true }, + [dir]: undefined, + verifyCachedFiles: { finished: true }, + }, 'trackers all finished') + t.match(output, /npm ping\s*ok/, 'ping output is ok') + t.match(output, /npm -v\s*ok/, 'npm -v output is ok') + t.match(output, /node -v\s*ok/, 'node -v output is ok') + t.match(output, /npm config get registry\s*ok\s*using default/, 'npm config get registry output is ok') + t.match(output, /which git\s*ok/, 'which git output is ok') + t.match(output, /cache contents\s*ok/, 'cache contents is ok') + t.end() + }) +}) + +test('npm doctor ping error E{3}', t => { + const dir = t.testdir() + npm.cache = npm.flatOptions.cache = dir + npm.localDir = dir + npm.globalDir = dir + npm.localBin = dir + npm.globalBin = dir + pingError = { code: 'E111', message: 'this error is 111' } + const consoleError = console.error + // we just print an empty line here, so swallow it and ignore + console.error = () => {} + + t.teardown(() => { + delete npm.cache + delete npm.flatOptions.cache + delete npm.localDir + delete npm.globalDir + delete npm.localBin + delete npm.globalBin + pingError = null + console.error = consoleError + clearLogs() + }) + + doctor([], (err) => { + t.match(err, /Some problems found/, 'detected the ping error') + t.match(logs, { + checkPing: { finished: true }, + getLatestNpmVersion: { finished: true }, + getLatestNodejsVersion: { finished: true }, + getGitPath: { finished: true }, + [dir]: { finished: true }, + verifyCachedFiles: { finished: true }, + }, 'trackers all finished') + t.match(output, /npm ping\s*not ok\s*111 this error is 111/, 'ping output contains trimmed error') + t.match(output, /npm -v\s*ok/, 'npm -v output is ok') + t.match(output, /node -v\s*ok/, 'node -v output is ok') + t.match(output, /npm config get registry\s*ok\s*using default/, 'npm config get registry output is ok') + t.match(output, /which git\s*ok/, 'which git output is ok') + t.match(output, /cached files\s*ok/, 'cached files are ok') + t.match(output, /local node_modules\s*ok/, 'local node_modules are ok') + t.match(output, /global node_modules\s*ok/, 'global node_modules are ok') + t.match(output, /local bin folder\s*ok/, 'local bin is ok') + t.match(output, /global bin folder\s*ok/, 'global bin is ok') + t.match(output, /cache contents\s*ok/, 'cache contents is ok') + t.end() + }) +}) + +test('npm doctor generic ping error', t => { + const dir = t.testdir() + npm.cache = npm.flatOptions.cache = dir + npm.localDir = dir + npm.globalDir = dir + npm.localBin = dir + npm.globalBin = dir + pingError = { message: 'generic error' } + const consoleError = console.error + // we just print an empty line here, so swallow it and ignore + console.error = () => {} + + t.teardown(() => { + delete npm.cache + delete npm.flatOptions.cache + delete npm.localDir + delete npm.globalDir + delete npm.localBin + delete npm.globalBin + pingError = null + console.error = consoleError + clearLogs() + }) + + doctor([], (err) => { + t.match(err, /Some problems found/, 'detected the ping error') + t.match(logs, { + checkPing: { finished: true }, + getLatestNpmVersion: { finished: true }, + getLatestNodejsVersion: { finished: true }, + getGitPath: { finished: true }, + [dir]: { finished: true }, + verifyCachedFiles: { finished: true }, + }, 'trackers all finished') + t.match(output, /npm ping\s*not ok\s*generic error/, 'ping output contains trimmed error') + t.match(output, /npm -v\s*ok/, 'npm -v output is ok') + t.match(output, /node -v\s*ok/, 'node -v output is ok') + t.match(output, /npm config get registry\s*ok\s*using default/, 'npm config get registry output is ok') + t.match(output, /which git\s*ok/, 'which git output is ok') + t.match(output, /cached files\s*ok/, 'cached files are ok') + t.match(output, /local node_modules\s*ok/, 'local node_modules are ok') + t.match(output, /global node_modules\s*ok/, 'global node_modules are ok') + t.match(output, /local bin folder\s*ok/, 'local bin is ok') + t.match(output, /global bin folder\s*ok/, 'global bin is ok') + t.match(output, /cache contents\s*ok/, 'cache contents is ok') + t.end() + }) +}) + +test('npm doctor outdated npm version', t => { + const dir = t.testdir() + npm.cache = npm.flatOptions.cache = dir + npm.localDir = dir + npm.globalDir = dir + npm.localBin = dir + npm.globalBin = dir + latestNpm = '7.1.1' + const consoleError = console.error + // we just print an empty line here, so swallow it and ignore + console.error = () => {} + + t.teardown(() => { + delete npm.cache + delete npm.flatOptions.cache + delete npm.localDir + delete npm.globalDir + delete npm.localBin + delete npm.globalBin + latestNpm = npm.version + console.error = consoleError + clearLogs() + }) + + doctor([], (err) => { + t.match(err, /Some problems found/, 'detected the out of date npm') + t.match(logs, { + checkPing: { finished: true }, + getLatestNpmVersion: { finished: true }, + getLatestNodejsVersion: { finished: true }, + getGitPath: { finished: true }, + [dir]: { finished: true }, + verifyCachedFiles: { finished: true }, + }, 'trackers all finished') + t.match(output, /npm ping\s*ok/, 'ping output is ok') + t.match(output, /npm -v\s*not ok/, 'npm -v output is not ok') + t.match(output, /node -v\s*ok/, 'node -v output is ok') + t.match(output, /npm config get registry\s*ok\s*using default/, 'npm config get registry output is ok') + t.match(output, /which git\s*ok/, 'which git output is ok') + t.match(output, /cached files\s*ok/, 'cached files are ok') + t.match(output, /local node_modules\s*ok/, 'local node_modules are ok') + t.match(output, /global node_modules\s*ok/, 'global node_modules are ok') + t.match(output, /local bin folder\s*ok/, 'local bin is ok') + t.match(output, /global bin folder\s*ok/, 'global bin is ok') + t.match(output, /cache contents\s*ok/, 'cache contents is ok') + t.end() + }) +}) + +test('npm doctor outdated nodejs version', t => { + const dir = t.testdir() + npm.cache = npm.flatOptions.cache = dir + npm.localDir = dir + npm.globalDir = dir + npm.localBin = dir + npm.globalBin = dir + nodeVersions.push({ version: process.version.replace(/\d+$/, '999'), lts: false }) + const consoleError = console.error + // we just print an empty line here, so swallow it and ignore + console.error = () => {} + + t.teardown(() => { + delete npm.cache + delete npm.flatOptions.cache + delete npm.localDir + delete npm.globalDir + delete npm.localBin + delete npm.globalBin + nodeVersions.pop() + console.error = consoleError + clearLogs() + }) + + doctor([], (err) => { + t.match(err, /Some problems found/, 'detected the out of date nodejs') + t.match(logs, { + checkPing: { finished: true }, + getLatestNpmVersion: { finished: true }, + getLatestNodejsVersion: { finished: true }, + getGitPath: { finished: true }, + [dir]: { finished: true }, + verifyCachedFiles: { finished: true }, + }, 'trackers all finished') + t.match(output, /npm ping\s*ok/, 'ping output is ok') + t.match(output, /npm -v\s*ok/, 'npm -v output is ok') + t.match(output, /node -v\s*not ok/, 'node -v output is not ok') + t.match(output, /npm config get registry\s*ok\s*using default/, 'npm config get registry output is ok') + t.match(output, /which git\s*ok/, 'which git output is ok') + t.match(output, /cached files\s*ok/, 'cached files are ok') + t.match(output, /local node_modules\s*ok/, 'local node_modules are ok') + t.match(output, /global node_modules\s*ok/, 'global node_modules are ok') + t.match(output, /local bin folder\s*ok/, 'local bin is ok') + t.match(output, /global bin folder\s*ok/, 'global bin is ok') + t.match(output, /cache contents\s*ok/, 'cache contents is ok') + t.end() + }) +}) + +test('npm doctor file permission checks', t => { + const dir = t.testdir({ + cache: { + one: 'one', + link: t.fixture('symlink', './one'), + unreadable: 'unreadable', + baddir: {}, + }, + local: { + two: 'two', + notmine: 'notmine', + }, + global: { + three: 'three', + broken: 'broken', + }, + localBin: { + four: 'four', + five: 'five', + }, + globalBin: { + six: 'six', + seven: 'seven', + }, + }) + + const _fsLstat = fs.lstat + fs.lstat = (p, cb) => { + let err = null + let stat = null + + try { + stat = fs.lstatSync(p) + } catch (err) { + return cb(err) + } + + switch (p) { + case join(dir, 'local', 'notmine'): + stat.uid += 1 + stat.gid += 1 + break + case join(dir, 'global', 'broken'): + err = new Error('broken') + break + } + + return cb(err, stat) + } + + const _fsReaddir = fs.readdir + fs.readdir = (p, cb) => { + let err = null + let result = null + + try { + result = fs.readdirSync(p) + } catch (err) { + return cb(err) + } + + if (p === join(dir, 'cache', 'baddir')) + err = new Error('broken') + + return cb(err, result) + } + + const _fsAccess = fs.access + fs.access = (p, mask, cb) => { + const err = new Error('failed') + switch (p) { + case join(dir, 'cache', 'unreadable'): + case join(dir, 'localBin', 'four'): + case join(dir, 'globalBin', 'six'): + return cb(err) + default: + return cb(null) + } + } + + const doctor = requireInject('../../lib/doctor.js', { + '../../lib/utils/is-windows.js': false, + '../../lib/utils/ping.js': ping, + '../../lib/utils/output.js': (data) => { + output.push(data) + }, + '../../lib/npm.js': npm, + cacache, + pacote, + 'make-fetch-happen': fetch, + which, + fs, + }) + // it's necessary to allow tests in node 10.x to not mark 12.x as lted + + npm.cache = npm.flatOptions.cache = join(dir, 'cache') + npm.localDir = join(dir, 'local') + npm.globalDir = join(dir, 'global') + npm.localBin = join(dir, 'localBin') + npm.globalBin = join(dir, 'globalBin') + const _consoleError = console.error + console.error = () => {} + + t.teardown(() => { + delete npm.cache + delete npm.flatOptions.cache + delete npm.localDir + delete npm.globalDir + delete npm.localBin + delete npm.globalBin + console.error = _consoleError + fs.lstat = _fsLstat + fs.readdir = _fsReaddir + fs.access = _fsAccess + clearLogs() + }) + + doctor([], (err) => { + t.match(err, /Some problems found/, 'identified problems') + t.match(logs, { + checkPing: { finished: true }, + getLatestNpmVersion: { finished: true }, + getLatestNodejsVersion: { finished: true }, + getGitPath: { finished: true }, + [join(dir, 'cache')]: { finished: true }, + [join(dir, 'local')]: { finished: true }, + [join(dir, 'global')]: { finished: true }, + [join(dir, 'localBin')]: { finished: true }, + [join(dir, 'globalBin')]: { finished: true }, + verifyCachedFiles: { finished: true }, + }, 'trackers all finished') + t.match(output, /npm ping\s*ok/, 'ping output is ok') + t.match(output, /npm -v\s*ok/, 'npm -v output is ok') + t.match(output, /node -v\s*ok/, 'node -v output is ok') + t.match(output, /npm config get registry\s*ok\s*using default/, 'npm config get registry output is ok') + t.match(output, /which git\s*ok/, 'which git output is ok') + t.match(output, /cached files\s*not ok/, 'cached files are not ok') + t.match(output, /local node_modules\s*not ok/, 'local node_modules are not ok') + t.match(output, /global node_modules\s*not ok/, 'global node_modules are not ok') + t.match(output, /local bin folder\s*not ok/, 'local bin is not ok') + t.match(output, /global bin folder\s*not ok/, 'global bin is not ok') + t.match(output, /cache contents\s*ok/, 'cache contents is ok') + t.end() + }) +}) + +test('npm doctor missing git', t => { + const dir = t.testdir() + npm.cache = npm.flatOptions.cache = dir + npm.localDir = dir + npm.globalDir = dir + npm.localBin = dir + npm.globalBin = dir + whichError = new Error('boom') + const consoleError = console.error + // we just print an empty line here, so swallow it and ignore + console.error = () => {} + + t.teardown(() => { + delete npm.cache + delete npm.flatOptions.cache + delete npm.localDir + delete npm.globalDir + delete npm.localBin + delete npm.globalBin + whichError = null + console.error = consoleError + clearLogs() + }) + + doctor([], (err) => { + t.match(err, /Some problems found/, 'detected the missing git') + t.match(logs, { + checkPing: { finished: true }, + getLatestNpmVersion: { finished: true }, + getLatestNodejsVersion: { finished: true }, + getGitPath: { finished: true }, + [dir]: { finished: true }, + verifyCachedFiles: { finished: true }, + }, 'trackers all finished') + t.match(output, /npm ping\s*ok/, 'ping output is ok') + t.match(output, /npm -v\s*ok/, 'npm -v output is ok') + t.match(output, /node -v\s*ok/, 'node -v output is ok') + t.match(output, /npm config get registry\s*ok\s*using default/, 'npm config get registry output is ok') + t.match(output, /which git\s*not ok/, 'which git output is not ok') + t.match(output, /cached files\s*ok/, 'cached files are ok') + t.match(output, /local node_modules\s*ok/, 'local node_modules are ok') + t.match(output, /global node_modules\s*ok/, 'global node_modules are ok') + t.match(output, /local bin folder\s*ok/, 'local bin is ok') + t.match(output, /global bin folder\s*ok/, 'global bin is ok') + t.match(output, /cache contents\s*ok/, 'cache contents is ok') + t.end() + }) +}) + +test('npm doctor cache verification showed bad content', t => { + const dir = t.testdir() + npm.cache = npm.flatOptions.cache = dir + npm.localDir = dir + npm.globalDir = dir + npm.localBin = dir + npm.globalBin = dir + const _verifyResponse = verifyResponse + verifyResponse = { + ...verifyResponse, + badContentCount: 1, + } + const consoleError = console.error + // we just print an empty line here, so swallow it and ignore + console.error = () => {} + + t.teardown(() => { + delete npm.cache + delete npm.flatOptions.cache + delete npm.localDir + delete npm.globalDir + delete npm.localBin + delete npm.globalBin + verifyResponse = _verifyResponse + console.error = consoleError + clearLogs() + }) + + doctor([], (err) => { + // cache verification problems get fixed and so do not throw an error + if (err) { + t.fail(output) + return t.end() + } + + t.match(logs, { + checkPing: { finished: true }, + getLatestNpmVersion: { finished: true }, + getLatestNodejsVersion: { finished: true }, + getGitPath: { finished: true }, + [dir]: { finished: true }, + verifyCachedFiles: { finished: true }, + }, 'trackers all finished') + t.match(output, /npm ping\s*ok/, 'ping output is ok') + t.match(output, /npm -v\s*ok/, 'npm -v output is ok') + t.match(output, /node -v\s*ok/, 'node -v output is ok') + t.match(output, /npm config get registry\s*ok\s*using default/, 'npm config get registry output is ok') + t.match(output, /which git\s*ok/, 'which git output is ok') + t.match(output, /cached files\s*ok/, 'cached files are ok') + t.match(output, /local node_modules\s*ok/, 'local node_modules are ok') + t.match(output, /global node_modules\s*ok/, 'global node_modules are ok') + t.match(output, /local bin folder\s*ok/, 'local bin is ok') + t.match(output, /global bin folder\s*ok/, 'global bin is ok') + t.match(output, /cache contents\s*ok/, 'cache contents is not ok') + t.end() + }) +}) + +test('npm doctor cache verification showed reclaimed content', t => { + const dir = t.testdir() + npm.cache = npm.flatOptions.cache = dir + npm.localDir = dir + npm.globalDir = dir + npm.localBin = dir + npm.globalBin = dir + const _verifyResponse = verifyResponse + verifyResponse = { + ...verifyResponse, + reclaimedCount: 1, + reclaimedSize: 100, + } + const consoleError = console.error + // we just print an empty line here, so swallow it and ignore + console.error = () => {} + + t.teardown(() => { + delete npm.cache + delete npm.flatOptions.cache + delete npm.localDir + delete npm.globalDir + delete npm.localBin + delete npm.globalBin + verifyResponse = _verifyResponse + console.error = consoleError + clearLogs() + }) + + doctor([], (err) => { + // cache verification problems get fixed and so do not throw an error + if (err) { + t.fail(output) + return t.end() + } + + t.match(logs, { + checkPing: { finished: true }, + getLatestNpmVersion: { finished: true }, + getLatestNodejsVersion: { finished: true }, + getGitPath: { finished: true }, + [dir]: { finished: true }, + verifyCachedFiles: { finished: true }, + }, 'trackers all finished') + t.match(output, /npm ping\s*ok/, 'ping output is ok') + t.match(output, /npm -v\s*ok/, 'npm -v output is ok') + t.match(output, /node -v\s*ok/, 'node -v output is ok') + t.match(output, /npm config get registry\s*ok\s*using default/, 'npm config get registry output is ok') + t.match(output, /which git\s*ok/, 'which git output is ok') + t.match(output, /cached files\s*ok/, 'cached files are ok') + t.match(output, /local node_modules\s*ok/, 'local node_modules are ok') + t.match(output, /global node_modules\s*ok/, 'global node_modules are ok') + t.match(output, /local bin folder\s*ok/, 'local bin is ok') + t.match(output, /global bin folder\s*ok/, 'global bin is ok') + t.match(output, /cache contents\s*ok/, 'cache contents is not ok') + t.end() + }) +}) + +test('npm doctor cache verification showed missing content', t => { + const dir = t.testdir() + npm.cache = npm.flatOptions.cache = dir + npm.localDir = dir + npm.globalDir = dir + npm.localBin = dir + npm.globalBin = dir + const _verifyResponse = verifyResponse + verifyResponse = { + ...verifyResponse, + missingContent: 1, + } + const consoleError = console.error + // we just print an empty line here, so swallow it and ignore + console.error = () => {} + + t.teardown(() => { + delete npm.cache + delete npm.flatOptions.cache + delete npm.localDir + delete npm.globalDir + delete npm.localBin + delete npm.globalBin + verifyResponse = _verifyResponse + console.error = consoleError + clearLogs() + }) + + doctor([], (err) => { + // cache verification problems get fixed and so do not throw an error + if (err) { + t.fail(output) + return t.end() + } + + t.match(logs, { + checkPing: { finished: true }, + getLatestNpmVersion: { finished: true }, + getLatestNodejsVersion: { finished: true }, + getGitPath: { finished: true }, + [dir]: { finished: true }, + verifyCachedFiles: { finished: true }, + }, 'trackers all finished') + t.match(output, /npm ping\s*ok/, 'ping output is ok') + t.match(output, /npm -v\s*ok/, 'npm -v output is ok') + t.match(output, /node -v\s*ok/, 'node -v output is ok') + t.match(output, /npm config get registry\s*ok\s*using default/, 'npm config get registry output is ok') + t.match(output, /which git\s*ok/, 'which git output is ok') + t.match(output, /cached files\s*ok/, 'cached files are ok') + t.match(output, /local node_modules\s*ok/, 'local node_modules are ok') + t.match(output, /global node_modules\s*ok/, 'global node_modules are ok') + t.match(output, /local bin folder\s*ok/, 'local bin is ok') + t.match(output, /global bin folder\s*ok/, 'global bin is ok') + t.match(output, /cache contents\s*ok/, 'cache contents is not ok') + t.end() + }) +}) + +test('npm doctor not using default registry', t => { + const dir = t.testdir() + npm.cache = npm.flatOptions.cache = dir + npm.localDir = dir + npm.globalDir = dir + npm.localBin = dir + npm.globalBin = dir + const _currentRegistry = npm.flatOptions.registry + npm.flatOptions.registry = 'https://google.com' + const consoleError = console.error + // we just print an empty line here, so swallow it and ignore + console.error = () => {} + + t.teardown(() => { + delete npm.cache + delete npm.flatOptions.cache + delete npm.localDir + delete npm.globalDir + delete npm.localBin + delete npm.globalBin + npm.flatOptions.registry = _currentRegistry + console.error = consoleError + clearLogs() + }) + + doctor([], (err) => { + // cache verification problems get fixed and so do not throw an error + t.match(err, /Some problems found/, 'detected the non-default registry') + t.match(logs, { + checkPing: { finished: true }, + getLatestNpmVersion: { finished: true }, + getLatestNodejsVersion: { finished: true }, + getGitPath: { finished: true }, + [dir]: { finished: true }, + verifyCachedFiles: { finished: true }, + }, 'trackers all finished') + t.match(output, /npm ping\s*ok/, 'ping output is ok') + t.match(output, /npm -v\s*ok/, 'npm -v output is ok') + t.match(output, /node -v\s*ok/, 'node -v output is ok') + t.match(output, /npm config get registry\s*not ok/, 'npm config get registry output is not ok') + t.match(output, /which git\s*ok/, 'which git output is ok') + t.match(output, /cached files\s*ok/, 'cached files are ok') + t.match(output, /local node_modules\s*ok/, 'local node_modules are ok') + t.match(output, /global node_modules\s*ok/, 'global node_modules are ok') + t.match(output, /local bin folder\s*ok/, 'local bin is ok') + t.match(output, /global bin folder\s*ok/, 'global bin is ok') + t.match(output, /cache contents\s*ok/, 'cache contents is ok') + t.end() + }) +})