#!/bin/bash # Per-app install location: does the choice arrive, validate, and stick? # # scripts/dev/lp-storage-place-test # # Two routes carry the same choice — `app install --storage=` and the # storage dropdown in the App Center's install form, which travels inside # config_variables — so the first thing to pin is that they resolve to one # answer, with the explicit flag winning. # # The rest is ordering. installApp copies the app template, sources it, and # later applies the form overrides. The template ships CFG__STORAGE=default, # so a choice that is not folded into config_variables gets written back to # "default" by that last pass — leaving an app whose directory is on one disk # and whose config claims another. That resolves correctly only for as long as # nothing sources the config, which is why it is easy to miss. REPO="$(cd "$(dirname "$0")/../.." && pwd)" BASE="$(mktemp -d "${TMPDIR:-/tmp}/lp-place-test-XXXXXX")" trap 'rm -rf "$BASE"' EXIT fail=0 chk(){ if [[ "$2" == "$3" ]]; then echo " ok $1"; else echo " FAIL $1: got '$2' want '$3'"; fail=1; fi; } mkdir -p "$BASE/primary" "$BASE/disk1" isNotice(){ :; }; isError(){ LAST_ERR="$*"; }; isSuccessful(){ :; } primaryRoot(){ printf '%s' "$BASE/primary"; } storageLocationPath(){ [[ "$1" == "disk1" ]] && { printf '%s' "$BASE/disk1"; return 0; }; return 1; } storageLocationName(){ basename "$1"; } storageRoots(){ printf '%s\n%s\n' "$BASE/primary" "$BASE/disk1"; } storageRootAvailable(){ [[ "$1" == "$BASE/unmounted" ]] && return 1; return 0; } storageIndexSet(){ INDEXED="$1 -> $2"; } storageCacheReset(){ :; } updateConfigOption(){ CFG_WRITTEN="$1=$2 -> $3"; } source "$REPO/scripts/storage/storage_place.sh" echo "--- the two routes resolve to one answer ---" chk "flag only" "$(LP_INSTALL_STORAGE=disk1 storageChoiceFor myapp '')" "disk1" chk "form only" "$(storageChoiceFor myapp 'CFG_MYAPP_STORAGE=disk1|CFG_MYAPP_PORT=80')" "disk1" chk "flag wins" "$(LP_INSTALL_STORAGE=disk1 storageChoiceFor myapp 'CFG_MYAPP_STORAGE=primary')" "disk1" chk "neither" "$(storageChoiceFor myapp 'CFG_MYAPP_PORT=80')" "" echo "--- the choice is folded into config_variables ---" # Without this the later override pass writes the template's "default" back. out=$(storageChoiceMerge myapp disk1 'CFG_MYAPP_PORT=80|CFG_MYAPP_STORAGE=default') chk "storage set" "$(tr '|' '\n' <<< "$out" | grep -c '^CFG_MYAPP_STORAGE=disk1$')" "1" chk "no stale value" "$(tr '|' '\n' <<< "$out" | grep -c '^CFG_MYAPP_STORAGE=default$')" "0" chk "others kept" "$(tr '|' '\n' <<< "$out" | grep -c '^CFG_MYAPP_PORT=80$')" "1" chk "from empty" "$(storageChoiceMerge myapp disk1 '')" "CFG_MYAPP_STORAGE=disk1" echo "--- placement validates before anything is created ---" LAST_ERR=""; INDEXED="" storagePlaceAppPre myapp nosuch && { echo " FAIL unknown location accepted"; fail=1; } || echo " ok unknown location refused" chk "and says so" "${LAST_ERR:0:26}" "No such storage location: " chk "nothing indexed" "$INDEXED" "" storageRootAvailable(){ return 1; } LAST_ERR="" storagePlaceAppPre myapp disk1 && { echo " FAIL unmounted location accepted"; fail=1; } || echo " ok unmounted location refused" storageRootAvailable(){ return 0; } mkdir -p "$BASE/disk1/taken" LAST_ERR="" storagePlaceAppPre taken disk1 && { echo " FAIL installed over an existing directory"; fail=1; } || echo " ok refuses to install over an existing directory" echo "--- a good placement records both answers ---" INDEXED="" storagePlaceAppPre myapp disk1 || { echo " FAIL valid placement refused"; fail=1; } chk "index" "$INDEXED" "myapp -> $BASE/disk1" chk "exported" "$CFG_MYAPP_STORAGE" "disk1" echo "--- an app pinned to the primary root cannot be placed elsewhere ---" # Some apps are referenced by other apps at a literal path, so their template # marks the field **READONLY**. storageMoveApp refuses to move those; installing # one onto another disk is the same violation from the other end. install_containers_dir="$BASE/templates/" mkdir -p "$BASE/templates/pinned" printf 'CFG_PINNED_STORAGE=default # Storage Location - Fixed **READONLY**\n' \ > "$BASE/templates/pinned/pinned.config" LAST_ERR=""; INDEXED="" storagePlaceAppPre pinned disk1 && { echo " FAIL a pinned app was placed elsewhere"; fail=1; } \ || echo " ok refused" chk "nothing indexed" "$INDEXED" "" mkdir -p "$BASE/templates/free" printf 'CFG_FREE_STORAGE=default # Storage Location - Which disk [default:Primary]\n' \ > "$BASE/templates/free/free.config" INDEXED="" storagePlaceAppPre free disk1 || { echo " FAIL an unpinned app was refused"; fail=1; } chk "unpinned still placed" "$INDEXED" "free -> $BASE/disk1" echo "--- \"default\" is not a placement ---" # Templates ship it; treating it as a choice would pin every app to the primary # root and break "follow CFG_STORAGE_DEFAULT". INDEXED="" storagePlaceAppPre other default || { echo " FAIL default should be a no-op"; fail=1; } chk "nothing indexed" "$INDEXED" "" echo "" if (( fail )); then echo "FAILED"; exit 1; fi echo "All install-placement checks passed."