├── .gitignore ├── README.md ├── database.db ├── package-lock.json ├── package.json ├── src ├── DAO │ └── usuarioDAO.js ├── app.js ├── controller │ └── usuarioController.js ├── database │ ├── create-and-populate.js │ └── db-sqlite.js ├── middleware │ └── autenticacao.js ├── model │ └── usuarioModel.js ├── server.js └── services │ ├── logica.js │ └── usuario.js └── test ├── integracao └── usuario.test.js └── unitario ├── logica.test.js └── usuario.test.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # :ballot_box_with_check: ToDo API - T17 - Resilia 2 | 3 | Projeto de educacional do curso de Web Dev Full Stack da [Resilia Educação](https://www.resilia.com.br/) referente ao Módulo 04. 4 | 5 | Projeto realizado utilizando o [Node.js](https://nodejs.org/en/) com framework [Express](https://expressjs.com/). Como banco de dados, foi utilizado o SQLite. 6 | 7 | ## :pencil2: Pré-Requisitos 8 | 9 | * Node.js v.16.15.1 10 | * NPM v.8.11.0 11 | 12 | ## :outbox_tray: Packages 13 | 14 | * Express 15 | * Nodemon 16 | * SQLite 17 | * Jest 18 | * Super Test 19 | 20 | ## :outbox_tray: Instalação da Aplicação 21 | 22 | Abra o terminal/Powershell e rode os comandos abaixo: 23 | 24 | Clonando o repositório: 25 | ``` 26 | git clone https://github.com/cinmcantu/ToDoAPI-T17.git 27 | ``` 28 | 29 | Entrando na pasta: 30 | ``` 31 | cd ToDoAPI-T17 32 | ``` 33 | 34 | Instalando os pacotes: 35 | ``` 36 | npm install 37 | ``` 38 | 39 | Rodando o projeto: 40 | ``` 41 | npm start 42 | ``` 43 | 44 | ## :open_file_folder: Reinicializando Banco de Dados 45 | 46 | Para iniciar um banco de dados novo com os dados padrão, delete o arquivo `database.db` e rode o comando abaixo: 47 | ``` 48 | npm run populate 49 | ``` 50 | 51 | ## :bug: Testes 52 | 53 | Os testes foram implementados utilizando Jest e Super Test. Para executá-los, rode o comando no terminal: 54 | ``` 55 | npm test 56 | ``` 57 | 58 | ## :trolleybus: Rotas implementadas 59 | 60 | ### Usuário 61 | * __GET /usuario__ 62 | 63 | Esquema da resposta 64 | ```json 65 | { 66 | "usuarios": [ 67 | { 68 | "ID": 1, 69 | "NOME": "Neo", 70 | "EMAIL": "mr.anderson@email.com", 71 | "SENHA": "SenhaSuperSegura123456!" 72 | } 73 | ], 74 | "total": 1, 75 | "erro": false 76 | } 77 | ``` 78 | 79 | * __GET /usuario/email/:email__ 80 | 81 | Esquema da resposta 82 | ```json 83 | { 84 | "usuario": { 85 | "ID": 1, 86 | "NOME": "Neo", 87 | "EMAIL": "mr.anderson@email.com", 88 | "SENHA": "SenhaSuperSegura123456!" 89 | }, 90 | "erro": false 91 | } 92 | ``` 93 | 94 | * __POST /usuario__ 95 | 96 | Esquema da requisição 97 | ```json 98 | { 99 | "nome" : "Neo", 100 | "email" : "mr.anderson@email.com", 101 | "senha" : "SenhaSuperSegura123456!" 102 | } 103 | ``` 104 | 105 | Esquema da resposta 106 | ```json 107 | { 108 | "mensagem": "Usuario inserido com sucesso", 109 | "erro": false 110 | } 111 | ``` 112 | 113 | * __PUT /usuario/email/:email__ 114 | 115 | Esquema da requisição 116 | ```json 117 | { 118 | "nome" : "Novo Neo", 119 | "email" : "mr.anderson@email.com", 120 | "senha" : "NovaSenhaSuperSegura123456!" 121 | } 122 | ``` 123 | 124 | Esquema da resposta 125 | ```json 126 | { 127 | "mensagem": "Usuário com email mr.anderson@email.com atualizado com sucesso", 128 | "erro": false 129 | } 130 | ``` 131 | 132 | * __DELETE /usuario/email/:email__ 133 | 134 | Esquema da resposta 135 | ```json 136 | { 137 | "mensagem": "Usuário com email mr.anderson@email.com deletado com sucesso", 138 | "erro": false 139 | } 140 | ``` 141 | 142 | * __ERROS__ 143 | Esquema da resposta 144 | ```json 145 | { 146 | "mensagem": "Usuário de email neo@email.com não encontrado", 147 | "erro": true 148 | } 149 | ``` 150 | 151 | --- 152 | --- 153 | 154 | ## :computer: Atualizações da Aula 155 | 156 | __Aulas da ultima semana__ 157 | 158 | Criação da DAO para comunicação com o banco de dados através de queries. 159 | 160 | Intalação dos frameworks de teste Jest e Super Test e criação dos testes unitários e de integração. 161 | 162 | Criação do arquivo `server.js` que agora será resposável por "ligar" o servidor, dessa forma consequimos exportar o arquivo `app.js` e usá-lo no teste de integração. 163 | 164 | Instalação do package `cors` que permite que nossa API seja consumida pelo frontend. 165 | 166 | --- 167 | ## :nail_care: REFATORAÇÃO 168 | 169 | O projeto passou por uma refatoração, algo muito comum em projetos reais onde, depois de executar o básico, são feitas algumas melhorias no código e alterações para deixá-lo melhor e mais eficiente. As refatorações foram: 170 | 171 | * __Remoção da entidade TAREFA:__ para poder entregar o código mais rápido e poder fazer uma boa refatoração, eu foquei na entidade USUARIO. Para entidade TAREFA bastaria seguir a mesma estrutura inserindo as validações necessárias. 172 | 173 | * __Troca de classes por objetos:__ como boa parte do código já estava sendo feita com funções e objetos, eu removi as poucas classes que ainda tinham para manter um padrão. 174 | 175 | * __Validações:__ alteração na forma que estavam sendo feita as validações. Da forma que está agora é possível testá-las de maneira independente além de estarem mais desacopladas e ser possível utilizá-las em lugares diferente do projeto. 176 | 177 | * __Testes:__ foram incluidos testes unitários e de integração, os testes unitários estão testando as funções de validação enquanto os testes de integração estão testando cada uma das rotas. 178 | 179 | * __Alteração nos retornos da DAO/Model e na resposta do controler:__ essa foi a maior alteração. Para garantir que podemos ter uma visão maior dos erros, a resposta/return que cada um dos arquivos (DAO/Model/Controller) dá está diferente. Agora a model passa o status (status HTTP) para o controller dependendo da resposta que ela recebe da DAO. Dessa forma podemos separar erros do servidor (500) que geralmente ocorrem na controller de erros do clinte (400) que costumam ser pegos na Model/DAO. Todas funções também estão envolvidas por blocos `try/catch` para que nenhum erro passe batido. 180 | 181 | Lembrando: erros 500 ruins são aqueles que "quebram" nossa aplicação por não estarmos esperando que eles ocorram. Erros 500 enviados por nós "de propósito" com um json com a mensagem do que ocorreu não tem problema. 182 | 183 | * __Criação de uma função para montar a query de UPDATE:__ a query de UPDATE é a mais complicada quando queremos atualizar apenas alguns parâmetros. Por isso foi criada uma função que monta a query de acordo com o que foi recebido. Dessa forma a rota PUT também consegue receber apenas um atributo para atualizar (assim como a rota PATCH, apenas lembrando que para rota PUT temos que garantir indepotência). 184 | -------------------------------------------------------------------------------- /database.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cinmcantu/ToDoAPI-T17/93a29153896abe10676a0ab8a77742998a3bdb1c/database.db -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "todolist", 3 | "version": "1.0.0", 4 | "description": "Projeto de API TO DO LIST", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js", 8 | "start": "node ./src/server.js", 9 | "dev": "nodemon ./src/server.js", 10 | "populate" : "node ./src/database/create-and-populate.js" 11 | }, 12 | "keywords": [ 13 | "todolist", 14 | "api" 15 | ], 16 | "author": "cinmcantu", 17 | "license": "ISC", 18 | "type": "module", 19 | "dependencies": { 20 | "cors": "^2.8.5", 21 | "express": "^4.18.1", 22 | "sqlite3": "^5.0.10" 23 | }, 24 | "devDependencies": { 25 | "jest": "^28.1.3", 26 | "nodemon": "^2.0.19", 27 | "supertest": "^6.2.4" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/DAO/usuarioDAO.js: -------------------------------------------------------------------------------- 1 | import db from '../database/db-sqlite.js' 2 | 3 | const usuarioDAO = { 4 | pegaTodosUsuarios : ()=>{ 5 | return new Promise((resolve, reject)=>{ 6 | db.all('SELECT * FROM USUARIOS',(erro, linhas)=>{ 7 | if(erro){ 8 | reject(erro) 9 | }else{ 10 | resolve(linhas) 11 | } 12 | }) 13 | }) 14 | 15 | }, 16 | 17 | pegaUmUsuario : (email)=>{ 18 | return new Promise((resolve, reject)=>{ 19 | db.get('SELECT * FROM USUARIOS WHERE EMAIL = ?', email, 20 | (erro, dado)=>{ 21 | if(erro){ 22 | reject(erro) 23 | }else{ 24 | resolve(dado) 25 | } 26 | }) 27 | }) 28 | 29 | }, 30 | 31 | insereUsuario: (usuario)=>{ 32 | return new Promise((resolve, reject)=>{ 33 | db.run(`INSERT INTO USUARIOS (NOME, EMAIL, SENHA) 34 | VALUES (?, ?, ?)`, usuario.nome, usuario.email, usuario.senha, 35 | (erro)=>{ 36 | if(erro){ 37 | reject(erro) 38 | }else{ 39 | resolve("Usuario inserido com sucesso") 40 | } 41 | }) 42 | }) 43 | }, 44 | 45 | deletaUsuario : (email)=>{ 46 | return new Promise((resolve, reject)=>{ 47 | db.run(`DELETE FROM USUARIOS WHERE EMAIL = ?`, email, 48 | (erro)=>{ 49 | if(erro){ 50 | reject(erro) 51 | } else{ 52 | resolve(`Usuário com email ${email} deletado com sucesso`) 53 | } 54 | }) 55 | }) 56 | }, 57 | 58 | atualizaUsuario : (email, novoDado)=>{ 59 | // função que monta a query para atualzar apenas os 60 | // valores que forem recebidos pelo body 61 | const query = (novoDado)=>{ 62 | let nome = "" 63 | let email = "" 64 | let senha = "" 65 | if(novoDado.nome){ 66 | nome = `NOME = ?` 67 | } 68 | if(novoDado.email){ 69 | email = `EMAIL = ?` 70 | if(nome){ 71 | email = ', '+ email 72 | } 73 | } 74 | if(novoDado.senha){ 75 | senha = `SENHA = ?` 76 | if(nome || email){ 77 | senha = ', ' + senha 78 | } 79 | } 80 | 81 | return `UPDATE USUARIOS SET 82 | ${nome} ${email} ${senha} 83 | WHERE EMAIL = ? ` 84 | } 85 | 86 | return new Promise((resolve, reject)=>{ 87 | db.run(query(novoDado), 88 | ...Object.values(novoDado),email, 89 | (erro)=>{ 90 | if(erro){ 91 | reject(erro) 92 | } else{ 93 | resolve(`Usuário com email ${email} atualizado com sucesso`) 94 | } 95 | }) 96 | }) 97 | } 98 | 99 | } 100 | 101 | export default usuarioDAO -------------------------------------------------------------------------------- /src/app.js: -------------------------------------------------------------------------------- 1 | // Importando o packages 2 | import express from 'express' 3 | import cors from 'cors' 4 | 5 | 6 | // importando os controllers 7 | import usuarioController from './controller/usuarioController.js' 8 | 9 | // import de middlewares 10 | import autenticacao from './middleware/autenticacao.js' 11 | 12 | // instanciando o servidor 13 | const app = express() 14 | 15 | 16 | // middlewares 17 | app.use(cors()) // middleware que libera o frontend acessar nossa api 18 | app.use(express.json()) // middleware que faz o parse do json do body 19 | 20 | // outros middlewares (validações, autenticações, tratamento de erros) 21 | // Usado se necessário dependendo das regras de negócio do processo 22 | // Comentado para não ter problema, descomente se quiser usar essa validacao 23 | // autenticacao(app) 24 | 25 | // chamando os controllers e passando o servidor como parametro 26 | usuarioController(app) 27 | 28 | export default app 29 | -------------------------------------------------------------------------------- /src/controller/usuarioController.js: -------------------------------------------------------------------------------- 1 | import usuarioModel from "../model/usuarioModel.js" 2 | 3 | const usuarioController = (app)=>{ 4 | 5 | app.get('/usuario', async (req, res)=>{ 6 | try { 7 | const resposta = await usuarioModel.pegaUsuarios() 8 | 9 | res.status(resposta.status).json({ 10 | "usuarios": resposta.dados, 11 | "total" : resposta.total, 12 | "erro" : false 13 | }) 14 | 15 | } catch (error) { 16 | res.status(500).json({ 17 | "mensagem": error.mensagem, 18 | "erro" : true 19 | }) 20 | } 21 | 22 | }) 23 | 24 | app.get('/usuario/email/:email', async (req, res)=>{ 25 | const email = req.params.email 26 | 27 | try { 28 | const resposta = await usuarioModel.pegaUmUsuario(email) 29 | if(resposta.status===200){ 30 | res.status(resposta.status).json({ 31 | "usuario": resposta.dados, 32 | "erro" : false 33 | }) 34 | }else{ 35 | res.status(resposta.status).json({ 36 | "mensagem": resposta.mensagem, 37 | "erro" : true 38 | }) 39 | } 40 | 41 | } catch (error) { 42 | res.status(500).json({ 43 | "mensagem": error.message, 44 | "erro" : true 45 | }) 46 | } 47 | }) 48 | 49 | app.post('/usuario', async (req, res)=>{ 50 | const body = req.body 51 | try { 52 | const {status} = await usuarioModel.pegaUmUsuario(body.email) 53 | if(status === 404){ 54 | const resposta = await usuarioModel.insereUsuario(body) 55 | 56 | res.status(resposta.status).json({ 57 | "mensagem": resposta.mensagem, 58 | "erro" : false 59 | }) 60 | }else{ 61 | res.status(400).json({ 62 | "mensagem": `Usuário com email ${body.email} já existe`, 63 | "erro" : true 64 | }) 65 | } 66 | 67 | } catch (error) { 68 | res.status(500).json({ 69 | "mensagem": error.message, 70 | "erro" : true 71 | }) 72 | } 73 | 74 | }) 75 | 76 | app.delete('/usuario/email/:email', async (req,res)=>{ 77 | const email = req.params.email 78 | try { 79 | const {status,mensagem} = await usuarioModel.pegaUmUsuario(email) 80 | if(status===404){ 81 | res.status(404).json({ 82 | "mensagem": mensagem, 83 | "erro" : true 84 | }) 85 | }else{ 86 | const resposta = await usuarioModel.deletaUsuario(email) 87 | 88 | res.status(resposta.status).json({ 89 | "mensagem": resposta.mensagem, 90 | "erro" : false 91 | }) 92 | } 93 | 94 | } catch (error) { 95 | res.status(500).json({ 96 | "mensagem": error.message, 97 | "erro" : true 98 | }) 99 | } 100 | }) 101 | 102 | app.put('/usuario/email/:email',async (req, res)=>{ 103 | const body = req.body 104 | const email = req.params.email 105 | try { 106 | const {status,mensagem} = await usuarioModel.pegaUmUsuario(email) 107 | if(status===404){ 108 | res.status(404).json({ 109 | "mensagem": mensagem, 110 | "erro" : true 111 | }) 112 | }else{ 113 | const resposta = await usuarioModel.atualizaUsuario(email, body) 114 | 115 | res.status(resposta.status).json({ 116 | "mensagem": resposta.mensagem, 117 | "erro" : false 118 | }) 119 | } 120 | 121 | } catch (error) { 122 | res.status(500).json({ 123 | "mensagem": error.message, 124 | "erro" : true 125 | }) 126 | } 127 | }) 128 | } 129 | 130 | export default usuarioController 131 | -------------------------------------------------------------------------------- /src/database/create-and-populate.js: -------------------------------------------------------------------------------- 1 | /* 2 | Esse arquivo deve ser executado apenas uma vez 3 | */ 4 | import sqlite3 from 'sqlite3' 5 | sqlite3.verbose() 6 | const db = new sqlite3.Database('database.db'); 7 | 8 | const USUARIOS_SCHEMA = ` 9 | CREATE TABLE IF NOT EXISTS "USUARIOS" ( 10 | "ID" INTEGER PRIMARY KEY AUTOINCREMENT, 11 | "NOME" varchar(64), 12 | "EMAIL" varchar(64), 13 | "SENHA" varchar(64) 14 | );`; 15 | 16 | const ADD_USUARIOS_DATA = ` 17 | INSERT INTO USUARIOS (ID, NOME, EMAIL, SENHA) 18 | VALUES 19 | (1, 'Eugênio Oliveira', 'eugenio.oliveira@bol.com.br', '*******'), 20 | (2, 'Olívia Ribeiro', 'olivia.ribeiro@gmail.com', '********'), 21 | (3, 'Mirtes Faria Lima', 'mirtes_fl@yahoo.com', '********') 22 | ` 23 | 24 | function criaTabelaUsr() { 25 | db.run(USUARIOS_SCHEMA, (error)=> { 26 | if (error) console.log("Erro ao criar tabela de usuários"); 27 | }); 28 | } 29 | 30 | 31 | function populaTabelaUsr() { 32 | db.run(ADD_USUARIOS_DATA, (error)=> { 33 | if (error) console.log("Erro ao popular tabela de usuários"); 34 | }); 35 | } 36 | 37 | const TAREFAS_SCHEMA = ` 38 | CREATE TABLE IF NOT EXISTS TAREFAS ( 39 | ID INTEGER PRIMARY KEY AUTOINCREMENT, 40 | TITULO VARCHAR(64), 41 | DESCRICAO TEXT, 42 | STATUS VARCHAR(32), 43 | DATACRIACAO VARCHAR(32), 44 | ID_USUARIO INTEGER, 45 | FOREIGN KEY(ID_USUARIO) REFERENCES USUARIOD(ID) 46 | );`; 47 | 48 | const ADD_TAREFAS_DATA = `INSERT INTO TAREFAS (TITULO, DESCRICAO, STATUS, DATACRIACAO, ID_USUARIO) 49 | VALUES 50 | ('Yoga', 'Fazer yoga segunda e quarta', 'Continuo', '2021-01-10', 2), 51 | ('Médico', 'Consulta com Dr. Ayrton sexta', 'TODO', '2021-01-13', 1), 52 | ('Pagar contas', 'Pagar boletos de água e luz', 'DOING', '2021-01-02', 2), 53 | ('Mercado', 'Pegar lista na geladeira e fazer compras', 'TODO', '2021-01-08', 2), 54 | ('Dentista', 'Consulta com Dra Andreia sexta', 'TODO', '2021-01-11', 1), 55 | ('Pagar financiamento carro', 'Pagar parcela do mês do financiamento', 'Contínuo', '2021-01-05', 3) 56 | ` 57 | 58 | function criaTabelaTarefas() { 59 | db.run(TAREFAS_SCHEMA, (error)=> { 60 | if(error) console.log("Erro ao criar tabela de Tarefas"); 61 | }); 62 | } 63 | 64 | 65 | function populaTabelaTarefas() { 66 | db.run(ADD_TAREFAS_DATA, (error)=> { 67 | if (error) console.log("Erro ao popular tabela de Tarefas"); 68 | }); 69 | } 70 | 71 | db.serialize( ()=> { 72 | criaTabelaUsr(); 73 | populaTabelaUsr(); 74 | criaTabelaTarefas(); 75 | populaTabelaTarefas(); 76 | }); 77 | -------------------------------------------------------------------------------- /src/database/db-sqlite.js: -------------------------------------------------------------------------------- 1 | import sqlite3 from 'sqlite3' 2 | sqlite3.verbose() 3 | const db = new sqlite3.Database('database.db'); 4 | 5 | process.on('SIGINT', () => 6 | db.close(() => { 7 | console.log(' BD encerrado!'); 8 | process.exit(0); 9 | }) 10 | ); 11 | 12 | export default db -------------------------------------------------------------------------------- /src/middleware/autenticacao.js: -------------------------------------------------------------------------------- 1 | const autenticacao = (app)=>{ 2 | // Middleware que checa um atributo do header o cliente envia (usertype) 3 | // para saber se ele tem permissão para o acesso de determinados metodos 4 | app.use((req, res, next)=>{ 5 | if(req.method !== "GET"){ 6 | if(req.headers.usertype === "gerente"){ 7 | next() 8 | }else{ 9 | res.json({"erro" : "Usuario nao permitido"}) 10 | } 11 | }else{ 12 | // O metodo next() serve para informar que a requisição pode 13 | // seguir o caminho normal 14 | next() 15 | } 16 | }) 17 | 18 | } 19 | 20 | export default autenticacao -------------------------------------------------------------------------------- /src/model/usuarioModel.js: -------------------------------------------------------------------------------- 1 | import usuarioDAO from '../DAO/usuarioDAO.js' 2 | import { 3 | criaUsuario, 4 | validaUsuario, 5 | } from "../services/usuario.js" 6 | 7 | const usuarioModel = { 8 | 9 | pegaUsuarios : async ()=>{ 10 | try { 11 | const dados = await usuarioDAO.pegaTodosUsuarios() 12 | return { 13 | "dados" : dados, 14 | "total" : dados.length, 15 | "status" : 200 16 | } 17 | 18 | } catch (error) { 19 | throw error 20 | } 21 | }, 22 | 23 | pegaUmUsuario : async (email)=>{ 24 | try { 25 | const dados = await usuarioDAO.pegaUmUsuario(email) 26 | if(dados){ 27 | return { 28 | "dados" : dados, 29 | "status" : 200 30 | } 31 | } else{ 32 | return { 33 | "mensagem" : `Usuário de email ${email} não encontrado`, 34 | "status" : 404 35 | } 36 | } 37 | 38 | } catch (error) { 39 | return { 40 | "mensagem" : error.message, 41 | "status" : 400 42 | } 43 | } 44 | 45 | }, 46 | 47 | // metodo para insercao do usuario no banco de dados 48 | insereUsuario : async (usuario)=>{ 49 | try { 50 | // cria a instancia de usuario com os dados recebidos da requisição 51 | const novoUsuario = criaUsuario(usuario) 52 | const mensagem = await usuarioDAO.insereUsuario(novoUsuario) 53 | return { 54 | "mensagem" : mensagem, 55 | "status" : 201 56 | } 57 | } catch (error) { 58 | return { 59 | "mensagem" : error.message, 60 | "status" : 400 61 | } 62 | } 63 | }, 64 | 65 | deletaUsuario : async (email)=>{ 66 | try { 67 | const mensagem = await usuarioDAO.deletaUsuario(email) 68 | return { 69 | "mensagem" : mensagem, 70 | "status" : 200 71 | } 72 | 73 | } catch (error) { 74 | return { 75 | "mensagem" : error.message, 76 | "status" : 400 77 | } 78 | } 79 | }, 80 | 81 | atualizaUsuario : async (email, novosDados)=>{ 82 | try { 83 | validaUsuario(novosDados) 84 | const mensagem = await usuarioDAO.atualizaUsuario(email,novosDados) 85 | return { 86 | "mensagem" : mensagem, 87 | "status" : 200 88 | } 89 | 90 | } catch (error) { 91 | return { 92 | "mensagem" : error.message, 93 | "status" : 400 94 | } 95 | } 96 | }, 97 | 98 | } 99 | 100 | export default usuarioModel -------------------------------------------------------------------------------- /src/server.js: -------------------------------------------------------------------------------- 1 | import app from './app.js' 2 | 3 | // escolhendo a porta em que o servidor será aberto 4 | const port = 3000 5 | 6 | // abrindo o servidor na porta escolhida 7 | app.listen(port, ()=>{ 8 | console.log(`http://localhost:${port}/`) 9 | }) -------------------------------------------------------------------------------- /src/services/logica.js: -------------------------------------------------------------------------------- 1 | export const seisDigitos = (senha)=>{ 2 | return senha?.length >= 6 3 | } 4 | 5 | export const emailValido = (email)=>{ 6 | return email?.includes("@") 7 | } 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/services/usuario.js: -------------------------------------------------------------------------------- 1 | import {emailValido, seisDigitos} from "./logica.js" 2 | 3 | // metodo de validacao de senha 4 | const validaSenha = (senha)=>{ 5 | if(!seisDigitos(senha)){ 6 | throw new Error("A senha precisa ter 6 ou mais caracteres") 7 | } 8 | } 9 | 10 | // metodo de validacao de email 11 | const validaEmail = (email)=>{ 12 | if(emailValido(email)){ 13 | return email 14 | }else{ 15 | throw new Error("Email inválido") 16 | } 17 | } 18 | 19 | // verifica se os dados recebidos são validos, 20 | // caso receba algum erro das funções chamados, o erro é repassado 21 | export const validaUsuario = (dados)=>{ 22 | if(dados.email) 23 | validaEmail(dados.email) 24 | if(dados.senha) 25 | validaSenha(dados.senha) 26 | } 27 | 28 | // cria um usuario atualizado de acordo com os dados recebido 29 | export const mesclaDadosUsuario = (atual, novo)=>{ 30 | validaUsuario(novo) 31 | return { 32 | "nome" : novo.nome || atual.nome, 33 | "email" : novo.email || atual.email, 34 | "senha" : novo.senha || atual.senha, 35 | } 36 | } 37 | 38 | // cria um novo objeto usuario 39 | export const criaUsuario = (dados)=>{ 40 | validaUsuario(dados) 41 | return { 42 | "nome" : dados.nome, 43 | "email" : dados.email, 44 | "senha" : dados.senha 45 | } 46 | } 47 | 48 | 49 | -------------------------------------------------------------------------------- /test/integracao/usuario.test.js: -------------------------------------------------------------------------------- 1 | import app from '../../src/app.js' 2 | import request from 'supertest' 3 | 4 | const mock = { 5 | "nome" : "nome", 6 | "senha" : "abd1234", 7 | "email" : "e@e.com" 8 | } 9 | 10 | describe("Testando rotas usuario", ()=>{ 11 | test("Rota GET", async ()=>{ 12 | const resposta = await request(app).get('/usuario') 13 | expect(resposta.status).toBe(200) 14 | }) 15 | 16 | test("Rota POST", async ()=>{ 17 | // No teste de uma rota post é possivel enviar um body 18 | // com o .send(). 19 | const resposta = await request(app).post('/usuario') 20 | .send(mock) 21 | expect(resposta.status).toBe(201) 22 | }) 23 | 24 | test("Rota POST com um mesmo usuário", async ()=>{ 25 | const resposta = await request(app).post('/usuario') 26 | .send(mock) 27 | expect(resposta.status).toBe(400) 28 | }) 29 | 30 | test("Rota GET um usuario", async ()=>{ 31 | const resposta = await request(app).get(`/usuario/email/${mock.email}`) 32 | expect(resposta.status).toBe(200) 33 | expect(resposta.body.usuario.NOME).toBe(mock.nome) 34 | }) 35 | 36 | test("Rota GET um usuario que não existe", async ()=>{ 37 | const resposta = await request(app).get(`/usuario/email/email@nao.existe`) 38 | expect(resposta.status).toBe(404) 39 | }) 40 | 41 | test("Rota PUT", async ()=>{ 42 | const respostaPut = await request(app).put(`/usuario/email/${mock.email}`) 43 | .send({ 44 | "nome" : "outro nome", 45 | "senha" : "1234abcd" 46 | }) 47 | // Verifica se a atualização retornou 200 48 | expect(respostaPut.status).toBe(200) 49 | 50 | // Faz um get para verificar se os dados foram mesmo alterados 51 | const respostaGet = await request(app).get(`/usuario/email/${mock.email}`) 52 | expect(respostaGet.body.usuario.NOME).toBe("outro nome") 53 | expect(respostaGet.body.usuario.SENHA).toBe("1234abcd") 54 | }) 55 | 56 | test("Rota DELETE", async ()=>{ 57 | const respostaDelete = await request(app).delete(`/usuario/email/${mock.email}`) 58 | expect(respostaDelete.status).toBe(200) 59 | expect(respostaDelete.body.erro).toBeFalsy() 60 | 61 | // Verifica se o usuario foi realmente deletado 62 | const respostaGet = await request(app).get(`/usuario/email/${mock.email}`) 63 | expect(respostaGet.status).toBe(404) 64 | }) 65 | 66 | }) 67 | -------------------------------------------------------------------------------- /test/unitario/logica.test.js: -------------------------------------------------------------------------------- 1 | import { 2 | seisDigitos, 3 | emailValido 4 | } from "../../src/services/logica.js" 5 | 6 | describe("Validacao de senha", ()=>{ 7 | test("Deve aceitar senhas com seis digitos", ()=>{ 8 | expect(seisDigitos("abcdet")).toBe(true) 9 | }) 10 | 11 | test("Deve rejeitar senhas com menos de seis digitos", ()=>{ 12 | expect(seisDigitos("abcde")).toBe(false) 13 | }) 14 | 15 | test("Deve rejeitar numeros", ()=>{ 16 | expect(seisDigitos(1234567)).toBe(false) 17 | }) 18 | 19 | test("Deve rejeitar caso não venha senha", ()=>{ 20 | expect(seisDigitos()).toBe(false) 21 | }) 22 | }) 23 | 24 | describe("Validacao de email", ()=>{ 25 | test("Deve rejeitar emails sem @", ()=>{ 26 | expect(emailValido("email.com")).toBeFalsy() 27 | }) 28 | 29 | test("Deve rejeitar se não receber nenhum email", ()=>{ 30 | expect(emailValido()).toBeFalsy() 31 | }) 32 | 33 | test("Deve aceitar emails com @", ()=>{ 34 | expect(emailValido("email@.com")).toBeTruthy() 35 | }) 36 | }) 37 | -------------------------------------------------------------------------------- /test/unitario/usuario.test.js: -------------------------------------------------------------------------------- 1 | import { 2 | criaUsuario, 3 | validaUsuario 4 | } from "../../src/services/usuario.js" 5 | 6 | describe("Verifica se o usuario é valido", ()=>{ 7 | test("deve rejeitar se a senha for invalida", ()=>{ 8 | expect(()=>validaUsuario({ 9 | "nome" : "nome", 10 | "email": "email@email.com", 11 | "senha" : "abcde" 12 | })).toThrow("A senha precisa ter 6 ou mais caracteres") 13 | }) 14 | 15 | test("deve rejeitar se o email for invalido", ()=>{ 16 | expect(()=>validaUsuario({ 17 | "nome" : "nome", 18 | "email": "email.email.com", 19 | "senha" : "abcdef" 20 | })).toThrow("Email inválido") 21 | }) 22 | 23 | test("Não deve expor erro se o usuario for valido", ()=>{ 24 | expect(()=>validaUsuario({ 25 | "nome" : "nome", 26 | "email": "email@email.com", 27 | "senha" : "abcdef" 28 | })).not.toThrow() 29 | }) 30 | }) 31 | 32 | describe("Criando usuario", ()=>{ 33 | test("Deve criar um usuario valido", ()=>{ 34 | expect(criaUsuario({ 35 | "nome" : "nome", 36 | "email": "email@email.com", 37 | "senha" : "abcdef" 38 | })).toEqual({ 39 | "nome" : "nome", 40 | "email": "email@email.com", 41 | "senha" : "abcdef" 42 | }) 43 | }) 44 | 45 | test("deve rejeitar se for invalido", ()=>{ 46 | expect(()=>criaUsuario({ 47 | "nome" : "nome", 48 | "email": "email.email.com", 49 | "senha" : "abcdef" 50 | })).toThrow() 51 | }) 52 | }) 53 | --------------------------------------------------------------------------------