Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
The diff you're trying to view is too large. We only load the first 3000 changed files.
37 changes: 23 additions & 14 deletions .github/workflows/experimento-ci-cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,29 @@ jobs:

# Primeiro job: Executar testes unitários
unit-test:
runs-on: ubuntu-latest # Define o sistema operacional usado no runner (Ubuntu na versão mais recente)
steps:
- name: Checkout do código
uses: actions/checkout@v4 # Faz o download do repositório no runner

- name: Instalar dependências do backend
run: |
cd backend # Acessa a pasta do backend
npm install # Instala as dependências do Node.js

- name: Executar testes unitários com Jest
run: |
cd backend # Acessa novamente a pasta do backend
npm test -- --coverage # Executa os testes e gera relatório de cobertura
runs-on: ubuntu-latest
steps:
- name: Checkout do código
uses: actions/checkout@v4


- name: Cache de dependências do backend
uses: actions/cache@v4
with:
path: backend/node_modules
key: npm-${{ hashFiles('backend/package-lock.json') }}
restore-keys: |
npm-

- name: Instalar dependências do backend
run: |
cd backend
npm install

- name: Executar testes unitários com Jest
run: |
cd backend
npm test -- --coverage

# Segundo job: Build (construção) das imagens Docker
build:
Expand Down
51 changes: 49 additions & 2 deletions backend/src/repositories/animal.dto.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,51 @@
import { Animal } from '@prisma/client'
import { Animal as PrismaAnimal } from '@prisma/client'

export type AnimalStatus = 'available' | 'adopted' | 'pending'

export class Animal {
constructor(
public id: string,
public name: string,
public type: string,
public gender: 'Macho' | 'Fêmea',
public userId: string,
public status: AnimalStatus,
public race?: string,
public description?: string,
public createdAt?: Date,
public updatedAt?: Date,
) {}

isAvailable(): boolean {
return this.status === 'available'
}

isAdopted(): boolean {
return this.status === 'adopted'
}

shortDescription(maxLength = 50): string {
if (!this.description) return ''
return this.description.length > maxLength
? this.description.slice(0, maxLength) + '...'
: this.description
}

static fromPrisma(animal: PrismaAnimal): Animal {
return new Animal(
animal.id,
animal.name,
animal.type,
animal.gender as 'Macho' | 'Fêmea',
animal.userId,
animal.status as AnimalStatus,
animal.race,
animal.description,
animal.createdAt,
animal.updatedAt,
)
}
}

export namespace CreateAnimalRepositoryDTO {
export type Params = {
Expand All @@ -21,4 +68,4 @@ export namespace UpdateAnimalStatusRepositoryDTO {
}

export type Result = Animal | null
}
}
10 changes: 10 additions & 0 deletions cypress.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const { defineConfig } = require('cypress')

module.exports = defineConfig({
e2e: {
experimentalStudio: true, // <--- ativa o Cypress Studio
setupNodeEvents(on, config) {
// eventos de teste aqui, se precisar
}
}
})
13 changes: 13 additions & 0 deletions cypress/e2e/adotar.cy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/* ==== Test Created with Cypress Studio ==== */
it('adotar', function() {
/* ==== Generated with Cypress Studio ==== */
cy.visit('localhost:3000');
cy.get('.sc-29a00fbd-5 > :nth-child(1) > input').clear('ka');
cy.get('.sc-29a00fbd-5 > :nth-child(1) > input').type('kaiquerennan@gmail.com');
cy.get('.sc-b3c6402b-0 > input').clear('1');
cy.get('.sc-b3c6402b-0 > input').type('12345678');
cy.get('.sc-ea747762-0').click();
cy.get('.sc-26506e6-5 > .sc-c7cdb42d-0 > [href="/area_logada/meus_animais"] > .sc-c7cdb42d-1 > span').click();
cy.get(':nth-child(1) > .sc-66a75d78-2 > .sc-66a75d78-4 > .sc-ea747762-0').click();
/* ==== End Cypress Studio ==== */
});
42 changes: 42 additions & 0 deletions cypress/e2e/disponibilizar.cy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/* ==== Test Created with Cypress Studio ==== */
it('disponibilizar', function() {
/* ==== Generated with Cypress Studio ==== */
cy.visit('localhost:3000');

cy.get('.sc-29a00fbd-5 > :nth-child(1) > input')
.clear()
.type('kaiquerennan@gmail.com');

cy.get('.sc-b3c6402b-0 > input')
.clear()
.type('12345678{enter}');

cy.get('.sc-ea747762-0').click();

cy.get('.sc-26506e6-5 > .sc-c7cdb42d-0 > [href="/area_logada/disponibilizar_animal"] > .sc-c7cdb42d-1').click();

cy.get('.sc-b77a2f6d-7 > label > input')
.clear()
.type('rony');

// Selecionar o tipo (exemplo: "Cachorro")
cy.contains('button', 'Selecione um tipo').click();
cy.get('[role="option"]').contains('Cachorro').click({ force: true });

// Selecionar o gênero (exemplo: "Macho")
cy.contains('button', 'Selecione um gênero').click();
cy.get('[role="option"]').contains('Macho').click({ force: true });

cy.get('.sc-b77a2f6d-10 > label > input')
.clear()
.type('BAGRE');

cy.get('.sc-b77a2f6d-16 > div > svg').click();

// Upload correto do arquivo rony.png
cy.get('#animalPictures')
.selectFile('cypress/fixtures/rony.jpg', { force: true });

cy.get('.sc-ea747762-0').click();
/* ==== End Cypress Studio ==== */
});
Empty file added cypress/e2e/login.cy.js
Empty file.
Binary file added cypress/e2e/rony.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions cypress/fixtures/example.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "Using fixtures to represent data",
"email": "hello@cypress.io",
"body": "Fixtures are a great way to mock data for responses to routes"
}
Binary file added cypress/fixtures/rony.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
25 changes: 25 additions & 0 deletions cypress/support/commands.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// ***********************************************
// This example commands.js shows you how to
// create various custom commands and overwrite
// existing commands.
//
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************
//
//
// -- This is a parent command --
// Cypress.Commands.add('login', (email, password) => { ... })
//
//
// -- This is a child command --
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
//
//
// -- This is a dual command --
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
//
//
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
17 changes: 17 additions & 0 deletions cypress/support/e2e.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// ***********************************************************
// This example support/e2e.js is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************

// Import commands.js using ES2015 syntax:
import './commands'
27 changes: 11 additions & 16 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,21 +1,16 @@
services:
adote-facil-postgres:
image: postgres:14-alpine
container_name: 'adote-facil-postgres'
env_file:
- ./backend/.env
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_CONTAINER_PORT: ${POSTGRES_CONTAINER_PORT}
ports:
- ${POSTGRES_CONTAINER_PORT}:5432
volumes:
- db_data:/var/lib/postgresql/data
networks:
- adote-facil-network
healthcheck:
image: postgres:14-alpine
container_name: 'adote-facil-postgres'
env_file:
./backend/.env
ports:
${POSTGRES_CONTAINER_PORT}:5432
volumes:
db_data:/var/lib/postgresql/data
networks:
adote-facil-network
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 3s
timeout: 3s
Expand Down
17 changes: 17 additions & 0 deletions documentacao/arquitetura.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# 1. Arquitetura adotada

O sistema adota uma arquitetura **Cliente-Servidor** com as seguintes características:

## 1.1 Backend Monolítico com Estrutura em Camadas

- O backend é construído com **Node.js** e **Express**. Utiliza o ORM **Prisma** para acesso ao banco de dados **PostgreSQL**. A autenticação é feita via **JWT**, e o upload de imagens utiliza o **Multer**

- O backend é organizado em camadas para separar responsabilidades, como a camada de Roteamento, Middlewares (para autenticação) e Acesso a Dados (abstraído pelo Prisma)

## 1.2 Frontend Desacoplado

- O frontend está em `frontend` e é desenvolvido com **Next.js** (React). Ele consome as APIs do backend via chamadas HTTP usando **Axios**. Para validação de formulários, utiliza **Zod**. O frontend é desacoplado do backend, rodando em seu próprio container Docker e se comunicando apenas por API REST. As páginas e componentes estão organizados em `frontend/src/app`.
Essa separação de contêineres entre o frontend e o backend é orquestrada pelo `docker-compose.yml`

# 2.
![diagrama](image.png)
13 changes: 13 additions & 0 deletions documentacao/devops.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
## Há pipeline CI/CD?
Sim, há um pipeline CI/CD definido no arquivo `.github\workflows\experimento-ci-cd.yml`, que é executado via GitHub Actions em pull requests para a branch `main`.

## Há testes automatizados no pipeline?
Sim, o pipeline inclui um job (`unit-test`) que executa os testes unitários do backend.

## Há uso de containers?
Sim, existem `Dockerfile`s para o backend e frontend, e um arquivo `docker-compose.yml` que orquestra todos os serviços (aplicações e banco de dados), garantindo consistência entre os ambientes.

## Sugestão de melhoria
- Remover variáveis env_file duplicadas no docker-compose. Já é definido um env_file: ./backend/.env, então não precisa repetir manualmente as mesmas variáveis em environment, pois isso gera duplicação e risco de inconsistência.

- Adicionar cache de dependências no job unit-test para reduzir o tempo de execução do pipeline.
Binary file added documentacao/image.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions documentacao/padroes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# 1. Princípios
- Single Responsibility Principle (Princípio da Responsabilidade Única - SRP): A classe Authenticator tem a responsabilidade de gerenciar a criação e validação de tokens JWT. Ela não se preocupa com a lógica de negócio do usuário ou com o armazenamento de dados, delegando essas tarefas a outras partes do sistema. A separação de responsabilidades também é visível na estrutura de pastas (controllers, services, repositories).

- Dependency Inversion Principle (Princípio da Inversão de Dependência - DIP): Os serviços (módulos de alto nível, em services/) dependem de abstrações, como as classes de repositório (em repositories/), em vez de implementações concretas de acesso a dados. Da mesma forma, os controladores (em controllers/) dependem de serviços. Isso desacopla os componentes, tornando o sistema mais flexível e fácil de testar, pois as dependências concretas podem ser substituídas (por exemplo, por mocks em testes)

# 2. Padrões de Projeto (Design Patterns)
- Singleton: A classe Authenticator no arquivo authenticator.ts é exportada como uma única instância (authenticatorInstance). Isso garante que apenas um objeto de autenticação seja utilizado em toda a aplicação, centralizando o controle e o estado (como o segredo do JWT) em um único ponto.

- Repository: A estrutura do projeto inclui uma pasta repositories (ex: repositories/user.ts, repositories/animal.ts). Este padrão abstrai a camada de acesso a dados, separando a lógica de negócio das interações diretas com o banco de dados. As classes de serviço (em services/) dependem desses repositórios para persistir e recuperar dados, sem conhecer os detalhes da implementação da persistência.
Loading