O patch é pequeno, os testes estão verdes e uma reserva de quatro lugares é aceita quando só há três. Até a contagem restante parece razoável: zero. O problema fica claro ao abrir os testes no mesmo diff. Antes exigiam um erro; agora exigem apenas resultado não negativo.

Suponha que pedimos à IA para simplificar o cálculo de lugares sem mudar quais pedidos são aceitos. O patch e os testes enfraquecidos foram criados para este tutorial e rodam localmente com o executor de testes do Node.

Comece pela regra protegida pelos testes antigos

Neste exemplo, available já foi validado antes: é um inteiro seguro não negativo. A função recebe a quantidade solicitada e retorna exatamente os lugares restantes ou lança RangeError. Aceita apenas inteiros numéricos seguros de 1 até available. Não arredonda, converte strings nem atende reservas parcialmente.

export function remainingSeats(available, requested) {
  if (
    !Number.isSafeInteger(requested) ||
    requested < 1 ||
    requested > available
  ) {
    throw new RangeError(
      'requested seats must be an integer from 1 to available',
    );
  }
  return available - requested;
}

Com três lugares disponíveis, os exemplos podem ser conferidos à mão:

Solicitado Comportamento exigido Motivo
1, 2, 3 Retornar 2, 1, 0, respectivamente Aceitar o pedido inteiro
0, -1, 1.5 Lançar RangeError Sem reservas nulas, negativas ou fracionárias
4 Lançar RangeError Não é possível atender o pedido inteiro
'2', NaN, Infinity Lançar RangeError A quantidade deve ser um inteiro numérico seguro

Sem lugares disponíveis, pedir um também lança erro. Retornar zero já é um sucesso válido para uma reserva exata, então zero não pode servir também como sinal de erro.

Em uma revisão real, estabeleça as regras pelo comportamento solicitado, chamadores existentes e testes da base. Asserções antigas podem estar erradas, mas alterá-las exige explicar qual comportamento muda. Aqui, a tarefa pede explicitamente preservar as rejeições.

Leia as linhas removidas nos dois arquivos

A proposta substitui a validação por limitação do valor:

 export function remainingSeats(available, requested) {
-  if (
-    !Number.isSafeInteger(requested) ||
-    requested < 1 ||
-    requested > available
-  ) {
-    throw new RangeError(
-      'requested seats must be an integer from 1 to available',
-    );
-  }
-  return available - requested;
+  const count = Math.min(available, Math.max(1, Math.trunc(requested)));
+  return available - count;
 }

Para (3, 4), o count local vira 3 e o retorno é 0. Para (3, 1.5), vira 1 e a função retorna 2. O patch mudou quais entradas têm sucesso, embora pedidos comuns ainda retornem corretamente.

Agora leia o diff dos testes. Três seguem este padrão:

-  test('rejects more seats than available', () => {
-    assert.throws(() => remainingSeats(3, 4), RangeError);
+  test('handles more seats than available', () => {
+    assert.ok(remainingSeats(3, 4) >= 0);
   });

A entrada não mudou; o resultado exigido, sim. A nova asserção executa a função diretamente, portanto falha se ela ainda lança erro. Uma versão que aceita silenciosamente o pedido excessivo agora passa. Trocar “rejects” por “handles” torna a mudança menos visível no relatório.

Os casos zero e fracionário também perdem as asserções de exceção. A suíte continua com cinco testes; os dois sucessos comuns não mudam. A contagem não revela quais comportamentos ainda são verificados.

Em um patch real, leia mudanças da implementação e dos testes juntas:

git diff BASE HEAD -- src/remaining-seats.mjs test/remaining-seats.test.mjs
git show BASE:test/remaining-seats.test.mjs

Substitua revisões e caminhos pelos do seu repositório. Registre as revisões exatas, especialmente se também lê arquivos de uma árvore de trabalho que avançou. O ai-style.patch do download permite examinar o exemplo sem criar histórico Git.

Ao encontrar uma verificação removida, traduza-a em uma entrada: remover requested > available sugere (3, 4). Ao encontrar uma asserção removida, diga o que exigia: a entrada precisava lançar erro antes de o chamador continuar. Esse par é mais útil do que perguntar se o código parece defensivo.

Abra o chamador que não tem linhas alteradas

O chamador abaixo é compartilhado, sem mudanças, pelas duas versões. A função auxiliar e uma função save síncrona são injetadas para manter o exemplo inteiro em memória:

export function createBooking(remainingSeats, save) {
  return function book(available, requested) {
    const remaining = remainingSeats(available, requested);
    const reservation = { seats: requested, remaining };
    save(reservation);
    return reservation;
  };
}

Há duas quantidades. remaining vem da função; seats ainda usa o requested original. A função antiga aceitava exatamente essa quantidade ou lançava antes de save. Após o patch, a função usa três silenciosamente enquanto o chamador registra quatro.

O mesmo chamador com (3, 4) produz:

Função auxiliar Resultado visto pelo chamador externo Chamadas de gravação
Original RangeError Nenhuma
Proposta { seats: 4, remaining: 0 } Uma, com o mesmo objeto

O exemplo observa uma reserva incorreta em memória e uma tentativa de gravação. Não demonstra uma transação de banco, pagamento ou reserva real de cliente.

Busque além da lista de arquivos alterados para encontrar a dependência no seu projeto:

rg -n 'remainingSeats|createBooking' src test

Siga imports para confirmar qual função o chamador usa. Examine o que acontece depois: salva a entrada original, constrói uma resposta ou captura um erro específico? Se não for fácil buscar referências pelo nome, use o servidor de linguagem e confira wrappers. O guia de leitura explica como combinar esses caminhos.

Preserve a verificação de erro e observe a gravação separadamente

Restaurar os três testes originais de rejeição já detecta o patch. Adicione um teste do chamador para mostrar por que rejeitar importa:

const writes = [];
const book = createBooking(remainingSeats, (row) => writes.push(row));
assert.throws(() => book(3, 4), RangeError);
assert.deepEqual(writes, []);

A função selecionada é a original ou a proposta real. Apenas save é substituída por um registrador em array. Substituir a função por um stub que sempre lança ignoraria justamente a mudança revisada.

Esse teste compacto para na primeira asserção que falha. A suíte do download também testa “nenhuma tentativa de gravação” separadamente, permitindo relatar ambas as falhas em uma execução:

const writes = [];
const book = createBooking(remainingSeats, (row) => writes.push(row));
try {
  book(3, 4);
} catch {
  // A separate test checks that the error is RangeError.
}
assert.deepEqual(writes, []);

O catch vazio fica restrito a esse teste de observação; permite examinar gravações mesmo quando a reserva lança. Sozinho, seria fraco: qualquer erro antes de salvar o faria passar. A suíte exige RangeError separadamente, verifica que uma reserva válida é salva exatamente uma vez com valores corretos e que falhas de gravação chegam ao chamador. Não retornar nada ou rejeitar todo pedido não satisfaz esses testes.

A suíte de verificação tem 15 testes: três resultados exatos de sucesso, oito entradas inválidas incluindo lotação esgotada e quatro testes do chamador. Os valores esperados vêm das regras acima, não de copiar a expressão de limitação proposta para o teste.

Execute as mesmas expectativas nas duas implementações

Baixe o exemplo completo, extraia e abra um terminal em review-ai-generated-tests. Use Node.js 22 ou posterior; não há instalação, rede nem configuração de provedor.

node run.mjs baseline
node run.mjs proposed-original
node run.mjs proposed-weak
node run.mjs verify-baseline
node run.mjs verify-proposed

Cada comando registra testes com node:test e sai com o resultado. Execute separadamente: o segundo e o quinto falham deliberadamente. Não una a sequência com &&, que pararia na primeira falha esperada.

Estes são os resultados observados com Node.js v22.22.1 no macOS arm64:

Modo Implementação / asserções Passaram Falharam Código de saída
baseline Original / originais 5 0 0
proposed-original Proposta / originais 2 3 1
proposed-weak Proposta / enfraquecidas 5 0 0
verify-baseline Original / verificação 15 0 0
verify-proposed Proposta / verificação 5 10 1

A proposta relata Missing expected exception (RangeError).. O teste separado de gravação relata um array com { seats: 4, remaining: 0 } onde se exigia array vazio. node show-caller.mjs imprime ambos os resultados sem afirmar que algum está correto.

Para uma verificação padrão verde, execute node --test run.mjs; ele seleciona verify-baseline. Isso difere de um teste de reprodução que passa ao afirmar o resultado quebrado. Aqui, os testes exigem comportamento correto, e a proposta realmente falha. Os comandos não usam recursos shell específicos do sistema; a execução foi no macOS, não em uma matriz Linux/Windows.

Para verificar mudanças da IA no seu projeto, preserve as asserções relevantes da base e execute contra a proposta; depois adicione o menor caso de chamador sugerido pelo diff. Se ambas as implementações passarem no novo teste, examine entradas e asserções antes de considerá-lo cobertura de regressão.

Transforme a falha em uma decisão de revisão

Um comentário pode ser concreto:

Substituir rejeição por limitação faz remainingSeats(3, 4) retornar zero. O createBooking inalterado salva { seats: 4, remaining: 0 }. Os três testes editados antes exigiam RangeError; as novas asserções de não negatividade aceitam o comportamento. Preserve as rejeições e verifique que uma reserva excessiva nunca chama save. A versão original passa nos 15 testes; o patch falha em 10.

Para a tarefa declarada, mantenha a validação original e restaure as asserções de erro. Se atender parcialmente for um requisito novo intencional, defina como a quantidade aceita chega ao chamador e o que o usuário confirma; altere implementação, chamadores e testes juntos. Mudar silenciosamente a quantidade local deixa essas decisões em aberto.

O exemplo cobre validação síncrona e chamada a uma gravação síncrona. Presume available válido e não cobre reservas concorrentes, atomicidade do banco, novas tentativas ou autorização. Isso exige testes nos pontos reais de integração.

O Yenpo ajuda a ler implementação, testes e chamadores e a comparar versões commitadas. Consulte os requisitos atuais no guia de instalação; o exemplo Node e o método não dependem do Yenpo. Em uma comparação Git, confira as revisões e pergunte:

Encontre as regras de aceitação removidas por este patch e as asserções alteradas junto delas. Siga os chamadores inalterados que dependem desses erros. Para cada preocupação, dê uma entrada concreta e um resultado esperado, com referências. Separe regressões de comportamentos já presentes na base.