#!/usr/bin/env bash # Jornada assíncrona ponta a ponta contra o compose, ENTRE PROCESSOS: # a web (um contêiner) enfileira no Redis, o worker (outro contêiner) processa, # e o download sai pela rota autenticada. É o que a suíte de testes não cobre: # lá o tick roda no mesmo processo. # # Roda contra docker-compose.yml, que sobe em ADAPTER_ENV=development com # ADAPTER_JOB_MODE=worker. Nenhuma credencial real é usada. set -euo pipefail BASE="${BASE:-http://127.0.0.1:3000}" JAR="$(mktemp)" ANA="aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa" ALPHA="11111111-1111-4111-8111-111111111111" TMP="$(mktemp -d)" trap 'rm -rf "$TMP" "$JAR"' EXIT jq_get() { python3 -c "import json,sys;d=json.load(sys.stdin);print(eval(sys.argv[1],{},{'d':d}))" "$1"; } echo "== 1. sessão local ==" curl -fsS -c "$JAR" -X POST -H 'Content-Type: application/json' \ -d "{\"userId\":\"$ANA\",\"workspaceId\":\"$ALPHA\"}" \ "$BASE/api/dev/session" > /dev/null echo "ok" echo "== 2. modelo ==" printf 'Nome,Valor\n' > "$TMP/modelo.csv" MODEL_ID=$(curl -fsS -b "$JAR" -X POST \ -F "name=Clientes CI" \ -F "requiredColumns=[\"Nome\",\"Valor\"]" \ -F "file=@$TMP/modelo.csv;type=text/csv" \ "$BASE/api/spreadsheet-adapter/models" | jq_get "d['model']['id']") echo "modelo=$MODEL_ID" echo "== 3. operação ==" JOB_ID=$(curl -fsS -b "$JAR" -X POST -H 'Content-Type: application/json' \ -d "{\"modelId\":\"$MODEL_ID\",\"idempotencyKey\":\"ci-job\"}" \ "$BASE/api/spreadsheet-adapter/jobs" | jq_get "d['job']['id']") echo "job=$JOB_ID" echo "== 4. fonte ==" printf 'Nome,Valor\nAna,10\nBruno,20\n' > "$TMP/fonte.csv" SOURCE_ID=$(curl -fsS -b "$JAR" -X POST \ -F "jobId=$JOB_ID" \ -F "file=@$TMP/fonte.csv;type=text/csv" \ "$BASE/api/spreadsheet-adapter/sources/validate" | jq_get "d['job']['sources'][0]['id']") echo "fonte=$SOURCE_ID" echo "== 5. mapeamento ==" curl -fsS -b "$JAR" -X PUT -H 'Content-Type: application/json' \ -d "{\"sourceId\":\"$SOURCE_ID\",\"mappings\":[{\"sourceColumn\":\"Nome\",\"modelColumn\":\"Nome\"},{\"sourceColumn\":\"Valor\",\"modelColumn\":\"Valor\"}]}" \ "$BASE/api/spreadsheet-adapter/jobs/$JOB_ID/mappings" > /dev/null echo "ok" echo "== 6. processar: a web só enfileira ==" STATUS=$(curl -fsS -b "$JAR" -X POST -H 'Content-Type: application/json' \ -d "{\"sourceId\":\"$SOURCE_ID\",\"idempotencyKey\":\"ci-proc\"}" \ "$BASE/api/spreadsheet-adapter/jobs/$JOB_ID/process" | jq_get "d['job']['status']") echo "status imediato=$STATUS" if [ "$STATUS" != "queued" ] && [ "$STATUS" != "processing" ]; then echo "ERRO: a web deveria enfileirar, não processar. status=$STATUS" exit 1 fi echo "== 7. aguardando o WORKER (outro contêiner) concluir ==" FINAL="" for i in $(seq 1 60); do FINAL=$(curl -fsS -b "$JAR" "$BASE/api/spreadsheet-adapter/jobs/$JOB_ID" | jq_get "d['job']['status']") echo " tentativa $i: $FINAL" [ "$FINAL" = "completed" ] && break [ "$FINAL" = "failed" ] && break sleep 2 done if [ "$FINAL" != "completed" ]; then echo "ERRO: o worker não concluiu. status=$FINAL" docker compose logs worker | tail -40 exit 1 fi echo "== 8. downloads ==" for fmt in xlsx csv pdf; do curl -fsS -b "$JAR" -o "$TMP/out.$fmt" \ "$BASE/api/spreadsheet-adapter/jobs/$JOB_ID/download?format=$fmt&sourceId=$SOURCE_ID" size=$(wc -c < "$TMP/out.$fmt") [ "$size" -gt 0 ] || { echo "ERRO: $fmt vazio"; exit 1; } echo " $fmt: $size bytes" done head -c 2 "$TMP/out.xlsx" | grep -q 'PK' || { echo "ERRO: xlsx não é zip"; exit 1; } head -c 4 "$TMP/out.pdf" | grep -q '%PDF' || { echo "ERRO: pdf inválido"; exit 1; } grep -q 'Ana' "$TMP/out.csv" || { echo "ERRO: csv sem os dados"; exit 1; } echo "== 9. o worker registrou o processamento ==" docker compose logs worker | tail -30 > "$TMP/worker.log" grep -q "\[worker\] pronto" "$TMP/worker.log" || { echo "ERRO: worker não reportou start"; exit 1; } echo "== 10. o modelo original não foi alterado ==" curl -fsS -b "$JAR" "$BASE/api/spreadsheet-adapter/models" | grep -q "Clientes CI" echo "" echo "JORNADA ASSÍNCRONA ENTRE PROCESSOS: OK"