From c7d5d7748b92560783f84d4bfab423cb5de1db02 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Thu, 9 Jul 2026 22:40:07 -0700 Subject: [PATCH] [Tests] run every remaining test file: exec bits, fixes; add `check-exec` to CI urchin only executes test files that have the executable bit, silently skipping the rest, and `npm run test:check-exec` would catch non-executable ones, but no workflow ran it - so several test files have never actually run. Make all of them executable, and fix the ones that were broken: - the io.js source-install tests, skipped since 2016: io.js requires python 2 and gcc <= 5, so they now skip on toolchains that can not compile it, and a new gcc:4.9-container CI job runs them unmodified - resolving, downloading, checksumming, and compiling io.js for real. New `fake source` variants exercise nvm's source pipeline everywhere - cache lookup, offline resolution, extraction, configure, make, make install, activation - via a tiny fake source tree seeded into the download cache and installed with `--offline`. Also strengthen the `nvm run | grep` assertions with `--silent`, since the version banner used to satisfy the grep even when the built binary could not run, and give the final assertions the `die` they always meant to call. - `nvm uninstall ... incorrect file permissions fails nicely`, parked in 2025 as failing: `sudo npm` resets PATH, so node 5's npm crashed under the system node. The permissions check only trips on files that are neither writable nor self-owned, and that precondition only needs `sudo touch` - not npm, jspm, or the network - so use that, against a fake installed version, and skip only where passwordless sudo does not exist. - `nvm install --offline`: `nvm ls | tail -1` always grabbed an alias line, never a version; use `nvm_ls` and skip its trailing `system ` line. - `nvm exec ... help should not parse`: in a BRE, `[options]` is a bracket expression, so the grep could never match node's literal `Usage: node [options]` output. - `nvm use ... nvmrc containing not installed version`: current output once again matches its expectations verbatim; no changes needed. Rewrite `test:check-exec` in POSIX sh - npm runs scripts with /bin/sh, which is dash on Ubuntu, where the previous `[[`/`$'\n'` bashisms would break; match by extension, so that filenames containing dots can not dodge the check; and disable git's `core.quotePath` mangling of the non-ASCII test filenames. Then wire it into the lint workflow. --- .github/workflows/lint.yml | 20 ++++++- .github/workflows/tests-installation-iojs.yml | 36 ++++++++++++- package.json | 2 +- test/fast/Unit tests/nvm install --offline | 6 ++- test/fixtures/fake-source-tarball/Makefile | 15 ++++++ test/fixtures/fake-source-tarball/configure | 22 ++++++++ .../install from fake source | 44 +++++++++++++++ test/installation_iojs/install from source | 19 ++++++- ...rsion specified in .nvmrc from fake source | 45 ++++++++++++++++ ...ll version specified in .nvmrc from source | 24 +++++++-- test/installation_iojs/teardown_dir | 2 + ...ning 'nvm exec' with help should not parse | 4 +- ...th incorrect file permissions fails nicely | 53 +++++++++++++------ ...ith nvmrc containing not installed version | 0 14 files changed, 266 insertions(+), 26 deletions(-) mode change 100644 => 100755 test/fast/Unit tests/nvm install --offline create mode 100644 test/fixtures/fake-source-tarball/Makefile create mode 100644 test/fixtures/fake-source-tarball/configure create mode 100755 test/installation_iojs/install from fake source mode change 100644 => 100755 test/installation_iojs/install from source create mode 100755 test/installation_iojs/install version specified in .nvmrc from fake source mode change 100644 => 100755 test/installation_iojs/install version specified in .nvmrc from source mode change 100644 => 100755 test/slow/nvm exec/Running 'nvm exec' with help should not parse mode change 100644 => 100755 test/slow/nvm uninstall/Running 'nvm uninstall' with incorrect file permissions fails nicely mode change 100644 => 100755 test/slow/nvm use/Running 'nvm use' with nvmrc containing not installed version diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 04aa3b76..c55dcceb 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -71,11 +71,29 @@ jobs: - name: check tests filenames run: ./rename_test.sh --check + check_exec: + runs-on: ubuntu-latest + steps: + - uses: step-security/harden-runner@v2 + with: + allowed-endpoints: + github.com:443 + raw.githubusercontent.com:443 + nodejs.org:443 + registry.npmjs.org:443 + - uses: actions/checkout@v6 + - uses: ljharb/actions/node/install@main + name: 'nvm install lts/* && npm install' + with: + node-version: 'lts/*' + - name: check that test files are executable + run: npm run test:check-exec + all: permissions: contents: none name: 'all linting' - needs: [eclint, dockerfile_lint, doctoc, test_naming] + needs: [eclint, dockerfile_lint, doctoc, test_naming, check_exec] runs-on: ubuntu-latest steps: - run: true diff --git a/.github/workflows/tests-installation-iojs.yml b/.github/workflows/tests-installation-iojs.yml index 941b9681..eb4160da 100644 --- a/.github/workflows/tests-installation-iojs.yml +++ b/.github/workflows/tests-installation-iojs.yml @@ -98,11 +98,45 @@ jobs: run: sudo apt-get install curl -y shell: bash + installation_iojs_source_compile: + permissions: + contents: read + + name: 'actually compile io.js from source (gcc 4.9 container)' + runs-on: ubuntu-latest + timeout-minutes: 60 + + steps: + - name: Harden Runner + uses: step-security/harden-runner@v2 + with: + allowed-endpoints: + github.com:443 + mirror.gcr.io:443 + storage.googleapis.com:443 + iojs.org:443 + - uses: actions/checkout@v6 + # io.js requires python 2 and gcc <= 5 to compile; the gcc:4.9 image + # (debian jessie) has both, plus curl/wget/xz and CA certificates that + # still validate iojs.org - so the source-install tests run unmodified, + # resolving, downloading, checksumming, and compiling for real. + # The image is pulled through Google's Docker Hub mirror: it serves + # manifests and blobs from a single stable hostname (Docker Hub's blob + # CDN hostnames rotate, which the blocked-egress policy can not allow), + # and it is not subject to Docker Hub's anonymous pull rate limits. + - name: compile io.js from source, for real + run: | + docker run --rm -v "${PWD}:/nvm-under-test" -e NVM_DIR=/nvm-under-test mirror.gcr.io/library/gcc:4.9 bash -ec ' + cd "${NVM_DIR}/test/installation_iojs" + sh "./install from source" + sh "./install version specified in .nvmrc from source" + ' + all: permissions: contents: none name: 'all installation_iojs tests' - needs: [installation_iojs_without_curl] + needs: [installation_iojs_without_curl, installation_iojs_source_compile] runs-on: ubuntu-latest steps: - run: true diff --git a/package.json b/package.json index 04206e03..410ecdf8 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "test/installation/node": "shell=$(basename -- $(ps -o comm= $(ps -o ppid= -p $PPID)) | sed 's/^-//'); make TEST_SUITE=installation_node test-$shell", "test/installation/iojs": "shell=$(basename -- $(ps -o comm= $(ps -o ppid= -p $PPID)) | sed 's/^-//'); make TEST_SUITE=installation_iojs test-$shell", "test/sourcing": "shell=$(basename -- $(ps -o comm= $(ps -o ppid= -p $PPID)) | sed 's/^-//'); make TEST_SUITE=sourcing test-$shell", - "test:check-exec": "(IFS=$'\\n'; for file in $(git ls-files test); do if [ ! -x \"$file\" ] && [[ \"$file\" != *.* ]] && [[ \"$file\" != test/fixtures/* ]]; then echo \"$file\"; fi; done) | tee /dev/stderr | awk 'END {if (NR > 0) exit 1}'", + "test:check-exec": "git -c core.quotePath=false ls-files test | while IFS= read -r file; do if [ ! -x \"$file\" ]; then case \"$file\" in test/fixtures/*|*.json|*.txt|*.sh|*.js|*.log|*.tab|*.gitkeep) ;; *) echo \"$file\" ;; esac; fi; done | tee /dev/stderr | awk 'END {if (NR > 0) exit 1}'", "test:check-nonexec": "(IFS=$'\\n'; for file in $(git ls-files test); do if [ -x \"$file\" ] && [ ! -d \"$file\" ] && { [[ \"$file\" =~ '\\.(json|txt|sh|js|log)$' ]] || [[ \"$file\" =~ '^test/(mocks|fixtures)/.*' ]]; }; then echo \"$file\"; fi; done) | tee /dev/stderr | awk 'END {if (NR > 0) exit 1}'", "doctoc": "doctoc --title='## Table of Contents' --github README.md", "predoctoc:check": "cp README.md v-README.md.orig && npm run doctoc", diff --git a/test/fast/Unit tests/nvm install --offline b/test/fast/Unit tests/nvm install --offline old mode 100644 new mode 100755 index d4e7cbbc..9853ddd0 --- a/test/fast/Unit tests/nvm install --offline +++ b/test/fast/Unit tests/nvm install --offline @@ -12,8 +12,10 @@ nvm_download() { } # --offline with an already-installed version should succeed -INSTALLED_VERSION="$(nvm ls | command tail -1 | command awk '{print $1}' | command sed 's/\x1b\[[0-9;]*m//g')" -if [ -n "${INSTALLED_VERSION}" ] && [ "_${INSTALLED_VERSION}" != '_N/A' ] && [ "_${INSTALLED_VERSION}" != '_system' ]; then +# (`nvm ls` ends with the alias listing, so use `nvm_ls`, which emits bare +# versions, and skip its trailing `system ` line) +INSTALLED_VERSION="$(nvm_ls | command grep -v '^system' | command tail -n1)" +if [ -n "${INSTALLED_VERSION}" ] && [ "_${INSTALLED_VERSION}" != '_N/A' ]; then try nvm install --offline "${INSTALLED_VERSION}" [ "_$CAPTURED_EXIT_CODE" = "_0" ] \ || die "nvm install --offline with installed version '${INSTALLED_VERSION}' should succeed, got exit code $CAPTURED_EXIT_CODE" diff --git a/test/fixtures/fake-source-tarball/Makefile b/test/fixtures/fake-source-tarball/Makefile new file mode 100644 index 00000000..e6109555 --- /dev/null +++ b/test/fixtures/fake-source-tarball/Makefile @@ -0,0 +1,15 @@ +include config.mk + +all: out/fake-bin + +out/fake-bin: VERSION + mkdir -p out + printf '#!/bin/sh\necho %s\n' "$$(cat VERSION)" > out/fake-bin + chmod +x out/fake-bin + +install: all + mkdir -p "$(PREFIX)/bin" + cp out/fake-bin "$(PREFIX)/bin/node" + cp out/fake-bin "$(PREFIX)/bin/iojs" + +.PHONY: all install diff --git a/test/fixtures/fake-source-tarball/configure b/test/fixtures/fake-source-tarball/configure new file mode 100644 index 00000000..ebbf7e4d --- /dev/null +++ b/test/fixtures/fake-source-tarball/configure @@ -0,0 +1,22 @@ +#!/bin/sh + +# a fake node/io.js `configure`, for source-install tests: real io.js (and +# old node) can no longer be compiled on modern toolchains (python 2 +# `configure`, ancient V8), so source-install tests exercise nvm's entire +# source pipeline against this tiny stand-in instead. + +PREFIX='' +while [ $# -gt 0 ]; do + case "${1}" in + --prefix=*) PREFIX="${1#--prefix=}" ;; + esac + shift +done + +if [ -z "${PREFIX}" ]; then + echo 'configure: a --prefix is required' >&2 + exit 1 +fi + +printf 'PREFIX = %s\n' "${PREFIX}" > config.mk +echo "configured with PREFIX=${PREFIX}" diff --git a/test/installation_iojs/install from fake source b/test/installation_iojs/install from fake source new file mode 100755 index 00000000..567c498e --- /dev/null +++ b/test/installation_iojs/install from fake source @@ -0,0 +1,44 @@ +#!/bin/sh + +\. ../common.sh + +die () { echo "$@" ; exit 1; } + +\. ../../nvm.sh + +set -ex + +NVM_TEST_VERSION='v3.99.9' +NVM_PREFIXED_TEST_VERSION="iojs-${NVM_TEST_VERSION}" + +# Remove the stuff we're clobbering. +nvm uninstall "${NVM_TEST_VERSION}" || echo 'not installed' + +# Exercise nvm's entire source-install pipeline - cache, extract, configure, +# make, make install, activation - against a tiny fake source tree, seeded +# into the download cache and installed with --offline. This runs everywhere, +# including on toolchains where real io.js can not compile; `install from +# source` covers the real compile, in CI's gcc:4.9 container job. +SLUG="$(nvm_get_download_slug iojs source "${NVM_TEST_VERSION}")" +COMPRESSION="$(nvm_get_artifact_compression "${NVM_TEST_VERSION}")" +CACHE_DIR="$(nvm_cache_dir)/src/${SLUG}" +STAGING="$(mktemp -d)" +mkdir -p "${STAGING}/${SLUG}" "${CACHE_DIR}" +cp ../fixtures/fake-source-tarball/configure ../fixtures/fake-source-tarball/Makefile "${STAGING}/${SLUG}/" +chmod +x "${STAGING}/${SLUG}/configure" +echo "${NVM_TEST_VERSION}" > "${STAGING}/${SLUG}/VERSION" +if [ "${COMPRESSION}" = 'tar.xz' ]; then + (cd "${STAGING}" && tar -cJf "${CACHE_DIR}/${SLUG}.${COMPRESSION}" "${SLUG}") +else + (cd "${STAGING}" && tar -czf "${CACHE_DIR}/${SLUG}.${COMPRESSION}" "${SLUG}") +fi +rm -rf "${STAGING}" + +# Install from source +nvm install -s --offline "${NVM_PREFIXED_TEST_VERSION}" || die "'nvm install -s --offline ${NVM_PREFIXED_TEST_VERSION}' failed" + +# Check +nvm_is_version_installed "${NVM_PREFIXED_TEST_VERSION}" || die 'version not installed' +nvm run --silent "${NVM_PREFIXED_TEST_VERSION}" --version | grep "${NVM_TEST_VERSION}" || die "'nvm run ${NVM_PREFIXED_TEST_VERSION} --version | grep ${NVM_TEST_VERSION}' failed" + +rm -rf "${CACHE_DIR}" diff --git a/test/installation_iojs/install from source b/test/installation_iojs/install from source old mode 100644 new mode 100755 index d834e786..a49cb42b --- a/test/installation_iojs/install from source +++ b/test/installation_iojs/install from source @@ -8,6 +8,23 @@ die () { echo "$@" ; exit 1; } set -ex +# Compiling io.js requires a period toolchain: python 2 (its `configure`), +# and gcc <= 5 (its bundled V8). CI compiles it for real in a gcc:4.9 +# container job; on modern toolchains, this can only be skipped +# (`install from fake source` covers nvm's source pipeline everywhere). +GCC_MAJOR="$(gcc -dumpversion 2>/dev/null | command cut -d. -f1)" +if ! command -v python2 >/dev/null 2>&1 && ! python -V 2>&1 | command grep -q '^Python 2'; then + echo 'python 2 is not available; skipping the io.js source install' + exit 0 +fi +case "${GCC_MAJOR}" in + '' | *[!0-9]*) echo 'gcc is not available; skipping the io.js source install'; exit 0 ;; +esac +if [ "${GCC_MAJOR}" -gt 5 ]; then + echo "gcc <= 5 is required to compile io.js, got ${GCC_MAJOR}; skipping the io.js source install" + exit 0 +fi + NVM_TEST_VERSION='v3.3.1' NVM_PREFIXED_TEST_VERSION="iojs-${NVM_TEST_VERSION}" @@ -19,4 +36,4 @@ nvm uninstall "${NVM_TEST_VERSION}" || echo 'not installed' # Check nvm_is_version_installed "${NVM_PREFIXED_TEST_VERSION}" || die 'version not installed' -nvm run "${NVM_PREFIXED_TEST_VERSION}" --version | grep "${NVM_TEST_VERSION}" || "'nvm run ${NVM_PREFIXED_TEST_VERSION} --version | grep ${NVM_TEST_VERSION}' failed" +nvm run --silent "${NVM_PREFIXED_TEST_VERSION}" --version | grep "${NVM_TEST_VERSION}" || die "'nvm run ${NVM_PREFIXED_TEST_VERSION} --version | grep ${NVM_TEST_VERSION}' failed" diff --git a/test/installation_iojs/install version specified in .nvmrc from fake source b/test/installation_iojs/install version specified in .nvmrc from fake source new file mode 100755 index 00000000..c6bd45d6 --- /dev/null +++ b/test/installation_iojs/install version specified in .nvmrc from fake source @@ -0,0 +1,45 @@ +#!/bin/sh + +die () { echo "$@" ; exit 1; } + +\. ../../nvm.sh + +set -ex + +NVM_TEST_VERSION='v3.99.8' +NVM_PREFIXED_TEST_VERSION="iojs-${NVM_TEST_VERSION}" + +# Remove the stuff we're clobbering. +nvm uninstall "${NVM_TEST_VERSION}" || echo 'not installed' + +# Exercise nvm's source-install pipeline against a tiny fake source tree, +# seeded into the download cache and installed with --offline (see `install +# from fake source`). This runs everywhere, including on toolchains where +# real io.js can not compile; `install version specified in .nvmrc from +# source` covers the real compile, in CI's gcc:4.9 container job. +SLUG="$(nvm_get_download_slug iojs source "${NVM_TEST_VERSION}")" +COMPRESSION="$(nvm_get_artifact_compression "${NVM_TEST_VERSION}")" +CACHE_DIR="$(nvm_cache_dir)/src/${SLUG}" +STAGING="$(mktemp -d)" +mkdir -p "${STAGING}/${SLUG}" "${CACHE_DIR}" +cp ../fixtures/fake-source-tarball/configure ../fixtures/fake-source-tarball/Makefile "${STAGING}/${SLUG}/" +chmod +x "${STAGING}/${SLUG}/configure" +echo "${NVM_TEST_VERSION}" > "${STAGING}/${SLUG}/VERSION" +if [ "${COMPRESSION}" = 'tar.xz' ]; then + (cd "${STAGING}" && tar -cJf "${CACHE_DIR}/${SLUG}.${COMPRESSION}" "${SLUG}") +else + (cd "${STAGING}" && tar -czf "${CACHE_DIR}/${SLUG}.${COMPRESSION}" "${SLUG}") +fi +rm -rf "${STAGING}" + +# Install from source, with the version specified in .nvmrc +echo "${NVM_PREFIXED_TEST_VERSION}" > .nvmrc + +nvm install -s --offline || die "'nvm install -s --offline' failed" + +# Check +nvm_is_version_installed "${NVM_PREFIXED_TEST_VERSION}" || die 'version is not installed' +nvm run --silent "${NVM_PREFIXED_TEST_VERSION}" --version | grep "${NVM_TEST_VERSION}" \ + || die "'nvm run ${NVM_PREFIXED_TEST_VERSION} --version | grep ${NVM_TEST_VERSION}' failed" + +rm -rf "${CACHE_DIR}" diff --git a/test/installation_iojs/install version specified in .nvmrc from source b/test/installation_iojs/install version specified in .nvmrc from source old mode 100644 new mode 100755 index d02ddc7b..274108cf --- a/test/installation_iojs/install version specified in .nvmrc from source +++ b/test/installation_iojs/install version specified in .nvmrc from source @@ -6,18 +6,36 @@ die () { echo "$@" ; exit 1; } set -ex +# Compiling io.js requires a period toolchain: python 2 (its `configure`), +# and gcc <= 5 (its bundled V8). CI compiles it for real in a gcc:4.9 +# container job; on modern toolchains, this can only be skipped +# (`install version specified in .nvmrc from fake source` covers nvm's +# source pipeline everywhere). +GCC_MAJOR="$(gcc -dumpversion 2>/dev/null | command cut -d. -f1)" +if ! command -v python2 >/dev/null 2>&1 && ! python -V 2>&1 | command grep -q '^Python 2'; then + echo 'python 2 is not available; skipping the io.js source install' + exit 0 +fi +case "${GCC_MAJOR}" in + '' | *[!0-9]*) echo 'gcc is not available; skipping the io.js source install'; exit 0 ;; +esac +if [ "${GCC_MAJOR}" -gt 5 ]; then + echo "gcc <= 5 is required to compile io.js, got ${GCC_MAJOR}; skipping the io.js source install" + exit 0 +fi + NVM_TEST_VERSION='v3.3.0' NVM_PREFIXED_TEST_VERSION="iojs-${NVM_TEST_VERSION}" # Remove the stuff we're clobbering. nvm uninstall "${NVM_TEST_VERSION}" || echo 'not installed' -# Install from binary +# Install from source, with the version specified in .nvmrc echo "${NVM_PREFIXED_TEST_VERSION}" > .nvmrc -nvm install -s || "'nvm install -s' failed" +nvm install -s || die "'nvm install -s' failed" # Check nvm_is_version_installed "${NVM_PREFIXED_TEST_VERSION}" || die 'version is not installed' -nvm run "${NVM_PREFIXED_TEST_VERSION}" --version | grep "${NVM_TEST_VERSION}" \ +nvm run --silent "${NVM_PREFIXED_TEST_VERSION}" --version | grep "${NVM_TEST_VERSION}" \ || die "'nvm run ${NVM_PREFIXED_TEST_VERSION} --version | grep ${NVM_TEST_VERSION}' failed" diff --git a/test/installation_iojs/teardown_dir b/test/installation_iojs/teardown_dir index 2ca9cf11..d8ca2d4e 100755 --- a/test/installation_iojs/teardown_dir +++ b/test/installation_iojs/teardown_dir @@ -6,6 +6,8 @@ nvm uninstall iojs-v1.0.0 nvm uninstall iojs-v1.0.3 nvm uninstall iojs-v3.3.0 nvm uninstall iojs-v3.3.1 +nvm uninstall iojs-v3.99.8 +nvm uninstall iojs-v3.99.9 if [ -f ".nvmrc" ]; then rm .nvmrc diff --git a/test/slow/nvm exec/Running 'nvm exec' with help should not parse b/test/slow/nvm exec/Running 'nvm exec' with help should not parse old mode 100644 new mode 100755 index 4e8b6327..bde5ee4b --- a/test/slow/nvm exec/Running 'nvm exec' with help should not parse +++ b/test/slow/nvm exec/Running 'nvm exec' with help should not parse @@ -6,4 +6,6 @@ die () { echo "$@" ; exit 1; } nvm use 0.10 -nvm exec stable -- node --help | grep 'Usage: node [options]' || die "Help menu should have been displayed for node and not nvm" +# note: in a BRE, `[options]` is a bracket expression, so the literal `[options]` +# in node's help output could never match; match the unbracketed prefix instead +nvm exec stable -- node --help | grep 'Usage: node' || die "Help menu should have been displayed for node and not nvm" diff --git a/test/slow/nvm uninstall/Running 'nvm uninstall' with incorrect file permissions fails nicely b/test/slow/nvm uninstall/Running 'nvm uninstall' with incorrect file permissions fails nicely old mode 100644 new mode 100755 index 0c19db8f..e91e5ed0 --- a/test/slow/nvm uninstall/Running 'nvm uninstall' with incorrect file permissions fails nicely +++ b/test/slow/nvm uninstall/Running 'nvm uninstall' with incorrect file permissions fails nicely @@ -1,30 +1,51 @@ #!/bin/sh -die () { echo "$@" ; exit 1; } +die () { echo "$@" ; cleanup ; exit 1; } # Source nvm \. ../../../nvm.sh -# Version to install/uninstall -NVM_TEST_VERSION=5.10.1 +\. ../../common.sh -# Make sure it's not already here -[ -e ../../../$NVM_TEST_VERSION ] && rm -R ../../../$NVM_TEST_VERSION +NVM_TEST_VERSION=v5.99.99 -# Install it -nvm install $NVM_TEST_VERSION +cleanup() { + VERSION_PATH="${VERSION_PATH-}" + if [ -n "${VERSION_PATH}" ] && [ -d "${VERSION_PATH}" ]; then + sudo -n rm -f "${VERSION_PATH}/lib/root-owned-file" 2>/dev/null + rm -rf "${VERSION_PATH}" + fi +} -# Make sure it installed -nvm ls | grep "$NVM_TEST_VERSION" || die "Failed to install node" +# The permissions check only trips on files that are neither writable nor +# self-owned, and creating one requires another user: skip where passwordless +# sudo is unavailable; CI always has it. +if ! sudo -n true 2>/dev/null; then + echo 'passwordless sudo is not available, skipping' + exit 0 +fi -# Install global module as root -npm_path=$(which npm) -sudo -n "$npm_path" install jspm@'<2' -g || die 'either sudo failed, or `npm install jspm -g` failed`' +# Install a fake version to uninstall +make_fake_node "${NVM_TEST_VERSION}" || die 'unable to make fake node' +VERSION_PATH="$(nvm_version_path "${NVM_TEST_VERSION}")" -# Switch to another version so we can uninstall -nvm use 0.12.7 +# Make part of the installation folder unwritable, and not self-owned +mkdir -p "${VERSION_PATH}/lib" +sudo -n touch "${VERSION_PATH}/lib/root-owned-file" || die 'sudo touch failed' +sudo -n chmod 444 "${VERSION_PATH}/lib/root-owned-file" || die 'sudo chmod failed' + +# Make sure the version to uninstall is not the active one +nvm deactivate >/dev/null 2>&1 || true # Attempt to uninstall it -RETURN_MESSAGE="$(nvm uninstall $NVM_TEST_VERSION 2>&1)" +RETURN_MESSAGE="$(nvm uninstall "${NVM_TEST_VERSION}" 2>&1)" CHECK_FOR="Cannot uninstall, incorrect permissions on installation folder" -test "${RETURN_MESSAGE#*$CHECK_FOR}" != "$RETURN_MESSAGE" || die "Failed to show error message" +case "${RETURN_MESSAGE}" in + *"${CHECK_FOR}"*) ;; + *) die "Failed to show error message; got: ${RETURN_MESSAGE}" ;; +esac + +# ... and the version must not have been removed +nvm_is_version_installed "${NVM_TEST_VERSION}" || die 'version was uninstalled despite the permissions error' + +cleanup diff --git a/test/slow/nvm use/Running 'nvm use' with nvmrc containing not installed version b/test/slow/nvm use/Running 'nvm use' with nvmrc containing not installed version old mode 100644 new mode 100755