From 908d3e9b6b21cbba163afb29f2f22db5f17317a2 Mon Sep 17 00:00:00 2001 From: "musab.bozkurt" Date: Sun, 4 Feb 2024 17:14:26 +0300 Subject: [PATCH] testcontainers changes are implemented, and more tests are added --- README.md | 7 + .../unit_test_service.postman_collection.json | 170 ++++++++++++++ pom.xml | 35 ++- .../api/controller/TutorialController.java | 60 +++++ .../api/request/ApiTutorialRequest.java | 19 ++ .../api/request/ApiTutorialUpdateRequest.java | 19 ++ .../api/response/ApiTutorialResponse.java | 21 ++ .../livedataservice/data/model/Tutorial.java | 46 ++++ .../data/repository/TutorialRepository.java | 13 + .../exception/LiveDataErrorCode.java | 6 +- .../mapper/TutorialMapper.java | 24 ++ .../service/TutorialService.java | 22 ++ .../service/impl/TutorialServiceImpl.java | 64 +++++ src/main/resources/application.yml | 4 + .../controller/ScoreBoardControllerTests.java | 1 + .../controller/TutorialControllerTests.java | 222 ++++++++++++++++++ .../mb/livedataservice/base/BaseUnitTest.java | 49 ++++ .../controller/TutorialControllerTest.java | 141 +++++++++++ .../mapper/TutorialMapperTest.java | 76 ++++++ .../service/impl/TutorialServiceImplTest.java | 159 +++++++++++++ src/test/resources/application.yml | 30 +++ 21 files changed, 1179 insertions(+), 9 deletions(-) create mode 100644 docs/unit_test_service.postman_collection.json create mode 100644 src/main/java/com/mb/livedataservice/api/controller/TutorialController.java create mode 100644 src/main/java/com/mb/livedataservice/api/request/ApiTutorialRequest.java create mode 100644 src/main/java/com/mb/livedataservice/api/request/ApiTutorialUpdateRequest.java create mode 100644 src/main/java/com/mb/livedataservice/api/response/ApiTutorialResponse.java create mode 100644 src/main/java/com/mb/livedataservice/data/model/Tutorial.java create mode 100644 src/main/java/com/mb/livedataservice/data/repository/TutorialRepository.java create mode 100644 src/main/java/com/mb/livedataservice/mapper/TutorialMapper.java create mode 100644 src/main/java/com/mb/livedataservice/service/TutorialService.java create mode 100644 src/main/java/com/mb/livedataservice/service/impl/TutorialServiceImpl.java create mode 100644 src/test/java/com/mb/livedataservice/api/controller/TutorialControllerTests.java create mode 100644 src/test/java/com/mb/livedataservice/integration_tests/api/controller/TutorialControllerTest.java create mode 100644 src/test/java/com/mb/livedataservice/mapper/TutorialMapperTest.java create mode 100644 src/test/java/com/mb/livedataservice/service/impl/TutorialServiceImplTest.java create mode 100644 src/test/resources/application.yml diff --git a/README.md b/README.md index 87e2e8b..2cbc7d8 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,7 @@ - Centralize exception handling by `ControllerAdvice` - `Mapstruct` to map different type of objects to each other - `Micrometer` dependencies were added to track the logs easily +- `Testcontainers` dependencies were added for integration tests - `docker-compose.yml` contains `Grafana`, `Prometheus` and `Zipkin` to track metrics, `Kafka` for event-driven architecture - `Actuator`: http://localhost:8080/actuator @@ -62,6 +63,12 @@ - `Prometheus`: http://localhost:9090/graph - `Zipkin UI`: http://localhost:9411 +- Test via `Postman` (OPTIONAL): + 1. Import [Postman Collection](docs%2Funit_test_service.postman_collection.json) + 2. Right-click the imported Postman Collection and click _**Run Collection**_ section. + 3. On the right panel choose _**Functional**_ or _**Performance**_ section, edit _**Run configuration**_ and click + _**run**_ to test the application. + ------- ### How_To_Run_And_Test_Application diff --git a/docs/unit_test_service.postman_collection.json b/docs/unit_test_service.postman_collection.json new file mode 100644 index 0000000..0c26a57 --- /dev/null +++ b/docs/unit_test_service.postman_collection.json @@ -0,0 +1,170 @@ +{ + "info": { + "_postman_id": "ebe84f96-4ce4-4dcf-98b5-ebc2a6120236", + "name": "Unit Test Service", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", + "_exporter_id": "31512047" + }, + "item": [ + { + "name": "Create Tutorial", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var response = JSON.parse(responseBody);", + "", + "pm.test(\"Status code is 201\", function () {", + " pm.response.to.have.status(201);", + " postman.setEnvironmentVariable(\"tutorialId\", response.id);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"title\": \"title\",\n \"description\": \"description\",\n \"published\": true\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://localhost:8080/api/tutorials", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8080", + "path": [ + "api", + "tutorials" + ] + } + }, + "response": [] + }, + { + "name": "Get Tutorial by Id", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var response = JSON.parse(responseBody);", + "", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + " postman.setEnvironmentVariable(\"tutorialId\", response.id);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "http://localhost:8080/api/tutorials/:tutorialId", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8080", + "path": [ + "api", + "tutorials", + ":tutorialId" + ], + "variable": [ + { + "key": "tutorialId", + "value": "{{tutorialId}}" + } + ] + } + }, + "response": [] + }, + { + "name": "Update Tutorial by Id", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var response = JSON.parse(responseBody);", + "", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + " postman.setEnvironmentVariable(\"tutorialId\", response.id);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"title\": \"updatedTitle\",\n \"description\": \"updateddescription\",\n \"published\": true\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://localhost:8080/api/tutorials/:tutorialId", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8080", + "path": [ + "api", + "tutorials", + ":tutorialId" + ], + "variable": [ + { + "key": "tutorialId", + "value": "{{tutorialId}}" + } + ] + } + }, + "response": [] + }, + { + "name": "Get All Tutorials", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "http://localhost:8080/api/tutorials", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8080", + "path": [ + "api", + "tutorials" + ] + } + }, + "response": [] + } + ] +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index c482333..db24a3f 100644 --- a/pom.xml +++ b/pom.xml @@ -57,12 +57,6 @@ true - - org.springframework.boot - spring-boot-starter-test - test - - org.springdoc springdoc-openapi-starter-webmvc-ui @@ -119,6 +113,35 @@ spring-boot-docker-compose + + org.springframework.boot + spring-boot-starter-test + test + + + + org.springframework.boot + spring-boot-testcontainers + test + + + + org.testcontainers + junit-jupiter + test + + + + org.postgresql + postgresql + + + + org.testcontainers + postgresql + test + + diff --git a/src/main/java/com/mb/livedataservice/api/controller/TutorialController.java b/src/main/java/com/mb/livedataservice/api/controller/TutorialController.java new file mode 100644 index 0000000..f9a35ba --- /dev/null +++ b/src/main/java/com/mb/livedataservice/api/controller/TutorialController.java @@ -0,0 +1,60 @@ +package com.mb.livedataservice.api.controller; + +import com.mb.livedataservice.api.request.ApiTutorialRequest; +import com.mb.livedataservice.api.request.ApiTutorialUpdateRequest; +import com.mb.livedataservice.api.response.ApiTutorialResponse; +import com.mb.livedataservice.mapper.TutorialMapper; +import com.mb.livedataservice.service.TutorialService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/api") +@CrossOrigin(origins = "http://localhost:8081") +public class TutorialController { + + private final TutorialService tutorialService; + private final TutorialMapper tutorialMapper; + + @PostMapping("/tutorials") + public ResponseEntity createTutorial(@RequestBody ApiTutorialRequest apiTutorialRequest) { + return new ResponseEntity<>(tutorialMapper.map(tutorialService.save(tutorialMapper.map(apiTutorialRequest))), HttpStatus.CREATED); + } + + @GetMapping("/tutorials/{id}") + public ResponseEntity getTutorialById(@PathVariable("id") long id) { + return ResponseEntity.ok(tutorialMapper.map(tutorialService.findById(id))); + } + + @GetMapping("/tutorials") + public ResponseEntity> getAllTutorials(@RequestParam(required = false) String title) { + return ResponseEntity.ok(tutorialMapper.map(tutorialService.findByTitleContaining(title))); + } + + @PutMapping("/tutorials/{id}") + public ResponseEntity updateTutorial(@PathVariable("id") long id, @RequestBody ApiTutorialUpdateRequest apiTutorialUpdateRequest) { + return new ResponseEntity<>(tutorialMapper.map(tutorialService.update(id, tutorialMapper.map(apiTutorialUpdateRequest))), HttpStatus.OK); + } + + @DeleteMapping("/tutorials/{id}") + public ResponseEntity deleteTutorial(@PathVariable("id") long id) { + tutorialService.deleteById(id); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + + @DeleteMapping("/tutorials") + public ResponseEntity deleteAllTutorials() { + tutorialService.deleteAll(); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + + @GetMapping("/tutorials/published") + public ResponseEntity> findByPublished() { + return new ResponseEntity<>(tutorialMapper.map(tutorialService.findByPublished(true)), HttpStatus.OK); + } +} diff --git a/src/main/java/com/mb/livedataservice/api/request/ApiTutorialRequest.java b/src/main/java/com/mb/livedataservice/api/request/ApiTutorialRequest.java new file mode 100644 index 0000000..7570201 --- /dev/null +++ b/src/main/java/com/mb/livedataservice/api/request/ApiTutorialRequest.java @@ -0,0 +1,19 @@ +package com.mb.livedataservice.api.request; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.ToString; + +@Data +@ToString +@NoArgsConstructor +@AllArgsConstructor +public class ApiTutorialRequest { + + private String title; + + private String description; + + private boolean published; +} diff --git a/src/main/java/com/mb/livedataservice/api/request/ApiTutorialUpdateRequest.java b/src/main/java/com/mb/livedataservice/api/request/ApiTutorialUpdateRequest.java new file mode 100644 index 0000000..ecb90fc --- /dev/null +++ b/src/main/java/com/mb/livedataservice/api/request/ApiTutorialUpdateRequest.java @@ -0,0 +1,19 @@ +package com.mb.livedataservice.api.request; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.ToString; + +@Data +@ToString +@NoArgsConstructor +@AllArgsConstructor +public class ApiTutorialUpdateRequest { + + private String title; + + private String description; + + private boolean published; +} diff --git a/src/main/java/com/mb/livedataservice/api/response/ApiTutorialResponse.java b/src/main/java/com/mb/livedataservice/api/response/ApiTutorialResponse.java new file mode 100644 index 0000000..2d9993b --- /dev/null +++ b/src/main/java/com/mb/livedataservice/api/response/ApiTutorialResponse.java @@ -0,0 +1,21 @@ +package com.mb.livedataservice.api.response; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.ToString; + +@Data +@ToString +@NoArgsConstructor +@AllArgsConstructor +public class ApiTutorialResponse { + + private long id; + + private String title; + + private String description; + + private boolean published; +} diff --git a/src/main/java/com/mb/livedataservice/data/model/Tutorial.java b/src/main/java/com/mb/livedataservice/data/model/Tutorial.java new file mode 100644 index 0000000..3a61a85 --- /dev/null +++ b/src/main/java/com/mb/livedataservice/data/model/Tutorial.java @@ -0,0 +1,46 @@ +package com.mb.livedataservice.data.model; + +import jakarta.persistence.*; +import lombok.*; +import org.hibernate.proxy.HibernateProxy; + +import java.util.Objects; + +@Getter +@Setter +@Entity +@ToString +@NoArgsConstructor +@AllArgsConstructor +@Table(name = "tutorials") +public class Tutorial { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + @Column(name = "title", nullable = false) + private String title; + + @Column(name = "description", nullable = false) + private String description; + + @Column(name = "published") + private boolean published; + + @Override + public final boolean equals(Object o) { + if (this == o) return true; + if (o == null) return false; + Class oEffectiveClass = o instanceof HibernateProxy ? ((HibernateProxy) o).getHibernateLazyInitializer().getPersistentClass() : o.getClass(); + Class thisEffectiveClass = this instanceof HibernateProxy ? ((HibernateProxy) this).getHibernateLazyInitializer().getPersistentClass() : this.getClass(); + if (thisEffectiveClass != oEffectiveClass) return false; + Tutorial tutorial = (Tutorial) o; + return Objects.equals(getId(), tutorial.getId()); + } + + @Override + public final int hashCode() { + return this instanceof HibernateProxy ? ((HibernateProxy) this).getHibernateLazyInitializer().getPersistentClass().hashCode() : getClass().hashCode(); + } +} diff --git a/src/main/java/com/mb/livedataservice/data/repository/TutorialRepository.java b/src/main/java/com/mb/livedataservice/data/repository/TutorialRepository.java new file mode 100644 index 0000000..b671e84 --- /dev/null +++ b/src/main/java/com/mb/livedataservice/data/repository/TutorialRepository.java @@ -0,0 +1,13 @@ +package com.mb.livedataservice.data.repository; + +import com.mb.livedataservice.data.model.Tutorial; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.List; + +public interface TutorialRepository extends JpaRepository { + + List findByPublished(boolean published); + + List findByTitleContaining(String title); +} diff --git a/src/main/java/com/mb/livedataservice/exception/LiveDataErrorCode.java b/src/main/java/com/mb/livedataservice/exception/LiveDataErrorCode.java index 9251395..e4b6e1b 100644 --- a/src/main/java/com/mb/livedataservice/exception/LiveDataErrorCode.java +++ b/src/main/java/com/mb/livedataservice/exception/LiveDataErrorCode.java @@ -5,6 +5,7 @@ import java.io.Serializable; +@Getter public enum LiveDataErrorCode implements Serializable, ErrorCode { UNEXPECTED_ERROR(HttpStatus.BAD_REQUEST), @@ -12,12 +13,11 @@ public enum LiveDataErrorCode implements Serializable, ErrorCode { INVALID_VALUE(HttpStatus.BAD_REQUEST), SCORE_BOARD_NOT_FOUND(HttpStatus.NOT_FOUND), SCORE_BOARD_HAS_NOT_ENDED(HttpStatus.BAD_REQUEST), - CANNOT_MAP_RESPONSE(HttpStatus.BAD_REQUEST); + CANNOT_MAP_RESPONSE(HttpStatus.BAD_REQUEST), + NOT_FOUND(HttpStatus.NOT_FOUND); - @Getter private final HttpStatus httpStatus; - @Getter private String message; LiveDataErrorCode(HttpStatus httpStatus) { diff --git a/src/main/java/com/mb/livedataservice/mapper/TutorialMapper.java b/src/main/java/com/mb/livedataservice/mapper/TutorialMapper.java new file mode 100644 index 0000000..bcdfe92 --- /dev/null +++ b/src/main/java/com/mb/livedataservice/mapper/TutorialMapper.java @@ -0,0 +1,24 @@ +package com.mb.livedataservice.mapper; + +import com.mb.livedataservice.api.request.ApiTutorialRequest; +import com.mb.livedataservice.api.request.ApiTutorialUpdateRequest; +import com.mb.livedataservice.api.response.ApiTutorialResponse; +import com.mb.livedataservice.data.model.Tutorial; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +import java.util.List; + +@Mapper(componentModel = "spring") +public interface TutorialMapper { + + @Mapping(target = "id", ignore = true) + Tutorial map(ApiTutorialRequest apiTutorialRequest); + + @Mapping(target = "id", ignore = true) + Tutorial map(ApiTutorialUpdateRequest apiTutorialRequest); + + ApiTutorialResponse map(Tutorial tutorial); + + List map(List tutorial); +} diff --git a/src/main/java/com/mb/livedataservice/service/TutorialService.java b/src/main/java/com/mb/livedataservice/service/TutorialService.java new file mode 100644 index 0000000..b2f7524 --- /dev/null +++ b/src/main/java/com/mb/livedataservice/service/TutorialService.java @@ -0,0 +1,22 @@ +package com.mb.livedataservice.service; + +import com.mb.livedataservice.data.model.Tutorial; + +import java.util.List; + +public interface TutorialService { + + List findByTitleContaining(String title); + + Tutorial findById(long id); + + Tutorial save(Tutorial tutorial); + + Tutorial update(long id, Tutorial tutorial); + + void deleteById(long id); + + void deleteAll(); + + List findByPublished(boolean b); +} diff --git a/src/main/java/com/mb/livedataservice/service/impl/TutorialServiceImpl.java b/src/main/java/com/mb/livedataservice/service/impl/TutorialServiceImpl.java new file mode 100644 index 0000000..079de3d --- /dev/null +++ b/src/main/java/com/mb/livedataservice/service/impl/TutorialServiceImpl.java @@ -0,0 +1,64 @@ +package com.mb.livedataservice.service.impl; + +import com.mb.livedataservice.data.model.Tutorial; +import com.mb.livedataservice.data.repository.TutorialRepository; +import com.mb.livedataservice.exception.BaseException; +import com.mb.livedataservice.exception.LiveDataErrorCode; +import com.mb.livedataservice.service.TutorialService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Slf4j +@Service +@RequiredArgsConstructor +public class TutorialServiceImpl implements TutorialService { + + private final TutorialRepository tutorialRepository; + + @Override + public List findByTitleContaining(String title) { + if (StringUtils.isNotBlank(title)) { + return tutorialRepository.findByTitleContaining(title); + } + return tutorialRepository.findAll(); + } + + @Override + public Tutorial findById(long id) { + return tutorialRepository.findById(id) + .orElseThrow(() -> new BaseException(LiveDataErrorCode.NOT_FOUND)); + } + + @Override + public Tutorial save(Tutorial tutorial) { + return tutorialRepository.save(tutorial); + } + + @Override + public Tutorial update(long id, Tutorial tutorial) { + Tutorial byId = findById(id); + byId.setTitle(tutorial.getTitle()); + byId.setDescription(tutorial.getDescription()); + byId.setPublished(tutorial.isPublished()); + return tutorialRepository.save(byId); + } + + @Override + public void deleteById(long id) { + tutorialRepository.deleteById(id); + } + + @Override + public void deleteAll() { + tutorialRepository.deleteAll(); + } + + @Override + public List findByPublished(boolean b) { + return tutorialRepository.findByPublished(b); + } +} \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 75e8a7c..24082a3 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -54,6 +54,10 @@ spring: compose: enabled: true + threads: + virtual: + enabled: true + management: endpoints: web: diff --git a/src/test/java/com/mb/livedataservice/api/controller/ScoreBoardControllerTests.java b/src/test/java/com/mb/livedataservice/api/controller/ScoreBoardControllerTests.java index 43265fb..b707b10 100644 --- a/src/test/java/com/mb/livedataservice/api/controller/ScoreBoardControllerTests.java +++ b/src/test/java/com/mb/livedataservice/api/controller/ScoreBoardControllerTests.java @@ -37,6 +37,7 @@ @WebMvcTest @ContextConfiguration(classes = {ScoreBoardController.class, RestResponseExceptionHandler.class}) class ScoreBoardControllerTests extends BaseUnitTest { + @MockBean private ScoreBoardService scoreBoardService; diff --git a/src/test/java/com/mb/livedataservice/api/controller/TutorialControllerTests.java b/src/test/java/com/mb/livedataservice/api/controller/TutorialControllerTests.java new file mode 100644 index 0000000..d2a0b02 --- /dev/null +++ b/src/test/java/com/mb/livedataservice/api/controller/TutorialControllerTests.java @@ -0,0 +1,222 @@ +package com.mb.livedataservice.api.controller; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.mb.livedataservice.api.request.ApiTutorialRequest; +import com.mb.livedataservice.api.request.ApiTutorialUpdateRequest; +import com.mb.livedataservice.api.response.ApiTutorialResponse; +import com.mb.livedataservice.base.BaseUnitTest; +import com.mb.livedataservice.data.model.Tutorial; +import com.mb.livedataservice.exception.BaseException; +import com.mb.livedataservice.exception.LiveDataErrorCode; +import com.mb.livedataservice.exception.RestResponseExceptionHandler; +import com.mb.livedataservice.mapper.TutorialMapper; +import com.mb.livedataservice.service.TutorialService; +import org.junit.jupiter.api.Assertions; +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.http.MediaType; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; + +import java.util.Collections; +import java.util.List; + +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@WebMvcTest +@ContextConfiguration(classes = {TutorialController.class, RestResponseExceptionHandler.class}) +class TutorialControllerTests extends BaseUnitTest { + + @MockBean + private TutorialService tutorialService; + + @MockBean + private TutorialMapper tutorialMapper; + + @Autowired + private MockMvc mockMvc; + + @Autowired + private ObjectMapper objectMapper; + + @Test + void shouldCreateTutorial() throws Exception { + ApiTutorialRequest apiTutorialRequest = getApiTutorialRequest(); + Tutorial tutorial = getTutorial(); + ApiTutorialResponse apiTutorialResponse = getApiTutorialResponse(); + + when(tutorialMapper.map(apiTutorialRequest)).thenReturn(tutorial); + when(tutorialService.save(tutorial)).thenReturn(tutorial); + when(tutorialMapper.map(tutorial)).thenReturn(apiTutorialResponse); + + mockMvc.perform(post("/api/tutorials").contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(apiTutorialRequest))) + .andExpect(status().isCreated()) + .andExpect(jsonPath("$.id").value(apiTutorialResponse.getId())) + .andExpect(jsonPath("$.title").value(apiTutorialResponse.getTitle())) + .andExpect(jsonPath("$.description").value(apiTutorialResponse.getDescription())) + .andExpect(jsonPath("$.published").value(apiTutorialResponse.isPublished())) + .andDo(print()); + } + + @Test + void shouldReturnTutorial() throws Exception { + long id = 1L; + Tutorial tutorial = getTutorial(); + ApiTutorialResponse apiTutorialResponse = getApiTutorialResponse(); + + when(tutorialService.findById(id)).thenReturn(tutorial); + when(tutorialMapper.map(tutorial)).thenReturn(apiTutorialResponse); + + mockMvc.perform(get("/api/tutorials/{id}", id)).andExpect(status().isOk()) + .andExpect(jsonPath("$.id").value(id)) + .andExpect(jsonPath("$.title").value(apiTutorialResponse.getTitle())) + .andExpect(jsonPath("$.description").value(apiTutorialResponse.getDescription())) + .andExpect(jsonPath("$.published").value(apiTutorialResponse.isPublished())) + .andDo(print()); + } + + @Test + void shouldThrowException_WhenTutorialNotFound() throws Exception { + long id = 1L; + + when(tutorialService.findById(id)).thenThrow(new BaseException(LiveDataErrorCode.NOT_FOUND)); + + mockMvc.perform(get("/api/tutorials/{id}", id)) + .andExpect(status().isNotFound()) + .andExpect(result -> Assertions.assertInstanceOf(BaseException.class, result.getResolvedException())) + .andDo(print()); + } + + @Test + void shouldReturnListOfTutorials() throws Exception { + List tutorials = getTutorials(); + + List apiTutorialResponses = getApiTutorialResponses(); + + when(tutorialService.findByTitleContaining(null)).thenReturn(tutorials); + when(tutorialMapper.map(tutorials)).thenReturn(apiTutorialResponses); + + mockMvc.perform(get("/api/tutorials")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.size()").value(apiTutorialResponses.size())) + .andDo(print()); + } + + @Test + void shouldReturnListOfTutorialsWithFilter() throws Exception { + List tutorials = getTutorialList(); + + List apiTutorialResponses = getApiTutorialResponseList(); + + String title = "Boot"; + MultiValueMap paramsMap = new LinkedMultiValueMap<>(); + paramsMap.add("title", title); + + when(tutorialService.findByTitleContaining(title)).thenReturn(tutorials); + when(tutorialMapper.map(tutorials)).thenReturn(apiTutorialResponses); + + mockMvc.perform(get("/api/tutorials").params(paramsMap)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.size()").value(apiTutorialResponses.size())) + .andDo(print()); + } + + @Test + void shouldReturnNoContentWhenFilter() throws Exception { + String title = "mb"; + MultiValueMap paramsMap = new LinkedMultiValueMap<>(); + paramsMap.add("title", title); + + List tutorials = Collections.emptyList(); + + when(tutorialService.findByTitleContaining(title)).thenReturn(tutorials); + when(tutorialMapper.map(tutorials)).thenReturn(Collections.emptyList()); + + mockMvc.perform(get("/api/tutorials").params(paramsMap)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.size()").value(0)) + .andDo(print()); + } + + @Test + void shouldUpdateTutorial() throws Exception { + long id = 1L; + + ApiTutorialUpdateRequest apiTutorialUpdateRequest = getApiTutorialUpdateRequest(); + Tutorial updatedTutorial = getUpdatedTutorial(); + + ApiTutorialResponse apiTutorialResponse = getUpdatedApiTutorialResponse(); + + when(tutorialMapper.map(apiTutorialUpdateRequest)).thenReturn(updatedTutorial); + when(tutorialService.update(id, updatedTutorial)).thenReturn(updatedTutorial); + when(tutorialMapper.map(updatedTutorial)).thenReturn(apiTutorialResponse); + + mockMvc.perform(put("/api/tutorials/{id}", id).contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(apiTutorialUpdateRequest))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.title").value(apiTutorialResponse.getTitle())) + .andExpect(jsonPath("$.description").value(apiTutorialResponse.getDescription())) + .andExpect(jsonPath("$.published").value(apiTutorialResponse.isPublished())) + .andDo(print()); + } + + @Test + void shouldReturnNotFoundUpdateTutorial() throws Exception { + long id = 1L; + + ApiTutorialUpdateRequest apiTutorialUpdateRequest = getApiTutorialUpdateRequest(); + Tutorial updatedTutorial = getUpdatedTutorial(); + + when(tutorialMapper.map(apiTutorialUpdateRequest)).thenReturn(updatedTutorial); + when(tutorialService.update(id, updatedTutorial)).thenThrow(new BaseException(LiveDataErrorCode.NOT_FOUND)); + + mockMvc.perform(put("/api/tutorials/{id}", id).contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(apiTutorialUpdateRequest))) + .andExpect(status().isNotFound()) + .andExpect(result -> Assertions.assertInstanceOf(BaseException.class, result.getResolvedException())) + .andDo(print()); + } + + @Test + void shouldDeleteTutorial() throws Exception { + long id = 1L; + + doNothing().when(tutorialService).deleteById(id); + mockMvc.perform(delete("/api/tutorials/{id}", id)) + .andExpect(status().isNoContent()) + .andDo(print()); + } + + @Test + void shouldDeleteAllTutorials() throws Exception { + doNothing().when(tutorialService).deleteAll(); + mockMvc.perform(delete("/api/tutorials")) + .andExpect(status().isNoContent()) + .andDo(print()); + } + + @Test + void shouldReturnListOfTutorials_WhenPublishedIsTrue() throws Exception { + List tutorials = getTutorials(); + + List apiTutorialResponses = getApiTutorialResponses(); + + when(tutorialService.findByPublished(true)).thenReturn(tutorials); + when(tutorialMapper.map(tutorials)).thenReturn(apiTutorialResponses); + + mockMvc.perform(get("/api/tutorials/published")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.size()").value(apiTutorialResponses.size())) + .andDo(print()); + } +} diff --git a/src/test/java/com/mb/livedataservice/base/BaseUnitTest.java b/src/test/java/com/mb/livedataservice/base/BaseUnitTest.java index 71e9ea5..f9b7fc6 100644 --- a/src/test/java/com/mb/livedataservice/base/BaseUnitTest.java +++ b/src/test/java/com/mb/livedataservice/base/BaseUnitTest.java @@ -2,8 +2,12 @@ import com.mb.livedataservice.api.request.ApiScoreBoardRequest; import com.mb.livedataservice.api.request.ApiScoreBoardUpdateRequest; +import com.mb.livedataservice.api.request.ApiTutorialRequest; +import com.mb.livedataservice.api.request.ApiTutorialUpdateRequest; import com.mb.livedataservice.api.response.ApiScoreBoardResponse; +import com.mb.livedataservice.api.response.ApiTutorialResponse; import com.mb.livedataservice.data.model.ScoreBoard; +import com.mb.livedataservice.data.model.Tutorial; import java.time.OffsetDateTime; import java.util.Arrays; @@ -90,4 +94,49 @@ public List getScoreBoardsAsStringList() { "5. Germany - France : 2 - 2"); } + public ApiTutorialRequest getApiTutorialRequest() { + return new ApiTutorialRequest("Spring Boot @WebMvcTest", "Description", true); + } + + public Tutorial getTutorial() { + return new Tutorial(1, "Spring Boot @WebMvcTest", "Description", true); + } + + public ApiTutorialResponse getApiTutorialResponse() { + return new ApiTutorialResponse(1, "Spring Boot @WebMvcTest", "Description", true); + } + + public List getTutorials() { + return Arrays.asList(new Tutorial(1, "Spring Boot @WebMvcTest 1", "Description 1", true), + new Tutorial(2, "Spring Boot @WebMvcTest 2", "Description 2", true), + new Tutorial(3, "Spring Boot @WebMvcTest 3", "Description 3", true)); + } + + public List getApiTutorialResponses() { + return Arrays.asList(new ApiTutorialResponse(1, "Spring Boot @WebMvcTest 1", "Description 1", true), + new ApiTutorialResponse(2, "Spring Boot @WebMvcTest 2", "Description 2", true), + new ApiTutorialResponse(3, "Spring Boot @WebMvcTest 3", "Description 3", true)); + } + + public List getTutorialList() { + return Arrays.asList(new Tutorial(1, "Spring Boot @WebMvcTest", "Description 1", true), + new Tutorial(3, "Spring Boot Web MVC", "Description 3", true)); + } + + public List getApiTutorialResponseList() { + return Arrays.asList(new ApiTutorialResponse(1, "Spring Boot @WebMvcTest", "Description 1", true), + new ApiTutorialResponse(3, "Spring Boot Web MVC", "Description 3", true)); + } + + public ApiTutorialUpdateRequest getApiTutorialUpdateRequest() { + return new ApiTutorialUpdateRequest("Updated", "Updated", true); + } + + public Tutorial getUpdatedTutorial() { + return new Tutorial(1, "Updated", "Updated", true); + } + + public ApiTutorialResponse getUpdatedApiTutorialResponse() { + return new ApiTutorialResponse(1, "Updated", "Updated", true); + } } diff --git a/src/test/java/com/mb/livedataservice/integration_tests/api/controller/TutorialControllerTest.java b/src/test/java/com/mb/livedataservice/integration_tests/api/controller/TutorialControllerTest.java new file mode 100644 index 0000000..8f9de07 --- /dev/null +++ b/src/test/java/com/mb/livedataservice/integration_tests/api/controller/TutorialControllerTest.java @@ -0,0 +1,141 @@ +package com.mb.livedataservice.integration_tests.api.controller; + +import com.mb.livedataservice.api.request.ApiTutorialRequest; +import com.mb.livedataservice.api.request.ApiTutorialUpdateRequest; +import com.mb.livedataservice.api.response.ApiTutorialResponse; +import com.mb.livedataservice.base.BaseUnitTest; +import com.mb.livedataservice.data.model.Tutorial; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.boot.testcontainers.service.connection.ServiceConnection; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.test.annotation.Rollback; +import org.springframework.transaction.annotation.Transactional; +import org.testcontainers.containers.PostgreSQLContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +import java.util.Objects; + +import static org.assertj.core.api.Assertions.assertThat; + +@Transactional +@Testcontainers +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +class TutorialControllerTest extends BaseUnitTest { + + @Container + @ServiceConnection + private static final PostgreSQLContainer postgres = new PostgreSQLContainer<>("postgres:16.1"); + + @Autowired + private TestRestTemplate restTemplate; + + @BeforeAll + static void setup(@Autowired TestRestTemplate restTemplate) { + //Prepare some test data + for (int i = 0; i <= 100; i++) { + ApiTutorialRequest apiTutorialRequest = new ApiTutorialRequest("Spring Boot @WebMvcTest%d".formatted(i), "Description%d".formatted(i), true); + restTemplate.exchange("/api/tutorials", HttpMethod.POST, new HttpEntity<>(apiTutorialRequest), ApiTutorialResponse.class); + } + } + + @Test + void connectionEstablished() { + assertThat(postgres.isCreated()).isTrue(); + assertThat(postgres.isRunning()).isTrue(); + } + + @Test + @Rollback + void shouldCreateNewTutorialWhenTutorialIsValid() { + ApiTutorialRequest apiTutorialRequest = getApiTutorialRequest(); + + ResponseEntity response = restTemplate.exchange("/api/tutorials", HttpMethod.POST, new HttpEntity<>(apiTutorialRequest), ApiTutorialResponse.class); + + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.CREATED); + assertThat(response.getBody()).isNotNull(); + assertThat(Objects.requireNonNull(response.getBody()).getId()).isEqualTo(102); + assertThat(response.getBody().getTitle()).isEqualTo("Spring Boot @WebMvcTest"); + assertThat(response.getBody().getDescription()).isEqualTo("Description"); + } + + @Test + void shouldNotCreateNewTutorialWhenValidationFails() { + ApiTutorialRequest apiTutorialRequest = new ApiTutorialRequest("Spring Boot @WebMvcTest", null, true); + + ResponseEntity response = restTemplate.exchange("/api/tutorials", HttpMethod.POST, new HttpEntity<>(apiTutorialRequest), ApiTutorialResponse.class); + + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST); + } + + @Test + void shouldGetTutorialWhenValidTutorialId() { + ResponseEntity response = restTemplate.exchange("/api/tutorials/1", HttpMethod.GET, null, Tutorial.class); + + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(response.getBody()).isNotNull(); + assertThat(response.getBody().getTitle()).isEqualTo("Spring Boot @WebMvcTest0"); + assertThat(response.getBody().getDescription()).isEqualTo("Description0"); + } + + @Test + void shouldGetAllTutorials() { + Tutorial[] tutorials = restTemplate.getForObject("/api/tutorials", Tutorial[].class); + + assertThat(tutorials.length).isGreaterThan(100); + } + + @Test + void shouldThrowNotFoundWhenInvalidTutorialId() { + ResponseEntity response = restTemplate.exchange("/api/tutorials/999", HttpMethod.GET, null, Tutorial.class); + + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND); + } + + @Test + @Rollback + void shouldUpdateTutorialWhenTutorialIsValid() { + ApiTutorialUpdateRequest apiTutorialUpdateRequest = getApiTutorialUpdateRequest(); + + ResponseEntity updatedResponse = restTemplate.exchange("/api/tutorials/99", HttpMethod.PUT, new HttpEntity<>(apiTutorialUpdateRequest), ApiTutorialResponse.class); + + assertThat(updatedResponse).isNotNull(); + assertThat(updatedResponse.getBody()).isNotNull(); + + assertThat(updatedResponse.getBody().getId()).isEqualTo(99); + assertThat(updatedResponse.getBody().getTitle()).isEqualTo("Updated"); + assertThat(updatedResponse.getBody().getDescription()).isEqualTo("Updated"); + } + + @Test + @Rollback + void shouldDeleteWithValidId() { + ResponseEntity response = restTemplate.exchange("/api/tutorials/88", HttpMethod.DELETE, null, Void.class); + + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NO_CONTENT); + } + + @Test + @Rollback + @Disabled("This test will delete all tutorials, so it should be disabled.") + void shouldDeleteAllTutorials() { + ResponseEntity response = restTemplate.exchange("/api/tutorials", HttpMethod.DELETE, null, HttpStatus.class); + + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NO_CONTENT); + } + + @Test + void shouldGetAllTutorialsByPublishedTrue() { + Tutorial[] tutorials = restTemplate.getForObject("/api/tutorials/published", Tutorial[].class); + + assertThat(tutorials.length).isGreaterThan(100); + } +} \ No newline at end of file diff --git a/src/test/java/com/mb/livedataservice/mapper/TutorialMapperTest.java b/src/test/java/com/mb/livedataservice/mapper/TutorialMapperTest.java new file mode 100644 index 0000000..cd4c815 --- /dev/null +++ b/src/test/java/com/mb/livedataservice/mapper/TutorialMapperTest.java @@ -0,0 +1,76 @@ +package com.mb.livedataservice.mapper; + +import com.mb.livedataservice.api.request.ApiTutorialRequest; +import com.mb.livedataservice.api.request.ApiTutorialUpdateRequest; +import com.mb.livedataservice.api.response.ApiTutorialResponse; +import com.mb.livedataservice.base.BaseUnitTest; +import com.mb.livedataservice.data.model.Tutorial; +import org.junit.jupiter.api.Test; +import org.mapstruct.factory.Mappers; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class TutorialMapperTest extends BaseUnitTest { + + TutorialMapper tutorialMapper = Mappers.getMapper(TutorialMapper.class); + + @Test + void map_ApiTutorialRequestToTutorial_ShouldSucceed() { + // arrange + ApiTutorialRequest apiTutorialRequest = getApiTutorialRequest(); + + // act + Tutorial result = tutorialMapper.map(apiTutorialRequest); + + // assertion + assertEquals(apiTutorialRequest.getTitle(), result.getTitle()); + assertEquals(apiTutorialRequest.getDescription(), result.getDescription()); + assertEquals(apiTutorialRequest.isPublished(), result.isPublished()); + } + + @Test + void map_ApiTutorialUpdateRequestToTutorial_ShouldSucceed() { + // arrange + ApiTutorialUpdateRequest apiTutorialUpdateRequest = getApiTutorialUpdateRequest(); + + // act + Tutorial result = tutorialMapper.map(apiTutorialUpdateRequest); + + // assertion + assertEquals(apiTutorialUpdateRequest.getTitle(), result.getTitle()); + assertEquals(apiTutorialUpdateRequest.getDescription(), result.getDescription()); + assertEquals(apiTutorialUpdateRequest.isPublished(), result.isPublished()); + } + + @Test + void map_TutorialToApiTutorialResponse_ShouldSucceed() { + // arrange + Tutorial tutorial = getTutorial(); + + // act + ApiTutorialResponse result = tutorialMapper.map(tutorial); + + // assertion + assertEquals(tutorial.getId(), result.getId()); + assertEquals(tutorial.getTitle(), result.getTitle()); + assertEquals(tutorial.getDescription(), result.getDescription()); + assertEquals(tutorial.isPublished(), result.isPublished()); + } + + @Test + void map_ListOfTutorialToListOfApiTutorialResponse_ShouldSucceed() { + // arrange + List tutorials = getTutorials(); + + // act + List result = tutorialMapper.map(tutorials); + + // assertion + assertEquals(tutorials.getFirst().getId(), result.getFirst().getId()); + assertEquals(tutorials.getFirst().getTitle(), result.getFirst().getTitle()); + assertEquals(tutorials.getFirst().getDescription(), result.getFirst().getDescription()); + assertEquals(tutorials.getFirst().isPublished(), result.getFirst().isPublished()); + } +} diff --git a/src/test/java/com/mb/livedataservice/service/impl/TutorialServiceImplTest.java b/src/test/java/com/mb/livedataservice/service/impl/TutorialServiceImplTest.java new file mode 100644 index 0000000..408f0b0 --- /dev/null +++ b/src/test/java/com/mb/livedataservice/service/impl/TutorialServiceImplTest.java @@ -0,0 +1,159 @@ +package com.mb.livedataservice.service.impl; + +import com.mb.livedataservice.base.BaseUnitTest; +import com.mb.livedataservice.data.model.Tutorial; +import com.mb.livedataservice.data.repository.TutorialRepository; +import com.mb.livedataservice.exception.BaseException; +import com.mb.livedataservice.exception.LiveDataErrorCode; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.List; +import java.util.Optional; +import java.util.Random; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.*; + +class TutorialServiceImplTest extends BaseUnitTest { + + @Mock + private TutorialRepository tutorialRepository; + + @InjectMocks + private TutorialServiceImpl tutorialService; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + } + + @Test + void shouldFindByTitleContaining_WhenTitleIsNotBlank() { + List tutorialList = getTutorials(); + + when(tutorialRepository.findByTitleContaining(anyString())).thenReturn(tutorialList); + + List result = tutorialService.findByTitleContaining("title"); + + Assertions.assertEquals(tutorialList, result); + Assertions.assertEquals(tutorialList.size(), result.size()); + } + + @Test + void shouldFindByTitleContaining_WhenTitleIsBlank() { + List tutorialList = getTutorials(); + + when(tutorialRepository.findAll()).thenReturn(tutorialList); + + List result = tutorialService.findByTitleContaining(null); + + Assertions.assertEquals(tutorialList, result); + Assertions.assertEquals(tutorialList.size(), result.size()); + } + + @Test + void shouldFindById_WhenTutorialIsFound() { + Tutorial tutorial = getTutorial(); + + when(tutorialRepository.findById(anyLong())).thenReturn(Optional.of(tutorial)); + + Tutorial result = tutorialService.findById(1L); + + Assertions.assertEquals(tutorial, result); + } + + @Test + void shouldFindByIdThrowException_WhenTutorialIsNotFound() { + when(tutorialRepository.findById(anyLong())).thenReturn(Optional.empty()); + + // Act + BaseException exception = assertThrows(BaseException.class, () -> tutorialService.findById(1L)); + + // Assertions + Assertions.assertEquals(LiveDataErrorCode.NOT_FOUND, exception.getErrorCode()); + } + + @Test + void shouldSave() { + Tutorial tutorial = getTutorial(); + + when(tutorialRepository.save(any(Tutorial.class))).thenReturn(tutorial); + + Tutorial result = tutorialService.save(tutorial); + + Assertions.assertEquals(tutorial, result); + } + + @Test + void shouldUpdate() { + Tutorial tutorial = getTutorial(); + + when(tutorialRepository.findById(1L)).thenReturn(Optional.of(tutorial)); + when(tutorialRepository.save(any(Tutorial.class))).thenReturn(tutorial); + + Tutorial result = tutorialService.update(1L, tutorial); + + Assertions.assertEquals(tutorial, result); + } + + @Test + void shouldUpdateThrowException_WhenTutorialIsNotFound() { + Tutorial tutorial = getTutorial(); + + when(tutorialRepository.findById(1L)).thenReturn(Optional.empty()); + + // Act + BaseException exception = assertThrows(BaseException.class, () -> tutorialService.update(1L, tutorial)); + + // Assertions + Assertions.assertEquals(LiveDataErrorCode.NOT_FOUND, exception.getErrorCode()); + } + + @Test + void shouldDeleteById() { + long tutorialId = new Random().nextLong(); + + // Act + tutorialService.deleteById(tutorialId); + + // Assertions + verify(tutorialRepository).deleteById(tutorialId); + } + + + @Test + void shouldDeleteAll() { + tutorialService.deleteAll(); + + verify(tutorialRepository).deleteAll(); + } + + @Test + void shouldFindByPublished_WhenPublishedIsTrue() { + List tutorials = getTutorials(); + + when(tutorialRepository.findByPublished(true)).thenReturn(tutorials); + + List result = tutorialService.findByPublished(true); + + Assertions.assertEquals(tutorials, result); + Assertions.assertEquals(tutorials.size(), result.size()); + } + + @Test + void shouldFindByPublished_WhenPublishedIsFalse() { + List tutorials = getTutorials(); + + when(tutorialRepository.findByPublished(false)).thenReturn(tutorials); + + List result = tutorialService.findByPublished(false); + + Assertions.assertEquals(tutorials, result); + Assertions.assertEquals(tutorials.size(), result.size()); + } +} \ No newline at end of file diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml new file mode 100644 index 0000000..c3462d1 --- /dev/null +++ b/src/test/resources/application.yml @@ -0,0 +1,30 @@ +spring: + jpa: + database-platform: org.hibernate.dialect.PostgreSQLDialect + show-sql: ${SHOW_SQL_ENABLED:true} + hibernate: + ddl-auto: update + + kafka: + consumer: + bootstrap-servers: localhost:9092 + group-id: mb_group_id + auto-offset-reset: earliest + key-deserializer: org.apache.kafka.common.serialization.StringDeserializer + value-deserializer: org.apache.kafka.common.serialization.StringDeserializer + producer: + bootstrap-servers: localhost:9092 + key-serializer: org.apache.kafka.common.serialization.StringSerializer + value-serializer: org.apache.kafka.common.serialization.StringSerializer + kafka-topics: + test-topic: test_topic + +swagger: + documentation: + services: + - name: live-data-service + url: /v2/api-docs + version: 2.0 + - name: live-data-service-2 + url: /v2/api-docs + version: 2.0 \ No newline at end of file