mirror of
https://github.com/fsolla/teqo.git
synced 2026-11-13 06:43:06 +00:00
refactor(ops96): simplify — dedup ms/DEFAULT_TOP no core, exit fail-closed sem relatório, precisão no desarme e pins de fallback
This commit is contained in:
parent
36dde76cc7
commit
bd977cd2ae
4 changed files with 32 additions and 9 deletions
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
name: testing-audit
|
||||
description: Auditoria autônoma noturna da suíte de testes do Teqo — retrato honesto (contagens, tempos, duplicatas por camada), melhorias seguras aplicadas com prova verde antes/depois e revert fail-closed, entregues num único PR ready SEM auto-merge armado cuja descrição é o relatório final.
|
||||
description: 'Auditoria autônoma noturna da suíte de testes do Teqo — retrato honesto (contagens, tempos, duplicatas por camada), melhorias seguras aplicadas com prova verde antes/depois e revert fail-closed, entregues num único PR ready SEM auto-merge armado cuja descrição é o relatório final.'
|
||||
---
|
||||
|
||||
# Skill: testing-audit
|
||||
|
|
@ -28,7 +28,7 @@ Intocáveis estruturais (nenhum diff toca): `scripts/ci-scope.mjs`, `scripts/lib
|
|||
|
||||
## Critérios de nível (aponte, não redefina)
|
||||
|
||||
A pirâmide das três camadas já está codificada: `.agents/skills/test-driven-development/SKILL.md` §144–198 e `docs/TESTING.md`. As disposições da auditoria julgam contra esses critérios existentes — nunca criem critérios novos paralelos.
|
||||
A pirâmide das três camadas já está codificada: `.agents/skills/test-driven-development/SKILL.md` (seção da pirâmide, "as três camadas do repo" — hoje §144–198) e `docs/TESTING.md`. As disposições da auditoria julgam contra esses critérios existentes — nunca criem critérios novos paralelos.
|
||||
|
||||
## Tetos duros da noite
|
||||
|
||||
|
|
@ -135,7 +135,7 @@ Evidência por passo no relatório: timestamp, hash curto, linhas-resumo antes/d
|
|||
O safety-net arma o automerge no evento de abertura e REARMA a cada push. Sequência obrigatória:
|
||||
|
||||
1. Confirmar que nenhum push novo acontecerá (todo o trabalho já foi empurrado na Fase 4).
|
||||
2. Aguardar o run do workflow `PR Ready + auto-merge` **do SHA final** terminar (foi ele que armou): `gh run list --workflow agent-pr-ready-automerge.yml --limit 1`.
|
||||
2. Aguardar o run do workflow `PR Ready + auto-merge` **do SHA final** terminar (foi ele que armou): `gh run list --workflow agent-pr-ready-automerge.yml --branch <branch-deste-PR> --limit 1` — filtre pela branch/SHA, nunca pelo "último run" global (PRs concorrentes poluem a lista).
|
||||
3. Desarmar: `gh pr merge <N> --disable-auto`.
|
||||
4. Verificar: `gh pr view <N> --json isDraft,autoMergeRequest,mergeable,mergeStateStatus` → esperado `{isDraft: false, autoMergeRequest: null, mergeable: MERGEABLE}`.
|
||||
5. **Fallback fail-closed:** desarme falhou ou `autoMergeRequest` persistiu não-nulo → converter para draft (`gh pr ready <N> --undo`; draft fora de `cursor/*` é veto estrutural do safety-net) e registrar no relatório que o humano vira ready pela manhã. Draft protegido é preferível a main mergeado sem querer.
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@
|
|||
* functions; the unit spec pins their behavior against fixture payloads.
|
||||
*/
|
||||
|
||||
const DEFAULT_TOP = 15
|
||||
export const DEFAULT_TOP = 15
|
||||
|
||||
const ms = (value) => `${(value / 1000).toFixed(1)}s`
|
||||
export const ms = (value) => `${(value / 1000).toFixed(1)}s`
|
||||
|
||||
/** @returns {number} */
|
||||
const fileDurationMs = (entry) => {
|
||||
|
|
@ -80,6 +80,8 @@ export const renderSlowestFilesTable = (summary, { top = DEFAULT_TOP } = {}) =>
|
|||
|
||||
/**
|
||||
* Static inventory of an e2e spec source — counting only, never execution.
|
||||
* Deliberate approximation: `test.each`/`test.skip` variants are not counted
|
||||
* (the audit's portrait is structural, not exhaustive).
|
||||
*
|
||||
* @param {{ file: string, content: string }} source
|
||||
* @returns {{ file: string, describes: number, tests: number }}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,9 @@ import path from 'node:path'
|
|||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
import {
|
||||
DEFAULT_TOP,
|
||||
inventoryE2ESource,
|
||||
ms,
|
||||
renderE2EInventoryTable,
|
||||
renderSlowestFilesTable,
|
||||
summarizeVitestReport,
|
||||
|
|
@ -28,7 +30,6 @@ import {
|
|||
|
||||
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
|
||||
const E2E_DIR = 'tests/e2e'
|
||||
const DEFAULT_TOP = 15
|
||||
|
||||
const usage = () => {
|
||||
console.error('usage: node scripts/testing-audit-metrics.mjs <unit|int|e2e-inventory> [--top N]')
|
||||
|
|
@ -56,7 +57,7 @@ const runVitestLayer = async (layer) => {
|
|||
if (jsonStart === -1) {
|
||||
console.error(`[metrics] vitest não produziu relatório JSON no stdout (exit ${result.status})`)
|
||||
console.error((result.stderr ?? '').slice(-4000))
|
||||
process.exit(result.status ?? 2)
|
||||
process.exit(result.status === 0 ? 2 : result.status)
|
||||
}
|
||||
let report
|
||||
try {
|
||||
|
|
@ -76,8 +77,6 @@ const runVitestLayer = async (layer) => {
|
|||
if (result.status !== 0) process.exit(result.status)
|
||||
}
|
||||
|
||||
const ms = (value) => `${(value / 1000).toFixed(1)}s`
|
||||
|
||||
const runE2EInventory = () => {
|
||||
const dir = path.join(repoRoot, E2E_DIR)
|
||||
const rows = readdirSync(dir)
|
||||
|
|
|
|||
|
|
@ -60,6 +60,19 @@ describe('summarizeVitestReport', () => {
|
|||
it('rejects payloads without testResults', () => {
|
||||
expect(() => summarizeVitestReport({})).toThrow('missing testResults')
|
||||
})
|
||||
|
||||
it('falls back to counted totals and a placeholder when fields are absent', () => {
|
||||
const payload = {
|
||||
testResults: [{ assertionResults: [{ title: 't' }] }],
|
||||
}
|
||||
const summary = summarizeVitestReport(payload)
|
||||
|
||||
expect(summary.totals.files).toBe(1)
|
||||
expect(summary.totals.tests).toBe(1)
|
||||
expect(summary.totals.failedTests).toBe(0)
|
||||
expect(summary.rows[0].file).toBe('(desconhecido)')
|
||||
expect(summary.rows[0].slowestTest).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
describe('renderSlowestFilesTable', () => {
|
||||
|
|
@ -69,6 +82,15 @@ describe('renderSlowestFilesTable', () => {
|
|||
expect(table.split('\n')).toHaveLength(3)
|
||||
expect(table).toContain('| tests/unit/b.unit.spec.ts | 2 | 1.1s | slow one (0.4s) |')
|
||||
})
|
||||
|
||||
it('renders a placeholder for rows without a slowest test', () => {
|
||||
const table = renderSlowestFilesTable(
|
||||
summarizeVitestReport({ testResults: [{ name: '/x/y.unit.spec.ts', assertionResults: [] }] }),
|
||||
{ top: 5 },
|
||||
)
|
||||
|
||||
expect(table).toContain('| /x/y.unit.spec.ts | 0 | 0.0s | — |')
|
||||
})
|
||||
})
|
||||
|
||||
describe('inventoryE2ESource', () => {
|
||||
|
|
|
|||
Loading…
Reference in a new issue