Add install-script regression tests for the piped and version fixes

Cover the two installer bugs fixed in c98acb9e8 so they cannot regress:

- TestRootInstallScriptSourceGuardSurvivesPipedExecution runs the source guard
  through stdin the way curl ... | bash does and asserts it reaches the
  installer body instead of aborting on BASH_SOURCE[0] unbound. (#1526)

- TestInstallSHAgentVersionMismatchIgnoresVPrefix drives the version check and
  asserts v6.0.4 vs 6.0.4 does not warn while a real 6.0.3 vs 6.0.4 does, plus
  content assertions pinning the normalization in scripts/install.sh. (#1527)

Refs #1526 #1527
This commit is contained in:
rcourtman 2026-07-09 12:04:41 +01:00
parent 6999ca0119
commit fbddd91fcb
2 changed files with 75 additions and 0 deletions

View file

@ -132,6 +132,9 @@ func TestInstallSHAgentDownloadIsServerVersionAware(t *testing.T) {
`SERVER_VERSION="$(printf '%s' "$server_version_json" | sed -n 's/.*"version"`,
`DOWNLOAD_QUERY="${DOWNLOAD_QUERY}&serverVersion=${SERVER_VERSION}"`,
`log_info "Pulse server version: ${SERVER_VERSION}"`,
`NEW_VERSION_NORMALIZED="${NEW_VERSION#v}"`,
`SERVER_VERSION_NORMALIZED="${SERVER_VERSION#v}"`,
`"$NEW_VERSION_NORMALIZED" != "$SERVER_VERSION_NORMALIZED"`,
`Downloaded agent version (${NEW_VERSION}) does not match Pulse server version (${SERVER_VERSION})`,
}
for _, needle := range required {
@ -141,6 +144,41 @@ func TestInstallSHAgentDownloadIsServerVersionAware(t *testing.T) {
}
}
// Regression test for #1527: the agent binary reports its version as "v6.0.4"
// while the server /api/version reports "6.0.4", so the mismatch check must
// strip a leading "v" before comparing and only warn on a genuine difference.
func TestInstallSHAgentVersionMismatchIgnoresVPrefix(t *testing.T) {
harness := `
set -euo pipefail
log_warn() { echo "WARN:$*"; }
SERVER_VERSION="$1"
NEW_VERSION="$2"
NEW_VERSION_NORMALIZED="${NEW_VERSION#v}"
SERVER_VERSION_NORMALIZED="${SERVER_VERSION#v}"
if [[ -n "$SERVER_VERSION" && -n "$NEW_VERSION" && "$NEW_VERSION" != "unknown" && "$NEW_VERSION_NORMALIZED" != "$SERVER_VERSION_NORMALIZED" ]]; then
log_warn "Downloaded agent version (${NEW_VERSION}) does not match Pulse server version (${SERVER_VERSION})."
fi
echo DONE
`
run := func(server, agent string) string {
out, err := exec.Command("bash", "-c", harness, "_", server, agent).CombinedOutput()
if err != nil {
t.Fatalf("bash: %v\n%s", err, out)
}
return string(out)
}
if got := run("6.0.4", "v6.0.4"); strings.Contains(got, "WARN:") {
t.Fatalf("equal versions differing only by a leading v warned:\n%s", got)
}
if got := run("6.0.4", "6.0.4"); strings.Contains(got, "WARN:") {
t.Fatalf("identical versions warned:\n%s", got)
}
if got := run("6.0.4", "v6.0.3"); !strings.Contains(got, "WARN:") {
t.Fatalf("genuine version mismatch did not warn:\n%s", got)
}
}
func TestInstallSHAgentServiceSecurityDefaults(t *testing.T) {
content, err := os.ReadFile(repoFile("scripts", "install.sh"))
if err != nil {

View file

@ -1242,3 +1242,40 @@ func TestRootInstallScriptUpdateFlowsRefreshExistingAutoUpdateAssets(t *testing.
t.Fatalf("expected at least 5 install flows to refresh existing auto-update assets, found %d", got)
}
}
// Regression test for #1526 (and the earlier #1396): when the installer is piped
// to bash (curl ... | bash) there is no source file, so BASH_SOURCE is unset.
// The "am I being sourced?" guard must default the lookup or `set -u` aborts the
// whole run before the installer body with "BASH_SOURCE[0]: unbound variable".
func TestRootInstallScriptSourceGuardSurvivesPipedExecution(t *testing.T) {
content, err := os.ReadFile(filepath.Join("..", "..", "install.sh"))
if err != nil {
t.Fatalf("read root install.sh: %v", err)
}
script := string(content)
if strings.Contains(script, `[[ "${BASH_SOURCE[0]}" == "$0" ]] || return 0`) {
t.Fatal("install.sh still uses the unguarded BASH_SOURCE source check that aborts under curl | bash")
}
guard := `if [[ -n "${BASH_SOURCE[0]:-}" && "${BASH_SOURCE[0]}" != "$0" ]]; then`
if !strings.Contains(script, guard) {
t.Fatalf("install.sh missing piped-safe source guard: %s", guard)
}
// Run the guard the way `curl ... | bash` does: fed through stdin with no
// source file, so BASH_SOURCE is empty. It must fall through to the body.
harness := "set -euo pipefail\n" + guard + "\n return 0\nfi\necho INSTALLER_BODY_REACHED\n"
cmd := exec.Command("bash")
cmd.Stdin = strings.NewReader(harness)
out, err := cmd.CombinedOutput()
if err != nil {
t.Fatalf("piped source guard failed: %v\n%s", err, out)
}
got := string(out)
if strings.Contains(got, "unbound variable") {
t.Fatalf("source guard aborted piped execution with unbound variable:\n%s", got)
}
if !strings.Contains(got, "INSTALLER_BODY_REACHED") {
t.Fatalf("source guard did not fall through to the installer body when piped:\n%s", got)
}
}