update to V2
This commit is contained in:
Executable
+83
@@ -0,0 +1,83 @@
|
||||
#!/usr/bin/env bash
|
||||
set -u -o pipefail
|
||||
|
||||
TEST_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
declare -a shell_files=()
|
||||
declare -a test_files=()
|
||||
failures=0
|
||||
required_command=''
|
||||
|
||||
if (( BASH_VERSINFO[0] < 4 ||
|
||||
(BASH_VERSINFO[0] == 4 && BASH_VERSINFO[1] < 3) )); then
|
||||
printf 'TA-ProxMenu tests require Bash 4.3 or newer; found %s.\n' \
|
||||
"$BASH_VERSION" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for required_command in git python3; do
|
||||
if ! command -v "$required_command" >/dev/null 2>&1; then
|
||||
printf 'TA-ProxMenu tests require %s.\n' "$required_command" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
while IFS= read -r -d '' file; do
|
||||
shell_files+=("$file")
|
||||
done < <(
|
||||
find "$TEST_ROOT" -type f \( -name '*.sh' -o -name '*.inc' \) \
|
||||
-not -path '*/.git/*' -print0 |
|
||||
sort -z
|
||||
)
|
||||
|
||||
printf 'Bash syntax\n'
|
||||
for file in "${shell_files[@]}"; do
|
||||
if ! bash -n "$file"; then
|
||||
(( failures += 1 ))
|
||||
fi
|
||||
done
|
||||
(( failures == 0 )) && printf ' PASS: %d files\n' "${#shell_files[@]}"
|
||||
|
||||
printf '\nGit whitespace\n'
|
||||
if git -C "$TEST_ROOT" diff --check &&
|
||||
git -C "$TEST_ROOT" diff --cached --check; then
|
||||
printf ' PASS\n'
|
||||
else
|
||||
(( failures += 1 ))
|
||||
fi
|
||||
|
||||
printf '\nShellCheck\n'
|
||||
if command -v shellcheck >/dev/null 2>&1; then
|
||||
# These rules cannot follow TAPM's runtime-sourced defaults/colors or
|
||||
# functions invoked indirectly through traps and menu dispatch.
|
||||
if shellcheck -x -e SC1091,SC2034,SC2154,SC2317 "${shell_files[@]}"; then
|
||||
printf ' PASS\n'
|
||||
else
|
||||
(( failures += 1 ))
|
||||
fi
|
||||
else
|
||||
printf ' SKIP: shellcheck is not installed\n'
|
||||
fi
|
||||
|
||||
while IFS= read -r -d '' file; do
|
||||
test_files+=("$file")
|
||||
done < <(
|
||||
find "${TEST_ROOT}/tests" -maxdepth 1 -type f -name 'test-*.sh' \
|
||||
-print0 | sort -z
|
||||
)
|
||||
|
||||
printf '\nBehavior tests\n'
|
||||
for file in "${test_files[@]}"; do
|
||||
printf ' %-30s ' "${file##*/}"
|
||||
if bash "$file"; then
|
||||
:
|
||||
else
|
||||
(( failures += 1 ))
|
||||
fi
|
||||
done
|
||||
|
||||
printf '\n'
|
||||
if (( failures > 0 )); then
|
||||
printf 'FAILED: %d check group(s) failed.\n' "$failures" >&2
|
||||
exit 1
|
||||
fi
|
||||
printf 'All TA-ProxMenu checks passed.\n'
|
||||
@@ -0,0 +1,79 @@
|
||||
#!/usr/bin/env bash
|
||||
set -u -o pipefail
|
||||
|
||||
TEST_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
source "${TEST_ROOT}/tests/testlib.sh"
|
||||
source "${TEST_ROOT}/inc/cluster-update.inc"
|
||||
|
||||
nodes_json='[
|
||||
{"node":"pve3","status":"offline"},
|
||||
{"node":"pve1","status":"online"},
|
||||
{"node":"pve2","status":"online"},
|
||||
{"node":"bad node","status":"online"}
|
||||
]'
|
||||
expected=$'pve1\tonline\npve2\tonline\npve3\toffline'
|
||||
assert_equal "$expected" \
|
||||
"$(printf '%s' "$nodes_json" | TAPM_CLUSTER_NODES_FROM_JSON)" \
|
||||
"cluster node discovery filters and sorts API output"
|
||||
|
||||
wrapped_json='{"data":[{"node":"pve2.example","status":"ONLINE"}]}'
|
||||
assert_equal $'pve2.example\tonline' \
|
||||
"$(printf '%s' "$wrapped_json" | TAPM_CLUSTER_NODES_FROM_JSON)" \
|
||||
"wrapped API response and normalized status"
|
||||
|
||||
if printf '%s' 'not-json' | TAPM_CLUSTER_NODES_FROM_JSON 2>/dev/null; then
|
||||
printf 'FAIL: malformed cluster JSON was accepted\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
assert_failure "remote node rejects shell characters" \
|
||||
TAPM_UPDATE_REMOTE_NODE 'pve1;reboot'
|
||||
|
||||
test_dir="$(mktemp -d)"
|
||||
trap 'rm -rf "$test_dir"' EXIT
|
||||
TAPM_COROSYNC_CONFIG="${test_dir}/corosync.conf"
|
||||
printf 'totem {}\n' >"$TAPM_COROSYNC_CONFIG"
|
||||
update_log="${test_dir}/updates"
|
||||
LightCyan=0
|
||||
LightYellow=0
|
||||
Green=0
|
||||
Red=0
|
||||
Default=0
|
||||
idsCL[0]=''
|
||||
pvesh() {
|
||||
return 0
|
||||
}
|
||||
TAPM_CLUSTER_NODE_ROWS() {
|
||||
printf 'pve1\tonline\npve2\tonline\npve3\toffline\n'
|
||||
}
|
||||
TAPM_LOCAL_CLUSTER_NODE() {
|
||||
printf 'pve1\n'
|
||||
}
|
||||
TAPM_UPDATE_REMOTE_NODE() {
|
||||
printf 'remote:%s\n' "$1" >>"$update_log"
|
||||
}
|
||||
INSTALL_LOCAL_UPDATES() {
|
||||
printf 'local:pve1\n' >>"$update_log"
|
||||
}
|
||||
if INSTALL_CLUSTER_UPDATES >/dev/null; then
|
||||
printf 'FAIL: cluster update ignored an offline node\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
assert_equal $'remote:pve2\nlocal:pve1' \
|
||||
"$(cat "$update_log")" \
|
||||
"online remotes and initiating node update once"
|
||||
|
||||
TAPM_CLUSTER_NODE_ROWS() {
|
||||
printf 'pve1\tonline\npve2\tonline\n'
|
||||
}
|
||||
TAPM_UPDATE_REMOTE_NODE() {
|
||||
printf 'remote update output\n'
|
||||
}
|
||||
INSTALL_LOCAL_UPDATES() {
|
||||
printf 'local update output\n'
|
||||
}
|
||||
expected_output=$'Updating TA-ProxMenu across 2 cluster node(s)...\n\nUpdating remote node pve2...\nremote update output\nRemote node pve2 is updated.\n\nUpdating local node pve1...\nlocal update output\nLocal node pve1 is updated.\n\nTA-ProxMenu is updated on all 2 cluster node(s).'
|
||||
assert_equal "$expected_output" \
|
||||
"$(INSTALL_CLUSTER_UPDATES)" \
|
||||
"each node update is grouped with blank lines only between node blocks"
|
||||
|
||||
finish_tests
|
||||
@@ -0,0 +1,47 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
TEST_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_DIR="$(cd "${TEST_DIR}/.." && pwd)"
|
||||
|
||||
source "${REPO_DIR}/inc/cpu-compat.inc"
|
||||
|
||||
assert_equal() {
|
||||
local expected="${1:?expected value is required}"
|
||||
local actual="${2:?actual value is required}"
|
||||
local description="${3:?description is required}"
|
||||
|
||||
if [[ "$actual" != "$expected" ]]; then
|
||||
printf 'FAIL: %s\nExpected: %s\nActual: %s\n' \
|
||||
"$description" "$expected" "$actual" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
assert_equal \
|
||||
"Broadwell-EP" \
|
||||
"$(TAPM_CPU_GENERATION_FROM_NAME "Intel(R) Xeon(R) CPU E5-2690 v4 @ 2.60GHz")" \
|
||||
"identifies Xeon E5 v4"
|
||||
|
||||
assert_equal \
|
||||
"3rd Gen Xeon Scalable (Ice Lake)" \
|
||||
"$(TAPM_CPU_GENERATION_FROM_NAME "Intel(R) Xeon(R) Gold 6338 CPU @ 2.00GHz")" \
|
||||
"identifies third-generation Xeon Scalable"
|
||||
|
||||
assert_equal \
|
||||
"3rd Gen EPYC (Milan / Zen 3)" \
|
||||
"$(TAPM_CPU_GENERATION_FROM_NAME "AMD EPYC 7543 32-Core Processor")" \
|
||||
"identifies EPYC Milan"
|
||||
|
||||
proxclmc_fixture='node-a | 10.0.0.1 | x86-64-v4
|
||||
node-b | 10.0.0.2 | x86-64-v3
|
||||
|
||||
Cluster CPU type: x86-64-v3'
|
||||
|
||||
expected_rows=$'node-a\03410.0.0.1\034x86-64-v4\nnode-b\03410.0.0.2\034x86-64-v3'
|
||||
actual_rows="$(printf '%s\n' "$proxclmc_fixture" | TAPM_PROXCLMC_HOST_ROWS)"
|
||||
|
||||
assert_equal "$expected_rows" "$actual_rows" "parses ProxCLMC host rows"
|
||||
|
||||
printf 'CPU compatibility tests passed.\n'
|
||||
Executable
+101
@@ -0,0 +1,101 @@
|
||||
#!/usr/bin/env bash
|
||||
set -u -o pipefail
|
||||
|
||||
TEST_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
source "${TEST_ROOT}/tests/testlib.sh"
|
||||
source "${TEST_ROOT}/inc/evacuate-proxmox-node.sh"
|
||||
|
||||
test_stopped_ha_guest_does_not_block_wait() {
|
||||
LOCAL_NODE=node1
|
||||
HA_WAIT_SECONDS=0
|
||||
get_local_guests() {
|
||||
printf '%s\n' \
|
||||
$'100\x1fqemu\x1fstopped\x1fshutdown-ha-vm\x1f1024' \
|
||||
$'101\x1fqemu\x1frunning\x1fnon-ha-vm\x1f1024'
|
||||
}
|
||||
get_ha_guest_ids() {
|
||||
printf '%s\n' 100
|
||||
}
|
||||
wait_for_ha_evacuation
|
||||
}
|
||||
|
||||
test_running_ha_guest_blocks_wait() {
|
||||
LOCAL_NODE=node1
|
||||
HA_WAIT_SECONDS=0
|
||||
get_local_guests() {
|
||||
printf '%s\n' $'100\x1fqemu\x1frunning\x1frunning-ha-vm\x1f1024'
|
||||
}
|
||||
get_ha_guest_ids() {
|
||||
printf '%s\n' 100
|
||||
}
|
||||
wait_for_ha_evacuation
|
||||
}
|
||||
|
||||
assert_success "stopped HA guest does not block evacuation wait" \
|
||||
test_stopped_ha_guest_does_not_block_wait
|
||||
assert_failure "running HA guest still blocks evacuation wait" \
|
||||
test_running_ha_guest_blocks_wait
|
||||
|
||||
set_routing_fixture() {
|
||||
MIGRATION_NODES=(node2 node3)
|
||||
NODE_STORAGE_CACHE=()
|
||||
NODE_STORAGE_CACHE[node2]='shared-a,shared-b'
|
||||
NODE_STORAGE_CACHE[node3]='shared-a,shared-c'
|
||||
PREFERRED_TARGET=node2
|
||||
}
|
||||
|
||||
set_routing_fixture
|
||||
assert_success "preferred destination is eligible" \
|
||||
choose_destination shared-a '' 0
|
||||
assert_equal node2 "$CHOSEN_DESTINATION" "preferred destination selected"
|
||||
|
||||
set_routing_fixture
|
||||
assert_success "affinity fallback is available" \
|
||||
choose_destination shared-a node3 1
|
||||
assert_equal node3 "$CHOSEN_DESTINATION" "affinity destination selected"
|
||||
assert_equal "routed to satisfy HA node-affinity preference" "$CHOSEN_NOTE" \
|
||||
"affinity routing explanation"
|
||||
|
||||
set_routing_fixture
|
||||
assert_success "storage fallback is available" \
|
||||
choose_destination shared-c '' 0
|
||||
assert_equal node3 "$CHOSEN_DESTINATION" "storage-compatible destination selected"
|
||||
|
||||
set_routing_fixture
|
||||
assert_failure "strict affinity blocks two noncompliant destinations" \
|
||||
choose_destination shared-a node4 1
|
||||
|
||||
set_routing_fixture
|
||||
MIGRATION_NODES=(node2)
|
||||
assert_success "sole eligible node overrides affinity" \
|
||||
choose_destination shared-a node4 1
|
||||
assert_equal node2 "$CHOSEN_DESTINATION" "sole eligible destination selected"
|
||||
assert_equal "only eligible node; HA node-affinity preference overridden" \
|
||||
"$CHOSEN_NOTE" "sole-node override explanation"
|
||||
|
||||
TEST_RULES_FILE="$(mktemp /tmp/tapm-ha-rules.XXXXXX)"
|
||||
cleanup_evacuation_tests() {
|
||||
if [[ "$TEST_RULES_FILE" == /tmp/tapm-ha-rules.* ]]; then
|
||||
rm -f -- "$TEST_RULES_FILE"
|
||||
fi
|
||||
}
|
||||
trap cleanup_evacuation_tests EXIT
|
||||
|
||||
printf '%s\n' \
|
||||
'node-affinity: preferred-nodes' \
|
||||
' resources vm:210,ct:211' \
|
||||
' nodes node3:20,node2:10' \
|
||||
' strict 1' >"$TEST_RULES_FILE"
|
||||
HA_RULES_FILE="$TEST_RULES_FILE"
|
||||
|
||||
assert_equal $'preferred-nodes\x1f1\x1fnode3,node2' \
|
||||
"$(get_guest_node_affinity qemu 210)" \
|
||||
"QEMU affinity parsing"
|
||||
assert_equal $'preferred-nodes\x1f1\x1fnode3,node2' \
|
||||
"$(get_guest_node_affinity lxc 211)" \
|
||||
"LXC affinity parsing"
|
||||
assert_equal $'none\x1f0\x1f' \
|
||||
"$(get_guest_node_affinity qemu 999)" \
|
||||
"guest without affinity"
|
||||
|
||||
finish_tests
|
||||
@@ -0,0 +1,148 @@
|
||||
#!/usr/bin/env bash
|
||||
set -u -o pipefail
|
||||
|
||||
TEST_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
test_dir="$(mktemp -d)"
|
||||
trap 'rm -rf "$test_dir"' EXIT
|
||||
|
||||
TAPM_FLEET_STATE_DIR="${test_dir}/state"
|
||||
TAPM_FLEET_IDENTITY_FILE="${TAPM_FLEET_STATE_DIR}/identity.env"
|
||||
TAPM_BROKER_URL='https://tapm.example.com'
|
||||
source "${TEST_ROOT}/inc/fleet.inc"
|
||||
|
||||
TAPM_FLEET_ENSURE_IDENTITY
|
||||
if ! TAPM_FLEET_VALID_IDENTITY; then
|
||||
printf 'FAIL: generated fleet identity is invalid\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
if stat -c '%a' "$TAPM_FLEET_IDENTITY_FILE" >/dev/null 2>&1; then
|
||||
identity_mode="$(stat -c '%a' "$TAPM_FLEET_IDENTITY_FILE")"
|
||||
else
|
||||
identity_mode="$(stat -f '%Lp' "$TAPM_FLEET_IDENTITY_FILE")"
|
||||
fi
|
||||
if [[ "$identity_mode" != 600 ]]; then
|
||||
printf 'FAIL: fleet identity mode is not 600\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
TAPM_FLEET_VERSION='2026.7.26-7'
|
||||
TAPM_FLEET_HOSTNAME='pve01'
|
||||
TAPM_FLEET_GIT_COMMIT='0123456789abcdef0123456789abcdef01234567'
|
||||
TAPM_FLEET_PVE_VERSION='pve-manager/9.0.3/abc~1'
|
||||
TAPM_FLEET_OS_VERSION='Debian GNU/Linux 13 (trixie)'
|
||||
TAPM_FLEET_KERNEL_VERSION='6.14.11-2-pve'
|
||||
TAPM_FLEET_ARCHITECTURE='amd64'
|
||||
TAPM_FLEET_CLUSTERED=true
|
||||
|
||||
registration_json="$(
|
||||
TAPM_FLEET_INCLUDE_CREDENTIAL=1 TAPM_FLEET_JSON installed success
|
||||
)"
|
||||
REGISTRATION_JSON="$registration_json" python3 -c '
|
||||
import json, os
|
||||
payload = json.loads(os.environ["REGISTRATION_JSON"])
|
||||
assert payload["installation_id"]
|
||||
assert payload["hostname"] == "pve01"
|
||||
assert len(payload["credential"]) == 64
|
||||
assert payload["proxmenu_version"] == "2026.7.26-7"
|
||||
assert payload["clustered"] is True
|
||||
'
|
||||
|
||||
printf '0\n' >"${test_dir}/curl-count"
|
||||
curl_should_fail=0
|
||||
curl() {
|
||||
local count data_path='' previous='' argument=''
|
||||
count="$(cat "${test_dir}/curl-count")"
|
||||
count="$((count + 1))"
|
||||
printf '%s\n' "$count" >"${test_dir}/curl-count"
|
||||
for argument in "$@"; do
|
||||
if [[ "$previous" == '--data-binary' ]]; then
|
||||
data_path="${argument#@}"
|
||||
fi
|
||||
previous="$argument"
|
||||
done
|
||||
if [[ -n "$data_path" && "$data_path" != '-' ]]; then
|
||||
cp "$data_path" "${test_dir}/curl-body-${count}.json"
|
||||
fi
|
||||
cat >"${test_dir}/curl-config-${count}"
|
||||
(( curl_should_fail == 0 ))
|
||||
}
|
||||
|
||||
TAPM_FLEET_REGISTERED=1
|
||||
TAPM_FLEET_EVENT_SEND run_completed success '' 12
|
||||
EVENT_BODY="$(cat "${test_dir}/curl-body-1.json")" \
|
||||
EVENT_CONFIG="$(cat "${test_dir}/curl-config-1")" \
|
||||
EXPECTED_CREDENTIAL="$TAPM_FLEET_CREDENTIAL" \
|
||||
python3 -c '
|
||||
import json, os
|
||||
payload = json.loads(os.environ["EVENT_BODY"])
|
||||
assert payload["event"] == "run_completed"
|
||||
assert payload["hostname"] == "pve01"
|
||||
assert payload["result"] == "success"
|
||||
assert payload["duration_seconds"] == 12
|
||||
assert "credential" not in payload
|
||||
config = os.environ["EVENT_CONFIG"]
|
||||
assert "Authorization: Bearer " + os.environ["EXPECTED_CREDENTIAL"] in config
|
||||
assert "https://tapm.example.com/api/v1/hosts/events" in config
|
||||
'
|
||||
|
||||
if compgen -G "${TMPDIR:-/tmp}/tapm-fleet-event.*" >/dev/null; then
|
||||
printf 'FAIL: fleet event left a temporary payload file\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf '0\n' >"${test_dir}/curl-count"
|
||||
TAPM_FLEET_ENROLLED=0
|
||||
TAPM_FLEET_REGISTERED=0
|
||||
TAPM_FLEET_LAST_VERSION=''
|
||||
TAPM_FLEET_COLLECT_METADATA() {
|
||||
TAPM_FLEET_HOSTNAME='pve01'
|
||||
TAPM_FLEET_GIT_COMMIT='0123456789abcdef0123456789abcdef01234567'
|
||||
TAPM_FLEET_PVE_VERSION='pve-manager/9.2.4'
|
||||
TAPM_FLEET_OS_VERSION='Debian GNU/Linux 13 (trixie)'
|
||||
TAPM_FLEET_KERNEL_VERSION='6.14.11-5-pve'
|
||||
TAPM_FLEET_ARCHITECTURE='amd64'
|
||||
TAPM_FLEET_CLUSTERED=true
|
||||
}
|
||||
|
||||
TAPM_FLEET_START '2026.7.28-9'
|
||||
TAPM_FLEET_FINISH 0
|
||||
if [[ "$(cat "${test_dir}/curl-count")" != 2 ]]; then
|
||||
printf 'FAIL: initial run did not make exactly registration + completion calls\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
TAPM_FLEET_LOAD_IDENTITY
|
||||
if [[ "$TAPM_FLEET_ENROLLED" != 1 ||
|
||||
"$TAPM_FLEET_LAST_VERSION" != '2026.7.28-9' ]]; then
|
||||
printf 'FAIL: successful fleet enrollment was not persisted\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
TAPM_FLEET_REGISTERED=0
|
||||
TAPM_FLEET_START '2026.7.28-9'
|
||||
TAPM_FLEET_FINISH 0
|
||||
if [[ "$(cat "${test_dir}/curl-count")" != 3 ]]; then
|
||||
printf 'FAIL: normal run made more than one broker call\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
curl_should_fail=1
|
||||
TAPM_FLEET_REGISTERED=0
|
||||
TAPM_FLEET_START '2026.7.28-9'
|
||||
TAPM_FLEET_FINISH 0
|
||||
TAPM_FLEET_LOAD_IDENTITY
|
||||
if [[ "$TAPM_FLEET_ENROLLED" != 0 ]]; then
|
||||
printf 'FAIL: failed fleet event did not schedule next-run re-enrollment\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
curl_should_fail=0
|
||||
TAPM_FLEET_REGISTERED=0
|
||||
TAPM_FLEET_START '2026.7.28-9'
|
||||
TAPM_FLEET_FINISH 0
|
||||
TAPM_FLEET_LOAD_IDENTITY
|
||||
if [[ "$TAPM_FLEET_ENROLLED" != 1 ]]; then
|
||||
printf 'FAIL: fleet client did not recover enrollment after a failed event\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf 'PASS: fleet identity and event client\n'
|
||||
Executable
+82
@@ -0,0 +1,82 @@
|
||||
#!/usr/bin/env bash
|
||||
set -u -o pipefail
|
||||
|
||||
TEST_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
source "${TEST_ROOT}/tests/testlib.sh"
|
||||
source "${TEST_ROOT}/inc/git-update.inc"
|
||||
|
||||
assert_success "automatic Git check cache is 24 hours" \
|
||||
grep -q '^UPDATE_CACHE_SECONDS=86400$' "${TEST_ROOT}/proxmenu-scripts.sh"
|
||||
|
||||
TEST_TEMP_DIR="$(mktemp -d /tmp/tapm-git-tests.XXXXXX)"
|
||||
TEST_REPOSITORY="${TEST_TEMP_DIR}/repository"
|
||||
|
||||
cleanup_git_tests() {
|
||||
if [[ "$TEST_TEMP_DIR" == /tmp/tapm-git-tests.* && -d "$TEST_TEMP_DIR" ]]; then
|
||||
rm -rf -- "$TEST_TEMP_DIR"
|
||||
fi
|
||||
}
|
||||
trap cleanup_git_tests EXIT
|
||||
|
||||
git init -q -b main "$TEST_REPOSITORY"
|
||||
git -C "$TEST_REPOSITORY" config user.name 'TA-ProxMenu Tests'
|
||||
git -C "$TEST_REPOSITORY" config user.email 'tapm-tests@example.invalid'
|
||||
|
||||
printf 'first\n' >"${TEST_REPOSITORY}/fixture.txt"
|
||||
git -C "$TEST_REPOSITORY" add fixture.txt
|
||||
git -C "$TEST_REPOSITORY" commit -q -m first
|
||||
commit_a="$(git -C "$TEST_REPOSITORY" rev-parse HEAD)"
|
||||
|
||||
printf 'second\n' >>"${TEST_REPOSITORY}/fixture.txt"
|
||||
git -C "$TEST_REPOSITORY" commit -q -am second
|
||||
commit_b="$(git -C "$TEST_REPOSITORY" rev-parse HEAD)"
|
||||
|
||||
git -C "$TEST_REPOSITORY" switch -q --detach "$commit_a"
|
||||
printf 'alternate\n' >"${TEST_REPOSITORY}/alternate.txt"
|
||||
git -C "$TEST_REPOSITORY" add alternate.txt
|
||||
git -C "$TEST_REPOSITORY" commit -q -m alternate
|
||||
commit_c="$(git -C "$TEST_REPOSITORY" rev-parse HEAD)"
|
||||
|
||||
git -C "$TEST_REPOSITORY" update-ref refs/tapm/a "$commit_a"
|
||||
git -C "$TEST_REPOSITORY" update-ref refs/tapm/b "$commit_b"
|
||||
git -C "$TEST_REPOSITORY" update-ref refs/tapm/c "$commit_c"
|
||||
|
||||
assert_equal current \
|
||||
"$(TAPM_GIT_RELATION "$TEST_REPOSITORY" refs/tapm/a refs/tapm/a)" \
|
||||
"equal commits"
|
||||
assert_equal behind \
|
||||
"$(TAPM_GIT_RELATION "$TEST_REPOSITORY" refs/tapm/a refs/tapm/b)" \
|
||||
"local commit behind remote"
|
||||
assert_equal ahead \
|
||||
"$(TAPM_GIT_RELATION "$TEST_REPOSITORY" refs/tapm/b refs/tapm/a)" \
|
||||
"local commit ahead of remote"
|
||||
assert_equal diverged \
|
||||
"$(TAPM_GIT_RELATION "$TEST_REPOSITORY" refs/tapm/b refs/tapm/c)" \
|
||||
"diverged commits"
|
||||
|
||||
git -C "$TEST_REPOSITORY" switch -q -C main "$commit_a"
|
||||
git -C "$TEST_REPOSITORY" update-ref refs/remotes/origin/main "$commit_a"
|
||||
assert_equal current \
|
||||
"$(TAPM_GIT_BRANCH_STATE "$TEST_REPOSITORY" main)" \
|
||||
"clean current branch"
|
||||
|
||||
printf 'untracked\n' >"${TEST_REPOSITORY}/untracked.txt"
|
||||
assert_equal dirty \
|
||||
"$(TAPM_GIT_BRANCH_STATE "$TEST_REPOSITORY" main)" \
|
||||
"dirty working tree"
|
||||
rm -f -- "${TEST_REPOSITORY}/untracked.txt"
|
||||
|
||||
git -C "$TEST_REPOSITORY" switch -q -c feature
|
||||
assert_equal wrong-branch \
|
||||
"$(TAPM_GIT_BRANCH_STATE "$TEST_REPOSITORY" main)" \
|
||||
"wrong checked-out branch"
|
||||
|
||||
git -C "$TEST_REPOSITORY" switch -q main
|
||||
git -C "$TEST_REPOSITORY" update-ref refs/remotes/origin/main "$commit_b"
|
||||
assert_success "fast-forward merge" \
|
||||
TAPM_GIT_FAST_FORWARD "$TEST_REPOSITORY" main
|
||||
assert_equal "$commit_b" \
|
||||
"$(git -C "$TEST_REPOSITORY" rev-parse HEAD)" \
|
||||
"fast-forward destination"
|
||||
|
||||
finish_tests
|
||||
@@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env bash
|
||||
set -u -o pipefail
|
||||
|
||||
TEST_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
source "${TEST_ROOT}/tests/testlib.sh"
|
||||
source "${TEST_ROOT}/inc/ha-status.inc"
|
||||
|
||||
STATUS_FILE="$(mktemp /tmp/tapm-ha-status.XXXXXX)"
|
||||
cleanup_ha_status_test() {
|
||||
[[ "$STATUS_FILE" == /tmp/tapm-ha-status.* ]] && rm -f -- "$STATUS_FILE"
|
||||
}
|
||||
trap cleanup_ha_status_test EXIT
|
||||
|
||||
printf '%s\n' \
|
||||
'{"node_status":{"pve1":"online","pve2":"maintenance mode"}}' \
|
||||
>"$STATUS_FILE"
|
||||
assert_success "maintenance node detected in HA manager state" \
|
||||
TAPM_HA_NODE_IN_MAINTENANCE pve2 "$STATUS_FILE"
|
||||
assert_failure "active node is not reported as maintenance" \
|
||||
TAPM_HA_NODE_IN_MAINTENANCE pve1 "$STATUS_FILE"
|
||||
|
||||
printf '%s\n' \
|
||||
'{"nodes":[{"node":"pve3","state":"maintenance"}]}' \
|
||||
>"$STATUS_FILE"
|
||||
assert_success "structured HA node state detected" \
|
||||
TAPM_HA_NODE_IN_MAINTENANCE pve3 "$STATUS_FILE"
|
||||
|
||||
printf '%s\n' 'not-json' >"$STATUS_FILE"
|
||||
assert_failure "malformed HA state is rejected" \
|
||||
TAPM_HA_NODE_IN_MAINTENANCE pve1 "$STATUS_FILE"
|
||||
|
||||
finish_tests
|
||||
@@ -0,0 +1,44 @@
|
||||
#!/usr/bin/env bash
|
||||
set -u -o pipefail
|
||||
|
||||
TEST_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
test_dir="$(mktemp -d)"
|
||||
trap 'rm -rf "$test_dir"' EXIT
|
||||
|
||||
source "${TEST_ROOT}/tests/testlib.sh"
|
||||
source "${TEST_ROOT}/inc/header-info.inc"
|
||||
|
||||
pveversion() {
|
||||
printf '%s\n' 'pve-manager/9.2.4/5e5ae681198514d4 (running kernel: 7.0.14-5-pve)'
|
||||
}
|
||||
|
||||
cat >"${test_dir}/corosync.conf" <<'EOF'
|
||||
totem {
|
||||
version: 2
|
||||
cluster_name: production-cluster
|
||||
}
|
||||
EOF
|
||||
|
||||
TAPM_LOAD_HEADER_INFO "${test_dir}/corosync.conf"
|
||||
assert_equal '9.2.4' "$TAPM_HEADER_PVE_VERSION" \
|
||||
"header extracts the concise Proxmox VE version"
|
||||
assert_equal 'production-cluster' "$TAPM_HEADER_CLUSTER" \
|
||||
"header reads the local cluster name"
|
||||
|
||||
pveversion() {
|
||||
printf '%s\n' 'pve-manager/10.0.0/changed'
|
||||
}
|
||||
TAPM_LOAD_HEADER_INFO "${test_dir}/corosync.conf"
|
||||
assert_equal '9.2.4' "$TAPM_HEADER_PVE_VERSION" \
|
||||
"header information is cached between menu redraws"
|
||||
|
||||
TAPM_HEADER_INFO_LOADED=0
|
||||
TAPM_HEADER_PVE_VERSION='Unavailable'
|
||||
TAPM_HEADER_CLUSTER='Standalone'
|
||||
TAPM_LOAD_HEADER_INFO "${test_dir}/missing.conf"
|
||||
assert_equal '10.0.0' "$TAPM_HEADER_PVE_VERSION" \
|
||||
"header refresh reads the current Proxmox VE version"
|
||||
assert_equal 'Standalone' "$TAPM_HEADER_CLUSTER" \
|
||||
"host without corosync configuration is shown as standalone"
|
||||
|
||||
finish_tests
|
||||
Executable
+75
@@ -0,0 +1,75 @@
|
||||
#!/usr/bin/env bash
|
||||
set -u -o pipefail
|
||||
|
||||
TEST_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
source "${TEST_ROOT}/tests/testlib.sh"
|
||||
source "${TEST_ROOT}/inc/deploy-iso-nfs-lxc.sh"
|
||||
|
||||
assert_success "LXC creation uses the compatible cpuunits option" \
|
||||
grep -q -- '--cpuunits 100' "${TEST_ROOT}/inc/deploy-iso-nfs-lxc.sh"
|
||||
assert_failure "misspelled cpunits option is absent" \
|
||||
grep -q -- '--cpunits' "${TEST_ROOT}/inc/deploy-iso-nfs-lxc.sh"
|
||||
|
||||
assert_success "valid storage ID" TAPM_ISO_NFS_VALID_ID PVE-Shared-Storage
|
||||
assert_failure "storage ID cannot start with a number" TAPM_ISO_NFS_VALID_ID 1-storage
|
||||
assert_failure "storage ID rejects spaces" TAPM_ISO_NFS_VALID_ID 'shared storage'
|
||||
|
||||
assert_success "valid CTID" TAPM_ISO_NFS_VALID_CTID 210
|
||||
assert_failure "reserved two-digit CTID" TAPM_ISO_NFS_VALID_CTID 99
|
||||
assert_failure "CTID rejects text" TAPM_ISO_NFS_VALID_CTID ct210
|
||||
|
||||
assert_success "valid hostname" TAPM_ISO_NFS_VALID_HOSTNAME PVE-Shared-Storage
|
||||
assert_success "valid FQDN hostname" TAPM_ISO_NFS_VALID_HOSTNAME iso-nfs.example.net
|
||||
assert_failure "hostname rejects spaces" TAPM_ISO_NFS_VALID_HOSTNAME 'ISO Storage'
|
||||
|
||||
assert_success "valid IPv4 CIDR" TAPM_ISO_NFS_VALID_IPV4_CIDR 10.10.2.45/16
|
||||
assert_failure "IPv4 octet out of range" TAPM_ISO_NFS_VALID_IPV4_CIDR 10.10.2.256/24
|
||||
assert_failure "IPv4 prefix out of range" TAPM_ISO_NFS_VALID_IPV4_CIDR 10.10.2.45/33
|
||||
assert_failure "IPv4 prefix required" TAPM_ISO_NFS_VALID_IPV4_CIDR 10.10.2.45
|
||||
|
||||
pvesm() {
|
||||
[[ "${1:-}" == status ]] || return 1
|
||||
printf '%s\n' \
|
||||
'Name Type Status Total Used Available %' \
|
||||
'local-lvm lvmthin active 100 10 90 10%' \
|
||||
'iSCSI-Datastore1 lvm active 200 20 180 10%' \
|
||||
'iSCSI-Datastore1-2 lvm active 200 20 180 10%' \
|
||||
'offline-store dir inactive 100 0 100 0%'
|
||||
}
|
||||
|
||||
SELECT_MENU() {
|
||||
local title="$1"
|
||||
|
||||
assert_equal "Root filesystem storage" "$title" "storage menu title"
|
||||
assert_equal \
|
||||
'iSCSI-Datastore1-2 (lvm) — default' \
|
||||
"${labels[0]}" \
|
||||
"default storage placed first"
|
||||
assert_equal 3 "${#labels[@]}" "only active storages offered"
|
||||
MENU_SELECTION="${values[0]}"
|
||||
}
|
||||
|
||||
test_default_storage_selection() {
|
||||
local selected_storage=''
|
||||
|
||||
TAPM_ISO_NFS_SELECT_STORAGE selected_storage \
|
||||
"Root filesystem storage" "iSCSI-Datastore1-2" || return 1
|
||||
assert_equal iSCSI-Datastore1-2 "$selected_storage" \
|
||||
"pressing Enter retains default storage"
|
||||
}
|
||||
|
||||
assert_success "default storage menu selection" test_default_storage_selection
|
||||
|
||||
pveam() {
|
||||
[[ "${1:-}" == list && "${2:-}" == local ]] || return 1
|
||||
printf '%s\n' \
|
||||
'NAME VOLID FORMAT TYPE SIZE VMID' \
|
||||
'local:vztmpl/debian-12-standard_12.7-1_amd64.tar.zst 0 0 0 0 0' \
|
||||
'local:vztmpl/debian-13-standard_13.1-2_amd64.tar.zst 0 0 0 0 0'
|
||||
}
|
||||
assert_equal \
|
||||
'local:vztmpl/debian-13-standard_13.1-2_amd64.tar.zst' \
|
||||
"$(TAPM_ISO_NFS_LOCAL_TEMPLATE local)" \
|
||||
"newest local Debian template avoids catalog refresh"
|
||||
|
||||
finish_tests
|
||||
@@ -0,0 +1,173 @@
|
||||
#!/usr/bin/env bash
|
||||
set -u -o pipefail
|
||||
|
||||
TEST_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
source "${TEST_ROOT}/tests/testlib.sh"
|
||||
source "${TEST_ROOT}/inc/post-install.inc"
|
||||
|
||||
assert_equal 9 \
|
||||
"$(printf '%s\n' 'pve-manager/9.0.3/0255bb4e9600f70c (running kernel: 6.14.8-2-pve)' |
|
||||
sed -n 's/.*pve-manager\/\([0-9][0-9]*\).*/\1/p')" \
|
||||
"Proxmox major version extraction"
|
||||
|
||||
POST_TEST_ROOT="$(mktemp -d /tmp/tapm-post-install.XXXXXX)"
|
||||
TAPM_HOST_ROOT="$POST_TEST_ROOT"
|
||||
|
||||
cleanup_post_install_tests() {
|
||||
if [[ "$POST_TEST_ROOT" == /tmp/tapm-post-install.* &&
|
||||
-d "$POST_TEST_ROOT" ]]; then
|
||||
rm -rf -- "$POST_TEST_ROOT"
|
||||
fi
|
||||
}
|
||||
trap cleanup_post_install_tests EXIT
|
||||
|
||||
mkdir -p \
|
||||
"${POST_TEST_ROOT}/etc/apt/apt.conf.d" \
|
||||
"${POST_TEST_ROOT}/etc/systemd" \
|
||||
"${POST_TEST_ROOT}/etc/logrotate.d" \
|
||||
"${POST_TEST_ROOT}/etc/sysctl.d" \
|
||||
"${POST_TEST_ROOT}/etc/network" \
|
||||
"${POST_TEST_ROOT}/etc" \
|
||||
"${POST_TEST_ROOT}/bin"
|
||||
|
||||
printf 'Acquire::Languages "none";\n' \
|
||||
>"${POST_TEST_ROOT}/etc/apt/apt.conf.d/99-disable-translations"
|
||||
assert_success "ProxMenux APT language setting recognized" \
|
||||
TAPM_POST_EXACT_APT_LANGUAGES
|
||||
|
||||
printf 'Acquire::Languages "en";\n' \
|
||||
>"${POST_TEST_ROOT}/etc/apt/apt.conf.d/99-disable-translations"
|
||||
assert_failure "custom APT language setting preserved" \
|
||||
TAPM_POST_EXACT_APT_LANGUAGES
|
||||
|
||||
cat >"${POST_TEST_ROOT}/etc/systemd/journald.conf" <<'EOF'
|
||||
[Journal]
|
||||
Storage=persistent
|
||||
Seal=no
|
||||
SystemMaxUse=64M
|
||||
RuntimeMaxUse=60M
|
||||
MaxLevelStore=info
|
||||
EOF
|
||||
assert_success "ProxMenux journald replacement recognized" \
|
||||
TAPM_POST_PROXMENUX_JOURNALD
|
||||
|
||||
cat >"${POST_TEST_ROOT}/etc/logrotate.conf" <<'EOF'
|
||||
# ProxMenux optimized configuration (Log2RAM-friendly)
|
||||
daily
|
||||
rotate 7
|
||||
size 10M
|
||||
copytruncate
|
||||
include /etc/logrotate.d
|
||||
EOF
|
||||
assert_success "ProxMenux logrotate replacement recognized" \
|
||||
TAPM_POST_PROXMENUX_LOGROTATE
|
||||
|
||||
cat >"${POST_TEST_ROOT}/bin/gzip" <<'EOF'
|
||||
#!/bin/sh
|
||||
GZIP="-1"
|
||||
exec /usr/bin/pigz "$@"
|
||||
EOF
|
||||
cat >"${POST_TEST_ROOT}/bin/gzip.original" <<'EOF'
|
||||
#!/bin/sh
|
||||
if [ "${1:-}" = "--version" ]; then
|
||||
printf 'gzip 1.13\n'
|
||||
else
|
||||
cat
|
||||
fi
|
||||
EOF
|
||||
cp "${POST_TEST_ROOT}/bin/gzip" "${POST_TEST_ROOT}/bin/pigzwrapper"
|
||||
chmod 0755 \
|
||||
"${POST_TEST_ROOT}/bin/gzip" \
|
||||
"${POST_TEST_ROOT}/bin/gzip.original" \
|
||||
"${POST_TEST_ROOT}/bin/pigzwrapper"
|
||||
assert_success "ProxMenux gzip wrapper recognized" \
|
||||
TAPM_POST_PROXMENUX_GZIP_WRAPPER
|
||||
assert_success "ProxMenux gzip wrapper repaired from valid original" \
|
||||
TAPM_POST_REPAIR_GZIP
|
||||
assert_failure "repaired gzip is no longer recognized as wrapper" \
|
||||
TAPM_POST_PROXMENUX_GZIP_WRAPPER
|
||||
assert_failure "obsolete gzip original removed after verification" \
|
||||
test -e "${POST_TEST_ROOT}/bin/gzip.original"
|
||||
|
||||
assert_success "kernel panic profile written" TAPM_POST_CONFIGURE_PANIC
|
||||
assert_success "inotify profile written" TAPM_POST_CONFIGURE_LIMITS
|
||||
assert_success "memory profile written" TAPM_POST_CONFIGURE_MEMORY
|
||||
assert_success "network profile written" TAPM_POST_CONFIGURE_NETWORK
|
||||
assert_success "BBR profile written" TAPM_POST_CONFIGURE_BBR
|
||||
assert_equal 30 \
|
||||
"$(awk '/kernel.panic =/ {print $3}' \
|
||||
"${POST_TEST_ROOT}/etc/sysctl.d/99-ta-proxmenu-kernel-panic.conf")" \
|
||||
"kernel panic delay"
|
||||
assert_equal 524288 \
|
||||
"$(awk '/max_user_watches/ {print $3}' \
|
||||
"${POST_TEST_ROOT}/etc/sysctl.d/99-ta-proxmenu-limits.conf")" \
|
||||
"inotify watch limit"
|
||||
assert_equal 10 \
|
||||
"$(awk '/vm.swappiness/ {print $3}' \
|
||||
"${POST_TEST_ROOT}/etc/sysctl.d/99-ta-proxmenu-memory.conf")" \
|
||||
"host swappiness"
|
||||
|
||||
cat >"${POST_TEST_ROOT}/etc/vzdump.conf" <<'EOF'
|
||||
# existing setting
|
||||
bwlimit: 50000
|
||||
pigz: 4
|
||||
pigz: 8
|
||||
EOF
|
||||
assert_success "vzdump key replacement" \
|
||||
TAPM_POST_SET_COLON_KEY /etc/vzdump.conf pigz 1
|
||||
assert_equal 1 \
|
||||
"$(awk -F: '/^pigz:/ {gsub(/[[:space:]]/, "", $2); print $2}' \
|
||||
"${POST_TEST_ROOT}/etc/vzdump.conf")" \
|
||||
"vzdump pigz setting is unique"
|
||||
assert_success "vzdump key removal" \
|
||||
TAPM_POST_REMOVE_COLON_KEY /etc/vzdump.conf bwlimit
|
||||
assert_failure "vzdump bandwidth setting removed" \
|
||||
grep -q '^bwlimit:' "${POST_TEST_ROOT}/etc/vzdump.conf"
|
||||
|
||||
printf 'deb should-be-backed-up\n' \
|
||||
>"${POST_TEST_ROOT}/etc/apt/sources.list"
|
||||
TAPM_POST_BACKUP_BASE='/var/backups/ta-proxmenu/post-install'
|
||||
test_post_backup() {
|
||||
TAPM_POST_BACKUP >/dev/null
|
||||
}
|
||||
assert_success "host configuration backup created" test_post_backup
|
||||
printf 'changed\n' >"${POST_TEST_ROOT}/etc/apt/sources.list"
|
||||
assert_success "host configuration backup restored" \
|
||||
TAPM_POST_RESTORE_BACKUP "$TAPM_POST_LAST_BACKUP"
|
||||
assert_equal 'deb should-be-backed-up' \
|
||||
"$(cat "${POST_TEST_ROOT}/etc/apt/sources.list")" \
|
||||
"restored APT sources"
|
||||
|
||||
assert_success "APT source layout enforced in fixture root" \
|
||||
TAPM_POST_ENSURE_APT_LAYOUT
|
||||
assert_equal 0 \
|
||||
"$(wc -c <"${POST_TEST_ROOT}/etc/apt/sources.list" | tr -d ' ')" \
|
||||
"main APT sources list is empty"
|
||||
|
||||
mkdir -p \
|
||||
"${POST_TEST_ROOT}/etc/security/limits.d" \
|
||||
"${POST_TEST_ROOT}/usr/local/share/proxmenux" \
|
||||
"${POST_TEST_ROOT}/usr/local/bin"
|
||||
cat >"${POST_TEST_ROOT}/etc/sysctl.d/99-network.conf" <<'EOF'
|
||||
# Custom administrator network profile
|
||||
net.ipv4.ip_forward = 1
|
||||
EOF
|
||||
cat >"${POST_TEST_ROOT}/etc/security/limits.d/99-limits.conf" <<'EOF'
|
||||
# ProxMenux configuration
|
||||
* soft nofile 1048576
|
||||
EOF
|
||||
cat >"${POST_TEST_ROOT}/usr/local/bin/menu" <<'EOF'
|
||||
#!/bin/sh
|
||||
exec /usr/local/share/proxmenux/menu.sh
|
||||
EOF
|
||||
assert_success "recognized ProxMenux settings cleaned" \
|
||||
TAPM_POST_CLEAN_PROXMENUX_SETTINGS
|
||||
assert_success "custom similarly named network profile retained" \
|
||||
test -f "${POST_TEST_ROOT}/etc/sysctl.d/99-network.conf"
|
||||
assert_failure "recognized ProxMenux limits profile removed" \
|
||||
test -e "${POST_TEST_ROOT}/etc/security/limits.d/99-limits.conf"
|
||||
assert_success "ProxMenux application removed" TAPM_POST_REMOVE_PROXMENUX_APP
|
||||
assert_failure "ProxMenux menu launcher removed" \
|
||||
test -e "${POST_TEST_ROOT}/usr/local/bin/menu"
|
||||
|
||||
finish_tests
|
||||
Executable
+24
@@ -0,0 +1,24 @@
|
||||
#!/usr/bin/env bash
|
||||
set -u -o pipefail
|
||||
|
||||
TEST_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
source "${TEST_ROOT}/tests/testlib.sh"
|
||||
TAPM_PULSE_STANDALONE_NO_MAIN=1
|
||||
source "${TEST_ROOT}/install-pulse.sh"
|
||||
|
||||
assert_success "default V2 source branch accepted" \
|
||||
TAPM_PULSE_VALID_SOURCE_BRANCH V2
|
||||
assert_success "nested release branch accepted" \
|
||||
TAPM_PULSE_VALID_SOURCE_BRANCH releases/pulse-6
|
||||
assert_failure "empty source branch rejected" \
|
||||
TAPM_PULSE_VALID_SOURCE_BRANCH ''
|
||||
assert_failure "source branch traversal rejected" \
|
||||
TAPM_PULSE_VALID_SOURCE_BRANCH '../main'
|
||||
assert_success "HTTPS source base accepted" \
|
||||
TAPM_PULSE_VALID_SOURCE_BASE \
|
||||
https://tagit.technologyarch.com/TAI/TA-ProxMenu/raw/branch/V2
|
||||
assert_failure "HTTP source base rejected" \
|
||||
TAPM_PULSE_VALID_SOURCE_BASE \
|
||||
http://tagit.technologyarch.com/TAI/TA-ProxMenu/raw/branch/V2
|
||||
|
||||
finish_tests
|
||||
@@ -0,0 +1,217 @@
|
||||
#!/usr/bin/env bash
|
||||
set -u -o pipefail
|
||||
|
||||
TEST_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
source "${TEST_ROOT}/tests/testlib.sh"
|
||||
source "${TEST_ROOT}/inc/deploy-pulse-lxc.sh"
|
||||
|
||||
assert_success "stable Pulse release" TAPM_PULSE_VALID_RELEASE v6.1.1
|
||||
assert_success "prerelease Pulse release" TAPM_PULSE_VALID_RELEASE v6.2.0-rc.1
|
||||
assert_failure "Pulse release requires v prefix" TAPM_PULSE_VALID_RELEASE 6.1.1
|
||||
assert_failure "Pulse release rejects URL content" \
|
||||
TAPM_PULSE_VALID_RELEASE 'v6.1.1/../../latest'
|
||||
|
||||
assert_equal amd64 "$(TAPM_PULSE_ARCH x86_64)" "x86 architecture mapping"
|
||||
assert_equal arm64 "$(TAPM_PULSE_ARCH aarch64)" "ARM architecture mapping"
|
||||
assert_failure "unsupported Pulse architecture" TAPM_PULSE_ARCH riscv64
|
||||
|
||||
assert_equal 2 "$(TAPM_PULSE_BACKOFF_NEXT 1 8)" "Pulse backoff doubles"
|
||||
assert_equal 8 "$(TAPM_PULSE_BACKOFF_NEXT 4 8)" "Pulse backoff reaches cap"
|
||||
assert_equal 8 "$(TAPM_PULSE_BACKOFF_NEXT 8 8)" "Pulse backoff remains capped"
|
||||
|
||||
test_bridge_selection_assignment() {
|
||||
local bridge=''
|
||||
|
||||
TAPM_PULSE_SET_BRIDGE_FROM_SELECTION bridge 'bridge:vmbr0' || return 1
|
||||
assert_equal vmbr0 "$bridge" "selected Pulse bridge assigned to caller"
|
||||
}
|
||||
|
||||
assert_success "Pulse bridge selection assignment" test_bridge_selection_assignment
|
||||
assert_failure "empty Pulse bridge selection rejected" \
|
||||
TAPM_PULSE_SET_BRIDGE_FROM_SELECTION selected_bridge 'bridge:'
|
||||
|
||||
bootstrap_token='0123456789abcdef0123456789abcdef0123456789abcdef'
|
||||
bootstrap_output="║ Token: ${bootstrap_token} ║"
|
||||
assert_equal "$bootstrap_token" \
|
||||
"$(TAPM_PULSE_BOOTSTRAP_TOKEN_FROM_OUTPUT "$bootstrap_output")" \
|
||||
"Pulse bootstrap token parsed"
|
||||
assert_failure "malformed Pulse bootstrap output rejected" \
|
||||
TAPM_PULSE_BOOTSTRAP_TOKEN_FROM_OUTPUT 'Token: not-a-token'
|
||||
|
||||
agent_token='0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'
|
||||
assert_equal "$agent_token" \
|
||||
"$(TAPM_PULSE_TOKEN_FROM_RESPONSE "{\"token\":\"${agent_token}\"}")" \
|
||||
"Pulse agent token parsed"
|
||||
assert_failure "missing Pulse agent token rejected" \
|
||||
TAPM_PULSE_TOKEN_FROM_RESPONSE '{"command":"install"}'
|
||||
assert_failure "Pulse agent token containing whitespace rejected" \
|
||||
TAPM_PULSE_TOKEN_FROM_RESPONSE '{"token":"invalid token"}'
|
||||
assert_failure "non-hex Pulse agent token rejected" \
|
||||
TAPM_PULSE_TOKEN_FROM_RESPONSE '{"token":"not-a-token"}'
|
||||
assert_success "registered Pulse agent response accepted" \
|
||||
TAPM_PULSE_AGENT_REGISTERED_FROM_RESPONSE \
|
||||
'{"agent":{"id":"agent-123","hostname":"pve1"}}'
|
||||
assert_failure "missing Pulse agent ID rejected" \
|
||||
TAPM_PULSE_AGENT_REGISTERED_FROM_RESPONSE '{"agent":{"hostname":"pve1"}}'
|
||||
assert_equal '{"type":"pve","enableCommands":true}' \
|
||||
"$(TAPM_PULSE_AGENT_TOKEN_REQUEST_JSON)" \
|
||||
"Pulse PVE unified-agent enrollment requested with commands enabled"
|
||||
|
||||
setup_token='0123456789abcdef0123456789abcdef'
|
||||
setup_host='https://pve1.example.test:8006'
|
||||
setup_old_url='https://pulse.example.test/api/setup-script?backup_perms=true'
|
||||
setup_response="$(
|
||||
SETUP_TOKEN="$setup_token" SETUP_HOST="$setup_host" SETUP_OLD_URL="$setup_old_url" \
|
||||
python3 -c '
|
||||
import json, os, sys, time
|
||||
old_url = os.environ["SETUP_OLD_URL"]
|
||||
token = os.environ["SETUP_TOKEN"]
|
||||
json.dump({
|
||||
"type": "pve",
|
||||
"host": os.environ["SETUP_HOST"],
|
||||
"url": old_url,
|
||||
"downloadURL": old_url + "&setup_token=" + token,
|
||||
"scriptFileName": "pulse-setup-pve.sh",
|
||||
"command": old_url + " PULSE_SETUP_TOKEN=" + token + " if",
|
||||
"commandWithEnv": old_url + " PULSE_SETUP_TOKEN=" + token + " if",
|
||||
"commandWithoutEnv": old_url + " if",
|
||||
"expires": int(time.time()) + 300,
|
||||
"setupToken": token,
|
||||
"tokenHint": token[:3] + "..." + token[-3:],
|
||||
}, sys.stdout)
|
||||
'
|
||||
)"
|
||||
normalized_setup="$(
|
||||
TAPM_PULSE_NORMALIZE_V611_SETUP_ARTIFACT \
|
||||
"$setup_response" 'http://10.10.2.30:7655'
|
||||
)"
|
||||
assert_equal "$setup_token" \
|
||||
"$(SETUP_RESPONSE="$normalized_setup" python3 -c 'import json, os; print(json.loads(os.environ["SETUP_RESPONSE"])["setupToken"])')" \
|
||||
"Pulse v6.1.1 compatibility preserves server-issued setup token"
|
||||
assert_equal \
|
||||
'http://10.10.2.30:7655/api/setup-script?host=https%3A%2F%2Fpve1.example.test%3A8006&pulse_url=http%3A%2F%2F10.10.2.30%3A7655&type=pve' \
|
||||
"$(SETUP_RESPONSE="$normalized_setup" python3 -c 'import json, os; print(json.loads(os.environ["SETUP_RESPONSE"])["url"])')" \
|
||||
"Pulse v6.1.1 compatibility normalizes setup artifact URL"
|
||||
assert_failure "Pulse v6.1.1 compatibility rejects missing setup token" \
|
||||
TAPM_PULSE_NORMALIZE_V611_SETUP_ARTIFACT \
|
||||
'{"type":"pve","host":"https://pve1:8006"}' 'http://10.10.2.30:7655'
|
||||
|
||||
test_pve_token_output_assignment() {
|
||||
local token_output=''
|
||||
local token_status=99
|
||||
|
||||
pveum() {
|
||||
printf '%s\n' \
|
||||
'{"full-tokenid":"pulse-monitor@pve!pulse-test","value":"secret-value"}'
|
||||
}
|
||||
TAPM_PULSE_CREATE_PVE_AUTO_REGISTER_TOKEN \
|
||||
'pulse-test' token_output token_status || return 1
|
||||
unset -f pveum
|
||||
|
||||
assert_equal \
|
||||
'{"full-tokenid":"pulse-monitor@pve!pulse-test","value":"secret-value"}' \
|
||||
"$token_output" \
|
||||
"Pulse receives pveum token output"
|
||||
assert_equal 0 "$token_status" "Pulse receives pveum token status"
|
||||
}
|
||||
|
||||
assert_success "Pulse v6.1.1 pveum output-shadowing workaround" \
|
||||
test_pve_token_output_assignment
|
||||
|
||||
nodes_json='[
|
||||
{"node":"pve3","status":"offline"},
|
||||
{"node":"pve2","status":"online"},
|
||||
{"node":"pve1","status":"online"}
|
||||
]'
|
||||
assert_equal $'pve1\npve2' \
|
||||
"$(TAPM_PULSE_ONLINE_NODES_FROM_JSON "$nodes_json")" \
|
||||
"online Pulse nodes selected and sorted"
|
||||
assert_equal 'pve3' \
|
||||
"$(TAPM_PULSE_OFFLINE_NODES_FROM_JSON "$nodes_json")" \
|
||||
"offline Pulse nodes reported"
|
||||
|
||||
assert_success "valid Pulse CTID" TAPM_PULSE_VALID_CTID 210
|
||||
assert_failure "invalid Pulse CTID" TAPM_PULSE_VALID_CTID 99
|
||||
ctid_resources='[
|
||||
{"type":"qemu","vmid":200},
|
||||
{"type":"lxc","vmid":"201"},
|
||||
{"type":"storage","storage":"local"}
|
||||
]'
|
||||
assert_equal 202 \
|
||||
"$(TAPM_PULSE_FIRST_AVAILABLE_CTID_FROM_RESOURCES "$ctid_resources" 200)" \
|
||||
"first available Pulse CTID starts at 200 and skips occupied IDs"
|
||||
assert_equal 200 \
|
||||
"$(TAPM_PULSE_FIRST_AVAILABLE_CTID_FROM_RESOURCES '[]' 200)" \
|
||||
"Pulse CTID 200 selected when available"
|
||||
assert_failure "invalid Pulse CTID resource data rejected" \
|
||||
TAPM_PULSE_FIRST_AVAILABLE_CTID_FROM_RESOURCES '{}' 200
|
||||
assert_success "valid Pulse hostname" TAPM_PULSE_VALID_HOSTNAME pulse-monitor
|
||||
assert_failure "invalid Pulse hostname" TAPM_PULSE_VALID_HOSTNAME 'pulse monitor'
|
||||
assert_success "valid positive integer" TAPM_PULSE_VALID_POSITIVE_INTEGER 1024
|
||||
assert_failure "zero is not positive" TAPM_PULSE_VALID_POSITIVE_INTEGER 0
|
||||
assert_success "zero is a valid nonnegative integer" \
|
||||
TAPM_PULSE_VALID_NONNEGATIVE_INTEGER 0
|
||||
assert_failure "negative integer rejected" TAPM_PULSE_VALID_NONNEGATIVE_INTEGER -1
|
||||
assert_success "valid Pulse port" TAPM_PULSE_VALID_PORT 7655
|
||||
assert_failure "Pulse port too high" TAPM_PULSE_VALID_PORT 65536
|
||||
assert_success "valid Pulse IPv4 CIDR" TAPM_PULSE_VALID_IPV4_CIDR 10.10.1.50/24
|
||||
assert_failure "invalid Pulse IPv4 CIDR" TAPM_PULSE_VALID_IPV4_CIDR 10.10.1.500/24
|
||||
assert_success "valid Pulse gateway" TAPM_PULSE_VALID_IPV4 10.10.0.1
|
||||
assert_failure "gateway CIDR rejected" TAPM_PULSE_VALID_IPV4 10.10.0.1/16
|
||||
assert_success "blank optional Pulse address accepted" \
|
||||
TAPM_PULSE_VALID_OPTIONAL_IPV4_CIDR ''
|
||||
assert_success "valid Pulse VLAN" TAPM_PULSE_VALID_OPTIONAL_VLAN 4094
|
||||
assert_failure "Pulse VLAN zero rejected" TAPM_PULSE_VALID_OPTIONAL_VLAN 0
|
||||
assert_success "blank Pulse VLAN accepted" TAPM_PULSE_VALID_OPTIONAL_VLAN ''
|
||||
|
||||
assert_success "12-character Pulse admin password accepted" \
|
||||
TAPM_PULSE_VALID_ADMIN_PASSWORD '123456789012'
|
||||
assert_success "72-byte Pulse admin password accepted" \
|
||||
TAPM_PULSE_VALID_ADMIN_PASSWORD \
|
||||
'123456789012345678901234567890123456789012345678901234567890123456789012'
|
||||
assert_failure "short Pulse admin password rejected" \
|
||||
TAPM_PULSE_VALID_ADMIN_PASSWORD '12345678901'
|
||||
assert_failure "multibyte Pulse admin password still requires 12 characters" \
|
||||
TAPM_PULSE_VALID_ADMIN_PASSWORD 'éééééé'
|
||||
assert_failure "Pulse admin password above bcrypt limit rejected" \
|
||||
TAPM_PULSE_VALID_ADMIN_PASSWORD \
|
||||
'1234567890123456789012345678901234567890123456789012345678901234567890123'
|
||||
|
||||
resources='[
|
||||
{"type":"lxc","name":"pulse-a","tags":"tapm;pulse"},
|
||||
{"type":"qemu","name":"unrelated"}
|
||||
]'
|
||||
assert_success "tagged Pulse LXC detected" TAPM_PULSE_RESOURCE_INSTALLED "$resources"
|
||||
assert_success "legacy Pulse hostname detected" \
|
||||
TAPM_PULSE_RESOURCE_INSTALLED '[{"type":"lxc","name":"Pulse"}]'
|
||||
assert_success "untagged default Pulse hostname detected" \
|
||||
TAPM_PULSE_RESOURCE_INSTALLED '[{"type":"lxc","name":"Pulse-Monitor"}]'
|
||||
assert_failure "unrelated resource not detected" \
|
||||
TAPM_PULSE_RESOURCE_INSTALLED '[{"type":"qemu","name":"pulse"}]'
|
||||
|
||||
test_pulse_config_detection() {
|
||||
local fixture_root status
|
||||
|
||||
fixture_root="$(mktemp -d /tmp/tapm-pulse-configs.XXXXXX)" || return 1
|
||||
mkdir -p "${fixture_root}/pve1/lxc" || return 1
|
||||
printf '%s\n' \
|
||||
'arch: amd64' \
|
||||
'hostname: Pulse-Monitor' \
|
||||
'memory: 2048' >"${fixture_root}/pve1/lxc/200.conf"
|
||||
TAPM_PULSE_RESOURCE_INSTALLED_FROM_CONFIGS "$fixture_root"
|
||||
status=$?
|
||||
rm -rf -- "$fixture_root"
|
||||
return "$status"
|
||||
}
|
||||
|
||||
assert_success "Pulse LXC detected from shared Proxmox configuration" \
|
||||
test_pulse_config_detection
|
||||
assert_failure "missing Pulse configuration directory is not installed" \
|
||||
TAPM_PULSE_RESOURCE_INSTALLED_FROM_CONFIGS /does/not/exist
|
||||
|
||||
ha_status=$'quorum OK\nmaster pve1 (active, Sat Jul 25 12:00:00 2026)\nlrm pve1 (active, Sat Jul 25 12:00:00 2026)'
|
||||
assert_success "active Proxmox HA detected" TAPM_PULSE_HA_STATUS_ENABLED "$ha_status"
|
||||
assert_failure "inactive Proxmox HA rejected" \
|
||||
TAPM_PULSE_HA_STATUS_ENABLED $'quorum OK\nmaster pve1 (idle)'
|
||||
|
||||
finish_tests
|
||||
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
TEST_ROOT="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd -P)"
|
||||
# shellcheck source=../inc/rmm.inc
|
||||
source "${TEST_ROOT}/inc/rmm.inc"
|
||||
|
||||
example_token='a81581f5-2e8c-465a-9eff-a094a75d5bbf'
|
||||
example_url="https://prod.setup.itsupport247.net/linux/BareboneAgent/64/TA_Green_Bay-Technology_Arch_Corporate_Linux_Server_ITSPlatform_TKN${example_token}/RUN/setup"
|
||||
|
||||
actual_token="$(TAPM_RMM_TOKEN_FROM_URL "$example_url")"
|
||||
if [[ "$actual_token" != "$example_token" ]]; then
|
||||
printf 'FAIL: extracted %q, want %q\n' "$actual_token" "$example_token" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if TAPM_RMM_TOKEN_FROM_URL 'https://prod.setup.itsupport247.net/linux/setup' >/dev/null; then
|
||||
printf 'FAIL: accepted an RMM URL without a token\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if TAPM_RMM_TOKEN_FROM_URL \
|
||||
'https://prod.setup.itsupport247.net/TKNnot-a-token/RUN/setup' >/dev/null; then
|
||||
printf 'FAIL: accepted a malformed RMM token\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf 'PASS: RMM token extraction\n'
|
||||
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env bash
|
||||
set -u -o pipefail
|
||||
|
||||
TEST_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
test_dir="$(mktemp -d)"
|
||||
trap 'rm -rf "$test_dir"' EXIT
|
||||
|
||||
TAPM_CONFIG_FILE="${test_dir}/etc/config.env"
|
||||
TAPM_CONFIG_TEST_STDIN=1
|
||||
source "${TEST_ROOT}/inc/runtime-config.inc"
|
||||
|
||||
if ! printf '%s\n%s\n' \
|
||||
'https://tapm.example.com' \
|
||||
'git.example.com' |
|
||||
TAPM_ENSURE_RUNTIME_CONFIG >/dev/null; then
|
||||
printf 'FAIL: configuration wizard failed\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
expected=$'TAPM_BROKER_URL=https://tapm.example.com\nGITEA_DOMAIN=git.example.com'
|
||||
actual="$(cat "$TAPM_CONFIG_FILE")"
|
||||
if [[ "$actual" != "$expected" ]]; then
|
||||
printf 'FAIL: unexpected configuration contents\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
if stat -c '%a' "$TAPM_CONFIG_FILE" >/dev/null 2>&1; then
|
||||
config_mode="$(stat -c '%a' "$TAPM_CONFIG_FILE")"
|
||||
else
|
||||
config_mode="$(stat -f '%Lp' "$TAPM_CONFIG_FILE")"
|
||||
fi
|
||||
if [[ "$config_mode" != 600 ]]; then
|
||||
printf 'FAIL: configuration mode is not 600\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
unset TAPM_BROKER_URL GITEA_DOMAIN GITEA_URL
|
||||
TAPM_LOAD_RUNTIME_CONFIG
|
||||
if [[ "$TAPM_BROKER_URL" != 'https://tapm.example.com' ||
|
||||
"$GITEA_DOMAIN" != 'git.example.com' ||
|
||||
"$GITEA_URL" != 'https://git.example.com' ]]; then
|
||||
printf 'FAIL: saved configuration did not reload\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf 'PASS: runtime configuration wizard\n'
|
||||
@@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
TEST_ROOT="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd -P)"
|
||||
# shellcheck source=../inc/secure-input.inc
|
||||
source "${TEST_ROOT}/inc/secure-input.inc"
|
||||
|
||||
masked_value=''
|
||||
output_file="$(mktemp)"
|
||||
trap 'rm -f "$output_file"' EXIT
|
||||
|
||||
TAPM_READ_MASKED masked_value >"$output_file" <<'EOF'
|
||||
TAPM-secret
|
||||
EOF
|
||||
|
||||
if [[ "$masked_value" != 'TAPM-secret' ]]; then
|
||||
printf 'FAIL: masked input did not preserve the entered value\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ "$(cat "$output_file")" != '***********' ]]; then
|
||||
printf 'FAIL: masked input did not display one marker per character\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf 'PASS: masked secret input\n'
|
||||
Executable
+22
@@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env bash
|
||||
set -u -o pipefail
|
||||
|
||||
TEST_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
source "${TEST_ROOT}/tests/testlib.sh"
|
||||
source "${TEST_ROOT}/colors.inc"
|
||||
|
||||
assert_equal '\e[96m' "${idsCL[LightCyan]}" \
|
||||
"bundled foreground colors are available"
|
||||
assert_equal '\e[103m' "${idsBG[LightYellow]}" \
|
||||
"bundled background colors are available"
|
||||
assert_equal '\e[1m' "${idsST[Bold]}" \
|
||||
"bundled text styles are available"
|
||||
assert_success "EXIT1 is bundled in TA-ProxMenu defaults" \
|
||||
grep -q '^EXIT1()' "${TEST_ROOT}/defaults.inc"
|
||||
assert_success "ENTER2CONTINUE is bundled in TA-ProxMenu defaults" \
|
||||
grep -q '^ENTER2CONTINUE()' "${TEST_ROOT}/defaults.inc"
|
||||
assert_failure "runtime launchers do not reference external iDS defaults" \
|
||||
grep -E -q '/opt/idssys/defaults|iDS-Defaults' \
|
||||
"${TEST_ROOT}/run.sh" "${TEST_ROOT}/proxmenu-scripts.sh"
|
||||
|
||||
finish_tests
|
||||
Executable
+43
@@ -0,0 +1,43 @@
|
||||
#!/usr/bin/env bash
|
||||
set -u -o pipefail
|
||||
|
||||
TEST_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
source "${TEST_ROOT}/tests/testlib.sh"
|
||||
source "${TEST_ROOT}/inc/virtio-helpers.inc"
|
||||
|
||||
assert_equal virtio-win.iso \
|
||||
"$(TAPM_VIRTIO_FILENAME_FROM_URL \
|
||||
'https://fedorapeople.org/stable/virtio-win.iso?download=1')" \
|
||||
"stable source filename"
|
||||
assert_equal virtio-win-0.1.285.iso \
|
||||
"$(TAPM_VIRTIO_FILENAME_FROM_URL \
|
||||
'https://fedorapeople.org/archive/virtio-win-0.1.285.iso')" \
|
||||
"versioned source filename"
|
||||
assert_failure "unexpected source filename rejected" \
|
||||
TAPM_VIRTIO_FILENAME_FROM_URL \
|
||||
'https://example.invalid/virtio-win-latest.iso'
|
||||
|
||||
assert_equal virtio-win-latest-0.1.285.iso \
|
||||
"$(TAPM_VIRTIO_LABELED_FILENAME virtio-win-0.1.285.iso latest)" \
|
||||
"stable local filename"
|
||||
assert_equal virtio-win-server-2016-0.1.240.iso \
|
||||
"$(TAPM_VIRTIO_LABELED_FILENAME \
|
||||
virtio-win-0.1.240.iso server-2016)" \
|
||||
"Server 2016 local filename"
|
||||
assert_equal virtio-win-server-2008r2-0.1.172.iso \
|
||||
"$(TAPM_VIRTIO_LABELED_FILENAME \
|
||||
virtio-win-0.1.172.iso server-2008r2)" \
|
||||
"Server 2008 R2 local filename"
|
||||
assert_failure "unsafe local label rejected" \
|
||||
TAPM_VIRTIO_LABELED_FILENAME virtio-win-0.1.285.iso '../latest'
|
||||
|
||||
assert_success "fresh VirtIO cache accepted" \
|
||||
TAPM_VIRTIO_CACHE_VALID 1000 1500 86400 virtio-win-0.1.285.iso
|
||||
assert_failure "expired VirtIO cache rejected" \
|
||||
TAPM_VIRTIO_CACHE_VALID 1000 87400 86400 virtio-win-0.1.285.iso
|
||||
assert_failure "future VirtIO cache rejected" \
|
||||
TAPM_VIRTIO_CACHE_VALID 2000 1000 86400 virtio-win-0.1.285.iso
|
||||
assert_failure "unsafe VirtIO cache filename rejected" \
|
||||
TAPM_VIRTIO_CACHE_VALID 1000 1500 86400 ../../installer
|
||||
|
||||
finish_tests
|
||||
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
TEST_ASSERTIONS=0
|
||||
TEST_FAILURES=0
|
||||
|
||||
test_fail() {
|
||||
printf 'FAIL: %s\n' "$1" >&2
|
||||
(( TEST_FAILURES += 1 ))
|
||||
}
|
||||
|
||||
assert_equal() {
|
||||
local expected="$1"
|
||||
local actual="$2"
|
||||
local label="$3"
|
||||
|
||||
(( TEST_ASSERTIONS += 1 ))
|
||||
if [[ "$actual" != "$expected" ]]; then
|
||||
test_fail "${label}: expected '${expected}', received '${actual}'"
|
||||
fi
|
||||
}
|
||||
|
||||
assert_success() {
|
||||
local label="$1"
|
||||
shift
|
||||
|
||||
(( TEST_ASSERTIONS += 1 ))
|
||||
"$@" || test_fail "${label}: expected success"
|
||||
}
|
||||
|
||||
assert_failure() {
|
||||
local label="$1"
|
||||
shift
|
||||
|
||||
(( TEST_ASSERTIONS += 1 ))
|
||||
if "$@"; then
|
||||
test_fail "${label}: expected failure"
|
||||
fi
|
||||
}
|
||||
|
||||
finish_tests() {
|
||||
if (( TEST_FAILURES > 0 )); then
|
||||
printf '%d assertion(s), %d failure(s)\n' \
|
||||
"$TEST_ASSERTIONS" "$TEST_FAILURES" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
printf '%d assertion(s) passed\n' "$TEST_ASSERTIONS"
|
||||
}
|
||||
Reference in New Issue
Block a user