Load Account Router + Test no CrazyStack Node.js
A aula Load Account Router é responsável por estabelecer as rotas de requisições de carregamento de conta. Essas rotas são configuradas usando uma biblioteca de roteamento, como o Fastify, e usam o padrão de projeto Middleware para controlar a lógica de negócios relacionada ao carregamento de conta.
Nesta aula, você irá aprender a criar uma rota para o endpoint de atualização de token, que é responsável por fornecer ao cliente um novo token de acesso e refresh token. Esta rota irá usar um controlador, que foi construído na aula anterior, para manipular a lógica de negócios e retornar a resposta adequada para o cliente.
Ao final desta aula, você será capaz de criar rotas completas para gerenciar o carregamento de conta em sua aplicação e testar as requisições usando uma ferramenta de teste de API, como o Postman ou Insomnia.
Este artigo servirá como uma espécie de documentação de alguns códigos vistos durante as aulas apenas como material complementar.
const headersRefreshJsonSchema = {
type: "object",
required: ["refreshtoken"],
properties: {
refreshtoken: { type: "string" },
},
};
const refreshResponse = {
200: {
type: "object",
properties: {
refreshToken: { type: "string" },
accessToken: { type: "string" },
},
},
};
export const refreshGetSchema = {
schema: {
headers: headersRefreshJsonSchema,
response: refreshResponse,
},
};
Este código é uma definição de esquema para a rota de atualização de token (refresh). Ele está sendo usado para validar a requisição e a resposta para esta rota.
A constante headersRefreshJsonSchema é uma definição de objeto JSON que descreve os cabeçalhos da requisição. É esperado que haja um cabeçalho "refreshtoken" na requisição, e ele deve ser uma string.
A constante refreshResponse é uma definição de objeto JSON que descreve a resposta da rota. A resposta deve ser um objeto com dois campos: "refreshToken" e "accessToken", ambos do tipo string.
A constante refreshGetSchema é uma definição completa do esquema da rota, incluindo a descrição dos cabeçalhos da requisição e da resposta. Esta constante é usada para validar as requisições e respostas da rota de atualização de token.
import { refreshtokenLogged } from "@/application/infra/middlewares";
import { refreshAdapter } from "./accountAdapter";
import { refreshGetSchema } from "./accountSchema";
async function account(fastify: any, options: any) {
fastify.addHook("preHandler", refreshtokenLogged());
fastify.get("/account/refresh", refreshGetSchema, refreshAdapter());
}
export { account };
Esse código é uma rota que é adicionada a uma aplicação Fastify para atualizar o token de acesso de uma conta.
A primeira linha adiciona um hook "preHandler" que é o middleware "refreshtokenLogged".
Na segunda linha, o método GET para a rota "/account/refresh" é adicionado ao Fastify, passando o esquema de resposta esperado "refreshGetSchema" e o adaptador "refreshAdapter".
Ao chamar essa rota, o middleware "refreshtokenLogged" será executado primeiro e depois o adaptador "refreshAdapter".
O front-end precisa chamar esses endpoints de refresh token após o login inicial ou quando o token de acesso for expirado. Isso é necessário para a renovação do token de acesso sem ter que pedir novamente as credenciais do usuário.
A ideia é que, após o login inicial, o front-end receba um token de acesso e um token de refresh, ambos com tempo de expiração. Quando o token de acesso expirar, o front-end pode usar o token de refresh para solicitar um novo token de acesso sem ter que pedir as credenciais do usuário novamente. Para isso, o front-end fará uma chamada ao endpoint especificado para renovação de token (geralmente "/account/refresh") enviando o token de refresh no header da requisição.
Esse endpoint verificará a validade do token de refresh e, caso seja válido, retornará um novo token de acesso ao front-end, que pode ser usado nas próximas requisições.
Dessa forma, é possível manter a sessão do usuário sem ter que pedir as credenciais dele novamente, mesmo após o token de acesso ter expirado.
import { makeFastifyInstance } from "@/index";
import { Collection } from "mongodb";
import { MongoHelper } from "@/application/infra";
import { hash } from "bcrypt";
jest.setTimeout(50000);
let userCollection: Collection;
const userBody = {
email: "gustavoteste41@hotmail.com",
name: "Gustavo",
role: "client",
password: "123456",
passwordConfirmation: "123456",
coord: { type: "Point", coordinates: [-46.693419, -23.568704] },
};
describe("Route api/account", () => {
let fastify: any;
beforeAll(async () => {
const client = await MongoHelper.connect(process.env.MONGO_URL as string);
fastify = await makeFastifyInstance(client);
await fastify.listen({ port: 3000, host: "0.0.0.0" });
});
afterAll(async () => {
await fastify.close();
await MongoHelper.disconnect();
fastify = null;
});
beforeEach(async () => {
userCollection = await MongoHelper.getCollection("user");
await userCollection.deleteMany({});
});
describe("GET /api/account/refresh", () => {
test("Should return 200 on refresh", async () => {
const response = await fastify.inject({
method: "POST",
url: "/api/auth/signup",
payload: userBody,
});
const responseBody = JSON.parse(response.body);
const refreshtoken = responseBody.refreshToken;
expect(response.statusCode).toBe(200);
expect(responseBody.user).toBeTruthy();
expect(responseBody.accessToken).toBeTruthy();
expect(responseBody.refreshToken).toBeTruthy();
const responseRefresh = await fastify.inject({
method: "GET",
url: "/api/account/refresh",
headers: { refreshtoken },
});
const responseBodyRefresh = JSON.parse(responseRefresh.body);
expect(responseRefresh.statusCode).toBe(200);
expect(responseBodyRefresh.accessToken).toBeTruthy();
expect(responseBodyRefresh.refreshToken).toBeTruthy();
});
test("Should return 400 for bad requests", async () => {
await userCollection.insertOne(userBody);
const response = await fastify.inject({
method: "GET",
url: "/api/account/refresh",
});
expect(response.statusCode).toBe(400);
});
test("Should return 401 for unauthorized refresh token", async () => {
await userCollection.insertOne(userBody);
const response = await fastify.inject({
method: "GET",
url: "/api/account/refresh",
headers: { refreshtoken: "invalid_token" },
});
expect(response.statusCode).toBe(401);
});
});
});
Este é um teste de integração do endpoint de refresh de token de autenticação. Ele testa três casos de uso:
-
Deve retornar 200 ao atualizar o token: O teste envia uma requisição de POST para o endpoint de cadastro para criar um usuário, em seguida, envia uma requisição GET para o endpoint de refresh, passando o token de refresh obtido na resposta anterior como cabeçalho. É esperado que o código de resposta seja 200 e que o corpo da resposta contenha os novos tokens de acesso e refresh.
-
Deve retornar 400 para requisições mal formadas: O teste envia uma requisição GET para o endpoint de refresh sem incluir o token de refresh no cabeçalho. É esperado que o código de resposta seja 400, indicando que a requisição é inválida.
-
Deve retornar 401 para token de refresh não autorizado: O teste envia uma requisição GET para o endpoint de refresh incluindo um token de refresh inválido no cabeçalho. É esperado que o código de resposta seja 401, indicando que o token de refresh não é válido.
O teste usa o pacote Jest para gerenciar o fluxo de teste e o pacote Fastify para simular o servidor HTTP que responde às requisições. O banco de dados MongoDB é usado para armazenar os dados do usuário. https://github.com/gumiranda/CrazyStackNodeJs/commit/0d99575813180f428305a57c025a7ca4a5fa1bf2