Boas-vindas ao repositório do projeto Store Manager

Para realizar o projeto, atente-se a cada passo descrito a seguir, e se tiver qualquer dúvida, nos envie no Slack da turma! #vqv 🚀

Aqui, você vai encontrar os detalhes de como estruturar o desenvolvimento do seu projeto a partir desse repositório, utilizando uma branch específica e um Pull Request para colocar seus códigos.

📃 Termos e acordos

Entregáveis

🤷🏽‍♀️ Como entregar
  • Para entregar o seu projeto você deverá criar um Pull Request neste repositório.

  • Lembre-se que você pode consultar nosso conteúdo sobre Git & GitHub e nosso Blog - Git & GitHub sempre que precisar!

🧑‍💻 O que deverá ser desenvolvido
  • Você vai desenvolver uma API RESTful utilizando a arquitetura em camadas!

  • A API a ser construída é um sistema de gerenciamento de vendas em que será possível criar, visualizar, deletar e atualizar produtos e vendas. Você deverá utilizar o banco de dados MySQL para a gestão de dados.

  • Você também irá desenvolver testes para garantir as funcionalidade das implementações, uma habilidade essencial para a pessoa desenvolvedora.

📝 Habilidades a serem trabalhadas

Neste projeto, verificamos se você é capaz de:

  • Interagir com um banco de dados relacional MySQL;
  • Implementar uma API utilizando arquitetura em camadas;
  • Criar validações para os dados recebidos pela API;
  • Escrever testes para APIs para garantir a implementação dos endpoints;
📆 Data de Entrega
  • Este projeto é individual

  • Serão 5 dias de projeto

  • Data de entrega para avaliação regular do projeto: 13/07/2023 14h00

Orientações

⚠️ Aviso: Não é necessário entrar no container para rodar os testes e nem para iniciar a aplicação.

  • O container backend inicia a aplicação automaticamente.

  • Os testes do avaliador são executados fora do container. Caso não sejam definidas variáveis de ambiente, os testes irão assumir valores como os em env.example.

  • ⚠️ É necessário ter a versão Node 16.14 ou superior instalada localmente.

🐳 Iniciando a aplicação no Docker Compose
# Instale as dependências
npm install

# Inicie os containers do compose `backend` e `db`
# A aplicação estará disponível em `http://localhost:3001` em modo de desenvolvimento
docker-compose up -d

# É possível ver os logs da aplicação com `docker logs -n 20 -f <nome-do-container>`
docker logs -n 10 -f store_manager
🖥️ Iniciando a aplicação localmente

⚠️ Atenção: Ao rodar localmente, a aplicação deverá receber variáveis de ambiente como exemplificado em env.example para poder se comunicar com o serviço de banco de dados.

# Instale as dependências
npm install

# Inicie apenas o serviço `db` no compose
docker-compose up -d db

# Inicie a aplicação em modo de desenvolvimento
npm run dev:local
🛠 Desenvolvendo a aplicação e rodando testes

Passos básicos para o desenvolvimento:

  • Inicie a aplicação antes de rodar os testes do avaliador;
  • Desenvolva a aplicação dentro do diretório backend/src;
  • Desenvolva os testes dentro do diretório backend/tests;
    • Os arquivos de testes devem terminar com o sufixo .test.js.

Segue um resumo dos comandos relacionados aos testes:

⚠️ Atenção ⚠️

  • Os testes do avaliador são executados fora do container na raiz do projeto.
  • Os testes do avaliador só iniciam quando todos os testes do mocha estão passando.
#### Comandos dos testes do avaliador
npm run lint     # roda a verificação do linter
npm test         # roda todos os testes no terminal ou
REQ=01 npm test  # rodando apenas o teste do requisito 01 pelo terminal ou
npm run cy:open  # abre a interface gráfica do Cypress para rodar os testes

#### Comandos dos testes com mocha
npm run test:mocha     # roda os testes do mocha
npm run test:coverage  # roda os testes e mostra a cobertura geral
npm run test:mutation  # roda os testes e mostra a cobertura de mutações
🎲 Tabelas do banco de dados
Diagrama de Entidade-Relacionamento
DER
Tabela Formato Notas
products Tabela Produtos O id é gerado automaticamente
sales Tabela Vendas O id e date são gerados automaticamente
sales_products Tabela Vendas-Produtos Os registros nessa tabela são removidos automaticamente em caso de remoção do produto ou da venda relacionados (ON DELETE CASCADE)
  • Os scripts para criar e popular o banco de dados podem ser vistos no diretório sql;

💡 Dica: Para outros detalhes (ex: debugging 🐞), dê uma olhada na FAQ ou procure a monitoria. Bom projeto! 🚀

Requisitos do projeto

01 - Crie endpoints para listar produtos

💡Dica: Comece criando pelo menos um teste do mocha para que os testes do avaliador funcionem.

  • O endpoint para listar produtos deve ser acessível através do caminho GET /products e GET /products/:id;
  • Através do caminho GET /products, todos os produtos devem ser retornados;
  • Através do caminho GET /products/:id, apenas o produto com o id presente na URL deve ser retornado;
  • O resultado da listagem deve ser ordenado de forma crescente pelo campo id;
  • Crie testes que garantem a funcionalidade implementada;
O que será testado:
  • Será validado que é possível listar todos os produtos

    Ao fazer uma requisição para GET /products, o resultado retornado deverá ser conforme exibido abaixo, com um status http 200:

    [
      {
        "id": 1,
        "name": "Martelo de Thor"
      },
      {
        "id": 2,
        "name": "Traje de encolhimento"
      }
      /* ... */
    ]
  • Será validado que não é possível listar um produto que não existe

    Ao fazer uma requisição para GET /products/:id, se não existir um produto com o id presente na URL, o resultado retornado deverá ser conforme exibido abaixo, com um status http 404:

    { "message": "Product not found" }
  • Será validado que é possível listar um produto específico com sucesso

    Ao fazer uma requisição para GET /products/:id, caso exista um produto com o id presente na URL, o resultado retornado deverá ser conforme exibido abaixo, com um status http 200:

    {
      "id": 1,
      "name": "Martelo de Thor"
    }
  • Será validado que os testes estão cobrindo:

    • Pelo menos 30% das linhas e possíveis mutações em código
    • Pelo menos 6 funções do código
💡Se quiser buscar os 100% de cobertura de testes, veja esta dica!

Se quiser incluir as rotas na sua cobertura de testes, lembre-se que testes unitários testam funções - e o router só faz chamadas, ele não implementa nenhuma função. O teste mais adequado para ele é de integração - fique à vontade para fazê-los para complementar seus testes unitários!

02 - Crie endpoints para listar vendas

  • O endpoint para listar vendas deve ser acessível através do caminho GET /sales e GET /sales/:id;
  • Através do caminho GET /sales, todas as vendas devem ser retornadas;
  • Através do caminho GET /sales/:id, apenas a venda com o id presente na URL deve ser retornada;
  • O resultado deve ser ordenado de forma crescente pelo campo saleId, em caso de empate, ordenar também de forma crescente pelo campo productId;
O que será testado:
  • Será validado que é possível listar todas as vendas

    Ao fazer uma requisição para GET /sales, o resultado retornado deverá ser conforme exibido abaixo, com um status http 200:

    [
      {
        "saleId": 1,
        "date": "2021-09-09T04:54:29.000Z",
        "productId": 1,
        "quantity": 2
      },
      {
        "saleId": 1,
        "date": "2021-09-09T04:54:54.000Z",
        "productId": 2,
        "quantity": 2
      }
    
      /* ... */
    ]
  • Será validado que não é possível listar uma venda que não existe

    Ao fazer uma requisição para GET /sales/:id, se não existir uma venda com o id presente na URL, o resultado retornado deverá ser conforme exibido abaixo, com um status http 404:

    { "message": "Sale not found" }
  • Será validado que é possível listar uma venda específica com sucesso

    Ao fazer uma requisição para GET /sales/:id, caso exista uma venda com o id presente na URL, o resultado retornado deverá ser conforme exibido abaixo, com um status http 200:

    [
      {
        "date": "2021-09-09T04:54:29.000Z",
        "productId": 1,
        "quantity": 2
      },
      {
        "date": "2021-09-09T04:54:54.000Z",
        "productId": 2,
        "quantity": 2
      }
    
      /* ... */
    ]
  • Será validado que os testes estão cobrindo:

    • Pelo menos 30% das linhas e possíveis mutações em código
    • Pelo menos 12 funções do código

💡 Dica: Você vai precisar buscar dados de mais de uma tabela para este requisito. Relembre o conteúdo sobre JOIN nas seções sobre MySQL caso necessário.

03 - Crie endpoint para cadastrar produtos

  • O endpoint deve ser acessível através do caminho POST /products;
  • Os produtos enviados devem ser salvos na tabela products do banco de dados;
  • O corpo da requisição deverá seguir o formato abaixo:
{
  "name": "ProdutoX"
}
O que será testado:
  • Será validado que é possível cadastrar um produto com sucesso

    Ao fazer uma requisição válida para POST /products, o resultado retornado deverá ser conforme exibido abaixo, com um status http 201:

    {
      "id": 4,
      "name": "ProdutoX"
    }
  • Será validado que os testes estão cobrindo:

    • Pelo menos 30% das linhas e possíveis mutações em código
    • Pelo menos 15 funções do código

04 - Crie validações para o cadastro de produtos

  • O endpoint de cadastro de produtos deve retornar mensagens de erro para requisições com dados inválidos;
  • Lembre-se, o banco de dados não deve ser acessado nas validações iniciais do corpo da requisição;
O que será testado:
  • Será validado que não é possível cadastrar um produto sem o campo name

    Se a requisição para POST /products não tiver o campo name, o resultado retornado deverá ser conforme exibido abaixo, com um status http 400 :

    { "message": "\"name\" is required" }
  • Será validado que não é possível cadastrar um produto com o campo name menor que 5 caracteres

    Se a requisição para POST /products não tiver name com pelo menos 5 caracteres, o resultado retornado deverá ser conforme exibido abaixo, com um status http 422

    { "message": "\"name\" length must be at least 5 characters long" }
  • Será validado que os testes estão cobrindo:

    • Pelo menos 40% das linhas e possíveis mutações em código
    • Pelo menos 15 funções do código
💡 Dica: Para testar middlewares, você pode mockar a função next como no exemplo a seguir:
// ...
const next = sinon.stub().returns(); // crie um stub
>
myMiddlewares.validateMiddleware(req, res, next); // passe o `next` para o middleware junto com o `req` e `res`
>
expect(next).to.have.been.calledWith(); // verifica se o `next` foi chamado pelo middleware
// ...

05 - Crie endpoint para cadastrar vendas

  • O endpoint de vendas deve ser acessível através do caminho POST /sales;
  • As vendas enviadas devem ser salvas nas tabelas sales e sales_products do banco de dados;
  • Deve ser possível cadastrar a venda de vários produtos através da uma mesma requisição;
  • O corpo da requisição deverá seguir o formato abaixo:
[
  {
    "productId": 1,
    "quantity": 1
  },
  {
    "productId": 2,
    "quantity": 5
  }
]
O que será testado:
  • Será validado que é possível cadastrar uma venda com sucesso

    Ao fazer uma requisição válida para POST /sales, o resultado retornado deverá ser conforme exibido abaixo, com um status http 201:

    {
      "id": 3,
      "itemsSold": [
        {
          "productId": 1,
          "quantity": 1
        },
        {
          "productId": 2,
          "quantity": 5
        }
      ]
    }
  • Será validado que os testes estão cobrindo:

    • Pelo menos 40% das linhas e possíveis mutações em código
    • Pelo menos 18 funções do código

06 - Crie validações para o cadastro de vendas

  • O endpoint de cadastro de vendas deve retornar mensagens de erro para requisições com dados inválidos;
  • Lembre-se, o banco de dados não deve ser acessado nas validações iniciais do corpo da requisição;
O que será testado:
  • Será validado que não é possível cadastrar uma venda sem o campo productId

    Se algum dos itens da requisição para POST /sales não tiver o campo productId, o resultado retornado deverá ser conforme exibido abaixo, com um status http 400:

    { "message": "\"productId\" is required" }
  • Será validado que não é possível cadastrar uma venda sem o campo quantity

    Se algum dos itens da requisição para POST /sales não tiver o campo quantity, o resultado retornado deverá ser conforme exibido abaixo, com um status http 400 :

    { "message": "\"quantity\" is required" }
  • Será validado que não é possível cadastrar uma venda com o campo quantity menor ou igual a 0 (Zero)

    Se a requisição para POST /sales tiver algum item em que o campo quantity seja menor ou igual a zero, o resultado retornado deverá ser conforme exibido abaixo, com um status http 422

    { "message": "\"quantity\" must be greater than or equal to 1" }
  • Será validado que não é possível cadastrar uma venda com o campo productId inexistente, em uma requisição com um único item

    Se o campo productId do item da requisição para POST /sales não existir no banco de dados, o resultado retornado deverá ser conforme exibido abaixo, com um status http 404

    { "message": "Product not found" }
  • Será validado que não é possível cadastrar uma venda com o campo productId inexistente, em uma requisição com vários items

    Se a requisição para POST /sales tiver algum item cujo campo productId não existe no banco de dados, o resultado retornado deverá ser conforme exibido abaixo, com um status http 404

    { "message": "Product not found" }
  • Será validado que os testes estão cobrindo:

    • Pelo menos 50% das linhas e possíveis mutações em código
    • Pelo menos 18 funções do código

07 - Crie endpoint para atualizar um produto

  • O endpoint deve ser acessível através do caminho PUT /products/:id;
  • Apenas o produto com o id presente na URL deve ser atualizado;
  • O corpo da requisição deve ser validado igual no cadastro;
  • O corpo da requisição deverá seguir o formato abaixo:
{
  "name": "Martelo do Batman"
}
O que será testado:
  • Será validado que não é possível alterar um produto sem o campo name

    Se a requisição para PUT /products/:id não tiver o campo name, o resultado retornado deverá ser conforme exibido abaixo, com um status http 400 :

    { "message": "\"name\" is required" }
  • Será validado que não é possível alterar um produto com o campo name menor que 5 caracteres

    Se a requisição para PUT /products/:id não tiver name com pelo menos 5 caracteres, o resultado retornado deverá ser conforme exibido abaixo, com um status http 422

    { "message": "\"name\" length must be at least 5 characters long" }
  • Será validado que não é possível alterar um produto que não existe

    Se a requisição para PUT /products/:id informar o id de um produto inexistente, o resultado retornado deverá ser conforme exibido abaixo, com um status http 404:

      { "message": "Product not found" }
  • Será validado que é possível alterar um produto com sucesso

    Ao fazer uma requisição válida para PUT /products/:id, o resultado retornado deverá ser conforme exibido abaixo, com um status http200:

    {
      "id": 1,
      "name": "Martelo do Batman"
    }

    Também será verificado que o produto foi alterado corretamente no banco de dados.

  • Será validado que os testes estão cobrindo:

    • Pelo menos 50% das linhas e possíveis mutações em código
    • Pelo menos 21 funções do código

08 - Crie endpoint para deletar um produto

  • O endpoint deve ser acessível através do caminho DELETE /products/:id;
  • Apenas o produto com o id presente na URL deve ser deletado;
O que será testado:
  • Será validado que não é possível deletar um produto que não existe

    Se a requisição para DELETE /products/:id informar o id de um produto inexistente, o resultado retornado deverá ser conforme exibido abaixo, com um status http 404:

      { "message": "Product not found" }
  • Será validado que é possível deletar um produto com sucesso

    Ao fazer uma requisição válida para DELETE /products/:id, não deve ser retornada nenhuma resposta, apenas um status http 204;

    Também será verificado que o produto foi removido corretamente no banco de dados.

  • Será validado que os testes estão cobrindo:

    • Pelo menos 60% das linhas e possíveis mutações em código
    • Pelo menos 24 funções do código

Requisitos Bônus

09 - Crie endpoint para deletar uma venda

  • O endpoint deve ser acessível através do caminho DELETE /sales/:id;
  • Apenas a venda com o id presente na URL deve ser deletada;
O que será testado:
  • Será validado que não é possível deletar uma venda que não existe

    Se a requisição para DELETE /sales/:id informar o id de uma venda inexistente, o resultado retornado deverá ser conforme exibido abaixo, com um status http 404:

      { "message": "Sale not found" }
  • Será validado que é possível deletar uma venda com sucesso

    Ao fazer uma requisição válida para DELETE /sales/:id, não deve ser retornada nenhuma resposta, apenas um status http 204;

    Também será verificado que a venda foi removida corretamente no banco de dados.

  • Será validado que os testes estão cobrindo:

    • Pelo menos 70% das linhas e possíveis mutações em código
    • Pelo menos 27 funções do código

10 - Crie endpoint para atualizar a quantidade de um produto em uma venda

  • O endpoint deve ser acessível através do caminho /sales/:saleId/products/:productId/quantity;
  • Apenas a quantidade do produto vendido com o productId na URL deve ser atualizada;
  • O corpo da requisição receberá um valor quantity, que:
    • Deverá ser validado como o valor quantity para produtos recebidos na requisição de cadastro de venda;
    • Substituirá o valor atual de quantity do produto com o productId na venda;
  • O corpo da requisição deverá seguir o formato abaixo:
{
  "quantity": 20
}
O que será testado:
  • Será validado que não é possível realizar alterações em uma venda sem o campo quantity

    Se a requisição para PUT /sales/:saleId/products/:productId/quantity não tiver o campo quantity, o resultado retornado deverá ser conforme exibido abaixo, com um status http 400:

    { "message": "\"quantity\" is required" }
  • Será validado que não é possível realizar alterações em uma venda com o campo quantity menor ou igual a 0 (Zero)

    Se a requisição para PUT /sales/:saleId/products/:productId/quantity tiver o campo quantity menor que zero, o resultado retornado deverá ser conforme exibido abaixo, com um status http 422:

    { "message": "\"quantity\" must be greater than or equal to 1" }
  • Será validado que não é possível realizar alterações em uma venda com productId inexistente

    Se a requisição para PUT /sales/:saleId/products/:productId/quantity tiver o campo productId com um valor não existente no banco, o resultado retornado deverá ser conforme exibido abaixo, com um status http 404:

    { "message": "Product not found in sale" }
  • Será validado que não é possível alterar uma venda que não existe

    Se a requisição para PUT /sales/:saleId/products/:productId/quantity informar o saleId de uma venda inexistente, o resultado retornado deverá ser conforme exibido abaixo, com um status http 404:

      { "message": "Sale not found" }
  • Será validado que é possível alterar a quantidade de um produto de uma venda com sucesso

    Ao fazer uma requisição válida para PUT /sales/:saleId/products/:productId/quantity, o produto atualizado deverá ser retornado conforme exibido abaixo, com um status http 200:

    {
      "date": "2023-05-06T03:14:28.000Z",
      "productId": 2,
      "quantity": 20,
      "saleId": 1
    }

    Também será verificado que a quantidade do produto foi alterada corretamente no banco de dados.

  • Será validado que os testes estão cobrindo:

    • Pelo menos 50% das linhas e possíveis mutações em código
    • Pelo menos 21 funções do código

11 - Crie endpoint para pesquisar produtos

  • O endpoint deve ser acessível através do URL GET /products/search;
  • O endpoint deve ser capaz de trazer todos os produtos no banco de dados contendo o valor da query q em name, se existirem;
  • Sua aplicação deve ser capaz de retornar um array de produtos que contenham em seu nome o termo passado na URL;
  • Sua aplicação deve ser capaz de retornar todos os produtos caso query params q esteja vazia;
  • Sua aplicação deve ser capaz de retornar um array vazio caso nenhum nome satisfaça a busca;
  • O query params da requisição deverá seguir o formato abaixo:
  http://localhost:PORT/products/search?q=Martelo
O que será testado:
  • Será validado que é possível buscar um produto pelo name

    Se a requisição para GET /products/search for feita com um query params q cujo valor exista no atributo name de algum produto, o resultado retornado deverá ser conforme exibido abaixo, com um status http 200:

    // GET /products/search?q=Martelo
    
    [
      {
        "id": 1,
        "name": "Martelo de Thor"
      }
    ]
  • Será validado que é possível buscar todos os produtos quando passa a busca vazia

    Se a requisição para GET /products/search foi feita com um query params q vazio, o resultado retornado deverá ser conforme exibido abaixo, com um status http 200:

    // GET /products/search?q=
    
    [
      {
        "id": 1,
        "name": "Martelo de Thor",
      },
      {
        "id": 2,
        "name": "Traje de encolhimento",
      }
      /* ... */
    ]
  • Será validado que a busca retorna um array vazio quando não há produtos correspondentes

    Se a requisição para GET /products/search for feita com um query params q cujo valor não exista no atributo name de nenhum produto, o resultado retornado deverá ser um array vazio, com um status http 200:

    // GET /products/search?q=ProdutoInexistente
    
    []
  • Será validado que os testes estão cobrindo:

    • Pelo menos 70% das linhas e possíveis mutações em código
    • Pelo menos 33 funções do código

🗣 Nos dê feedbacks sobre o projeto!

Ao finalizar e submeter o projeto, não se esqueça de avaliar sua experiência preenchendo o formulário. Leva menos de 3 minutos!

Formulário de avaliação do projeto

🗂 Compartilhe seu portfólio!

Você sabia que o LinkedIn é a principal rede social profissional e compartilhar o seu aprendizado lá é muito importante para quem deseja construir uma carreira de sucesso? Compartilhe esse projeto no seu LinkedIn, marque o perfil da Trybe (@trybe) e mostre para a sua rede toda a sua evolução.

gcGVzc29hIGRlIGZhY2lsaXRhw6