/** * O comando de teste do PROJETO, executado de verdade. * * Até aqui a camada determinística do runtime tinha oito checks * (`artifact-valid | min-size | contains | not-contains | matches | * json-field | file-exists | references-exist`) e nenhum deles executava nada. * A consequência era medida e específica: a métrica `testResults` da avaliação * vinha de um artefato `test-results` que um AGENTE ESCREVEU. O runtime * reportava "testes passando" com base num texto produzido pelo mesmo processo * que deveria ser testado, que é exatamente a evidência circular que o * Verification Engine existe para recusar. * * ## Por que isto NÃO é o `code.execute` * * A sandbox de código bloqueia subprocessos de propósito, e afrouxar esse * isolamento para caber num item de roadmap seria trocar uma garantia real por * uma métrica. Este caminho é outro, e a diferença é qual é a fonte do comando: * * code.execute : o código vem do MODELO. Por isso roda isolado, sem * subprocesso, sem rede confiável e sem env herdado. * project.test : o comando vem do PROJETO (o `scripts.test` do manifesto, ou * o runner padrão da linguagem detectada) e o binário vem de * uma allowlist fixa deste arquivo. Nenhum campo de entrada * carrega um comando: não existe caminho pelo qual um modelo * escolha o que é executado. * * Rodar `npm test` num projeto é a mesma confiança de digitar `npm test` nele. * Por isso o caminho é OPT-IN (`--verify-tests`), a permissão é `shell` (que a * `PolicyEngine` nega a trust tier `generated` e `community`), o tempo tem * teto, e a saída tem teto declarado. * * ## O que este arquivo NÃO faz * * Não instala dependência, não escolhe framework de teste e não interpreta o * relatório: devolve o comando, o exit code e a saída cortada. "Passou" é * `exitCode === 0`, decidido pelo processo, não por leitura de texto — parsear * o stdout de um test runner para decidir aprovação seria reintroduzir a * adivinhação pelo lado da saída. */ export declare const DEFAULT_TEST_TIMEOUT_MS = 300000; export declare const MAX_TEST_OUTPUT_BYTES: number; /** * Runners aceitos, por linguagem. O binário é fixo aqui: o projeto escolhe * QUAL runner (pelos arquivos que tem), nunca QUE COMANDO roda. */ export interface TestRunner { /** Identificador estável, usado no relatório e nos testes. */ id: string; /** Binário e argumentos. Nada aqui vem de input. */ command: string; args: string[]; /** * O que é passado ao `spawn`, quando difere do rótulo acima. Existe por causa * do `npm` no Windows: ver `resolveNpmCli`. Ausente = `command`/`args`. */ execFile?: string; execArgs?: string[]; /** Por que este runner foi escolhido (aparece no artefato). */ reason: string; } /** * O entrypoint JS do npm, para ser executado pelo `node` que já está rodando. * * No Windows `npm` é um shim `.cmd`, e desde o CVE-2024-27980 o Node RECUSA * lançar `.cmd`/`.bat` sem `shell: true` (`spawn EINVAL`). As duas saídas * óbvias custam a garantia deste arquivo: `shell: true` e `cmd.exe /c` põem um * interpretador de comandos no caminho, justamente o que `shell: false` * remove. Esta terceira não: o binário passa a ser `process.execPath`, o * mesmo node que executa o runtime, e o npm entra como argumento de arquivo. * Sem shell, e com o binário mais confiável disponível. * * Os dois layouts cobrem instalação oficial no Windows (npm ao lado do node) e * em Unix (`../lib/node_modules`, que é o que nvm e o tarball usam). Se nenhum * resolver, o chamador fica com o binário `npm` do PATH: em Unix o shim tem * shebang e executa; no Windows o EINVAL é reportado como erro, e "não medi" * continua sendo ausência de `passed`, nunca reprovação. */ export declare function resolveNpmCli(): string | undefined; export interface DetectResult { runner?: TestRunner; /** Por que nenhum runner foi detectado. Presente quando `runner` é ausente. */ reason?: string; } /** * Descobre o comando de teste a partir do que existe no diretório. * * Ordem por especificidade: um manifesto Node com `scripts.test` é o sinal mais * forte que um projeto pode dar sobre como se testa. Ausente ele, a presença do * manifesto da linguagem decide. * * `scripts.test` NÃO é lido como comando: o que é verificado é que o script * existe e não é o placeholder que o `npm init` escreve. O binário continua * sendo `npm`, e o que ele roda é o que o dono do projeto escreveu ali. */ export declare function detectTestRunner(dir: string): DetectResult; export interface TestRunResult { /** Comando efetivamente executado, para o relatório. */ command: string; runner: string; /** Por que este runner. */ detectedBy: string; /** * `exitCode === 0`. Ausente quando o comando não chegou a rodar: "não medi" * nunca vira `false`, que se leria como "os testes falharam". */ passed?: boolean; exitCode: number | null; timedOut: boolean; durationMs: number; stdout: string; stderr: string; truncated: boolean; /** Presente quando o comando não pôde ser executado (binário ausente, etc). */ error?: string; } /** * Executa o runner detectado. Nunca lança: binário ausente, timeout e saída * não-zero voltam como resultado, porque quem chamou precisa registrar a falha * e não ser interrompido por ela. * * O ambiente do processo pai É herdado aqui, ao contrário da sandbox de código: * um test runner precisa de PATH, de HOME e do que o projeto configurou. Esta é * a diferença de confiança entre executar o comando do projeto e executar o * código de um modelo, e é o motivo de o caminho ser opt-in. */ export declare function runProjectTests(opts: { dir: string; timeoutMs?: number; maxOutputBytes?: number; signal?: AbortSignal; }): Promise; //# sourceMappingURL=project-test.d.ts.map