diff --git a/.github/workflows/pipeline-sun.yml b/.github/workflows/pipeline-sun.yml index f01d775b..d2682bad 100644 --- a/.github/workflows/pipeline-sun.yml +++ b/.github/workflows/pipeline-sun.yml @@ -10,7 +10,7 @@ on: - main - ci-cd paths: - - ".github/workflows/pipeline-sun.yaml" + - ".github/workflows/pipeline-sun.yml" jobs: compile: diff --git a/src/main/java/com/fjb/sunrise/config/ApplicationConfig.java b/src/main/java/com/fjb/sunrise/config/ApplicationConfig.java new file mode 100644 index 00000000..abc40af0 --- /dev/null +++ b/src/main/java/com/fjb/sunrise/config/ApplicationConfig.java @@ -0,0 +1,64 @@ +package com.fjb.sunrise.config; + +import com.fjb.sunrise.enums.ERole; +import com.fjb.sunrise.models.User; +import com.fjb.sunrise.repositories.UserRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.ApplicationRunner; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; + +@Configuration +@RequiredArgsConstructor +@Slf4j +public class ApplicationConfig { + + private final UserRepository userRepository; + + @Value("${application.admin.default.username}") + private String adminUsername; + + @Value("${application.admin.default.password}") + private String adminPassword; + + @Bean + public AuthenticationManager authenticationManager (AuthenticationConfiguration configuration) throws Exception { + return configuration.getAuthenticationManager(); + } + + @Bean + public PasswordEncoder passwordEncoder () { + return new BCryptPasswordEncoder(); + } + + @Bean + @ConditionalOnProperty( + prefix = "spring", + value = "datasource.driver-class-name", + havingValue = "org.postgresql.Driver") + ApplicationRunner applicationRunner () { + log.info("Initializing application....."); + return args -> { + if (!userRepository.existsByUsername(adminUsername)) { + + User user = User.builder() + .username(adminUsername) + .password(passwordEncoder().encode(adminPassword)) + .role(ERole.ADMIN) + .build(); + userRepository.save(user); + log.warn("admin user has been created: username = {}, password = {} ", adminUsername, adminPassword); + } else { + log.warn("admin user: username = {}, password = {} ", adminUsername, adminPassword); + } + log.info("Application initialization completed ....."); + }; + } +} diff --git a/src/main/java/com/fjb/sunrise/config/WebServletConfig.java b/src/main/java/com/fjb/sunrise/config/WebServletConfig.java new file mode 100644 index 00000000..dc56a188 --- /dev/null +++ b/src/main/java/com/fjb/sunrise/config/WebServletConfig.java @@ -0,0 +1,57 @@ +package com.fjb.sunrise.config; + +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.ViewResolver; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import org.thymeleaf.spring6.SpringTemplateEngine; +import org.thymeleaf.spring6.templateresolver.SpringResourceTemplateResolver; +import org.thymeleaf.spring6.view.ThymeleafViewResolver; + +@Configuration +@EnableWebMvc +@ComponentScan(basePackages = {"com.fjb.sunrise"}) +public class WebServletConfig implements WebMvcConfigurer { + @Bean + public SpringResourceTemplateResolver templateResolver() { + SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver(); + resolver.setPrefix("classpath:/templates/"); + resolver.setSuffix(".html"); + resolver.setTemplateMode("TemplateMode.HTML"); + resolver.setCacheable(false); + return resolver; + } + + @Bean + public SpringTemplateEngine templateEngine(@Qualifier("templateResolver") SpringResourceTemplateResolver templateResolver) { + SpringTemplateEngine engine = new SpringTemplateEngine(); + engine.setTemplateResolver(templateResolver()); + engine.setEnableSpringELCompiler(true); + return engine; + } + + @Bean + public ViewResolver viewResolver(@Qualifier("templateEngine") SpringTemplateEngine templateEngine) { + ThymeleafViewResolver resolver = new ThymeleafViewResolver(); + resolver.setTemplateEngine(templateEngine); + return resolver; + } + + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + registry + .addResourceHandler("/webjars/**", "/img/**", "/css/**", "/js/**") + .addResourceLocations("classpath:/META-INF/resources/webjars/", + "classpath:/static/img/", "classpath:/static/css/", "classpath:/static/js/"); + } + + @Override + public void addViewControllers(ViewControllerRegistry registry) { + registry.addViewController("/health").setViewName("health"); + } +} diff --git a/src/main/java/com/fjb/sunrise/dtos/responses/ErrorVm.java b/src/main/java/com/fjb/sunrise/dtos/responses/ErrorVm.java new file mode 100644 index 00000000..f5bf85c0 --- /dev/null +++ b/src/main/java/com/fjb/sunrise/dtos/responses/ErrorVm.java @@ -0,0 +1,11 @@ +package com.fjb.sunrise.dtos.responses; + +import java.util.ArrayList; +import java.util.List; + +public record ErrorVm(String statusCode, String title, String detail, List fieldErrors) { + + public ErrorVm(String statusCode, String title, String detail) { + this(statusCode, title, detail, new ArrayList()); + } +} diff --git a/src/main/java/com/fjb/sunrise/enums/ERole.java b/src/main/java/com/fjb/sunrise/enums/ERole.java new file mode 100644 index 00000000..242d997b --- /dev/null +++ b/src/main/java/com/fjb/sunrise/enums/ERole.java @@ -0,0 +1,6 @@ +package com.fjb.sunrise.enums; + +public enum ERole { + ADMIN, + USER +} diff --git a/src/main/java/com/fjb/sunrise/enums/ETrans.java b/src/main/java/com/fjb/sunrise/enums/ETrans.java new file mode 100644 index 00000000..990b9696 --- /dev/null +++ b/src/main/java/com/fjb/sunrise/enums/ETrans.java @@ -0,0 +1,6 @@ +package com.fjb.sunrise.enums; + +public enum ETrans { + IN, + OUT +} diff --git a/src/main/java/com/fjb/sunrise/exceptions/ApiExceptionHandler.java b/src/main/java/com/fjb/sunrise/exceptions/ApiExceptionHandler.java new file mode 100644 index 00000000..c80f024a --- /dev/null +++ b/src/main/java/com/fjb/sunrise/exceptions/ApiExceptionHandler.java @@ -0,0 +1,93 @@ +package com.fjb.sunrise.exceptions; + +import com.fjb.sunrise.dtos.responses.ErrorVm; +import jakarta.validation.ConstraintViolation; +import jakarta.validation.ConstraintViolationException; +import java.util.ArrayList; +import java.util.List; +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.NestedExceptionUtils; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.context.request.ServletWebRequest; +import org.springframework.web.context.request.WebRequest; + +@ControllerAdvice +@Slf4j +public class ApiExceptionHandler { + + private static final String ERROR_LOG_FORMAT = "Error: URI: {}, ErrorCode: {}, Message: {}"; + + @ExceptionHandler(NotFoundException.class) + public ErrorVm handleNotFoundException(NotFoundException ex, WebRequest request) { + String message = ex.getMessage(); + ErrorVm errorVm = new ErrorVm(HttpStatus.NOT_FOUND.toString(), + HttpStatus.NOT_FOUND.getReasonPhrase(), message); + log.warn(ERROR_LOG_FORMAT, this.getServletPath(request), 404, message); + log.debug(ex.toString()); + return errorVm; + } + + @ExceptionHandler(BadRequestException.class) + public ErrorVm handleBadRequestException(BadRequestException ex, + WebRequest request) { + String message = ex.getMessage(); + return new ErrorVm(HttpStatus.BAD_REQUEST.toString(), + HttpStatus.BAD_REQUEST.getReasonPhrase(), message); + } + + @ExceptionHandler(MethodArgumentNotValidException.class) + protected ErrorVm handleMethodArgumentNotValid(MethodArgumentNotValidException ex) { + List errors = ex.getBindingResult() + .getFieldErrors() + .stream() + .map(error -> error.getField() + " " + error.getDefaultMessage()) + .toList(); + + return new ErrorVm(HttpStatus.BAD_REQUEST.toString(), + HttpStatus.BAD_REQUEST.getReasonPhrase(), "Request information is not valid", errors); + } + + @ExceptionHandler({ConstraintViolationException.class}) + public ErrorVm handleConstraintViolation(ConstraintViolationException ex) { + List errors = new ArrayList<>(); + for (ConstraintViolation violation : ex.getConstraintViolations()) { + errors.add(violation.getRootBeanClass().getName() + " " + + violation.getPropertyPath() + ": " + violation.getMessage()); + } + + return new ErrorVm(HttpStatus.BAD_REQUEST.toString(), + HttpStatus.BAD_REQUEST.getReasonPhrase(), "Request information is not valid", errors); + } + + @ExceptionHandler(DataIntegrityViolationException.class) + public ErrorVm handleDataIntegrityViolationException(DataIntegrityViolationException e) { + String message = NestedExceptionUtils.getMostSpecificCause(e).getMessage(); + return new ErrorVm(HttpStatus.BAD_REQUEST.toString(), + HttpStatus.BAD_REQUEST.getReasonPhrase(), message); + } + + @ExceptionHandler(DuplicatedException.class) + protected ErrorVm handleDuplicated(DuplicatedException e) { + return new ErrorVm(HttpStatus.BAD_REQUEST.toString(), + HttpStatus.BAD_REQUEST.getReasonPhrase(), e.getMessage()); + } + + @ExceptionHandler(Exception.class) + protected ErrorVm handleOtherException(Exception ex, WebRequest request) { + String message = ex.getMessage(); + ErrorVm errorVm = new ErrorVm(HttpStatus.INTERNAL_SERVER_ERROR.toString(), + HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase(), message); + log.warn(ERROR_LOG_FORMAT, this.getServletPath(request), 500, message); + log.debug(ex.toString()); + return errorVm; + } + + private String getServletPath(WebRequest webRequest) { + ServletWebRequest servletRequest = (ServletWebRequest) webRequest; + return servletRequest.getRequest().getServletPath(); + } +} diff --git a/src/main/java/com/fjb/sunrise/exceptions/BadRequestException.java b/src/main/java/com/fjb/sunrise/exceptions/BadRequestException.java new file mode 100644 index 00000000..642b34aa --- /dev/null +++ b/src/main/java/com/fjb/sunrise/exceptions/BadRequestException.java @@ -0,0 +1,21 @@ +package com.fjb.sunrise.exceptions; + +import com.fjb.sunrise.utils.MessagesUtils; + +public class BadRequestException extends RuntimeException { + + private String message; + + public BadRequestException(String errorCode, Object... var2) { + this.message = MessagesUtils.getMessage(errorCode, var2); + } + + @Override + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } +} diff --git a/src/main/java/com/fjb/sunrise/exceptions/DuplicatedException.java b/src/main/java/com/fjb/sunrise/exceptions/DuplicatedException.java new file mode 100644 index 00000000..8d8a6c80 --- /dev/null +++ b/src/main/java/com/fjb/sunrise/exceptions/DuplicatedException.java @@ -0,0 +1,21 @@ +package com.fjb.sunrise.exceptions; + +import com.fjb.sunrise.utils.MessagesUtils; + +public class DuplicatedException extends RuntimeException { + + private String message; + + public DuplicatedException(String errorCode, Object... var2) { + this.message = MessagesUtils.getMessage(errorCode, var2); + } + + @Override + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } +} diff --git a/src/main/java/com/fjb/sunrise/exceptions/NotFoundException.java b/src/main/java/com/fjb/sunrise/exceptions/NotFoundException.java new file mode 100644 index 00000000..e67956a4 --- /dev/null +++ b/src/main/java/com/fjb/sunrise/exceptions/NotFoundException.java @@ -0,0 +1,21 @@ +package com.fjb.sunrise.exceptions; + +import com.fjb.sunrise.utils.MessagesUtils; + +public class NotFoundException extends RuntimeException { + + private String message; + + public NotFoundException(String errorCode, Object... var2) { + this.message = MessagesUtils.getMessage(errorCode, var2); + } + + @Override + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } +} diff --git a/src/main/java/com/fjb/sunrise/mappers/UserMapper.java b/src/main/java/com/fjb/sunrise/mappers/UserMapper.java new file mode 100644 index 00000000..7cb544d6 --- /dev/null +++ b/src/main/java/com/fjb/sunrise/mappers/UserMapper.java @@ -0,0 +1,7 @@ +package com.fjb.sunrise.mappers; + +import org.mapstruct.Mapper; + +@Mapper(componentModel = "spring") +public interface UserMapper { +} diff --git a/src/main/java/com/fjb/sunrise/models/AuditEntity.java b/src/main/java/com/fjb/sunrise/models/AuditEntity.java new file mode 100644 index 00000000..ec36b64f --- /dev/null +++ b/src/main/java/com/fjb/sunrise/models/AuditEntity.java @@ -0,0 +1,35 @@ +package com.fjb.sunrise.models; + +import jakarta.persistence.Column; +import jakarta.persistence.EntityListeners; +import jakarta.persistence.MappedSuperclass; +import java.time.LocalDateTime; +import lombok.Getter; +import lombok.Setter; +import org.springframework.data.annotation.CreatedBy; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedBy; +import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +@Getter +@Setter +@MappedSuperclass +@EntityListeners(AuditingEntityListener.class) +public class AuditEntity { + @CreatedBy + @Column(name = "created_by") + protected U createdBy; + + @CreatedDate + @Column(name = "created_date") + protected LocalDateTime createdDate; + + @LastModifiedBy + @Column(name = "last_modified_by") + protected U lastModifiedBy; + + @LastModifiedDate + @Column(name = "last_modified_date") + protected LocalDateTime lastModifiedDate; +} diff --git a/src/main/java/com/fjb/sunrise/models/Category.java b/src/main/java/com/fjb/sunrise/models/Category.java new file mode 100644 index 00000000..c6c09145 --- /dev/null +++ b/src/main/java/com/fjb/sunrise/models/Category.java @@ -0,0 +1,28 @@ +package com.fjb.sunrise.models; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Entity +@Getter +@Setter +@Builder +@Table(name = "categories") +@AllArgsConstructor +@NoArgsConstructor +public class Category extends AuditEntity{ + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String name; +} diff --git a/src/main/java/com/fjb/sunrise/models/Transaction.java b/src/main/java/com/fjb/sunrise/models/Transaction.java new file mode 100644 index 00000000..c9d0ff6c --- /dev/null +++ b/src/main/java/com/fjb/sunrise/models/Transaction.java @@ -0,0 +1,28 @@ +package com.fjb.sunrise.models; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Entity +@Getter +@Setter +@Builder +@Table(name = "transactions") +@AllArgsConstructor +@NoArgsConstructor +public class Transaction extends AuditEntity{ + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String transactionType; +} diff --git a/src/main/java/com/fjb/sunrise/models/User.java b/src/main/java/com/fjb/sunrise/models/User.java new file mode 100644 index 00000000..e6174131 --- /dev/null +++ b/src/main/java/com/fjb/sunrise/models/User.java @@ -0,0 +1,32 @@ +package com.fjb.sunrise.models; + +import com.fjb.sunrise.enums.ERole; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Entity +@Getter +@Setter +@Builder +@Table(name = "users") +@AllArgsConstructor +@NoArgsConstructor +public class User extends AuditEntity{ + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String username; + private String password; + + private ERole role; +} diff --git a/src/main/java/com/fjb/sunrise/repositories/UserRepository.java b/src/main/java/com/fjb/sunrise/repositories/UserRepository.java new file mode 100644 index 00000000..6eca60a7 --- /dev/null +++ b/src/main/java/com/fjb/sunrise/repositories/UserRepository.java @@ -0,0 +1,8 @@ +package com.fjb.sunrise.repositories; + +import com.fjb.sunrise.models.User; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface UserRepository extends JpaRepository { + boolean existsByUsername(String username); +} diff --git a/src/main/java/com/fjb/sunrise/services/UserService.java b/src/main/java/com/fjb/sunrise/services/UserService.java new file mode 100644 index 00000000..8e1c5bcb --- /dev/null +++ b/src/main/java/com/fjb/sunrise/services/UserService.java @@ -0,0 +1,4 @@ +package com.fjb.sunrise.services; + +public interface UserService { +} diff --git a/src/main/java/com/fjb/sunrise/services/impl/UserServiceImpl.java b/src/main/java/com/fjb/sunrise/services/impl/UserServiceImpl.java new file mode 100644 index 00000000..a9a5bb09 --- /dev/null +++ b/src/main/java/com/fjb/sunrise/services/impl/UserServiceImpl.java @@ -0,0 +1,8 @@ +package com.fjb.sunrise.services.impl; + +import com.fjb.sunrise.services.UserService; +import org.springframework.stereotype.Service; + +@Service +public class UserServiceImpl implements UserService { +} diff --git a/src/main/java/com/fjb/sunrise/utils/Constants.java b/src/main/java/com/fjb/sunrise/utils/Constants.java new file mode 100644 index 00000000..6033c25a --- /dev/null +++ b/src/main/java/com/fjb/sunrise/utils/Constants.java @@ -0,0 +1,34 @@ +package com.fjb.sunrise.utils; + +public class Constants { + + public final class ErrorCode { + + public static final String USER_NOT_FOUND = "USER_NOT_FOUND"; + public static final String USER_ALREADY_EXISTED = "USER_ALREADY_EXISTED"; + public static final String CATEGORY_NOT_FOUND = "CATEGORY_NOT_FOUND"; + public static final String CATEGORY_ALREADY_EXISTED = "CATEGORY_ALREADY_EXISTED"; + } + + public final class PageableConstant { + + public static final String DEFAULT_PAGE_SIZE = "10"; + public static final String DEFAULT_PAGE_NUMBER = "0"; + } + + public final class ApiConstant { + + public static final String HEALTH_URL = "/health"; + + public static final String CODE_200 = "200"; + public static final String OK = "Ok"; + public static final String CODE_404 = "404"; + public static final String NOT_FOUND = "Not found"; + public static final String CODE_201 = "201"; + public static final String CREATED = "Created"; + public static final String CODE_400 = "400"; + public static final String BAD_REQUEST = "Bad request"; + public static final String CODE_204 = "204"; + public static final String NO_CONTENT = "No content"; + } +} diff --git a/src/main/java/com/fjb/sunrise/utils/MessagesUtils.java b/src/main/java/com/fjb/sunrise/utils/MessagesUtils.java new file mode 100644 index 00000000..d8c35e8c --- /dev/null +++ b/src/main/java/com/fjb/sunrise/utils/MessagesUtils.java @@ -0,0 +1,25 @@ +package com.fjb.sunrise.utils; + +import java.util.Locale; +import java.util.MissingResourceException; +import java.util.ResourceBundle; +import org.slf4j.helpers.FormattingTuple; +import org.slf4j.helpers.MessageFormatter; + +public class MessagesUtils { + + static ResourceBundle messageBundle = ResourceBundle.getBundle("messages.messages", + Locale.getDefault()); + + public static String getMessage(String errorCode, Object... var2) { + String message; + try { + message = messageBundle.getString(errorCode); + } catch (MissingResourceException ex) { + // case message_code is not defined. + message = errorCode; + } + FormattingTuple formattingTuple = MessageFormatter.arrayFormat(message, var2); + return formattingTuple.getMessage(); + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index bc65d400..4774dd60 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -17,4 +17,14 @@ spring: format_sql: true database: postgresql database-platform: org.hibernate.dialect.PostgreSQLDialect - open-in-view: false \ No newline at end of file + open-in-view: false +server: + port: 8086 + servlet: + context-path: /sun + +application: + admin: + default: + username: testuser + password: 123456 \ No newline at end of file diff --git a/src/main/resources/messages/messages.properties b/src/main/resources/messages/messages.properties new file mode 100644 index 00000000..f861108f --- /dev/null +++ b/src/main/resources/messages/messages.properties @@ -0,0 +1,4 @@ +USER_NOT_FOUND = The user {} is not found +USER_ALREADY_EXISTED = The user {} is already existed +CATEGORY_NOT_FOUND = The category {} is not found +CATEGORY_ALREADY_EXISTED = The category {} is already existed \ No newline at end of file diff --git a/src/test/java/com/fjb/sunrise/controllers/HealthControllerTest.java b/src/test/java/com/fjb/sunrise/controllers/HealthControllerTest.java new file mode 100644 index 00000000..066528fc --- /dev/null +++ b/src/test/java/com/fjb/sunrise/controllers/HealthControllerTest.java @@ -0,0 +1,41 @@ +package com.fjb.sunrise.controllers; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view; + +import com.fjb.sunrise.repositories.UserRepository; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; + +@WebMvcTest(HealthController.class) +class HealthControllerTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private WebApplicationContext webApplicationContext; + + @MockBean + private UserRepository userRepository; + + @BeforeEach + public void setUp() { + this.mockMvc = MockMvcBuilders.webAppContextSetup(this.webApplicationContext).build(); + } + + @Test + void testHealthEndpoint() throws Exception { + mockMvc.perform(get("/health")) + .andExpect(status().isOk()) + .andExpect(view().name("health")); + } +} +