Guia de Criação de Flows com IA

Este recipe orienta agentes de IA (ou humanos auxiliados por IA) a produzir suites YAML completas para o Flow Test Engine com consistência, riqueza funcional e reaproveitamento máximo dos recursos já implementados no repositório.

1. Missão do Flow

  1. Entender o objetivo do cenário: qual API está sendo exercitada? Quais etapas de negócio precisam ser encadeadas? Há pré-condições ou dependências?
  2. Limite de escopo: priorize suites únicas com até três flows (caso precise dividir responsabilidades) e garanta que cada flow seja autoexplicativo nas descrições.
  3. Referência oficial: use o exemplo consolidado no final deste guia como checklist vivo dos recursos disponíveis.

2. Checklist Inicial Antes de Escrever YAML

  • Confirmar node_id, suite_name, description e metadata (priority + tags) coerentes com o propósito.
  • Definir base_url e variables necessários, incluindo anchors quando o fluxo for compartilhar cabeçalhos dentro do mesmo documento.
  • Mapear dados dinâmicos que exigem {{$env...}}, {{$faker...}} ou {{$js...}}.
  • Planejar quais etapas precisam de input interativo (incluindo selects encadeados, como no cenário de propostas).
  • Identificar quais valores precisarão ser compartilhados com outros flows (exports e exports_optional).

3. Estrutura Recomendada de Suite

node_id: "my_flow_id"
suite_name: "Descrição amigável"
description: "Propósito do fluxo"
metadata:
  priority: "high"
  tags: ["categoria", "subcategoria"]
base_url: "{{$js.return env.API_BASE_URL || 'https://api.example.local'}}"
variables:
  # Variáveis locais + anchors
exports: ["variavel_importante"]
steps:
  - name: "Etapa X"
    metadata: { ... }
    request: {...}
    assert: {...}
    capture: {...}
    input: {...}
    continue_on_failure: false

3.1 Variáveis e Interpolação

  • Env: {{$env.NOME}} para valores obrigatórios do ambiente.
  • Faker: {{$faker.internet.email}}, {{$faker.person.fullName}}, {{$faker.string.uuid}}, {{$faker.commerce.productName}}. Priorize métodos estáveis (person, internet, string, commerce, number).
  • JavaScript: {{$js.return ...}} ou expressões inline para composições, filtros e conversões.
  • Anchors YAML: use &common_headers / <<: *common_headers dentro do mesmo documento e exporte o objeto via capture se precisar reutilizá-lo em outro flow.

3.2 Headers Compartilhados Entre Flows

  1. No flow “setup”, capture o objeto de cabeçalhos e exporte:
    capture:
      common_headers: "body.resolved_headers"
    exports: ["common_headers", ...]
  2. Em flows dependentes, reconstrua manualmente usando as chaves exportadas:
    variables:
      shared_common_headers: &shared_common_headers
        Accept: "{{shared_setup.common_headers.Accept}}"
        Content-Type: "{{shared_setup.common_headers.\"Content-Type\"}}"
        x-correlation-id: "{{shared_setup.common_headers.\"x-correlation-id\"}}"
    request:
      headers:
        <<: *shared_common_headers
        X-Feature-Flag: "{{feature_toggle}}"

4. Padrões de Step

SituaçãoComponentes obrigatóriosExtras úteis
Request + Assertrequest, assert.status_code, assert.body, captureretry, metadata.tags, timeout, continue_on_failure
Input puroinput (single ou array)condition, ci_default, style
Request + Input dependenterequest, capture (gerando opções), input com selects encadeadosNovo step subsequente refinando dados (ver seção 5)
Iteraçãoiterate.over ou iterate.range + requestscenarios por iteração
Cenário condicionalscenarios + then/elseCapturas específicas por branch

5. Seleções Dependentes e Re-captura (modelo Proposta → Estágio)

Use o blueprint abaixo para construir o padrão Proposta → Estágio dentro de um único flow:

  1. Request inicial captura opções com JMESPath, incluindo snapshots completos (proposal_response_snapshot).
  2. Inputs encadeados: o primeiro select escolhe a proposta; o segundo usa o resultado anterior na expressão options.
  3. Reprocessamento: capture com {{$js: ...}} percorre o snapshot para retornar registros filtrados sem outra chamada.
  4. Follow-up request: valida a escolha e captura detalhes adicionais.
# Step 1 — Request + opções + inputs encadeados
steps:
  - name: "Seleciona proposta e estágio"
    request:
      method: GET
      url: "/admin/proposals/dashboard"
    capture:
      proposal_total_count: "length(body.rows)"
      proposal_selection_options: "body.rows[?flow.stages && length(flow.stages) > `0`].{value: to_string(id), label: join(' - ', [to_string(client.name), to_string(code), to_string(status)])}"
      proposal_response_snapshot: "body"
    input:
      - prompt: |
          Encontradas {{proposal_total_count}} propostas.
          Escolha uma opção para continuar:
        variable: proposal_choice_id
        type: "select"
        options: "proposal_selection_options"
        required: true
      - prompt: |
          Proposta {{proposal_choice_id}} selecionada.
          Agora escolha o estágio correspondente:
        variable: stage_choice_id
        type: "select"
        options: "proposal_response_snapshot.rows[?id == `{{proposal_choice_id}}`].flow.stages[*].{value: to_string(id), label: join(' - ', [to_string(name), to_string(code)])} | [0]"
        required: true

# Step 2 — Reprocessa snapshot localmente
  - name: "Deriva dados escolhidos"
    capture:
      selected_proposal_from_snapshot: |-
        {{js: (() => {
          const dataset = variables.proposal_response_snapshot || {};
          const rows = dataset.rows || [];
          return rows.find(row => String(row.id) === String(variables.proposal_choice_id));
        })() }}
      selected_stage_from_snapshot: |-
        {{js: (() => {
          const snapshot = variables.selected_proposal_from_snapshot || {};
          const stages = snapshot.flow?.stages || [];
          return stages.find(stage => String(stage.id) === String(variables.stage_choice_id));
        })() }}

6. Boas Práticas para IA ao Gerar Flows

  1. Narrativa consistente: mantenha descrições (description e metadata.description) claras e orientadas a negócio.
  2. Nomes autoexplicativos: node_id, step.name e variáveis devem refletir o contexto (ex.: reservation_id > id1).
  3. Falhas controladas: use continue_on_failure: true onde a suíte deve prosseguir (ex.: limpeza), e mantenha false quando a etapa for gate crítico.
  4. Retry consciente: adicione retry.max_attempts apenas em endpoints potencialmente instáveis.
  5. Capturas úteis: sempre registre IDs, estados, payloads essenciais e snapshots quando a próxima etapa depender do mesmo corpo.
  6. Exports enxutos: exporte apenas o necessário; se algo for opcional, mova para exports_optional.
  7. Referência cruzada: documente dependências via depends para encadear flows que compartilham variáveis exportadas.
  8. Validação: assegure assert.body para campos críticos (como success: { equals: true }) e assert.headers quando o contrato exigir.

7. Processo de Trabalho Recomendado para a IA

  1. Receber briefing: coletar requisitos do usuário (APIs envolvidas, dados necessários, entradas manuais, resultados esperados).
  2. Avaliar assets existentes: revisar o exemplo consolidado na seção “Apêndice A” e reutilizar os trechos aplicáveis.
  3. Planejar suite: desenhar steps em bloco, garantindo cobertura de setup → execução → validação → cleanup quando aplicável.
  4. Escrever YAML: produzir documento único com comentários claros, anotações de anchors e seções relevantes.
  5. Checagem estática: validar mentalmente (ou via lint interno da engine) se as expressões JMESPath e {{$js}} estão coerentes com a estrutura de resposta esperada.
  6. Orientar o usuário: junto do YAML, fornecer breve resumo das variáveis esperadas (envs, faker), dependências e próximos passos.

8. Snippets Prontos para Reuso

8.1 Cabeçalhos Compartilhados

variables:
  shared_headers_template: &shared_headers
    Accept: "application/json"
    Authorization: "Bearer {{session_token}}"

steps:
  - name: "Configura sessão"
    ...
    capture:
      shared_headers: "body.headers"
    exports: ["shared_headers"]

8.2 Input com fallback de CI

input:
  prompt: "Digite o limite de itens"
  variable: "page_size"
  type: "number"
  default: 25
  ci_default: 10
  validation:
    min: 1
    max: 100

8.3 Expressão JS para filtrar snapshot

capture:
  selected_item: |-
    {{js: (() => {
      const list = variables.items_snapshot || [];
      return list.find(item => item.id === variables.item_id);
    })() }}

9. Lista de Verificação Final

  • Todos os steps têm metadata.description descrevendo o objetivo.
  • Requests possuem assert.status_code e validações mínimas de payload.
  • Inputs retornam variáveis utilizadas posteriormente ou exportadas.
  • Variáveis reutilizadas entre flows estão exportadas corretamente.
  • Comentários explicam decisões não óbvias (anchors, js complexos, dependências).
  • Arquivo está formatado e organizado por blocos de comentários, se necessário.

10. Apêndice A — Exemplo Consolidado

Exemplo completo com três flows, cobrindo setup compartilhado, jornada principal com selects dependentes e cleanup resiliente. Use como referência imediata ao projetar novos artefatos.

---
node_id: "shared_setup"
suite_name: "Flow 1 – Configuração compartilhada"
description: "Constrói cabeçalhos base e variáveis globais"
metadata:
  priority: "critical"
  tags: ["setup", "shared", "headers"]
base_url: "{{$js.return env.CONFIG_BASE_URL || 'https://config.example.local'}}"
variables:
  tenant_id: "{{$js.return env.DEFAULT_TENANT_ID || 'tenant-demo'}}"
  store_code: "{{$faker.string.alphanumeric(6)}}"
  correlation_id: "{{$js.return `corr-${Math.random().toString(16).slice(2,10)}`}}"
  common_headers_template: &common_headers
    Accept: "application/json"
    Tenant-id: "{{tenant_id}}"
    x-store-code: "{{store_code}}"
steps:
  - name: "Captura cabeçalhos base"
    metadata:
      description: "Chama endpoint de configuração para exportar cabeçalhos"
    request:
      method: POST
      url: "/internal/config/build-headers"
      headers:
        <<: *common_headers
        X-Setup-Trace: "shared_setup"
      body:
        tenantId: "{{tenant_id}}"
        storeCode: "{{store_code}}"
        correlationId: "{{correlation_id}}"
    assert:
      status_code: 200
      body:
        resolved_headers:
          type: "object"
          notEmpty: true
    capture:
      common_headers: "body.resolved_headers"
      correlation_id: "body.resolved_headers.\"x-correlation-id\""
    continue_on_failure: false
exports: ["common_headers", "correlation_id", "tenant_id", "store_code"]

---
node_id: "catalog_and_proposals"
suite_name: "Flow 2 – Jornada principal"
description: "Busca catálogo, seleciona proposta/estágio e cria reserva"
metadata:
  priority: "high"
  tags: ["journey", "catalog", "proposals"]
base_url: "{{$js.return env.API_BASE_URL || 'https://api.demo.local'}}"
depends:
  - node_id: "shared_setup"
    required: true
variables:
  tenant_id: "{{shared_setup.tenant_id}}"
  correlation_id: "{{shared_setup.correlation_id}}"
  shared_common_headers: &shared_common_headers
    Accept: "{{shared_setup.common_headers.Accept}}"
    Tenant-id: "{{shared_setup.common_headers.\"Tenant-id\"}}"
    x-store-code: "{{shared_setup.common_headers.\"x-store-code\"}}"
  account_token: "{{$env.ACCOUNT_TOKEN}}"
steps:
  - name: "Itera catálogo"
    metadata:
      description: "Percorre páginas do catálogo para capturar itens"
    iterate:
      range: "1..2"
      as: "page_index"
    request:
      method: GET
      url: "/catalog/items"
      headers:
        <<: *shared_common_headers
        X-Page-Index: "{{page_index}}"
      params:
        tenant: "{{tenant_id}}"
        page: "{{page_index}}"
        size: 20
    assert:
      status_code: 200
      body:
        items:
          type: "array"
    capture:
      catalog_item_id: "body.items[0].id"
      owner_ids: "body.items[*].owner_id"

  - name: "Seleciona proposta e estágio"
    metadata:
      description: "Captura opções e executa selects encadeados"
    request:
      method: GET
      url: "/admin/proposals/dashboard"
      headers:
        <<: *shared_common_headers
        Authorization: "Bearer {{account_token}}"
    assert:
      status_code: 200
      body:
        rows:
          type: "array"
          notEmpty: true
    capture:
      proposal_total_count: "length(body.rows)"
      proposal_selection_options: "body.rows[?flow.stages && length(flow.stages) > `0`].{value: to_string(id), label: join(' - ', [to_string(client.name), to_string(code), to_string(status), 'Stages:', to_string(length(flow.stages))])}"
      proposal_response_snapshot: "body"
    input:
      - prompt: |
          Encontradas {{proposal_total_count}} propostas. Escolha uma:
        variable: proposal_choice_id
        type: "select"
        options: "proposal_selection_options"
        required: true
      - prompt: |
          Proposta {{proposal_choice_id}} selecionada. Escolha o estágio:
        variable: stage_choice_id
        type: "select"
        options: "proposal_response_snapshot.rows[?id == `{{proposal_choice_id}}`].flow.stages[*].{value: to_string(id), label: join(' - ', [to_string(name), to_string(code)])} | [0]"
        required: true

  - name: "Deriva dados da escolha"
    metadata:
      description: "Filtra snapshot localmente e confirma com nova chamada"
    capture:
      selected_proposal_from_snapshot: |-
        {{js: (() => {
          const dataset = variables.proposal_response_snapshot || {};
          const rows = dataset.rows || [];
          return rows.find(row => String(row.id) === String(variables.proposal_choice_id));
        })() }}
      selected_stage_from_snapshot: |-
        {{js: (() => {
          const snapshot = variables.selected_proposal_from_snapshot || {};
          const stages = snapshot.flow?.stages || [];
          return stages.find(stage => String(stage.id) === String(variables.stage_choice_id));
        })() }}
    request:
      method: GET
      url: "/admin/proposals/{{proposal_choice_id}}/stages/{{stage_choice_id}}"
      headers:
        <<: *shared_common_headers
        Authorization: "Bearer {{account_token}}"
    assert:
      status_code: 200
      body:
        proposal:
          type: "object"
    capture:
      proposal_stage_snapshot: "body"
    continue_on_failure: false

  - name: "Reserva item do catálogo"
    metadata:
      description: "Usa dados coletados para criar uma reserva"
    request:
      method: POST
      url: "/catalog/reservations"
      headers:
        <<: *shared_common_headers
        Authorization: "Bearer {{account_token}}"
      body:
        itemId: "{{catalog_item_id}}"
        proposalId: "{{proposal_choice_id}}"
        stageId: "{{stage_choice_id}}"
        correlationId: "{{correlation_id}}"
    assert:
      status_code: 201
      body:
        reservationId:
          exists: true
    capture:
      reservation_id: "body.reservationId"
exports: ["catalog_item_id", "owner_ids", "proposal_choice_id", "stage_choice_id", "reservation_id"]

---
node_id: "cleanup_flow"
suite_name: "Flow 3 – Limpeza"
description: "Remove dados artificiais e gera relatório"
metadata:
  priority: "medium"
  tags: ["cleanup", "report"]
depends:
  - node_id: "shared_setup"
    required: true
  - node_id: "catalog_and_proposals"
    required: true
variables:
  cleanup_headers: &cleanup_headers
    Accept: "application/json"
    Tenant-id: "{{shared_setup.tenant_id}}"
    x-store-code: "{{shared_setup.store_code}}"
steps:
  - name: "Confirma limpeza"
    metadata:
      description: "Permite pular cleanup localmente"
    input:
      prompt: "Executar cleanup final?"
      variable: "execute_cleanup"
      type: "confirm"
      default: true
      ci_default: true

  - name: "Remove usuários gerados"
    metadata:
      description: "Percorre IDs capturados na jornada"
    iterate:
      over: "{{catalog_and_proposals.owner_ids}}"
      as: "user_id"
    condition: "{{execute_cleanup}} == true"
    request:
      method: DELETE
      url: "/users/{{user_id}}"
      headers:
        <<: *cleanup_headers
    assert:
      status_code:
        greater_than: 199
        less_than: 300
    continue_on_failure: true

  - name: "Resumo final"
    metadata:
      description: "Publica relatório consolidado"
    request:
      method: POST
      url: "/reports/summary"
      headers:
        <<: *cleanup_headers
      body:
        flows: ["shared_setup", "catalog_and_proposals", "cleanup_flow"]
        reservationId: "{{catalog_and_proposals.reservation_id}}"
        correlationId: "{{shared_setup.correlation_id}}"
    assert:
      status_code: 201

Resumindo: a IA deve sempre produzir flows ricos, comentados, tecnicamente válidos e aproveitando ao máximo as capacidades da engine—capturas, inputs condicionais, reuso de variáveis e exportações. Use este guia como checklist para entregar artefatos prontos para execução ou rápida adaptação pelo time.

Flow Test Engine
© 2025 Flow Test Engine. Dynamic Reporting System.