#!/usr/bin/env bash # test-link-pi-extensions.sh — two-sided tests for # rootfs/usr/local/lib/pi-devbox/link-pi-extensions.sh. # # Runs the real script against a throwaway HOME / PI_CODING_AGENT_DIR, so it never # touches a live ~/.pi/agent. Every "must link" case has a "must NOT touch" twin: # the script's whole contract is that it only ever creates a link where nothing # exists, so the negative cases are the important half — and one of them (a real # extension of the same name wins) is what keeps a user's own file safe. # # bash scripts/test-link-pi-extensions.sh # CI (lint.yml) and locally # LINK_PI_EXTENSIONS=/path/to/variant.sh bash scripts/test-link-pi-extensions.sh set -u here="$(cd "$(dirname "$0")/.." && pwd)" SCRIPT="${LINK_PI_EXTENSIONS:-$here/rootfs/usr/local/lib/pi-devbox/link-pi-extensions.sh}" T="$(mktemp -d)"; trap 'rm -rf "$T"' EXIT PASS=0; FAIL=0 ok() { printf ' ✅ %s\n' "$1"; PASS=$((PASS+1)); } bad() { printf ' ❌ %s — %s\n' "$1" "$2"; FAIL=$((FAIL+1)); } # One fresh world per case: a shared dir holding two extensions (plus files that # are NOT extensions), an empty HOME, agent dir unset. world() { rm -rf "$T/w"; mkdir -p "$T/w/shared" "$T/w/home" printf 'export default {}\n' > "$T/w/shared/alpha.ts" printf 'export default {}\n' > "$T/w/shared/beta.ts" printf '{"providers":{}}\n' > "$T/w/shared/models.json" printf 'not an extension\n' > "$T/w/shared/README.md" SRC="$T/w/shared"; AG="$T/w/home/.pi/agent"; DEST="$AG/extensions" } # run [env assignments...] — run the script with HOME and PI_CODING_AGENT_DIR pointed into the world. # "$@" goes FIRST so an option like `-u PI_EXTENSIONS_SRC` is still parsed as one by env. run() { OUT="$(env "$@" HOME="$T/w/home" PI_CODING_AGENT_DIR="$AG" bash "$SCRIPT" 2>&1)"; RC=$?; } echo "── link-pi-extensions.sh ($SCRIPT)" world; run PI_EXTENSIONS_SRC="$SRC" if [ "$RC" = 0 ] && [ "$(readlink "$DEST/alpha.ts")" = "$SRC/alpha.ts" ] \ && [ "$(readlink "$DEST/beta.ts")" = "$SRC/beta.ts" ] && [[ "$OUT" == *"linked alpha.ts"* ]]; then ok "links every top-level *.ts when the shared dir exists and nothing is there (creates the extensions dir)" else bad "link when absent" "rc=$RC link=$(readlink "$DEST/alpha.ts" 2>&1) out=[$OUT]"; fi run PI_EXTENSIONS_SRC="$SRC" if [ "$RC" = 0 ] && [ -z "$OUT" ] && [ "$(readlink "$DEST/alpha.ts")" = "$SRC/alpha.ts" ]; then ok "second run is a silent no-op (every container start runs it)" else bad "idempotent" "rc=$RC out=[$OUT]"; fi world; run PI_EXTENSIONS_SRC="$SRC" if [ ! -e "$DEST/models.json" ] && [ ! -L "$DEST/models.json" ] \ && [ ! -e "$DEST/README.md" ] && [ ! -L "$DEST/README.md" ]; then ok "non-.ts files in the shared dir are ignored (models.json, README.md)" else bad "non-.ts ignored" "dest holds: $(ls -A "$DEST" 2>&1 | tr '\n' ' ')"; fi # A nested .ts must NOT be linked: the dir is fleet pi config, not an extension # tree, and loading something nobody intended to publish as an extension is worse # than missing one. world; mkdir -p "$SRC/nested"; printf 'export default {}\n' > "$SRC/nested/gamma.ts"; run PI_EXTENSIONS_SRC="$SRC" if [ "$RC" = 0 ] && [ ! -e "$DEST/gamma.ts" ] && [ ! -L "$DEST/gamma.ts" ]; then ok "does not recurse — a nested *.ts is left alone (only top level is the contract)" else bad "no recursion" "rc=$RC found=$(readlink "$DEST/gamma.ts" 2>&1)"; fi world; mkdir -p "$SRC/dir.ts"; run PI_EXTENSIONS_SRC="$SRC" if [ "$RC" = 0 ] && [ ! -e "$DEST/dir.ts" ] && [ ! -L "$DEST/dir.ts" ]; then ok "a directory whose name ends in .ts is not linked" else bad "dir named *.ts" "rc=$RC out=[$OUT]"; fi world; mkdir -p "$DEST"; printf 'my own\n' > "$DEST/alpha.ts"; before="$(sha256sum "$DEST/alpha.ts")"; run PI_EXTENSIONS_SRC="$SRC" if [ "$RC" = 0 ] && [ ! -L "$DEST/alpha.ts" ] && [ "$(sha256sum "$DEST/alpha.ts")" = "$before" ] \ && [[ "$OUT" == NOTE*"real file"* ]] && [ "$(readlink "$DEST/beta.ts")" = "$SRC/beta.ts" ]; then ok "never replaces the user's own extension of that name (content unchanged, one NOTE line) — and still links the others" else bad "real file kept" "rc=$RC islink=$([ -L "$DEST/alpha.ts" ] && echo y) out=[$OUT]"; fi world; mkdir -p "$DEST"; ln -s /somewhere/else.ts "$DEST/alpha.ts"; run PI_EXTENSIONS_SRC="$SRC" if [ "$RC" = 0 ] && [ "$(readlink "$DEST/alpha.ts")" = /somewhere/else.ts ] && [[ "$OUT" == NOTE*"left alone"* ]]; then ok "never re-points a link the user made to another file" else bad "foreign link kept" "rc=$RC link=$(readlink "$DEST/alpha.ts") out=[$OUT]"; fi world; rm -rf "$SRC"; run PI_EXTENSIONS_SRC="$SRC" if [ "$RC" = 0 ] && [ ! -e "$DEST" ] && [ ! -L "$DEST" ] && [ -z "$OUT" ]; then ok "no shared dir → creates nothing and says nothing (the default for most users)" else bad "absent source" "rc=$RC out=[$OUT]"; fi world; rm -f "$SRC"/*.ts; run PI_EXTENSIONS_SRC="$SRC" if [ "$RC" = 0 ] && [ ! -e "$DEST" ] && [ ! -L "$DEST" ] && [ -z "$OUT" ]; then ok "a shared dir with no *.ts → creates nothing (the dir is not treated as an extension itself)" else bad "empty source dir" "rc=$RC out=[$OUT]"; fi for v in off OFF ""; do world; run PI_EXTENSIONS_SRC="$v" if [ "$RC" = 0 ] && [ ! -e "$DEST" ] && [ ! -L "$DEST" ] && [ -z "$OUT" ]; then ok "PI_EXTENSIONS_SRC='$v' disables it" else bad "disable '$v'" "rc=$RC out=[$OUT]"; fi done world; run PI_EXTENSIONS_SRC="$SRC"; rm -f "$SRC"/alpha.ts; run PI_EXTENSIONS_SRC="$SRC" if [ "$RC" = 0 ] && [ -L "$DEST/alpha.ts" ] && [ "$(readlink "$DEST/alpha.ts")" = "$SRC/alpha.ts" ] && [ -z "$OUT" ]; then ok "our link whose target vanished (mount removed) is left as is, silently" else bad "dangling own link" "rc=$RC out=[$OUT]"; fi # pi honours PI_CODING_AGENT_DIR, so the links must land THERE, not in $HOME/.pi/agent. world; AG="$T/w/elsewhere/agent"; DEST="$AG/extensions"; run PI_EXTENSIONS_SRC="$SRC" if [ "$RC" = 0 ] && [ "$(readlink "$DEST/alpha.ts" 2>/dev/null)" = "$SRC/alpha.ts" ] \ && [ ! -e "$T/w/home/.pi/agent/extensions" ]; then ok "links into PI_CODING_AGENT_DIR when set (and leaves \$HOME/.pi/agent alone)" else bad "PI_CODING_AGENT_DIR" "rc=$RC link=$(readlink "$DEST/alpha.ts" 2>&1) out=[$OUT]"; fi # 'off' must be a switch, not merely a path that happens not to exist: with a # directory literally named "off" in the working directory it must still disable. world; mkdir -p "$T/w/off"; printf 'export default {}\n' > "$T/w/off/alpha.ts" OUT="$(cd "$T/w" && env PI_EXTENSIONS_SRC=off HOME="$T/w/home" PI_CODING_AGENT_DIR="$AG" bash "$SCRIPT" 2>&1)"; RC=$? if [ "$RC" = 0 ] && [ ! -e "$DEST" ] && [ ! -L "$DEST" ]; then ok "PI_EXTENSIONS_SRC=off disables it even when a directory named 'off' exists in the cwd" else bad "off is a switch" "rc=$RC link=$(readlink "$DEST/alpha.ts" 2>&1) out=[$OUT]"; fi # The default is /opt/fleet-pi — the same dir that holds models.json. Whether it # exists, and whether it holds any *.ts, depends on where this runs (a fleet devbox: # maybe both; CI: neither), so derive the expectation instead of assuming it. world; run -u PI_EXTENSIONS_SRC default_ts="$(ls -1 /opt/fleet-pi/*.ts 2>/dev/null | head -1)" if [ -n "$default_ts" ]; then _dn="$(basename "$default_ts")" if [ "$RC" = 0 ] && [ "$(readlink "$DEST/$_dn" 2>/dev/null)" = "$default_ts" ]; then ok "PI_EXTENSIONS_SRC unset → default /opt/fleet-pi (here: holds $_dn) is linked" else bad "default path (with .ts)" "rc=$RC link=$(readlink "$DEST/$_dn" 2>&1) out=[$OUT]"; fi unset _dn else if [ "$RC" = 0 ] && [ ! -e "$DEST" ] && [ ! -L "$DEST" ] && [ -z "$OUT" ]; then ok "PI_EXTENSIONS_SRC unset → default /opt/fleet-pi (absent or has no *.ts here) → nothing" else bad "default path (no .ts)" "rc=$RC out=[$OUT]"; fi fi echo "── $PASS passed, $FAIL failed" [ "$FAIL" = 0 ]