mirror of
https://github.com/rubenhensen/k8scd.git
synced 2026-09-17 10:22:54 +02:00
481 lines
13 KiB
Bash
481 lines
13 KiB
Bash
#!/usr/bin/env bash
|
|
# Assertion library for nix-infra-machine tests
|
|
# Provides reusable assertion functions with consistent output formatting
|
|
#
|
|
# All assertions follow this pattern:
|
|
# assert_* "label" [args...]
|
|
# Returns 0 on pass, 1 on fail
|
|
# Prints colored pass/fail message
|
|
#
|
|
# Requires: Colors (GREEN, RED, YELLOW, NC) from shared.sh
|
|
# Requires: cmd, cmd_value, cmd_clean functions from shared.sh
|
|
|
|
# ============================================================================
|
|
# Service Assertions
|
|
# ============================================================================
|
|
|
|
# Check if a systemd service is active
|
|
# Usage: assert_service_active "$node" "service-name" ["optional label"]
|
|
assert_service_active() {
|
|
local node="$1"
|
|
local service="$2"
|
|
local label="${3:-$service}"
|
|
|
|
local status
|
|
status=$(cmd_value "$node" "systemctl is-active $service")
|
|
|
|
if [[ "$status" == "active" ]]; then
|
|
echo -e " ${GREEN}✓${NC} $label: active [pass]"
|
|
return 0
|
|
else
|
|
echo -e " ${RED}✗${NC} $label: $status [fail]"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Check if a oneshot service completed successfully
|
|
# Usage: assert_service_completed "$node" "service-name" ["optional label"]
|
|
assert_service_completed() {
|
|
local node="$1"
|
|
local service="$2"
|
|
local label="${3:-$service}"
|
|
|
|
local status
|
|
status=$(cmd_value "$node" "systemctl is-active $service 2>/dev/null || echo 'inactive'")
|
|
|
|
if [[ "$status" == "inactive" ]]; then
|
|
local exit_status
|
|
exit_status=$(cmd_value "$node" "systemctl show -p ExecMainStatus $service | cut -d= -f2")
|
|
if [[ "$exit_status" == "0" ]]; then
|
|
echo -e " ${GREEN}✓${NC} $label: completed successfully [pass]"
|
|
return 0
|
|
else
|
|
echo -e " ${RED}✗${NC} $label: failed (exit status: $exit_status) [fail]"
|
|
return 1
|
|
fi
|
|
else
|
|
echo -e " ${GREEN}✓${NC} $label: $status [pass]"
|
|
return 0
|
|
fi
|
|
}
|
|
|
|
# Check if service SubState is running
|
|
# Usage: assert_service_running "$node" "service-name" ["optional label"]
|
|
assert_service_running() {
|
|
local node="$1"
|
|
local service="$2"
|
|
local label="${3:-$service}"
|
|
|
|
local state
|
|
state=$(cmd_value "$node" "systemctl show -p SubState $service --value")
|
|
|
|
if [[ "$state" == "running" ]]; then
|
|
echo -e " ${GREEN}✓${NC} $label: running [pass]"
|
|
return 0
|
|
else
|
|
echo -e " ${RED}✗${NC} $label: $state [fail]"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# ============================================================================
|
|
# Process Assertions
|
|
# ============================================================================
|
|
|
|
# Check if a process is running (using pgrep pattern)
|
|
# Usage: assert_process_running "$node" "pattern" "label"
|
|
assert_process_running() {
|
|
local node="$1"
|
|
local pattern="$2"
|
|
local label="$3"
|
|
|
|
local result
|
|
result=$(cmd_clean "$node" "pgrep -a $pattern || echo ''")
|
|
|
|
if [[ -n "$result" ]]; then
|
|
echo -e " ${GREEN}✓${NC} $label process running [pass]"
|
|
return 0
|
|
else
|
|
echo -e " ${RED}✗${NC} $label process not running [fail]"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Check process count meets minimum
|
|
# Usage: assert_process_count "$node" "pattern" "min_count" "label"
|
|
assert_process_count() {
|
|
local node="$1"
|
|
local pattern="$2"
|
|
local min_count="$3"
|
|
local label="$4"
|
|
|
|
local count
|
|
count=$(cmd_value "$node" "pgrep -c $pattern || echo 0")
|
|
|
|
if [[ "$count" -ge "$min_count" ]]; then
|
|
echo -e " ${GREEN}✓${NC} $count $label processes running [pass]"
|
|
return 0
|
|
else
|
|
echo -e " ${RED}✗${NC} Expected $min_count $label processes, found $count [fail]"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# ============================================================================
|
|
# Port Assertions
|
|
# ============================================================================
|
|
|
|
# Check if a port is listening
|
|
# Usage: assert_port_listening "$node" "port" ["label"]
|
|
assert_port_listening() {
|
|
local node="$1"
|
|
local port="$2"
|
|
local label="${3:-Port $port}"
|
|
|
|
local result
|
|
result=$(cmd "$node" "ss -tlnp | grep :$port")
|
|
|
|
if [[ "$result" == *":$port"* ]]; then
|
|
echo -e " ${GREEN}✓${NC} $label is listening [pass]"
|
|
return 0
|
|
else
|
|
echo -e " ${RED}✗${NC} $label is not listening [fail]"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# ============================================================================
|
|
# HTTP Assertions
|
|
# ============================================================================
|
|
|
|
# Check HTTP status code
|
|
# Usage: assert_http_status "$node" "url" "expected_codes" ["label"]
|
|
# expected_codes can be space-separated: "200 302 303"
|
|
assert_http_status() {
|
|
local node="$1"
|
|
local url="$2"
|
|
local expected="$3"
|
|
local label="${4:-HTTP $url}"
|
|
|
|
local code
|
|
code=$(cmd_value "$node" "curl -s -o /dev/null -w '%{http_code}' '$url' 2>/dev/null || echo '000'")
|
|
|
|
for exp in $expected; do
|
|
if [[ "$code" == "$exp" ]]; then
|
|
echo -e " ${GREEN}✓${NC} $label: HTTP $code [pass]"
|
|
return 0
|
|
fi
|
|
done
|
|
|
|
echo -e " ${RED}✗${NC} $label: HTTP $code (expected: $expected) [fail]"
|
|
return 1
|
|
}
|
|
|
|
# Check HTTP response contains string
|
|
# Usage: assert_http_contains "$node" "url" "expected_string" ["label"]
|
|
assert_http_contains() {
|
|
local node="$1"
|
|
local url="$2"
|
|
local expected="$3"
|
|
local label="${4:-HTTP $url}"
|
|
|
|
local response
|
|
response=$(cmd_clean "$node" "curl -s '$url' 2>/dev/null")
|
|
|
|
if [[ "$response" == *"$expected"* ]]; then
|
|
echo -e " ${GREEN}✓${NC} $label contains '$expected' [pass]"
|
|
return 0
|
|
else
|
|
echo -e " ${RED}✗${NC} $label missing '$expected' [fail]"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Check HTTP response contains multiple strings (all must match)
|
|
# Usage: assert_http_contains_all "$node" "url" "string1" "string2" ... ["--label" "label"]
|
|
assert_http_contains_all() {
|
|
local node="$1"
|
|
local url="$2"
|
|
shift 2
|
|
|
|
local label="HTTP $url"
|
|
local patterns=()
|
|
|
|
# Parse arguments - check for --label flag
|
|
while [[ $# -gt 0 ]]; do
|
|
if [[ "$1" == "--label" ]]; then
|
|
label="$2"
|
|
shift 2
|
|
else
|
|
patterns+=("$1")
|
|
shift
|
|
fi
|
|
done
|
|
|
|
local response
|
|
response=$(cmd_clean "$node" "curl -s '$url' 2>/dev/null")
|
|
|
|
for pattern in "${patterns[@]}"; do
|
|
if [[ "$response" != *"$pattern"* ]]; then
|
|
echo -e " ${RED}✗${NC} $label missing '$pattern' [fail]"
|
|
return 1
|
|
fi
|
|
done
|
|
|
|
echo -e " ${GREEN}✓${NC} $label [pass]"
|
|
return 0
|
|
}
|
|
|
|
# ============================================================================
|
|
# String Assertions
|
|
# ============================================================================
|
|
|
|
# Check if value equals expected
|
|
# Usage: assert_equals "actual" "expected" "label"
|
|
assert_equals() {
|
|
local actual="$1"
|
|
local expected="$2"
|
|
local label="$3"
|
|
|
|
if [[ "$actual" == "$expected" ]]; then
|
|
echo -e " ${GREEN}✓${NC} $label [pass]"
|
|
return 0
|
|
else
|
|
echo -e " ${RED}✗${NC} $label: got '$actual', expected '$expected' [fail]"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Check if value contains substring
|
|
# Usage: assert_contains "haystack" "needle" "label"
|
|
assert_contains() {
|
|
local haystack="$1"
|
|
local needle="$2"
|
|
local label="$3"
|
|
|
|
if [[ "$haystack" == *"$needle"* ]]; then
|
|
echo -e " ${GREEN}✓${NC} $label [pass]"
|
|
return 0
|
|
else
|
|
echo -e " ${RED}✗${NC} $label: missing '$needle' [fail]"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Check if value contains all substrings
|
|
# Usage: assert_contains_all "haystack" "label" "needle1" "needle2" ...
|
|
assert_contains_all() {
|
|
local haystack="$1"
|
|
local label="$2"
|
|
shift 2
|
|
|
|
for needle in "$@"; do
|
|
if [[ "$haystack" != *"$needle"* ]]; then
|
|
echo -e " ${RED}✗${NC} $label: missing '$needle' [fail]"
|
|
return 1
|
|
fi
|
|
done
|
|
|
|
echo -e " ${GREEN}✓${NC} $label [pass]"
|
|
return 0
|
|
}
|
|
|
|
# Check if value is not empty
|
|
# Usage: assert_not_empty "value" "label"
|
|
assert_not_empty() {
|
|
local value="$1"
|
|
local label="$2"
|
|
|
|
if [[ -n "$value" ]]; then
|
|
echo -e " ${GREEN}✓${NC} $label [pass]"
|
|
return 0
|
|
else
|
|
echo -e " ${RED}✗${NC} $label: empty [fail]"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Check if value is empty or nil (for Redis-style responses)
|
|
# Usage: assert_empty_or_nil "value" "label"
|
|
assert_empty_or_nil() {
|
|
local value="$1"
|
|
local label="$2"
|
|
|
|
if [[ -z "$value" ]] || [[ "$value" == "nil" ]] || [[ "$value" == "(nil)" ]]; then
|
|
echo -e " ${GREEN}✓${NC} $label [pass]"
|
|
return 0
|
|
else
|
|
echo -e " ${RED}✗${NC} $label: got '$value', expected empty/nil [fail]"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Check if value does NOT contain error indicators
|
|
# Usage: assert_no_error "value" "label"
|
|
assert_no_error() {
|
|
local value="$1"
|
|
local label="$2"
|
|
|
|
if [[ "$value" != *"ERROR"* ]] && [[ "$value" != *"error"* ]] && [[ "$value" != *"Error"* ]]; then
|
|
echo -e " ${GREEN}✓${NC} $label [pass]"
|
|
return 0
|
|
else
|
|
echo -e " ${RED}✗${NC} $label: error found [fail]"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# ============================================================================
|
|
# File System Assertions
|
|
# ============================================================================
|
|
|
|
# Check if file exists on remote node
|
|
# Usage: assert_file_exists "$node" "/path/to/file" ["label"]
|
|
assert_file_exists() {
|
|
local node="$1"
|
|
local path="$2"
|
|
local label="${3:-File $path}"
|
|
|
|
local result
|
|
result=$(cmd_value "$node" "test -f '$path' && echo 'exists' || echo 'missing'")
|
|
|
|
if [[ "$result" == "exists" ]]; then
|
|
echo -e " ${GREEN}✓${NC} $label exists [pass]"
|
|
return 0
|
|
else
|
|
echo -e " ${RED}✗${NC} $label not found [fail]"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Check if directory exists on remote node
|
|
# Usage: assert_dir_exists "$node" "/path/to/dir" ["label"]
|
|
assert_dir_exists() {
|
|
local node="$1"
|
|
local path="$2"
|
|
local label="${3:-Directory $path}"
|
|
|
|
local result
|
|
result=$(cmd_value "$node" "test -d '$path' && echo 'exists' || echo 'missing'")
|
|
|
|
if [[ "$result" == "exists" ]]; then
|
|
echo -e " ${GREEN}✓${NC} $label exists [pass]"
|
|
return 0
|
|
else
|
|
echo -e " ${RED}✗${NC} $label not found [fail]"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# ============================================================================
|
|
# Container Assertions
|
|
# ============================================================================
|
|
|
|
# Check if a podman container is running
|
|
# Usage: assert_container_running "$node" "container-name" ["label"]
|
|
assert_container_running() {
|
|
local node="$1"
|
|
local container="$2"
|
|
local label="${3:-Container $container}"
|
|
|
|
local status
|
|
status=$(cmd_clean "$node" "podman ps --filter name=$container --format '{{.Names}} {{.Status}}'")
|
|
|
|
if [[ "$status" == *"$container"* ]]; then
|
|
echo -e " ${GREEN}✓${NC} $label running [pass]"
|
|
return 0
|
|
else
|
|
echo -e " ${RED}✗${NC} $label not running [fail]"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# ============================================================================
|
|
# Comparison Assertions
|
|
# ============================================================================
|
|
|
|
# Check if numeric value is greater than or equal to expected
|
|
# Usage: assert_gte "actual" "expected" "label"
|
|
assert_gte() {
|
|
local actual="$1"
|
|
local expected="$2"
|
|
local label="$3"
|
|
|
|
if [[ "$actual" -ge "$expected" ]]; then
|
|
echo -e " ${GREEN}✓${NC} $label ($actual >= $expected) [pass]"
|
|
return 0
|
|
else
|
|
echo -e " ${RED}✗${NC} $label: $actual < $expected [fail]"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Check if numeric value is less than expected (for ordering/timestamps)
|
|
# Usage: assert_lt "actual" "expected" "label"
|
|
assert_lt() {
|
|
local actual="$1"
|
|
local expected="$2"
|
|
local label="$3"
|
|
|
|
if [[ -n "$actual" ]] && [[ -n "$expected" ]] && [[ "$actual" -lt "$expected" ]]; then
|
|
echo -e " ${GREEN}✓${NC} $label [pass]"
|
|
return 0
|
|
else
|
|
echo -e " ${YELLOW}!${NC} $label: could not verify [warn]"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# ============================================================================
|
|
# Soft Assertions (warnings instead of failures)
|
|
# ============================================================================
|
|
|
|
# Soft assertion that shows warning instead of failure
|
|
# Usage: assert_warn "condition_result" "label" "warn_message"
|
|
# condition_result should be "true" or "false"
|
|
assert_warn() {
|
|
local condition="$1"
|
|
local label="$2"
|
|
local warn_msg="${3:-}"
|
|
|
|
if [[ "$condition" == "true" ]]; then
|
|
echo -e " ${GREEN}✓${NC} $label [pass]"
|
|
return 0
|
|
else
|
|
if [[ -n "$warn_msg" ]]; then
|
|
echo -e " ${YELLOW}!${NC} $label ($warn_msg) [warn]"
|
|
else
|
|
echo -e " ${YELLOW}!${NC} $label [warn]"
|
|
fi
|
|
return 0 # Return success for soft assertions
|
|
fi
|
|
}
|
|
|
|
# ============================================================================
|
|
# Utility: Show logs on failure
|
|
# ============================================================================
|
|
|
|
# Show service logs (call after a failed assertion)
|
|
# Usage: show_service_logs "$node" "service-name" [lines]
|
|
show_service_logs() {
|
|
local node="$1"
|
|
local service="$2"
|
|
local lines="${3:-50}"
|
|
|
|
echo ""
|
|
echo "Service logs for $service:"
|
|
cmd "$node" "journalctl -n $lines -u $service"
|
|
}
|
|
|
|
# Show container logs (call after a failed assertion)
|
|
# Usage: show_container_logs "$node" "container-name" [lines]
|
|
show_container_logs() {
|
|
local node="$1"
|
|
local container="$2"
|
|
local lines="${3:-50}"
|
|
|
|
echo ""
|
|
echo "Container logs for $container:"
|
|
cmd "$node" "podman logs --tail $lines $container"
|
|
}
|