Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
fae927b
Update TaskController.java
edRibeiro Jan 24, 2024
82ce977
Update TaskController.java
edRibeiro Jan 25, 2024
a3cfb06
Atualizando dependencias de testes
edRibeiro Dec 31, 2025
0235ead
Merge branch 'master' of github.com:edRibeiro/tasks-backend
edRibeiro Dec 31, 2025
70926b5
Testes para validação de datas.
edRibeiro Jan 1, 2026
b0b70d0
Adicinoando testes para cadastro de tarefas.
edRibeiro Jan 1, 2026
efb3d62
Criando teste para falhar.
edRibeiro Jan 2, 2026
47c29f4
Removendo teste falho.
edRibeiro Jan 2, 2026
0920c08
Adicionando pipeline
edRibeiro Jan 6, 2026
9684bfe
Pipeline evol
edRibeiro Jan 6, 2026
eea318e
Pipeline evol with unit tests
edRibeiro Jan 6, 2026
f06c9f3
Pipeline evol with sonar
edRibeiro Jan 6, 2026
f6fa09b
Fix SonarQube plugin name
edRibeiro Jan 6, 2026
8279899
Pipeline evol with Quality Gate
edRibeiro Jan 6, 2026
2124a18
Pipeline evol with Quality Gate and sleep
edRibeiro Jan 6, 2026
29aada7
Pipeline evol with Deploy Backend
edRibeiro Jan 6, 2026
bf565fe
Pipeline evol
edRibeiro Jan 6, 2026
037e7e3
Pipeline evol
edRibeiro Jan 6, 2026
d6e7321
Pipeline evol
edRibeiro Jan 6, 2026
c196394
Fix branch name in stage Deploy Frontend
edRibeiro Jan 6, 2026
dda959d
Pipeline evol with functional test
edRibeiro Jan 6, 2026
9cde4dd
Estrutura de produção
edRibeiro Jan 8, 2026
cd1c8c1
Deploy to Production
edRibeiro Jan 8, 2026
bb5b52f
Fix
edRibeiro Jan 8, 2026
0f4a5a7
Health Check
edRibeiro Jan 8, 2026
673cb9e
Fix Health Check
edRibeiro Jan 8, 2026
9117184
Fix
edRibeiro Jan 8, 2026
8e1b2c6
Post actions
edRibeiro Jan 8, 2026
28544d2
Send emails
edRibeiro Jan 8, 2026
b75e3e3
Send emails
edRibeiro Jan 8, 2026
cc6db92
Salvando artefatos
edRibeiro Jan 8, 2026
2d37f09
Ajuste banco
edRibeiro Jan 8, 2026
009b323
Excluindo diretórios da cobertura do SonarQube
edRibeiro Jan 10, 2026
96f56f1
Afustando testes.
edRibeiro Jan 10, 2026
4693b36
Update Pipeline
edRibeiro Jan 10, 2026
286d299
Fix
edRibeiro Jan 10, 2026
0159492
Remoção de tarefa
edRibeiro Jan 10, 2026
2f5b11d
Fix
edRibeiro Jan 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
FROM tomcat:8.5.50-jdk8-openjdk

ARG WAR_FILE
ARG CONTEXT

COPY ${WAR_FILE} /usr/local/tomcat/webapps/${CONTEXT}.war
98 changes: 98 additions & 0 deletions Jenkinsfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
pipeline {
agent any
stages {
stage('Build Backend') {
steps {
bat 'mvn clean package -DskipTests=true'
}
}
stage('Unit Tests') {
steps {
bat 'mvn clean verify'
}
}
stage('SonarQube Analysis') {
environment {
SONARQUBE_SCANNER_HOME = tool 'SONAR_SCANNER'
}
steps {
withSonarQubeEnv('SONAR_LOCAL') {
bat """
"${SONARQUBE_SCANNER_HOME}/bin/sonar-scanner.bat" ^
-Dsonar.projectKey=DeployBack ^
-Dsonar.host.url=http://localhost:9000 ^
-Dsonar.login=76bd61c4ea9c4138e03a57e9d4be699ee5f2111f ^
-Dsonar.java.binaries=target ^
-Dsonar.coverage.jacoco.xmlReportPaths=target/site/jacoco/jacoco.xml ^
-Dsonar.exclusions=**/.mvn/**,**/target/** ^
-Dsonar.coverage.exclusions=**/.mvn/**,**/src/test/**,**/model/**,**/*Application.java
"""
}
}
}
stage('Quality Gate') {
steps {
sleep(5)
timeout(time: 1, unit: 'MINUTES') {
waitForQualityGate abortPipeline: true
}
}
}
stage('Deploy Backend') {
steps {
deploy adapters: [tomcat8(alternativeDeploymentContext: '', credentialsId: 'TomcatLogin', path: '', url: 'http://127.0.0.1:8001/')], contextPath: 'tasks-backend', war: 'target/tasks-backend.war'
}
}
stage('API Tests') {
steps {
dir('api-test') {
git branch: 'main', credentialsId: 'github_login', url: 'https://github.com/edRibeiro/tasks-api-test.git'
bat 'mvn test'
}
}
}
stage('Deploy Frontend') {
steps {
dir('frontend') {
git branch: 'master', credentialsId: 'github_login', url: 'https://github.com/edRibeiro/tasks-frontend.git'
bat 'mvn clean package'
deploy adapters: [tomcat8(alternativeDeploymentContext: '', credentialsId: 'TomcatLogin', path: '', url: 'http://127.0.0.1:8001/')], contextPath: 'tasks', war: 'target/tasks.war'
}
}
}
stage('Functional Test') {
steps {
dir('functional-test') {
git branch: 'main', credentialsId: 'github_login', url: 'https://github.com/edRibeiro/tasks-functional-tests.git'
bat 'mvn test'
}
}
}
stage('Deploy to Production') {
steps {
bat 'docker compose build'
bat 'docker compose up -d'
}
}
stage('Health Check') {
steps {
sleep(20)
dir('functional-test') {
bat 'mvn verify -Dskip.surefire.tests'
}
}
}
}
post {
always {
junit allowEmptyResults: true, testResults: 'target/surefire-reports/*.xml, api-test/target/surefire-reports/*.xml, functional-test/target/surefire-reports/*.xml, functional-test/target/failsafe-reports/*.xml'
archiveArtifacts artifacts: 'target/tasks-backend.war, frontend/target/tasks.war', onlyIfSuccessful: true
}
unsuccessful {
emailext attachLog: true, body: 'See the attached log below.', subject: 'Build $BUILD_NUMBER has failed', to: '[email protected]'
}
fixed {
emailext attachLog: true, body: 'See the attached log below.', subject: 'Build is fine!!!', to: '[email protected]'
}
}
}
62 changes: 62 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
version: "3"
services:
db:
container_name: pg-prod
image: postgres:9.6
networks:
- prod_net_back
environment:
- POSTGRES_PASSWORD=passwd
- POSTGRES_DB=tasks
volumes:
- prod_postgresql:/var/lib/postgresql
- prod_postgresql_data:/var/lib/postgresql/data

backend:
container_name: backend-prod
image: back_prod:build_${BUILD_NUMBER}
build:
context: .
args:
- WAR_FILE=target/tasks-backend.war
- CONTEXT=tasks-backend
networks:
- prod_net_back
- prod_net_front
#ports:
# - 9998:8080
environment:
- DATABASE_HOST=db
- DATABASE_PORT=5432
- DATABASE_USER=postgres
- DATABASE_PASSWD=passwd
- DATABASE_UPDATE=none
depends_on:
- db

frontend:
container_name: frontend-prod
image: front_prod:build_${BUILD_NUMBER}
build:
context: .
args:
- WAR_FILE=frontend/target/tasks.war
- CONTEXT=tasks
networks:
- prod_net_front
ports:
- 9999:8080
environment:
- BACKEND_HOST=backend
- BACKEND_PORT=8080
- APP_VERSION=build_${BUILD_NUMBER}
#depends_on:
# - backend

networks:
prod_net_front:
prod_net_back:

volumes:
prod_postgresql:
prod_postgresql_data:
28 changes: 14 additions & 14 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,9 @@
<name>tasks-backend</name>
<packaging>war</packaging>
<description>A very simple task management tool</description>

<properties>
<java.version>1.8</java.version>
<sonar.core.codeCoveragePlugin>jacoco</sonar.core.codeCoveragePlugin>
<sonar.dynamicAnalysis>reuseReports</sonar.dynamicAnalysis>
<sonar.jacoco.reportPath>${project.basedir}/target/jacoco.exec</sonar.jacoco.reportPath>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
Expand All @@ -37,25 +32,26 @@
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<!-- JUnit 4 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<!-- Mockito moderno (compatível com Java 8) -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.10.19</version>
<artifactId>mockito-core</artifactId>
<version>2.28.2</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<finalName>tasks-backend</finalName>
<plugins>
Expand All @@ -66,19 +62,23 @@
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.3</version>
<version>0.8.11</version>
<executions>
<execution>
<id>prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>verify</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
<configuration>
<destFile>${sonar.jacoco.reportPath}</destFile>
</configuration>
</plugin>
</plugins>
</build>

</project>
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

import br.ce.wcaquino.taskbackend.model.Task;
Expand All @@ -31,15 +34,21 @@ public List<Task> findAll() {
@PostMapping
public ResponseEntity<Task> save(@RequestBody Task todo) throws ValidationException {
if(todo.getTask() == null || todo.getTask() == "") {
throw new ValidationException("Fill the task description");
throw new ValidationException("Fill the task description.");
}
if(todo.getDueDate() == null) {
throw new ValidationException("Fill the due date");
throw new ValidationException("Fill the due date.");
}
if(!DateUtils.isEqualOrFutureDate(todo.getDueDate())) {
throw new ValidationException("Due date must not be in past");
}
Task saved = todoRepo.save(todo);
return new ResponseEntity<Task>(saved, HttpStatus.CREATED);
}

@DeleteMapping(value = "/{id}")
@ResponseStatus(code = HttpStatus.NO_CONTENT)
public void delete(@PathVariable Long id) {
todoRepo.deleteById(id);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package br.ce.wcaquino.taskbackend.controller;

import java.time.LocalDate;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;

import br.ce.wcaquino.taskbackend.model.Task;
import br.ce.wcaquino.taskbackend.repo.TaskRepo;
import br.ce.wcaquino.taskbackend.utils.ValidationException;


public class TaskControllerTest {

@InjectMocks
private TaskController controller;

@Mock
private TaskRepo taskRepo;

@Before
public void setup() {
MockitoAnnotations.initMocks(this);
}

@Test
public void naoDeveSalvarTarefaSemDescricao(){
Task todo = new Task();
todo.setDueDate(LocalDate.now());
try {
controller.save(todo);
Assert.fail("Não deveria cheager nesse ponto!");
} catch (ValidationException e) {
Assert.assertEquals("Fill the task description.", e.getMessage());
}
}

@Test
public void naoDeveSalvarTarefaSemData(){
Task todo = new Task();
todo.setTask("Estudar testes");
try {
controller.save(todo);
Assert.fail("Não deveria cheager nesse ponto!");
} catch (ValidationException e) {
Assert.assertEquals("Fill the due date.", e.getMessage());
}
}

@Test
public void naoDeveSalvarTarefaComDataPassada(){
Task todo = new Task();
todo.setTask("Tarefa atrasada");
todo.setDueDate(LocalDate.now().minusDays(1));
try {
controller.save(todo);
Assert.fail("Não deveria cheager nesse ponto!");
} catch (ValidationException e) {
Assert.assertEquals("Due date must not be in past", e.getMessage());
}
}

@Test
public void deveSalvarTarefaComSucesso() throws ValidationException{
Task todo = new Task();
todo.setTask("Estudar Mockito");
todo.setDueDate(LocalDate.now().plusDays(1));
controller.save(todo);
Mockito.verify(taskRepo).save(todo);
}
}

27 changes: 27 additions & 0 deletions src/test/java/br/ce/wcaquino/taskbackend/utils/DateUtilsTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package br.ce.wcaquino.taskbackend.utils;

import java.time.LocalDate;

import org.junit.Assert;
import org.junit.Test;

public class DateUtilsTest {

@Test
public void deveRetonarTrueParaDatasFuturas() {
LocalDate date = LocalDate.of(2030, 01, 01);
Assert.assertTrue(DateUtils.isEqualOrFutureDate(date));
}

@Test
public void deveRetonarFalseParaDatasPassadas() {
LocalDate date = LocalDate.of(2010, 01, 01);
Assert.assertFalse(DateUtils.isEqualOrFutureDate(date));
}

@Test
public void deveRetonarTrueParaDataAtual() {
LocalDate date = LocalDate.now();
Assert.assertTrue(DateUtils.isEqualOrFutureDate(date));
}
}