#!/usr/bin/env bash
# run-tests.sh — test suite untuk proyek firewall dan hardening server.
# Suite menyiapkan folder kerja terisolasi (mktemp), menyalin berkas
# config/, membangun fixture audit, menyalakan server latihan python3
# sebagai pengganti service sungguhan, lalu menguji: laporan audit dari
# fixture, validasi ruleset (valid dan rusak), verifikasi keamanan
# (PASS dan FAIL), beserta exit code tiap cabangnya.
# Jalankan dari mana saja: bash tests/run-tests.sh

set -u

SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
PROJ_DIR=$(dirname "$SCRIPT_DIR")
CONFIG="$PROJ_DIR/config"

PASS=0
FAIL=0
WORK=""
SERVER=""

# assert_eq <harapan> <nyata> <pesan>: bandingkan dua nilai.
assert_eq() {
    if [ "$1" = "$2" ]; then
        PASS=$((PASS + 1))
        printf 'OK    %s\n' "$3"
    else
        FAIL=$((FAIL + 1))
        printf 'GAGAL %s: harapan %s, nyata %s\n' "$3" "$1" "$2"
    fi
}

# assert_ada <pola> <berkas> <pesan>: pola harus ditemukan di berkas.
assert_ada() {
    if grep -qF -- "$1" "$2"; then
        PASS=$((PASS + 1))
        printf 'OK    %s\n' "$3"
    else
        FAIL=$((FAIL + 1))
        printf 'GAGAL %s: pola tidak ditemukan di %s\n' "$3" "$2"
    fi
}

bersihkan() {
    if [ -n "$SERVER" ]; then
        kill "$SERVER" 2> /dev/null
    fi
    if [ -n "$WORK" ]; then
        rm -rf "$WORK"
    fi
}
trap bersihkan EXIT

# --- siapkan folder kerja terisolasi ------------------------------------
WORK=$(mktemp -d)
cp "$CONFIG/latihan.nft" "$CONFIG/kebijakan-port.txt" \
    "$CONFIG/audit-baseline.sh" "$CONFIG/validasi-ruleset.sh" \
    "$CONFIG/verifikasi-keamanan.sh" "$WORK/"
cd "$WORK" || exit 1

# --- uji 1: audit menghasilkan laporan deterministik dari fixture --------
mkdir -p audit-data/home/budi/.ssh audit-data/home/siti/.ssh \
    audit-data/home/deploy audit-data/root
cat > audit-data/services.txt <<'EOF'
nginx.service          loaded active running A high performance web server and reverse proxy
postgresql.service      loaded active running PostgreSQL RDBMS
redis-server.service    loaded active running Advanced key-value store
ssh.service             loaded active running OpenBSD Secure Shell server
EOF
cat > audit-data/port-terbuka.txt <<'EOF'
LISTEN 0      128        0.0.0.0:22           0.0.0.0:*
LISTEN 0      511        0.0.0.0:80           0.0.0.0:*
LISTEN 0      511        0.0.0.0:443          0.0.0.0:*
LISTEN 0      511        0.0.0.0:8080         0.0.0.0:*
LISTEN 0      24         127.0.0.1:5432       0.0.0.0:*
LISTEN 0      511        127.0.0.1:6379       0.0.0.0:*
EOF
cat > audit-data/passwd <<'EOF'
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
budi:x:1000:1000:Budi Santoso:/home/budi:/bin/bash
siti:x:1001:1001:Siti Rahma:/home/siti:/bin/bash
deploy:x:1002:1002:Deploy Bot:/home/deploy:/bin/bash
EOF
printf 'ssh-ed25519 AAAAC3NzaC1lZDI1...kunci-budi budi@laptop-budi\n' \
    > audit-data/home/budi/.ssh/authorized_keys
printf 'ssh-ed25519 AAAAC3NzaC1lZDI1...kunci-siti-1 siti@laptop-siti\nssh-ed25519 AAAAC3NzaC1lZDI1...kunci-siti-2 siti@telepon-siti\n' \
    > audit-data/home/siti/.ssh/authorized_keys
cat > audit-data/upgradable.txt <<'EOF'
Listing... Done
libssl3/stable 3.0.15-1~deb12u1 [upgradable from: 3.0.16-1~deb12u1]
openssl/stable 3.0.15-1~deb12u1 [upgradable from: 3.0.16-1~deb12u1]
redis-server/stable 5:7.0.15-1~deb12u1 [upgradable from: 5:7.0.16-1~deb12u1]
EOF

bash audit-baseline.sh audit-data > laporan-audit.txt
assert_eq 0 "$?" "audit menghasilkan laporan (exit 0)"
assert_ada '[1] SERVICE BERJALAN: 4' laporan-audit.txt \
    "laporan memuat 4 service berjalan"
assert_ada '[2] PORT TCP TERBUKA: 6' laporan-audit.txt \
    "laporan memuat 6 port terbuka"
assert_ada '0.0.0.0:8080    semua interface' laporan-audit.txt \
    "port 8080 tercatat terbuka ke luar"
assert_ada '2 akun shell tanpa authorized_keys' laporan-audit.txt \
    "temuan akun shell tanpa kunci"
assert_ada '3 paket tertinggal pembaruan' laporan-audit.txt \
    "temuan paket tertinggal"
bash audit-baseline.sh > /dev/null 2>&1
assert_eq 1 "$?" "audit tanpa argumen keluar 1"

# --- uji 2: ruleset valid lolos validator --------------------------------
bash validasi-ruleset.sh latihan.nft > hasil-validasi.txt
assert_eq 0 "$?" "ruleset latihan.nft lolos validasi"
jumlah=$(grep -c '^OK' hasil-validasi.txt)
assert_eq 10 "$jumlah" "sepuluh pemeriksaan validasi semuanya OK"

# --- uji 3: ruleset rusak ditolak validator -------------------------------
cat > rusak.nft <<'EOF'
# rusak.nft — ruleset longgar: semua koneksi diterima.
table inet filter {
    chain input {
        type filter hook input priority filter; policy accept;
        accept
    }
}
EOF
bash validasi-ruleset.sh rusak.nft > hasil-rusak.txt
assert_eq 9 "$?" "ruleset rusak ditolak (9 pemeriksaan gagal)"
assert_ada 'GAGAL kebijakan chain input: drop' hasil-rusak.txt \
    "policy yang salah dilaporkan"
assert_ada 'GAGAL tidak ada aturan accept tanpa syarat' hasil-rusak.txt \
    "accept tanpa syarat dilaporkan"
bash validasi-ruleset.sh tidak-ada.nft > /dev/null 2>&1
assert_eq 1 "$?" "berkas ruleset hilang keluar 1"

# --- uji 4: verifikasi PASS pada port sesuai kebijakan --------------------
printf 'server latihan firewall\n' > index.html
python3 -m http.server 8765 --bind 127.0.0.1 > /dev/null 2>&1 &
SERVER=$!
siap=0
for i in 1 2 3 4 5 6 7 8 9 10; do
    if curl -fsS -o /dev/null http://127.0.0.1:8765/ 2> /dev/null; then
        siap=1
        break
    fi
    sleep 0.2
done
assert_eq 1 "$siap" "server latihan siap di port 8765"

bash verifikasi-keamanan.sh latihan.nft kebijakan-port.txt \
    http://127.0.0.1:8765/ > hasil-verifikasi.txt
assert_eq 0 "$?" "verifikasi PASS keluar 0"
assert_ada 'HASIL: PASS' hasil-verifikasi.txt "laporan berakhir PASS"
assert_ada 'port ruleset sama dengan kebijakan' hasil-verifikasi.txt \
    "port ditandai sesuai kebijakan"
assert_ada 'menjawab (kode 200)' hasil-verifikasi.txt \
    "service ditandai merespons"

# --- uji 5: kebijakan berbeda -> port melenceng ----------------------------
grep -v '^443' kebijakan-port.txt > kebijakan-tanpa-443.txt
bash verifikasi-keamanan.sh latihan.nft kebijakan-tanpa-443.txt \
    http://127.0.0.1:8765/ > hasil-beda.txt
assert_eq 1 "$?" "kebijakan beda keluar 1"
assert_ada 'port ruleset berbeda dengan kebijakan' hasil-beda.txt \
    "perbedaan port dilaporkan"

# --- uji 6: ruleset rusak di tangan verifikasi -----------------------------
bash verifikasi-keamanan.sh rusak.nft kebijakan-port.txt \
    http://127.0.0.1:8765/ > hasil-verif-rusak.txt
assert_eq 2 "$?" "ruleset rusak + port melenceng keluar 2"
assert_ada 'ditolak validasi-ruleset.sh' hasil-verif-rusak.txt \
    "kegagalan validator dilaporkan"

# --- uji 7: service mati -> FAIL, port kembali bebas -------------------------
kill "$SERVER" 2> /dev/null
wait "$SERVER" 2> /dev/null
SERVER=""
bash verifikasi-keamanan.sh latihan.nft kebijakan-port.txt \
    http://127.0.0.1:8765/ > hasil-mati.txt
assert_eq 1 "$?" "service mati keluar 1"
assert_ada 'tidak menjawab' hasil-mati.txt "service mati dilaporkan"
jumlah=$(ss -tlnH '( sport = :8765 )' | grep -c .)
assert_eq 0 "$jumlah" "port 8765 kembali bebas setelah server mati"

# --- uji 8: pemakaian salah --------------------------------------------------
bash verifikasi-keamanan.sh latihan.nft > /dev/null 2>&1
assert_eq 4 "$?" "argumen kurang keluar 4"
bash verifikasi-keamanan.sh tidak-ada.nft kebijakan-port.txt \
    http://127.0.0.1:8765/ > /dev/null 2>&1
assert_eq 4 "$?" "berkas hilang keluar 4"

# --- ringkasan ----------------------------------------------------------------
if [ "$FAIL" -eq 0 ]; then
    printf 'PASSED: %d/%d test lulus\n' "$PASS" "$PASS"
else
    printf 'FAILED: %d/%d test lulus\n' "$PASS" "$((PASS + FAIL))"
fi
[ "$FAIL" -eq 0 ]
