@analizza-ai/testspec 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/LICENSE +21 -0
  3. package/README.md +189 -0
  4. package/bin/cli.js +42 -0
  5. package/package.json +69 -0
  6. package/src/adapters/agents/claude.js +88 -0
  7. package/src/adapters/agents/copilot.js +39 -0
  8. package/src/adapters/agents/index.js +22 -0
  9. package/src/adapters/sdd/index.js +23 -0
  10. package/src/adapters/sdd/openspec.js +58 -0
  11. package/src/adapters/sdd/speckit.js +19 -0
  12. package/src/commands/generate.js +66 -0
  13. package/src/commands/init.js +112 -0
  14. package/src/commands/report.js +60 -0
  15. package/src/commands/validate.js +68 -0
  16. package/src/core/reporter.js +44 -0
  17. package/src/core/spec-parser.js +141 -0
  18. package/src/core/stub-generator.js +92 -0
  19. package/src/core/testcontainers.js +39 -0
  20. package/src/core/tests-builder.js +120 -0
  21. package/src/index.js +10 -0
  22. package/src/utils/config.js +29 -0
  23. package/src/utils/logger.js +13 -0
  24. package/src/utils/sdd-detector.js +23 -0
  25. package/templates/agent-instructions/AGENTS.md +39 -0
  26. package/templates/agent-instructions/CLAUDE.md +48 -0
  27. package/templates/agent-instructions/copilot.md +52 -0
  28. package/templates/agent-instructions/skills/testspec-apply-qa.md +424 -0
  29. package/templates/agent-instructions/skills/testspec-generate.md +138 -0
  30. package/templates/agent-instructions/skills/testspec-run-qa.md +338 -0
  31. package/templates/agent-instructions/skills/testspec-specify-qa.md +535 -0
  32. package/templates/stubs/jest/unit.template.js +17 -0
  33. package/templates/stubs/junit/unit.template.java +27 -0
  34. package/templates/stubs/pytest/unit.template.py +18 -0
  35. package/templates/stubs/testcontainers/node-pg-kafka.template.js +38 -0
  36. package/templates/stubs/testcontainers/node-pg.template.js +32 -0
  37. package/templates/stubs/testcontainers/spring-pg-kafka.template.java +41 -0
  38. package/templates/stubs/vitest/unit.template.js +19 -0
  39. package/templates/tests-md/default.md +43 -0
@@ -0,0 +1,29 @@
1
+ /**
2
+ * src/utils/config.js
3
+ * Reads and validates testspec.config.json from the project root.
4
+ */
5
+
6
+ import { readFileSync, existsSync } from 'fs';
7
+ import { join } from 'path';
8
+
9
+ const DEFAULTS = {
10
+ sdd: 'openspec',
11
+ agent: 'claude',
12
+ unitFramework: 'vitest',
13
+ stubs: { unit: true, integration: true },
14
+ loadHints: true,
15
+ chaosHints: true,
16
+ };
17
+
18
+ /**
19
+ * @param {string} root project root directory
20
+ * @returns {object}
21
+ */
22
+ export function loadConfig(root) {
23
+ const configPath = join(root, 'testspec.config.json');
24
+ if (!existsSync(configPath)) {
25
+ return { ...DEFAULTS };
26
+ }
27
+ const raw = JSON.parse(readFileSync(configPath, 'utf-8'));
28
+ return { ...DEFAULTS, ...raw };
29
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * src/utils/logger.js
3
+ * Thin logger with coloured output via chalk.
4
+ */
5
+
6
+ import chalk from 'chalk';
7
+
8
+ export const log = {
9
+ info: (msg) => console.log(chalk.cyan(msg)),
10
+ success: (msg) => console.log(chalk.green('✔ ' + msg)),
11
+ warn: (msg) => console.warn(chalk.yellow('⚠ ' + msg)),
12
+ error: (msg) => console.error(chalk.red('✖ ' + msg)),
13
+ };
@@ -0,0 +1,23 @@
1
+ /**
2
+ * src/utils/sdd-detector.js
3
+ * Auto-detects which SDD framework is in use by scanning known folder patterns.
4
+ */
5
+
6
+ import { existsSync } from 'fs';
7
+ import { join } from 'path';
8
+
9
+ const SIGNATURES = [
10
+ { name: 'openspec', path: 'openspec/changes' },
11
+ { name: 'speckit', path: 'specs' },
12
+ ];
13
+
14
+ /**
15
+ * @param {string} root project root directory
16
+ * @returns {string|null} detected SDD name, or null if none found
17
+ */
18
+ export function detectSdd(root) {
19
+ for (const sig of SIGNATURES) {
20
+ if (existsSync(join(root, sig.path))) return sig.name;
21
+ }
22
+ return null;
23
+ }
@@ -0,0 +1,39 @@
1
+ # testspec — Agent instructions (unified)
2
+
3
+ This project uses **@analizza-ai/testspec** (SDT — Spec Driven Test).
4
+
5
+ ## Generate tests.md
6
+
7
+ ```bash
8
+ testspec generate [--change <name>] [--api] [--no-stubs]
9
+ ```
10
+
11
+ Reads SDD spec artifacts → builds a prompt → writes `tests.md` + optional test stubs.
12
+
13
+ ## SDD framework: OpenSpec
14
+
15
+ Artifacts are in `openspec/changes/{change-name}/`:
16
+ - `proposal.md` — intent, non-goals, scope
17
+ - `design.md` — API contracts, DB schema
18
+ - `specs/**/*.md` — behaviour, scenarios, rules
19
+ - `tasks.md` — implementation boundary
20
+ - `tests.md` — **output**: CT-01..N test cases (written by testspec)
21
+
22
+ ## tests.md CT structure
23
+
24
+ | Field | Description |
25
+ |---------------------|------------------------------------------|
26
+ | Type | unit / integration / e2e / load / chaos |
27
+ | Layer | developer / qa / chaos |
28
+ | Precondition | Required system state before the test |
29
+ | Input | Request body / method params |
30
+ | Expected output | HTTP response / return value |
31
+ | DB validation | SQL query or description of DB state |
32
+ | Acceptance criteria | Bullet checklist from spec |
33
+
34
+ ## Other commands
35
+
36
+ ```bash
37
+ testspec validate --results <test-results.json> # map pass/fail to CTs
38
+ testspec report # gap: which CTs have no stub
39
+ ```
@@ -0,0 +1,48 @@
1
+ # /testspec-generate — Spec Driven Test generation
2
+
3
+ Run `testspec generate` to read the current SDD change and generate `tests.md` + test stubs.
4
+
5
+ ## What this command does
6
+
7
+ 1. Reads spec artifacts from `openspec/changes/{latest}/` (proposal.md, design.md, specs/**/*.md, tasks.md)
8
+ 2. Parses them into a SpecContext
9
+ 3. Builds a prompt and either:
10
+ - prints it to chat for you to paste back (default)
11
+ - calls the Claude API directly (`--api` flag)
12
+ 4. Writes `tests.md` with CT-01..N test cases
13
+ 5. Generates unit and integration test stubs
14
+
15
+ ## Usage
16
+
17
+ ```bash
18
+ # generate tests.md for the latest change (prints prompt to chat)
19
+ testspec generate
20
+
21
+ # generate for a specific change
22
+ testspec generate --change my-feature
23
+
24
+ # call Claude API directly (requires ANTHROPIC_API_KEY)
25
+ testspec generate --api
26
+
27
+ # skip stub generation
28
+ testspec generate --no-stubs
29
+ ```
30
+
31
+ ## tests.md structure
32
+
33
+ Each CT (test case) follows this table format:
34
+
35
+ | Field | Value |
36
+ |---------------------|------------------------------|
37
+ | Type | unit / integration / e2e / load / chaos |
38
+ | Layer | developer / qa / chaos |
39
+ | Precondition | ... |
40
+ | Input | request / params |
41
+ | Expected output | response / return value |
42
+ | DB validation | SQL assertion or description |
43
+ | Acceptance criteria | · bullet list |
44
+
45
+ ## When pasting the prompt back
46
+
47
+ After running `testspec generate` without `--api`, paste the printed prompt into this chat.
48
+ I will generate the full `tests.md` content. Copy my output and save it to the path shown in the CLI output.
@@ -0,0 +1,52 @@
1
+ # testspec — Spec Driven Test context for GitHub Copilot
2
+
3
+ This project uses **testspec** (`@analizza-ai/testspec`) to generate `tests.md` from SDD spec artifacts.
4
+
5
+ ## How to generate tests.md
6
+
7
+ Run in terminal:
8
+ ```bash
9
+ testspec generate
10
+ ```
11
+
12
+ This prints a structured prompt. Paste it here (in Copilot Chat) and I will generate the full `tests.md`.
13
+
14
+ ## tests.md format
15
+
16
+ ```
17
+ ---
18
+ feature: <name>
19
+ change: <change-folder>
20
+ generated: <ISO timestamp>
21
+ sdd: openspec
22
+ sdt: <version>
23
+ stack: { lang, db }
24
+ ---
25
+
26
+ # Tests — <feature>
27
+
28
+ ## Scope
29
+ ## Out of scope
30
+
31
+ ## Test cases
32
+
33
+ ### CT-01 — <title>
34
+ | Field | Value |
35
+ |---------------------|-------|
36
+ | Type | unit / integration / e2e / load / chaos |
37
+ | Layer | developer / qa / chaos |
38
+ | Precondition | ... |
39
+ | Input | ... |
40
+ | Expected output | ... |
41
+ | DB validation | ... |
42
+ | Acceptance criteria | · bullet |
43
+
44
+ ## Load profile hints
45
+ ## Chaos scenarios
46
+ ```
47
+
48
+ ## SDD artifact locations (OpenSpec)
49
+ - `openspec/changes/{name}/proposal.md`
50
+ - `openspec/changes/{name}/design.md`
51
+ - `openspec/changes/{name}/specs/**/*.md`
52
+ - `openspec/changes/{name}/tasks.md`
@@ -0,0 +1,424 @@
1
+ # testspec-apply-qa
2
+
3
+ Implementa os scripts de teste QA (k6, Gatling ou outra ferramenta) a partir das tasks pendentes em `./testspec/{feature-name}/tasks.qa.md`, usando `spec.qa.md` como contrato técnico. Gera código real e executável, marcando cada task como concluída após a implementação.
4
+
5
+ ## Quando usar
6
+
7
+ Após `/testspec-specify-qa` ter gerado `spec.qa.md` e `tasks.qa.md` para a feature.
8
+
9
+ ---
10
+
11
+ ## Instructions
12
+
13
+ ### 0. Ler arquivos globais do projeto QA (SEMPRE, antes de qualquer outra etapa)
14
+
15
+ Leia **ambos** os arquivos abaixo em paralelo. São obrigatórios — se não existirem, informe o usuário e encerre:
16
+
17
+ **`./testspec/instructions.md`**
18
+ - Contém: padrões de arquitetura do código, produto, princípios de qualidade, tecnologias e convenções do projeto QA
19
+ - Aplique todas as diretrizes ao gerar o código — elas têm precedência sobre os templates e defaults desta skill
20
+ - Exemplos do que pode conter: padrão de import, estrutura de funções, naming de variáveis, padrão de assertion, configurações de ambiente
21
+
22
+ **`./testspec/current-feature.md`**
23
+ - Contém: nome da feature atualmente em foco (ex: `kafka-consumer-order-request`)
24
+ - Se existir e tiver um nome válido, **use-o diretamente** como feature ativa — pule a seleção (passo 1)
25
+ - Se estiver vazio ou ausente, execute o passo 1 normalmente
26
+
27
+ ---
28
+
29
+ ### 1. Identificar a feature
30
+
31
+ > **Atenção:** se `./testspec/current-feature.md` já definiu a feature no passo 0, este passo é ignorado.
32
+
33
+ **Se argumento passado:** use como nome da feature diretamente.
34
+
35
+ **Se nenhum argumento e `current-feature.md` está vazio:**
36
+ - Liste os diretórios em `./testspec/` no diretório atual (excluindo `instructions.md` e `current-feature.md`)
37
+ - Se houver apenas um diretório de feature, use-o diretamente
38
+ - Se houver mais de um, pergunte via **AskUserQuestion** (single select):
39
+ > "Qual feature deseja implementar os testes?"
40
+
41
+ Se `./testspec/` não existir ou não tiver nenhum diretório de feature:
42
+ ```
43
+ Nenhuma feature encontrada em ./testspec/.
44
+ Execute /testspec-specify-qa primeiro.
45
+ ```
46
+
47
+ ### 1.1 Fixar a feature em `current-feature.md`
48
+
49
+ Independentemente de como a feature foi identificada (argumento, seleção ou `current-feature.md` já preenchido), **sempre escreva** o nome da feature no arquivo:
50
+
51
+ ```
52
+ ./testspec/current-feature.md
53
+ ```
54
+
55
+ Conteúdo do arquivo (apenas o nome, sem formatação extra):
56
+ ```
57
+ {feature-name}
58
+ ```
59
+
60
+ Isso garante que todas as skills subsequentes da sessão (`/testspec-run-qa` e outras) usem a mesma feature sem perguntar novamente.
61
+
62
+ ---
63
+
64
+ ### 2. Ler os artefatos de especificação
65
+
66
+ Leia **ambos** os arquivos antes de qualquer implementação:
67
+
68
+ 1. `./testspec/{feature-name}/spec.qa.md` — contrato técnico completo
69
+ 2. `./testspec/{feature-name}/tasks.qa.md` — checklist de tasks
70
+
71
+ Extraia de `spec.qa.md`:
72
+ - **Ferramenta e extensão** (`{tool}`, `{ext}`) — linha `> Ferramenta:`
73
+ - **Protocolo de entrada** — HTTP ou Kafka (seção `## Contrato Técnico`)
74
+ - **HTTP method, path, headers, request body** — com tipos e exemplos reais
75
+ - **Response esperada** — status, `Location` header (se presente), body fields
76
+ - **Regras de negócio** — seção `## Regras de Negócio para Testes`
77
+ - **Banco de dados** — tabela e campos a assertar (se presente)
78
+ - **Load profile** — estágios de RPS e duração (se seção Load presente)
79
+ - **Chaos scenarios** — tipo e mecanismo (se seção Chaos presente)
80
+ - **Mapeamento detalhado por CT** — seção `## Casos de Teste — Mapeamento Detalhado`
81
+
82
+ Extraia de `tasks.qa.md`:
83
+ - Lista de **tasks pendentes** (`- [ ]`) com caminho completo do arquivo a criar
84
+ - Lista de **tasks já concluídas** (`- [x]`) para não reimplementar
85
+
86
+ ---
87
+
88
+ ### 3. Confirmar o escopo de implementação
89
+
90
+ Se houver tasks pendentes E tasks já concluídas, pergunte via **AskUserQuestion** (single select):
91
+ > "Existem {N} tasks já concluídas e {M} pendentes. O que deseja fazer?"
92
+
93
+ - **Implementar apenas as pendentes** — continua de onde parou
94
+ - **Reimplementar tudo** — recria todos os scripts (sobrescreve)
95
+ - **Escolher tasks específicas** — exibe lista das pendentes para seleção
96
+
97
+ Se "Escolher tasks específicas": use **AskUserQuestion** (multi select) com a lista de tasks pendentes.
98
+
99
+ ---
100
+
101
+ ### 4. Implementar cada script
102
+
103
+ Para cada task selecionada, na ordem em que aparecem no `tasks.qa.md`:
104
+
105
+ #### 4a. Identificar o tipo de script
106
+
107
+ A partir do caminho da task:
108
+ - Contém `/e2e/` → script E2E funcional
109
+ - Contém `/load/` → script de carga
110
+ - Contém `/chaos-engineering/` → script de chaos
111
+
112
+ #### 4b. Identificar o CT correspondente
113
+
114
+ Extraia o número do CT do nome da task (ex: `CT-01`) e leia o bloco `### CT-01` no `spec.qa.md`.
115
+
116
+ #### 4c. Gerar o código
117
+
118
+ Gere o arquivo conforme as regras de implementação abaixo para a ferramenta identificada.
119
+
120
+ #### 4d. Escrever o arquivo de script
121
+
122
+ Escreva o arquivo de script (`.js`, `.scala`, etc.) no caminho exato especificado na task, relativo ao diretório atual. Crie os diretórios intermediários se não existirem.
123
+
124
+ #### 4d.1 Gerar o run plan `.md` (sempre, para todo script gerado)
125
+
126
+ Imediatamente após criar o script, gere o run plan correspondente:
127
+
128
+ - O nome do arquivo `.md` é idêntico ao do script, substituindo a extensão por `.md`
129
+ - Ex: `k6-e2e-create-order-success.js` → `k6-e2e-create-order-success.md`
130
+ - O conteúdo segue o **Formato do Run Plan** definido na skill `/testspec-specify-qa`
131
+ - Popule cada seção com os dados reais extraídos de `spec.qa.md`:
132
+ - `## Script` — caminho completo do `.js` gerado
133
+ - `## Execução` — ferramenta, comando com variáveis de ambiente identificadas no script
134
+ - `## Coleta de Logs` — namespace, pod selector, query Splunk derivados da descrição do run plan
135
+ - `## Análise de Banco de Dados` — tabela e queries SQL derivadas dos critérios do CT
136
+ - `## Relatório` — seções fixas conforme o formato padrão
137
+ - `## Publicação no Confluence` — espaço, página pai e título derivados da descrição do run plan
138
+ - Escreva o `.md` no mesmo diretório do script imediatamente após gerá-lo
139
+
140
+ #### 4e. Marcar tasks como concluídas
141
+
142
+ No `tasks.qa.md`, substitua `- [ ]` por `- [x]` nas linhas do script e do seu run plan `.md` (se gerado).
143
+ **Faça isso imediatamente após cada par de arquivos criado** — nunca em lote no final.
144
+
145
+ #### 4f. Reportar progresso
146
+
147
+ Após cada script (e run plan, se gerado):
148
+ ```
149
+ [x] {caminho do script} — CT-{NN}: {nome do cenário}
150
+ [x] {caminho do run plan} — run plan para /testspec-run-qa
151
+ ```
152
+
153
+ ---
154
+
155
+ ## Regras de Implementação por Ferramenta e Tipo
156
+
157
+ ---
158
+
159
+ ### K6 — E2E Script
160
+
161
+ **Estrutura obrigatória:**
162
+
163
+ ```javascript
164
+ import http from 'k6/http';
165
+ import { check, sleep } from 'k6';
166
+
167
+ // Variáveis de ambiente — nunca hardcode URLs ou credenciais
168
+ const BASE_URL = __ENV.BASE_URL || 'http://localhost:8080';
169
+ const HEADERS = {
170
+ 'Content-Type': 'application/json',
171
+ // Incluir outros headers do contrato (Authorization, X-Correlation-Id, etc.)
172
+ };
173
+
174
+ export const options = {
175
+ vus: 1,
176
+ iterations: 1,
177
+ thresholds: {
178
+ http_req_failed: ['rate==0'],
179
+ http_req_duration: ['p(95)<2000'],
180
+ },
181
+ };
182
+
183
+ export default function () {
184
+ // --- ARRANGE ---
185
+ // Payload extraído do CT — use valores reais do spec.qa.md, não placeholders
186
+ const payload = JSON.stringify({
187
+ // campos do request body com valores de exemplo reais
188
+ });
189
+
190
+ // --- ACT ---
191
+ const res = http.{method}(`${BASE_URL}{path}`, payload, { headers: HEADERS });
192
+
193
+ // --- ASSERT ---
194
+ check(res, {
195
+ // Um check por critério de aceite do CT
196
+ 'status is {código}': (r) => r.status === {código},
197
+ // Se Location header presente:
198
+ 'Location header presente': (r) => r.headers['Location'] !== undefined,
199
+ // Se body field:
200
+ '{campo} no body': (r) => JSON.parse(r.body).{campo} === {valor},
201
+ });
202
+
203
+ // Se Location header presente: extrair ID para uso em asserções
204
+ // const locationId = res.headers['Location']?.split('/').pop();
205
+
206
+ sleep(0.5);
207
+ }
208
+ ```
209
+
210
+ **Regras específicas para E2E:**
211
+ - `vus: 1`, `iterations: 1` — é um teste funcional, não de carga
212
+ - Um `check()` por critério de aceite listado no CT
213
+ - Nomes dos checks descrevem o que estão validando (string legível)
214
+ - Para CT de rejeição: o `check` de status deve ser o código de erro (400, 422, etc.)
215
+ - Se o CT tem critério de banco de dados: adicionar comentário `// TODO: validar banco via endpoint de healthcheck ou query direta`
216
+ - Nunca hardcode URL, senha ou token — sempre `__ENV.VARIAVEL`
217
+ - Adicionar `sleep(0.5)` ao final para não saturar o sistema em iterações sequenciais
218
+
219
+ ---
220
+
221
+ ### K6 — Load Script
222
+
223
+ **Estrutura obrigatória:**
224
+
225
+ ```javascript
226
+ import http from 'k6/http';
227
+ import { check, sleep } from 'k6';
228
+
229
+ const BASE_URL = __ENV.BASE_URL || 'http://localhost:8080';
230
+ const HEADERS = { 'Content-Type': 'application/json' };
231
+
232
+ // Estágios derivados do loadProfile do spec.qa.md
233
+ export const options = {
234
+ stages: [
235
+ { duration: '1m', target: {vus_ramp} }, // warmup — ramp up até o platô
236
+ { duration: '{duracao}', target: {vus_plateau} }, // platô — carga alvo
237
+ { duration: '30s', target: 0 }, // cooldown
238
+ ],
239
+ thresholds: {
240
+ http_req_failed: ['rate<0.01'], // menos de 1% de erros
241
+ http_req_duration: ['p(95)<500', 'p(99)<1000'], // latência p95 < 500ms
242
+ },
243
+ };
244
+
245
+ export default function () {
246
+ const payload = JSON.stringify({ /* campos do happy path CT-01 */ });
247
+ const res = http.{method}(`${BASE_URL}{path}`, payload, { headers: HEADERS });
248
+
249
+ check(res, {
250
+ 'status is {código}': (r) => r.status === {código},
251
+ });
252
+
253
+ sleep(1);
254
+ }
255
+ ```
256
+
257
+ **Regras específicas para Load:**
258
+ - Baseado **sempre** no CT de sucesso (happy path)
259
+ - VUs do platô: calcule como `VUs = RPS × latência_média` (use 0.2s se não informado)
260
+ - Exemplo: 100 RPS × 0.2s = 20 VUs
261
+ - Warmup: rampa de 1 minuto até o platô
262
+ - Cooldown: 30 segundos descendo a zero
263
+ - Thresholds: `p(95) < 500ms` e `error_rate < 1%` como defaults — ajuste se o `loadProfile` especificar outros
264
+ - `sleep(1)` ao final para simular comportamento de usuário real
265
+ - Um arquivo por estágio de RPS definido no `loadProfile`
266
+
267
+ ---
268
+
269
+ ### K6 — Chaos Script
270
+
271
+ **Estrutura obrigatória:**
272
+
273
+ ```javascript
274
+ import http from 'k6/http';
275
+ import { check, sleep } from 'k6';
276
+ import exec from 'k6/execution';
277
+
278
+ const BASE_URL = __ENV.BASE_URL || 'http://localhost:8080';
279
+ const HEADERS = { 'Content-Type': 'application/json' };
280
+
281
+ // Namespace e deployment alvo — configuráveis via env
282
+ const K8S_NAMESPACE = __ENV.K8S_NAMESPACE || 'default';
283
+ const K8S_DEPLOYMENT = __ENV.K8S_DEPLOYMENT || '{deployment-name}';
284
+
285
+ export const options = {
286
+ stages: [
287
+ { duration: '2m', target: 20 }, // carga baseline antes do caos
288
+ { duration: '3m', target: 20 }, // caos injetado durante este estágio
289
+ { duration: '2m', target: 20 }, // recuperação observada
290
+ { duration: '30s', target: 0 },
291
+ ],
292
+ thresholds: {
293
+ http_req_failed: ['rate<0.05'], // tolerância maior durante caos (5%)
294
+ http_req_duration: ['p(95)<2000'],
295
+ },
296
+ };
297
+
298
+ // Injeção de caos executada uma vez no início do segundo estágio
299
+ export function setup() {
300
+ // IMPORTANTE: este bloco requer kubectl configurado no ambiente de execução
301
+ // Substitua pelo mecanismo de caos definido no chaosScenarios do spec.qa.md
302
+ console.log(`[chaos] Iniciando injeção: {tipo-caos} em ${K8S_DEPLOYMENT}`);
303
+ // Ex para shutdown-pods:
304
+ // exec.test.abort() se kubectl não disponível — não falhe silenciosamente
305
+ }
306
+
307
+ export default function () {
308
+ const payload = JSON.stringify({ /* campos do happy path CT-01 */ });
309
+ const res = http.{method}(`${BASE_URL}{path}`, payload, { headers: HEADERS });
310
+
311
+ check(res, {
312
+ 'status is {código} or 503 durante caos': (r) => [200, 201, 503].includes(r.status),
313
+ });
314
+
315
+ sleep(1);
316
+ }
317
+
318
+ export function teardown() {
319
+ console.log('[chaos] Teardown: restaurar estado do ambiente');
320
+ }
321
+ ```
322
+
323
+ **Regras específicas para Chaos:**
324
+ - Baseado **sempre** no CT de sucesso (happy path)
325
+ - O mecanismo de caos vem do `chaosScenarios` do `spec.qa.md`
326
+ - Se o mecanismo for `shutdown-pods`: comentário explícito de que requer `kubectl`
327
+ - Thresholds mais tolerantes: `error_rate < 5%` e `p(95) < 2000ms`
328
+ - O script **não executa kubectl diretamente** — documenta o comando esperado via `console.log` e instrução em comentário
329
+ - Inclui `setup()` e `teardown()` para marcar início/fim do caos
330
+
331
+ ---
332
+
333
+ ### Gatling — E2E Script
334
+
335
+ **Estrutura obrigatória (Scala):**
336
+
337
+ ```scala
338
+ import io.gatling.core.Predef._
339
+ import io.gatling.http.Predef._
340
+ import scala.concurrent.duration._
341
+
342
+ class {FeatureName}E2E{CenarioName}Simulation extends Simulation {
343
+
344
+ val httpProtocol = http
345
+ .baseUrl(sys.env.getOrElse("BASE_URL", "http://localhost:8080"))
346
+ .header("Content-Type", "application/json")
347
+
348
+ val payload = """{
349
+ // payload do CT com valores reais
350
+ }"""
351
+
352
+ val scen = scenario("{nome do cenário}")
353
+ .exec(
354
+ http("{nome da requisição}")
355
+ .{method}("{path}")
356
+ .body(StringBody(payload))
357
+ .check(status.is({código}))
358
+ // Se Location header:
359
+ // .check(header("Location").exists)
360
+ // Se body field:
361
+ // .check(jsonPath("$.{campo}").is("{valor}"))
362
+ )
363
+
364
+ setUp(
365
+ scen.inject(atOnceUsers(1))
366
+ ).protocols(httpProtocol)
367
+ }
368
+ ```
369
+
370
+ ---
371
+
372
+ ### Kafka (qualquer ferramenta) — adaptações
373
+
374
+ Se o protocolo for Kafka (identificado no `spec.qa.md`):
375
+ - Substituir chamada HTTP por publish no tópico configurado via `__ENV.KAFKA_BOOTSTRAP_SERVERS`
376
+ - Para k6: usar extensão `xk6-kafka` com `import { Writer, SchemaRegistry } from 'k6/x/kafka'`
377
+ - Nomenclatura do arquivo: `{tool}-e2e-consume-{cenario}{ext}` (verbo `consume` em vez de método HTTP)
378
+ - O script publica a mensagem e faz polling do banco ou de um endpoint de healthcheck para confirmar persistência
379
+
380
+ ---
381
+
382
+ ### 5. Relatório final
383
+
384
+ Após implementar todas as tasks selecionadas:
385
+
386
+ ```
387
+ Implementação concluída para '{feature-name}'
388
+
389
+ Scripts criados:
390
+ [x] src/test/features/{feature-name}/e2e/{tool}-e2e-{...}{ext}
391
+ [x] src/test/features/{feature-name}/e2e/{tool}-e2e-{...}{ext}
392
+ [x] src/test/features/{feature-name}/load/{tool}-load-{...}{ext}
393
+ [x] src/test/features/{feature-name}/chaos-engineering/{tool}-dr-{...}{ext}
394
+
395
+ tasks.qa.md atualizado: {N}/{total} tasks concluídas
396
+
397
+ Como executar:
398
+ E2E: BASE_URL=http://seu-host k6 run src/test/features/{feature-name}/e2e/{tool}-e2e-{...}.js
399
+ Load: BASE_URL=http://seu-host k6 run src/test/features/{feature-name}/load/{tool}-load-{...}.js
400
+ Chaos: K8S_NAMESPACE=seu-ns BASE_URL=http://seu-host k6 run src/test/features/{feature-name}/chaos-engineering/{tool}-dr-{...}.js
401
+
402
+ Próximo passo: /testspec-run-qa para executar os testes via agente IA.
403
+ ```
404
+
405
+ ---
406
+
407
+ ## Guardrails
408
+
409
+ - **`instructions.md` e `current-feature.md` são lidos SEMPRE** como primeiro passo, antes de qualquer outra ação — não são opcionais
410
+ - **`instructions.md` tem precedência** sobre qualquer template ou default desta skill — se definir padrão de import, assertion, naming ou estrutura de função, siga-o exatamente
411
+ - **`current-feature.md` elimina a pergunta de seleção** — se contiver um nome de feature, use-o diretamente sem perguntar ao usuário
412
+ - **Leia `spec.qa.md` integralmente antes de gerar qualquer linha de código** — o contrato técnico é a fonte da verdade
413
+ - **Nunca use valores placeholder** no código gerado (ex: `"your-customer-id"`, `"TODO"`) — use os valores reais do `spec.qa.md`
414
+ - **Nunca hardcode** URL, host, porta, credencial ou token — sempre `__ENV.VARIAVEL`
415
+ - **Marque cada task como `- [x]` imediatamente** após criar o arquivo — nunca em lote
416
+ - **Um arquivo por task** — não combine múltiplos CTs em um mesmo script
417
+ - **Paths sempre relativos ao diretório atual** — nunca absolutos
418
+ - **Check names em k6 devem ser frases descritivas** — o relatório k6 os exibe como labels; evite nomes genéricos como `"ok"` ou `"check1"`
419
+ - **Não invente critérios de aceite** — implemente somente o que está no `spec.qa.md`; se um critério for impossível de validar na ferramenta, documente com `// NOTE:` e implemente o que for possível
420
+ - **Kafka**: nunca usar chamada HTTP no script se o protocolo for Kafka; adapter explicitamente para xk6-kafka ou equivalente
421
+ - **Chaos**: nunca executar comandos destrutivos diretamente — apenas documentar e logar a intenção via `console.log` e comentários
422
+ - **Run plan**: gerado **sempre** junto com cada script — incondicional, não há flag nem verificação
423
+ - **Um `.md` por script**: o run plan acompanha exatamente o script que descreve — nunca um `.md` cobrindo múltiplos scripts
424
+ - **Conteúdo do run plan**: use apenas dados reais extraídos de `spec.qa.md` — nunca placeholders como `{namespace}` no arquivo final