diff --git a/src/main/java/camp/woowak/lab/vendor/domain/Vendor.java b/src/main/java/camp/woowak/lab/vendor/domain/Vendor.java index 0e39e56d..79dfe7d4 100644 --- a/src/main/java/camp/woowak/lab/vendor/domain/Vendor.java +++ b/src/main/java/camp/woowak/lab/vendor/domain/Vendor.java @@ -1,5 +1,7 @@ package camp.woowak.lab.vendor.domain; +import java.util.UUID; + import camp.woowak.lab.payaccount.domain.PayAccount; import camp.woowak.lab.vendor.exception.InvalidVendorCreationException; import camp.woowak.lab.web.authentication.PasswordEncoder; @@ -16,8 +18,8 @@ @Getter public class Vendor { @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; + @GeneratedValue(strategy = GenerationType.UUID) + private UUID id; @Column(nullable = false, length = 50) private String name; @Column(unique = true, nullable = false, length = 100) @@ -45,7 +47,11 @@ public Vendor(String name, String email, String password, String phone, PayAccou this.payAccount = payAccount; } - public Long getId() { + public UUID getId() { return id; } + + public boolean matches(String password, PasswordEncoder passwordEncoder) { + return passwordEncoder.matches(password, this.password); + } } diff --git a/src/main/java/camp/woowak/lab/vendor/exception/NotFoundVendorException.java b/src/main/java/camp/woowak/lab/vendor/exception/NotFoundVendorException.java new file mode 100644 index 00000000..96decdb6 --- /dev/null +++ b/src/main/java/camp/woowak/lab/vendor/exception/NotFoundVendorException.java @@ -0,0 +1,9 @@ +package camp.woowak.lab.vendor.exception; + +import camp.woowak.lab.common.exception.NotFoundException; + +public class NotFoundVendorException extends NotFoundException { + public NotFoundVendorException() { + super(VendorErrorCode.NOT_FOUND_VENDOR); + } +} diff --git a/src/main/java/camp/woowak/lab/vendor/exception/PasswordMismatchException.java b/src/main/java/camp/woowak/lab/vendor/exception/PasswordMismatchException.java new file mode 100644 index 00000000..08376197 --- /dev/null +++ b/src/main/java/camp/woowak/lab/vendor/exception/PasswordMismatchException.java @@ -0,0 +1,9 @@ +package camp.woowak.lab.vendor.exception; + +import camp.woowak.lab.common.exception.BadRequestException; + +public class PasswordMismatchException extends BadRequestException { + public PasswordMismatchException() { + super(VendorErrorCode.WRONG_PASSWORD); + } +} diff --git a/src/main/java/camp/woowak/lab/vendor/exception/VendorErrorCode.java b/src/main/java/camp/woowak/lab/vendor/exception/VendorErrorCode.java index 8ff33311..895363d9 100644 --- a/src/main/java/camp/woowak/lab/vendor/exception/VendorErrorCode.java +++ b/src/main/java/camp/woowak/lab/vendor/exception/VendorErrorCode.java @@ -14,7 +14,9 @@ public enum VendorErrorCode implements ErrorCode { INVALID_NAME_EMPTY(HttpStatus.BAD_REQUEST, "v1_7", "이름이 입력되지 않았습니다."), INVALID_NAME_RANGE(HttpStatus.BAD_REQUEST, "v1_8", "이름은 50자를 넘을 수 없습니다."), INVALID_PAY_ACCOUNT_EMPTY(HttpStatus.BAD_REQUEST, "v_1_9", "포인트 계좌가 입력되지 않았습니다."), - DUPLICATE_EMAIL(HttpStatus.BAD_REQUEST, "v_2", "이미 가입된 이메일입니다."); + DUPLICATE_EMAIL(HttpStatus.BAD_REQUEST, "v_2", "이미 가입된 이메일입니다."), + NOT_FOUND_VENDOR(HttpStatus.BAD_REQUEST, "v3", "존재하지 않는 점주입니다."), + WRONG_PASSWORD(HttpStatus.BAD_REQUEST, "v4_1", "잘못된 비밀번호입니다."); private final int status; private final String errorCode; diff --git a/src/main/java/camp/woowak/lab/vendor/repository/VendorRepository.java b/src/main/java/camp/woowak/lab/vendor/repository/VendorRepository.java index 1d1d6f51..a144fc41 100644 --- a/src/main/java/camp/woowak/lab/vendor/repository/VendorRepository.java +++ b/src/main/java/camp/woowak/lab/vendor/repository/VendorRepository.java @@ -1,8 +1,17 @@ package camp.woowak.lab.vendor.repository; +import java.util.Optional; +import java.util.UUID; + import org.springframework.data.jpa.repository.JpaRepository; import camp.woowak.lab.vendor.domain.Vendor; +import camp.woowak.lab.vendor.exception.NotFoundVendorException; + +public interface VendorRepository extends JpaRepository { + Optional findByEmail(String email); -public interface VendorRepository extends JpaRepository { + default Vendor findByEmailOrThrow(String email) { + return findByEmail(email).orElseThrow(NotFoundVendorException::new); + } } \ No newline at end of file diff --git a/src/main/java/camp/woowak/lab/vendor/service/SignInVendorService.java b/src/main/java/camp/woowak/lab/vendor/service/SignInVendorService.java new file mode 100644 index 00000000..c7b262ae --- /dev/null +++ b/src/main/java/camp/woowak/lab/vendor/service/SignInVendorService.java @@ -0,0 +1,35 @@ +package camp.woowak.lab.vendor.service; + +import java.util.UUID; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import camp.woowak.lab.vendor.domain.Vendor; +import camp.woowak.lab.vendor.exception.PasswordMismatchException; +import camp.woowak.lab.vendor.repository.VendorRepository; +import camp.woowak.lab.vendor.service.command.SignInVendorCommand; +import camp.woowak.lab.web.authentication.PasswordEncoder; + +@Service +@Transactional(readOnly = true) +public class SignInVendorService { + private final VendorRepository repository; + private final PasswordEncoder passwordEncoder; + + public SignInVendorService(VendorRepository repository, PasswordEncoder passwordEncoder) { + this.repository = repository; + this.passwordEncoder = passwordEncoder; + } + + /** + * @throws PasswordMismatchException 비밀번호가 일치하지 않으면 + */ + public UUID signIn(SignInVendorCommand cmd) { + Vendor findVendor = repository.findByEmailOrThrow(cmd.email()); + if (!findVendor.matches(cmd.password(), passwordEncoder)) { + throw new PasswordMismatchException(); + } + return findVendor.getId(); + } +} diff --git a/src/main/java/camp/woowak/lab/vendor/service/SignUpVendorService.java b/src/main/java/camp/woowak/lab/vendor/service/SignUpVendorService.java index 13b9e30a..07e5343c 100644 --- a/src/main/java/camp/woowak/lab/vendor/service/SignUpVendorService.java +++ b/src/main/java/camp/woowak/lab/vendor/service/SignUpVendorService.java @@ -26,16 +26,16 @@ public SignUpVendorService( this.passwordEncoder = passwordEncoder; } - public Long signUp(SignUpVendorCommand cmd) { + public String signUp(SignUpVendorCommand cmd) { PayAccount newPayAccount = new PayAccount(); payAccountRepository.save(newPayAccount); - Vendor newVendor = - new Vendor(cmd.name(), cmd.email(), cmd.password(), cmd.phone(), newPayAccount, passwordEncoder); + Vendor savedVendor; try { - vendorRepository.save(newVendor); + savedVendor = vendorRepository.saveAndFlush( + new Vendor(cmd.name(), cmd.email(), cmd.password(), cmd.phone(), newPayAccount, passwordEncoder)); } catch (DataIntegrityViolationException e) { throw new DuplicateEmailException(); } - return newVendor.getId(); + return savedVendor.getId().toString(); } } diff --git a/src/main/java/camp/woowak/lab/vendor/service/command/SignInVendorCommand.java b/src/main/java/camp/woowak/lab/vendor/service/command/SignInVendorCommand.java new file mode 100644 index 00000000..e63dbed6 --- /dev/null +++ b/src/main/java/camp/woowak/lab/vendor/service/command/SignInVendorCommand.java @@ -0,0 +1,7 @@ +package camp.woowak.lab.vendor.service.command; + +public record SignInVendorCommand( + String email, + String password +) { +} diff --git a/src/main/java/camp/woowak/lab/web/api/vendor/VendorApiController.java b/src/main/java/camp/woowak/lab/web/api/vendor/VendorApiController.java index 492217cc..5f37f6ae 100644 --- a/src/main/java/camp/woowak/lab/web/api/vendor/VendorApiController.java +++ b/src/main/java/camp/woowak/lab/web/api/vendor/VendorApiController.java @@ -1,23 +1,34 @@ package camp.woowak.lab.web.api.vendor; +import java.util.UUID; + import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; +import camp.woowak.lab.vendor.service.SignInVendorService; import camp.woowak.lab.vendor.service.SignUpVendorService; +import camp.woowak.lab.vendor.service.command.SignInVendorCommand; import camp.woowak.lab.vendor.service.command.SignUpVendorCommand; +import camp.woowak.lab.web.authentication.LoginVendor; +import camp.woowak.lab.web.dto.request.vendor.SignInVendorRequest; import camp.woowak.lab.web.dto.request.vendor.SignUpVendorRequest; +import camp.woowak.lab.web.dto.response.vendor.SignInVendorResponse; import camp.woowak.lab.web.dto.response.vendor.SignUpVendorResponse; +import camp.woowak.lab.web.resolver.session.SessionConst; +import jakarta.servlet.http.HttpSession; import jakarta.validation.Valid; @RestController public class VendorApiController { private final SignUpVendorService signUpVendorService; + private final SignInVendorService signInVendorService; - public VendorApiController(SignUpVendorService signUpVendorService) { + public VendorApiController(SignUpVendorService signUpVendorService, SignInVendorService signInVendorService) { this.signUpVendorService = signUpVendorService; + this.signInVendorService = signInVendorService; } @PostMapping("/vendors") @@ -26,7 +37,16 @@ public SignUpVendorResponse signUpVendor(@Valid @RequestBody SignUpVendorRequest SignUpVendorCommand command = new SignUpVendorCommand(request.name(), request.email(), request.password(), request.phone()); - Long registeredId = signUpVendorService.signUp(command); + String registeredId = signUpVendorService.signUp(command); return new SignUpVendorResponse(registeredId); } + + @PostMapping("/vendors/login") + @ResponseStatus(HttpStatus.NO_CONTENT) + public SignInVendorResponse login(@Valid @RequestBody SignInVendorRequest request, HttpSession session) { + SignInVendorCommand command = new SignInVendorCommand(request.email(), request.password()); + UUID vendorId = signInVendorService.signIn(command); + session.setAttribute(SessionConst.SESSION_VENDOR_KEY, new LoginVendor(vendorId)); + return new SignInVendorResponse("success"); + } } diff --git a/src/main/java/camp/woowak/lab/web/api/vendor/VendorApiControllerAdvice.java b/src/main/java/camp/woowak/lab/web/api/vendor/VendorApiControllerAdvice.java index 1e328a3f..9f72fe1d 100644 --- a/src/main/java/camp/woowak/lab/web/api/vendor/VendorApiControllerAdvice.java +++ b/src/main/java/camp/woowak/lab/web/api/vendor/VendorApiControllerAdvice.java @@ -8,11 +8,23 @@ import camp.woowak.lab.common.advice.DomainExceptionHandler; import camp.woowak.lab.vendor.exception.DuplicateEmailException; import camp.woowak.lab.vendor.exception.InvalidVendorCreationException; +import camp.woowak.lab.vendor.exception.NotFoundVendorException; +import camp.woowak.lab.vendor.exception.PasswordMismatchException; import lombok.extern.slf4j.Slf4j; @Slf4j @DomainExceptionHandler(basePackageClasses = VendorApiController.class) public class VendorApiControllerAdvice { + @ExceptionHandler(PasswordMismatchException.class) + public ResponseEntity handlePasswordMismatchException(PasswordMismatchException ex) { + return ResponseEntity.of(ProblemDetail.forStatusAndDetail(HttpStatus.BAD_REQUEST, ex.getMessage())).build(); + } + + @ExceptionHandler(NotFoundVendorException.class) + public ResponseEntity handleNotFoundVendorException(NotFoundVendorException ex) { + return ResponseEntity.of(ProblemDetail.forStatusAndDetail(HttpStatus.BAD_REQUEST, ex.getMessage())).build(); + } + @ExceptionHandler(InvalidVendorCreationException.class) public ResponseEntity handleInvalidVendorCreationException(InvalidVendorCreationException ex) { return ResponseEntity.of(ProblemDetail.forStatusAndDetail(HttpStatus.BAD_REQUEST, ex.getMessage())).build(); diff --git a/src/main/java/camp/woowak/lab/web/authentication/LoginMember.java b/src/main/java/camp/woowak/lab/web/authentication/LoginMember.java index 5079e9b8..27cffece 100644 --- a/src/main/java/camp/woowak/lab/web/authentication/LoginMember.java +++ b/src/main/java/camp/woowak/lab/web/authentication/LoginMember.java @@ -1,5 +1,5 @@ package camp.woowak.lab.web.authentication; public interface LoginMember { - Long getId(); + Object getId(); } diff --git a/src/main/java/camp/woowak/lab/web/authentication/LoginVendor.java b/src/main/java/camp/woowak/lab/web/authentication/LoginVendor.java index 4ac97539..c4ad20a3 100644 --- a/src/main/java/camp/woowak/lab/web/authentication/LoginVendor.java +++ b/src/main/java/camp/woowak/lab/web/authentication/LoginVendor.java @@ -1,14 +1,16 @@ package camp.woowak.lab.web.authentication; +import java.util.UUID; + public class LoginVendor implements LoginMember { - private final Long id; + private final UUID id; - public LoginVendor(Long id) { + public LoginVendor(UUID id) { this.id = id; } @Override - public Long getId() { + public UUID getId() { return id; } } diff --git a/src/main/java/camp/woowak/lab/web/dto/request/vendor/SignInVendorRequest.java b/src/main/java/camp/woowak/lab/web/dto/request/vendor/SignInVendorRequest.java new file mode 100644 index 00000000..fb5e3fa8 --- /dev/null +++ b/src/main/java/camp/woowak/lab/web/dto/request/vendor/SignInVendorRequest.java @@ -0,0 +1,14 @@ +package camp.woowak.lab.web.dto.request.vendor; + +import org.hibernate.validator.constraints.Length; + +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; + +public record SignInVendorRequest( + @NotBlank @Email + String email, + @NotBlank @Length(min = 8, max = 30) + String password +) { +} diff --git a/src/main/java/camp/woowak/lab/web/dto/response/vendor/SignInVendorResponse.java b/src/main/java/camp/woowak/lab/web/dto/response/vendor/SignInVendorResponse.java new file mode 100644 index 00000000..36a69963 --- /dev/null +++ b/src/main/java/camp/woowak/lab/web/dto/response/vendor/SignInVendorResponse.java @@ -0,0 +1,6 @@ +package camp.woowak.lab.web.dto.response.vendor; + +public record SignInVendorResponse( + String message +) { +} diff --git a/src/main/java/camp/woowak/lab/web/dto/response/vendor/SignUpVendorResponse.java b/src/main/java/camp/woowak/lab/web/dto/response/vendor/SignUpVendorResponse.java index 48216659..74d73329 100644 --- a/src/main/java/camp/woowak/lab/web/dto/response/vendor/SignUpVendorResponse.java +++ b/src/main/java/camp/woowak/lab/web/dto/response/vendor/SignUpVendorResponse.java @@ -1,6 +1,6 @@ package camp.woowak.lab.web.dto.response.vendor; public record SignUpVendorResponse( - Long id + String id ) { } diff --git a/src/test/java/camp/woowak/lab/fixture/VendorFixture.java b/src/test/java/camp/woowak/lab/fixture/VendorFixture.java index ccf7063d..cb5f1850 100644 --- a/src/test/java/camp/woowak/lab/fixture/VendorFixture.java +++ b/src/test/java/camp/woowak/lab/fixture/VendorFixture.java @@ -1,6 +1,9 @@ package camp.woowak.lab.fixture; +import java.util.UUID; + import camp.woowak.lab.payaccount.domain.PayAccount; +import camp.woowak.lab.vendor.TestVendor; import camp.woowak.lab.vendor.domain.Vendor; import camp.woowak.lab.web.authentication.PasswordEncoder; @@ -9,6 +12,12 @@ default PayAccount createPayAccount() { return new PayAccount(); } + default Vendor createSavedVendor(UUID id, PayAccount payAccount, PasswordEncoder passwordEncoder) { + return new TestVendor( + id, "vendorName", "vendorEmail@example.com", "vendorPassword", "010-0000-0000", payAccount, + passwordEncoder); + } + default Vendor createVendor(PayAccount payAccount, PasswordEncoder passwordEncoder) { return new Vendor("vendorName", "vendorEmail@example.com", "vendorPassword", "010-0000-0000", payAccount, passwordEncoder); diff --git a/src/test/java/camp/woowak/lab/vendor/TestVendor.java b/src/test/java/camp/woowak/lab/vendor/TestVendor.java new file mode 100644 index 00000000..a913ab98 --- /dev/null +++ b/src/test/java/camp/woowak/lab/vendor/TestVendor.java @@ -0,0 +1,21 @@ +package camp.woowak.lab.vendor; + +import java.util.UUID; + +import camp.woowak.lab.payaccount.domain.PayAccount; +import camp.woowak.lab.vendor.domain.Vendor; +import camp.woowak.lab.web.authentication.PasswordEncoder; + +public class TestVendor extends Vendor { + private UUID id; + + public TestVendor(UUID id, String name, String email, String password, String phone, PayAccount payAccount, + PasswordEncoder passwordEncoder) { + super(name, email, password, phone, payAccount, passwordEncoder); + this.id = id; + } + + public UUID getId() { + return id; + } +} diff --git a/src/test/java/camp/woowak/lab/vendor/domain/VendorTest.java b/src/test/java/camp/woowak/lab/vendor/domain/VendorTest.java index ad89b05e..bd48189f 100644 --- a/src/test/java/camp/woowak/lab/vendor/domain/VendorTest.java +++ b/src/test/java/camp/woowak/lab/vendor/domain/VendorTest.java @@ -1,18 +1,21 @@ package camp.woowak.lab.vendor.domain; +import java.util.UUID; + import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import camp.woowak.lab.fixture.VendorFixture; import camp.woowak.lab.payaccount.domain.PayAccount; import camp.woowak.lab.payaccount.domain.TestPayAccount; import camp.woowak.lab.vendor.exception.InvalidVendorCreationException; import camp.woowak.lab.web.authentication.NoOpPasswordEncoder; import camp.woowak.lab.web.authentication.PasswordEncoder; -class VendorTest { +class VendorTest implements VendorFixture { private PayAccount payAccount; private PasswordEncoder passwordEncoder; @@ -209,4 +212,40 @@ void failWithNull() { } } } + + @Nested + @DisplayName("비밀번화 확인은") + class Matches { + @Test + @DisplayName("[성공] 비밀번호가 일치하면 true를 반환한다.") + void return_true() { + // given + Vendor savedVendor = createSavedVendor(UUID.randomUUID(), createPayAccount(), passwordEncoder); + + // when + boolean matches = savedVendor.matches("vendorPassword", passwordEncoder); + + // then + Assertions.assertTrue(matches); + } + + @Test + @DisplayName("[예외] 비밀번호가 불일치하면 false를 반환한다.") + void return_false() { + // given + Vendor savedVendor = createSavedVendor(UUID.randomUUID(), createPayAccount(), passwordEncoder); + + // when + boolean matches = savedVendor.matches("something_wrong", passwordEncoder); + + // then + Assertions.assertFalse(matches); + } + + @Test + void test() { + boolean matches = passwordEncoder.matches("vendorPassword", "vendorPassword"); + Assertions.assertTrue(matches); + } + } } diff --git a/src/test/java/camp/woowak/lab/vendor/repository/VendorRepositoryTest.java b/src/test/java/camp/woowak/lab/vendor/repository/VendorRepositoryTest.java index 173fb485..b68756f4 100644 --- a/src/test/java/camp/woowak/lab/vendor/repository/VendorRepositoryTest.java +++ b/src/test/java/camp/woowak/lab/vendor/repository/VendorRepositoryTest.java @@ -1,6 +1,7 @@ package camp.woowak.lab.vendor.repository; import java.util.Optional; +import java.util.UUID; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; @@ -54,7 +55,7 @@ void success() { // when Vendor vendor = createVendor(payAccount, passwordEncoder); Vendor savedVendor = vendorRepository.save(vendor); - Long savedVendorId = savedVendor.getId(); + UUID savedVendorId = savedVendor.getId(); vendorRepository.flush(); // then diff --git a/src/test/java/camp/woowak/lab/vendor/service/SignInVendorServiceTest.java b/src/test/java/camp/woowak/lab/vendor/service/SignInVendorServiceTest.java new file mode 100644 index 00000000..489ca33d --- /dev/null +++ b/src/test/java/camp/woowak/lab/vendor/service/SignInVendorServiceTest.java @@ -0,0 +1,82 @@ +package camp.woowak.lab.vendor.service; + +import static org.mockito.BDDMockito.*; + +import java.util.UUID; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import camp.woowak.lab.fixture.VendorFixture; +import camp.woowak.lab.payaccount.domain.PayAccount; +import camp.woowak.lab.vendor.domain.Vendor; +import camp.woowak.lab.vendor.exception.DuplicateEmailException; +import camp.woowak.lab.vendor.exception.NotFoundVendorException; +import camp.woowak.lab.vendor.exception.PasswordMismatchException; +import camp.woowak.lab.vendor.repository.VendorRepository; +import camp.woowak.lab.vendor.service.command.SignInVendorCommand; +import camp.woowak.lab.web.authentication.NoOpPasswordEncoder; +import camp.woowak.lab.web.authentication.PasswordEncoder; + +@ExtendWith(MockitoExtension.class) +class SignInVendorServiceTest implements VendorFixture { + @InjectMocks + private SignInVendorService service; + @Mock + private VendorRepository vendorRepository; + @Mock + private PasswordEncoder passwordEncoder; + + @Test + @DisplayName("[성공] 비밀번호가 일치하면 id를 반환한다.") + void success() throws DuplicateEmailException { + // given + PayAccount payAccount = createPayAccount(); + UUID fakeVendorId = UUID.randomUUID(); + Vendor vendor = createSavedVendor(fakeVendorId, payAccount, new NoOpPasswordEncoder()); + given(vendorRepository.findByEmailOrThrow(anyString())).willReturn(vendor); + given(passwordEncoder.matches(anyString(), anyString())).willReturn(true); + + // when + SignInVendorCommand command = new SignInVendorCommand("vendorEmail@email.com", "validPassword"); + UUID id = service.signIn(command); + + // then + Assertions.assertEquals(vendor.getId(), id); + } + + @Test + @DisplayName("[예외] 존재하지 않는 Vendor면 NotFoundVendorException 발생") + void failWithNotFound() throws DuplicateEmailException { + // given + given(vendorRepository.findByEmailOrThrow(anyString())).willThrow(NotFoundVendorException.class); + + // when + SignInVendorCommand command = new SignInVendorCommand("notExists@email.com", "validPassword"); + + // then + Assertions.assertThrows(NotFoundVendorException.class, () -> service.signIn(command)); + } + + @Test + @DisplayName("[예외] 비밀번호가 불일치하면 PasswordMismatchException 발생") + void failWithPasswordMismatch() throws DuplicateEmailException { + // given + PayAccount payAccount = createPayAccount(); + UUID fakeVendorId = UUID.randomUUID(); + Vendor vendor = createSavedVendor(fakeVendorId, payAccount, new NoOpPasswordEncoder()); + given(vendorRepository.findByEmailOrThrow(anyString())).willReturn(vendor); + given(passwordEncoder.matches(anyString(), anyString())).willReturn(false); + + // when + SignInVendorCommand command = new SignInVendorCommand("vendorEmail@email.com", "validPassword"); + + // then + Assertions.assertThrows(PasswordMismatchException.class, () -> service.signIn(command)); + } +} \ No newline at end of file diff --git a/src/test/java/camp/woowak/lab/vendor/service/SignUpVendorServiceTest.java b/src/test/java/camp/woowak/lab/vendor/service/SignUpVendorServiceTest.java index daeb2398..0bd77a5d 100644 --- a/src/test/java/camp/woowak/lab/vendor/service/SignUpVendorServiceTest.java +++ b/src/test/java/camp/woowak/lab/vendor/service/SignUpVendorServiceTest.java @@ -2,6 +2,8 @@ import static org.mockito.BDDMockito.*; +import java.util.UUID; + import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -37,20 +39,21 @@ class SignUpVendorServiceTest implements VendorFixture { @DisplayName("[성공] Vendor가 저장된다.") void success() throws DuplicateEmailException { // given - given(passwordEncoder.encode(Mockito.anyString())).willReturn("password"); PayAccount payAccount = createPayAccount(); - Vendor vendor = createVendor(payAccount, new NoOpPasswordEncoder()); - given(payAccountRepository.save(Mockito.any(PayAccount.class))).willReturn(payAccount); - given(vendorRepository.save(Mockito.any(Vendor.class))).willReturn(vendor); + UUID fakeVendorId = UUID.randomUUID(); + Vendor vendor = createSavedVendor(fakeVendorId, payAccount, new NoOpPasswordEncoder()); + given(passwordEncoder.encode(Mockito.anyString())).willReturn("password"); // when + when(payAccountRepository.save(Mockito.any(PayAccount.class))).thenReturn(payAccount); + when(vendorRepository.saveAndFlush(Mockito.any(Vendor.class))).thenReturn(vendor); + + // then SignUpVendorCommand command = new SignUpVendorCommand("vendorName", "vendorEmail@example.com", "password", "010-0000-0000"); service.signUp(command); - - // then then(payAccountRepository).should().save(Mockito.any(PayAccount.class)); - then(vendorRepository).should().save(Mockito.any(Vendor.class)); + then(vendorRepository).should().saveAndFlush(Mockito.any(Vendor.class)); } @Test @@ -58,16 +61,16 @@ void success() throws DuplicateEmailException { void failWithDuplicateEmail() throws DuplicateEmailException { // given given(passwordEncoder.encode(Mockito.anyString())).willReturn("password"); - given(payAccountRepository.save(Mockito.any(PayAccount.class))).willReturn(createPayAccount()); // when - when(vendorRepository.save(Mockito.any(Vendor.class))).thenThrow(DataIntegrityViolationException.class); - SignUpVendorCommand command = - new SignUpVendorCommand("vendorName", "vendorEmail@example.com", "password", "010-0000-0000"); + when(payAccountRepository.save(Mockito.any(PayAccount.class))).thenReturn(createPayAccount()); + when(vendorRepository.saveAndFlush(Mockito.any(Vendor.class))).thenThrow(DataIntegrityViolationException.class); // then + SignUpVendorCommand command = + new SignUpVendorCommand("vendorName", "vendorEmail@example.com", "password", "010-0000-0000"); Assertions.assertThrows(DuplicateEmailException.class, () -> service.signUp(command)); then(payAccountRepository).should().save(Mockito.any(PayAccount.class)); - then(vendorRepository).should().save(Mockito.any(Vendor.class)); + then(vendorRepository).should().saveAndFlush(Mockito.any(Vendor.class)); } } diff --git a/src/test/java/camp/woowak/lab/web/api/VendorApiControllerTest.java b/src/test/java/camp/woowak/lab/web/api/VendorApiControllerTest.java index b6083049..139a6a0e 100644 --- a/src/test/java/camp/woowak/lab/web/api/VendorApiControllerTest.java +++ b/src/test/java/camp/woowak/lab/web/api/VendorApiControllerTest.java @@ -4,8 +4,9 @@ import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; -import java.util.Random; +import java.util.UUID; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -22,10 +23,18 @@ import com.fasterxml.jackson.databind.ObjectMapper; import camp.woowak.lab.vendor.exception.DuplicateEmailException; +import camp.woowak.lab.vendor.exception.NotFoundVendorException; +import camp.woowak.lab.vendor.exception.PasswordMismatchException; +import camp.woowak.lab.vendor.service.SignInVendorService; import camp.woowak.lab.vendor.service.SignUpVendorService; +import camp.woowak.lab.vendor.service.command.SignInVendorCommand; import camp.woowak.lab.vendor.service.command.SignUpVendorCommand; import camp.woowak.lab.web.api.vendor.VendorApiController; +import camp.woowak.lab.web.authentication.LoginVendor; +import camp.woowak.lab.web.dto.request.vendor.SignInVendorRequest; import camp.woowak.lab.web.dto.request.vendor.SignUpVendorRequest; +import camp.woowak.lab.web.resolver.session.SessionConst; +import jakarta.servlet.http.HttpSession; @WebMvcTest(controllers = VendorApiController.class) @MockBean(JpaMetamodelMappingContext.class) @@ -34,6 +43,8 @@ class VendorApiControllerTest { private MockMvc mockMvc; @MockBean private SignUpVendorService signUpVendorService; + @MockBean + private SignInVendorService signInVendorService; @Nested @DisplayName("판매자 회원가입: POST /vendors") @@ -41,9 +52,9 @@ class SignUpVendor { @Test @DisplayName("[성공] 201") void success() throws Exception { - long fakeVendorId = new Random().nextLong(1000L); + UUID fakeVendorId = UUID.randomUUID(); BDDMockito.given(signUpVendorService.signUp(BDDMockito.any(SignUpVendorCommand.class))) - .willReturn(fakeVendorId); + .willReturn(fakeVendorId.toString()); // when ResultActions actions = mockMvc.perform( @@ -58,7 +69,7 @@ void success() throws Exception { // then actions.andExpect(status().isCreated()) .andExpect(jsonPath("$.status").value(HttpStatus.CREATED.value())) - .andExpect(jsonPath("$.data.id").value(fakeVendorId)) + .andExpect(jsonPath("$.data.id").value(fakeVendorId.toString())) .andDo(print()); } @@ -71,9 +82,9 @@ class NameMust { @Test @DisplayName("비어있는 경우") void failWithEmptyName() throws Exception { - long fakeVendorId = new Random().nextLong(1000L); + UUID fakeVendorId = UUID.randomUUID(); BDDMockito.given(signUpVendorService.signUp(BDDMockito.any(SignUpVendorCommand.class))) - .willReturn(fakeVendorId); + .willReturn(fakeVendorId.toString()); // when ResultActions actions = mockMvc.perform( @@ -97,9 +108,9 @@ void failWithEmptyName() throws Exception { @Test @DisplayName("공란인 경우") void failWithBlankName() throws Exception { - long fakeVendorId = new Random().nextLong(1000L); + UUID fakeVendorId = UUID.randomUUID(); BDDMockito.given(signUpVendorService.signUp(BDDMockito.any(SignUpVendorCommand.class))) - .willReturn(fakeVendorId); + .willReturn(fakeVendorId.toString()); // when ResultActions actions = mockMvc.perform( @@ -127,9 +138,9 @@ class EmailMust { @Test @DisplayName("비어있는 경우") void failWithEmptyEmail() throws Exception { - long fakeVendorId = new Random().nextLong(1000L); + UUID fakeVendorId = UUID.randomUUID(); BDDMockito.given(signUpVendorService.signUp(BDDMockito.any(SignUpVendorCommand.class))) - .willReturn(fakeVendorId); + .willReturn(fakeVendorId.toString()); // when ResultActions actions = mockMvc.perform( @@ -152,9 +163,9 @@ void failWithEmptyEmail() throws Exception { @Test @DisplayName("공란인 경우") void failWithBlankEmail() throws Exception { - long fakeVendorId = new Random().nextLong(1000L); + UUID fakeVendorId = UUID.randomUUID(); BDDMockito.given(signUpVendorService.signUp(BDDMockito.any(SignUpVendorCommand.class))) - .willReturn(fakeVendorId); + .willReturn(fakeVendorId.toString()); // when ResultActions actions = mockMvc.perform( @@ -181,9 +192,9 @@ class PasswordMust { @Test @DisplayName("비어있는 경우") void failWithEmptyPassword() throws Exception { - long fakeVendorId = new Random().nextLong(1000L); + UUID fakeVendorId = UUID.randomUUID(); BDDMockito.given(signUpVendorService.signUp(BDDMockito.any(SignUpVendorCommand.class))) - .willReturn(fakeVendorId); + .willReturn(fakeVendorId.toString()); // when ResultActions actions = mockMvc.perform( @@ -207,9 +218,9 @@ void failWithEmptyPassword() throws Exception { @Test @DisplayName("공란인 경우") void failWithBlankPassword() throws Exception { - long fakeVendorId = new Random().nextLong(1000L); + UUID fakeVendorId = UUID.randomUUID(); BDDMockito.given(signUpVendorService.signUp(BDDMockito.any(SignUpVendorCommand.class))) - .willReturn(fakeVendorId); + .willReturn(fakeVendorId.toString()); // when ResultActions actions = mockMvc.perform( @@ -233,9 +244,9 @@ void failWithBlankPassword() throws Exception { @Test @DisplayName("8자 미만인 경우") void failWith7Password() throws Exception { - long fakeVendorId = new Random().nextLong(1000L); + UUID fakeVendorId = UUID.randomUUID(); BDDMockito.given(signUpVendorService.signUp(BDDMockito.any(SignUpVendorCommand.class))) - .willReturn(fakeVendorId); + .willReturn(fakeVendorId.toString()); // when ResultActions actions = mockMvc.perform( @@ -259,9 +270,9 @@ void failWith7Password() throws Exception { @Test @DisplayName("30자 초과인 경우") void failWith31Password() throws Exception { - long fakeVendorId = new Random().nextLong(1000L); + UUID fakeVendorId = UUID.randomUUID(); BDDMockito.given(signUpVendorService.signUp(BDDMockito.any(SignUpVendorCommand.class))) - .willReturn(fakeVendorId); + .willReturn(fakeVendorId.toString()); // when ResultActions actions = mockMvc.perform( @@ -290,9 +301,9 @@ class PhoneMust { @Test @DisplayName("비어있는 경우") void failWithEmptyPhone() throws Exception { - long fakeVendorId = new Random().nextLong(1000L); + UUID fakeVendorId = UUID.randomUUID(); BDDMockito.given(signUpVendorService.signUp(BDDMockito.any(SignUpVendorCommand.class))) - .willReturn(fakeVendorId); + .willReturn(fakeVendorId.toString()); // when ResultActions actions = mockMvc.perform( @@ -316,9 +327,9 @@ void failWithEmptyPhone() throws Exception { @Test @DisplayName("공란인 경우") void failWithBlankPhone() throws Exception { - long fakeVendorId = new Random().nextLong(1000L); + UUID fakeVendorId = UUID.randomUUID(); BDDMockito.given(signUpVendorService.signUp(BDDMockito.any(SignUpVendorCommand.class))) - .willReturn(fakeVendorId); + .willReturn(fakeVendorId.toString()); // when ResultActions actions = mockMvc.perform( @@ -341,9 +352,9 @@ void failWithBlankPhone() throws Exception { @Test @DisplayName("잘못된 형식인 경우") void failWithInvalidPhone() throws Exception { - long fakeVendorId = new Random().nextLong(1000L); + UUID fakeVendorId = UUID.randomUUID(); BDDMockito.given(signUpVendorService.signUp(BDDMockito.any(SignUpVendorCommand.class))) - .willReturn(fakeVendorId); + .willReturn(fakeVendorId.toString()); // when ResultActions actions = mockMvc.perform( @@ -391,4 +402,80 @@ void failWithDuplicateEmail() throws Exception { .andDo(print()); } } + + @Nested + @DisplayName("판매자 로그인: POST /vendors/login") + class SignInVendor { + @Test + @DisplayName("[성공] 204") + void success() throws Exception { + UUID fakeVendorId = UUID.randomUUID(); + BDDMockito.given(signInVendorService.signIn(BDDMockito.any(SignInVendorCommand.class))) + .willReturn(fakeVendorId); + + // when + ResultActions actions = mockMvc.perform( + post("/vendors/login") + .content(new ObjectMapper().writeValueAsString( + new SignInVendorRequest("validEmail@validEmail.com", "validPassword"))) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + ); + + // then + actions.andExpect(status().isNoContent()) + .andExpect(jsonPath("$.status").value(HttpStatus.NO_CONTENT.value())) + .andExpect(result -> { + HttpSession session = result.getRequest().getSession(); + LoginVendor loginVendor = (LoginVendor)session.getAttribute(SessionConst.SESSION_VENDOR_KEY); + Assertions.assertNotNull(loginVendor); + Assertions.assertEquals(loginVendor.getId(), fakeVendorId); + }) + .andDo(print()); + } + + @Test + @DisplayName("[실패] 400 : 존재하지 않는 판매자 이메일") + void failWithNotFoundVendor() throws Exception { + UUID fakeVendorId = UUID.randomUUID(); + BDDMockito.given(signInVendorService.signIn(BDDMockito.any(SignInVendorCommand.class))) + .willThrow(NotFoundVendorException.class); + + // when + ResultActions actions = mockMvc.perform( + post("/vendors/login") + .content(new ObjectMapper().writeValueAsString( + new SignInVendorRequest("notFound@validEmail.com", "validPassword"))) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + ); + + // then + actions.andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value())) + .andDo(print()); + } + + @Test + @DisplayName("[실패] 400 : 잘못된 비밀번호") + void failWithPasswordMismatch() throws Exception { + UUID fakeVendorId = UUID.randomUUID(); + BDDMockito.given(signInVendorService.signIn(BDDMockito.any(SignInVendorCommand.class))) + .willThrow(PasswordMismatchException.class); + + // when + ResultActions actions = mockMvc.perform( + post("/vendors/login") + .content(new ObjectMapper().writeValueAsString( + new SignInVendorRequest("validEmail@validEmail.com", "wrongPassword"))) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + ); + + // then + actions.andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value())) + .andDo(print()); + } + } } diff --git a/src/test/java/camp/woowak/lab/web/api/store/StoreApiControllerTest.java b/src/test/java/camp/woowak/lab/web/api/store/StoreApiControllerTest.java index f4eb004f..473978fd 100644 --- a/src/test/java/camp/woowak/lab/web/api/store/StoreApiControllerTest.java +++ b/src/test/java/camp/woowak/lab/web/api/store/StoreApiControllerTest.java @@ -7,6 +7,7 @@ import java.time.LocalDateTime; import java.util.Optional; +import java.util.UUID; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; @@ -76,7 +77,7 @@ void setUp() { void storeRegistrationSuccess() throws Exception { // given Vendor vendor = createVendor(); - LoginVendor loginVendor = new LoginVendor(5L); + LoginVendor loginVendor = new LoginVendor(UUID.randomUUID()); StoreRegistrationRequest request = new StoreRegistrationRequest( "Store Name", "Store Address", @@ -109,7 +110,7 @@ void storeRegistrationSuccess() throws Exception { void storeRegistrationFailure() throws Exception { // given Vendor vendor = createVendor(); - LoginVendor loginVendor = new LoginVendor(5L); + LoginVendor loginVendor = new LoginVendor(UUID.randomUUID()); StoreRegistrationRequest request = new StoreRegistrationRequest( "Store Name", "Invalid Category",