#!/bin/sh
#
# A carga do corte para na tabela que a origem nao tem, com o nome dela? (D462)
#
#     sh scripts/ensaiar-carga-que-para.sh
#     sh scripts/ensaiar-carga-que-para.sh <copia do scripts/migrar-dados.sh>
#
# Sai com 0 quando todo caso abaixo da o esperado; 1 quando algum nao da, e diz
# qual; 2 quando nao consegue medir (falta `mktemp` ou `cmp`, o roteiro nao tem
# tabela nenhuma, o duble nao vem antes do `docker` de verdade, ou ouviu um
# pedido que nao sabe responder). O argumento aponta o ensaio para uma COPIA do
# roteiro -- e como se falsifica sem tocar no de verdade.
#
# ---------------------------------------------------------------------------
# POR QUE ISTO EXISTE (08/11/2026, a fatia 128; o D462)
#
# Cada tabela da carga sai do MySQL pelo `mysql_` e entra no PostgreSQL pelo
# `\copy`, e a conferencia aceita a linha `COPY <n>` como o sucesso. Ate esta
# fatia o `mysql_` jogava fora o erro do cliente (`2>/dev/null`) e o codigo de
# saida dele se perdia no cano: a tabela ausente na origem virava entrada
# vazia, o PostgreSQL respondia `COPY 0`, e a carga a dava por carregada, sem
# aviso. Isso morde no ensaio sobre uma copia sem a migracao do passo 0, e em
# qualquer tabela que o passo 0 esquecer.
#
# ---------------------------------------------------------------------------
# COMO -- O ROTEIRO GERADO RODA INTEIRO, E NENHUM BANCO E TOCADO
#
# Uma copia do roteiro roda com `sh`, num diretorio temporario, com um `docker`
# de mentira no comeco do PATH: o pedido ao MySQL roda o texto do proprio
# roteiro (o `sh -c` do `mysql_`, com o filtro do retorno de carro e o arquivo
# do codigo de saida, os dois em `/tmp` trocado pelo diretorio do ensaio) sobre
# um `mysql` de mentira; o pedido ao PostgreSQL le a entrada e responde
# `COPY <linhas>` -- o que o `psql` de verdade responde. O projeto do compose e
# um nome que nao existe, os bancos tambem, e o duble nunca chama o `docker` de
# verdade: nada sai do diretorio. Os casos:
#
#   a carga inteira, verde         sai 0, e cada tabela do roteiro e lida no
#                                  MySQL e copiada uma vez
#   a tabela que a origem nao tem  sai 1 NELA: a saida diz o nome e o erro do
#                                  cliente, e nenhuma tabela depois dela e lida
#   a leitura que morre no meio    sai 1 nela: o que veio antes do erro nao vale
#                                  como carga
#   o COPY que falha no PostgreSQL sai 1 nela (a conferencia de antes segue)
#
# O QUE ELE NAO AFIRMA: nada do MySQL nem do PostgreSQL de verdade. O codigo de
# saida do cliente `mysql` 8.4 na tabela ausente (1, com "ERROR 1146 ... doesn't
# exist" no erro padrao) e o MYSQL_PWD sem aviso foram medidos no conteiner
# daqui em 08/11/2026, com um SELECT; a carga de verdade nao rodou (ela nunca
# roda fora da janela e dos clones do ensaio geral).

set -u

cd "$(dirname "$0")/.." || exit 2

nao_medi() { echo "NAO CONSEGUI MEDIR: $1" >&2; exit 2; }

ROTEIRO="${1:-scripts/migrar-dados.sh}"
[ -r "$ROTEIRO" ] || nao_medi "nao li $ROTEIRO"

command -v cmp > /dev/null 2>&1 || nao_medi "sem cmp."

T=$(mktemp -d 2>/dev/null) || nao_medi "mktemp -d falhou."
trap 'rm -rf "$T"' EXIT INT TERM
T=$(cd "$T" && pwd -P)

PROJ="ensaio-de-mentira-$$"
mkdir -p "$T/bin" "$T/bin-mysql"
: > "$T/nao-entendi.txt"

# As tabelas, na ordem do roteiro: e por elas que os casos escolhem onde falhar.
sed -n 's/^conferir "\([a-z0-9_]*\)" .*/\1/p' "$ROTEIRO" > "$T/tabelas.txt"
TOTAL=$(wc -l < "$T/tabelas.txt" | tr -d ' ')
[ "$TOTAL" -ge 5 ] || nao_medi "o roteiro tem $TOTAL tabela(s): este ensaio nao sabe ler a carga dele."
QUINTA=$(sed -n 5p "$T/tabelas.txt")
SETIMA=$(sed -n 7p "$T/tabelas.txt")
NONA=$(sed -n 9p "$T/tabelas.txt")
cp "$ROTEIRO" "$T/roteiro.sh"

# ---------------------------------------------------------------------------
# Os dubles.
# ---------------------------------------------------------------------------

cat > "$T/bin/docker" <<'FIM'
#!/bin/sh
# O duble do `docker` deste ensaio. Responde o minimo e nunca chama o de verdade.
nao_entendi() { printf '%s\n' "$*" >> "$ENSAIO_NAO_ENTENDI"; exit 0; }

[ "${1:-}" = compose ] && [ "${2:-}" = -p ] && [ "${3:-}" = "$ENSAIO_PROJETO" ] &&
    [ "${4:-}" = exec ] && [ "${5:-}" = -T ] || nao_entendi "docker $*"
servico=$6
shift 6

case "$servico" in
mysql)
    [ "${1:-}" = sh ] && [ "${2:-}" = -c ] || nao_entendi "mysql $*"
    # O TEXTO DO PROPRIO ROTEIRO, com o /tmp do conteiner no diretorio do ensaio.
    texto=$(printf '%s' "$3" | sed "s#/tmp/#$ENSAIO_TMP/#g")
    shift 3
    PATH="$ENSAIO_BIN_MYSQL:$PATH" sh -c "$texto" "$@"
    exit $?
    ;;
postgres)
    [ "${1:-}" = psql ] || nao_entendi "postgres $*"
    copia=''
    for a in "$@"; do
        case "$a" in
            '\copy '*) copia=$(printf '%s' "$a" | sed -n 's/^\\copy \([a-z0-9_]*\) .*/\1/p') ;;
        esac
    done
    linhas=$(wc -l | tr -d ' ')
    [ -n "$copia" ] || exit 0
    echo "copy|$copia|$linhas" >> "$ENSAIO_EVENTOS"
    if [ "$copia" = "${ENSAIO_COPY_FALHA:-}" ]; then
        printf 'ERROR:  duplicate key value violates unique constraint (de mentira)\nCONTEXT:  COPY %s, line 1\n' "$copia"
        exit 1
    fi
    echo "SET"
    echo "SET"
    echo "COPY $linhas"
    exit 0
    ;;
esac
nao_entendi "servico $servico: $*"
FIM

cat > "$T/bin-mysql/mysql" <<'FIM'
#!/bin/sh
# O `mysql` de mentira: le o SELECT da entrada e responde uma linha por tabela,
# ou o erro do cliente de verdade quando o caso pede.
for a in "$@"; do banco=$a; done
sql=$(cat)
tabela=$(printf '%s' "$sql" | sed -n 's/.* FROM `\([a-z0-9_]*\)`;.*/\1/p')
echo "mysql|$banco|$tabela" >> "$ENSAIO_EVENTOS"
[ -n "${MYSQL_PWD:-}" ] || echo "mysql: [Warning] Using a password on the command line interface can be insecure." >&2
if [ "$tabela" = "${ENSAIO_TABELA_AUSENTE:-}" ]; then
    echo "ERROR 1146 (42S02) at line 1: Table '$banco.$tabela' doesn't exist" >&2
    exit 1
fi
printf '1\tde mentira\n'
if [ "$tabela" = "${ENSAIO_LEITURA_MORRE:-}" ]; then
    echo "ERROR 2013 (HY000) at line 1: Lost connection to MySQL server during query" >&2
    exit 1
fi
exit 0
FIM

chmod +x "$T/bin/docker" "$T/bin-mysql/mysql"

[ "$(PATH="$T/bin:$PATH" command -v docker)" = "$T/bin/docker" ] || nao_medi "o duble do docker nao vem antes do de verdade no PATH."

# rodar <caso> [VAR=valor ...]: o roteiro inteiro, com os dubles; o codigo em $codigo.
rodar() {
    c="$T/caso-$1"
    shift
    mkdir -p "$c/tmp"
    : > "$c/eventos.txt"
    (cd "$c" && env PATH="$T/bin:$PATH" ENSAIO_EVENTOS="$c/eventos.txt" ENSAIO_TMP="$c/tmp" \
        ENSAIO_NAO_ENTENDI="$T/nao-entendi.txt" ENSAIO_PROJETO="$PROJ" ENSAIO_BIN_MYSQL="$T/bin-mysql" \
        PROJETO="$PROJ" BANCO_MYSQL=ens_origem_de_mentira BANCO_PG=ens_destino_de_mentira USUARIO_PG=ens_dono \
        "$@" sh "$T/roteiro.sh") < /dev/null > "$c/saida.txt" 2>&1
    codigo=$?
}

# ---------------------------------------------------------------------------
# As conferencias.
# ---------------------------------------------------------------------------

falhas=0
casos=0
caso=''
falhou() { echo "  - $caso: $1"; falhas=$((falhas + 1)); }
saiu() { [ "$codigo" = "$1" ] || falhou "saiu $codigo, e devia sair $1 ($(tail -n 1 "$c/saida.txt"))"; }
lidas() { grep -c '^mysql|' "$c/eventos.txt"; }
copiadas() { grep -c '^copy|' "$c/eventos.txt"; }
diz() { grep -qF -- "$2" "$c/saida.txt" || falhou "$1 (a saida nao diz: $2)"; }
nao_diz() { ! grep -qF -- "$2" "$c/saida.txt" || falhou "$1 (a saida diz: $2)"; }

# 1. A carga inteira, verde.
casos=$((casos + 1)); caso='a carga inteira, verde'
rodar verde
saiu 0
[ "$(lidas)" = "$TOTAL" ] || falhou "cada tabela e lida uma vez no MySQL: $(lidas) de $TOTAL"
[ "$(copiadas)" = "$TOTAL" ] || falhou "cada tabela e copiada uma vez: $(copiadas) de $TOTAL"
nao_diz 'nenhuma tabela falha' 'FALHOU'
nao_diz 'o aviso da senha na linha de comando nao aparece' 'Using a password'
grep -q "^mysql|ens_origem_de_mentira|$QUINTA\$" "$c/eventos.txt" || falhou 'a leitura vai ao banco de BANCO_MYSQL'

# 2. A tabela que a origem nao tem para a carga nela, com o nome.
casos=$((casos + 1)); caso='a tabela que a origem nao tem'
rodar ausente ENSAIO_TABELA_AUSENTE="$QUINTA"
saiu 1
diz 'a saida diz que a leitura da tabela falhou' "A leitura de $QUINTA no MySQL falhou"
diz 'e mostra o erro do cliente mysql, com o nome' "Table 'ens_origem_de_mentira.$QUINTA' doesn't exist"
[ "$(lidas)" = 5 ] || falhou "nenhuma tabela depois dela e lida: $(lidas) lidas, e deviam ser 5"
grep -q "^copy|$QUINTA|0\$" "$c/eventos.txt" || falhou 'o COPY dela (o 0 que passava por sucesso) chegou ao PostgreSQL'
nao_diz 'a carga nao segue para as sequencias' 'Avancando as sequencias'

# 3. A leitura que morre no meio: o que veio antes do erro nao vale como carga.
casos=$((casos + 1)); caso='a leitura que morre no meio'
rodar morre ENSAIO_LEITURA_MORRE="$SETIMA"
saiu 1
diz 'a saida diz que a leitura da tabela falhou' "A leitura de $SETIMA no MySQL falhou"
diz 'e mostra o erro do cliente' 'Lost connection to MySQL server during query'
[ "$(lidas)" = 7 ] || falhou "nenhuma tabela depois dela e lida: $(lidas) lidas, e deviam ser 7"

# 4. O COPY que falha no PostgreSQL: a conferencia de antes continua.
casos=$((casos + 1)); caso='o COPY que falha no PostgreSQL'
rodar copy ENSAIO_COPY_FALHA="$NONA"
saiu 1
diz 'a saida diz que a carga da tabela nao completou' "A carga de $NONA nao completou"
[ "$(lidas)" = 9 ] || falhou "nenhuma tabela depois dela e lida: $(lidas) lidas, e deviam ser 9"

# ---------------------------------------------------------------------------

if [ -s "$T/nao-entendi.txt" ]; then
    echo "NAO CONSEGUI MEDIR: o duble ouviu pedidos que nao sabe responder:" >&2
    sort -u "$T/nao-entendi.txt" | head -5 | sed 's/^/    /' >&2
    exit 2
fi

if [ "$falhas" -gt 0 ]; then
    echo "O ENSAIO DA CARGA QUE PARA FALHOU: $falhas conferencia(s) em $casos casos."
    exit 1
fi

echo "A carga do corte para na tabela que falta: $casos casos, $TOTAL tabelas, todos como deviam."
exit 0
