Merge pull request #1934 from webtecnica/fix/1895-busca-inbox-parentese

fix(inbox): o piso da busca sai o parêntese da régua, e a lista inteira deixa de voltar (#1895)
This commit is contained in:
Rafael Melgaço
2026-09-29 13:21:17 -03:00
committed by GitHub
3 changed files with 54 additions and 1 deletions
@@ -0,0 +1,9 @@
---
impacto: nada_mudou
secao: corrigido
titulo: A busca da caixa de entrada para de consultar o banco com um termo feito só de parênteses
---
Um termo como `()`, `((` ou `(a` passava pelo tamanho mínimo da busca da caixa de entrada: o parêntese contava como letra, mas na consulta vira curinga, e a busca casava todas as conversas. Era uma consulta cara que não filtrava nada. Agora o tamanho mínimo é medido sem os parênteses, com a mesma régua que a busca de contatos já usava. Um termo assim conta como curto: a tela mostra a lista sem filtro, como acontece com uma letra só, e quem chama a API recebe a recusa de termo curto em vez da lista inteira. Nenhuma configuração ou ação é necessária.
Contribuição de @webtecnica (#1934).
+16 -1
View File
@@ -66,7 +66,22 @@ export function normalizarTermoDeBusca(bruto: string): string {
* piso veio consertar, reintroduzido por outra porta.
*
* O piso em caracteres crus não pega esse caso: `", ,"` tem 3 caracteres.
*
* O PARÊNTESE entra aqui pelos MESMOS dois motivos:
*
* - `normalizarTermoDeBusca` colapsa `\s,;` — parêntese não é separador, então
* `"()"` vira 2 caracteres e passa um piso qualquer.
* - `termoSeguroParaOr` (o `or=` da inbox) troca `()` por `*`: `"()"` vira `**`,
* o PostgREST vira `%%%%`, e a busca devolve a LISTA INTEIRA — o defeito que a
* #1892 consertou no handler de contatos e que este módulo torna régua única,
* valendo também para o schema (`lib/schemas/messaging.ts`) e a tela
* (`components/inbox/InboxLayout.tsx`), que leem daqui.
*
* O `replace` NO PISO não muda o termo que vai ao banco — ele só decide se vale
* consultar. O parêntese que sobra (ex. um telefone `(15) 99259-4261`) continua
* vindo atrás dele e segue funcionando, como os controles abaixo provam.
*/
export function buscaValeConsulta(bruto: string): boolean {
return normalizarTermoDeBusca(bruto).length >= PISO_DA_BUSCA;
const semParenteses = bruto.replace(/[()]/g, " ");
return normalizarTermoDeBusca(semParenteses).length >= PISO_DA_BUSCA;
}
@@ -101,3 +101,32 @@ describe("termo que não sobra nada depois de normalizado não vale consulta", (
expect(buscaValeConsulta("+55 15 99259-4261")).toBe(true);
});
});
/**
* O parêntese (#1895): a lista inteira de volta pela porta do `termoSeguroParaOr`.
*
* Medido na instalação real (vira `%` no PostgREST): `buscaValeConsulta("()")`
* passava no piso — `normalizarTermoDeBusca` não colapsa `()`, então o termo vira
* 2 caracteres; `termoSeguroParaOr` troca `()` por `**`; e o `or=` vira `%%%%`,
* que casa TUDO. `"(a"` vira `%a%`, igualmente amplíssimo.
*
* A régua agora tira os parênteses ANTES de medir o piso — dentro de
* `buscaValeConsulta`. Se alguém remover o `replace`, estes dois casos ficam
* VERMELHOS (a sabotagem da #1895 prevê exatamente isso).
*/
describe("termo de busca não devolve a lista inteira pelo parêntese (#1895)", () => {
it("'()' NÃO vale consulta", () => {
expect(buscaValeConsulta("()")).toBe(false);
});
it("parêntese aberto não vale consulta", () => {
expect(buscaValeConsulta("((")).toBe(false);
expect(buscaValeConsulta("(a")).toBe(false);
});
it("CONTROLE: parêntese com conteúdo real continua valendo", () => {
// Sem estes, uma implementação que recusasse qualquer parêntese passaria.
expect(buscaValeConsulta("paulo (jr)")).toBe(true);
expect(buscaValeConsulta("(15) 99259")).toBe(true);
});
});