mirror of
https://github.com/melgarafael/DeskcommCRM.git
synced 2026-10-02 01:28:34 +08:00
test(extensoes): a guarda da bancada media a forma, não o efeito — a previsão errada denunciou
Sabotei a bancada de duas formas e escrevi antes quantos casos cada uma deveria derrubar. A primeira bateu (1 e 1). A segunda não: previ 2, deu 1 — e é a previsão errada que vale, porque ela apontou um ponto cego real. Os casos existentes comparavam as LISTAS e procuravam o texto proibido. Nenhum garantia que a validação USASSE as listas: dá para manter as constantes em dia e validar contra outra coisa, e os três ficam verdes. Era exatamente o caso da sabotagem. O caso novo mede o EFEITO: carrega `validate_manifest` do próprio `catalog.py` e valida um pacote com porta nova (`inbox.open`). Com controle positivo — se o `python3` não existir, o teste diz que não mediu nada, em vez de ler a ausência como recusa legítima. Refeita a sabotagem com a guarda nova: 2 previstas, 2 obtidas. Controle em 5 verdes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Binary file not shown.
@@ -1,4 +1,6 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { readFileSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
|
||||
import { describe, expect, it } from "vitest";
|
||||
@@ -58,4 +60,71 @@ describe("o catálogo de ensaio espelha o vocabulário do host", () => {
|
||||
expect(fonte).not.toContain('!= ["navigation.tasks"]');
|
||||
expect(fonte).not.toContain('!= "tasks.open"');
|
||||
});
|
||||
|
||||
/**
|
||||
* Os casos acima medem a FORMA — as listas batem, o texto proibido sumiu. Nenhum deles
|
||||
* garante que a validação USE as listas: dá para manter as constantes em dia e validar
|
||||
* contra outra coisa, e os três ficam verdes.
|
||||
*
|
||||
* Isto foi medido, não imaginado: uma sabotagem que trocou só a condição de validação,
|
||||
* mantendo a constante intacta, derrubou 1 caso quando eu tinha previsto 2. A previsão
|
||||
* errada é o que revelou o ponto cego.
|
||||
*
|
||||
* Este caso mede o EFEITO: roda a bancada de verdade contra um pacote de porta nova.
|
||||
*/
|
||||
it("aceita, de fato, um pacote que usa porta nova — prova de efeito, não de forma", () => {
|
||||
const pacote = {
|
||||
format_version: 1,
|
||||
profile: "declarative",
|
||||
publisher: "laboratorio-local",
|
||||
name: "porta-nova",
|
||||
version: "1.0.0",
|
||||
license: "MIT",
|
||||
host_api: { min: 1, max: 2 },
|
||||
permissions: ["navigation.inbox"],
|
||||
dependencies: [],
|
||||
data: { mode: "none" },
|
||||
display: {
|
||||
title: { "pt-BR": "Porta nova" },
|
||||
summary: { "pt-BR": "Pacote de controle do invariante." },
|
||||
category: "service",
|
||||
icon: "ListChecks",
|
||||
},
|
||||
configuration: { density: "comfortable", show_description: true },
|
||||
contributions: {
|
||||
crm_cards: [
|
||||
{
|
||||
id: "cartao",
|
||||
title: { "pt-BR": "Cartão" },
|
||||
description: { "pt-BR": "Descrição do cartão de controle." },
|
||||
icon: "ListChecks",
|
||||
blocks: [{ heading: { "pt-BR": "Título" }, body: { "pt-BR": "Corpo." } }],
|
||||
action: { label: { "pt-BR": "Abrir" }, capability: "inbox.open" },
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
const arquivo = join(tmpdir(), `pacote-porta-nova-${process.pid}.json`);
|
||||
writeFileSync(arquivo, JSON.stringify(pacote));
|
||||
try {
|
||||
// `catalog.py` não tem subcomando de validação isolada (quem valida é o `publish`, que
|
||||
// exige banco). Chamamos a função real do módulo — é o mesmo código que o `publish` usa.
|
||||
const script = [
|
||||
"import importlib.util, json, sys, pathlib",
|
||||
`spec = importlib.util.spec_from_file_location("catalogo", ${JSON.stringify(CATALOG_PY)})`,
|
||||
"mod = importlib.util.module_from_spec(spec); spec.loader.exec_module(mod)",
|
||||
`mod.validate_manifest(json.loads(pathlib.Path(${JSON.stringify(arquivo)}).read_text()))`,
|
||||
].join("\n");
|
||||
const saida = spawnSync("python3", ["-c", script], { encoding: "utf8" });
|
||||
// Controle positivo: se o comando nem existir, `status` é diferente de 0 por outro
|
||||
// motivo, e um teste que só olhasse "status !== 0" leria isso como recusa legítima.
|
||||
expect(saida.error, "python3 indisponível: o caso não mediu nada").toBeUndefined();
|
||||
expect(
|
||||
saida.status,
|
||||
`bancada recusou porta nova:\n${saida.stdout}\n${saida.stderr}`,
|
||||
).toBe(0);
|
||||
} finally {
|
||||
rmSync(arquivo, { force: true });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user