#!/usr/bin/env bash # Integración local para POST /wp-json/fea/v1/subir-audio (#222). # Requiere FEA_TEST_URL, FEA_TEST_AUTH y FEA_TEST_POST_ID (post temporal). set -euo pipefail : "${FEA_TEST_URL:?Falta FEA_TEST_URL}" : "${FEA_TEST_AUTH:?Falta FEA_TEST_AUTH}" : "${FEA_TEST_POST_ID:?Falta FEA_TEST_POST_ID}" endpoint="${FEA_TEST_URL%/}/wp-json/fea/v1/subir-audio" tmpdir="$(mktemp -d)" trap 'rm -rf "$tmpdir"' EXIT # MP3 pequeño y válido (silencio MPEG-1 Layer III), sin depender de ffmpeg. printf '\377\373\220\144' > "$tmpdir/audio.mp3" dd if=/dev/zero bs=1 count=413 >> "$tmpdir/audio.mp3" 2>/dev/null printf 'no es un mp3' > "$tmpdir/not-audio.txt" # Un byte por encima del límite de aplicación de 25 MiB. Se comprueba antes # del tipo de fichero, por lo que puede ser disperso y no necesita ser audio. truncate -s 26214401 "$tmpdir/too-large.mp3" assert_status() { local expected="$1" actual="$2" label="$3" response_file="$4" if [[ "$actual" != "$expected" ]]; then echo "FAIL: $label — esperado HTTP $expected, recibido $actual" >&2 cat "$response_file" >&2 || true exit 1 fi } status="$(curl -sS -o "$tmpdir/unauth.json" -w '%{http_code}' -X POST "$endpoint")" assert_status 401 "$status" 'llamada sin autenticar' "$tmpdir/unauth.json" status="$(curl -sS -o "$tmpdir/no-post.json" -w '%{http_code}' -u "$FEA_TEST_AUTH" -X POST "$endpoint")" assert_status 400 "$status" 'falta post_id' "$tmpdir/no-post.json" status="$(curl -sS -o "$tmpdir/no-audio.json" -w '%{http_code}' -u "$FEA_TEST_AUTH" -F "post_id=$FEA_TEST_POST_ID" "$endpoint")" assert_status 400 "$status" 'falta audio' "$tmpdir/no-audio.json" status="$(curl -sS -o "$tmpdir/too-large.json" -w '%{http_code}' -u "$FEA_TEST_AUTH" -F "post_id=$FEA_TEST_POST_ID" -F "audio=@$tmpdir/too-large.mp3;type=audio/mpeg" "$endpoint")" assert_status 400 "$status" 'audio por encima de 25 MiB' "$tmpdir/too-large.json" status="$(curl -sS -o "$tmpdir/wrong-type.json" -w '%{http_code}' -u "$FEA_TEST_AUTH" -F "post_id=$FEA_TEST_POST_ID" -F "audio=@$tmpdir/not-audio.txt;type=text/plain" "$endpoint")" assert_status 400 "$status" 'extensión o mime no MP3' "$tmpdir/wrong-type.json" status="$(curl -sS -o "$tmpdir/no-such-post.json" -w '%{http_code}' -u "$FEA_TEST_AUTH" -F 'post_id=999999999' -F "audio=@$tmpdir/audio.mp3;type=audio/mpeg" "$endpoint")" assert_status 404 "$status" 'post inexistente' "$tmpdir/no-such-post.json" status="$(curl -sS -o "$tmpdir/success.json" -w '%{http_code}' -u "$FEA_TEST_AUTH" -F "post_id=$FEA_TEST_POST_ID" -F 'voice_id=NicoFeadulta2026' -F "audio=@$tmpdir/audio.mp3;type=audio/mpeg" "$endpoint")" assert_status 201 "$status" 'subida válida' "$tmpdir/success.json" python3 - "$tmpdir/success.json" "$FEA_TEST_POST_ID" "$tmpdir/audio.mp3" <<'PY' import hashlib, json, sys payload = json.load(open(sys.argv[1])) assert int(payload['post_id']) == int(sys.argv[2]), payload assert payload['audio_url'].endswith(f'/wp-content/uploads/tts/{sys.argv[2]}.mp3'), payload assert payload['voice_id'] == 'NicoFeadulta2026', payload assert payload['sha256'] == hashlib.sha256(open(sys.argv[3], 'rb').read()).hexdigest(), payload assert 'previous_audio' in payload, payload print('PASS: audio subido', payload['audio_url']) PY # La siguiente subida del mismo post debe exponer el estado anterior, para que # el cliente pueda restaurarlo si su flujo posterior falla. status="$(curl -sS -o "$tmpdir/replaced.json" -w '%{http_code}' -u "$FEA_TEST_AUTH" -F "post_id=$FEA_TEST_POST_ID" -F 'voice_id=NicoFeadulta2026' -F "audio=@$tmpdir/audio.mp3;type=audio/mpeg" "$endpoint")" assert_status 201 "$status" 'reemplazo de audio' "$tmpdir/replaced.json" python3 - "$tmpdir/replaced.json" "$FEA_TEST_POST_ID" <<'PY' import json, sys payload = json.load(open(sys.argv[1])) assert payload['previous_audio']['url'].endswith(f'/wp-content/uploads/tts/{sys.argv[2]}.mp3'), payload assert payload['previous_audio']['done'] == '1', payload print('PASS: estado anterior devuelto') PY