Files
linearvoid/scripts/validate-edge-cache.sh
T
zwitschi 55a128dc3a
CI / test (push) Failing after 11s
CI / docker (push) Skipped
fix: flat local HLS paths for nginx compat + edge cache validation script
- Revert local HLS to flat /app/hls/seg-{seq}.m4s (nginx serves from this dir)
- runID only used in R2 keys (live/{runID}/seg-{seq}.m4s)
- This fixes 404 on linearvoid.tv/hls/playlist.m3u8 caused by r-{runID} subdir
- Added scripts/validate-edge-cache.sh: MISS→HIT, immutable headers, manifest freshness
- Phase 4 marked complete. Cleaned up LEGACY streaming gaps in TODO.md
2026-08-10 18:01:55 +02:00

142 lines
4.5 KiB
Bash
Executable File

#!/bin/bash
# Edge cache hit/miss validation (Phase 4 — STREAMING.md cache contract)
# Verifies: .m4s segments go MISS→HIT, init.mp4 is cached, .m3u8 refreshes quickly
# Usage: ./scripts/validate-edge-cache.sh [base_url]
# Default base_url: https://media.linearvoid.tv
set -euo pipefail
BASE_URL="${1:-https://media.linearvoid.tv}"
TIMEOUT=10
FAILURES=0
PASSES=0
green() { echo -e "\033[32m$1\033[0m"; }
red() { echo -e "\033[31m$1\033[0m"; }
pass() { PASSES=$((PASSES + 1)); green " ✓ PASS: $1"; }
fail() { FAILURES=$((FAILURES + 1)); red " ✗ FAIL: $1"; }
# Fetch a header from a URL
fetch_header() {
local url="$1" header="$2"
curl -sI --connect-timeout "$TIMEOUT" --max-time "$TIMEOUT" "$url" 2>/dev/null | \
grep -i "^${header}:" | sed 's/.*: //' | tr -d '\r' || echo ""
}
echo "=== Edge Cache Validation ==="
echo "Target: $BASE_URL"
echo ""
# 1. Get current playlist manifest and extract a segment name
echo "--- Test 1: Manifest freshness ---"
MANIFEST_URL="$BASE_URL/hls/playlist.m3u8"
MANIFEST=$(curl -s --connect-timeout "$TIMEOUT" --max-time "$TIMEOUT" "$MANIFEST_URL" 2>/dev/null || echo "")
if [[ -z "$MANIFEST" ]]; then
fail "Manifest is empty or unreachable"
else
if echo "$MANIFEST" | grep -q "#EXTM3U"; then
pass "Manifest is valid M3U8"
else
fail "Manifest missing #EXTM3U header"
fi
fi
SEGMENT=$(echo "$MANIFEST" | grep "\.m4s" | tail -1 | tr -d '\r')
if [[ -z "$SEGMENT" ]]; then
fail "No .m4s segment found in manifest"
else
pass "Found segment: $SEGMENT"
fi
# 2. Test manifest Cache-Control
MANIFEST_CC=$(fetch_header "$MANIFEST_URL" "Cache-Control")
echo ""
echo "--- Test 2: Manifest Cache-Control ---"
if echo "$MANIFEST_CC" | grep -q "must-revalidate"; then
pass "Manifest has must-revalidate ($MANIFEST_CC)"
else
fail "Manifest missing must-revalidate ($MANIFEST_CC)"
fi
if echo "$MANIFEST_CC" | grep -q "max-age"; then
pass "Manifest has max-age ($MANIFEST_CC)"
else
fail "Manifest missing max-age ($MANIFEST_CC)"
fi
# 3. Test segment cache (MISS → HIT)
if [[ -n "$SEGMENT" ]]; then
SEGMENT_URL="$BASE_URL/hls/$SEGMENT"
echo ""
echo "--- Test 3: Segment cache MISS→HIT ---"
FIRST_STATUS=$(fetch_header "$SEGMENT_URL" "CF-Cache-Status")
echo " First request: CF-Cache-Status=$FIRST_STATUS"
sleep 1
SECOND_STATUS=$(fetch_header "$SEGMENT_URL" "CF-Cache-Status")
echo " Second request: CF-Cache-Status=$SECOND_STATUS"
if [[ "$SECOND_STATUS" == "HIT" ]]; then
pass "Segment cache HIT on second request (was $FIRST_STATUS)"
elif [[ "$FIRST_STATUS" == "HIT" ]]; then
pass "Segment already cached (HIT on first request)"
else
fail "Segment not cached: first=$FIRST_STATUS, second=$SECOND_STATUS"
fi
fi
# 4. Test segment Cache-Control (immutable)
if [[ -n "$SEGMENT" ]]; then
echo ""
echo "--- Test 4: Segment immutable cache ---"
SEGMENT_CC=$(fetch_header "$SEGMENT_URL" "Cache-Control")
if echo "$SEGMENT_CC" | grep -q "immutable"; then
pass "Segment has immutable ($SEGMENT_CC)"
else
fail "Segment missing immutable ($SEGMENT_CC)"
fi
if echo "$SEGMENT_CC" | grep -q "max-age=31536000"; then
pass "Segment has 1-year max-age ($SEGMENT_CC)"
else
fail "Segment missing 1-year max-age ($SEGMENT_CC)"
fi
fi
# 5. Test init.mp4 cache
echo ""
echo "--- Test 5: Init segment cache ---"
INIT_URL="$BASE_URL/hls/init.mp4"
INIT_STATUS=$(fetch_header "$INIT_URL" "CF-Cache-Status")
INIT_CC=$(fetch_header "$INIT_URL" "Cache-Control")
if [[ "$INIT_STATUS" == "HIT" ]]; then
pass "Init segment cached (CF-Cache-Status=$INIT_STATUS, CC=$INIT_CC)"
else
fail "Init segment not cached: $INIT_STATUS"
fi
# 6. Test manifest refresh (should NOT be cached long-term)
echo ""
echo "--- Test 6: Manifest refresh rate ---"
MANIFEST_CF=$(fetch_header "$MANIFEST_URL" "CF-Cache-Status")
echo " Manifest CF-Cache-Status: $MANIFEST_CF"
# DYNAMIC or BYPASS is correct for live manifests. HIT with long TTL is wrong.
if [[ "$MANIFEST_CF" == "DYNAMIC" ]] || [[ "$MANIFEST_CF" == "BYPASS" ]] || [[ "$MANIFEST_CF" == "MISS" ]] || [[ "$MANIFEST_CF" == "REVALIDATED" ]]; then
pass "Manifest not long-term cached ($MANIFEST_CF)"
elif [[ "$MANIFEST_CF" == "HIT" ]]; then
fail "Manifest is HIT — may be stale (check TTL)"
else
pass "Manifest status: $MANIFEST_CF (no HIT = acceptable)"
fi
# Summary
echo ""
echo "=== Results: $PASSES passed, $FAILURES failed ==="
if [[ $FAILURES -gt 0 ]]; then
exit 1
fi
exit 0