diff --git a/test_elemes_sh.sh b/test_elemes_sh.sh new file mode 100755 index 0000000..cd99149 --- /dev/null +++ b/test_elemes_sh.sh @@ -0,0 +1,185 @@ +#!/usr/bin/env bash +# Regression test untuk resolusi PROJECT_NAME dinamis di elemes.sh. +# +# Memastikan elemes.sh TIDAK pernah menulis nama container hard-coded +# (mis. lms-dev_* / lms-sinau-c_*): semua operasi di-resolve lewat +# podman-compose (exec/restart/config) dan label filter project+service, +# apa pun nama direktori parent / PROJECT_NAME-nya. +# +# Berjalan dengan fake `podman`/`podman-compose` di workspace temp sehingga +# tidak menyentuh container asli sama sekali. + +set -euo pipefail + +TESTROOT=$(mktemp -d) +trap 'rm -rf "$TESTROOT"' EXIT + +# PARENT_DIR memakai NAMA DINAMIS (bukan "lms-dev") -> PROJECT_NAME ikut dinamis. +# elemes.sh menghitung PROJECT_NAME dari basename PARENT_DIR, jadi script +# ditaruh di sub-direktori /elemes. +PARENT_DIR=$(mktemp -d "$TESTROOT/my-deploy.XXXX") +WORKSPACE="$PARENT_DIR/elemes" +PROJECT_NAME=$(basename "$PARENT_DIR") +FAKEBIN="$TESTROOT/fakebin" +mkdir -p "$WORKSPACE" "$FAKEBIN" +touch "$PARENT_DIR/.env" # PARENT_DIR/.env (dibaca run_compose & db_init) + +cp "$(dirname "$0")/elemes.sh" "$WORKSPACE/elemes.sh" +export FAKE_PROJECT="$PROJECT_NAME" + +# --- Fake podman-compose: catat semua panggilan, jawab config/exec/restart ---- +cat > "$FAKEBIN/podman-compose" <<'EOF' +#!/usr/bin/env bash +LOG="$(dirname "$0")/calls.log" +echo "podman-compose $*" >> "$LOG" +ARGS=" $* " +if [[ "$ARGS" == *" config --services "* ]]; then + printf 'elemes-ts\npostgres\nelemes\ncompiler-worker\nelemes-frontend\nvelxio\nflowchart\n' +elif [[ "$ARGS" == *" config "* ]]; then + cat <<'YAML' +services: + compiler-worker: + image: lms-compiler-worker:latest + elemes: + image: lms-backend:latest + elemes-frontend: + image: lms-frontend:latest + elemes-ts: + image: docker.io/tailscale/tailscale:latest + flowchart: + image: lms-flowchart:latest + postgres: + image: postgres:18-alpine + velxio: + image: lms-velxio:latest +YAML +elif [[ "$ARGS" == *" exec "* ]]; then + # psql -tAc dipakai db_init untuk deteksi schema kosong -> jawab "t" + if [[ "$ARGS" == *" -tAc "* ]]; then + echo "t" + else + echo "fake-exec:$*" + fi +elif [[ "$ARGS" == *" restart "* ]]; then + echo "fake-restart:$*" +fi +exit 0 +EOF + +# --- Fake podman: catat panggilan, jawab ps/images/inspect -------------------- +cat > "$FAKEBIN/podman" <<'EOF' +#!/usr/bin/env bash +LOG="$(dirname "$0")/calls.log" +echo "podman $*" >> "$LOG" +ARGS=" $* " +if [[ "$ARGS" == *" ps "* ]]; then + if [[ "$ARGS" == *"{{.ID}}"* ]]; then + echo "cid111" + else + echo "${FAKE_PROJECT}_elemes-ts_1|docker.io/tailscale/tailscale:latest|abc123|Up|map[com.docker.compose.project:$FAKE_PROJECT com.docker.compose.service:elemes-ts io.podman.compose.project:$FAKE_PROJECT]" + echo "${FAKE_PROJECT}_postgres_1|postgres:18-alpine|def456|Up (healthy)|map[com.docker.compose.project:$FAKE_PROJECT com.docker.compose.service:postgres io.podman.compose.project:$FAKE_PROJECT]" + echo "${FAKE_PROJECT}_elemes_1|localhost/lms-backend:latest|old123|Up|map[com.docker.compose.project:$FAKE_PROJECT com.docker.compose.service:elemes io.podman.compose.project:$FAKE_PROJECT]" + echo "${FAKE_PROJECT}_compiler-worker_1|localhost/lms-compiler-worker:latest|jkl012|Up|map[com.docker.compose.project:$FAKE_PROJECT com.docker.compose.service:compiler-worker io.podman.compose.project:$FAKE_PROJECT]" + echo "${FAKE_PROJECT}_elemes-frontend_1|localhost/lms-frontend:latest|mno345|Up|map[com.docker.compose.project:$FAKE_PROJECT com.docker.compose.service:elemes-frontend io.podman.compose.project:$FAKE_PROJECT]" + echo "${FAKE_PROJECT}_velxio_1|localhost/lms-velxio:latest|pqr678|Up|map[com.docker.compose.project:$FAKE_PROJECT com.docker.compose.service:velxio io.podman.compose.project:$FAKE_PROJECT]" + echo "${FAKE_PROJECT}_flowchart_1|localhost/lms-flowchart:latest|stu901|Up|map[com.docker.compose.project:$FAKE_PROJECT com.docker.compose.service:flowchart io.podman.compose.project:$FAKE_PROJECT]" + fi +elif [[ "$ARGS" == *" images "* ]]; then + case "$ARGS" in + *"lms-backend:latest"*) echo "new456" ;; # != old123 (ImageID container) -> STALE + *"lms-frontend:latest"*) echo "mno345" ;; + *"lms-velxio:latest"*) echo "pqr678" ;; + *"lms-compiler-worker:latest"*) echo "jkl012" ;; + *"lms-flowchart:latest"*) echo "stu901" ;; + *"postgres:18-alpine"*) echo "def456" ;; + *"tailscale/tailscale:latest"*) echo "abc123" ;; + *) echo "deadbeef" ;; + esac +elif [[ "$ARGS" == *" inspect "* ]]; then + echo '["/bin/sh","-c","gunicorn --config gunicorn.conf.py app:create_app()"]' + echo '["gunicorn"]' + echo "localhost/lms-backend:latest" +fi +exit 0 +EOF + +chmod +x "$FAKEBIN/podman" "$FAKEBIN/podman-compose" +export PATH="$FAKEBIN:$PATH" + +FAILED=0 +fail() { echo "❌ $1"; FAILED=1; } +ok() { echo "✅ $1"; } + +# ===== 1) verify: resolusi via label + service, tanpa nama container hard-coded +if ! OUT=$(cd "$WORKSPACE" && bash elemes.sh verify 2>&1); then + fail "verify tidak berjalan" + echo "$OUT" +else + echo "$OUT" | grep -q "${PROJECT_NAME}_elemes_1" \ + && ok "verify menampilkan container ${PROJECT_NAME}_elemes_1" \ + || fail "verify tidak menampilkan container ${PROJECT_NAME}_elemes_1" + echo "$OUT" | grep -q "flowchart" \ + && ok "verify menampilkan service flowchart" \ + || fail "verify tidak menampilkan service flowchart" + echo "$OUT" | grep -q "STALE" \ + && ok "verify mendeteksi image STALE (elemes)" \ + || fail "verify tidak mendeteksi image STALE" + echo "$OUT" | grep -q "lms-dev" \ + && fail "verify masih menyebut 'lms-dev'" || true +fi + +# ===== 2) run: down/up + db_init memakai compose_exec / compose_restart ====== +if ! OUT=$(cd "$WORKSPACE" && bash elemes.sh run 2>&1); then + fail "run tidak berjalan" + echo "$OUT" +else + grep -q "exec -T postgres pg_isready" "$FAKEBIN/calls.log" \ + && ok "db_init memakai compose_exec postgres (pg_isready)" \ + || fail "db_init tidak memakai compose_exec postgres (pg_isready)" + grep -q "exec -T postgres psql" "$FAKEBIN/calls.log" \ + && ok "db_init memakai compose_exec postgres (psql -tAc)" \ + || fail "db_init tidak memakai compose_exec postgres (psql -tAc)" + grep -q "exec -T -w /app -e PYTHONPATH=services elemes python -m alembic upgrade head" "$FAKEBIN/calls.log" \ + && ok "db_init memakai compose_exec elemes (alembic)" \ + || fail "db_init tidak memakai compose_exec elemes (alembic)" + grep -q "restart elemes" "$FAKEBIN/calls.log" \ + && ok "first-run memakai compose_restart elemes" \ + || fail "first-run tidak memakai compose_restart elemes" +fi + +# ===== 3) dbbackup: dump lewat compose_exec postgres (bukan lms-dev_postgres_1) +if ! OUT=$(cd "$WORKSPACE" && bash elemes.sh dbbackup 2>&1); then + fail "dbbackup tidak berjalan" + echo "$OUT" +else + DUMP=$(ls "$PARENT_DIR"/backups/elemes_*.sql 2>/dev/null | head -1 || true) + if [ -n "$DUMP" ] && [ -s "$DUMP" ]; then + ok "dbbackup menghasilkan file dump" + else + fail "dbbackup tidak menghasilkan file dump" + fi + grep -q "exec -T postgres pg_dump" "$FAKEBIN/calls.log" \ + && ok "dbbackup memakai compose_exec postgres (pg_dump)" \ + || fail "dbbackup tidak memakai compose_exec postgres (pg_dump)" +fi + +# ===== 4) Tidak boleh ada container name hard-coded / `podman exec` langsung +if grep -qE "lms-dev_|lms-sinau-c_|podman exec " "$FAKEBIN/calls.log"; then + fail "masih ada panggilan container name hard-coded:" + grep -E "lms-dev_|lms-sinau-c_|podman exec " "$FAKEBIN/calls.log" || true +else + ok "tidak ada nama container hard-coded di semua panggilan" +fi + +# ===== 5) Label filter memakai PROJECT_NAME dinamis +grep -q "label=io.podman.compose.project=$PROJECT_NAME" "$FAKEBIN/calls.log" \ + && ok "label filter memakai PROJECT_NAME dinamis ($PROJECT_NAME)" \ + || fail "label filter tidak memakai PROJECT_NAME dinamis" + +echo "" +if [ "$FAILED" -eq 0 ]; then + echo "🎉 Semua regression test lulus (PROJECT_NAME=$PROJECT_NAME)" +else + echo "💥 Ada test yang gagal" + exit 1 +fi