3 formas de aplicar um replace em todas as ocorrencias encontradas em um texto, com JavaScript

Fala pessoal, tudo em ordem? Espero que sim!

Alguns dias atrás estava pesquisando sobre formas de aplicar um replace em um texto para todas as ocorrências que tiver encontrado, com nosso querido JavaScript =)

– Um forma de fazer, seria o uso das funções “split” e “join”:

let originalText = "teste 1 teste 1 teste teste 1 ";
let replaceText = originalText.split("1 ").join("");

document.write(`Texto original "${originalText}"`);
document.write("<br><br>");
document.write(`Texto aplicando replace com join ${replaceText}`);

Outras duas opções seriam com uso de REGEX, contudo seu uso em trechos de código que a perfomance seja um ponto importante não é recomendado, fiz um teste e o split com join foi mais rápido, no final eu posto o resultado:

– Com a function replace e regex por parametro.

let originalText = "teste 1 teste 1 teste teste 1 ";
const regex = /1 /gi;
let replaceAllText = originalText.replace(new RegExp('1 ','g'), '');

document.write(`Texto original "${originalText}"`);
document.write("<br><br>");
document.write(`Texto aplicando replace com join ${replaceAllText}`);

– Com a function replaceAll e regex por parametro, entretanto não é suportado por todos os navegadores, por se tratar do ECMA SCRIPT 2021 conhecido como ECMA-262

let originalText = "teste 1 teste 1 teste teste 1 ";
const regex = /1 /gi;
let replaceAllText = originalText.replaceAll(regex,'');

document.write(`Texto original "${originalText}"`);
document.write("<br><br>");
document.write(`Texto aplicando replace com join ${replaceAllText}`);

Resultado do benchmark:

Espero que tenha ajudado.

Abraços e até a próxima. 😉

Links:

Benchmark: https://jsben.ch/WlYZp/
Codigo-Fonte: https://jsfiddle.net/joaopauloit/gnm1vap0/62/
Fonte: https://stackoverflow.com/questions/1144783/how-to-replace-all-occurrences-of-a-string/

Como verificar o tamanho das tabelas de um banco de dados no SQL Server

Fala pessoal, fiquei ausente por um tempo resolvendo alguns compromissos e por este motivo não consegui postar material nesse periodo, mas estou voltando aos poucos.

Hoje vou compartilhar uma query que costumo usar para ver o tamanho das tabelas de um banco de dados, isso vai ajudar muito quando você está com uma base grande e precisando identificar qual tabela está consumindo mais dados de armazenamento para poder planejar rotinas de manutenção/arquivamento de dados.

Vamos para o script? Go go go ahead 🙂

SELECT 
    t.NAME AS TableName,
    s.Name AS SchemaName,
	FORMAT (p.rows, 'N0', 'pt-BR' ) as rows,
    FORMAT ((SUM(a.total_pages) * 8), 'N0', 'pt-BR' )  AS TotalSpaceKB, 
    CAST(ROUND(((SUM(a.total_pages) * 8) / 1024.00), 2) AS NUMERIC(36, 2)) AS TotalSpaceMB,
    SUM(a.used_pages) * 8 AS UsedSpaceKB, 
    CAST(ROUND(((SUM(a.used_pages) * 8) / 1024.00), 2) AS NUMERIC(36, 2)) AS UsedSpaceMB, 
    (SUM(a.total_pages) - SUM(a.used_pages)) * 8 AS UnusedSpaceKB,
    CAST(ROUND(((SUM(a.total_pages) - SUM(a.used_pages)) * 8) / 1024.00, 2) AS NUMERIC(36, 2)) AS UnusedSpaceMB
FROM 
    sys.tables t
INNER JOIN      
    sys.indexes i ON t.OBJECT_ID = i.object_id
INNER JOIN 
    sys.partitions p ON i.object_id = p.OBJECT_ID AND i.index_id = p.index_id
INNER JOIN 
    sys.allocation_units a ON p.partition_id = a.container_id
LEFT OUTER JOIN 
    sys.schemas s ON t.schema_id = s.schema_id
WHERE 
    t.NAME NOT LIKE 'dt%' 
    AND t.is_ms_shipped = 0
    AND i.OBJECT_ID > 255 
GROUP BY 
    t.Name, s.Name, p.Rows
ORDER BY 
    TotalSpaceMB DESC, t.Name

Espero que tenha ajudado. 🙂

Abraços e até a próxima.

Referências: Get size of all tables in database

Resolvendo Error: ENOSPC: System limit for number of file watchers reached no Ubuntu

Fala pessoal, tudo em ordem? Espero que sim!
Recentemente precisei subir um servidor de aplicação web com LiveReload local com webpack-server e estava apresentando o erro “Error: ENOSPC: System limit for number of file watchers reached”.

Primeiro o que é LiveReload?
Recurso que monitora o código fonte da aplicação web (Angular, ReactJS, Ember e etc) em execução e toda vez que detecta uma alteração compila e da um refresh no browser (se tiver aberto) automaticamente.

ExamploLiveReload

Bora a solução agora? GoGoGo 😉

Digitei no terminal o comando a seguir para poder editar o arquivo “sysctl.conf”:

sudo gedit /etc/sysctl.conf

Adicionei a linha a seguir:

fs.inotify.max_user_watches=524288

Fechei o gedit, em seguida no terminal digitei o comando a seguir para aplicar a nova configuração:

sudo sysctl -p

Executei o serviço e não apresentou mais o erro 🙂

Espero que tenha ajudado! Até a próxima 😉

Fonte:
https://stackoverflow.com/questions/55763428/react-native-error-enospc-system-limit-for-number-of-file-watchers-reached

Como configurar no Ubuntu autenticação SSH com RSA no git via CommandLine?

Fala pessoal, tudo em ordem? Desejo que sim!
Recentemente estava adicionando um remote de um repositório novo para um projeto e optei por usar SSH para não ter a necessidade de trocar dados de credenciais com repositório remoto no momento que precisar submeter atualizações de códigos ou vice-versa, então após uma pesquisa, peguei um passo a passo.

Vamos ao código? Go Go Go 🙂

Gerando as chaves SSH RSA

Digitar o comando a seguir no terminal:

ssh-keygen -t rsa -b 2048 -C "seuemail@dominio"

Será exibido algo parecido com isso:

Generating public/private rsa key pair.
Enter file in which to save the key (/home/*nome-do-seu-usuario*/.ssh/id_rsa):

Pressione enter, deixe ele gerar o arquivo com o nome padrão.

Após, será solicitado que seja informado a palavara-chave que será usada quando precisar abrir o certificado, em seguinte confirme a senha.

Enter passphrase (empty for no passphrase):
Enter same passphrase again:

Para copiar o conteúdo da chave pública, você pode abrir o arquivo e copiar todo o conteúdo ou usar o comando a seguir.

xclip -sel clip < /home/*nome-do-seu-usuario*/.ssh/id_rsa.pub

Dica: Para instalar o xclip, “sudo apt-get install xclip” 🙂

Agora a próxima etapa é no serviço de repositório remoto do git que você usa, colocar a chave a pública, vou mostrar como configurar no no bitbucket e gitlab, mas a base teórica de como funciona é essa, o que vai mudar talvez a tela e local onde serambasicamente o mesmo procedimento, sem muito segredo. no exemplo a seguir vou mostrar.

Procedimento para adicionar a chave no GitLab

Adicionar-SSH-KEY-GitLab

Procedimento para adicionar a chave no Bitbucket

Adicionar-SSH-KEY-BitBucket-Passo-01

Adicionar-SSH-KEY-BitBucket-Passo-02

Para testar você digite o comando a seguir no terminal:

ssh -Tvvv git@bitbucket.com

Se tudo correr como o esperado será exibido algo parecido com o a seguir:

OpenSSH_7.2p2 Ubuntu-4ubuntu2.8, OpenSSL 1.0.2g  1 Mar 2016
debug1: Reading configuration data /etc/ssh/ssh_config
debug1: /etc/ssh/ssh_config line 19: Applying options for *
debug2: resolving "bitbucket.com" port 22
debug2: ssh_connect_direct: needpriv 0
debug1: Connecting to bitbucket.com [2406:da00:ff00::12d0:47c8] port 22.
debug1: Connection established.
debug1: identity file /home/*SeuUsuario*/.ssh/id_rsa type 1
debug1: key_load_public: No such file or directory
debug1: identity file /home/*SeuUsuario*/.ssh/id_rsa-cert type -1
debug1: key_load_public: No such file or directory
debug1: identity file /home/*SeuUsuario*/.ssh/id_dsa type -1
debug1: key_load_public: No such file or directory
debug1: identity file /home/*SeuUsuario*/.ssh/id_dsa-cert type -1
debug1: key_load_public: No such file or directory
debug1: identity file /home/*SeuUsuario*/.ssh/id_ecdsa type -1
debug1: key_load_public: No such file or directory
debug1: identity file /home/*SeuUsuario*/.ssh/id_ecdsa-cert type -1
debug1: key_load_public: No such file or directory
debug1: identity file /home/*SeuUsuario*/.ssh/id_ed25519 type -1
debug1: key_load_public: No such file or directory
debug1: identity file /home/*SeuUsuario*/.ssh/id_ed25519-cert type -1
debug1: Enabling compatibility mode for protocol 2.0
debug1: Local version string SSH-2.0-OpenSSH_7.2p2 Ubuntu-4ubuntu2.8
debug1: Remote protocol version 2.0, remote software version conker_405f5f426f-dirty app-152
debug1: no match: conker_405f5f426f-dirty app-152
debug2: fd 3 setting O_NONBLOCK
debug1: Authenticating to bitbucket.com:22 as 'git'
debug3: hostkeys_foreach: reading file "/home/*SeuUsuario*/.ssh/known_hosts"
debug3: send packet: type 20
debug1: SSH2_MSG_KEXINIT sent
debug3: receive packet: type 20
debug1: SSH2_MSG_KEXINIT received
debug2: local client KEXINIT proposal
debug2: KEX algorithms: curve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group-exchange-sha1,diffie-hellman-group14-sha1,ext-info-c
debug2: host key algorithms: ecdsa-sha2-nistp256-cert-v01@openssh.com,ecdsa-sha2-nistp384-cert-v01@openssh.com,ecdsa-sha2-nistp521-cert-v01@openssh.com,ssh-ed25519-cert-v01@openssh.com,ssh-rsa-cert-v01@openssh.com,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-ed25519,rsa-sha2-512,rsa-sha2-256,ssh-rsa
debug2: ciphers ctos: chacha20-poly1305@openssh.com,aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,aes256-gcm@openssh.com,aes128-cbc,aes192-cbc,aes256-cbc,3des-cbc
debug2: ciphers stoc: chacha20-poly1305@openssh.com,aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,aes256-gcm@openssh.com,aes128-cbc,aes192-cbc,aes256-cbc,3des-cbc
debug2: MACs ctos: umac-64-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha1-etm@openssh.com,umac-64@openssh.com,umac-128@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-sha1
debug2: MACs stoc: umac-64-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha1-etm@openssh.com,umac-64@openssh.com,umac-128@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-sha1
debug2: compression ctos: none,zlib@openssh.com,zlib
debug2: compression stoc: none,zlib@openssh.com,zlib
debug2: languages ctos: 
debug2: languages stoc: 
debug2: first_kex_follows 0 
debug2: reserved 0 
debug2: peer server KEXINIT proposal
debug2: KEX algorithms: curve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group14-sha1,diffie-hellman-group1-sha1
debug2: host key algorithms: ssh-dss,ssh-rsa
debug2: ciphers ctos: aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,chacha20-poly1305@openssh.com,arcfour256,arcfour128
debug2: ciphers stoc: aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,chacha20-poly1305@openssh.com,arcfour256,arcfour128
debug2: MACs ctos: hmac-sha2-256-etm@openssh.com,hmac-sha2-256,hmac-sha1,hmac-sha1-96
debug2: MACs stoc: hmac-sha2-256-etm@openssh.com,hmac-sha2-256,hmac-sha1,hmac-sha1-96
debug2: compression ctos: none
debug2: compression stoc: none
debug2: languages ctos: 
debug2: languages stoc: 
debug2: first_kex_follows 0 
debug2: reserved 0 
debug1: kex: algorithm: curve25519-sha256@libssh.org
debug1: kex: host key algorithm: ssh-rsa
debug1: kex: server->client cipher: chacha20-poly1305@openssh.com MAC:  compression: none
debug1: kex: client->server cipher: chacha20-poly1305@openssh.com MAC:  compression: none
debug3: send packet: type 30
debug1: expecting SSH2_MSG_KEX_ECDH_REPLY
debug3: receive packet: type 31
debug1: Server host key: ssh-rsa SHA256:zXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXS
debug3: hostkeys_foreach: reading file "/home/*SeuUsuario*/.ssh/known_hosts"
debug3: hostkeys_foreach: reading file "/home/*SeuUsuario*/.ssh/known_hosts"
The authenticity of host 'bitbucket.com (2406:da00:ff00::12d0:47c8)' can't be established.
RSA key fingerprint is SHA256:zXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXS.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'bitbucket.com,2406:da00:ff00::12d0:47c8' (RSA) to the list of known hosts.
debug3: send packet: type 21
debug2: set_newkeys: mode 1
debug1: rekey after 134217728 blocks
debug1: SSH2_MSG_NEWKEYS sent
debug1: expecting SSH2_MSG_NEWKEYS
debug3: receive packet: type 21
debug1: SSH2_MSG_NEWKEYS received
debug2: set_newkeys: mode 0
debug1: rekey after 134217728 blocks
debug2: key: /home/*SeuUsuario*/.ssh/id_rsa (0x562d08b263d0), agent
debug2: key: /home/*SeuUsuario*/.ssh/id_dsa ((nil))
debug2: key: /home/*SeuUsuario*/.ssh/id_ecdsa ((nil))
debug2: key: /home/*SeuUsuario*/.ssh/id_ed25519 ((nil))
debug3: send packet: type 5
debug3: receive packet: type 6
debug2: service_accept: ssh-userauth
debug1: SSH2_MSG_SERVICE_ACCEPT received
debug3: send packet: type 50
debug3: receive packet: type 51
debug1: Authentications that can continue: publickey
debug3: start over, passed a different list publickey
debug3: preferred gssapi-keyex,gssapi-with-mic,publickey,keyboard-interactive,password
debug3: authmethod_lookup publickey
debug3: remaining preferred: keyboard-interactive,password
debug3: authmethod_is_enabled publickey
debug1: Next authentication method: publickey
debug1: Offering RSA public key: /home/*SeuUsuario*/.ssh/id_rsa
debug3: send_pubkey_test
debug3: send packet: type 50
debug2: we sent a publickey packet, wait for reply
debug3: receive packet: type 60
debug1: Server accepts key: pkalg ssh-rsa blen 279
debug2: input_userauth_pk_ok: fp SHA256:e5lqpfup8GsqGy7gI5BRizjyTsY3qqJ3DtazGK613Nw
debug3: sign_and_send_pubkey: RSA SHA256:e5lqpfup8GsqGy7gI5BRizjyTsY3qqJ3DtazGK613Nw
debug3: send packet: type 50
debug3: receive packet: type 52
debug1: Authentication succeeded (publickey).
Authenticated to bitbucket.com ([2406:da00:ff00::12d0:47c8]:22).
debug1: channel 0: new [client-session]
debug3: ssh_session2_open: channel_new: 0
debug2: channel 0: send open
debug3: send packet: type 90
debug1: Entering interactive session.
debug1: pledge: network
debug3: receive packet: type 91
debug2: callback start
debug2: fd 3 setting TCP_NODELAY
debug3: ssh_packet_set_tos: set IPV6_TCLASS 0x08
debug2: client_session2_setup: id 0
debug1: Sending environment.
debug3: Ignored env XDG_VTNR
debug1: Sending env LC_PAPER = pt_BR.UTF-8
debug2: channel 0: request env confirm 0
debug3: send packet: type 98
debug1: Sending env LC_ADDRESS = pt_BR.UTF-8
debug2: channel 0: request env confirm 0
debug3: send packet: type 98
debug3: Ignored env XDG_SESSION_ID
debug3: Ignored env XDG_GREETER_DATA_DIR
debug1: Sending env LC_MONETARY = pt_BR.UTF-8
debug2: channel 0: request env confirm 0
debug3: send packet: type 98
debug3: Ignored env CLUTTER_IM_MODULE
debug3: Ignored env GPG_AGENT_INFO
debug3: Ignored env TERM
debug3: Ignored env VTE_VERSION
debug3: Ignored env SHELL
debug3: Ignored env DERBY_HOME
debug3: Ignored env QT_LINUX_ACCESSIBILITY_ALWAYS_ON
debug3: Ignored env WINDOWID
debug1: Sending env LC_NUMERIC = pt_BR.UTF-8
debug2: channel 0: request env confirm 0
debug3: send packet: type 98
debug3: Ignored env MONGODB_HOME
debug3: Ignored env UPSTART_SESSION
debug3: Ignored env GNOME_KEYRING_CONTROL
debug3: Ignored env GTK_MODULES
debug3: Ignored env USER
debug3: Ignored env LS_COLORS
debug1: Sending env LC_TELEPHONE = pt_BR.UTF-8
debug2: channel 0: request env confirm 0
debug3: send packet: type 98
debug3: Ignored env QT_ACCESSIBILITY
debug3: Ignored env XDG_SESSION_PATH
debug3: Ignored env XDG_SEAT_PATH
debug3: Ignored env SSH_AUTH_SOCK
debug3: Ignored env DEFAULTS_PATH
debug3: Ignored env XDG_CONFIG_DIRS
debug3: Ignored env DESKTOP_SESSION
debug3: Ignored env PATH
debug3: Ignored env QT_IM_MODULE
debug3: Ignored env QT_QPA_PLATFORMTHEME
debug1: Sending env LC_IDENTIFICATION = pt_BR.UTF-8
debug2: channel 0: request env confirm 0
debug3: send packet: type 98
debug3: Ignored env XDG_SESSION_TYPE
debug3: Ignored env PWD
debug3: Ignored env JOB
debug3: Ignored env XMODIFIERS
debug3: Ignored env JAVA_HOME
debug3: Ignored env GNOME_KEYRING_PID
debug1: Sending env LANG = en_US.UTF-8
debug2: channel 0: request env confirm 0
debug3: send packet: type 98
debug3: Ignored env GDM_LANG
debug3: Ignored env MANDATORY_PATH
debug3: Ignored env NODE_PATH
debug1: Sending env LC_MEASUREMENT = pt_BR.UTF-8
debug2: channel 0: request env confirm 0
debug3: send packet: type 98
debug3: Ignored env COMPIZ_CONFIG_PROFILE
debug3: Ignored env IM_CONFIG_PHASE
debug3: Ignored env GDMSESSION
debug3: Ignored env SESSIONTYPE
debug3: Ignored env GTK2_MODULES
debug3: Ignored env SHLVL
debug3: Ignored env HOME
debug3: Ignored env XDG_SEAT
debug3: Ignored env LANGUAGE
debug3: Ignored env GNOME_DESKTOP_SESSION_ID
debug3: Ignored env UPSTART_INSTANCE
debug3: Ignored env NODEJS_HOME
debug3: Ignored env UPSTART_EVENTS
debug3: Ignored env XDG_SESSION_DESKTOP
debug3: Ignored env LOGNAME
debug3: Ignored env DBUS_SESSION_BUS_ADDRESS
debug3: Ignored env J2SDKDIR
debug3: Ignored env XDG_DATA_DIRS
debug3: Ignored env QT4_IM_MODULE
debug1: Sending env LC_CTYPE = pt_BR.UTF-8
debug2: channel 0: request env confirm 0
debug3: send packet: type 98
debug3: Ignored env LESSOPEN
debug3: Ignored env INSTANCE
debug3: Ignored env UPSTART_JOB
debug3: Ignored env XDG_RUNTIME_DIR
debug3: Ignored env DISPLAY
debug3: Ignored env XDG_CURRENT_DESKTOP
debug3: Ignored env GTK_IM_MODULE
debug3: Ignored env J2REDIR
debug3: Ignored env LESSCLOSE
debug1: Sending env LC_TIME = pt_BR.UTF-8
debug2: channel 0: request env confirm 0
debug3: send packet: type 98
debug1: Sending env LC_NAME = pt_BR.UTF-8
debug2: channel 0: request env confirm 0
debug3: send packet: type 98
debug3: Ignored env XAUTHORITY
debug3: Ignored env _
debug2: channel 0: request shell confirm 1
debug3: send packet: type 98
debug2: callback done
debug2: channel 0: open confirm rwindow 2097152 rmax 32768
debug3: receive packet: type 99
debug2: channel_input_status_confirm: type 99 id 0
debug2: shell request accepted on channel 0
logged in as *SeuUsuario*

You can use git or hg to connect to Bitbucket. Shell access is disabled
debug3: receive packet: type 98
debug1: client_input_channel_req: channel 0 rtype exit-status reply 0
debug3: receive packet: type 96
debug2: channel 0: rcvd eof
debug2: channel 0: output open -> drain
debug2: channel 0: obuf empty
debug2: channel 0: close_write
debug2: channel 0: output drain -> closed
debug3: receive packet: type 97
debug2: channel 0: rcvd close
debug2: channel 0: close_read
debug2: channel 0: input open -> closed
debug3: channel 0: will not send data after close
debug2: channel 0: almost dead
debug2: channel 0: gc: notify user
debug2: channel 0: gc: user detached
debug2: channel 0: send close
debug3: send packet: type 97
debug2: channel 0: is dead
debug2: channel 0: garbage collecting
debug1: channel 0: free: client-session, nchannels 1
debug3: channel 0: status: The following connections are open:
  #0 client-session (t4 r0 i3/0 o3/0 fd -1/-1 cc -1)

debug3: send packet: type 1
Transferred: sent 3164, received 1832 bytes, in 0.3 seconds
Bytes per second: sent 11021.2, received 6381.4
debug1: Exit status 0

The authenticity of host 'bitbucket.com (2406:da00:ff00::12d0:47c8)' can't be established.
RSA key fingerprint is SHA256:zXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXS.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'bitbucket.com,2406:da00:ff00::12d0:47c8' (RSA) to the list of known

Para validar se está funcionando você vai precisar adicionar o remote para usar o SSH e em seguida digitar algum comando para acessar o repositório remoto, se tudo ocorreu como esperado, ao tentar conectar com o repositório não será necessário informar o usuário e senha.

Espero que tenha ajudado. 🙂

Abraços e até a próxima.

Guia rápido de comandos DOCKER

Importante: Não será explicando em detalhes o que é cada comando, objetivo é apenas usar como guia rápido para consulta de comandos.

Fala pessoal, tudo em ordem? Espero que sim!

O objetivo do artigo de hoje é anotar comandos para usar como um guia rápido, então será atualizado constantemente.

Iniciar um container

docker start container_name ou id

Parar um container

docker stop container_name ou id

Reiniciar um container

docker restart container_name ou id

Pausar um container

docker pause container_name ou id

Tirar do modo pause um container

docker unpause container_name ou id

Visualizar log de um container

docker container logs container_name ou id

Visualizar em tempo real recursos utilizado por um container

docker container stats container_name ou id

Abrindo o terminal dentro de um container em modo interativo

docker exec -it $CONTAINER_ID /bin/bash

Obter o IP de um container

docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' container_name_or_id

Renomear um container

docker rename CONTAINER NEW_NAME

Espero poder ter ajudado!
Até a próxima 😉

Retry o designer pattern para aplicações resilientes

Importante: Não será apresentando sobre como criar projeto do tipo ConsoleApplication e classes no visual studio code, pois não é o objetivo.

Objetivo: preparar aplicações para lidar com falhas ao consumir recursos externos sobre uma rede de computadores ou códigos isolados e de forma transparente tentar novamente a execução da operação.

Uma ilustração:retry-pattern

(Imagem: https://docs.microsoft.com/en-us/azure/architecture/patterns/retry)

1 – A aplicação submeteu uma requisição e a resposta foi HTTP 500 (Erro interno no servidor)
2 – A aplicação aguardou um intervalo de tempo, realizou novamente a requisição com os mesmo dados anteriormente e a resposta foi HTTP 500.
3 – A aplicação aguardou um novo intervalo, realizou novamente a requisição com os mesmos dados anteriormente e a respota foi HTTP 200 (SUCESSO).

Qual o problema iremos resolver para aplicar um exemplo de implementação?
Uma aplicação precisa gerar pedido em outro sistema através de uma api disponibilizada na internet, devido ao alto volume de requisições em alguns momentos sobrecargas no servidor ocorrem, retornando “HTTP 503 – Service Unavailable”, como é uma api de terceiros, não existindo a possibilidade de tratar na origem, será aplicado o padrão Retry, então essa aplicação será configurada para em casos de problemas na geração de pedido, após passados 30s enviar novamente a requisição a api, com limite máximo de 3 tentativas e não obtendo êxito será registrado o log do erro para analise e reporte ao fornecedor.

Vamos ao código de demonstração do funcionamento na prática do Retry? Go Go Go 🙂

Foi criado uma entidade chamada “PurchaseOrder” que será usada para trafegar os dados de Pedidos de um sistema (client) para outro sistema(serviço).

public class PurchaseOrder
{
  public string ReferenceNumber { get; set; }
  public string CodeProduct { get; set; }
  public int Quantity { get; set; }
}

Criado a interface generica “IBusinessOperation”:
public interface IBusinessOperation
{
  T Execute();    
}

Criado a classe “GenerateOrder” que implementa o método Execute da interface “IBusinessOperation”:

public class GenerateOrder: IBusinessOperation  {
	private PurchaseOrder _purchaseOrder;
	private Queue  _errors;

	public GenerateOrder() {
		_purchaseOrder = new PurchaseOrder();
		_errors = new Queue  ();
	}

	public GenerateOrder(PurchaseOrder purchaseOrder, List  exception = null) {
		_errors = new Queue  ();

		this._purchaseOrder = purchaseOrder;
		if (exception != null) {
			exception.ForEach(x = >_errors.Enqueue(x));
		}
	}
	public PurchaseOrder Execute() {
		if (this._errors.Any()) {
			var error = this._errors.Peek();
			this._errors.Dequeue();
			throw error;
		}

		//Ocorreu com sucesso
		this._purchaseOrder.ReferenceNumber = DateTime.Now.ToString("yyyyMMddhhmmssfff");

		return this._purchaseOrder;
	}
}

Criando a classe “Retry” que receberá os dados para requisição, tempo de espera para realizar nova requisição (em caso de erros) e total máximo de tentativas.

public class Retry  :IBusinessOperation  {
	private ILog _log;
	private IBusinessOperation  _operation;
	private int _maxAttempts;
	private int _delay;
	private int _attempts;
	private List  _test;
	private List  _errors;

	public Retry(
	ILog log, IBusinessOperation  op, int maxAttempts, int delay, Type ignoreTest) {
		this._attempts = 0;
		this._test = new List  ();
		this._errors = new List  ();
		this._log = log;
		this._operation = op;
		this._maxAttempts = maxAttempts;
		this._delay = delay;
		this._test.Add(ignoreTest);
	}
	public T Execute() {
		do {
			try {
				_log.Register($ "Tentativa Nro. {this._attempts + 1}");
				return this._operation.Execute();
			}
			catch(Exception e) {
				_log.Register($ "Tentativa Nro. {this._attempts + 1} com erro  \"{e.Message}\" ");

				this._errors.Add(e);

				this._attempts++;
				var exists = this._test.Contains(e.GetType());

				if (this._attempts >= this._maxAttempts || !exists) {
					_log.Register($ "Excedeu {this._maxAttempts} tentativas e não será reenviado.");
					throw e;
				}

				try {
					_log.Register($ "Aguardando {(this._delay / 1000)}s para a proxima tentativa");
					Thread.Sleep(this._delay);
				}
				catch(ThreadInterruptedException f) {
					//ignore
				}
			}
		} while ( true );
	}
}

Criado a classe “RetryExponentialBackoff” que receberá os dados para requisição, tempo de espera para realizar nova requisição (em caso de erros) e total máximo de tentativas. O tempo de espera será randomico.

public class RetryExponentialBackoff  :IBusinessOperation  {
	private static Random RANDOM = new Random();
	private ILog _log;
	private IBusinessOperation  _operation;
	private int _maxAttempts;
	private int _maxDelay;
	private int _attempts;
	private List  _test;
	private List  _errors;
	public RetryExponentialBackoff(
	ILog log, IBusinessOperation  op, int maxAttempts, int maxDelay, Type ignoreTest) {
		this._test = new List  ();
		this._errors = new List  ();
		this._attempts = 0;
		this._log = log;
		this._operation = op;
		this._maxAttempts = maxAttempts;
		this._maxDelay = maxDelay;
		this._test.Add(ignoreTest);
	}
	public T Execute() {
		do {
			try {
				_log.Register($ "Tentativa Nro. {this._attempts + 1}");
				return this._operation.Execute();
			}
			catch(Exception e) {
				_log.Register($ "Tentativa Nro. {this._attempts + 1} com erro  \"{e.Message}\" ");
				this._errors.Add(e);

				this._attempts++;
				var exists = this._test.Contains(e.GetType());

				if (this._attempts >= this._maxAttempts || !exists) {
					_log.Register($ "Excedeu {this._maxAttempts} tentativas e não será reenviado.");
					throw e;
				}

				try {
					int testDelay = RANDOM.Next(50000);
					int delay = testDelay < this._maxDelay ? testDelay: _maxDelay;

					_log.Register($ "Aguardando {(delay / 1000)}s para a proxima tentativa");
					Thread.Sleep(delay);
				}
				catch(Exception f) {
					//ignore
				}
			}
		} while ( true );
	}
}

Criado a interface “ILog” para registrar os log’s de sistema.

public interface ILog {
void Register(string message);
}

Criado a classe “LogConsole” que implementa o método Register da interface “ILog”:

public class LogConsole: ILog {
public LogConsole() {}

public void Register(string message) {
Console.WriteLine("/***********************************************************/");
Console.WriteLine("");
Console.WriteLine($ "Data: {DateTime.Now.ToString("
dd / MM / yyyy HH: mm: ss ")}");
Console.WriteLine($ "Message Info: {message}");
Console.WriteLine("");
Console.WriteLine("/***********************************************************/");
}
}

Enfim a “program” que estará simulando alguns problemas aplicando o “Retry”:

public class Program {
private static IBusinessOperation operation;
private static ILog _log;

public static void Main(String[] args) {
_log = new LogConsole();
NoErrors();
ErrorNoRetry();
ErrorWithRetry();
ErrorWithRetryNetworking();
ErrorWithRetryExceeded();
}

private static void NoErrors() {
var purchaseOrder = new PurchaseOrder() {
CodeProduct = "COD10",
Quantity = 500
};
operation = new GenerateOrder(purchaseOrder);
operation.Execute();
_log.Register("Executado sem erros.");
}

private static void ErrorNoRetry() {
var purchaseOrder = new PurchaseOrder() {
CodeProduct = "COD01",
Quantity = 1000
};
operation = new GenerateOrder(purchaseOrder, new List {
new WebException("503 Service Unavailable – HTTP")
});

try {
operation.Execute();
}
catch(WebException e) {
_log.Register("Executado com erro e não tentou novamente por que não utiliza \"o padrão Retry\".");
}
}

private static void ErrorWithRetry() {
var purchaseOrder = new PurchaseOrder() {
CodeProduct = "COD02",
Quantity = 1000
};
Retry retry = new Retry (
_log, new GenerateOrder(purchaseOrder, new List {
new WebException("503 Service Unavailable – HTTP")
}), 3, //3 tentativas
5000, //2 s de espera entre as tentativas
typeof(WebException) //tipo de erro que deve passar pelas 3 tentativas
);

operation = retry;

var purchaseOrderReturn = operation.Execute();
_log.Register($ "Reference Number \"{purchaseOrderReturn.ReferenceNumber}\" da Ordem de Compra, gerado com sucesso");
}

private static void ErrorWithRetryExceeded() {
int tentativas = 3;
try {
var purchaseOrder = new PurchaseOrder() {
CodeProduct = "COD03",
Quantity = 1000
};

Retry retry = new Retry (
_log, new GenerateOrder(purchaseOrder, new List {
new WebException("503 Service Unavailable – HTTP"),
new WebException("503 Service Unavailable – HTTP"),
new WebException("503 Service Unavailable – HTTP"),
new WebException("503 Service Unavailable – HTTP"),
}), tentativas, //3 tentativas
30000, //30 s de espera entre as tentativas
typeof(WebException) //tipo de erro que deve passar pelas 3 tentativas
);

operation = retry;
var purchaseOrderReturn = operation.Execute();
_log.Register($ "Reference Number \"{purchaseOrderReturn.ReferenceNumber}\" da Ordem de Compra, gerado com sucesso");
}
catch(Exception ex) {
_log.Register($ "Erro \"{ex.Message}\" disparado após {tentativas}.");
}
}

private static void ErrorWithRetryNetworking() {
var purchaseOrder = new PurchaseOrder() {
CodeProduct = "COD04",
Quantity = 1000
};
RetryExponentialBackoff retry = new RetryExponentialBackoff (
_log, new GenerateOrder(purchaseOrder, new List {
new WebException("503 Service Unavailable – HTTP")
}), 6, //6 tentativas
5000, //5 s de espera entre tentativas
typeof(WebException) //tipo de erro que deve passar pelas 6 tentativas
);
operation = retry;

var purchaseOrderReturn = operation.Execute();
_log.Register($ "Reference Number \"{purchaseOrderReturn.ReferenceNumber}\" da Ordem de Compra, gerado com sucesso");
}
}

Executando a programa será exibido algumas tentativas com erro e no final sucesso, em outra requisição somente erros retornados até que a solução para com as tentativas e exibe via console.
Resultado da execução:

/***********************************************************/

Data: 20/02/2020 22:33:50
Message Info: Executado sem erros.

/***********************************************************/
/***********************************************************/

Data: 20/02/2020 22:33:50
Message Info: Executado com erro e não tentou novamente por que não utiliza "o padrão Retry".

/***********************************************************/
/***********************************************************/

Data: 20/02/2020 22:33:50
Message Info: Tentativa Nro. 1

/***********************************************************/
/***********************************************************/

Data: 20/02/2020 22:33:50
Message Info: Tentativa Nro. 1 com erro "503 Service Unavailable – HTTP"

/***********************************************************/
/***********************************************************/

Data: 20/02/2020 22:33:50
Message Info: Aguardando 5s para a proxima tentativa

/***********************************************************/
/***********************************************************/

Data: 20/02/2020 22:33:55
Message Info: Tentativa Nro. 2

/***********************************************************/
/***********************************************************/

Data: 20/02/2020 22:33:55
Message Info: Reference Number "20200220103355158" da Ordem de Compra, gerado com sucesso

/***********************************************************/
/***********************************************************/

Data: 20/02/2020 22:33:55
Message Info: Tentativa Nro. 1

/***********************************************************/
/***********************************************************/

Data: 20/02/2020 22:33:55
Message Info: Tentativa Nro. 1 com erro "503 Service Unavailable – HTTP"

/***********************************************************/
/***********************************************************/

Data: 20/02/2020 22:33:55
Message Info: Aguardando 5s para a proxima tentativa

/***********************************************************/
/***********************************************************/

Data: 20/02/2020 22:34:00
Message Info: Tentativa Nro. 2

/***********************************************************/
/***********************************************************/

Data: 20/02/2020 22:34:00
Message Info: Reference Number "20200220103400166" da Ordem de Compra, gerado com sucesso

/***********************************************************/
/***********************************************************/

Data: 20/02/2020 22:34:00
Message Info: Tentativa Nro. 1

/***********************************************************/
/***********************************************************/

Data: 20/02/2020 22:34:00
Message Info: Tentativa Nro. 1 com erro "503 Service Unavailable – HTTP"

/***********************************************************/
/***********************************************************/

Data: 20/02/2020 22:34:00
Message Info: Aguardando 30s para a proxima tentativa

/***********************************************************/
/***********************************************************/

Data: 20/02/2020 22:34:30
Message Info: Tentativa Nro. 2

/***********************************************************/
/***********************************************************/

Data: 20/02/2020 22:34:30
Message Info: Tentativa Nro. 2 com erro "503 Service Unavailable – HTTP"

/***********************************************************/
/***********************************************************/

Data: 20/02/2020 22:34:30
Message Info: Aguardando 30s para a proxima tentativa

/***********************************************************/
/***********************************************************/

Data: 20/02/2020 22:35:00
Message Info: Tentativa Nro. 3

/***********************************************************/
/***********************************************************/

Data: 20/02/2020 22:35:00
Message Info: Tentativa Nro. 3 com erro "503 Service Unavailable – HTTP"

/***********************************************************/
/***********************************************************/

Data: 20/02/2020 22:35:00
Message Info: Excedeu 3 tentativas e não será reenviado.

/***********************************************************/
/***********************************************************/

Data: 20/02/2020 22:35:00
Message Info: Erro "503 Service Unavailable – HTTP" disparado após 3.

/***********************************************************/

Espero que tenha ajudado.
Até a próxima e caso não publique mais nada esse ano, boas festas!!!

[]’s

Código-Fonte: https://github.com/joaopauloit/desginer-patterns

Como retornar um texto formatado em PascalCase no SQL SERVER

Fala pessoal, tudo em ordem? Espero que sim!
Recentemente precisei aplicar pascal case em textos e cheguei ao código a seguir para atender a necessidade.

Vamos para o script? Go go go ahead 🙂

CREATE FUNCTION dbo.fnPascalCase(@DATA VARCHAR(8000))
RETURNS VARCHAR(8000)
AS
BEGIN
DECLARE @Position INT

SELECT @DATA = STUFF(LOWER(@DATA), 1, 1, UPPER(LEFT(@DATA, 1))),
@Position = PATINDEX('%[^a-zA-Z][a-z]%', @DATA COLLATE Latin1_General_Bin)

WHILE @Position > 0
SELECT @DATA = STUFF(@DATA, @Position, 2, UPPER(SUBSTRING(@DATA, @Position, 2))),
@Position = PATINDEX('%[^a-zA-Z][a-z]%', @DATA COLLATE Latin1_General_Bin)

SET @DATA = REPLACE(@DATA, ' ','')
RETURN @DATA
END

select dbo.fnPascalCase('quem veio primeiro ovo ou galinha?') as PascalCase

Execute o código e se tudo ocorreu como esperado, será exibido algo parecido com isso:
01

Espero que tenha ajudado, até a próxima. 😉

Fonte: https://stackoverflow.com/questions/4383038/convert-text-in-pascalcase

Resolvendo um ThreadAbortException em aplicações ASP.NET

Fala pessoal, tudo em ordem? Espero que sim!
Hoje quero compartilhar com vocês dicas para evitar ThreadAbortException em projetos ASP.NET.

Entendendo o problema.
Quando temos processamentos demorados em execução para atender a uma requisição HTTP, é normal que o ASP.NET aborte a execução e dispare a exceção “ThreadAbortException” após excedido “90s”, isso quando, a execução não é em modo DEBUG, pois em modo DEBUG o valor padrão é “300000s” então dificilmente será disparado essa exceção em desenvolvimento.

O que podemos fazer para evitar o problema?
Podem existir diversas formas de resolver isso, mas irei citar 3,
1 – Analisar o código responsável pela resposta ao recurso HTTP, identificar se é um processamento que está sendo prejudicado por performance no banco de dados e fazer os ajustes necessários, criando indices ou refatorando o código que realiza a consulta.
2 – Sendo um processo demorado devido as regras que precisam ser executadas, é um forte candidato a colocar em Job para ser processado em background.
3 – Se por algum motivo não tenha tempo suficiente para aplicar as opções 1, 2 ou outras que existam e seu negócio permita o recurso demore, é possível alterar no web.config do projeto aumentando o tempo que o asp.net deve aguardar pelo processamento até que realize um TreadAbortException. Contudo, tal medida pode gerar enfileiramento de requisições no servidor e causar indisponibilidade no serviço web quando chegar no seu limite de processamento.

Como aumentar o tempo da TreadAbortException?
Foi proposital elenca-la como última opção, pois não recomendo você ter um processo HTTP em execução por muito tempo, mas vamos lá, no web.config do projeto inclua/altere a tag “httpRuntime” para um valor em segundos suficiente para aguardar seu processo mais demorado.

ThreadAbortException

Espero que tenha ajudado. 🙂

Abraços e até a próxima.

Fontes:
https://stackoverflow.com/questions/17118236/httpruntime-targetframework-4-5-causes-500-internal-server-error
https://dotnetvisio-msdn.blogspot.com/2013/07/solution-for-thread-being-aborted-call.html

No ENUM como recuperar o texto do atributo Description e um item dado o indice

Importante: Não será apresentando sobre o que é ENUM e por que usar, como criar projeto do tipo ConsoleApplication e classes no visual studio code, pois não é o objetivo do artigo.

Fala pessoal, tudo em ordem? Espero que sim!
Hoje vou compartilhar com vocês um código que costumo usar para recuperar texto do atributo description de um ENUM e um método conversão genérico que recebe o indice do item e converte para um item de um tipo de ENUM informado.

Projeto
O projeto é do tipo ConsoleApplication e será usado o framework .Net Core 3.0
Agora vamos ao código? Go Go Go

Implementação da biblioteca Tools

Criar o projeto do tipo Console Application.

Criar um arquivo Enum.cs e colocar o código:

using System.ComponentModel;

namespace console_libs
{
    public enum Enviroment
    {
        Development = 1,
        [Description("Homologação")]
        Homologation = 2,
        [Description("Produção")]
        Production = 3
    }

    public enum ProcessingStatus
    {
        [Description("Aguardando")]
        Waiting = 1,
        [Description("Processado")]
        Processed = 2,
        [Description("Ocorreu um erro no processamento")]
        Error = 3
    }
}

Criar um arquivo Enumerators.cs e colocar o código:

using System;
using System.ComponentModel;

namespace console_libs.Tools
{
    public static class Enumerators
    {
        public static string GetDescription(this Enum value)
        {
            //Recuperando o tipo do enum
            var enumType = value.GetType();
            
            //Recuperando o nome do item do enum
            var field = enumType.GetField(value.ToString());

            //Recuperando o texto do enum
            var attributes = field.GetCustomAttributes(typeof(DescriptionAttribute), false);

            //Senão tiver declarado o atributo Description é retornado o nome do item do enum
            return attributes.Length == 0 ? value.ToString() : ((DescriptionAttribute)attributes[0]).Description;
        }

        public static T GetEnum(string index) where T : Enum
        {
            //Dado o tipo do enum, é realizado o parse para um item válido dado o indice informado.
            return (T)Enum.Parse(typeof(T), index);
        }


    }
}

Na class program.cs do projeto console adicionar o código para testar as implementações:

static void Main(string[] args)
        {
            Console.WriteLine("/**********************************************************************************************/");
            Console.WriteLine("");
            Console.WriteLine("******** Retornando o Texto contido no atributo \"Description\" ou nome do item do ENUM *********");
            Console.WriteLine("");
            Console.WriteLine($"Texto do atributo \"Description\" do enum \"Enviroment.Homologation\" é {Enumerators.GetDescription(Enviroment.Homologation)}");
            Console.WriteLine("");
            Console.WriteLine($"Texto do atributo \"Description\" do enum \"Enviroment.Production\" é {Enumerators.GetDescription(Enviroment.Production)}");
            Console.WriteLine("");
            Console.WriteLine($"Para o enum \"Enviroment.Development\" foi retornado o nome do item que é {Enumerators.GetDescription(Enviroment.Development)} e não um texto, por que não foi declarado um atributo atributo \"Description\" a ele.");
            Console.WriteLine("");            
            Console.WriteLine("/**********************************************************************************************/");
            Console.WriteLine("");
            Console.WriteLine("");
            Console.WriteLine("/**********************************************************************************************/");
            Console.WriteLine("");
            Console.WriteLine("******** Retornando o item do enum fornecendo o indice e tipo de ENUM *********");
            Console.WriteLine("");
            Console.WriteLine($"Para o indice 1 do tipo \"ProcessingStatus\", retornou o {Enumerators.GetEnum("1")}");
            Console.WriteLine($"Para o indice 2 do tipo \"Enviroment\", retornou o {Enumerators.GetEnum("2")}");
        }

Agora vamos compilar o projeto pelo terminal executando:

dotnet build console-libs.csproj

Vai aparecer algo parecido com a imagem a seguir:
DotNet_Build

Agora vamos excutar o projeto pelo terminal executando:

dotnet console-libs.dll

Vai aparecer algo parecido com a imagem a seguir:
DotNet_Run

Código-Fonte: https://github.com/joaopauloit/libraries-helpers-csharper

Fontes:
https://docs.microsoft.com/pt-br/dotnet/core/tools/dotnet-new?tabs=netcore22
https://docs.microsoft.com/pt-br/dotnet/core/tools/dotnet-build
https://docs.microsoft.com/pt-br/dotnet/core/tools/dotnet-run?tabs=netcore30

Testes unitários com Tape em NodeJS

Importante: O objetivo aqui não é ensinar sobre Teste Unitário, TDD, NodeJS e bibliotecas usadas (além de Tape e Tape-Spec), mas qualquer dúvida entre em contato.

Fala pessoal, estava pesquisando sobre alguns componentes no NodeJS que auxilie na escrita de testes unitários e encontrei o Tape, então vou compartilhar com vocês o que aprendi. =)

O que é o Tape?
É um módulo npm que interpreta e executa testes unitários em JS.

Qual projeto vamos usar?
O projeto de exemplo será o “text-algorithms”, caso não o conheça, ele é destinado para a implementação de algoritmos para texto e vamos escrever testes para validar o algoritmo de palindromo.

Instalando o módulo Tape
No diretório raiz do projeto, digite o comando a seguir no terminal para instalar o Tape no projeto:
“npm install tape –save-dev”

Algo parecido com a imagem a seguir será exibido:
Npm-Install-Tape

Execute também o comando para intalação do módulo “tape-spec”:
“npm install tap-spec –save-dev”

Algo parecido com a imagem a seguir será exibido:
Npm-Install-Tape-Spec

Escrevendo o primeiro teste
Primeiro vamos criar um arquivo chamado “palindromo.test.js” e uma index.tst.js, a “index.tst.js” será acionada no terminal e vai executar o teste contido na “palindromo.test.js” que valida o algoritmo de palindromo com alguns asserts.
O diretório do projeto após as modificações, vai ficar assim:
DiretorioProjectUnitTestTextAlgorithmPalindromo

Agora vamos configura o package.json para quando for executado o comando “npm test” o node entenda que deve ser executado o arquivo “index.test.js”, o arquivo package.json vai ficar assim:

{
  "name": "textalgorithms",
  "version": "1.0.0",
  "description": "Text Algorithms",
  "main": "./bin/index.js",
  "scripts": {
    "test": "node ./bin/index.test | tap-spec"
  },
  "author": "João Paulo R. S.",
  "license": "MIT",
  "bin": {
    "textAlgorithms": "./bin/index.js"
  },
  "dependencies": {
    "boxen": "^4.0.0",
    "chalk": "^2.4.2",
    "tap-spec": "^5.0.0",
    "yargs": "^13.2.4"
  },
  "devDependencies": {
    "tape": "^4.11.0"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/joaopauloit/text-algorithms.git"
  },
  "bugs": {
    "url": "https://github.com/joaopauloit/text-algorithms/issues"
  },
  "homepage": "https://github.com/joaopauloit/text-algorithms#readme"
}

Foi incluido após o paipe o “tap-spec”, para que o módulo tap-spec seja executado para deixar o resultado do teste colorido e formatado.

O código da para testar o palindromo vai ficar no arquivo “palindromo.test.js” e será:

const test = require('tape')
const palindromo = require('../algorithms/palindromo')

function validatePalindromo(){
	test('Verificar se é palindromo', (t) => {
		t.assert(palindromo.isPalindromo("ovo") === true, "Ovo é um palindromo")
		t.assert(palindromo.isPalindromo("Test set") === true, "Test set é um palindromo")
		t.assert(palindromo.isPalindromo("To idiot") === true, "To idiot é um palindromo")
	 	t.assert(palindromo.isPalindromo("casa") === false, "Casa não é um palindromo")
	 	t.end()
	})
}

module.exports = {validatePalindromo}

Como podemos ver, na linha 4 foi declarado uma function chamada “validatePalindromo” que irá executar os asserts do teste declarado como “Verificar se é palindromo” para validar a function “isPalindromo” do arquivo “..algorithms/palindromo.js”, depois de declarados os asserts e acionado a function “end” para encerrar o teste.

O código para acionar o teste da “index.test.js” será:

const test = require('tape')
const palindromoTest = require('./tests/palindromo.test')

palindromoTest.validatePalindromo();

Na linha 1 estamos importando o módulo tape, na linha 2 importando o código da palindromo.test e na última linha executamos a function “validatePalindromo”.

Executando o teste
Para executar o teste, no diretório raiz do projeto, digite:
“npm test”

Se tudo ocorrer como esperado, será exibido algo parecido com isso:
ResultadoUnitTestPalindromo

Espero ter ajudado.
Abraços e até a próxima 😉

Links

Código-Fonte: https://github.com/joaopauloit/text-algorithms
Tape: https://www.npmjs.com/package/tape