Files
ESPDocs/scripts/verifica-busca.mjs
T
Lucas RayanandClaude Sonnet 5.5 71c5708726
CI / Lint e auditorias (push) Successful in 46s
CI / Trivy (dependências e segredos) (push) Successful in 10s
CI / Imagem Docker (linux/arm64) (push) Successful in 3m50s
Release / Plano (reaproveitar a imagem?) (push) Successful in 5s
Release / ci (push) Skipped
Release / Publica a imagem (linux/arm64) (push) Successful in 23s
Release / Cria a release no Gitea (push) Successful in 5s
feat(busca): pin-number queries in every language, pin answer card
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-02 00:43:23 -03:00

108 lines
4.3 KiB
JavaScript
Executable File

#!/usr/bin/env node
// Respostas da busca por idioma: texto no idioma da página e links dentro dele.
//
// node scripts/verifica-busca.mjs http://127.0.0.1:3998
//
// O texto das "respostas diretas" é montado no navegador pelo motor; as varreduras
// de página não o enxergam. Aqui rodamos o mesmo motor sobre o índice servido.
import { preparar, buscar } from "../lib/searchEngine.js";
const base = process.argv[2];
if (!base) {
console.error("uso: verifica-busca.mjs <url-base>");
process.exit(2);
}
const idiomas = (process.env.LOCALES || "pt en es fr de zh-CN").split(/\s+/);
const consultas = {
pt: ["flash s3", "gpio26 s3", "pinos usb c6", "deep sleep p4", "psram"],
en: ["flash s3", "gpio26 s3", "usb pins c6", "deep sleep p4", "psram"],
es: ["flash s3", "gpio26 s3", "usb c6", "deep sleep p4", "psram"],
fr: ["flash s3", "gpio26 s3", "usb c6", "deep sleep p4", "psram"],
de: ["flash s3", "gpio26 s3", "usb c6", "deep sleep p4", "psram"],
"zh-CN": ["s3 闪存", "gpio26 s3", "c6 usb 引脚", "低功耗 p4", "哪款 esp32 有 psram", "安全启动"],
};
// Palavras que só existem em português (as respostas dos outros idiomas não podem ter).
const SO_PT = /(ligados à|dados da série|diagrama de pinos|que dependem do modo|já filtrado|em cada série|especificações do|\bcondicionais\b|\bpinos\b)/i;
const strings = (r) => [r.titulo, r.descricao, ...(r.linhas ?? []).flatMap((l) => [l.k, l.v]), ...(r.chips ?? [])].filter(Boolean);
let falhas = 0;
for (const loc of idiomas) {
const res = await fetch(`${base}/api/busca/${loc}`);
if (!res.ok) {
console.log(`FALHOU /api/busca/${loc}: ${res.status}`);
falhas++;
continue;
}
const indice = preparar(await res.json());
for (const q of consultas[loc] ?? consultas.en) {
const { resultados } = buscar(indice, q);
if (!resultados.length) {
console.log(`FALHOU [${loc}] "${q}": sem resultados`);
falhas++;
continue;
}
for (const r of resultados.filter((x) => x.tipo === "resposta" || x.tipo === "pino")) {
if (loc !== "pt" && !r.href.startsWith(`/${loc}/`)) {
console.log(`FALHOU [${loc}] "${q}": link fora do idioma: ${r.href}`);
falhas++;
}
if (loc !== "pt") {
for (const s of strings(r)) {
if (SO_PT.test(s)) {
console.log(`FALHOU [${loc}] "${q}": português em "${s.slice(0, 80)}"`);
falhas++;
}
}
}
}
}
}
// Consulta por pino físico: "pino 32 esp32-s3" traz o cartão do pino 32, com nome, funções e avisos.
const comoDizer = {
pt: ["pino 32 esp32-s3", "pino 32", "pino 999 s3"],
en: ["pin 32 esp32-s3", "pin 32", "pin 999 s3"],
es: ["pin 32 esp32-s3", "pin 32", "pin 999 s3"],
fr: ["broche 32 esp32-s3", "broche 32", "broche 999 s3"],
de: ["pin 32 esp32-s3", "pin 32", "pin 999 s3"],
"zh-CN": ["esp32-s3 32号引脚", "引脚 32", "s3 999号引脚"],
};
for (const loc of idiomas) {
const res = await fetch(`${base}/api/busca/${loc}`);
if (!res.ok) continue;
const indice = preparar(await res.json());
const [comChip, semChip, inexistente] = comoDizer[loc];
const a = buscar(indice, comChip);
const cartao = a.resultados.find((r) => r.tipo === "resposta" && r.linhas?.[0]?.v === "32");
if (!cartao || !cartao.linhas.some((l) => l.k && l.v) || cartao.linhas.length < 3) {
console.log(`FALHOU [${loc}] "${comChip}": sem cartão do pino 32`);
falhas++;
} else if (!cartao.href.startsWith(`/${loc}/`)) {
console.log(`FALHOU [${loc}] "${comChip}": link fora do idioma: ${cartao.href}`);
falhas++;
}
if (!a.interpretacao.pinos?.length) {
console.log(`FALHOU [${loc}] "${comChip}": o pino não aparece em "entendi"`);
falhas++;
}
const b = buscar(indice, semChip);
const geral = b.resultados.find((r) => r.tipo === "resposta" && (r.linhas?.length ?? 0) >= 8);
if (!geral) {
console.log(`FALHOU [${loc}] "${semChip}": sem o quadro "pino 32 em cada série"`);
falhas++;
}
const c = buscar(indice, inexistente);
if (!c.resultados.some((r) => r.tipo === "pino" && r.titulo.includes("999"))) {
console.log(`FALHOU [${loc}] "${inexistente}": sem aviso de pino inexistente`);
falhas++;
}
if (loc !== "pt") {
for (const r of [...a.resultados, ...b.resultados, ...c.resultados]) {
for (const s of strings(r)) if (SO_PT.test(s)) { console.log(`FALHOU [${loc}]: português em "${s.slice(0, 80)}"`); falhas++; }
}
}
}
if (falhas === 0) console.log(` ✔ Busca: respostas e links corretos em ${idiomas.join(", ")}.`);
process.exit(falhas > 0 ? 1 : 0);