Pular para o conteúdo

Emuladores ​

Um emulador é o Signal Lab fazendo o papel da API, do dispositivo ou do serviço com que o seu sistema conversa. Ele escuta em um endereço e responde por regras: uma API HTTP, por rotas; um dispositivo OSC, UDP ou TCP, por "quando chegar isto, responda aquilo"; um broker MQTT, como qualquer broker, mais regras próprias. Ele pode ser lento, falhar ou sair do ar de vez em quando, para que você teste o que o seu sistema faz quando uma dependência se comporta mal. Cada troca é contada, listada e enviada ao Inspetor.

Um emulador é um documento. A tela Emuladores guarda uma biblioteca deles; o mesmo documento é executado dentro de um experimento como nó Emulador, pela linha de comando com signallab emulate e pela API e pelo MCP, e responde do mesmo jeito em todo lugar.

A tela ​

À esquerda fica a biblioteca (Biblioteca): cada emulador com o protocolo e o endereço, e um ponto pulsante e uma contagem de requisições nos que estão rodando. À direita ficam as configurações e as regras do emulador selecionado e, abaixo delas, o que ele recebeu (Ao vivo).

Criar um emulador ​

  1. Pressione um dos botões no alto da biblioteca:

    BotãoCriaEscuta emCom uma regra que funciona como está
    + API HTTPUma API HTTP127.0.0.1:18080GET /health → 200 {"status":"ok"}
    + Dispositivo OSCUm dispositivo OSC127.0.0.1:9100/ping → /pong com a contagem como int
    + Dispositivo UDPUm dispositivo UDP127.0.0.1:7100um datagrama que contém PING → PONG 1, PONG 2, …
    + Dispositivo TCPUm dispositivo TCP127.0.0.1:7200uma linha que contém PING → PONG
    + Broker MQTTUm broker MQTT127.0.0.1:1883uma publicação em lab/<name>/set → a mesma carga útil, retida, em lab/<name>/state

    Quando outro emulador da biblioteca já usa essa porta, a próxima porta livre é usada.

  2. Dê a ele um Nome (no máximo 120 caracteres).

  3. Preencha o campo Escutar em com IP:port. 127.0.0.1 responde só a este computador; 0.0.0.0 responde também à rede.

  4. Altere as regras (abaixo) e diga na Nota o que ele substitui.

As alterações são salvas sozinhas. O botão Duplicar faz uma cópia na próxima porta livre. O botão Excluir pergunta mais uma vez (Excluir?), para o emulador se ele estiver rodando e o remove da biblioteca.

As regras são testadas em ordem, da primeira à última; a primeira que corresponde responde. O cabeçalho de cada regra mostra um resumo de uma linha; clique nele para abrir ou recolher a regra. Os botões ↑ e ↓ movem uma regra, e × a remove.

Executá-lo ​

  1. Selecione o emulador e pressione Iniciar. A porta dele abre antes que o botão volte: se a porta já estiver ocupada, ou se o emulador tiver um problema, o início é recusado ali, com o motivo.
  2. Aponte o seu sistema para ele. Para uma API HTTP, Copiar URL copia o endereço dela (http://127.0.0.1:18080), e cada rota tem um botão Copiar a URL para o seu próprio endereço (não quando o caminho dela contém um modelo {{…}}).
  3. Veja a lista Recebidas se encher.
  4. Pressione Parar, ou pare a tarefa dele na faixa do console.

O estado ao lado dos botões diz Parado, onde ele responde ou que está fora do ar.

Um emulador continua respondendo com as regras com que foi iniciado. Quando você o altera enquanto ele roda, aparece o botão Reiniciar: pressione-o para iniciá-lo de novo com as regras como estão agora. Até lá, as contagens de correspondências das regras ficam escondidas, pois pertencem às regras antigas.

O botão Derrubar deixa indisponível um emulador em execução até você pressionar Reativar: uma requisição HTTP recebe 503, um dispositivo TCP e um broker MQTT derrubam as conexões e recusam novas, um dispositivo OSC ou UDP não responde nada. Veja Fora do ar.

Dois emuladores do mesmo transporte não podem compartilhar uma porta: os emuladores HTTP, TCP e MQTT escutam em portas TCP; os OSC e UDP, em portas UDP. Uma API HTTP e um dispositivo OSC podem ambos usar a porta 8080; duas APIs HTTP, não. Um segundo emulador em uma porta ocupada é recusado quando é iniciado.

TIP

Em um navegador conectado a um servidor, o emulador roda no servidor. Um emulador que escute em 0.0.0.0 é alcançado pelo nome do servidor, e Copiar URL copia esse endereço; um em 127.0.0.1 responde só a programas no próprio servidor.

O que chegou ​

Enquanto ele roda, o painel Ao vivo conta:

ContagemO quê
RequisiçõesTudo o que chegou: requisições, mensagens, linhas.
Sem regraO que nenhuma regra aceitou. Uma requisição HTTP sem rota ainda recebe a sua resposta (veja Requisições que nenhuma rota aceita); as outras não recebem nenhuma.
Com falhaTrocas em que não foi possível montar ou enviar uma resposta.
Fora do arO que chegou enquanto o emulador estava fora do ar. Aparece quando ele tem uma queda programada ou quando algo o encontrou fora do ar. Nunca conta como Sem regra.
Não entreguesSó MQTT, quando acontece: mensagens que um cliente estava atrasado demais para receber.

O cabeçalho de cada regra mostra quantas vezes ela correspondeu desde o início.

A lista Recebidas mostra as 300 trocas mais recentes, a mais nova primeiro:

ColunaO quê
HoraQuando chegou.
OrigemO endereço do cliente.
RequisiçãoO que chegou, em notação do protocolo: GET /users/7, /ping 1, POWER?.
RegraA regra que a aceitou (#2), ou —.
RespostaO que voltou: 200 OK · 37 B, /pong 3, uma carga útil; segurada ou fechada, para uma falha; o erro, quando a resposta falhou; fora do ar, quando chegou com o emulador fora do ar.
msDa chegada até a saída da resposta, com o atraso incluído.

O botão ⌕ de uma linha (Abrir no Inspetor) abre essa troca no Inspetor, quando a captura estava ativa. Quando chegam mais de 200 trocas em um quinto de segundo, a lista pula algumas e diz quantas. O motor guarda as 500 trocas mais recentes de cada emulador em execução, com o que chegou, para a linha de comando, a API e o MCP.

API HTTP ​

Um servidor HTTP/1.1. Cada requisição é respondida pela primeira rota que a aceita.

Rotas ​

Uma rota aceita uma requisição quando o método, o caminho e todas as condições dela correspondem.

CampoO quê
MétodoGET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS ou Qualquer. Uma rota GET responde também a HEAD.
CaminhoComeça com /. Um segmento :name aceita qualquer segmento, lido como {{request.params.name}}; um último segmento * aceita tudo o que vem abaixo. Uma / no final não faz diferença; a query string não faz parte do caminho.
CondiçõesTodas precisam se cumprir. Acrescente uma com + Condição.

Exemplos de caminho:

CaminhoAceitaNão aceita
/health/health, /health//health/db, /Health
/users/:id/users/7 (params.id é 7), /users/a%20b (a b)/users, /users/7/orders
/files/*/files, /files/a, /files/a/b/c/file, /other/files/a

Uma condição lê uma parte da requisição (Onde) e a compara:

OndeNomeLê
CabeçalhoO nome de um cabeçalho, sem distinção de maiúsculasO valor do cabeçalho; para um cabeçalho enviado várias vezes, os valores unidos com , .
ConsultaUm parâmetro da queryO valor dele, decodificado; o primeiro, quando ele se repete.
Corpo—O corpo inteiro como texto.
JSONUm caminho JSON, como $.user.idEsse campo de um corpo JSON.

As comparações são é igual a, é diferente de, menor que, no máximo, maior que, no mínimo, contém, corresponde à regex, está vazio e não está vazio. Números são comparados como números; texto, de forma exata. Um cabeçalho, parâmetro ou campo que não existe está vazio. Uma comparação que não pode ser feita — texto contra número — não se cumpre.

Respostas ​

Uma rota tem de uma a 16 respostas (Respostas).

CampoO quêPadrão
Status100–599.200
FalhaAlgo diferente de uma resposta; veja Falhas.Nenhuma — responder
Atraso, msQuanto esperar antes de responder, 0–60.000 ms.0
Jitter, msAté este tanto a mais, ao acaso, 0–60.000 ms.0
PesoA participação dela quando a rota responde ao acaso. Só aparece nesse caso.1
CabeçalhosAté 32. Os nomes podem usar parâmetros; os valores são modelos.nenhum
CorpoUm modelo, até 256 KiB como escrito.vazio

Sem um cabeçalho Content-Type, um corpo que é JSON válido vai como application/json, e qualquer outro corpo como text/plain; charset=utf-8.

Com duas respostas ou mais, o campo Qual resposta diz qual delas uma requisição recebe:

Qual respostaAs requisições recebemPara
Em sequência, depois a últimaA primeira, a segunda, …, e daí em diante a última: 500, 500, 200, 200, 200…Novas tentativas: falhar duas vezes e depois funcionar.
Em rodízioA primeira de novo depois da última: 200, 500, 200, 500…Uma dependência que falha de vez em quando, com regularidade.
Aleatoriamente, por pesoCada uma sorteada pelo seu peso. Pesos 8 e 2 dão a primeira em cerca de 80% das vezes. Pelo menos um peso precisa ser maior que 0.Uma proporção realista de falhas.

O menu Adicionar uma resposta acrescenta à rota uma resposta pronta:

PredefiniçãoAcrescenta
200 JSON200, {"ok":true}
201 Created201, {"id":"{{uuid}}"}, cabeçalho Location: {{request.path}}/{{counter}}
404 Not found404, {"error":"not found"}
500 Server error500, {"error":"internal"}
503 Unavailable503, {"error":"unavailable"}, cabeçalho Retry-After: 1
Lenta — 2 s200, {"ok":true} depois de 2.000 ms
Sem respostaA falha Sem resposta
Conexão fechadaA falha Fechar a conexão
JSON malformado200, {"items":[{"id":1},{"id":2}]} com a falha Corpo malformado

Falhas ​

FalhaO que o cliente encontra
Nenhuma — responderA resposta.
Sem respostaNada. A requisição fica segurada por até 2 minutos e depois a conexão é fechada — assim, o que se testa é o próprio timeout do cliente. O atraso não se aplica.
Fechar a conexãoA conexão fecha sem resposta, depois do atraso.
Corpo malformadoUma resposta HTTP completa, com o status e os cabeçalhos definidos, cujo corpo para no meio: um JSON que não pode ser interpretado. Quando o corpo inteiro era JSON, o tipo de conteúdo continua dizendo application/json.

Requisições que nenhuma rota aceita ​

A opção Requisições que nenhuma rota aceita decide o que recebe uma requisição que não corresponde a nenhuma rota:

  • 404 Not found — 404 com o corpo {"error":"no_route"};
  • Esta resposta — uma resposta que você define, com tudo o que a resposta de uma rota tem. O {{counter}} dela conta as requisições que nenhuma rota aceitou.

De um jeito ou de outro, a requisição conta como Sem regra.

O que uma resposta HTTP pode ler ​

ModeloÉ
{{request.method}}GET, POST, …
{{request.path}}O caminho, sem a query.
{{request.params.id}}O segmento do caminho chamado :id.
{{request.query.page}}Um parâmetro da query, decodificado.
{{request.headers.x-key}}Um cabeçalho; nomes em minúsculas.
{{request.body}}O corpo como texto: os primeiros 64 KiB dele.
{{request.json.name}}Um campo de um corpo JSON, quando o corpo é JSON e cabe em 64 KiB.
{{request.from}}O IP:port do cliente.

Um corpo de requisição maior que 1 MiB recebe 413 e conta como Com falha. Uma resposta que não pode ser montada — um modelo que cita algo que a requisição não tem — recebe 500 com o erro no corpo e conta como Com falha.

Dispositivo OSC ​

Cada mensagem que chega — cada mensagem de um bundle, separadamente — é respondida pela primeira regra a que ela corresponde. Um datagrama que não é OSC conta como Sem regra.

CampoO quê
Padrão de endereçoUm padrão de endereço OSC 1.0: * quaisquer caracteres, ? um caractere, [a-z] um conjunto, {a,b} um ou outro, cada um dentro de um segmento (veja OSC).
Regras de argumentoAté 16 condições sobre os argumentos, como em Aguardar OSC (veja Nós).
ResponderDesmarcado: aceita a mensagem e não responde nada.
Endereço da respostaO endereço da resposta, um modelo.
Argumentos da respostaAté 16 argumentos, cada um com um Tipo (int, float, str, long, double, bool, blob, nil) e um modelo em Valor.
Responder paraVazio: de volta ao endereço e à porta do remetente. Caso contrário, IP:port.
Atraso, ms, Jitter, ms0–60.000 ms cada.

O valor de um argumento é lido como o tipo dele depois que o modelo é preenchido: {{request.args[0]}} devolve o primeiro argumento como número quando o tipo é numérico. Um bool aceita true, 1, yes, on ou false, 0, no, off; um blob aceita bytes em hex; um valor vazio é o zero do tipo.

As respostas saem da própria porta do emulador, então um cliente que escuta na porta de onde enviou as ouve.

Uma resposta OSC pode ler {{request.address}}, {{request.args[0]}} e {{request.from}}.

Dispositivo UDP ​

Cada datagrama é respondido pela primeira regra a que ele corresponde.

CampoO quê
CorrespondênciaQualquer datagrama, Contém o texto, Corresponde à regex ou Contém os bytes (hex).
PadrãoO texto, a expressão regular ou os bytes a procurar.
RespostaAceitar sem responder, Texto ou Hex e depois a própria resposta, como modelo.
Responder paraVazio: de volta ao remetente. Caso contrário, IP:port.
Atraso, ms, Jitter, ms0–60.000 ms cada.

Uma resposta UDP ou TCP pode ler:

ModeloÉ
{{request.text}}A carga útil como texto.
{{request.match}}O que correspondeu: o texto, o primeiro grupo de uma expressão regular (ou a correspondência inteira), os bytes.
{{request.hex}}A carga útil em bytes hex, os primeiros 1.024 deles.
{{request.bytes}}O tamanho da carga útil.
{{request.from}}O IP:port do remetente.

Uma resposta em texto tem no máximo 65.507 bytes.

Dispositivo TCP ​

Um dispositivo que fala por linhas em uma conexão TCP, como fazem um projetor ou um switcher matricial. Cada mensagem que um cliente envia é respondida pela primeira regra a que ela corresponde; a resposta volta pela mesma conexão.

CampoO quê
Fim de mensagemO que termina uma mensagem e é acrescentado depois de cada resposta e da saudação: LF (\n) (um \r antes dele é descartado), CR LF (\r\n), CR (\r) ou Nenhum — cada bloco. Linhas vazias são ignoradas.
SaudaçãoEnviada quando um cliente se conecta; vazia para nenhuma. Pode ler {{request.from}}.
Correspondência, Padrão, RespostaComo em um dispositivo UDP.
Depois fechar a conexãoFecha a conexão depois da resposta desta regra — para QUIT, por exemplo.
Atraso, ms, Jitter, ms0–60.000 ms cada.

Uma mensagem com mais de 64 KiB sem o delimitador é aceita como está.

Broker MQTT ​

Um pequeno broker MQTT 3.1.1 sobre TCP simples. Ele faz o que um broker faz: os clientes se conectam, assinam com + e #, publicam com QoS 0, 1 e 2, mensagens retidas e mensagens de última vontade (will) funcionam, e uma segunda conexão com o id de um cliente assume o lugar da primeira. As sessões são sempre limpas: um cliente que pede para manter a sessão recebe uma nova, e nada fica na fila para um cliente ausente.

Além disso, cada mensagem publicada nele é comparada com as regras: a primeira que corresponde também publica uma resposta — um dispositivo informando o que fez.

CampoO quê
Nome de usuário, SenhaQuando um nome de usuário está definido, um cliente precisa se conectar com ele e com a senha; vazio: qualquer um pode se conectar. Uma senha sem nome de usuário é recusada, pois o MQTT 3.1.1 não consegue transportá-la.
RetidasAté 64 mensagens (Tópico, Carga útil, QoS) guardadas desde o início, como se publicadas com retain: um cliente que assina as recebe primeiro.
Filtro de tópicosQuais tópicos uma regra aceita: + um nível, # o resto — lab/+/set.
Correspondência, PadrãoUma condição sobre a carga útil, como em um dispositivo UDP.
ResponderDesmarcado: aceita a mensagem e não publica mais nada.
Tópico da resposta, Carga útil da respostaModelos. O tópico não pode conter + nem #.
QoS, RetainDa resposta.
Atraso, ms, Jitter, ms0–60.000 ms cada.

Uma resposta MQTT pode ler {{request.topic}}, {{request.levels[1]}} (os níveis do tópico, a partir de 0), {{request.payload}}, {{request.json.state}}, {{request.match}}, {{request.qos}}, {{request.retain}}, {{request.client}} (o id do cliente) e {{request.from}}.

Modelos nas respostas ​

As respostas são escritas na mesma linguagem de modelos dos experimentos, então um campo significa a mesma coisa aqui e lá. Uma resposta pode ler:

  • request — o que chegou, conforme listado acima para cada protocolo;
  • {{counter}} — quantas mensagens esta regra aceitou desde que o emulador foi iniciado, incluindo esta;
  • os geradores — {{uuid}}, {{now.iso}}, valores aleatórios e os demais; os aleatórios são sorteados a partir da semente do emulador;
  • parâmetros, quando o emulador roda em um experimento ou é iniciado com signallab emulate --param.

Uma resposta nunca lê segredos, e um nome desconhecido é um erro, não um texto vazio.

Alguns campos são fixados quando o emulador é iniciado, antes que algo chegue: um caminho, uma condição, um padrão de endereço, um padrão de carga útil, um filtro de tópicos, Responder para, o nome de um cabeçalho, as mensagens retidas e o login do broker. Eles aceitam só texto e parâmetros, sem request e sem geradores.

A semente comanda a ordem aleatória das respostas, o jitter e os geradores aleatórios. Na tela Emuladores, cada início usa uma semente nova; um experimento usa a semente da execução, e signallab emulate --seed usa a que você informar.

Fora do ar ​

Para testar o que o seu sistema faz quando uma dependência oscila, marque a opção Cai de vez em quando:

CampoO quêPadrão
No ar por, msPor quanto tempo ele responde, 10–3.600.000 ms.10.000
Fora do ar por, msPor quanto tempo ele fica fora do ar, 10–3.600.000 ms.3.000
Enquanto fora do arSó HTTP: o que uma requisição encontra enquanto ele está fora do ar.503 Unavailable

O ciclo começa quando o emulador é iniciado e se repete: no ar, fora do ar, no ar, fora do ar… Enquanto ele está fora do ar:

EmuladorO que se encontra
HTTP503 Unavailable: 503 com Retry-After definido com os segundos que faltam para ele voltar (pelo menos 1). Fechar a conexão: a conexão fecha sem resposta. Sem resposta: segurada por até 2 minutos e depois fechada.
Dispositivo TCPAs conexões abertas caem em até 0,1 s; as novas são fechadas assim que chegam.
Broker MQTTTodas as conexões caem; as novas são recusadas (código de retorno 3 no CONNACK, servidor indisponível).
Dispositivo OSC, UDPNada é respondido.

O que chega enquanto ele está fora do ar conta como Fora do ar, não como Sem regra, e as regras dele não são consultadas.

O botão Derrubar faz o mesmo quando você quiser, diga o ciclo o que disser, até você pressionar Reativar; o HTTP então encontra 503 sem Retry-After. Em um experimento, o nó Derrubar/reativar emulador faz isso em uma etapa da execução (veja Nós e Falhas).

Problemas ​

Enquanto você edita, o emulador é verificado um instante depois de cada alteração, e um problema aparece abaixo dos botões dele antes que você pressione Iniciar. Um problema diz onde está — a regra, a resposta ou a mensagem retida, e o campo — e o que está errado: um caminho sem a sua /, uma expressão regular que não compila, um modelo de resposta que cita algo além de request, parâmetros e geradores, um valor fora do intervalo. O botão Iniciar recusa um emulador com um problema.

Limites ​

O quêLimiteNo limite
Rotas ou regras por emulador64Recusado na verificação.
Respostas por rota16Recusado.
Condições por rota16Recusado.
Cabeçalhos por resposta32Recusado.
Condições de argumento, argumentos da resposta (OSC)16 cadaRecusado.
Mensagens retidas (MQTT)64Recusado.
Um corpo, uma resposta ou uma saudação, como escrito256 KiBRecusado.
Um atraso ou um jitter60.000 msRecusado.
Corpo de requisição HTTP1 MiB413.
Conexões HTTP simultâneas512As excedentes são fechadas assim que chegam.
Cabeçalho de requisição HTTP30 sUm cliente precisa enviá-lo dentro desse prazo.
Conexões TCP simultâneas256As excedentes são fechadas assim que chegam.
Respostas OSC e UDP aguardando o atraso1.024As excedentes são descartadas e contadas como Com falha.
Clientes MQTT simultâneos256Os excedentes são fechados assim que chegam.
Pacote MQTT256 KiBA conexão do cliente termina.
Assinaturas MQTT por cliente100As excedentes são recusadas.
Tópicos retidos MQTT1.000 tópicos, 16 MiBUma nova mensagem retida é encaminhada, mas não retida.
Mensagens MQTT aguardando um cliente lento1.024 mensagens, 8 MiBEle as perde; contadas como Não entregues.

Simular isto ​

Para criar um emulador a partir de uma resposta que funcionou:

  1. Na tela HTTP, envie uma requisição e obtenha uma resposta — ou use Enviar agora em um nó HTTP de um experimento.
  2. Pressione ⧉ Simular isto ao lado da resposta. A caixa de diálogo Simular esta resposta mostra a rota que será criada.
  3. No campo Adicionar a, escolha um dos seus emuladores HTTP, ou Um novo emulador.
  4. Pressione Adicionar a rota. A tela Emuladores abre nesse emulador.

A rota responde ao método e ao caminho da requisição (sem a query) com o status, os cabeçalhos e o corpo da resposta. Os cabeçalhos que pertencem àquela troca específica (Content-Length, Date, Server, ETag e afins) ficam de fora, e o corpo é enviado como era, mesmo que contenha {{. Um emulador novo contém só essa rota. Acrescentada a um emulador existente, a rota vai para o início, para responder antes de uma rota mais ampla; um emulador em execução a adota quando você pressiona Reiniciar.

A partir de um experimento, uma URL escrita com modelos vira um padrão: a base dela ({{api}}) é descartada, um segmento que é um único modelo (/orders/{{order_id}}) vira :order_id, e um segmento só em parte com modelo termina o caminho com *.

O conjunto inicial ​

Na primeira vez que o Signal Lab não encontra uma biblioteca de emuladores, ele grava cinco, todos neste computador. Os nomes e as notas deles são escritos no idioma que a interface tem nesse momento.

EmuladorEscuta emFaz
API de demonstração127.0.0.1:8080GET /health → {"status":"ok","time":…}; GET /users/:id → um usuário com esse id; POST /users → 201 com um Location; GET /slow → depois de 1.500 ms; /flaky → 503, 503 e depois 200 daí em diante.
Dispositivo OSC de demonstração127.0.0.1:9100/ping → /pong com a contagem; /fader/* → /ack com o endereço recebido; /cue/* aceito sem resposta.
Dispositivo UDP de demonstração127.0.0.1:7100PING → PONG e a contagem; qualquer outra coisa → ACK e o tamanho dela em bytes.
Dispositivo TCP de demonstração127.0.0.1:7200Linhas terminadas em CR LF. Cumprimenta com READY; POWER? → POWER=ON; POWER ON ou POWER OFF → OK ON / OK OFF; QUIT → BYE e depois desliga.
Broker MQTT de demonstração127.0.0.1:1883Retém online em lab/status; ON ou OFF publicado em lab/<name>/set → o mesmo, retido, em lab/<name>/state.

O sinal inicial O serviço está no ar? da biblioteca de sinais consulta http://127.0.0.1:8080/, o endereço da API de demonstração: como ela não tem rota para /, o sinal recebe 404.

O arquivo da biblioteca ​

A biblioteca é o emulators.json na pasta de dados (veja Arquivos); passe o ponteiro sobre a contagem abaixo da lista para ver o caminho dele. Ele é gravado inteiro 0,7 s depois da última alteração, por meio de um arquivo temporário, então uma gravação que falha deixa o anterior. Se o arquivo não puder ser lido, a lista mostra o erro com o caminho, a linha e a coluna, e o arquivo fica como está: corrija-o e pressione Recarregar o arquivo. Pressione Recarregar o arquivo também depois de editá-lo à mão. Sem arquivo, o conjunto inicial é gravado de novo.

json
{
  "version": 1,
  "emulators": [
    {
      "id": "orders-api",
      "note": "Stands in for the orders service.",
      "emulator": {
        "name": "Orders API",
        "bind": "127.0.0.1:18080",
        "protocol": "http",
        "routes": [
          { "method": "GET", "path": "/orders/:id",
            "responses": [{ "body": "{\"id\":\"{{request.params.id}}\",\"state\":\"open\"}" }] },
          { "method": "POST", "path": "/orders", "order": "sequence",
            "responses": [{ "status": 503 }, { "status": 201, "body": "{\"id\":\"{{uuid}}\"}" }] }
        ],
        "outage": { "up_ms": 20000, "down_ms": 2000, "fault": "unavailable" }
      }
    }
  ]
}

Só o objeto emulator já é um documento que o signallab emulate também lê.

Em experimentos e scripts ​

  • Em um experimento, um nó Emulador abre o emulador dele antes da primeira etapa e responde até o fim da execução; o que ele recebeu é contado no relatório. Um emulador HTTP ali é também o que Aguardar requisição HTTP (Nós) escuta, e um emulador OSC ou UDP compartilha a porta dele com as esperas da execução. Dois emuladores do mesmo transporte em um mesmo experimento não podem compartilhar uma porta. Veja Nós e Falhas.
  • signallab emulate executa emuladores a partir de arquivos ou desta biblioteca até Ctrl+C ou --for, imprimindo o que eles respondem; veja A linha de comando.