Files
nvm/test/fast/Aliases/nvm_resolve_alias handles cycles
T
Jake LodwickandJordan Harband d22bf7f538 [Refactor] nvm_alias, nvm_resolve_alias: use builtins
[Tests] `nvm_alias`, `nvm_resolve_alias`: add edge-case tests

nvm_alias() used a sed/awk pipeline to strip comments and blank lines from alias files that almost always contain a single word.
A while-read loop with parameter expansion does the same filtering more directly.

nvm_resolve_alias() piped nvm_alias through head and tail to extract one line, and used printf/grep for cycle detection.
Parameter expansion and a case statement replace both without the extra plumbing.

All replacements are POSIX (read -r, case, IFS=, parameter expansion).
As a side effect, this also removes 4 external process invocations during shell init.

[Fix] `nvm_resolve_alias`: detect cycles via newline-anchored `case`

The original commit referenced above changed SEEN_ALIASES from `\n`-delimited
storage (interpreted by `printf '%b' | nvm_grep -e "^${name}$"`) to space-
delimited but left the line-anchored grep in place — without newlines in
the haystack the anchored pattern can never match, so cycles never break.

Switch to literal-newline storage and a `case` pattern anchored on those
newlines. Newline anchoring also handles alias names containing spaces,
which token-based patterns false-positive on (e.g. lookup of `bar` matches
substring " bar " inside " foo bar midway " when the chain visits the
multi-token alias `foo bar`).

New test file covers self-loop, multi-hop loop, cycle through a
space-bearing alias name, and a non-cycle through a space-bearing
alias name. Existing `test/fast/Aliases/circular/` fixtures continue
to pass.
2026-02-17 13:44:31 -07:00

53 lines
1.7 KiB
Bash
Executable File

#!/bin/sh
die () { echo "$@" ; exit 1; }
export NVM_DIR="$(cd ../../.. && pwd)"
: nvm.sh
\. "${NVM_DIR}/nvm.sh"
# 1-hop self-reference: alias points to itself
echo 'self' > '../../../alias/self'
ACTUAL="$(nvm_resolve_alias self)"
EXPECTED='∞'
[ "${ACTUAL}" = "${EXPECTED}" ] || die "expected >${EXPECTED}< for self-reference, got >${ACTUAL}<"
rm -f '../../../alias/self'
# Multi-hop loop: link1 -> link2 -> link3 -> link1
echo 'link2' > '../../../alias/link1'
echo 'link3' > '../../../alias/link2'
echo 'link1' > '../../../alias/link3'
ACTUAL="$(nvm_resolve_alias link1)"
EXPECTED='∞'
[ "${ACTUAL}" = "${EXPECTED}" ] || die "expected >${EXPECTED}< for 3-hop cycle, got >${ACTUAL}<"
rm -f '../../../alias/link1' '../../../alias/link2' '../../../alias/link3'
# Cycle through an alias name containing a space
echo 'midway' > '../../../alias/foo bar'
echo 'foo bar' > '../../../alias/midway'
ACTUAL="$(nvm_resolve_alias 'foo bar')"
EXPECTED='∞'
[ "${ACTUAL}" = "${EXPECTED}" ] || die "expected >${EXPECTED}< for space-name cycle, got >${ACTUAL}<"
rm -f '../../../alias/foo bar' '../../../alias/midway'
# Non-cycle through an alias name containing a space.
# Token-based cycle detection would false-positive on this chain because
# 'bar' appears as a substring of the multi-token alias name 'foo bar'.
# Resolves: 'foo bar' -> 'midway' -> 'bar' -> 0.0.99
echo 'midway' > '../../../alias/foo bar'
echo 'bar' > '../../../alias/midway'
echo '0.0.99' > '../../../alias/bar'
ACTUAL="$(nvm_resolve_alias 'foo bar')"
EXPECTED='v0.0.99'
[ "${ACTUAL}" = "${EXPECTED}" ] || die "expected >${EXPECTED}< for space-name chain, got >${ACTUAL}<"
rm -f '../../../alias/foo bar' '../../../alias/midway' '../../../alias/bar'