|
| 1 | +#!/bin/bash |
| 2 | +# ============================================================================ |
| 3 | +# Dream Server dream-preflight.sh Test Suite |
| 4 | +# ============================================================================ |
| 5 | +# Validates that dream-preflight.sh is syntactically correct, follows the |
| 6 | +# project's shell style requirements, and encodes the correct LLM port |
| 7 | +# default (8080, not 11434). |
| 8 | +# |
| 9 | +# These tests are static (no running Docker) — they inspect the script itself. |
| 10 | +# Integration-level tests (actual service probing) are covered in |
| 11 | +# tests/test-health-check.sh and tests/test-integration.sh. |
| 12 | +# |
| 13 | +# Usage: bash tests/test-preflight.sh |
| 14 | +# Exit codes: 0 = all pass, 1 = one or more failures |
| 15 | +# ============================================================================ |
| 16 | + |
| 17 | +set -euo pipefail |
| 18 | + |
| 19 | +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" |
| 20 | +ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" |
| 21 | +PREFLIGHT="$ROOT_DIR/dream-preflight.sh" |
| 22 | + |
| 23 | +GREEN='\033[0;32m' |
| 24 | +RED='\033[0;31m' |
| 25 | +YELLOW='\033[1;33m' |
| 26 | +NC='\033[0m' |
| 27 | + |
| 28 | +PASSED=0 |
| 29 | +FAILED=0 |
| 30 | + |
| 31 | +pass() { printf " ${GREEN}✓ PASS${NC} %s\n" "$1"; PASSED=$((PASSED + 1)); } |
| 32 | +fail() { printf " ${RED}✗ FAIL${NC} %s\n" "$1"; FAILED=$((FAILED + 1)); } |
| 33 | +skip() { printf " ${YELLOW}⊘ SKIP${NC} %s\n" "$1"; } |
| 34 | + |
| 35 | +echo "" |
| 36 | +echo "╔════════════════════════════════════════════════╗" |
| 37 | +echo "║ dream-preflight.sh Test Suite ║" |
| 38 | +echo "╚════════════════════════════════════════════════╝" |
| 39 | +echo "" |
| 40 | + |
| 41 | +# ── Static checks ────────────────────────────────────────────────────────── |
| 42 | + |
| 43 | +# 1. Script exists |
| 44 | +if [[ ! -f "$PREFLIGHT" ]]; then |
| 45 | + fail "dream-preflight.sh not found at $PREFLIGHT" |
| 46 | + echo "" |
| 47 | + echo "Result: $PASSED passed, $FAILED failed" |
| 48 | + exit 1 |
| 49 | +fi |
| 50 | +pass "dream-preflight.sh exists" |
| 51 | + |
| 52 | +# 2. Script passes bash syntax check |
| 53 | +if bash -n "$PREFLIGHT" 2>/dev/null; then |
| 54 | + pass "bash -n syntax check passes" |
| 55 | +else |
| 56 | + fail "bash -n syntax check failed" |
| 57 | +fi |
| 58 | + |
| 59 | +# 3. Uses set -euo pipefail (project style requirement per CONTRIBUTING.md) |
| 60 | +if grep -q 'set -euo pipefail' "$PREFLIGHT"; then |
| 61 | + pass "set -euo pipefail present" |
| 62 | +else |
| 63 | + fail "set -euo pipefail missing — CONTRIBUTING.md requires this in all bash files" |
| 64 | +fi |
| 65 | + |
| 66 | +# 4. LLM port default is 8080, NOT 11434 |
| 67 | +# config/ports.json and docker-compose.base.yml define the canonical default as 8080. |
| 68 | +# 11434 is only the Strix Halo AMD override (written to .env by phase 06). |
| 69 | +# The fallback in the script must be 8080 so plain installs without OLLAMA_PORT in |
| 70 | +# .env get the correct port probed. |
| 71 | +if grep -q 'LLAMA_SERVER_PORT:-8080' "$PREFLIGHT"; then |
| 72 | + pass "LLM port fallback is 8080 (aligns with config/ports.json)" |
| 73 | +else |
| 74 | + fail "LLM port fallback is not 8080 — check the OLLAMA_PORT expansion in dream-preflight.sh" |
| 75 | +fi |
| 76 | + |
| 77 | +# 5. Does NOT contain the old wrong fallback of 11434 in port resolution |
| 78 | +if grep -q 'LLAMA_SERVER_PORT:-11434' "$PREFLIGHT"; then |
| 79 | + fail "Old wrong LLM port fallback 11434 still present — should be 8080" |
| 80 | +else |
| 81 | + pass "Old wrong LLM port fallback 11434 is gone" |
| 82 | +fi |
| 83 | + |
| 84 | +# 6. detect_backend function is present |
| 85 | +if grep -q 'detect_backend()' "$PREFLIGHT"; then |
| 86 | + pass "detect_backend function present" |
| 87 | +else |
| 88 | + fail "detect_backend function missing" |
| 89 | +fi |
| 90 | + |
| 91 | +# 7. AMD sysfs scan iterates all DRM cards (not just card1) |
| 92 | +if grep -q '/sys/class/drm/card\*/device' "$PREFLIGHT"; then |
| 93 | + pass "AMD sysfs scan uses glob (all DRM cards)" |
| 94 | +else |
| 95 | + fail "AMD sysfs scan missing wildcard — may miss some AMD GPUs" |
| 96 | +fi |
| 97 | + |
| 98 | +# 8. Script uses BASH_SOURCE for portability (not $0) |
| 99 | +if grep -q 'BASH_SOURCE' "$PREFLIGHT"; then |
| 100 | + pass "Uses BASH_SOURCE for script dir resolution" |
| 101 | +else |
| 102 | + fail "Missing BASH_SOURCE — \$0 breaks when script is sourced" |
| 103 | +fi |
| 104 | + |
| 105 | +# 9. Summary section has all expected check sections |
| 106 | +for check_label in "Checking Docker" "Checking Docker Compose" "Checking GPU" \ |
| 107 | + "Checking LLM endpoint" "Checking Whisper" "Checking TTS" \ |
| 108 | + "Checking Embeddings" "Checking Dashboard"; do |
| 109 | + if grep -q "$check_label" "$PREFLIGHT"; then |
| 110 | + pass "Check section present: $check_label" |
| 111 | + else |
| 112 | + fail "Check section missing: $check_label" |
| 113 | + fi |
| 114 | +done |
| 115 | + |
| 116 | +# 10. Uses docker port to probe actual external port mapping (not just hardcoded) |
| 117 | +if grep -q 'docker port dream-llama-server' "$PREFLIGHT"; then |
| 118 | + pass "Probes actual Docker port mapping via 'docker port'" |
| 119 | +else |
| 120 | + fail "Does not probe actual Docker port mapping" |
| 121 | +fi |
| 122 | + |
| 123 | +# ── Runtime smoke test (no Docker required) ───────────────────────────────── |
| 124 | + |
| 125 | +# 11. Script runs to completion without unbound variable or syntax errors |
| 126 | +# (Services won't be up, so we expect exit 1 — that is correct behavior) |
| 127 | +set +e |
| 128 | +err_output=$( |
| 129 | + SERVICE_HOST=localhost \ |
| 130 | + OLLAMA_PORT="" \ |
| 131 | + LLAMA_SERVER_PORT="" \ |
| 132 | + GPU_BACKEND="" \ |
| 133 | + bash "$PREFLIGHT" 2>&1 |
| 134 | +) |
| 135 | +run_exit=$? |
| 136 | +set -e |
| 137 | + |
| 138 | +if echo "$err_output" | grep -qiE 'unbound variable|syntax error|command not found: \['; then |
| 139 | + fail "Script produced shell error: $(echo "$err_output" | grep -iE 'unbound variable|syntax error' | head -1)" |
| 140 | +else |
| 141 | + pass "Script runs without shell errors (exit $run_exit is expected)" |
| 142 | +fi |
| 143 | + |
| 144 | +# 12. Exit code is 0 or 1; never an unexpected crash code |
| 145 | +if [[ "$run_exit" -eq 0 ]] || [[ "$run_exit" -eq 1 ]]; then |
| 146 | + pass "Exit code is valid (0=pass, 1=fail): $run_exit" |
| 147 | +else |
| 148 | + fail "Unexpected exit code $run_exit — script may have crashed" |
| 149 | +fi |
| 150 | + |
| 151 | +# ── Summary ──────────────────────────────────────────────────────────────── |
| 152 | + |
| 153 | +echo "" |
| 154 | +echo "Result: $PASSED passed, $FAILED failed" |
| 155 | +echo "" |
| 156 | +[[ $FAILED -eq 0 ]] |
0 commit comments