commit 9daa2517e9c9fadfb4d44ba1005f9867cc96d817 Author: vladimir-shperling Date: Wed Oct 30 01:50:45 2024 +0700 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..98f5c59 --- /dev/null +++ b/.gitignore @@ -0,0 +1,32 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### IntelliJ IDEA ### +.idea + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..88282ee --- /dev/null +++ b/pom.xml @@ -0,0 +1,57 @@ + + + 4.0.0 + + org.example + NTO-2024 + 1.0-SNAPSHOT + + + 11 + 11 + UTF-8 + + + + org.springframework.boot + spring-boot-starter-parent + 2.5.5 + + + + + org.projectlombok + lombok + + + org.springframework.boot + spring-boot-starter-web + + + com.h2database + h2 + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springdoc + springdoc-openapi-ui + 1.7.0 + + + org.junit.jupiter + junit-jupiter-engine + test + + + org.springframework.boot + spring-boot-starter-test + test + + + + \ No newline at end of file diff --git a/src/main/java/com/example/nto/App.java b/src/main/java/com/example/nto/App.java new file mode 100644 index 0000000..d4add94 --- /dev/null +++ b/src/main/java/com/example/nto/App.java @@ -0,0 +1,11 @@ +package com.example.nto; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class App { + public static void main(String[] args) { + SpringApplication.run(App.class, args); + } +} diff --git a/src/main/java/com/example/nto/controller/EmployeeController.java b/src/main/java/com/example/nto/controller/EmployeeController.java new file mode 100644 index 0000000..8082033 --- /dev/null +++ b/src/main/java/com/example/nto/controller/EmployeeController.java @@ -0,0 +1,34 @@ +package com.example.nto.controller; + +import com.example.nto.entity.Code; +import com.example.nto.entity.Employee; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.*; +import com.example.nto.service.EmployeeService; + +@RestController +@RequestMapping("api") +@RequiredArgsConstructor +public class EmployeeController { + + private final EmployeeService employeeService; + + @GetMapping("/{login}/auth") + @ResponseStatus(code = HttpStatus.OK) + public void login(@PathVariable String login) { + employeeService.getByLogin(login); + } + + @GetMapping("/{login}/info") + @ResponseStatus(HttpStatus.OK) + public Employee getByLogin(@PathVariable String login) { + return employeeService.getByLogin(login); + } + + @PatchMapping("/{login}/open") + @ResponseStatus(HttpStatus.OK) + public void updateLastVisitByLogin(@PathVariable String login, @RequestBody Code code) { + employeeService.updateLastVisitByLogin(login, code); + } +} diff --git a/src/main/java/com/example/nto/entity/Code.java b/src/main/java/com/example/nto/entity/Code.java new file mode 100644 index 0000000..9c0cc53 --- /dev/null +++ b/src/main/java/com/example/nto/entity/Code.java @@ -0,0 +1,25 @@ +package com.example.nto.entity; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Data +@Entity +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Table(name = "code") +public class Code { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + @Column(name = "value") + private long value; + +} diff --git a/src/main/java/com/example/nto/entity/Employee.java b/src/main/java/com/example/nto/entity/Employee.java new file mode 100644 index 0000000..3fa42c9 --- /dev/null +++ b/src/main/java/com/example/nto/entity/Employee.java @@ -0,0 +1,37 @@ +package com.example.nto.entity; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; +import java.time.LocalDateTime; + +@Data +@Entity +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Table(name = "employee") +public class Employee { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + @Column(name = "login") + private String login; + + @Column(name = "name") + private String name; + + @Column(name = "photo") + private String photo; + + @Column(name = "position") + private String position; + + @Column(name = "last_visit") + private LocalDateTime lastVisit; +} diff --git a/src/main/java/com/example/nto/exception/CodeNotFoundException.java b/src/main/java/com/example/nto/exception/CodeNotFoundException.java new file mode 100644 index 0000000..c53390b --- /dev/null +++ b/src/main/java/com/example/nto/exception/CodeNotFoundException.java @@ -0,0 +1,7 @@ +package com.example.nto.exception; + +public class CodeNotFoundException extends RuntimeException { + public CodeNotFoundException(String msg) { + super(msg); + } +} diff --git a/src/main/java/com/example/nto/exception/EmployeeNotFoundException.java b/src/main/java/com/example/nto/exception/EmployeeNotFoundException.java new file mode 100644 index 0000000..f7f3126 --- /dev/null +++ b/src/main/java/com/example/nto/exception/EmployeeNotFoundException.java @@ -0,0 +1,7 @@ +package com.example.nto.exception; + +public class EmployeeNotFoundException extends RuntimeException { + public EmployeeNotFoundException(String msg) { + super(msg); + } +} diff --git a/src/main/java/com/example/nto/exception/GlobalExceptionHandler.java b/src/main/java/com/example/nto/exception/GlobalExceptionHandler.java new file mode 100644 index 0000000..bb422cd --- /dev/null +++ b/src/main/java/com/example/nto/exception/GlobalExceptionHandler.java @@ -0,0 +1,19 @@ +package com.example.nto.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; + +@ControllerAdvice +public class GlobalExceptionHandler { + @ExceptionHandler(EmployeeNotFoundException.class) + public ResponseEntity handleEmployeeNotFoundException(EmployeeNotFoundException e) { + return new ResponseEntity<>(e.getMessage(), HttpStatus.UNAUTHORIZED); + } + + @ExceptionHandler(CodeNotFoundException.class) + public ResponseEntity handleCodeNotFoundException(CodeNotFoundException e) { + return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST); + } +} diff --git a/src/main/java/com/example/nto/repository/CodeRepository.java b/src/main/java/com/example/nto/repository/CodeRepository.java new file mode 100644 index 0000000..7e0e14e --- /dev/null +++ b/src/main/java/com/example/nto/repository/CodeRepository.java @@ -0,0 +1,10 @@ +package com.example.nto.repository; + +import com.example.nto.entity.Code; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; + +public interface CodeRepository extends JpaRepository { + Optional findByValue(long value); +} diff --git a/src/main/java/com/example/nto/repository/EmployeeRepository.java b/src/main/java/com/example/nto/repository/EmployeeRepository.java new file mode 100644 index 0000000..a3b6b97 --- /dev/null +++ b/src/main/java/com/example/nto/repository/EmployeeRepository.java @@ -0,0 +1,10 @@ +package com.example.nto.repository; + +import com.example.nto.entity.Employee; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; + +public interface EmployeeRepository extends JpaRepository { + Optional findByLogin(String login); +} diff --git a/src/main/java/com/example/nto/service/EmployeeService.java b/src/main/java/com/example/nto/service/EmployeeService.java new file mode 100644 index 0000000..ae75319 --- /dev/null +++ b/src/main/java/com/example/nto/service/EmployeeService.java @@ -0,0 +1,10 @@ +package com.example.nto.service; + +import com.example.nto.entity.Code; +import com.example.nto.entity.Employee; + +public interface EmployeeService { + Employee getByLogin(String login); + + Employee updateLastVisitByLogin(String login, Code code); +} diff --git a/src/main/java/com/example/nto/service/impl/EmployeeServiceImpl.java b/src/main/java/com/example/nto/service/impl/EmployeeServiceImpl.java new file mode 100644 index 0000000..79bf964 --- /dev/null +++ b/src/main/java/com/example/nto/service/impl/EmployeeServiceImpl.java @@ -0,0 +1,40 @@ +package com.example.nto.service.impl; + +import com.example.nto.entity.Code; +import com.example.nto.entity.Employee; +import com.example.nto.exception.CodeNotFoundException; +import com.example.nto.exception.EmployeeNotFoundException; +import com.example.nto.repository.CodeRepository; +import com.example.nto.repository.EmployeeRepository; +import com.example.nto.service.EmployeeService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.time.LocalDateTime; +import java.time.ZoneId; + +@Service +@RequiredArgsConstructor +public class EmployeeServiceImpl implements EmployeeService { + + private final EmployeeRepository employeeRepository; + private final CodeRepository codeRepository; + + @Override + public Employee getByLogin(String login) { + return employeeRepository.findByLogin(login) + .orElseThrow(() -> new EmployeeNotFoundException("Employee with " + login + " login not found!")); + } + + @Override + public Employee updateLastVisitByLogin(String login, Code code) { + Employee employee = getByLogin(login); + + codeRepository.findByValue(code.getValue()) + .orElseThrow(() -> new CodeNotFoundException("Code with " + code.getValue() + " value not found!")); + + employee.setLastVisit(LocalDateTime.now(ZoneId.of("Europe/Moscow"))); + + return employeeRepository.save(employee); + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000..c6bfd72 --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,28 @@ +spring: + + datasource: + url: jdbc:h2:mem:testdb + + h2: + console: + #enabled: false + enabled: true + + jpa: + #generate-ddl: false + generate-ddl: true + + hibernate: + #ddl-auto: none + ddl-auto: create-drop + + # Показываем запросы + show-sql: true + + # Своевременный запуск data.sql + defer-datasource-initialization: true + + spring-doc: + swagger-ui: + path: /swagger-ui.html + operationsSorter: method \ No newline at end of file diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql new file mode 100644 index 0000000..03720a9 --- /dev/null +++ b/src/main/resources/data.sql @@ -0,0 +1,14 @@ +INSERT INTO employee (id, login, name, photo, position, last_visit) +VALUES +(1, 'pivanov', 'Иванов Петр Федорович', 'https://funnyducks.ru/upload/iblock/0cd/0cdeb7ec3ed6fddda0f90fccee05557d.jpg', 'Разработчик', '2024-02-12T08:30'), +(2, 'ipetrov', 'Петров Иван Константинович', 'https://funnyducks.ru/upload/iblock/0cd/0cdeb7ec3ed6fddda0f90fccee05557d.jpg', 'Аналитик', '2024-02-13T08:35'), +(3, 'asemenov', 'Семенов Анатолий Анатольевич', 'https://funnyducks.ru/upload/iblock/0cd/0cdeb7ec3ed6fddda0f90fccee05557d.jpg', 'Разработчик', '2024-02-13T08:31'), +(4, 'afedorov', 'Федоров Александр Сергеевич', 'https://funnyducks.ru/upload/iblock/0cd/0cdeb7ec3ed6fddda0f90fccee05557d.jpg', 'Тестировщик', '2024-02-12T08:36'); + +INSERT INTO code (value) +VALUES +(1234567890123456789), +(9223372036854775807), +(1122334455667788990), +(998877665544332211), +(5566778899001122334); \ No newline at end of file diff --git a/src/test/java/com/example/nto/intagration/EmployeeControllerIntegrationTest.java b/src/test/java/com/example/nto/intagration/EmployeeControllerIntegrationTest.java new file mode 100644 index 0000000..0eedd5b --- /dev/null +++ b/src/test/java/com/example/nto/intagration/EmployeeControllerIntegrationTest.java @@ -0,0 +1,286 @@ +package com.example.nto.intagration; + +import com.example.nto.entity.Code; +import com.example.nto.entity.Employee; +import com.example.nto.repository.EmployeeRepository; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.context.WebApplicationContext; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.ThreadLocalRandom; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch; +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; + +@Transactional +@SpringBootTest +@DisplayName("Микросервис должен") +public class EmployeeControllerIntegrationTest { + @Autowired + private WebApplicationContext webApplicationContext; + @Autowired + private ObjectMapper objectMapper; + @Autowired + private EmployeeRepository employeeRepository; + private MockMvc mockMvc; + + private List expectedEmployeesList; + private List expectedCodeList; + private Employee testEmployee; + private long testEmployeeId; + private DateTimeFormatter formatter; + + @BeforeEach + public void setup() { + mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build(); + + expectedEmployeesList = List.of( + Employee.builder() + .id(1).login("pivanov").name("Иванов Петр Федорович") + .photo("https://funnyducks.ru/upload/iblock/0cd/0cdeb7ec3ed6fddda0f90fccee05557d.jpg") + .position("Разработчик").lastVisit(LocalDateTime.parse("2024-02-12T08:30")) + .build(), + Employee.builder() + .id(2).login("ipetrov").name("Петров Иван Константинович") + .photo("https://funnyducks.ru/upload/iblock/0cd/0cdeb7ec3ed6fddda0f90fccee05557d.jpg") + .position("Аналитик").lastVisit(LocalDateTime.parse("2024-02-13T08:35")) + .build(), + Employee.builder() + .id(3).login("asemenov").name("Семенов Анатолий Анатольевич") + .photo("https://funnyducks.ru/upload/iblock/0cd/0cdeb7ec3ed6fddda0f90fccee05557d.jpg") + .position("Разработчик").lastVisit(LocalDateTime.parse("2024-02-13T08:31")) + .build(), + Employee.builder() + .id(4).login("afedorov").name("Федоров Александр Сергеевич") + .photo("https://funnyducks.ru/upload/iblock/0cd/0cdeb7ec3ed6fddda0f90fccee05557d.jpg") + .position("Тестировщик").lastVisit(LocalDateTime.parse("2024-02-12T08:36")) + .build() + ); + + expectedCodeList = List.of( + Code.builder().value(1234567890123456789L).build(), + Code.builder().value(9223372036854775807L).build(), + Code.builder().value(1122334455667788990L).build(), + Code.builder().value(998877665544332211L).build(), + Code.builder().value(5566778899001122334L).build() + ); + + testEmployee = Employee.builder() + .login("anepretimov") + .name("Непретимов Александр Евгеньевич") + .photo("https://testImg.jpg") + .position("Тестировщик") + .lastVisit(LocalDateTime.parse("2024-02-12T08:36:00")) + .build(); + + testEmployeeId = employeeRepository.save(testEmployee).getId(); + + // Используем ISO-формат для преобразования LocalDateTime + formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME; + } + + @Test + @DisplayName("логинеться со всеми предзаполненными сотрудниками") + public void testLoginWithPrefilledData() throws Exception { + + for (Employee employee : expectedEmployeesList) { + mockMvc.perform(get("/api/{login}/auth", employee.getLogin())) + .andExpect(status().isOk()) + .andDo(print()); + } + } + + @Test + @DisplayName("вернуть 401 Unauthorized при логининге в несуществующим логином") + public void testLoginWithWrongLogin() throws Exception { + mockMvc.perform(get("/api/{login}/auth", "WrongEmployeeLogin")) + .andExpect(status().isUnauthorized()) + .andDo(print()); + } + + @Test + @DisplayName("работать не только с предзаполненными данными") + public void testLoginWithNewEmployee() throws Exception { + mockMvc.perform(get("/api/{login}/auth", testEmployee.getLogin())) + .andExpect(status().isOk()) + .andDo(print()); + } + + @Test + @DisplayName("получать информацию по логину о всех предзаполненных сотрудниках") + public void testGetInformationByLoginWithPrefilledData() throws Exception { + + for (Employee employee : expectedEmployeesList) { + mockMvc.perform(get("/api/{login}/info", employee.getLogin())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.login").value(employee.getLogin())) + .andExpect(jsonPath("$.name").value(employee.getName())) + .andExpect(jsonPath("$.position").value(employee.getPosition())) + .andExpect(jsonPath("$.photo").value(employee.getPhoto())) + .andExpect(jsonPath("$.lastVisit").value(employee.getLastVisit().format(formatter))) + .andDo(print()); + } + } + + @Test + @DisplayName("получать информацию по логину о всех предзаполненных сотрудниках без учета дат") + public void testGetInformationByLoginWithPrefilledDataExcludeDate() throws Exception { + + for (Employee employee : expectedEmployeesList) { + mockMvc.perform(get("/api/{login}/info", employee.getLogin())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.login").value(employee.getLogin())) + .andExpect(jsonPath("$.name").value(employee.getName())) + .andExpect(jsonPath("$.position").value(employee.getPosition())) + .andExpect(jsonPath("$.photo").value(employee.getPhoto())) + .andDo(print()); + } + } + + @Test + @DisplayName("получать информацию по логину о новых сотрудниках") + public void testGetInformationByLoginWithNewEmployee() throws Exception { + mockMvc.perform(get("/api/{login}/info", testEmployee.getLogin())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.login").value(testEmployee.getLogin())) + .andExpect(jsonPath("$.name").value(testEmployee.getName())) + .andExpect(jsonPath("$.position").value(testEmployee.getPosition())) + .andExpect(jsonPath("$.photo").value(testEmployee.getPhoto())) + .andExpect(jsonPath("$.lastVisit").value(testEmployee.getLastVisit().format(formatter))) + .andDo(print()); + } + + @Test + @DisplayName("получать информацию по логину о новых сотрудниках без учета даты") + public void testGetInformationByLoginWithNewEmployeeExcludeDate() throws Exception { + mockMvc.perform(get("/api/{login}/info", testEmployee.getLogin())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.login").value(testEmployee.getLogin())) + .andExpect(jsonPath("$.name").value(testEmployee.getName())) + .andExpect(jsonPath("$.position").value(testEmployee.getPosition())) + .andExpect(jsonPath("$.photo").value(testEmployee.getPhoto())) + .andDo(print()); + } + + @Test + @DisplayName("вернуть 401 Unauthorized при попытке получить информацию в несуществующим логином") + public void testGetInformationByLoginWithWrongLogin() throws Exception { + mockMvc.perform(get("/api/{login}/auth", "WrongEmployeeLogin")) + .andExpect(status().isUnauthorized()) + .andDo(print()); + } + + @Test + @DisplayName("открыть дверь новому сотруднику, который предоставил верный код") + public void testUpdateLastVisitByLoginOpenNewEmployeeAnyCode() throws Exception { + Code expectedCode = expectedCodeList.stream() + .skip(ThreadLocalRandom.current().nextInt(expectedCodeList.size())) + .findFirst() + .orElseThrow(); + + mockMvc.perform(patch("/api/{login}/open", testEmployee.getLogin()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(expectedCode))) + .andExpect(status().isOk()) + .andDo(print()); + } + + @Test + @DisplayName("открыть дверь каждому предзаполненному пользователю, который предоставил верный код") + public void testUpdateLastVisitByLoginOpenAnyPrefilled() throws Exception { + for (Employee employee : expectedEmployeesList) { + for (Code code : expectedCodeList) { + mockMvc.perform(patch("/api/{login}/open", employee.getLogin()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(code))) + .andExpect(status().isOk()) + .andDo(print()); + } + } + } + + @Test + @DisplayName("открыть дверь новому сотруднику, который предоставил верный код и менять время последнего визита") + public void testUpdateLastVisitByLoginOpenNewEmployeeAndUpdateDate() throws Exception { + Employee expectedEmployee = Employee.builder() + .id(testEmployeeId) + .login("anepretimov") + .name("Непретимов Александр Евгеньевич") + .photo("https://testImg.jpg") + .position("Тестировщик") + .lastVisit(LocalDateTime.parse("2024-03-12T08:36:00")) + .build(); + + Optional actualEmployeeOptional = employeeRepository.findById(testEmployeeId); + actualEmployeeOptional.ifPresent( + employee -> assertThat(testEmployee.getLastVisit()) + .isEqualTo(employee.getLastVisit().format(formatter)) + ); + + Code expectedCode = expectedCodeList.stream() + .skip(ThreadLocalRandom.current().nextInt(expectedCodeList.size())) + .findFirst() + .orElseThrow(); + + mockMvc.perform(patch("/api/{login}/open", expectedEmployee.getLogin()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(expectedCode))) + .andExpect(status().isOk()) + .andDo(print()); + + actualEmployeeOptional = employeeRepository.findById(testEmployeeId); + actualEmployeeOptional.ifPresent( + employee -> assertThat(expectedEmployee.getLastVisit()) + .isNotEqualTo(employee.getLastVisit()) + ); + } + + @Test + @DisplayName("вернуть 400 Bad Request, если код не верный") + public void testUpdateLastVisitByLoginWrongCode() throws Exception { + + Code expectedCode = Code.builder().value(-1L).build(); + + mockMvc.perform(patch("/api/{login}/open", expectedEmployeesList.get(0).getLogin()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(expectedCode))) + .andExpect(status().isBadRequest()) + .andDo(print()); + } + + @Test + @DisplayName("не обновлять визит, если код не верный") + public void testUpdateLastVisitByLoginWrongCodeUpdateData() throws Exception { + + Code expectedCode = Code.builder().value(-1L).build(); + + Employee expectedEmployee = employeeRepository.findAll().stream().findFirst().orElseThrow(); + + mockMvc.perform(patch("/api/{login}/open", expectedEmployee.getLogin()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(expectedCode))) + .andExpect(status().isBadRequest()) + .andDo(print()); + + Employee actualEmployee = employeeRepository.findById(expectedEmployee.getId()).orElseThrow(); + + assertThat(actualEmployee).isEqualTo(expectedEmployee); + } + +}