Bom pessoal, nós vamos começar com os testes unitários. De qualquer forma, nós podemos escrever testes para qualquer um dos nossos módulos. Então, nós podemos testar aqui, unitariamente, os módulos de cluster, network, storage, ou mesmo os nossos módulos raízes, como é o caso do módulo de desenvolvimento ou de produção. Porém, como nós estamos falando em teste unitário, eu acho que faz muito mais sentido nós testarmos unitariamente os nossos módulos, já que os nossos módulos raízes, eles simplesmente referenciam esses outros módulos que nós criamos, tá? Então, para os testes unitários, nós vamos focar aqui nos nossos módulos de cluster network e storage, especificamente de network, uma vez que a ideia aqui é simplesmente nós fazermos alguns exemplos, entendermos a lógica por trás dos testes unitários, beleza? Então, teremos aqui agora um arquivo para os testes dentro de network. Se nós estamos testando o módulo de network, nós vamos colocar os arquivos de teste dentro de network. Então, aqui eu vou criar um novo arquivo, que será o arquivo unit, pode ser qualquer nome, que eu vou colocar unit só para mostrar que esse aqui é um teste unitário, .tftest.hcl. Esse padrão de nomenclatura aqui deve ser seguido, tá? Então, todo o arquivo deve terminar, ele deve ter o sufixo .tftest.hcl para que ele seja entendido como um arquivo de testes. Então, quando nós rodarmos terraform-teste, o terraform vai ler estes arquivos de teste. Beleza? E, bom, nós precisamos entender que quando nós executamos um teste, nós executamos no contexto daquele módulo. Então, quando nós rodarmos terraform-teste, nós precisamos rodar dentro da pasta network para que ele execute estes testes. E, quando nós fizermos isso, precisamos notar também que o Terraform não vai entender o contexto dos nossos outros módulos. Ele está olhando simplesmente, unitariamente, para o módulo de network. E com isso nós temos um problema, devido à forma como nós estruturamos aqui o nosso projeto. Nós colocamos as configurações de providers simplesmente aqui nos nossos módulos raízes. Então, quando nós executarmos os nossos testes aqui dentro de network, o Terraform não vai saber quais os providers que ele deve utilizar. Na verdade, quando nós dermos aqui um Terraform init, ele vai baixar as versões mais atuais dos providers que ele identificou que estão sendo utilizados. Mas não é o ideal, uma vez que nós podemos ter ali uma atualização do provider que vai introduzir uma incompatibilidade com o que nós temos até esse momento. Então o ideal é que a gente coloque também os providers, quais são os providers que nós precisamos, dentro de cada um dos nossos módulos para garantir que tudo vai funcionar corretamente. Então aqui dentro de network eu terei um novo arquivo que será o arquivo providers. Providers.tf eu vou pegar aqui de produção e vou copiar aqui terraform, beleza. Bom, como nós estamos dentro somente aqui do módulo de network, nós não precisamos do provider do Azure, tá? E aqui nós podemos utilizar aquela sintaxe de version constraint para nós termos uma flexibilidade maior na utilização deste provider. Nós podemos aqui colocar uma restrição que a versão deste provider precisa ser maior do que 5.49. Maior ou igual a 5.49. Ou seja, se nós tivermos aqui um provider 5.50.50, está ok. Nós não estamos aqui incompatíveis com essas construintes da nossa versão. Então, nós deixamos aqui um pouco mais flexível, porque isso garante que o provider que nós especificamos aqui vai funcionar. E se nós fizermos um upgrade para 50, nós não teremos também nenhum problema, porque o nosso módulo é compatível com essa nova versão, utilizando essa sintaxe. E nós precisamos, claro, copiar esse arquivo aqui para dentro de cluster também, então deixa eu copiar aqui e colocar dentro de cluster, teremos essa mesma restrição e agora nós vamos copiar também para dentro de storage, então deixa eu colocar aqui storage, depois nós precisamos adicionar aqui outros providers ou alguma outra coisa nós adicionamos, só para nós não esquecermos, então aqui dentro do provider de storage nós precisamos lembrar que nós estamos trabalhando com Azure, então nós vamos colocar aqui essa restrição aqui do Azure, tá? E vamos utilizar uma abordagem um pouco semelhante. Vamos colocar aqui este carinha para garantir que nós temos pelo menos 3.113, mas se houver uma atualização para 114, não teremos nenhum problema, tá bom? Já temos aqui os nossos providers. Agora, dentro do nosso arquivo de testes, uma coisa que nós podemos especificar são as configurações do nosso provider. Então, imagina que a gente queira executar os nossos testes em uma outra região. Nós subimos aqui o nosso ambiente de desenvolvimento para a seguinte região. Deixa eu verificar aqui, providers. Nós subimos para o S2. Mas, às vezes, por qualquer questão que nós tivermos, nós podemos executar os testes em uma região diferente. Ou então, nós podemos utilizar outras credenciais específicas para os nossos testes. Nesse caso, basta nós sobrescrevermos. Então, nós podemos ter um bloco provider aqui dentro do nosso arquivo de testes também e sobrescrever. Nós poderíamos ter um profile aqui, por exemplo, teste. E beleza, as credenciais para executar os testes serão obtidas a partir desse profile. Mas não é o nosso caso, tá? Eu não terei nenhum profile aqui e região eu deixarei como S, como S, o S2, para manter aqui a mesma região. E aí você se pergunta, por que nós não teremos um profile? Bom, quando nós executarmos ali os nossos testes na nossa pipeline, nós iremos criar uma pipeline de continuous deployment, continuous integration lá no GitHub Actions, nós não teremos esse profile default, tá? Nós vamos precisar confiar nas nossas variáveis de ambiente, utilizar as variáveis de ambiente para definir qual é o nosso profile, então nós já começaremos a trabalhar dessa forma aqui. Inclusive, o nosso profile também será removido dos nossos arquivos ou dos nossos módulos raízes. Mas, por enquanto, vamos trabalhar aqui em um teste. Então, esse primeiro bloco aqui são as configurações dos nossos providers, especificamente para os nossos testes, para a execução dos nossos testes. Em seguida, nós precisamos definir as variáveis que nós queremos sobrescrever. Na verdade, nós precisamos definir as variáveis aqui, porque nós não temos aqui nenhum TF-Vars dentro do nosso módulo de network. Os nossos TF-Vars estão nos nossos módulos raízes, que novamente não são acessíveis aqui por dentro deste módulo de network. Então nós teremos as seguintes variáveis, nós precisamos informar aqui o prefixo, um CIDER block para a nossa VPC e o CIDER block para as nossas subnets, já que são essas as variáveis que nós precisamos, são essas três variáveis aqui. Bom, primeiro nós vamos começar com um teste bem simples, nós vamos validar que a nossa VPC será criada com esse CIDER block específico. Então nós temos esse barra 18 aqui, nós esperamos que quando nós dermos terraform plan, que seja planejada a criação de uma VPC exatamente com esse bloco. E nós precisamos então agora de utilizar aqui um bloco específico para os testes que é o bloco run, nós colocamos aqui run e nós especificamos aqui um identificador qualquer, eu vou colocar aqui validate vpc tá bom? Então temos aqui o nosso bloco run, esse bloco run o primeiro argumento que ele espera que a gente preenche aqui é o comando, na verdade nós podemos deixar aqui este este comando na verdade nós podemos deixar aqui este este comando sem ser especificado mas neste caso vai indicar aqui que o nosso teste um teste de integração se nós não especificarmos tá quando nós trabalhamos com teste unitado nós precisamos especificar aqui comando igual a pleno dessa forma terraforma ao invés de provision, ao invés de rodar no Terraform e Apply para executar os testes, ele vai rodar simplesmente o Plano. Por padrão, ele roda o Apply e faz um teste de integração. Mas aqui nós queremos um teste unitário, é por isso que nós executamos aqui, ou que nós colocamos comando igual a Plano. Uma outra coisa que nós podemos fazer também é sobrescrever uma dessas variáveis. Então, essas variáveis aqui serão utilizadas para todos os testes, para todos os blocos run. Mas caso tenha algum bloco run específico que a gente queira alterar uma dessas variáveis, a gente pode alterar aqui também. Mas não é o nosso caso, nós vamos deixar aqui as mesmas variáveis que nós definimos para todos os testes. Em seguida, nós temos a parte principal dos nossos testes, que são os acertos. Então, nós temos aqui um acerto, onde nós vamos especificar uma condição que deve ser verificada e uma mensagem de erro caso essa condição não seja atendida. No nosso caso, a nossa condition será o seguinte, awf.tpc.tpc a WSVPC.VPC e por que nós temos aqui esse carinha aqui? Esse carinha é uma referência a este recurso que nós criamos. Este recurso aqui é a WSVPC.VPC. Então, dentro dos blocos da search, nós temos acesso a todos os recursos que foram criados nos arquivos de configuração deste módulo. Então, a WSVPCc.vpc.siderblock. Nós esperamos que esse carinha aqui seja exatamente igual a essa variável. Essa daqui é a nossa validação. Caso isso não aconteça, nós precisamos exibir a mensagem errorMessage igual a... Nós vamos colocar aqui que esse bloco sider, ele não é esperado então nós vamos colocar one expected sider block, simples assim, vamos só formatar aqui o nosso arquivo beleza, temos o nosso primeiro assert, nós teremos um outro assert também que eu acho que faz sentido, que é a validação da nossa tag, nós precisamos ter essa tag aqui que utiliza o prefixo e depois coloca um sufixo traço VPC. Então vamos fazer isso. Eu vou copiar e colar aqui, porque é uma condição bem simples e eu acho que já deu para entender a lógica aqui por trás desses asserts. Então nós estamos validando aqui VPC.tags.name, precisa ser igual a teste traço VPC. tags.name precisa ser igual a teste-vpc. Então, awsvpc.vpc.tags.name exatamente igual a teste-vpc, porque o nosso prefixo no contexto dos nossos testes é igual a teste. Beleza? Temos aqui, então, os testes, dois testes, duas verificações para validar a nossa VPC eu gostaria de escrever aqui algumas validações também para as nossas subnets, já que nós temos uma lógica ali para nós criarmos as subnets, primeiro que nós temos aqui essa lista de CIDER blocks, então a quantidade de subnets vai depender de quantos itens nós temos dentro desses CIDER blocks, já que nós estamos definindo aqui um CID Blocks, uma lista com dois itens, nós precisamos ter exatamente duas subnets aqui no nosso planejamento da infraestrutura, na execução dos nossos testes. Então nós teremos aqui novamente agora um Command Plane e nós vamos verificar primeiramente o número de subnets que foram criados. Então nós precisamos aqui garantir que a quantidade de subnets aqui, ela é exatamente igual à quantidade que nós definimos nesse bloco. E aqui uma coisa bem interessante, nós estamos referenciando as nossas variáveis, ao invés de colocar o valor hardcode. Então nós poderíamos aqui também, ao invés de colocar hardcode, nós poderíamos colocar var.prefix. Então, vai depender aí da abordagem que você preferir. Geralmente, não ter valores hardcode é mais interessante. Mas eu vou deixar assim somente como um exemplo. E aqui nós estamos lidando, olha, ws.subnet.subnet, que é exatamente esse recurso aqui, precisa ter um tamanho de var.subnet.sideblocks, que são os blocos que nós indicamos. Então, primeiro acerte, nós teremos aqui um outro acerte agora, que será a verificação dos nossos siderblocks. Nós precisamos verificar se o siderblock da subnet 0 está correto e nós podemos fazer a mesma coisa também para a subnet 1. Então, nós verificamos aqui que as subnets foram criadas ou estão planejadas para serem criadas com os CIDR blocks corretos. Uma outra verificação também que eu acho interessante para nós celebrarmos aqui é a questão da availability zone. Nós estamos tentando distribuir as nossas subnets através de todas as availability zones. Isso indica que se eu tenho duas subnets, a Z de cada uma dessas subnets deve ser diferente. Uma Z de uma subnet deve ser diferente da Z da outra subnet. Nós podemos fazer essa verificação também de uma forma muito simples. Nós verificamos que a Z da subnet 0 é diferente da Z da subnet 1. Caso essa condição seja falsa, nós vamos exibir aqui que as subnets não deveriam estar na mesma AZ. Beleza? Eu acho que já temos aqui bons exemplos de testes, nós temos aqui várias garantias executando esses testes. E agora nós vamos entender como nós rodamos esses testes. Então vamos abrir aqui o novo terminal, nós vamos agora acessar o nosso módulo de network. Vamos navegar aqui até o módulo de network. Nós precisamos inicializar, então vamos dar aqui um terraform init. É importante inicializarmos dentro aqui do contexto do módulo. Aqui ele já encontrou a restrição e observe que ele baixou aqui o AWS 5.60. Então, ele desconhece as nossas restrições aqui que nós colocamos dentro dos nossos módulos principais. Nós podemos observar aqui que nos módulos principais nós colocamos 5.49 e aqui ele já baixou o 5.60, que é a versão mais atual que nós temos disponível de acordo com as restrições que nós colocamos. Agora que nós inicializamos aqui o nosso módulo, nós podemos executar Terraform Test. Beleza, nós vamos verificar aqui o que vai acontecer. Bom, nós temos aqui que nenhuma credencial foi encontrada, claro, porque nós não especificamos o nosso profile, mas nós podemos aqui que nenhuma credencial foi encontrada, claro, porque nós não especificamos o nosso profile, mas nós podemos aqui, se nós lermos a documentação ali do nosso provider, nós vemos que uma das formas de o Terraform encontrar as credenciais para esse provider da AWS é consultando as variáveis de ambiente. Então, nós podemos exportar aqui a AWS profile, tá? E aqui eu vou colocar default. Então simplesmente acertando essa variável de ambiente, o Terraform vai considerar as credenciais deste profile default. Agora eu vou executar novamente, vamos verificar, agora está em progresso, aparentemente começou bem, o validate VPC passou e o nosso Validate Subnet também passou. Todos os dois testes que nós temos, os dois blocos run, rodaram com sucesso. Você pode observar aqui que foi bem rápido porque nós não provisionamos os recursos. Nós utilizamos somente o comando plain para fazer essa validação. E dessa forma, nós encerramos aqui o nosso assunto sobre testes unitários. Foi simplesmente para você entender a lógica, mas tudo isso que nós aplicamos aqui pode ser utilizado em todos os demais módulos, tá? Nada te impede de executar esses testes unitários para os demais módulos. A questão é que fica um pouco mais complicado de nós executarmos testes unitários para os nossos módulos raízes aqui, porque nós temos uma dependência de outros módulos, mas também é perfeitamente possível nós fazermos isso. Beleza? Eu espero que você tenha gostado. Vejo você na nossa próxima aula.