generated from pricelees/issue-pr-template
<!-- 제목 양식 --> <!-- [이슈번호] 작업 요약 (예시: [#10] Gitea 템플릿 생성) --> ## 📝 관련 이슈 및 PR **PR과 관련된 이슈 번호** - #3 ## ✨ 작업 내용 <!-- 어떤 작업을 했는지 알려주세요! --> ### 0. 공통 - 패키지 구조 수정(web, business, infrastructure 구조) - Swagger-UI 어노테이션을 별도의 인터페이스를 만들어 컨트롤러에서 분리 - 결합도 높은 클래스 통합(ex: Member 엔티티와 Role enum 등) ### 1. 회원 도메인 - 기능 자체가 적어서 변화된 내용이 크게 없음. 패키지 구조 수정과 클래스 통합 정도의 과정이 대부분이었음. ### 2. 인증 도메인 - 전체적으로 코드 중복이 많아, 확장함수 및 클래스 통합으로 중복 코드를 상당히 많이 제거하였음. - JwtHandler와 Interceptor에서 모두 이뤄지던 null 예외 처리를 JwtHandler에서만 처리하도록 수정 ## 🧪 테스트 <!-- 어떤 테스트를 생각했고 진행했는지 알려주세요! --> - 모든 테스트는 Kotest 기반으로 수정 & 로그인 및 토큰 처리가 필요한 API 테스트는 mocking을 활용하도록 수정 - 향후 테스트도 꼭 필요하다고 느껴지는 테스트가 아니라면 DB 사용보다는 mocking을 활용할 예정 ## 📚 참고 자료 및 기타 <!-- 참고한 자료, 또는 논의할 사항이 있다면 알려주세요! --> Reviewed-on: #4 Co-authored-by: pricelees <priceelees@gmail.com> Co-committed-by: pricelees <priceelees@gmail.com>
This commit is contained in:
parent
7121d36523
commit
22e6ad4f71
43
src/main/java/roomescape/member/business/MemberService.kt
Normal file
43
src/main/java/roomescape/member/business/MemberService.kt
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package roomescape.member.business
|
||||||
|
|
||||||
|
import org.springframework.data.repository.findByIdOrNull
|
||||||
|
import org.springframework.http.HttpStatus
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
import org.springframework.transaction.annotation.Transactional
|
||||||
|
import roomescape.member.infrastructure.persistence.Member
|
||||||
|
import roomescape.member.infrastructure.persistence.MemberRepository
|
||||||
|
import roomescape.member.web.MembersResponse
|
||||||
|
import roomescape.member.web.toResponse
|
||||||
|
import roomescape.system.exception.ErrorType
|
||||||
|
import roomescape.system.exception.RoomEscapeException
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
class MemberService(
|
||||||
|
private val memberRepository: MemberRepository
|
||||||
|
) {
|
||||||
|
fun readAllMembers(): MembersResponse = MembersResponse(
|
||||||
|
memberRepository.findAll()
|
||||||
|
.map { it.toResponse() }
|
||||||
|
.toList()
|
||||||
|
)
|
||||||
|
|
||||||
|
fun findById(memberId: Long): Member = memberRepository.findByIdOrNull(memberId)
|
||||||
|
?: throw RoomEscapeException(
|
||||||
|
ErrorType.MEMBER_NOT_FOUND,
|
||||||
|
String.format("[memberId: %d]", memberId),
|
||||||
|
HttpStatus.BAD_REQUEST
|
||||||
|
)
|
||||||
|
|
||||||
|
fun findMemberByEmailAndPassword(email: String, password: String): Member =
|
||||||
|
memberRepository.findByEmailAndPassword(email, password)
|
||||||
|
?: throw RoomEscapeException(
|
||||||
|
ErrorType.MEMBER_NOT_FOUND,
|
||||||
|
String.format("[email: %s, password: %s]", email, password),
|
||||||
|
HttpStatus.BAD_REQUEST
|
||||||
|
)
|
||||||
|
|
||||||
|
fun existsById(memberId: Long): Boolean = memberRepository.existsById(memberId)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@ -1,37 +0,0 @@
|
|||||||
package roomescape.member.controller;
|
|
||||||
|
|
||||||
import org.springframework.http.HttpStatus;
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
|
||||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
|
||||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
|
||||||
import io.swagger.v3.oas.annotations.responses.ApiResponses;
|
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
|
||||||
import roomescape.member.dto.MembersResponse;
|
|
||||||
import roomescape.member.service.MemberService;
|
|
||||||
import roomescape.system.auth.annotation.Admin;
|
|
||||||
import roomescape.system.dto.response.RoomEscapeApiResponse;
|
|
||||||
|
|
||||||
@RestController
|
|
||||||
@Tag(name = "2. 회원 API", description = "회원 정보를 관리할 때 사용합니다.")
|
|
||||||
public class MemberController {
|
|
||||||
|
|
||||||
private final MemberService memberService;
|
|
||||||
|
|
||||||
public MemberController(MemberService memberService) {
|
|
||||||
this.memberService = memberService;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Admin
|
|
||||||
@GetMapping("/members")
|
|
||||||
@ResponseStatus(HttpStatus.OK)
|
|
||||||
@Operation(summary = "모든 회원 조회", tags = "관리자 로그인이 필요한 API")
|
|
||||||
@ApiResponses({
|
|
||||||
@ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true)
|
|
||||||
})
|
|
||||||
public RoomEscapeApiResponse<MembersResponse> getAllMembers() {
|
|
||||||
return RoomEscapeApiResponse.success(memberService.findAllMembers());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,98 +0,0 @@
|
|||||||
package roomescape.member.domain;
|
|
||||||
|
|
||||||
import org.springframework.http.HttpStatus;
|
|
||||||
|
|
||||||
import jakarta.persistence.Entity;
|
|
||||||
import jakarta.persistence.EnumType;
|
|
||||||
import jakarta.persistence.Enumerated;
|
|
||||||
import jakarta.persistence.GeneratedValue;
|
|
||||||
import jakarta.persistence.GenerationType;
|
|
||||||
import jakarta.persistence.Id;
|
|
||||||
import roomescape.system.exception.ErrorType;
|
|
||||||
import roomescape.system.exception.RoomEscapeException;
|
|
||||||
|
|
||||||
@Entity
|
|
||||||
public class Member {
|
|
||||||
@Id
|
|
||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
|
||||||
private Long id;
|
|
||||||
|
|
||||||
private String name;
|
|
||||||
|
|
||||||
private String email;
|
|
||||||
|
|
||||||
private String password;
|
|
||||||
|
|
||||||
@Enumerated(value = EnumType.STRING)
|
|
||||||
private Role role;
|
|
||||||
|
|
||||||
protected Member() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public Member(
|
|
||||||
String name,
|
|
||||||
String email,
|
|
||||||
String password,
|
|
||||||
Role role
|
|
||||||
) {
|
|
||||||
this(null, name, email, password, role);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Member(
|
|
||||||
Long id,
|
|
||||||
String name,
|
|
||||||
String email,
|
|
||||||
String password,
|
|
||||||
Role role
|
|
||||||
) {
|
|
||||||
this.id = id;
|
|
||||||
this.name = name;
|
|
||||||
this.email = email;
|
|
||||||
this.password = password;
|
|
||||||
this.role = role;
|
|
||||||
|
|
||||||
validateRole();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void validateRole() {
|
|
||||||
if (role == null) {
|
|
||||||
throw new RoomEscapeException(ErrorType.REQUEST_DATA_BLANK, String.format("[values: %s]", this),
|
|
||||||
HttpStatus.BAD_REQUEST);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isAdmin() {
|
|
||||||
return this.role == Role.ADMIN;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Long getId() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getEmail() {
|
|
||||||
return email;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getPassword() {
|
|
||||||
return password;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Role getRole() {
|
|
||||||
return role;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "Member{" +
|
|
||||||
"id=" + id +
|
|
||||||
", name=" + name +
|
|
||||||
", email=" + email +
|
|
||||||
", password=" + password +
|
|
||||||
", role=" + role +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
package roomescape.member.domain;
|
|
||||||
|
|
||||||
public enum Role {
|
|
||||||
MEMBER,
|
|
||||||
ADMIN
|
|
||||||
}
|
|
||||||
@ -1,12 +0,0 @@
|
|||||||
package roomescape.member.domain.repository;
|
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
|
||||||
|
|
||||||
import roomescape.member.domain.Member;
|
|
||||||
|
|
||||||
public interface MemberRepository extends JpaRepository<Member, Long> {
|
|
||||||
|
|
||||||
Optional<Member> findByEmailAndPassword(String email, String password);
|
|
||||||
}
|
|
||||||
@ -1,14 +0,0 @@
|
|||||||
package roomescape.member.dto;
|
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
|
||||||
import roomescape.member.domain.Member;
|
|
||||||
|
|
||||||
@Schema(name = "회원 조회 응답", description = "회원 정보 조회 응답시 사용됩니다.")
|
|
||||||
public record MemberResponse(
|
|
||||||
@Schema(description = "회원 번호. 회원을 식별할 때 사용합니다.") Long id,
|
|
||||||
@Schema(description = "회원의 이름") String name
|
|
||||||
) {
|
|
||||||
public static MemberResponse fromEntity(Member member) {
|
|
||||||
return new MemberResponse(member.getId(), member.getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
package roomescape.member.dto;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
|
||||||
|
|
||||||
@Schema(name = "회원 목록 조회 응답", description = "모든 회원의 정보 조회 응답시 사용됩니다.")
|
|
||||||
public record MembersResponse(
|
|
||||||
@Schema(description = "모든 회원의 ID 및 이름") List<MemberResponse> members
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
package roomescape.member.infrastructure.persistence
|
||||||
|
|
||||||
|
import jakarta.persistence.*
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
class Member(
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
var id: Long? = null,
|
||||||
|
var name: String,
|
||||||
|
var email: String,
|
||||||
|
var password: String,
|
||||||
|
|
||||||
|
@Enumerated(value = EnumType.STRING)
|
||||||
|
var role: Role
|
||||||
|
) {
|
||||||
|
fun isAdmin(): Boolean = role == Role.ADMIN
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class Role {
|
||||||
|
MEMBER,
|
||||||
|
ADMIN,
|
||||||
|
}
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
package roomescape.member.infrastructure.persistence
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository
|
||||||
|
|
||||||
|
interface MemberRepository : JpaRepository<Member, Long> {
|
||||||
|
fun findByEmailAndPassword(email: String, password: String): Member?
|
||||||
|
}
|
||||||
@ -1,47 +0,0 @@
|
|||||||
package roomescape.member.service;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.springframework.http.HttpStatus;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
|
||||||
|
|
||||||
import roomescape.member.domain.Member;
|
|
||||||
import roomescape.member.domain.repository.MemberRepository;
|
|
||||||
import roomescape.member.dto.MemberResponse;
|
|
||||||
import roomescape.member.dto.MembersResponse;
|
|
||||||
import roomescape.system.exception.ErrorType;
|
|
||||||
import roomescape.system.exception.RoomEscapeException;
|
|
||||||
|
|
||||||
@Service
|
|
||||||
public class MemberService {
|
|
||||||
|
|
||||||
private final MemberRepository memberRepository;
|
|
||||||
|
|
||||||
public MemberService(MemberRepository memberRepository) {
|
|
||||||
this.memberRepository = memberRepository;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Transactional(readOnly = true)
|
|
||||||
public MembersResponse findAllMembers() {
|
|
||||||
List<MemberResponse> response = memberRepository.findAll().stream()
|
|
||||||
.map(MemberResponse::fromEntity)
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
return new MembersResponse(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Transactional(readOnly = true)
|
|
||||||
public Member findMemberById(Long memberId) {
|
|
||||||
return memberRepository.findById(memberId)
|
|
||||||
.orElseThrow(() -> new RoomEscapeException(ErrorType.MEMBER_NOT_FOUND,
|
|
||||||
String.format("[memberId: %d]", memberId), HttpStatus.BAD_REQUEST));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Transactional(readOnly = true)
|
|
||||||
public Member findMemberByEmailAndPassword(String email, String password) {
|
|
||||||
return memberRepository.findByEmailAndPassword(email, password)
|
|
||||||
.orElseThrow(() -> new RoomEscapeException(ErrorType.MEMBER_NOT_FOUND,
|
|
||||||
String.format("[email: %s, password: %s]", email, password), HttpStatus.BAD_REQUEST));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
21
src/main/java/roomescape/member/web/MemberAPI.kt
Normal file
21
src/main/java/roomescape/member/web/MemberAPI.kt
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package roomescape.member.web
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.Operation
|
||||||
|
import io.swagger.v3.oas.annotations.responses.ApiResponse
|
||||||
|
import io.swagger.v3.oas.annotations.responses.ApiResponses
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag
|
||||||
|
import org.springframework.http.HttpStatus
|
||||||
|
import org.springframework.web.bind.annotation.ResponseStatus
|
||||||
|
import roomescape.system.auth.web.support.Admin
|
||||||
|
import roomescape.system.dto.response.RoomEscapeApiResponse
|
||||||
|
|
||||||
|
@Tag(name = "2. 회원 API", description = "회원 정보를 관리할 때 사용합니다.")
|
||||||
|
interface MemberAPI {
|
||||||
|
|
||||||
|
@Admin
|
||||||
|
@Operation(summary = "모든 회원 조회", tags = ["관리자 로그인이 필요한 API"])
|
||||||
|
@ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true))
|
||||||
|
@ResponseStatus(HttpStatus.OK)
|
||||||
|
fun readAllMembers(): RoomEscapeApiResponse<MembersResponse>
|
||||||
|
|
||||||
|
}
|
||||||
48
src/main/java/roomescape/member/web/MemberController.kt
Normal file
48
src/main/java/roomescape/member/web/MemberController.kt
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
package roomescape.member.web
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping
|
||||||
|
import org.springframework.web.bind.annotation.RestController
|
||||||
|
import roomescape.member.business.MemberService
|
||||||
|
import roomescape.member.infrastructure.persistence.Member
|
||||||
|
import roomescape.system.dto.response.RoomEscapeApiResponse
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
class MemberController(
|
||||||
|
private val memberService: MemberService
|
||||||
|
) : MemberAPI {
|
||||||
|
|
||||||
|
@GetMapping("/members")
|
||||||
|
override fun readAllMembers(): RoomEscapeApiResponse<MembersResponse> {
|
||||||
|
val result: MembersResponse = memberService.readAllMembers()
|
||||||
|
|
||||||
|
return RoomEscapeApiResponse.success(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Schema(name = "회원 조회 응답", description = "회원 정보 조회 응답시 사용됩니다.")
|
||||||
|
data class MemberResponse(
|
||||||
|
@field:Schema(description = "회원의 고유 번호")
|
||||||
|
val id: Long,
|
||||||
|
|
||||||
|
@field:Schema(description = "회원의 이름")
|
||||||
|
val name: String
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
@JvmStatic
|
||||||
|
fun fromEntity(member: Member): MemberResponse {
|
||||||
|
return MemberResponse(member.id!!, member.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Member.toResponse(): MemberResponse = MemberResponse(
|
||||||
|
id = id!!,
|
||||||
|
name = name
|
||||||
|
)
|
||||||
|
|
||||||
|
@Schema(name = "회원 목록 조회 응답", description = "모든 회원의 정보 조회 응답시 사용됩니다.")
|
||||||
|
data class MembersResponse(
|
||||||
|
@field:Schema(description = "모든 회원의 ID 및 이름")
|
||||||
|
val members: List<MemberResponse>
|
||||||
|
)
|
||||||
@ -37,9 +37,9 @@ import roomescape.reservation.dto.response.ReservationResponse;
|
|||||||
import roomescape.reservation.dto.response.ReservationsResponse;
|
import roomescape.reservation.dto.response.ReservationsResponse;
|
||||||
import roomescape.reservation.service.ReservationService;
|
import roomescape.reservation.service.ReservationService;
|
||||||
import roomescape.reservation.service.ReservationWithPaymentService;
|
import roomescape.reservation.service.ReservationWithPaymentService;
|
||||||
import roomescape.system.auth.annotation.Admin;
|
import roomescape.system.auth.web.support.Admin;
|
||||||
import roomescape.system.auth.annotation.LoginRequired;
|
import roomescape.system.auth.web.support.LoginRequired;
|
||||||
import roomescape.system.auth.annotation.MemberId;
|
import roomescape.system.auth.web.support.MemberId;
|
||||||
import roomescape.system.dto.response.ErrorResponse;
|
import roomescape.system.dto.response.ErrorResponse;
|
||||||
import roomescape.system.dto.response.RoomEscapeApiResponse;
|
import roomescape.system.dto.response.RoomEscapeApiResponse;
|
||||||
import roomescape.system.exception.RoomEscapeException;
|
import roomescape.system.exception.RoomEscapeException;
|
||||||
|
|||||||
@ -28,8 +28,8 @@ import roomescape.reservation.dto.response.ReservationTimeInfosResponse;
|
|||||||
import roomescape.reservation.dto.response.ReservationTimeResponse;
|
import roomescape.reservation.dto.response.ReservationTimeResponse;
|
||||||
import roomescape.reservation.dto.response.ReservationTimesResponse;
|
import roomescape.reservation.dto.response.ReservationTimesResponse;
|
||||||
import roomescape.reservation.service.ReservationTimeService;
|
import roomescape.reservation.service.ReservationTimeService;
|
||||||
import roomescape.system.auth.annotation.Admin;
|
import roomescape.system.auth.web.support.Admin;
|
||||||
import roomescape.system.auth.annotation.LoginRequired;
|
import roomescape.system.auth.web.support.LoginRequired;
|
||||||
import roomescape.system.dto.response.ErrorResponse;
|
import roomescape.system.dto.response.ErrorResponse;
|
||||||
import roomescape.system.dto.response.RoomEscapeApiResponse;
|
import roomescape.system.dto.response.RoomEscapeApiResponse;
|
||||||
|
|
||||||
|
|||||||
@ -15,7 +15,7 @@ import jakarta.persistence.GenerationType;
|
|||||||
import jakarta.persistence.Id;
|
import jakarta.persistence.Id;
|
||||||
import jakarta.persistence.JoinColumn;
|
import jakarta.persistence.JoinColumn;
|
||||||
import jakarta.persistence.ManyToOne;
|
import jakarta.persistence.ManyToOne;
|
||||||
import roomescape.member.domain.Member;
|
import roomescape.member.infrastructure.persistence.Member;
|
||||||
import roomescape.system.exception.ErrorType;
|
import roomescape.system.exception.ErrorType;
|
||||||
import roomescape.system.exception.RoomEscapeException;
|
import roomescape.system.exception.RoomEscapeException;
|
||||||
import roomescape.theme.domain.Theme;
|
import roomescape.theme.domain.Theme;
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import java.time.LocalDate;
|
|||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import roomescape.member.dto.MemberResponse;
|
import roomescape.member.web.MemberResponse;
|
||||||
import roomescape.reservation.domain.Reservation;
|
import roomescape.reservation.domain.Reservation;
|
||||||
import roomescape.reservation.domain.ReservationStatus;
|
import roomescape.reservation.domain.ReservationStatus;
|
||||||
import roomescape.theme.dto.ThemeResponse;
|
import roomescape.theme.dto.ThemeResponse;
|
||||||
|
|||||||
@ -9,8 +9,8 @@ import org.springframework.http.HttpStatus;
|
|||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import roomescape.member.domain.Member;
|
import roomescape.member.business.MemberService;
|
||||||
import roomescape.member.service.MemberService;
|
import roomescape.member.infrastructure.persistence.Member;
|
||||||
import roomescape.reservation.domain.Reservation;
|
import roomescape.reservation.domain.Reservation;
|
||||||
import roomescape.reservation.domain.ReservationStatus;
|
import roomescape.reservation.domain.ReservationStatus;
|
||||||
import roomescape.reservation.domain.ReservationTime;
|
import roomescape.reservation.domain.ReservationTime;
|
||||||
@ -147,7 +147,7 @@ public class ReservationService {
|
|||||||
ReservationStatus status) {
|
ReservationStatus status) {
|
||||||
ReservationTime time = reservationTimeService.findTimeById(timeId);
|
ReservationTime time = reservationTimeService.findTimeById(timeId);
|
||||||
Theme theme = themeService.findThemeById(themeId);
|
Theme theme = themeService.findThemeById(themeId);
|
||||||
Member member = memberService.findMemberById(memberId);
|
Member member = memberService.findById(memberId);
|
||||||
|
|
||||||
validateDateAndTime(date, time);
|
validateDateAndTime(date, time);
|
||||||
return new Reservation(date, time, theme, member, status);
|
return new Reservation(date, time, theme, member, status);
|
||||||
@ -213,7 +213,7 @@ public class ReservationService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void validateIsMemberAdmin(Long memberId) {
|
private void validateIsMemberAdmin(Long memberId) {
|
||||||
Member member = memberService.findMemberById(memberId);
|
Member member = memberService.findById(memberId);
|
||||||
if (member.isAdmin()) {
|
if (member.isAdmin()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +0,0 @@
|
|||||||
package roomescape.system.auth.annotation;
|
|
||||||
|
|
||||||
import java.lang.annotation.ElementType;
|
|
||||||
import java.lang.annotation.Retention;
|
|
||||||
import java.lang.annotation.RetentionPolicy;
|
|
||||||
import java.lang.annotation.Target;
|
|
||||||
|
|
||||||
@Target(ElementType.METHOD)
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
|
||||||
public @interface Admin {
|
|
||||||
}
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
package roomescape.system.auth.annotation;
|
|
||||||
|
|
||||||
import java.lang.annotation.ElementType;
|
|
||||||
import java.lang.annotation.Retention;
|
|
||||||
import java.lang.annotation.RetentionPolicy;
|
|
||||||
import java.lang.annotation.Target;
|
|
||||||
|
|
||||||
@Target(ElementType.METHOD)
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
|
||||||
public @interface LoginRequired {
|
|
||||||
}
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
package roomescape.system.auth.annotation;
|
|
||||||
|
|
||||||
import java.lang.annotation.ElementType;
|
|
||||||
import java.lang.annotation.Retention;
|
|
||||||
import java.lang.annotation.RetentionPolicy;
|
|
||||||
import java.lang.annotation.Target;
|
|
||||||
|
|
||||||
@Target(ElementType.PARAMETER)
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
|
||||||
public @interface MemberId {
|
|
||||||
}
|
|
||||||
@ -1,102 +0,0 @@
|
|||||||
package roomescape.system.auth.controller;
|
|
||||||
|
|
||||||
import org.springframework.http.HttpStatus;
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
|
||||||
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 io.swagger.v3.oas.annotations.Operation;
|
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
|
||||||
import io.swagger.v3.oas.annotations.media.Content;
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
|
||||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
|
||||||
import io.swagger.v3.oas.annotations.responses.ApiResponses;
|
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
|
||||||
import jakarta.servlet.http.Cookie;
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
|
||||||
import jakarta.validation.Valid;
|
|
||||||
import roomescape.system.auth.annotation.LoginRequired;
|
|
||||||
import roomescape.system.auth.annotation.MemberId;
|
|
||||||
import roomescape.system.auth.dto.LoginCheckResponse;
|
|
||||||
import roomescape.system.auth.dto.LoginRequest;
|
|
||||||
import roomescape.system.auth.jwt.dto.TokenDto;
|
|
||||||
import roomescape.system.auth.service.AuthService;
|
|
||||||
import roomescape.system.dto.response.ErrorResponse;
|
|
||||||
import roomescape.system.dto.response.RoomEscapeApiResponse;
|
|
||||||
|
|
||||||
@RestController
|
|
||||||
@Tag(name = "1. 인증 / 인가 API", description = "로그인, 로그아웃 및 로그인 상태를 확인합니다")
|
|
||||||
public class AuthController {
|
|
||||||
|
|
||||||
private final AuthService authService;
|
|
||||||
|
|
||||||
public AuthController(AuthService authService) {
|
|
||||||
this.authService = authService;
|
|
||||||
}
|
|
||||||
|
|
||||||
@PostMapping("/login")
|
|
||||||
@ResponseStatus(HttpStatus.OK)
|
|
||||||
@Operation(summary = "로그인")
|
|
||||||
@ApiResponses({
|
|
||||||
@ApiResponse(responseCode = "200", description = "로그인 성공시 쿠키에 토큰 정보를 저장합니다."),
|
|
||||||
@ApiResponse(responseCode = "400", description = "존재하지 않는 회원이거나, 이메일 또는 비밀번호가 잘못 입력되었습니다.",
|
|
||||||
content = @Content(schema = @Schema(implementation = ErrorResponse.class)))
|
|
||||||
})
|
|
||||||
public RoomEscapeApiResponse<Void> login(
|
|
||||||
@Valid @RequestBody LoginRequest loginRequest,
|
|
||||||
HttpServletResponse response
|
|
||||||
) {
|
|
||||||
TokenDto tokenInfo = authService.login(loginRequest);
|
|
||||||
addCookieToResponse(new Cookie("accessToken", tokenInfo.accessToken()), response);
|
|
||||||
return RoomEscapeApiResponse.success();
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/login/check")
|
|
||||||
@ResponseStatus(HttpStatus.OK)
|
|
||||||
@Operation(summary = "로그인 상태 확인")
|
|
||||||
@ApiResponses({
|
|
||||||
@ApiResponse(responseCode = "200", description = "로그인 상태이며, 로그인된 회원의 이름을 반환합니다."),
|
|
||||||
@ApiResponse(responseCode = "400", description = "쿠키에 있는 토큰 정보로 회원을 조회할 수 없습니다.",
|
|
||||||
content = @Content(schema = @Schema(implementation = ErrorResponse.class))),
|
|
||||||
})
|
|
||||||
public RoomEscapeApiResponse<LoginCheckResponse> checkLogin(@MemberId @Parameter(hidden = true) Long memberId) {
|
|
||||||
LoginCheckResponse response = authService.checkLogin(memberId);
|
|
||||||
return RoomEscapeApiResponse.success(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
@LoginRequired
|
|
||||||
@PostMapping("/logout")
|
|
||||||
@ResponseStatus(HttpStatus.OK)
|
|
||||||
@Operation(summary = "로그아웃", tags = "로그인이 필요한 API")
|
|
||||||
@ApiResponses({
|
|
||||||
@ApiResponse(responseCode = "200", description = "로그아웃 성공시 쿠키에 저장된 토큰 정보를 삭제합니다.")
|
|
||||||
})
|
|
||||||
public RoomEscapeApiResponse<Void> logout(
|
|
||||||
HttpServletRequest request,
|
|
||||||
HttpServletResponse response
|
|
||||||
) {
|
|
||||||
Cookie cookie = getTokenCookie(request);
|
|
||||||
cookie.setValue(null);
|
|
||||||
cookie.setMaxAge(0);
|
|
||||||
addCookieToResponse(cookie, response);
|
|
||||||
return RoomEscapeApiResponse.success();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Cookie getTokenCookie(HttpServletRequest request) {
|
|
||||||
for (Cookie cookie : request.getCookies()) {
|
|
||||||
if (cookie.getName().equals("accessToken")) {
|
|
||||||
return cookie;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new Cookie("accessToken", null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addCookieToResponse(Cookie cookie, HttpServletResponse response) {
|
|
||||||
cookie.setHttpOnly(true);
|
|
||||||
|
|
||||||
response.addCookie(cookie);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
package roomescape.system.auth.dto;
|
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
|
||||||
|
|
||||||
@Schema(name = "로그인 체크 응답", description = "로그인 상태 체크 응답시 사용됩니다.")
|
|
||||||
public record LoginCheckResponse(
|
|
||||||
@Schema(description = "로그인된 회원의 이름") String name
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
package roomescape.system.auth.dto;
|
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
|
||||||
import jakarta.validation.constraints.Email;
|
|
||||||
import jakarta.validation.constraints.NotBlank;
|
|
||||||
|
|
||||||
@Schema(name = "로그인 요청", description = "로그인 요청 시 사용됩니다.")
|
|
||||||
public record LoginRequest(
|
|
||||||
@NotBlank(message = "이메일은 null 또는 공백일 수 없습니다.")
|
|
||||||
@Email(message = "이메일 형식이 일치하지 않습니다. 예시: abc123@gmail.com)")
|
|
||||||
@Schema(description = "필수 값이며, 이메일 형식으로 입력해야 합니다.", example = "abc123@gmail.com")
|
|
||||||
String email,
|
|
||||||
@NotBlank(message = "비밀번호는 null 또는 공백일 수 없습니다.")
|
|
||||||
@Schema(description = "최소 1글자 이상 입력해야 합니다.")
|
|
||||||
String password
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
@ -0,0 +1,50 @@
|
|||||||
|
package roomescape.system.auth.infrastructure.jwt
|
||||||
|
|
||||||
|
import io.jsonwebtoken.*
|
||||||
|
import org.springframework.beans.factory.annotation.Value
|
||||||
|
import org.springframework.http.HttpStatus
|
||||||
|
import org.springframework.stereotype.Component
|
||||||
|
import roomescape.system.exception.ErrorType
|
||||||
|
import roomescape.system.exception.RoomEscapeException
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class JwtHandler(
|
||||||
|
@Value("\${security.jwt.token.secret-key}")
|
||||||
|
private val secretKey: String,
|
||||||
|
|
||||||
|
@Value("\${security.jwt.token.access.expire-length}")
|
||||||
|
private val accessTokenExpireTime: Long
|
||||||
|
) {
|
||||||
|
fun createToken(memberId: Long): String {
|
||||||
|
val date = Date()
|
||||||
|
val accessTokenExpiredAt = Date(date.time + accessTokenExpireTime)
|
||||||
|
|
||||||
|
return Jwts.builder()
|
||||||
|
.claim("memberId", memberId)
|
||||||
|
.setIssuedAt(date)
|
||||||
|
.setExpiration(accessTokenExpiredAt)
|
||||||
|
.signWith(SignatureAlgorithm.HS256, secretKey.toByteArray())
|
||||||
|
.compact()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getMemberIdFromToken(token: String?): Long {
|
||||||
|
try {
|
||||||
|
return Jwts.parser()
|
||||||
|
.setSigningKey(secretKey.toByteArray())
|
||||||
|
.parseClaimsJws(token)
|
||||||
|
.getBody()
|
||||||
|
.get("memberId", Number::class.java)
|
||||||
|
.toLong()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
when (e) {
|
||||||
|
is ExpiredJwtException -> throw RoomEscapeException(ErrorType.EXPIRED_TOKEN, HttpStatus.UNAUTHORIZED)
|
||||||
|
is UnsupportedJwtException -> throw RoomEscapeException(ErrorType.UNSUPPORTED_TOKEN, HttpStatus.UNAUTHORIZED)
|
||||||
|
is MalformedJwtException -> throw RoomEscapeException(ErrorType.MALFORMED_TOKEN, HttpStatus.UNAUTHORIZED)
|
||||||
|
is SignatureException -> throw RoomEscapeException(ErrorType.INVALID_SIGNATURE_TOKEN, HttpStatus.UNAUTHORIZED)
|
||||||
|
is IllegalArgumentException -> throw RoomEscapeException(ErrorType.INVALID_TOKEN, HttpStatus.UNAUTHORIZED)
|
||||||
|
else -> throw RoomEscapeException(ErrorType.UNEXPECTED_ERROR, HttpStatus.INTERNAL_SERVER_ERROR)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,86 +0,0 @@
|
|||||||
package roomescape.system.auth.interceptor;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
import org.springframework.http.HttpStatus;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
import org.springframework.web.method.HandlerMethod;
|
|
||||||
import org.springframework.web.servlet.HandlerInterceptor;
|
|
||||||
|
|
||||||
import jakarta.servlet.http.Cookie;
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
|
||||||
import roomescape.member.domain.Member;
|
|
||||||
import roomescape.member.service.MemberService;
|
|
||||||
import roomescape.system.auth.annotation.Admin;
|
|
||||||
import roomescape.system.auth.jwt.JwtHandler;
|
|
||||||
import roomescape.system.exception.ErrorType;
|
|
||||||
import roomescape.system.exception.RoomEscapeException;
|
|
||||||
|
|
||||||
@Component
|
|
||||||
public class AdminInterceptor implements HandlerInterceptor {
|
|
||||||
|
|
||||||
private static final String ACCESS_TOKEN_COOKIE_NAME = "accessToken";
|
|
||||||
private final MemberService memberService;
|
|
||||||
private final JwtHandler jwtHandler;
|
|
||||||
|
|
||||||
public AdminInterceptor(MemberService memberService, JwtHandler jwtHandler) {
|
|
||||||
this.memberService = memberService;
|
|
||||||
this.jwtHandler = jwtHandler;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean preHandle(
|
|
||||||
HttpServletRequest request,
|
|
||||||
HttpServletResponse response,
|
|
||||||
Object handler
|
|
||||||
)
|
|
||||||
throws Exception {
|
|
||||||
if (isHandlerIrrelevantWithAdmin(handler)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Member member;
|
|
||||||
try {
|
|
||||||
Cookie token = getToken(request);
|
|
||||||
Long memberId = jwtHandler.getMemberIdFromToken(token.getValue());
|
|
||||||
member = memberService.findMemberById(memberId);
|
|
||||||
} catch (RoomEscapeException e) {
|
|
||||||
response.sendRedirect("/login");
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (member.isAdmin()) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
response.sendRedirect("/login");
|
|
||||||
throw new RoomEscapeException(ErrorType.PERMISSION_DOES_NOT_EXIST,
|
|
||||||
String.format("[memberId: %d, Role: %s]", member.getId(), member.getRole()), HttpStatus.FORBIDDEN);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Cookie getToken(HttpServletRequest request) {
|
|
||||||
validateCookieHeader(request);
|
|
||||||
|
|
||||||
Cookie[] cookies = request.getCookies();
|
|
||||||
return Arrays.stream(cookies)
|
|
||||||
.filter(cookie -> cookie.getName().equals(ACCESS_TOKEN_COOKIE_NAME))
|
|
||||||
.findAny()
|
|
||||||
.orElseThrow(() -> new RoomEscapeException(ErrorType.INVALID_TOKEN, HttpStatus.UNAUTHORIZED));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void validateCookieHeader(HttpServletRequest request) {
|
|
||||||
String cookieHeader = request.getHeader("Cookie");
|
|
||||||
if (cookieHeader == null) {
|
|
||||||
throw new RoomEscapeException(ErrorType.NOT_EXIST_COOKIE, HttpStatus.UNAUTHORIZED);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isHandlerIrrelevantWithAdmin(Object handler) {
|
|
||||||
if (!(handler instanceof HandlerMethod handlerMethod)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
Admin adminAnnotation = handlerMethod.getMethodAnnotation(Admin.class);
|
|
||||||
return adminAnnotation == null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,79 +0,0 @@
|
|||||||
package roomescape.system.auth.interceptor;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
import org.springframework.http.HttpStatus;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
import org.springframework.web.method.HandlerMethod;
|
|
||||||
import org.springframework.web.servlet.HandlerInterceptor;
|
|
||||||
|
|
||||||
import jakarta.servlet.http.Cookie;
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
|
||||||
import roomescape.member.domain.Member;
|
|
||||||
import roomescape.member.service.MemberService;
|
|
||||||
import roomescape.system.auth.annotation.LoginRequired;
|
|
||||||
import roomescape.system.auth.jwt.JwtHandler;
|
|
||||||
import roomescape.system.exception.ErrorType;
|
|
||||||
import roomescape.system.exception.RoomEscapeException;
|
|
||||||
|
|
||||||
@Component
|
|
||||||
public class LoginInterceptor implements HandlerInterceptor {
|
|
||||||
|
|
||||||
private static final String ACCESS_TOKEN_COOKIE_NAME = "accessToken";
|
|
||||||
private final MemberService memberService;
|
|
||||||
private final JwtHandler jwtHandler;
|
|
||||||
|
|
||||||
public LoginInterceptor(MemberService memberService, JwtHandler jwtHandler) {
|
|
||||||
this.memberService = memberService;
|
|
||||||
this.jwtHandler = jwtHandler;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean preHandle(
|
|
||||||
HttpServletRequest request,
|
|
||||||
HttpServletResponse response,
|
|
||||||
Object handler
|
|
||||||
)
|
|
||||||
throws Exception {
|
|
||||||
if (isHandlerIrrelevantWithLoginRequired(handler)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Member member;
|
|
||||||
try {
|
|
||||||
Cookie token = getToken(request);
|
|
||||||
Long memberId = jwtHandler.getMemberIdFromToken(token.getValue());
|
|
||||||
member = memberService.findMemberById(memberId);
|
|
||||||
return member != null;
|
|
||||||
} catch (RoomEscapeException e) {
|
|
||||||
response.sendRedirect("/login");
|
|
||||||
throw new RoomEscapeException(ErrorType.LOGIN_REQUIRED, HttpStatus.FORBIDDEN);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Cookie getToken(HttpServletRequest request) {
|
|
||||||
validateCookieHeader(request);
|
|
||||||
|
|
||||||
Cookie[] cookies = request.getCookies();
|
|
||||||
return Arrays.stream(cookies)
|
|
||||||
.filter(cookie -> cookie.getName().equals(ACCESS_TOKEN_COOKIE_NAME))
|
|
||||||
.findAny()
|
|
||||||
.orElseThrow(() -> new RoomEscapeException(ErrorType.INVALID_TOKEN, HttpStatus.UNAUTHORIZED));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void validateCookieHeader(HttpServletRequest request) {
|
|
||||||
String cookieHeader = request.getHeader("Cookie");
|
|
||||||
if (cookieHeader == null) {
|
|
||||||
throw new RoomEscapeException(ErrorType.NOT_EXIST_COOKIE, HttpStatus.UNAUTHORIZED);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isHandlerIrrelevantWithLoginRequired(Object handler) {
|
|
||||||
if (!(handler instanceof HandlerMethod handlerMethod)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
LoginRequired loginRequiredAnnotation = handlerMethod.getMethodAnnotation(LoginRequired.class);
|
|
||||||
return loginRequiredAnnotation == null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,65 +0,0 @@
|
|||||||
package roomescape.system.auth.jwt;
|
|
||||||
|
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
import org.springframework.http.HttpStatus;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
import io.jsonwebtoken.ExpiredJwtException;
|
|
||||||
import io.jsonwebtoken.Jwts;
|
|
||||||
import io.jsonwebtoken.MalformedJwtException;
|
|
||||||
import io.jsonwebtoken.SignatureAlgorithm;
|
|
||||||
import io.jsonwebtoken.SignatureException;
|
|
||||||
import io.jsonwebtoken.UnsupportedJwtException;
|
|
||||||
import roomescape.system.auth.jwt.dto.TokenDto;
|
|
||||||
import roomescape.system.exception.ErrorType;
|
|
||||||
import roomescape.system.exception.RoomEscapeException;
|
|
||||||
|
|
||||||
@Component
|
|
||||||
public class JwtHandler {
|
|
||||||
|
|
||||||
@Value("${security.jwt.token.secret-key}")
|
|
||||||
private String secretKey;
|
|
||||||
|
|
||||||
@Value("${security.jwt.token.access.expire-length}")
|
|
||||||
private long accessTokenExpireTime;
|
|
||||||
|
|
||||||
public TokenDto createToken(Long memberId) {
|
|
||||||
Date date = new Date();
|
|
||||||
Date accessTokenExpiredAt = new Date(date.getTime() + accessTokenExpireTime);
|
|
||||||
|
|
||||||
String accessToken = Jwts.builder()
|
|
||||||
.claim("memberId", memberId)
|
|
||||||
.setIssuedAt(date)
|
|
||||||
.setExpiration(accessTokenExpiredAt)
|
|
||||||
.signWith(SignatureAlgorithm.HS256, secretKey.getBytes())
|
|
||||||
.compact();
|
|
||||||
|
|
||||||
return new TokenDto(accessToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Long getMemberIdFromToken(String token) {
|
|
||||||
validateToken(token);
|
|
||||||
|
|
||||||
return Jwts.parser().setSigningKey(secretKey.getBytes()).parseClaimsJws(token)
|
|
||||||
.getBody()
|
|
||||||
.get("memberId", Long.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void validateToken(String token) {
|
|
||||||
try {
|
|
||||||
Jwts.parser().setSigningKey(secretKey.getBytes()).parseClaimsJws(token);
|
|
||||||
} catch (ExpiredJwtException e) {
|
|
||||||
throw new RoomEscapeException(ErrorType.EXPIRED_TOKEN, HttpStatus.UNAUTHORIZED);
|
|
||||||
} catch (UnsupportedJwtException e) {
|
|
||||||
throw new RoomEscapeException(ErrorType.UNSUPPORTED_TOKEN, HttpStatus.UNAUTHORIZED);
|
|
||||||
} catch (MalformedJwtException e) {
|
|
||||||
throw new RoomEscapeException(ErrorType.MALFORMED_TOKEN, HttpStatus.UNAUTHORIZED);
|
|
||||||
} catch (SignatureException e) {
|
|
||||||
throw new RoomEscapeException(ErrorType.INVALID_SIGNATURE_TOKEN, HttpStatus.UNAUTHORIZED);
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
throw new RoomEscapeException(ErrorType.ILLEGAL_TOKEN, HttpStatus.UNAUTHORIZED);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,4 +0,0 @@
|
|||||||
package roomescape.system.auth.jwt.dto;
|
|
||||||
|
|
||||||
public record TokenDto(String accessToken) {
|
|
||||||
}
|
|
||||||
@ -1,53 +0,0 @@
|
|||||||
package roomescape.system.auth.resolver;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
import org.springframework.core.MethodParameter;
|
|
||||||
import org.springframework.http.HttpStatus;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
|
||||||
import org.springframework.web.context.request.NativeWebRequest;
|
|
||||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
|
||||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
|
||||||
|
|
||||||
import jakarta.servlet.http.Cookie;
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import roomescape.system.auth.annotation.MemberId;
|
|
||||||
import roomescape.system.auth.jwt.JwtHandler;
|
|
||||||
import roomescape.system.exception.ErrorType;
|
|
||||||
import roomescape.system.exception.RoomEscapeException;
|
|
||||||
|
|
||||||
@Component
|
|
||||||
public class MemberIdResolver implements HandlerMethodArgumentResolver {
|
|
||||||
|
|
||||||
private static final String ACCESS_TOKEN_COOKIE_NAME = "accessToken";
|
|
||||||
|
|
||||||
private final JwtHandler jwtHandler;
|
|
||||||
|
|
||||||
public MemberIdResolver(JwtHandler jwtHandler) {
|
|
||||||
this.jwtHandler = jwtHandler;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean supportsParameter(MethodParameter parameter) {
|
|
||||||
return parameter.hasParameterAnnotation(MemberId.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object resolveArgument(
|
|
||||||
MethodParameter parameter,
|
|
||||||
ModelAndViewContainer mavContainer,
|
|
||||||
NativeWebRequest webRequest,
|
|
||||||
WebDataBinderFactory binderFactory
|
|
||||||
) throws Exception {
|
|
||||||
Cookie[] cookies = webRequest.getNativeRequest(HttpServletRequest.class).getCookies();
|
|
||||||
if (cookies == null) {
|
|
||||||
throw new RoomEscapeException(ErrorType.NOT_EXIST_COOKIE, HttpStatus.UNAUTHORIZED);
|
|
||||||
}
|
|
||||||
return Arrays.stream(cookies)
|
|
||||||
.filter(cookie -> cookie.getName().equals(ACCESS_TOKEN_COOKIE_NAME))
|
|
||||||
.findAny()
|
|
||||||
.map(cookie -> jwtHandler.getMemberIdFromToken(cookie.getValue()))
|
|
||||||
.orElseThrow(() -> new RoomEscapeException(ErrorType.INVALID_TOKEN, HttpStatus.UNAUTHORIZED));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,34 +0,0 @@
|
|||||||
package roomescape.system.auth.service;
|
|
||||||
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
import roomescape.member.domain.Member;
|
|
||||||
import roomescape.member.service.MemberService;
|
|
||||||
import roomescape.system.auth.dto.LoginCheckResponse;
|
|
||||||
import roomescape.system.auth.dto.LoginRequest;
|
|
||||||
import roomescape.system.auth.jwt.JwtHandler;
|
|
||||||
import roomescape.system.auth.jwt.dto.TokenDto;
|
|
||||||
|
|
||||||
@Service
|
|
||||||
public class AuthService {
|
|
||||||
|
|
||||||
private final MemberService memberService;
|
|
||||||
private final JwtHandler jwtHandler;
|
|
||||||
|
|
||||||
public AuthService(MemberService memberService, JwtHandler jwtHandler) {
|
|
||||||
this.memberService = memberService;
|
|
||||||
this.jwtHandler = jwtHandler;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TokenDto login(LoginRequest request) {
|
|
||||||
Member member = memberService.findMemberByEmailAndPassword(request.email(), request.password());
|
|
||||||
|
|
||||||
return jwtHandler.createToken(member.getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
public LoginCheckResponse checkLogin(Long memberId) {
|
|
||||||
Member member = memberService.findMemberById(memberId);
|
|
||||||
|
|
||||||
return new LoginCheckResponse(member.getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
32
src/main/java/roomescape/system/auth/service/AuthService.kt
Normal file
32
src/main/java/roomescape/system/auth/service/AuthService.kt
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package roomescape.system.auth.service
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
import roomescape.member.business.MemberService
|
||||||
|
import roomescape.member.infrastructure.persistence.Member
|
||||||
|
import roomescape.system.auth.infrastructure.jwt.JwtHandler
|
||||||
|
import roomescape.system.auth.web.LoginCheckResponse
|
||||||
|
import roomescape.system.auth.web.LoginRequest
|
||||||
|
import roomescape.system.auth.web.TokenResponse
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class AuthService(
|
||||||
|
private val memberService: MemberService,
|
||||||
|
private val jwtHandler: JwtHandler
|
||||||
|
) {
|
||||||
|
fun login(request: LoginRequest): TokenResponse {
|
||||||
|
val member: Member = memberService.findMemberByEmailAndPassword(
|
||||||
|
request.email,
|
||||||
|
request.password
|
||||||
|
)
|
||||||
|
|
||||||
|
val accessToken: String = jwtHandler.createToken(member.id!!)
|
||||||
|
|
||||||
|
return TokenResponse(accessToken)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun checkLogin(memberId: Long): LoginCheckResponse {
|
||||||
|
val member = memberService.findById(memberId)
|
||||||
|
|
||||||
|
return LoginCheckResponse(member.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
67
src/main/java/roomescape/system/auth/web/AuthAPI.kt
Normal file
67
src/main/java/roomescape/system/auth/web/AuthAPI.kt
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
package roomescape.system.auth.web
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.Operation
|
||||||
|
import io.swagger.v3.oas.annotations.Parameter
|
||||||
|
import io.swagger.v3.oas.annotations.media.Content
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema
|
||||||
|
import io.swagger.v3.oas.annotations.responses.ApiResponse
|
||||||
|
import io.swagger.v3.oas.annotations.responses.ApiResponses
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag
|
||||||
|
import jakarta.servlet.http.HttpServletRequest
|
||||||
|
import jakarta.servlet.http.HttpServletResponse
|
||||||
|
import jakarta.validation.Valid
|
||||||
|
import org.springframework.http.HttpStatus
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody
|
||||||
|
import org.springframework.web.bind.annotation.ResponseStatus
|
||||||
|
import roomescape.system.auth.web.support.LoginRequired
|
||||||
|
import roomescape.system.auth.web.support.MemberId
|
||||||
|
import roomescape.system.dto.response.ErrorResponse
|
||||||
|
import roomescape.system.dto.response.RoomEscapeApiResponse
|
||||||
|
|
||||||
|
@Tag(name = "1. 인증 / 인가 API", description = "로그인, 로그아웃 및 로그인 상태를 확인합니다")
|
||||||
|
interface AuthAPI {
|
||||||
|
|
||||||
|
@ResponseStatus(HttpStatus.OK)
|
||||||
|
@Operation(summary = "로그인")
|
||||||
|
@ApiResponses(
|
||||||
|
ApiResponse(
|
||||||
|
responseCode = "200",
|
||||||
|
description = "로그인 성공시 쿠키에 토큰 정보를 저장합니다."
|
||||||
|
),
|
||||||
|
ApiResponse(
|
||||||
|
responseCode = "400",
|
||||||
|
description = "존재하지 않는 회원이거나, 이메일 또는 비밀번호가 잘못 입력되었습니다.",
|
||||||
|
content = [Content(schema = Schema(implementation = ErrorResponse::class))]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
fun login(
|
||||||
|
@Valid @RequestBody loginRequest: LoginRequest,
|
||||||
|
response: HttpServletResponse
|
||||||
|
): RoomEscapeApiResponse<Void>
|
||||||
|
|
||||||
|
@ResponseStatus(HttpStatus.OK)
|
||||||
|
@Operation(summary = "로그인 상태 확인")
|
||||||
|
@ApiResponses(
|
||||||
|
ApiResponse(
|
||||||
|
responseCode = "200",
|
||||||
|
description = "로그인 상태이며, 로그인된 회원의 이름을 반환합니다."
|
||||||
|
),
|
||||||
|
ApiResponse(
|
||||||
|
responseCode = "400",
|
||||||
|
description = "쿠키에 있는 토큰 정보로 회원을 조회할 수 없습니다.",
|
||||||
|
content = [Content(schema = Schema(implementation = ErrorResponse::class))]
|
||||||
|
),
|
||||||
|
ApiResponse(
|
||||||
|
responseCode = "401",
|
||||||
|
description = "토큰 정보가 없거나, 만료되었습니다.",
|
||||||
|
content = [Content(schema = Schema(implementation = ErrorResponse::class))]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
fun checkLogin(@MemberId @Parameter(hidden = true) memberId: Long): RoomEscapeApiResponse<LoginCheckResponse>
|
||||||
|
|
||||||
|
@LoginRequired
|
||||||
|
@ResponseStatus(HttpStatus.OK)
|
||||||
|
@Operation(summary = "로그아웃", tags = ["로그인이 필요한 API"])
|
||||||
|
@ApiResponses(ApiResponse(responseCode = "200", description = "로그아웃 성공시 쿠키에 저장된 토큰 정보를 삭제합니다."))
|
||||||
|
fun logout(request: HttpServletRequest, response: HttpServletResponse): RoomEscapeApiResponse<Void>
|
||||||
|
}
|
||||||
54
src/main/java/roomescape/system/auth/web/AuthController.kt
Normal file
54
src/main/java/roomescape/system/auth/web/AuthController.kt
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
package roomescape.system.auth.web
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.Parameter
|
||||||
|
import jakarta.servlet.http.Cookie
|
||||||
|
import jakarta.servlet.http.HttpServletRequest
|
||||||
|
import jakarta.servlet.http.HttpServletResponse
|
||||||
|
import jakarta.validation.Valid
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody
|
||||||
|
import org.springframework.web.bind.annotation.RestController
|
||||||
|
import roomescape.system.auth.service.AuthService
|
||||||
|
import roomescape.system.auth.web.support.*
|
||||||
|
import roomescape.system.dto.response.RoomEscapeApiResponse
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
class AuthController(
|
||||||
|
private val authService: AuthService
|
||||||
|
) : AuthAPI {
|
||||||
|
|
||||||
|
@PostMapping("/login")
|
||||||
|
override fun login(
|
||||||
|
@Valid @RequestBody loginRequest: LoginRequest,
|
||||||
|
response: HttpServletResponse
|
||||||
|
): RoomEscapeApiResponse<Void> {
|
||||||
|
val accessToken: TokenResponse = authService.login(loginRequest)
|
||||||
|
val cookie: Cookie = accessToken.toCookie()
|
||||||
|
|
||||||
|
response.addAccessTokenCookie(cookie)
|
||||||
|
|
||||||
|
return RoomEscapeApiResponse.success()
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/login/check")
|
||||||
|
override fun checkLogin(
|
||||||
|
@MemberId @Parameter(hidden = true) memberId: Long
|
||||||
|
): RoomEscapeApiResponse<LoginCheckResponse> {
|
||||||
|
val response = authService.checkLogin(memberId)
|
||||||
|
|
||||||
|
return RoomEscapeApiResponse.success(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/logout")
|
||||||
|
override fun logout(
|
||||||
|
request: HttpServletRequest,
|
||||||
|
response: HttpServletResponse
|
||||||
|
): RoomEscapeApiResponse<Void> {
|
||||||
|
val cookie: Cookie = request.accessTokenCookie()
|
||||||
|
cookie.expire()
|
||||||
|
response.addAccessTokenCookie(cookie)
|
||||||
|
|
||||||
|
return RoomEscapeApiResponse.success()
|
||||||
|
}
|
||||||
|
}
|
||||||
30
src/main/java/roomescape/system/auth/web/AuthDTO.kt
Normal file
30
src/main/java/roomescape/system/auth/web/AuthDTO.kt
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package roomescape.system.auth.web
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema
|
||||||
|
import jakarta.validation.constraints.Email
|
||||||
|
import jakarta.validation.constraints.NotBlank
|
||||||
|
|
||||||
|
@JvmRecord
|
||||||
|
data class TokenResponse(
|
||||||
|
val accessToken: String
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@Schema(name = "로그인 체크 응답", description = "로그인 상태 체크 응답시 사용됩니다.")
|
||||||
|
@JvmRecord
|
||||||
|
data class LoginCheckResponse(
|
||||||
|
@field:Schema(description = "로그인된 회원의 이름")
|
||||||
|
val name: String
|
||||||
|
)
|
||||||
|
|
||||||
|
@Schema(name = "로그인 요청", description = "로그인 요청 시 사용됩니다.")
|
||||||
|
@JvmRecord
|
||||||
|
data class LoginRequest(
|
||||||
|
@Email(message = "이메일 형식이 일치하지 않습니다. 예시: abc123@gmail.com")
|
||||||
|
@field:Schema(description = "필수 값이며, 이메일 형식으로 입력해야 합니다.", example = "abc123@gmail.com")
|
||||||
|
val email: String,
|
||||||
|
|
||||||
|
@NotBlank(message = "비밀번호는 공백일 수 없습니다.")
|
||||||
|
@field:Schema(description = "최소 1글자 이상 입력해야 합니다.")
|
||||||
|
val password: String
|
||||||
|
)
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
package roomescape.system.auth.web.support
|
||||||
|
|
||||||
|
@Target(AnnotationTarget.FUNCTION)
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
annotation class Admin
|
||||||
|
|
||||||
|
@Target(AnnotationTarget.FUNCTION)
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
annotation class LoginRequired
|
||||||
|
|
||||||
|
@Target(AnnotationTarget.VALUE_PARAMETER)
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
annotation class MemberId
|
||||||
@ -0,0 +1,90 @@
|
|||||||
|
package roomescape.system.auth.web.support
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest
|
||||||
|
import jakarta.servlet.http.HttpServletResponse
|
||||||
|
import org.springframework.http.HttpStatus
|
||||||
|
import org.springframework.stereotype.Component
|
||||||
|
import org.springframework.web.method.HandlerMethod
|
||||||
|
import org.springframework.web.servlet.HandlerInterceptor
|
||||||
|
import roomescape.member.business.MemberService
|
||||||
|
import roomescape.member.infrastructure.persistence.Member
|
||||||
|
import roomescape.system.auth.infrastructure.jwt.JwtHandler
|
||||||
|
import roomescape.system.exception.ErrorType
|
||||||
|
import roomescape.system.exception.RoomEscapeException
|
||||||
|
|
||||||
|
private fun Any.isIrrelevantWith(annotationType: Class<out Annotation>): Boolean {
|
||||||
|
if (this !is HandlerMethod) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return !this.hasMethodAnnotation(annotationType)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class LoginInterceptor(
|
||||||
|
private val memberService: MemberService,
|
||||||
|
private val jwtHandler: JwtHandler
|
||||||
|
) : HandlerInterceptor {
|
||||||
|
|
||||||
|
@Throws(Exception::class)
|
||||||
|
override fun preHandle(
|
||||||
|
request: HttpServletRequest,
|
||||||
|
response: HttpServletResponse,
|
||||||
|
handler: Any
|
||||||
|
): Boolean {
|
||||||
|
if (handler.isIrrelevantWith(LoginRequired::class.java)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
val token: String? = request.accessTokenCookie().value
|
||||||
|
val memberId: Long = jwtHandler.getMemberIdFromToken(token)
|
||||||
|
|
||||||
|
return memberService.existsById(memberId)
|
||||||
|
} catch (e: RoomEscapeException) {
|
||||||
|
response.sendRedirect("/login")
|
||||||
|
throw RoomEscapeException(ErrorType.LOGIN_REQUIRED, HttpStatus.FORBIDDEN)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class AdminInterceptor(
|
||||||
|
private val memberService: MemberService,
|
||||||
|
private val jwtHandler: JwtHandler
|
||||||
|
) : HandlerInterceptor {
|
||||||
|
|
||||||
|
@Throws(Exception::class)
|
||||||
|
override fun preHandle(
|
||||||
|
request: HttpServletRequest,
|
||||||
|
response: HttpServletResponse,
|
||||||
|
handler: Any
|
||||||
|
): Boolean {
|
||||||
|
if (handler.isIrrelevantWith(Admin::class.java)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
val member: Member?
|
||||||
|
|
||||||
|
try {
|
||||||
|
val token: String? = request.accessTokenCookie().value
|
||||||
|
val memberId: Long = jwtHandler.getMemberIdFromToken(token)
|
||||||
|
member = memberService.findById(memberId)
|
||||||
|
} catch (e: RoomEscapeException) {
|
||||||
|
response.sendRedirect("/login")
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
|
||||||
|
with(member) {
|
||||||
|
if (this.isAdmin()) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
response.sendRedirect("/login")
|
||||||
|
throw RoomEscapeException(
|
||||||
|
ErrorType.PERMISSION_DOES_NOT_EXIST,
|
||||||
|
String.format("[memberId: %d, Role: %s]", this.id, this.role),
|
||||||
|
HttpStatus.FORBIDDEN
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
package roomescape.system.auth.web.support
|
||||||
|
|
||||||
|
import jakarta.servlet.http.Cookie
|
||||||
|
import jakarta.servlet.http.HttpServletRequest
|
||||||
|
import jakarta.servlet.http.HttpServletResponse
|
||||||
|
import roomescape.system.auth.web.TokenResponse
|
||||||
|
|
||||||
|
const val ACCESS_TOKEN_COOKIE_NAME = "accessToken"
|
||||||
|
|
||||||
|
fun Cookie.expire(): Unit {
|
||||||
|
this.value = ""
|
||||||
|
this.maxAge = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fun TokenResponse.toCookie(): Cookie = Cookie(ACCESS_TOKEN_COOKIE_NAME, this.accessToken)
|
||||||
|
.also { it.maxAge = 1800000 }
|
||||||
|
|
||||||
|
fun HttpServletRequest.accessTokenCookie(): Cookie = this.cookies
|
||||||
|
?.firstOrNull { it.name == ACCESS_TOKEN_COOKIE_NAME }
|
||||||
|
?: Cookie(ACCESS_TOKEN_COOKIE_NAME, "")
|
||||||
|
|
||||||
|
fun HttpServletResponse.addAccessTokenCookie(cookie: Cookie) {
|
||||||
|
cookie.isHttpOnly = true
|
||||||
|
cookie.secure = true
|
||||||
|
cookie.path = "/"
|
||||||
|
this.addCookie(cookie)
|
||||||
|
}
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
package roomescape.system.auth.web.support
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest
|
||||||
|
import org.springframework.core.MethodParameter
|
||||||
|
import org.springframework.stereotype.Component
|
||||||
|
import org.springframework.web.bind.support.WebDataBinderFactory
|
||||||
|
import org.springframework.web.context.request.NativeWebRequest
|
||||||
|
import org.springframework.web.method.support.HandlerMethodArgumentResolver
|
||||||
|
import org.springframework.web.method.support.ModelAndViewContainer
|
||||||
|
import roomescape.system.auth.infrastructure.jwt.JwtHandler
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class MemberIdResolver(
|
||||||
|
private val jwtHandler: JwtHandler
|
||||||
|
) : HandlerMethodArgumentResolver {
|
||||||
|
|
||||||
|
override fun supportsParameter(parameter: MethodParameter): Boolean {
|
||||||
|
return parameter.hasParameterAnnotation(MemberId::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(Exception::class)
|
||||||
|
override fun resolveArgument(
|
||||||
|
parameter: MethodParameter,
|
||||||
|
mavContainer: ModelAndViewContainer?,
|
||||||
|
webRequest: NativeWebRequest,
|
||||||
|
binderFactory: WebDataBinderFactory?
|
||||||
|
): Any {
|
||||||
|
val request: HttpServletRequest = webRequest.nativeRequest as HttpServletRequest
|
||||||
|
val token: String = request.accessTokenCookie().value
|
||||||
|
|
||||||
|
return jwtHandler.getMemberIdFromToken(token)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -7,9 +7,9 @@ import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
|||||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||||
|
|
||||||
import roomescape.system.auth.interceptor.AdminInterceptor;
|
import roomescape.system.auth.web.support.AdminInterceptor;
|
||||||
import roomescape.system.auth.interceptor.LoginInterceptor;
|
import roomescape.system.auth.web.support.LoginInterceptor;
|
||||||
import roomescape.system.auth.resolver.MemberIdResolver;
|
import roomescape.system.auth.web.support.MemberIdResolver;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
public class WebMvcConfig implements WebMvcConfigurer {
|
public class WebMvcConfig implements WebMvcConfigurer {
|
||||||
|
|||||||
@ -43,6 +43,7 @@ public enum ErrorType {
|
|||||||
|
|
||||||
// 500 Internal Server Error,
|
// 500 Internal Server Error,
|
||||||
INTERNAL_SERVER_ERROR("서버 내부에서 에러가 발생하였습니다."),
|
INTERNAL_SERVER_ERROR("서버 내부에서 에러가 발생하였습니다."),
|
||||||
|
UNEXPECTED_ERROR("예상치 못한 에러가 발생하였습니다. 잠시 후 다시 시도해주세요."),
|
||||||
|
|
||||||
// Payment Error
|
// Payment Error
|
||||||
PAYMENT_ERROR("결제(취소)에 실패했습니다. 결제(취소) 정보를 확인해주세요."),
|
PAYMENT_ERROR("결제(취소)에 실패했습니다. 결제(취소) 정보를 확인해주세요."),
|
||||||
|
|||||||
@ -21,8 +21,8 @@ import io.swagger.v3.oas.annotations.tags.Tag;
|
|||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import jakarta.validation.Valid;
|
import jakarta.validation.Valid;
|
||||||
import jakarta.validation.constraints.NotNull;
|
import jakarta.validation.constraints.NotNull;
|
||||||
import roomescape.system.auth.annotation.Admin;
|
import roomescape.system.auth.web.support.Admin;
|
||||||
import roomescape.system.auth.annotation.LoginRequired;
|
import roomescape.system.auth.web.support.LoginRequired;
|
||||||
import roomescape.system.dto.response.ErrorResponse;
|
import roomescape.system.dto.response.ErrorResponse;
|
||||||
import roomescape.system.dto.response.RoomEscapeApiResponse;
|
import roomescape.system.dto.response.RoomEscapeApiResponse;
|
||||||
import roomescape.theme.dto.ThemeRequest;
|
import roomescape.theme.dto.ThemeRequest;
|
||||||
|
|||||||
@ -4,8 +4,8 @@ import org.springframework.stereotype.Controller
|
|||||||
import org.springframework.web.bind.annotation.GetMapping
|
import org.springframework.web.bind.annotation.GetMapping
|
||||||
import org.springframework.web.bind.annotation.PathVariable
|
import org.springframework.web.bind.annotation.PathVariable
|
||||||
import org.springframework.web.bind.annotation.RequestMapping
|
import org.springframework.web.bind.annotation.RequestMapping
|
||||||
import roomescape.system.auth.annotation.Admin
|
import roomescape.system.auth.web.support.Admin
|
||||||
import roomescape.system.auth.annotation.LoginRequired
|
import roomescape.system.auth.web.support.LoginRequired
|
||||||
|
|
||||||
@Controller
|
@Controller
|
||||||
class AuthPageController {
|
class AuthPageController {
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
package roomescape.common
|
package roomescape.common
|
||||||
|
|
||||||
import roomescape.member.domain.Member
|
import roomescape.member.infrastructure.persistence.Member
|
||||||
import roomescape.member.domain.Role
|
import roomescape.member.infrastructure.persistence.Role
|
||||||
|
import roomescape.system.auth.infrastructure.jwt.JwtHandler
|
||||||
|
import roomescape.system.auth.web.LoginRequest
|
||||||
import java.util.concurrent.atomic.AtomicLong
|
import java.util.concurrent.atomic.AtomicLong
|
||||||
|
|
||||||
object MemberFixture {
|
object MemberFixture {
|
||||||
@ -16,5 +18,24 @@ object MemberFixture {
|
|||||||
): Member = Member(id, name, "$account@email.com", password, role)
|
): Member = Member(id, name, "$account@email.com", password, role)
|
||||||
|
|
||||||
fun admin(): Member = create(account = "admin", role = Role.ADMIN)
|
fun admin(): Member = create(account = "admin", role = Role.ADMIN)
|
||||||
|
fun adminLoginRequest(): LoginRequest = LoginRequest(
|
||||||
|
email = admin().email,
|
||||||
|
password = admin().password
|
||||||
|
)
|
||||||
|
|
||||||
fun user(): Member = create(account = "user", role = Role.MEMBER)
|
fun user(): Member = create(account = "user", role = Role.MEMBER)
|
||||||
|
fun userLoginRequest(): LoginRequest = LoginRequest(
|
||||||
|
email = user().email,
|
||||||
|
password = user().password
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
object JwtFixture {
|
||||||
|
const val SECRET_KEY: String = "daijawligagaf@LIJ$@U)9nagnalkkgalijaddljfi"
|
||||||
|
const val EXPIRATION_TIME: Long = 1000 * 60 * 60
|
||||||
|
|
||||||
|
fun create(
|
||||||
|
secretKey: String = SECRET_KEY,
|
||||||
|
expirationTime: Long = EXPIRATION_TIME
|
||||||
|
): JwtHandler = JwtHandler(secretKey, expirationTime)
|
||||||
}
|
}
|
||||||
|
|||||||
93
src/test/java/roomescape/common/RoomescapeApiTest.kt
Normal file
93
src/test/java/roomescape/common/RoomescapeApiTest.kt
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
package roomescape.common
|
||||||
|
|
||||||
|
import com.ninjasquad.springmockk.MockkBean
|
||||||
|
import io.kotest.core.spec.style.BehaviorSpec
|
||||||
|
import io.mockk.every
|
||||||
|
import io.restassured.http.ContentType
|
||||||
|
import io.restassured.module.kotlin.extensions.Given
|
||||||
|
import io.restassured.module.kotlin.extensions.Then
|
||||||
|
import io.restassured.module.kotlin.extensions.When
|
||||||
|
import io.restassured.response.ValidatableResponse
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest
|
||||||
|
import org.springframework.boot.test.web.server.LocalServerPort
|
||||||
|
import org.springframework.data.repository.findByIdOrNull
|
||||||
|
import org.springframework.http.HttpStatus
|
||||||
|
import roomescape.member.infrastructure.persistence.Member
|
||||||
|
import roomescape.member.infrastructure.persistence.MemberRepository
|
||||||
|
import roomescape.system.auth.infrastructure.jwt.JwtHandler
|
||||||
|
import roomescape.system.exception.ErrorType
|
||||||
|
import roomescape.system.exception.RoomEscapeException
|
||||||
|
|
||||||
|
const val NOT_LOGGED_IN_USERID: Long = 0;
|
||||||
|
|
||||||
|
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||||
|
@NoSqlInitialize
|
||||||
|
class RoomescapeApiTest(
|
||||||
|
@LocalServerPort val port: Int? = 9090,
|
||||||
|
) : BehaviorSpec() {
|
||||||
|
|
||||||
|
@MockkBean
|
||||||
|
lateinit var memberRepository: MemberRepository
|
||||||
|
|
||||||
|
@MockkBean
|
||||||
|
lateinit var jwtHandler: JwtHandler
|
||||||
|
|
||||||
|
|
||||||
|
val admin: Member = MemberFixture.admin()
|
||||||
|
val user: Member = MemberFixture.user()
|
||||||
|
|
||||||
|
fun runGetTest(endpoint: String, token: String? = "token", assert: ValidatableResponse.() -> Unit): ValidatableResponse {
|
||||||
|
return Given {
|
||||||
|
port(port!!)
|
||||||
|
header("Cookie", "accessToken=$token")
|
||||||
|
} When {
|
||||||
|
get(endpoint)
|
||||||
|
} Then assert
|
||||||
|
}
|
||||||
|
|
||||||
|
fun runPostTest(
|
||||||
|
endpoint: String,
|
||||||
|
token: String? = "token",
|
||||||
|
body: Any? = null,
|
||||||
|
assert: ValidatableResponse.() -> Unit
|
||||||
|
): ValidatableResponse {
|
||||||
|
return Given {
|
||||||
|
port(port!!)
|
||||||
|
contentType(ContentType.JSON)
|
||||||
|
body?.let { body(it) }
|
||||||
|
header("Cookie", "accessToken=$token")
|
||||||
|
} When {
|
||||||
|
post(endpoint)
|
||||||
|
} Then assert
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setUpAdmin() {
|
||||||
|
every {
|
||||||
|
jwtHandler.getMemberIdFromToken(any())
|
||||||
|
} returns admin.id!!
|
||||||
|
|
||||||
|
every { memberRepository.existsById(admin.id!!) } returns true
|
||||||
|
every { memberRepository.findByIdOrNull(admin.id!!) } returns admin
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setUpUser() {
|
||||||
|
every {
|
||||||
|
jwtHandler.getMemberIdFromToken(any())
|
||||||
|
} returns user.id!!
|
||||||
|
|
||||||
|
every { memberRepository.existsById(user.id!!) } returns true
|
||||||
|
every { memberRepository.findByIdOrNull(user.id!!) } returns user
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setUpNotLoggedIn() {
|
||||||
|
every {
|
||||||
|
jwtHandler.getMemberIdFromToken(any())
|
||||||
|
} returns NOT_LOGGED_IN_USERID
|
||||||
|
|
||||||
|
every { memberRepository.existsById(NOT_LOGGED_IN_USERID) } throws RoomEscapeException(
|
||||||
|
ErrorType.LOGIN_REQUIRED,
|
||||||
|
HttpStatus.FORBIDDEN
|
||||||
|
)
|
||||||
|
every { memberRepository.findByIdOrNull(NOT_LOGGED_IN_USERID) } returns null
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,118 +0,0 @@
|
|||||||
package roomescape.global.auth.jwt;
|
|
||||||
|
|
||||||
import static roomescape.system.exception.ErrorType.*;
|
|
||||||
|
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
import org.assertj.core.api.Assertions;
|
|
||||||
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.beans.factory.annotation.Value;
|
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
|
||||||
import org.springframework.boot.test.web.server.LocalServerPort;
|
|
||||||
import org.springframework.test.context.jdbc.Sql;
|
|
||||||
|
|
||||||
import io.jsonwebtoken.Jwts;
|
|
||||||
import io.jsonwebtoken.SignatureAlgorithm;
|
|
||||||
import io.restassured.RestAssured;
|
|
||||||
import roomescape.system.auth.jwt.JwtHandler;
|
|
||||||
import roomescape.system.exception.ErrorType;
|
|
||||||
import roomescape.system.exception.RoomEscapeException;
|
|
||||||
|
|
||||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
|
||||||
@Sql(scripts = "/truncate.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
|
|
||||||
class JwtHandlerTest {
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private JwtHandler jwtHandler;
|
|
||||||
|
|
||||||
@Value("${security.jwt.token.secret-key}")
|
|
||||||
private String secretKey;
|
|
||||||
|
|
||||||
@LocalServerPort
|
|
||||||
private int port;
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
void setUp() {
|
|
||||||
RestAssured.port = port;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@DisplayName("토큰이 만료되면 401 Unauthorized 를 발생시킨다.")
|
|
||||||
void jwtExpired() {
|
|
||||||
//given
|
|
||||||
Date date = new Date();
|
|
||||||
Date expiredAt = new Date(date.getTime() - 1);
|
|
||||||
|
|
||||||
String accessToken = Jwts.builder()
|
|
||||||
.claim("memberId", 1L)
|
|
||||||
.setIssuedAt(date)
|
|
||||||
.setExpiration(expiredAt)
|
|
||||||
.signWith(SignatureAlgorithm.HS256, secretKey.getBytes())
|
|
||||||
.compact();
|
|
||||||
|
|
||||||
// when & then
|
|
||||||
Assertions.assertThatThrownBy(() -> jwtHandler.getMemberIdFromToken(accessToken))
|
|
||||||
.isInstanceOf(RoomEscapeException.class)
|
|
||||||
.hasMessage(EXPIRED_TOKEN.getDescription());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@DisplayName("지원하지 않는 토큰이면 401 Unauthorized 를 발생시킨다.")
|
|
||||||
void jwtMalformed() {
|
|
||||||
// given
|
|
||||||
Date date = new Date();
|
|
||||||
Date expiredAt = new Date(date.getTime() + 100000);
|
|
||||||
|
|
||||||
String accessToken = Jwts.builder()
|
|
||||||
.claim("memberId", 1L)
|
|
||||||
.setIssuedAt(date)
|
|
||||||
.setExpiration(expiredAt)
|
|
||||||
.signWith(SignatureAlgorithm.HS256, secretKey.getBytes())
|
|
||||||
.compact();
|
|
||||||
|
|
||||||
String[] splitAccessToken = accessToken.split("\\.");
|
|
||||||
String unsupportedAccessToken = splitAccessToken[0] + "." + splitAccessToken[1];
|
|
||||||
|
|
||||||
// when & then
|
|
||||||
Assertions.assertThatThrownBy(() -> jwtHandler.getMemberIdFromToken(unsupportedAccessToken))
|
|
||||||
.isInstanceOf(RoomEscapeException.class)
|
|
||||||
.hasMessage(ErrorType.MALFORMED_TOKEN.getDescription());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@DisplayName("토큰의 Signature 가 잘못되었다면 401 Unauthorized 를 발생시킨다.")
|
|
||||||
void jwtInvalidSignature() {
|
|
||||||
// given
|
|
||||||
Date date = new Date();
|
|
||||||
Date expiredAt = new Date(date.getTime() + 100000);
|
|
||||||
|
|
||||||
String invalidSecretKey = secretKey.substring(1);
|
|
||||||
|
|
||||||
String accessToken = Jwts.builder()
|
|
||||||
.claim("memberId", 1L)
|
|
||||||
.setIssuedAt(date)
|
|
||||||
.setExpiration(expiredAt)
|
|
||||||
.signWith(SignatureAlgorithm.HS256, invalidSecretKey.getBytes()) // 기존은 HS256 알고리즘
|
|
||||||
.compact();
|
|
||||||
|
|
||||||
// when & then
|
|
||||||
Assertions.assertThatThrownBy(() -> jwtHandler.getMemberIdFromToken(accessToken))
|
|
||||||
.isInstanceOf(RoomEscapeException.class)
|
|
||||||
.hasMessage(ErrorType.INVALID_SIGNATURE_TOKEN.getDescription());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@DisplayName("토큰이 공백값이라면 401 Unauthorized 를 발생시킨다.")
|
|
||||||
void jwtIllegal() {
|
|
||||||
// given
|
|
||||||
String accessToken = "";
|
|
||||||
|
|
||||||
// when & then
|
|
||||||
Assertions.assertThatThrownBy(() -> jwtHandler.getMemberIdFromToken(accessToken))
|
|
||||||
.isInstanceOf(RoomEscapeException.class)
|
|
||||||
.hasMessage(ErrorType.ILLEGAL_TOKEN.getDescription());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,72 +0,0 @@
|
|||||||
package roomescape.member.controller;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
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.boot.test.web.server.LocalServerPort;
|
|
||||||
import org.springframework.test.context.jdbc.Sql;
|
|
||||||
|
|
||||||
import io.restassured.RestAssured;
|
|
||||||
import io.restassured.http.ContentType;
|
|
||||||
import io.restassured.http.Header;
|
|
||||||
import roomescape.member.domain.Member;
|
|
||||||
import roomescape.member.domain.Role;
|
|
||||||
import roomescape.member.domain.repository.MemberRepository;
|
|
||||||
|
|
||||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
|
||||||
@Sql(scripts = "/truncate.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
|
|
||||||
class MemberControllerTest {
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private MemberRepository memberRepository;
|
|
||||||
|
|
||||||
@LocalServerPort
|
|
||||||
private int port;
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
void setUp() {
|
|
||||||
RestAssured.port = port;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@DisplayName("/members 로 GET 요청을 보내면 회원 정보와 200 OK 를 받는다.")
|
|
||||||
void getAdminPage() {
|
|
||||||
// given
|
|
||||||
String accessTokenCookie = getAdminAccessTokenCookieByLogin("admin@admin.com", "12341234");
|
|
||||||
|
|
||||||
memberRepository.save(new Member("이름1", "test@test.com", "password", Role.MEMBER));
|
|
||||||
memberRepository.save(new Member("이름2", "test@test.com", "password", Role.MEMBER));
|
|
||||||
memberRepository.save(new Member("이름3", "test@test.com", "password", Role.MEMBER));
|
|
||||||
memberRepository.save(new Member("이름4", "test@test.com", "password", Role.MEMBER));
|
|
||||||
|
|
||||||
// when & then
|
|
||||||
RestAssured.given().log().all()
|
|
||||||
.port(port)
|
|
||||||
.header(new Header("Cookie", accessTokenCookie))
|
|
||||||
.when().get("/members")
|
|
||||||
.then().log().all()
|
|
||||||
.statusCode(200);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getAdminAccessTokenCookieByLogin(final String email, final String password) {
|
|
||||||
memberRepository.save(new Member("이름", email, password, Role.ADMIN));
|
|
||||||
|
|
||||||
Map<String, String> loginParams = Map.of(
|
|
||||||
"email", email,
|
|
||||||
"password", password
|
|
||||||
);
|
|
||||||
|
|
||||||
String accessToken = RestAssured.given().log().all()
|
|
||||||
.contentType(ContentType.JSON)
|
|
||||||
.port(port)
|
|
||||||
.body(loginParams)
|
|
||||||
.when().post("/login")
|
|
||||||
.then().log().all().extract().cookie("accessToken");
|
|
||||||
|
|
||||||
return "accessToken=" + accessToken;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -0,0 +1,66 @@
|
|||||||
|
package roomescape.member.controller
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||||
|
import io.kotest.assertions.assertSoftly
|
||||||
|
import io.kotest.matchers.collections.shouldContainAll
|
||||||
|
import io.kotest.matchers.shouldBe
|
||||||
|
import io.mockk.every
|
||||||
|
import io.restassured.module.kotlin.extensions.Extract
|
||||||
|
import org.hamcrest.Matchers.containsString
|
||||||
|
import roomescape.common.MemberFixture
|
||||||
|
import roomescape.common.RoomescapeApiTest
|
||||||
|
import roomescape.member.web.MembersResponse
|
||||||
|
|
||||||
|
class MemberControllerTest : RoomescapeApiTest() {
|
||||||
|
|
||||||
|
init {
|
||||||
|
given("GET /members 요청을") {
|
||||||
|
val endpoint = "/members"
|
||||||
|
|
||||||
|
every { memberRepository.findAll() } returns listOf(
|
||||||
|
MemberFixture.create(name = "name1"),
|
||||||
|
MemberFixture.create(name = "name2"),
|
||||||
|
MemberFixture.create(name = "name3"),
|
||||||
|
)
|
||||||
|
|
||||||
|
`when`("관리자가 보내면") {
|
||||||
|
setUpAdmin()
|
||||||
|
|
||||||
|
then("성공한다.") {
|
||||||
|
val result: Any = runGetTest(endpoint) {
|
||||||
|
statusCode(200)
|
||||||
|
} Extract {
|
||||||
|
path("data")
|
||||||
|
}
|
||||||
|
|
||||||
|
val response: MembersResponse = jacksonObjectMapper().convertValue(result, MembersResponse::class.java)
|
||||||
|
|
||||||
|
assertSoftly(response.members) {
|
||||||
|
it.size shouldBe 3
|
||||||
|
it.map { m -> m.name } shouldContainAll listOf("name1", "name2", "name3")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
`when`("관리자가 아니면 로그인 페이지로 이동한다.") {
|
||||||
|
then("비회원") {
|
||||||
|
setUpNotLoggedIn()
|
||||||
|
|
||||||
|
runGetTest(endpoint) {
|
||||||
|
statusCode(200)
|
||||||
|
body(containsString("<title>Login</title>"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
then("일반 회원") {
|
||||||
|
setUpUser()
|
||||||
|
|
||||||
|
runGetTest(endpoint) {
|
||||||
|
statusCode(200)
|
||||||
|
body(containsString("<title>Login</title>"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,26 +0,0 @@
|
|||||||
package roomescape.member.domain;
|
|
||||||
|
|
||||||
import org.assertj.core.api.Assertions;
|
|
||||||
import org.junit.jupiter.api.DisplayName;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import roomescape.system.exception.RoomEscapeException;
|
|
||||||
|
|
||||||
class MemberTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@DisplayName("Member 객체를 생성할 때 Role은 반드시 입력되어야 한다.")
|
|
||||||
void createMemberWithoutRole() {
|
|
||||||
// given
|
|
||||||
String name = "name";
|
|
||||||
String email = "email";
|
|
||||||
String password = "password";
|
|
||||||
|
|
||||||
// when
|
|
||||||
Role role = null;
|
|
||||||
|
|
||||||
// then
|
|
||||||
Assertions.assertThatThrownBy(() -> new Member(name, email, password, null))
|
|
||||||
.isInstanceOf(RoomEscapeException.class);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -13,8 +13,8 @@ import org.junit.jupiter.params.provider.CsvSource;
|
|||||||
import org.junit.jupiter.params.provider.NullAndEmptySource;
|
import org.junit.jupiter.params.provider.NullAndEmptySource;
|
||||||
import org.junit.jupiter.params.provider.NullSource;
|
import org.junit.jupiter.params.provider.NullSource;
|
||||||
|
|
||||||
import roomescape.member.domain.Member;
|
import roomescape.member.infrastructure.persistence.Member;
|
||||||
import roomescape.member.domain.Role;
|
import roomescape.member.infrastructure.persistence.Role;
|
||||||
import roomescape.reservation.domain.Reservation;
|
import roomescape.reservation.domain.Reservation;
|
||||||
import roomescape.reservation.domain.ReservationStatus;
|
import roomescape.reservation.domain.ReservationStatus;
|
||||||
import roomescape.reservation.domain.ReservationTime;
|
import roomescape.reservation.domain.ReservationTime;
|
||||||
@ -30,7 +30,7 @@ class PaymentTest {
|
|||||||
LocalDate now = LocalDate.now();
|
LocalDate now = LocalDate.now();
|
||||||
ReservationTime reservationTime = new ReservationTime(LocalTime.now());
|
ReservationTime reservationTime = new ReservationTime(LocalTime.now());
|
||||||
Theme theme = new Theme("name", "desc", "thumb");
|
Theme theme = new Theme("name", "desc", "thumb");
|
||||||
Member member = new Member("name", "email", "password", Role.MEMBER);
|
Member member = new Member(null, "name", "email", "password", Role.MEMBER);
|
||||||
|
|
||||||
reservation = new Reservation(now, reservationTime, theme, member, ReservationStatus.CONFIRMED);
|
reservation = new Reservation(now, reservationTime, theme, member, ReservationStatus.CONFIRMED);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,9 +13,9 @@ import org.springframework.boot.test.context.SpringBootTest;
|
|||||||
import org.springframework.test.context.jdbc.Sql;
|
import org.springframework.test.context.jdbc.Sql;
|
||||||
import org.springframework.test.context.jdbc.Sql.ExecutionPhase;
|
import org.springframework.test.context.jdbc.Sql.ExecutionPhase;
|
||||||
|
|
||||||
import roomescape.member.domain.Member;
|
import roomescape.member.infrastructure.persistence.Member;
|
||||||
import roomescape.member.domain.Role;
|
import roomescape.member.infrastructure.persistence.MemberRepository;
|
||||||
import roomescape.member.domain.repository.MemberRepository;
|
import roomescape.member.infrastructure.persistence.Role;
|
||||||
import roomescape.payment.domain.repository.CanceledPaymentRepository;
|
import roomescape.payment.domain.repository.CanceledPaymentRepository;
|
||||||
import roomescape.payment.dto.request.PaymentCancelRequest;
|
import roomescape.payment.dto.request.PaymentCancelRequest;
|
||||||
import roomescape.payment.dto.response.PaymentResponse;
|
import roomescape.payment.dto.response.PaymentResponse;
|
||||||
@ -54,7 +54,7 @@ class PaymentServiceTest {
|
|||||||
LocalDateTime localDateTime = LocalDateTime.now().plusHours(1L);
|
LocalDateTime localDateTime = LocalDateTime.now().plusHours(1L);
|
||||||
LocalDate date = localDateTime.toLocalDate();
|
LocalDate date = localDateTime.toLocalDate();
|
||||||
ReservationTime time = reservationTimeRepository.save(new ReservationTime(localDateTime.toLocalTime()));
|
ReservationTime time = reservationTimeRepository.save(new ReservationTime(localDateTime.toLocalTime()));
|
||||||
Member member = memberRepository.save(new Member("member", "email@email.com", "password", Role.MEMBER));
|
Member member = memberRepository.save(new Member(null, "member", "email@email.com", "password", Role.MEMBER));
|
||||||
Theme theme = themeRepository.save(new Theme("name", "desc", "thumbnail"));
|
Theme theme = themeRepository.save(new Theme("name", "desc", "thumbnail"));
|
||||||
Reservation reservation = reservationRepository.save(new Reservation(date, time, theme, member,
|
Reservation reservation = reservationRepository.save(new Reservation(date, time, theme, member,
|
||||||
ReservationStatus.CONFIRMED));
|
ReservationStatus.CONFIRMED));
|
||||||
@ -75,7 +75,7 @@ class PaymentServiceTest {
|
|||||||
LocalDateTime localDateTime = LocalDateTime.now().plusHours(1L);
|
LocalDateTime localDateTime = LocalDateTime.now().plusHours(1L);
|
||||||
LocalDate date = localDateTime.toLocalDate();
|
LocalDate date = localDateTime.toLocalDate();
|
||||||
ReservationTime time = reservationTimeRepository.save(new ReservationTime(localDateTime.toLocalTime()));
|
ReservationTime time = reservationTimeRepository.save(new ReservationTime(localDateTime.toLocalTime()));
|
||||||
Member member = memberRepository.save(new Member("member", "email@email.com", "password", Role.MEMBER));
|
Member member = memberRepository.save(new Member(null, "member", "email@email.com", "password", Role.MEMBER));
|
||||||
Theme theme = themeRepository.save(new Theme("name", "desc", "thumbnail"));
|
Theme theme = themeRepository.save(new Theme("name", "desc", "thumbnail"));
|
||||||
Reservation reservation = reservationRepository.save(new Reservation(date, time, theme, member,
|
Reservation reservation = reservationRepository.save(new Reservation(date, time, theme, member,
|
||||||
ReservationStatus.CONFIRMED));
|
ReservationStatus.CONFIRMED));
|
||||||
@ -111,7 +111,7 @@ class PaymentServiceTest {
|
|||||||
LocalDateTime localDateTime = LocalDateTime.now().plusHours(1L);
|
LocalDateTime localDateTime = LocalDateTime.now().plusHours(1L);
|
||||||
LocalDate date = localDateTime.toLocalDate();
|
LocalDate date = localDateTime.toLocalDate();
|
||||||
ReservationTime time = reservationTimeRepository.save(new ReservationTime(localDateTime.toLocalTime()));
|
ReservationTime time = reservationTimeRepository.save(new ReservationTime(localDateTime.toLocalTime()));
|
||||||
Member member = memberRepository.save(new Member("member", "email@email.com", "password", Role.MEMBER));
|
Member member = memberRepository.save(new Member(null, "member", "email@email.com", "password", Role.MEMBER));
|
||||||
Theme theme = themeRepository.save(new Theme("name", "desc", "thumbnail"));
|
Theme theme = themeRepository.save(new Theme("name", "desc", "thumbnail"));
|
||||||
Reservation reservation = reservationRepository.save(new Reservation(date, time, theme, member,
|
Reservation reservation = reservationRepository.save(new Reservation(date, time, theme, member,
|
||||||
ReservationStatus.CONFIRMED));
|
ReservationStatus.CONFIRMED));
|
||||||
|
|||||||
@ -2,7 +2,6 @@ package roomescape.reservation.controller;
|
|||||||
|
|
||||||
import static org.assertj.core.api.Assertions.*;
|
import static org.assertj.core.api.Assertions.*;
|
||||||
import static org.hamcrest.Matchers.*;
|
import static org.hamcrest.Matchers.*;
|
||||||
import static org.mockito.ArgumentMatchers.*;
|
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.Mockito.*;
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
@ -33,9 +32,9 @@ import org.springframework.test.context.jdbc.Sql.ExecutionPhase;
|
|||||||
import io.restassured.RestAssured;
|
import io.restassured.RestAssured;
|
||||||
import io.restassured.http.ContentType;
|
import io.restassured.http.ContentType;
|
||||||
import io.restassured.http.Header;
|
import io.restassured.http.Header;
|
||||||
import roomescape.member.domain.Member;
|
import roomescape.member.infrastructure.persistence.Member;
|
||||||
import roomescape.member.domain.Role;
|
import roomescape.member.infrastructure.persistence.MemberRepository;
|
||||||
import roomescape.member.domain.repository.MemberRepository;
|
import roomescape.member.infrastructure.persistence.Role;
|
||||||
import roomescape.payment.client.TossPaymentClient;
|
import roomescape.payment.client.TossPaymentClient;
|
||||||
import roomescape.payment.domain.CanceledPayment;
|
import roomescape.payment.domain.CanceledPayment;
|
||||||
import roomescape.payment.domain.Payment;
|
import roomescape.payment.domain.Payment;
|
||||||
@ -124,12 +123,12 @@ public class ReservationControllerTest {
|
|||||||
@DisplayName("대기중인 예약을 취소한다.")
|
@DisplayName("대기중인 예약을 취소한다.")
|
||||||
void cancelWaiting() {
|
void cancelWaiting() {
|
||||||
// given
|
// given
|
||||||
Member member = memberRepository.save(new Member("name", "email@email.com", "password", Role.MEMBER));
|
Member member = memberRepository.save(new Member(null, "name", "email@email.com", "password", Role.MEMBER));
|
||||||
String accessTokenCookie = getAccessTokenCookieByLogin("email@email.com", "password");
|
String accessTokenCookie = getAccessTokenCookieByLogin("email@email.com", "password");
|
||||||
|
|
||||||
ReservationTime reservationTime = reservationTimeRepository.save(new ReservationTime(LocalTime.of(17, 30)));
|
ReservationTime reservationTime = reservationTimeRepository.save(new ReservationTime(LocalTime.of(17, 30)));
|
||||||
Theme theme = themeRepository.save(new Theme("테마명", "설명", "썸네일URL"));
|
Theme theme = themeRepository.save(new Theme("테마명", "설명", "썸네일URL"));
|
||||||
Member member1 = memberRepository.save(new Member("name1", "email1r@email.com", "password", Role.MEMBER));
|
Member member1 = memberRepository.save(new Member(null, "name1", "email1r@email.com", "password", Role.MEMBER));
|
||||||
|
|
||||||
// when
|
// when
|
||||||
reservationRepository.save(new Reservation(LocalDate.now().plusDays(1), reservationTime, theme, member1,
|
reservationRepository.save(new Reservation(LocalDate.now().plusDays(1), reservationTime, theme, member1,
|
||||||
@ -151,12 +150,14 @@ public class ReservationControllerTest {
|
|||||||
@DisplayName("회원은 자신이 아닌 다른 회원의 예약을 취소할 수 없다.")
|
@DisplayName("회원은 자신이 아닌 다른 회원의 예약을 취소할 수 없다.")
|
||||||
void cantCancelOtherMembersWaiting() {
|
void cantCancelOtherMembersWaiting() {
|
||||||
// given
|
// given
|
||||||
Member confirmedMember = memberRepository.save(new Member("name", "email@email.com", "password", Role.MEMBER));
|
Member confirmedMember = memberRepository.save(
|
||||||
|
new Member(null, "name", "email@email.com", "password", Role.MEMBER));
|
||||||
String accessTokenCookie = getAccessTokenCookieByLogin("email@email.com", "password");
|
String accessTokenCookie = getAccessTokenCookieByLogin("email@email.com", "password");
|
||||||
|
|
||||||
ReservationTime reservationTime = reservationTimeRepository.save(new ReservationTime(LocalTime.of(17, 30)));
|
ReservationTime reservationTime = reservationTimeRepository.save(new ReservationTime(LocalTime.of(17, 30)));
|
||||||
Theme theme = themeRepository.save(new Theme("테마명", "설명", "썸네일URL"));
|
Theme theme = themeRepository.save(new Theme("테마명", "설명", "썸네일URL"));
|
||||||
Member waitingMember = memberRepository.save(new Member("name1", "email1r@email.com", "password", Role.MEMBER));
|
Member waitingMember = memberRepository.save(
|
||||||
|
new Member(null, "name1", "email1r@email.com", "password", Role.MEMBER));
|
||||||
|
|
||||||
// when
|
// when
|
||||||
reservationRepository.save(new Reservation(LocalDate.now().plusDays(1), reservationTime, theme, confirmedMember,
|
reservationRepository.save(new Reservation(LocalDate.now().plusDays(1), reservationTime, theme, confirmedMember,
|
||||||
@ -182,7 +183,7 @@ public class ReservationControllerTest {
|
|||||||
|
|
||||||
ReservationTime reservationTime = reservationTimeRepository.save(new ReservationTime(LocalTime.of(17, 30)));
|
ReservationTime reservationTime = reservationTimeRepository.save(new ReservationTime(LocalTime.of(17, 30)));
|
||||||
Theme theme = themeRepository.save(new Theme("테마명", "설명", "썸네일URL"));
|
Theme theme = themeRepository.save(new Theme("테마명", "설명", "썸네일URL"));
|
||||||
Member member = memberRepository.save(new Member("name", "email@email.com", "password", Role.MEMBER));
|
Member member = memberRepository.save(new Member(null, "name", "email@email.com", "password", Role.MEMBER));
|
||||||
|
|
||||||
// when
|
// when
|
||||||
reservationRepository.save(
|
reservationRepository.save(
|
||||||
@ -206,7 +207,7 @@ public class ReservationControllerTest {
|
|||||||
@DisplayName("예약 취소는 관리자만 할 수 있다.")
|
@DisplayName("예약 취소는 관리자만 할 수 있다.")
|
||||||
void canRemoveMyReservation() {
|
void canRemoveMyReservation() {
|
||||||
// given
|
// given
|
||||||
Member member = memberRepository.save(new Member("name", "email@email.com", "password", Role.MEMBER));
|
Member member = memberRepository.save(new Member(null, "name", "email@email.com", "password", Role.MEMBER));
|
||||||
String accessTokenCookie = getAccessTokenCookieByLogin(member.getEmail(), member.getPassword());
|
String accessTokenCookie = getAccessTokenCookieByLogin(member.getEmail(), member.getPassword());
|
||||||
|
|
||||||
ReservationTime reservationTime = reservationTimeRepository.save(new ReservationTime(LocalTime.of(17, 30)));
|
ReservationTime reservationTime = reservationTimeRepository.save(new ReservationTime(LocalTime.of(17, 30)));
|
||||||
@ -231,8 +232,10 @@ public class ReservationControllerTest {
|
|||||||
|
|
||||||
ReservationTime reservationTime = reservationTimeRepository.save(new ReservationTime(LocalTime.of(17, 30)));
|
ReservationTime reservationTime = reservationTimeRepository.save(new ReservationTime(LocalTime.of(17, 30)));
|
||||||
Theme theme = themeRepository.save(new Theme("테마명", "설명", "썸네일URL"));
|
Theme theme = themeRepository.save(new Theme("테마명", "설명", "썸네일URL"));
|
||||||
Member confirmedMember = memberRepository.save(new Member("name1", "email@email.com", "password", Role.MEMBER));
|
Member confirmedMember = memberRepository.save(
|
||||||
Member waitingMember = memberRepository.save(new Member("name1", "email1@email.com", "password", Role.MEMBER));
|
new Member(null, "name1", "email@email.com", "password", Role.MEMBER));
|
||||||
|
Member waitingMember = memberRepository.save(
|
||||||
|
new Member(null, "name1", "email1@email.com", "password", Role.MEMBER));
|
||||||
|
|
||||||
reservationRepository.save(
|
reservationRepository.save(
|
||||||
new Reservation(LocalDate.now(), reservationTime, theme, confirmedMember, ReservationStatus.CONFIRMED));
|
new Reservation(LocalDate.now(), reservationTime, theme, confirmedMember, ReservationStatus.CONFIRMED));
|
||||||
@ -252,12 +255,13 @@ public class ReservationControllerTest {
|
|||||||
@DisplayName("본인의 예약이 아니더라도 관리자 권한이 있으면 예약 정보를 삭제할 수 있다.")
|
@DisplayName("본인의 예약이 아니더라도 관리자 권한이 있으면 예약 정보를 삭제할 수 있다.")
|
||||||
void readReservationsSizeAfterPostAndDelete() {
|
void readReservationsSizeAfterPostAndDelete() {
|
||||||
// given
|
// given
|
||||||
Member member = memberRepository.save(new Member("name", "admin@admin.com", "password", Role.ADMIN));
|
Member member = memberRepository.save(new Member(null, "name", "admin@admin.com", "password", Role.ADMIN));
|
||||||
String accessTokenCookie = getAccessTokenCookieByLogin(member.getEmail(), member.getPassword());
|
String accessTokenCookie = getAccessTokenCookieByLogin(member.getEmail(), member.getPassword());
|
||||||
|
|
||||||
ReservationTime reservationTime = reservationTimeRepository.save(new ReservationTime(LocalTime.of(17, 30)));
|
ReservationTime reservationTime = reservationTimeRepository.save(new ReservationTime(LocalTime.of(17, 30)));
|
||||||
Theme theme = themeRepository.save(new Theme("테마명", "설명", "썸네일URL"));
|
Theme theme = themeRepository.save(new Theme("테마명", "설명", "썸네일URL"));
|
||||||
Member anotherMember = memberRepository.save(new Member("name", "email@email.com", "password", Role.MEMBER));
|
Member anotherMember = memberRepository.save(
|
||||||
|
new Member(null, "name", "email@email.com", "password", Role.MEMBER));
|
||||||
|
|
||||||
Reservation reservation = reservationRepository.save(
|
Reservation reservation = reservationRepository.save(
|
||||||
new Reservation(LocalDate.now(), reservationTime, theme, anotherMember, ReservationStatus.CONFIRMED));
|
new Reservation(LocalDate.now(), reservationTime, theme, anotherMember, ReservationStatus.CONFIRMED));
|
||||||
@ -341,7 +345,7 @@ public class ReservationControllerTest {
|
|||||||
ReservationTime time1 = reservationTimeRepository.save(new ReservationTime(LocalTime.of(18, 30)));
|
ReservationTime time1 = reservationTimeRepository.save(new ReservationTime(LocalTime.of(18, 30)));
|
||||||
ReservationTime time2 = reservationTimeRepository.save(new ReservationTime(LocalTime.of(19, 30)));
|
ReservationTime time2 = reservationTimeRepository.save(new ReservationTime(LocalTime.of(19, 30)));
|
||||||
|
|
||||||
Member member = memberRepository.save(new Member("name", "email@email.com", "password", Role.ADMIN));
|
Member member = memberRepository.save(new Member(null, "name", "email@email.com", "password", Role.ADMIN));
|
||||||
String accessToken = getAccessTokenCookieByLogin("email@email.com", "password");
|
String accessToken = getAccessTokenCookieByLogin("email@email.com", "password");
|
||||||
|
|
||||||
// when : 예약은 2개, 예약 대기는 1개 조회되어야 한다.
|
// when : 예약은 2개, 예약 대기는 1개 조회되어야 한다.
|
||||||
@ -371,7 +375,7 @@ public class ReservationControllerTest {
|
|||||||
|
|
||||||
// when
|
// when
|
||||||
Reservation saved = reservationRepository.save(new Reservation(date, time, theme,
|
Reservation saved = reservationRepository.save(new Reservation(date, time, theme,
|
||||||
memberRepository.save(new Member("name", "email@email.com", "password", Role.MEMBER)),
|
memberRepository.save(new Member(null, "name", "email@email.com", "password", Role.MEMBER)),
|
||||||
ReservationStatus.CONFIRMED_PAYMENT_REQUIRED));
|
ReservationStatus.CONFIRMED_PAYMENT_REQUIRED));
|
||||||
|
|
||||||
// then
|
// then
|
||||||
@ -391,7 +395,7 @@ public class ReservationControllerTest {
|
|||||||
LocalDate date = LocalDate.now().plusDays(1);
|
LocalDate date = LocalDate.now().plusDays(1);
|
||||||
Theme theme = themeRepository.save(new Theme("테마명", "설명", "썸네일URL"));
|
Theme theme = themeRepository.save(new Theme("테마명", "설명", "썸네일URL"));
|
||||||
ReservationTime time = reservationTimeRepository.save(new ReservationTime(LocalTime.of(17, 30)));
|
ReservationTime time = reservationTimeRepository.save(new ReservationTime(LocalTime.of(17, 30)));
|
||||||
Member member = memberRepository.save(new Member("name", "email@email.com", "password", Role.MEMBER));
|
Member member = memberRepository.save(new Member(null, "name", "email@email.com", "password", Role.MEMBER));
|
||||||
|
|
||||||
Reservation saved = reservationRepository.save(
|
Reservation saved = reservationRepository.save(
|
||||||
new Reservation(date, time, theme, member, ReservationStatus.CONFIRMED));
|
new Reservation(date, time, theme, member, ReservationStatus.CONFIRMED));
|
||||||
@ -421,7 +425,7 @@ public class ReservationControllerTest {
|
|||||||
LocalDate date = localDateTime.toLocalDate();
|
LocalDate date = localDateTime.toLocalDate();
|
||||||
ReservationTime time = reservationTimeRepository.save(new ReservationTime(localDateTime.toLocalTime()));
|
ReservationTime time = reservationTimeRepository.save(new ReservationTime(localDateTime.toLocalTime()));
|
||||||
Theme theme = themeRepository.save(new Theme("테마명", "설명", "썸네일URL"));
|
Theme theme = themeRepository.save(new Theme("테마명", "설명", "썸네일URL"));
|
||||||
Member member = memberRepository.save(new Member("name", "email@email.com", "password", Role.MEMBER));
|
Member member = memberRepository.save(new Member(null, "name", "email@email.com", "password", Role.MEMBER));
|
||||||
String accessToken = getAccessTokenCookieByLogin(member.getEmail(), member.getPassword());
|
String accessToken = getAccessTokenCookieByLogin(member.getEmail(), member.getPassword());
|
||||||
|
|
||||||
// when : 이전 날짜의 예약을 추가하여 결제 승인 이후 DB 저장 과정에서 예외를 발생시킨다.
|
// when : 이전 날짜의 예약을 추가하여 결제 승인 이후 DB 저장 과정에서 예외를 발생시킨다.
|
||||||
@ -514,8 +518,8 @@ public class ReservationControllerTest {
|
|||||||
LocalDate date = localDateTime.toLocalDate();
|
LocalDate date = localDateTime.toLocalDate();
|
||||||
ReservationTime time = reservationTimeRepository.save(new ReservationTime(localDateTime.toLocalTime()));
|
ReservationTime time = reservationTimeRepository.save(new ReservationTime(localDateTime.toLocalTime()));
|
||||||
Theme theme = themeRepository.save(new Theme("테마명", "설명", "썸네일URL"));
|
Theme theme = themeRepository.save(new Theme("테마명", "설명", "썸네일URL"));
|
||||||
Member member = memberRepository.save(new Member("name", "email@email.com", "password", Role.MEMBER));
|
Member member = memberRepository.save(new Member(null, "name", "email@email.com", "password", Role.MEMBER));
|
||||||
Member member1 = memberRepository.save(new Member("name1", "email1@email.com", "password", Role.MEMBER));
|
Member member1 = memberRepository.save(new Member(null, "name1", "email1@email.com", "password", Role.MEMBER));
|
||||||
|
|
||||||
String accessToken = getAccessTokenCookieByLogin(member.getEmail(), member.getPassword());
|
String accessToken = getAccessTokenCookieByLogin(member.getEmail(), member.getPassword());
|
||||||
reservationRepository.save(new Reservation(date, time, theme, member1, ReservationStatus.CONFIRMED));
|
reservationRepository.save(new Reservation(date, time, theme, member1, ReservationStatus.CONFIRMED));
|
||||||
@ -540,7 +544,7 @@ public class ReservationControllerTest {
|
|||||||
LocalDate date = localDateTime.toLocalDate();
|
LocalDate date = localDateTime.toLocalDate();
|
||||||
ReservationTime time = reservationTimeRepository.save(new ReservationTime(localDateTime.toLocalTime()));
|
ReservationTime time = reservationTimeRepository.save(new ReservationTime(localDateTime.toLocalTime()));
|
||||||
Theme theme = themeRepository.save(new Theme("테마명", "설명", "썸네일URL"));
|
Theme theme = themeRepository.save(new Theme("테마명", "설명", "썸네일URL"));
|
||||||
Member member = memberRepository.save(new Member("name", "email@email.com", "password", Role.MEMBER));
|
Member member = memberRepository.save(new Member(null, "name", "email@email.com", "password", Role.MEMBER));
|
||||||
|
|
||||||
String accessToken = getAdminAccessTokenCookieByLogin("admin@email.com", "password");
|
String accessToken = getAdminAccessTokenCookieByLogin("admin@email.com", "password");
|
||||||
Reservation waiting = reservationRepository.save(
|
Reservation waiting = reservationRepository.save(
|
||||||
@ -585,7 +589,7 @@ public class ReservationControllerTest {
|
|||||||
LocalDate date = localDateTime.toLocalDate();
|
LocalDate date = localDateTime.toLocalDate();
|
||||||
ReservationTime time = reservationTimeRepository.save(new ReservationTime(localDateTime.toLocalTime()));
|
ReservationTime time = reservationTimeRepository.save(new ReservationTime(localDateTime.toLocalTime()));
|
||||||
Theme theme = themeRepository.save(new Theme("테마명", "설명", "썸네일URL"));
|
Theme theme = themeRepository.save(new Theme("테마명", "설명", "썸네일URL"));
|
||||||
Member member = memberRepository.save(new Member("name", "email@email.com", "password", Role.MEMBER));
|
Member member = memberRepository.save(new Member(null, "name", "email@email.com", "password", Role.MEMBER));
|
||||||
|
|
||||||
String adminAccessToken = getAdminAccessTokenCookieByLogin("admin@email.com", "password");
|
String adminAccessToken = getAdminAccessTokenCookieByLogin("admin@email.com", "password");
|
||||||
|
|
||||||
@ -601,7 +605,7 @@ public class ReservationControllerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String getAdminAccessTokenCookieByLogin(final String email, final String password) {
|
private String getAdminAccessTokenCookieByLogin(final String email, final String password) {
|
||||||
memberRepository.save(new Member("이름", email, password, Role.ADMIN));
|
memberRepository.save(new Member(null, "이름", email, password, Role.ADMIN));
|
||||||
|
|
||||||
Map<String, String> loginParams = Map.of(
|
Map<String, String> loginParams = Map.of(
|
||||||
"email", email,
|
"email", email,
|
||||||
|
|||||||
@ -21,9 +21,9 @@ import org.springframework.test.context.jdbc.Sql.ExecutionPhase;
|
|||||||
import io.restassured.RestAssured;
|
import io.restassured.RestAssured;
|
||||||
import io.restassured.http.ContentType;
|
import io.restassured.http.ContentType;
|
||||||
import io.restassured.http.Header;
|
import io.restassured.http.Header;
|
||||||
import roomescape.member.domain.Member;
|
import roomescape.member.infrastructure.persistence.Member;
|
||||||
import roomescape.member.domain.Role;
|
import roomescape.member.infrastructure.persistence.MemberRepository;
|
||||||
import roomescape.member.domain.repository.MemberRepository;
|
import roomescape.member.infrastructure.persistence.Role;
|
||||||
import roomescape.reservation.domain.Reservation;
|
import roomescape.reservation.domain.Reservation;
|
||||||
import roomescape.reservation.domain.ReservationStatus;
|
import roomescape.reservation.domain.ReservationStatus;
|
||||||
import roomescape.reservation.domain.ReservationTime;
|
import roomescape.reservation.domain.ReservationTime;
|
||||||
@ -199,7 +199,7 @@ public class ReservationTimeControllerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String getAdminAccessTokenCookieByLogin(String email, String password) {
|
private String getAdminAccessTokenCookieByLogin(String email, String password) {
|
||||||
memberRepository.save(new Member("이름", email, password, Role.ADMIN));
|
memberRepository.save(new Member(null, "이름", email, password, Role.ADMIN));
|
||||||
|
|
||||||
Map<String, String> loginParams = Map.of(
|
Map<String, String> loginParams = Map.of(
|
||||||
"email", email,
|
"email", email,
|
||||||
@ -225,7 +225,7 @@ public class ReservationTimeControllerTest {
|
|||||||
ReservationTime reservationTime2 = reservationTimeRepository.save(new ReservationTime(LocalTime.of(17, 30)));
|
ReservationTime reservationTime2 = reservationTimeRepository.save(new ReservationTime(LocalTime.of(17, 30)));
|
||||||
ReservationTime reservationTime3 = reservationTimeRepository.save(new ReservationTime(LocalTime.of(18, 30)));
|
ReservationTime reservationTime3 = reservationTimeRepository.save(new ReservationTime(LocalTime.of(18, 30)));
|
||||||
Theme theme = themeRepository.save(new Theme("테마명1", "설명", "썸네일URL"));
|
Theme theme = themeRepository.save(new Theme("테마명1", "설명", "썸네일URL"));
|
||||||
Member member = memberRepository.save(new Member("name", "email@email.com", "password", Role.MEMBER));
|
Member member = memberRepository.save(new Member(null, "name", "email@email.com", "password", Role.MEMBER));
|
||||||
|
|
||||||
reservationRepository.save(
|
reservationRepository.save(
|
||||||
new Reservation(today.plusDays(1), reservationTime1, theme, member, ReservationStatus.CONFIRMED));
|
new Reservation(today.plusDays(1), reservationTime1, theme, member, ReservationStatus.CONFIRMED));
|
||||||
|
|||||||
@ -10,8 +10,8 @@ import org.junit.jupiter.params.ParameterizedTest;
|
|||||||
import org.junit.jupiter.params.provider.Arguments;
|
import org.junit.jupiter.params.provider.Arguments;
|
||||||
import org.junit.jupiter.params.provider.MethodSource;
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
|
|
||||||
import roomescape.member.domain.Member;
|
import roomescape.member.infrastructure.persistence.Member;
|
||||||
import roomescape.member.domain.Role;
|
import roomescape.member.infrastructure.persistence.Role;
|
||||||
import roomescape.system.exception.RoomEscapeException;
|
import roomescape.system.exception.RoomEscapeException;
|
||||||
import roomescape.theme.domain.Theme;
|
import roomescape.theme.domain.Theme;
|
||||||
|
|
||||||
@ -34,17 +34,17 @@ public class ReservationTest {
|
|||||||
Arguments.of(null,
|
Arguments.of(null,
|
||||||
new ReservationTime(LocalTime.now().plusHours(1)),
|
new ReservationTime(LocalTime.now().plusHours(1)),
|
||||||
new Theme("테마명", "설명", "썸네일URI"),
|
new Theme("테마명", "설명", "썸네일URI"),
|
||||||
new Member("name", "email@email.com", "password", Role.MEMBER)),
|
new Member(null, "name", "email@email.com", "password", Role.MEMBER)),
|
||||||
Arguments.of(
|
Arguments.of(
|
||||||
LocalDate.now(),
|
LocalDate.now(),
|
||||||
null,
|
null,
|
||||||
new Theme("테마명", "설명", "썸네일URI"),
|
new Theme("테마명", "설명", "썸네일URI"),
|
||||||
new Member("name", "email@email.com", "password", Role.MEMBER)),
|
new Member(null, "name", "email@email.com", "password", Role.MEMBER)),
|
||||||
Arguments.of(
|
Arguments.of(
|
||||||
LocalDate.now(),
|
LocalDate.now(),
|
||||||
new ReservationTime(LocalTime.now().plusHours(1)),
|
new ReservationTime(LocalTime.now().plusHours(1)),
|
||||||
null,
|
null,
|
||||||
new Member("name", "email@email.com", "password", Role.MEMBER)),
|
new Member(null, "name", "email@email.com", "password", Role.MEMBER)),
|
||||||
Arguments.of(
|
Arguments.of(
|
||||||
LocalDate.now(),
|
LocalDate.now(),
|
||||||
new ReservationTime(LocalTime.now().plusHours(1)),
|
new ReservationTime(LocalTime.now().plusHours(1)),
|
||||||
|
|||||||
@ -13,9 +13,9 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||||||
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
|
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
|
||||||
import org.springframework.data.jpa.domain.Specification;
|
import org.springframework.data.jpa.domain.Specification;
|
||||||
|
|
||||||
import roomescape.member.domain.Member;
|
import roomescape.member.infrastructure.persistence.Member;
|
||||||
import roomescape.member.domain.Role;
|
import roomescape.member.infrastructure.persistence.MemberRepository;
|
||||||
import roomescape.member.domain.repository.MemberRepository;
|
import roomescape.member.infrastructure.persistence.Role;
|
||||||
import roomescape.reservation.domain.Reservation;
|
import roomescape.reservation.domain.Reservation;
|
||||||
import roomescape.reservation.domain.ReservationStatus;
|
import roomescape.reservation.domain.ReservationStatus;
|
||||||
import roomescape.reservation.domain.ReservationTime;
|
import roomescape.reservation.domain.ReservationTime;
|
||||||
@ -50,7 +50,7 @@ class ReservationSearchSpecificationTest {
|
|||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUp() {
|
void setUp() {
|
||||||
LocalDateTime dateTime = LocalDateTime.now();
|
LocalDateTime dateTime = LocalDateTime.now();
|
||||||
Member member = memberRepository.save(new Member("name", "email@email.com", "password", Role.MEMBER));
|
Member member = memberRepository.save(new Member(null, "name", "email@email.com", "password", Role.MEMBER));
|
||||||
ReservationTime time = timeRepository.save(new ReservationTime(dateTime.toLocalTime()));
|
ReservationTime time = timeRepository.save(new ReservationTime(dateTime.toLocalTime()));
|
||||||
Theme theme = themeRepository.save(new Theme("name", "description", "thumbnail"));
|
Theme theme = themeRepository.save(new Theme("name", "description", "thumbnail"));
|
||||||
|
|
||||||
|
|||||||
@ -14,10 +14,10 @@ import org.springframework.context.annotation.Import;
|
|||||||
import org.springframework.test.context.jdbc.Sql;
|
import org.springframework.test.context.jdbc.Sql;
|
||||||
import org.springframework.test.context.jdbc.Sql.ExecutionPhase;
|
import org.springframework.test.context.jdbc.Sql.ExecutionPhase;
|
||||||
|
|
||||||
import roomescape.member.domain.Member;
|
import roomescape.member.business.MemberService;
|
||||||
import roomescape.member.domain.Role;
|
import roomescape.member.infrastructure.persistence.Member;
|
||||||
import roomescape.member.domain.repository.MemberRepository;
|
import roomescape.member.infrastructure.persistence.MemberRepository;
|
||||||
import roomescape.member.service.MemberService;
|
import roomescape.member.infrastructure.persistence.Role;
|
||||||
import roomescape.reservation.domain.Reservation;
|
import roomescape.reservation.domain.Reservation;
|
||||||
import roomescape.reservation.domain.ReservationStatus;
|
import roomescape.reservation.domain.ReservationStatus;
|
||||||
import roomescape.reservation.domain.ReservationTime;
|
import roomescape.reservation.domain.ReservationTime;
|
||||||
@ -53,8 +53,8 @@ class ReservationServiceTest {
|
|||||||
// given
|
// given
|
||||||
ReservationTime reservationTime = reservationTimeRepository.save(new ReservationTime(LocalTime.of(12, 30)));
|
ReservationTime reservationTime = reservationTimeRepository.save(new ReservationTime(LocalTime.of(12, 30)));
|
||||||
Theme theme = themeRepository.save(new Theme("테마명", "설명", "썸네일URL"));
|
Theme theme = themeRepository.save(new Theme("테마명", "설명", "썸네일URL"));
|
||||||
Member member1 = memberRepository.save(new Member("name", "email@email.com", "password", Role.MEMBER));
|
Member member1 = memberRepository.save(new Member(null, "name", "email@email.com", "password", Role.MEMBER));
|
||||||
Member member2 = memberRepository.save(new Member("name2", "email2@email.com", "password", Role.MEMBER));
|
Member member2 = memberRepository.save(new Member(null, "name2", "email2@email.com", "password", Role.MEMBER));
|
||||||
LocalDate date = LocalDate.now().plusDays(1L);
|
LocalDate date = LocalDate.now().plusDays(1L);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
@ -75,7 +75,7 @@ class ReservationServiceTest {
|
|||||||
// given
|
// given
|
||||||
ReservationTime reservationTime = reservationTimeRepository.save(new ReservationTime(LocalTime.of(12, 30)));
|
ReservationTime reservationTime = reservationTimeRepository.save(new ReservationTime(LocalTime.of(12, 30)));
|
||||||
Theme theme = themeRepository.save(new Theme("테마명", "설명", "썸네일URL"));
|
Theme theme = themeRepository.save(new Theme("테마명", "설명", "썸네일URL"));
|
||||||
Member member = memberRepository.save(new Member("name", "email@email.com", "password", Role.MEMBER));
|
Member member = memberRepository.save(new Member(null, "name", "email@email.com", "password", Role.MEMBER));
|
||||||
LocalDate date = LocalDate.now().plusDays(1L);
|
LocalDate date = LocalDate.now().plusDays(1L);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
@ -95,8 +95,8 @@ class ReservationServiceTest {
|
|||||||
// given
|
// given
|
||||||
ReservationTime reservationTime = reservationTimeRepository.save(new ReservationTime(LocalTime.of(12, 30)));
|
ReservationTime reservationTime = reservationTimeRepository.save(new ReservationTime(LocalTime.of(12, 30)));
|
||||||
Theme theme = themeRepository.save(new Theme("테마명", "설명", "썸네일URL"));
|
Theme theme = themeRepository.save(new Theme("테마명", "설명", "썸네일URL"));
|
||||||
Member member = memberRepository.save(new Member("name", "email@email.com", "password", Role.MEMBER));
|
Member member = memberRepository.save(new Member(null, "name", "email@email.com", "password", Role.MEMBER));
|
||||||
Member member1 = memberRepository.save(new Member("name1", "email1@email.com", "password", Role.MEMBER));
|
Member member1 = memberRepository.save(new Member(null, "name1", "email1@email.com", "password", Role.MEMBER));
|
||||||
LocalDate date = LocalDate.now().plusDays(1L);
|
LocalDate date = LocalDate.now().plusDays(1L);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
@ -119,7 +119,7 @@ class ReservationServiceTest {
|
|||||||
// given
|
// given
|
||||||
ReservationTime reservationTime = reservationTimeRepository.save(new ReservationTime(LocalTime.of(12, 30)));
|
ReservationTime reservationTime = reservationTimeRepository.save(new ReservationTime(LocalTime.of(12, 30)));
|
||||||
Theme theme = themeRepository.save(new Theme("테마명", "설명", "썸네일URL"));
|
Theme theme = themeRepository.save(new Theme("테마명", "설명", "썸네일URL"));
|
||||||
Member member = memberRepository.save(new Member("name", "email@email.com", "password", Role.MEMBER));
|
Member member = memberRepository.save(new Member(null, "name", "email@email.com", "password", Role.MEMBER));
|
||||||
LocalDate beforeDate = LocalDate.now().minusDays(1L);
|
LocalDate beforeDate = LocalDate.now().minusDays(1L);
|
||||||
|
|
||||||
// when & then
|
// when & then
|
||||||
@ -136,7 +136,7 @@ class ReservationServiceTest {
|
|||||||
LocalDateTime beforeTime = LocalDateTime.now().minusHours(1L).withNano(0);
|
LocalDateTime beforeTime = LocalDateTime.now().minusHours(1L).withNano(0);
|
||||||
ReservationTime reservationTime = reservationTimeRepository.save(new ReservationTime(beforeTime.toLocalTime()));
|
ReservationTime reservationTime = reservationTimeRepository.save(new ReservationTime(beforeTime.toLocalTime()));
|
||||||
Theme theme = themeRepository.save(new Theme("테마명", "설명", "썸네일URL"));
|
Theme theme = themeRepository.save(new Theme("테마명", "설명", "썸네일URL"));
|
||||||
Member member = memberRepository.save(new Member("name", "email@email.com", "password", Role.MEMBER));
|
Member member = memberRepository.save(new Member(null, "name", "email@email.com", "password", Role.MEMBER));
|
||||||
|
|
||||||
// when & then
|
// when & then
|
||||||
assertThatThrownBy(() -> reservationService.addReservation(
|
assertThatThrownBy(() -> reservationService.addReservation(
|
||||||
@ -180,9 +180,9 @@ class ReservationServiceTest {
|
|||||||
// given
|
// given
|
||||||
ReservationTime reservationTime = reservationTimeRepository.save(new ReservationTime(LocalTime.of(12, 30)));
|
ReservationTime reservationTime = reservationTimeRepository.save(new ReservationTime(LocalTime.of(12, 30)));
|
||||||
Theme theme = themeRepository.save(new Theme("테마명", "설명", "썸네일URL"));
|
Theme theme = themeRepository.save(new Theme("테마명", "설명", "썸네일URL"));
|
||||||
Member admin = memberRepository.save(new Member("admin", "admin@email.com", "password", Role.ADMIN));
|
Member admin = memberRepository.save(new Member(null, "admin", "admin@email.com", "password", Role.ADMIN));
|
||||||
Member member = memberRepository.save(new Member("name", "email@email.com", "password", Role.MEMBER));
|
Member member = memberRepository.save(new Member(null, "name", "email@email.com", "password", Role.MEMBER));
|
||||||
Member member1 = memberRepository.save(new Member("name1", "email1@email.com", "password", Role.MEMBER));
|
Member member1 = memberRepository.save(new Member(null, "name1", "email1@email.com", "password", Role.MEMBER));
|
||||||
|
|
||||||
reservationService.addReservation(
|
reservationService.addReservation(
|
||||||
new ReservationRequest(LocalDate.now().plusDays(1L), reservationTime.getId(), theme.getId(),
|
new ReservationRequest(LocalDate.now().plusDays(1L), reservationTime.getId(), theme.getId(),
|
||||||
@ -203,8 +203,8 @@ class ReservationServiceTest {
|
|||||||
// given
|
// given
|
||||||
ReservationTime reservationTime = reservationTimeRepository.save(new ReservationTime(LocalTime.of(12, 30)));
|
ReservationTime reservationTime = reservationTimeRepository.save(new ReservationTime(LocalTime.of(12, 30)));
|
||||||
Theme theme = themeRepository.save(new Theme("테마명", "설명", "썸네일URL"));
|
Theme theme = themeRepository.save(new Theme("테마명", "설명", "썸네일URL"));
|
||||||
Member admin = memberRepository.save(new Member("admin", "admin@email.com", "password", Role.ADMIN));
|
Member admin = memberRepository.save(new Member(null, "admin", "admin@email.com", "password", Role.ADMIN));
|
||||||
Member member = memberRepository.save(new Member("name", "email@email.com", "password", Role.MEMBER));
|
Member member = memberRepository.save(new Member(null, "name", "email@email.com", "password", Role.MEMBER));
|
||||||
|
|
||||||
// when
|
// when
|
||||||
ReservationResponse waiting = reservationService.addWaiting(
|
ReservationResponse waiting = reservationService.addWaiting(
|
||||||
|
|||||||
@ -13,9 +13,9 @@ import org.springframework.context.annotation.Import;
|
|||||||
import org.springframework.test.context.jdbc.Sql;
|
import org.springframework.test.context.jdbc.Sql;
|
||||||
import org.springframework.test.context.jdbc.Sql.ExecutionPhase;
|
import org.springframework.test.context.jdbc.Sql.ExecutionPhase;
|
||||||
|
|
||||||
import roomescape.member.domain.Member;
|
import roomescape.member.infrastructure.persistence.Member;
|
||||||
import roomescape.member.domain.Role;
|
import roomescape.member.infrastructure.persistence.MemberRepository;
|
||||||
import roomescape.member.domain.repository.MemberRepository;
|
import roomescape.member.infrastructure.persistence.Role;
|
||||||
import roomescape.reservation.domain.Reservation;
|
import roomescape.reservation.domain.Reservation;
|
||||||
import roomescape.reservation.domain.ReservationStatus;
|
import roomescape.reservation.domain.ReservationStatus;
|
||||||
import roomescape.reservation.domain.ReservationTime;
|
import roomescape.reservation.domain.ReservationTime;
|
||||||
@ -75,7 +75,7 @@ class ReservationTimeServiceTest {
|
|||||||
ReservationTime reservationTime = reservationTimeRepository.save(
|
ReservationTime reservationTime = reservationTimeRepository.save(
|
||||||
new ReservationTime(localDateTime.toLocalTime()));
|
new ReservationTime(localDateTime.toLocalTime()));
|
||||||
Theme theme = themeRepository.save(new Theme("테마명", "설명", "썸네일URL"));
|
Theme theme = themeRepository.save(new Theme("테마명", "설명", "썸네일URL"));
|
||||||
Member member = memberRepository.save(new Member("name", "email@email.com", "password", Role.MEMBER));
|
Member member = memberRepository.save(new Member(null, "name", "email@email.com", "password", Role.MEMBER));
|
||||||
|
|
||||||
// when
|
// when
|
||||||
reservationRepository.save(new Reservation(localDateTime.toLocalDate(), reservationTime, theme, member,
|
reservationRepository.save(new Reservation(localDateTime.toLocalDate(), reservationTime, theme, member,
|
||||||
|
|||||||
@ -13,9 +13,9 @@ import org.springframework.boot.test.context.SpringBootTest;
|
|||||||
import org.springframework.test.context.jdbc.Sql;
|
import org.springframework.test.context.jdbc.Sql;
|
||||||
import org.springframework.test.context.jdbc.Sql.ExecutionPhase;
|
import org.springframework.test.context.jdbc.Sql.ExecutionPhase;
|
||||||
|
|
||||||
import roomescape.member.domain.Member;
|
import roomescape.member.infrastructure.persistence.Member;
|
||||||
import roomescape.member.domain.Role;
|
import roomescape.member.infrastructure.persistence.MemberRepository;
|
||||||
import roomescape.member.domain.repository.MemberRepository;
|
import roomescape.member.infrastructure.persistence.Role;
|
||||||
import roomescape.payment.domain.repository.CanceledPaymentRepository;
|
import roomescape.payment.domain.repository.CanceledPaymentRepository;
|
||||||
import roomescape.payment.domain.repository.PaymentRepository;
|
import roomescape.payment.domain.repository.PaymentRepository;
|
||||||
import roomescape.payment.dto.request.PaymentCancelRequest;
|
import roomescape.payment.dto.request.PaymentCancelRequest;
|
||||||
@ -57,7 +57,7 @@ class ReservationWithPaymentServiceTest {
|
|||||||
LocalDateTime localDateTime = LocalDateTime.now().plusDays(1L).withNano(0);
|
LocalDateTime localDateTime = LocalDateTime.now().plusDays(1L).withNano(0);
|
||||||
LocalDate date = localDateTime.toLocalDate();
|
LocalDate date = localDateTime.toLocalDate();
|
||||||
ReservationTime time = reservationTimeRepository.save(new ReservationTime(localDateTime.toLocalTime()));
|
ReservationTime time = reservationTimeRepository.save(new ReservationTime(localDateTime.toLocalTime()));
|
||||||
Member member = memberRepository.save(new Member("member", "email@email.com", "password", Role.MEMBER));
|
Member member = memberRepository.save(new Member(null, "member", "email@email.com", "password", Role.MEMBER));
|
||||||
Theme theme = themeRepository.save(new Theme("name", "desc", "thumbnail"));
|
Theme theme = themeRepository.save(new Theme("name", "desc", "thumbnail"));
|
||||||
ReservationRequest reservationRequest = new ReservationRequest(date, time.getId(), theme.getId(), "payment-key",
|
ReservationRequest reservationRequest = new ReservationRequest(date, time.getId(), theme.getId(), "payment-key",
|
||||||
"order-id", 10000L, "NORMAL");
|
"order-id", 10000L, "NORMAL");
|
||||||
@ -92,7 +92,7 @@ class ReservationWithPaymentServiceTest {
|
|||||||
LocalDateTime localDateTime = LocalDateTime.now().plusDays(1L).withNano(0);
|
LocalDateTime localDateTime = LocalDateTime.now().plusDays(1L).withNano(0);
|
||||||
LocalDate date = localDateTime.toLocalDate();
|
LocalDate date = localDateTime.toLocalDate();
|
||||||
ReservationTime time = reservationTimeRepository.save(new ReservationTime(localDateTime.toLocalTime()));
|
ReservationTime time = reservationTimeRepository.save(new ReservationTime(localDateTime.toLocalTime()));
|
||||||
Member member = memberRepository.save(new Member("member", "admin@email.com", "password", Role.ADMIN));
|
Member member = memberRepository.save(new Member(null, "member", "admin@email.com", "password", Role.ADMIN));
|
||||||
Theme theme = themeRepository.save(new Theme("name", "desc", "thumbnail"));
|
Theme theme = themeRepository.save(new Theme("name", "desc", "thumbnail"));
|
||||||
ReservationRequest reservationRequest = new ReservationRequest(date, time.getId(), theme.getId(), "payment-key",
|
ReservationRequest reservationRequest = new ReservationRequest(date, time.getId(), theme.getId(), "payment-key",
|
||||||
"order-id", 10000L, "NORMAL");
|
"order-id", 10000L, "NORMAL");
|
||||||
@ -119,7 +119,7 @@ class ReservationWithPaymentServiceTest {
|
|||||||
LocalDateTime localDateTime = LocalDateTime.now().plusHours(1L);
|
LocalDateTime localDateTime = LocalDateTime.now().plusHours(1L);
|
||||||
LocalDate date = localDateTime.toLocalDate();
|
LocalDate date = localDateTime.toLocalDate();
|
||||||
ReservationTime time = reservationTimeRepository.save(new ReservationTime(localDateTime.toLocalTime()));
|
ReservationTime time = reservationTimeRepository.save(new ReservationTime(localDateTime.toLocalTime()));
|
||||||
Member member = memberRepository.save(new Member("member", "admin@email.com", "password", Role.ADMIN));
|
Member member = memberRepository.save(new Member(null, "member", "admin@email.com", "password", Role.ADMIN));
|
||||||
Theme theme = themeRepository.save(new Theme("name", "desc", "thumbnail"));
|
Theme theme = themeRepository.save(new Theme("name", "desc", "thumbnail"));
|
||||||
|
|
||||||
Reservation saved = reservationRepository.save(
|
Reservation saved = reservationRepository.save(
|
||||||
@ -140,7 +140,7 @@ class ReservationWithPaymentServiceTest {
|
|||||||
LocalDateTime localDateTime = LocalDateTime.now().plusDays(1L).withNano(0);
|
LocalDateTime localDateTime = LocalDateTime.now().plusDays(1L).withNano(0);
|
||||||
LocalDate date = localDateTime.toLocalDate();
|
LocalDate date = localDateTime.toLocalDate();
|
||||||
ReservationTime time = reservationTimeRepository.save(new ReservationTime(localDateTime.toLocalTime()));
|
ReservationTime time = reservationTimeRepository.save(new ReservationTime(localDateTime.toLocalTime()));
|
||||||
Member member = memberRepository.save(new Member("member", "admin@email.com", "password", Role.ADMIN));
|
Member member = memberRepository.save(new Member(null, "member", "admin@email.com", "password", Role.ADMIN));
|
||||||
Theme theme = themeRepository.save(new Theme("name", "desc", "thumbnail"));
|
Theme theme = themeRepository.save(new Theme("name", "desc", "thumbnail"));
|
||||||
ReservationRequest reservationRequest = new ReservationRequest(date, time.getId(), theme.getId(), "payment-key",
|
ReservationRequest reservationRequest = new ReservationRequest(date, time.getId(), theme.getId(), "payment-key",
|
||||||
"order-id", 10000L, "NORMAL");
|
"order-id", 10000L, "NORMAL");
|
||||||
|
|||||||
@ -0,0 +1,83 @@
|
|||||||
|
package roomescape.system.auth.business
|
||||||
|
|
||||||
|
import io.kotest.assertions.assertSoftly
|
||||||
|
import io.kotest.assertions.throwables.shouldThrow
|
||||||
|
import io.kotest.core.spec.style.BehaviorSpec
|
||||||
|
import io.kotest.matchers.shouldBe
|
||||||
|
import io.mockk.every
|
||||||
|
import io.mockk.mockk
|
||||||
|
import org.springframework.data.repository.findByIdOrNull
|
||||||
|
import roomescape.common.JwtFixture
|
||||||
|
import roomescape.common.MemberFixture
|
||||||
|
import roomescape.member.business.MemberService
|
||||||
|
import roomescape.member.infrastructure.persistence.Member
|
||||||
|
import roomescape.member.infrastructure.persistence.MemberRepository
|
||||||
|
import roomescape.system.auth.infrastructure.jwt.JwtHandler
|
||||||
|
import roomescape.system.auth.service.AuthService
|
||||||
|
import roomescape.system.exception.ErrorType
|
||||||
|
import roomescape.system.exception.RoomEscapeException
|
||||||
|
|
||||||
|
|
||||||
|
class AuthServiceTest : BehaviorSpec({
|
||||||
|
val memberRepository: MemberRepository = mockk()
|
||||||
|
val memberService: MemberService = MemberService(memberRepository)
|
||||||
|
val jwtHandler: JwtHandler = JwtFixture.create()
|
||||||
|
|
||||||
|
val authService = AuthService(memberService, jwtHandler)
|
||||||
|
val user: Member = MemberFixture.user()
|
||||||
|
|
||||||
|
Given("로그인 요청을 받으면") {
|
||||||
|
When("이메일과 비밀번호로 회원을 찾고") {
|
||||||
|
val request = MemberFixture.userLoginRequest()
|
||||||
|
|
||||||
|
Then("회원이 있다면 JWT 토큰을 생성한 뒤 반환한다.") {
|
||||||
|
every {
|
||||||
|
memberRepository.findByEmailAndPassword(request.email, request.password)
|
||||||
|
} returns user
|
||||||
|
|
||||||
|
val accessToken: String = authService.login(request).accessToken
|
||||||
|
|
||||||
|
accessToken.isNotBlank() shouldBe true
|
||||||
|
jwtHandler.getMemberIdFromToken(accessToken) shouldBe user.id
|
||||||
|
}
|
||||||
|
|
||||||
|
Then("회원이 없다면 예외를 던진다.") {
|
||||||
|
every {
|
||||||
|
memberRepository.findByEmailAndPassword(request.email, request.password)
|
||||||
|
} returns null
|
||||||
|
|
||||||
|
val exception = shouldThrow<RoomEscapeException> {
|
||||||
|
authService.login(request)
|
||||||
|
}
|
||||||
|
|
||||||
|
exception.errorType shouldBe ErrorType.MEMBER_NOT_FOUND
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Given("로그인 확인 요청을 받으면") {
|
||||||
|
When("회원 ID로 회원을 찾고") {
|
||||||
|
val userId: Long = user.id!!
|
||||||
|
|
||||||
|
Then("회원이 있다면 회원의 이름을 반환한다.") {
|
||||||
|
every { memberRepository.findByIdOrNull(userId) } returns user
|
||||||
|
|
||||||
|
val response = authService.checkLogin(userId)
|
||||||
|
|
||||||
|
assertSoftly(response) {
|
||||||
|
this.name shouldBe user.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Then("회원이 없다면 예외를 던진다.") {
|
||||||
|
every { memberRepository.findByIdOrNull(userId) } returns null
|
||||||
|
|
||||||
|
val exception = shouldThrow<RoomEscapeException> {
|
||||||
|
authService.checkLogin(userId)
|
||||||
|
}
|
||||||
|
|
||||||
|
exception.errorType shouldBe ErrorType.MEMBER_NOT_FOUND
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
@ -1,118 +0,0 @@
|
|||||||
package roomescape.system.auth.controller;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.*;
|
|
||||||
import static org.hamcrest.Matchers.*;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
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.boot.test.web.server.LocalServerPort;
|
|
||||||
import org.springframework.test.context.jdbc.Sql;
|
|
||||||
|
|
||||||
import io.restassured.RestAssured;
|
|
||||||
import io.restassured.http.ContentType;
|
|
||||||
import roomescape.member.domain.Member;
|
|
||||||
import roomescape.member.domain.Role;
|
|
||||||
import roomescape.member.domain.repository.MemberRepository;
|
|
||||||
|
|
||||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
|
||||||
@Sql(scripts = "/truncate.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
|
|
||||||
class AuthControllerTest {
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private MemberRepository memberRepository;
|
|
||||||
|
|
||||||
@LocalServerPort
|
|
||||||
private int port;
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@DisplayName("로그인에 성공하면 JWT accessToken을 응답 받는다.")
|
|
||||||
void getJwtAccessTokenWhenlogin() {
|
|
||||||
// given
|
|
||||||
String email = "test@email.com";
|
|
||||||
String password = "12341234";
|
|
||||||
memberRepository.save(new Member("이름", email, password, Role.MEMBER));
|
|
||||||
|
|
||||||
Map<String, String> loginParams = Map.of(
|
|
||||||
"email", email,
|
|
||||||
"password", password
|
|
||||||
);
|
|
||||||
|
|
||||||
// when
|
|
||||||
Map<String, String> cookies = RestAssured.given().log().all()
|
|
||||||
.contentType(ContentType.JSON)
|
|
||||||
.port(port)
|
|
||||||
.body(loginParams)
|
|
||||||
.when().post("/login")
|
|
||||||
.then().log().all().extract().cookies();
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertThat(cookies.get("accessToken")).isNotNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@DisplayName("로그인 검증 시, 회원의 name을 응답 받는다.")
|
|
||||||
void checkLogin() {
|
|
||||||
// given
|
|
||||||
String email = "test@test.com";
|
|
||||||
String password = "12341234";
|
|
||||||
String accessTokenCookie = getAccessTokenCookieByLogin(email, password);
|
|
||||||
|
|
||||||
// when & then
|
|
||||||
RestAssured.given().log().all()
|
|
||||||
.contentType(ContentType.JSON)
|
|
||||||
.port(port)
|
|
||||||
.header("cookie", accessTokenCookie)
|
|
||||||
.when().get("/login/check")
|
|
||||||
.then()
|
|
||||||
.body("data.name", is("이름"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@DisplayName("로그인 없이 검증요청을 보내면 401 Unauthorized 를 응답한다.")
|
|
||||||
void checkLoginFailByNotAuthorized() {
|
|
||||||
RestAssured.given().log().all()
|
|
||||||
.contentType(ContentType.JSON)
|
|
||||||
.port(port)
|
|
||||||
.when().get("/login/check")
|
|
||||||
.then()
|
|
||||||
.statusCode(401);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@DisplayName("로그아웃 요청 시, accessToken 쿠키가 삭제된다.")
|
|
||||||
void checkLogout() {
|
|
||||||
// given
|
|
||||||
String accessToken = getAccessTokenCookieByLogin("email@email.com", "password");
|
|
||||||
|
|
||||||
// when & then
|
|
||||||
RestAssured.given().log().all()
|
|
||||||
.port(port)
|
|
||||||
.header("cookie", accessToken)
|
|
||||||
.when().post("/logout")
|
|
||||||
.then()
|
|
||||||
.statusCode(200)
|
|
||||||
.cookie("accessToken", "");
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getAccessTokenCookieByLogin(final String email, final String password) {
|
|
||||||
memberRepository.save(new Member("이름", email, password, Role.ADMIN));
|
|
||||||
|
|
||||||
Map<String, String> loginParams = Map.of(
|
|
||||||
"email", email,
|
|
||||||
"password", password
|
|
||||||
);
|
|
||||||
|
|
||||||
String accessToken = RestAssured.given().log().all()
|
|
||||||
.contentType(ContentType.JSON)
|
|
||||||
.port(port)
|
|
||||||
.body(loginParams)
|
|
||||||
.when().post("/login")
|
|
||||||
.then().log().all().extract().cookie("accessToken");
|
|
||||||
|
|
||||||
return "accessToken=" + accessToken;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -0,0 +1,61 @@
|
|||||||
|
package roomescape.system.auth.infrastructure.jwt
|
||||||
|
|
||||||
|
import io.jsonwebtoken.Jwts
|
||||||
|
import io.jsonwebtoken.SignatureAlgorithm
|
||||||
|
import io.kotest.assertions.throwables.shouldThrow
|
||||||
|
import io.kotest.core.spec.style.FunSpec
|
||||||
|
import io.kotest.matchers.shouldBe
|
||||||
|
import roomescape.common.JwtFixture
|
||||||
|
import roomescape.system.exception.ErrorType
|
||||||
|
import roomescape.system.exception.RoomEscapeException
|
||||||
|
import java.util.*
|
||||||
|
import kotlin.random.Random
|
||||||
|
|
||||||
|
class JwtHandlerTest : FunSpec({
|
||||||
|
|
||||||
|
context("JWT 토큰 조회") {
|
||||||
|
val memberId = Random.nextLong()
|
||||||
|
val jwtHandler: JwtHandler = JwtFixture.create()
|
||||||
|
|
||||||
|
test("토큰에서 멤버 ID를 올바르게 추출한다.") {
|
||||||
|
val token = jwtHandler.createToken(memberId)
|
||||||
|
val extractedMemberId = jwtHandler.getMemberIdFromToken(token)
|
||||||
|
|
||||||
|
extractedMemberId shouldBe memberId
|
||||||
|
}
|
||||||
|
|
||||||
|
test("만료된 토큰이면 예외를 던진다.") {
|
||||||
|
// given
|
||||||
|
val expirationTime = 0L
|
||||||
|
val shortExpirationTimeJwtHandler: JwtHandler = JwtFixture.create(expirationTime = expirationTime)
|
||||||
|
val token = shortExpirationTimeJwtHandler.createToken(memberId)
|
||||||
|
|
||||||
|
Thread.sleep(expirationTime) // 만료 시간 이후로 대기
|
||||||
|
|
||||||
|
// when & then
|
||||||
|
shouldThrow<RoomEscapeException> {
|
||||||
|
shortExpirationTimeJwtHandler.getMemberIdFromToken(token)
|
||||||
|
}.errorType shouldBe ErrorType.EXPIRED_TOKEN
|
||||||
|
}
|
||||||
|
|
||||||
|
test("토큰이 빈 값이면 예외를 던진다.") {
|
||||||
|
shouldThrow<RoomEscapeException> {
|
||||||
|
jwtHandler.getMemberIdFromToken("")
|
||||||
|
}.errorType shouldBe ErrorType.INVALID_TOKEN
|
||||||
|
}
|
||||||
|
|
||||||
|
test("시크릿 키가 잘못된 경우 예외를 던진다.") {
|
||||||
|
val now: Date = Date()
|
||||||
|
val invalidSignatureToken: String = Jwts.builder()
|
||||||
|
.claim("memberId", memberId)
|
||||||
|
.setIssuedAt(now)
|
||||||
|
.setExpiration(Date(now.time + JwtFixture.EXPIRATION_TIME))
|
||||||
|
.signWith(SignatureAlgorithm.HS256, JwtFixture.SECRET_KEY.substring(1).toByteArray())
|
||||||
|
.compact()
|
||||||
|
|
||||||
|
shouldThrow<RoomEscapeException> {
|
||||||
|
jwtHandler.getMemberIdFromToken(invalidSignatureToken)
|
||||||
|
}.errorType shouldBe ErrorType.INVALID_SIGNATURE_TOKEN
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
@ -1,65 +0,0 @@
|
|||||||
package roomescape.system.auth.service;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.*;
|
|
||||||
|
|
||||||
import org.assertj.core.api.Assertions;
|
|
||||||
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.context.annotation.Import;
|
|
||||||
|
|
||||||
import roomescape.member.domain.Member;
|
|
||||||
import roomescape.member.domain.Role;
|
|
||||||
import roomescape.member.domain.repository.MemberRepository;
|
|
||||||
import roomescape.member.service.MemberService;
|
|
||||||
import roomescape.system.auth.dto.LoginRequest;
|
|
||||||
import roomescape.system.auth.jwt.JwtHandler;
|
|
||||||
import roomescape.system.auth.jwt.dto.TokenDto;
|
|
||||||
import roomescape.system.exception.RoomEscapeException;
|
|
||||||
|
|
||||||
@SpringBootTest
|
|
||||||
@Import({AuthService.class, JwtHandler.class, MemberService.class})
|
|
||||||
class AuthServiceTest {
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private AuthService authService;
|
|
||||||
@Autowired
|
|
||||||
private MemberRepository memberRepository;
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@DisplayName("로그인 성공시 JWT accessToken 을 반환한다.")
|
|
||||||
void loginSuccess() {
|
|
||||||
// given
|
|
||||||
Member member = memberRepository.save(new Member("이름", "test@test.com", "12341234", Role.MEMBER));
|
|
||||||
|
|
||||||
// when
|
|
||||||
TokenDto response = authService.login(new LoginRequest(member.getEmail(), member.getPassword()));
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertThat(response.accessToken()).isNotNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@DisplayName("존재하지 않는 회원 email 또는 password로 로그인하면 예외가 발생한다.")
|
|
||||||
void loginFailByNotExistMemberInfo() {
|
|
||||||
// given
|
|
||||||
String notExistEmail = "invalid@test.com";
|
|
||||||
String notExistPassword = "invalid1234";
|
|
||||||
|
|
||||||
// when & then
|
|
||||||
Assertions.assertThatThrownBy(() -> authService.login(new LoginRequest(notExistEmail, notExistPassword)))
|
|
||||||
.isInstanceOf(RoomEscapeException.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@DisplayName("존재하지 않는 회원의 memberId로 로그인 여부를 체크하면 예외가 발생한다.")
|
|
||||||
void checkLoginFailByNotExistMemberInfo() {
|
|
||||||
// given
|
|
||||||
Long notExistMemberId = (long)(memberRepository.findAll().size() + 1);
|
|
||||||
|
|
||||||
// when & then
|
|
||||||
Assertions.assertThatThrownBy(() -> authService.checkLogin(notExistMemberId))
|
|
||||||
.isInstanceOf(RoomEscapeException.class);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
139
src/test/java/roomescape/system/auth/web/AuthControllerTest.kt
Normal file
139
src/test/java/roomescape/system/auth/web/AuthControllerTest.kt
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
package roomescape.system.auth.web
|
||||||
|
|
||||||
|
import io.mockk.every
|
||||||
|
import org.hamcrest.Matchers.containsString
|
||||||
|
import org.hamcrest.Matchers.`is`
|
||||||
|
import org.springframework.data.repository.findByIdOrNull
|
||||||
|
import roomescape.common.MemberFixture
|
||||||
|
import roomescape.common.RoomescapeApiTest
|
||||||
|
import roomescape.system.exception.ErrorType
|
||||||
|
|
||||||
|
class AuthControllerTest : RoomescapeApiTest() {
|
||||||
|
|
||||||
|
val userRequest: LoginRequest = MemberFixture.userLoginRequest()
|
||||||
|
|
||||||
|
init {
|
||||||
|
Given("로그인 요청을 보낼 때") {
|
||||||
|
val endpoint: String = "/login"
|
||||||
|
|
||||||
|
When("로그인에 성공하면") {
|
||||||
|
val expectedToken: String = "expectedToken"
|
||||||
|
|
||||||
|
every {
|
||||||
|
memberRepository.findByEmailAndPassword(userRequest.email, userRequest.password)
|
||||||
|
} returns user
|
||||||
|
|
||||||
|
every {
|
||||||
|
jwtHandler.createToken(user.id!!)
|
||||||
|
} returns expectedToken
|
||||||
|
|
||||||
|
Then("토큰을 쿠키에 담아 응답한다") {
|
||||||
|
runPostTest(endpoint, body = MemberFixture.userLoginRequest()) {
|
||||||
|
statusCode(200)
|
||||||
|
cookie("accessToken", expectedToken)
|
||||||
|
header("Set-Cookie", containsString("Max-Age=1800000"))
|
||||||
|
header("Set-Cookie", containsString("HttpOnly"))
|
||||||
|
header("Set-Cookie", containsString("Secure"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
When("회원을 찾지 못하면") {
|
||||||
|
every {
|
||||||
|
memberRepository.findByEmailAndPassword(userRequest.email, userRequest.password)
|
||||||
|
} returns null
|
||||||
|
|
||||||
|
Then("400 에러를 응답한다") {
|
||||||
|
runPostTest(endpoint, body = userRequest) {
|
||||||
|
log().all()
|
||||||
|
statusCode(400)
|
||||||
|
body("errorType", `is`(ErrorType.MEMBER_NOT_FOUND.name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
When("잘못된 요청을 보내면 400 에러를 응답한다.") {
|
||||||
|
|
||||||
|
Then("이메일 형식이 잘못된 경우") {
|
||||||
|
val invalidRequest: LoginRequest = userRequest.copy(email = "invalid")
|
||||||
|
|
||||||
|
runPostTest(endpoint, body = invalidRequest) {
|
||||||
|
log().all()
|
||||||
|
statusCode(400)
|
||||||
|
body("message", `is`("이메일 형식이 일치하지 않습니다. 예시: abc123@gmail.com"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Then("비밀번호가 공백인 경우") {
|
||||||
|
val invalidRequest = userRequest.copy(password = " ")
|
||||||
|
|
||||||
|
runPostTest(endpoint, body = invalidRequest) {
|
||||||
|
log().all()
|
||||||
|
statusCode(400)
|
||||||
|
body("message", `is`("비밀번호는 공백일 수 없습니다."))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Given("로그인 상태를 확인할 때") {
|
||||||
|
val endpoint: String = "/login/check"
|
||||||
|
|
||||||
|
When("로그인된 회원의 ID로 요청하면") {
|
||||||
|
every { jwtHandler.getMemberIdFromToken(any()) } returns user.id!!
|
||||||
|
every { memberRepository.findByIdOrNull(user.id!!) } returns user
|
||||||
|
|
||||||
|
Then("회원의 이름을 응답한다") {
|
||||||
|
runGetTest(endpoint) {
|
||||||
|
statusCode(200)
|
||||||
|
body("data.name", `is`(user.name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
When("토큰은 있지만 회원을 찾을 수 없으면") {
|
||||||
|
val invalidMemberId: Long = -1L
|
||||||
|
|
||||||
|
every { jwtHandler.getMemberIdFromToken(any()) } returns invalidMemberId
|
||||||
|
every { memberRepository.findByIdOrNull(invalidMemberId) } returns null
|
||||||
|
|
||||||
|
Then("400 에러를 응답한다.") {
|
||||||
|
runGetTest(endpoint) {
|
||||||
|
statusCode(400)
|
||||||
|
body("errorType", `is`(ErrorType.MEMBER_NOT_FOUND.name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Given("로그아웃 요청을 보낼 때") {
|
||||||
|
val endpoint: String = "/logout"
|
||||||
|
|
||||||
|
When("로그인 상태가 아니라면") {
|
||||||
|
setUpNotLoggedIn()
|
||||||
|
|
||||||
|
Then("로그인 페이지로 이동한다.") {
|
||||||
|
runPostTest(endpoint) {
|
||||||
|
log().all()
|
||||||
|
statusCode(302)
|
||||||
|
header("Location", containsString("/login"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
When("로그인 상태라면") {
|
||||||
|
setUpUser()
|
||||||
|
every { memberRepository.findByIdOrNull(user.id!!) } returns user
|
||||||
|
|
||||||
|
Then("토큰의 존재 여부와 무관하게 토큰을 만료시킨다.") {
|
||||||
|
runPostTest(endpoint) {
|
||||||
|
log().all()
|
||||||
|
statusCode(200)
|
||||||
|
cookie("accessToken", "")
|
||||||
|
header("Set-Cookie", containsString("Max-Age=0"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -18,9 +18,9 @@ import org.springframework.test.context.jdbc.Sql.ExecutionPhase;
|
|||||||
import io.restassured.RestAssured;
|
import io.restassured.RestAssured;
|
||||||
import io.restassured.http.ContentType;
|
import io.restassured.http.ContentType;
|
||||||
import io.restassured.http.Header;
|
import io.restassured.http.Header;
|
||||||
import roomescape.member.domain.Member;
|
import roomescape.member.infrastructure.persistence.Member;
|
||||||
import roomescape.member.domain.Role;
|
import roomescape.member.infrastructure.persistence.MemberRepository;
|
||||||
import roomescape.member.domain.repository.MemberRepository;
|
import roomescape.member.infrastructure.persistence.Role;
|
||||||
|
|
||||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||||
@Sql(scripts = "/truncate.sql", executionPhase = ExecutionPhase.BEFORE_TEST_METHOD)
|
@Sql(scripts = "/truncate.sql", executionPhase = ExecutionPhase.BEFORE_TEST_METHOD)
|
||||||
@ -165,7 +165,7 @@ class ThemeControllerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String getAdminAccessTokenCookieByLogin(final String email, final String password) {
|
private String getAdminAccessTokenCookieByLogin(final String email, final String password) {
|
||||||
memberRepository.save(new Member("이름", email, password, Role.ADMIN));
|
memberRepository.save(new Member(null, "이름", email, password, Role.ADMIN));
|
||||||
|
|
||||||
Map<String, String> loginParams = Map.of(
|
Map<String, String> loginParams = Map.of(
|
||||||
"email", email,
|
"email", email,
|
||||||
|
|||||||
@ -13,10 +13,10 @@ import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
|
|||||||
import org.springframework.context.annotation.Import;
|
import org.springframework.context.annotation.Import;
|
||||||
import org.springframework.test.context.jdbc.Sql;
|
import org.springframework.test.context.jdbc.Sql;
|
||||||
|
|
||||||
import roomescape.member.domain.Member;
|
import roomescape.member.business.MemberService;
|
||||||
import roomescape.member.domain.Role;
|
import roomescape.member.infrastructure.persistence.Member;
|
||||||
import roomescape.member.domain.repository.MemberRepository;
|
import roomescape.member.infrastructure.persistence.MemberRepository;
|
||||||
import roomescape.member.service.MemberService;
|
import roomescape.member.infrastructure.persistence.Role;
|
||||||
import roomescape.reservation.dto.request.ReservationRequest;
|
import roomescape.reservation.dto.request.ReservationRequest;
|
||||||
import roomescape.reservation.dto.request.ReservationTimeRequest;
|
import roomescape.reservation.dto.request.ReservationTimeRequest;
|
||||||
import roomescape.reservation.dto.response.ReservationTimeResponse;
|
import roomescape.reservation.dto.response.ReservationTimeResponse;
|
||||||
@ -152,7 +152,7 @@ class ThemeServiceTest {
|
|||||||
ReservationTimeResponse time = reservationTimeService.addTime(
|
ReservationTimeResponse time = reservationTimeService.addTime(
|
||||||
new ReservationTimeRequest(dateTime.toLocalTime()));
|
new ReservationTimeRequest(dateTime.toLocalTime()));
|
||||||
Theme theme = themeRepository.save(new Theme("name", "description", "thumbnail"));
|
Theme theme = themeRepository.save(new Theme("name", "description", "thumbnail"));
|
||||||
Member member = memberRepository.save(new Member("member", "password", "name", Role.MEMBER));
|
Member member = memberRepository.save(new Member(null, "member", "password", "name", Role.MEMBER));
|
||||||
reservationService.addReservation(
|
reservationService.addReservation(
|
||||||
new ReservationRequest(dateTime.toLocalDate(), time.id(), theme.getId(), "paymentKey", "orderId", 1000L,
|
new ReservationRequest(dateTime.toLocalDate(), time.id(), theme.getId(), "paymentKey", "orderId", 1000L,
|
||||||
"NORMAL"), member.getId());
|
"NORMAL"), member.getId());
|
||||||
|
|||||||
@ -1,66 +1,34 @@
|
|||||||
package roomescape.view.controller
|
package roomescape.view.controller
|
||||||
|
|
||||||
import com.ninjasquad.springmockk.MockkBean
|
|
||||||
import io.kotest.core.spec.style.BehaviorSpec
|
|
||||||
import io.mockk.every
|
|
||||||
import io.restassured.module.kotlin.extensions.Given
|
|
||||||
import io.restassured.module.kotlin.extensions.Then
|
|
||||||
import io.restassured.module.kotlin.extensions.When
|
|
||||||
import io.restassured.response.ValidatableResponse
|
|
||||||
import org.hamcrest.Matchers.containsString
|
import org.hamcrest.Matchers.containsString
|
||||||
import org.springframework.boot.test.context.SpringBootTest
|
import roomescape.common.RoomescapeApiTest
|
||||||
import org.springframework.boot.test.web.server.LocalServerPort
|
|
||||||
import org.springframework.http.HttpStatus
|
|
||||||
import roomescape.common.MemberFixture
|
|
||||||
import roomescape.common.NoSqlInitialize
|
|
||||||
import roomescape.member.domain.Member
|
|
||||||
import roomescape.member.service.MemberService
|
|
||||||
import roomescape.system.auth.jwt.JwtHandler
|
|
||||||
import roomescape.system.exception.ErrorType
|
|
||||||
import roomescape.system.exception.RoomEscapeException
|
|
||||||
|
|
||||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
|
||||||
@NoSqlInitialize
|
|
||||||
class PageControllerTest(
|
|
||||||
@LocalServerPort val port: Int,
|
|
||||||
) : BehaviorSpec() {
|
|
||||||
|
|
||||||
@MockkBean
|
|
||||||
private lateinit var jwtHandler: JwtHandler
|
|
||||||
|
|
||||||
@MockkBean
|
|
||||||
private lateinit var memberService: MemberService
|
|
||||||
|
|
||||||
private val admin: Member = MemberFixture.admin()
|
|
||||||
private val user: Member = MemberFixture.user()
|
|
||||||
|
|
||||||
|
class PageControllerTest() : RoomescapeApiTest() {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
listOf("/", "/login").forEach {
|
listOf("/", "/login").forEach {
|
||||||
given("GET $it 요청은") {
|
given("GET $it 요청은") {
|
||||||
`when`("로그인 및 권한 여부와 관계없이 성공한다.") {
|
`when`("로그인 및 권한 여부와 관계없이 성공한다.") {
|
||||||
then("비회원") {
|
then("비회원") {
|
||||||
runTest(it) {
|
setUpNotLoggedIn()
|
||||||
|
|
||||||
|
runGetTest(it) {
|
||||||
statusCode(200)
|
statusCode(200)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
then("회원") {
|
then("회원") {
|
||||||
every {
|
setUpUser()
|
||||||
jwtHandler.getMemberIdFromToken(any())
|
|
||||||
} returns user.id
|
|
||||||
|
|
||||||
runTest(it) {
|
runGetTest(it) {
|
||||||
statusCode(200)
|
statusCode(200)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
then("관리자") {
|
then("관리자") {
|
||||||
every {
|
setUpAdmin()
|
||||||
jwtHandler.getMemberIdFromToken(any())
|
|
||||||
} returns admin.id
|
|
||||||
|
|
||||||
runTest(it) {
|
runGetTest(it) {
|
||||||
statusCode(200)
|
statusCode(200)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -71,24 +39,20 @@ class PageControllerTest(
|
|||||||
listOf("/admin", "/admin/reservation", "/admin/time", "/admin/theme", "/admin/waiting").forEach {
|
listOf("/admin", "/admin/reservation", "/admin/time", "/admin/theme", "/admin/waiting").forEach {
|
||||||
given("GET $it 요청을") {
|
given("GET $it 요청을") {
|
||||||
`when`("관리자가 보내면") {
|
`when`("관리자가 보내면") {
|
||||||
every {
|
setUpAdmin()
|
||||||
jwtHandler.getMemberIdFromToken(any())
|
|
||||||
} returns admin.id
|
|
||||||
|
|
||||||
then("성공한다.") {
|
then("성공한다.") {
|
||||||
runTest(it) {
|
runGetTest(it) {
|
||||||
statusCode(200)
|
statusCode(200)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
`when`("회원이 보내면") {
|
`when`("회원이 보내면") {
|
||||||
every {
|
setUpUser()
|
||||||
jwtHandler.getMemberIdFromToken(any())
|
|
||||||
} returns user.id
|
|
||||||
|
|
||||||
then("로그인 페이지로 이동한다.") {
|
then("로그인 페이지로 이동한다.") {
|
||||||
runTest(it) {
|
runGetTest(it) {
|
||||||
statusCode(200)
|
statusCode(200)
|
||||||
body(containsString("<title>Login</title>"))
|
body(containsString("<title>Login</title>"))
|
||||||
}
|
}
|
||||||
@ -101,20 +65,16 @@ class PageControllerTest(
|
|||||||
given("GET $it 요청을") {
|
given("GET $it 요청을") {
|
||||||
`when`("로그인 된 회원이 보내면 성공한다.") {
|
`when`("로그인 된 회원이 보내면 성공한다.") {
|
||||||
then("회원") {
|
then("회원") {
|
||||||
every {
|
setUpUser()
|
||||||
jwtHandler.getMemberIdFromToken(any())
|
|
||||||
} returns user.id
|
|
||||||
|
|
||||||
runTest(it) {
|
runGetTest(it) {
|
||||||
statusCode(200)
|
statusCode(200)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
then("관리자") {
|
then("관리자") {
|
||||||
every {
|
setUpAdmin()
|
||||||
jwtHandler.getMemberIdFromToken(any())
|
|
||||||
} returns admin.id
|
|
||||||
|
|
||||||
runTest(it) {
|
runGetTest(it) {
|
||||||
statusCode(200)
|
statusCode(200)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -122,11 +82,9 @@ class PageControllerTest(
|
|||||||
|
|
||||||
`when`("로그인 없이 보내면") {
|
`when`("로그인 없이 보내면") {
|
||||||
then("로그인 페이지로 이동한다.") {
|
then("로그인 페이지로 이동한다.") {
|
||||||
every {
|
setUpNotLoggedIn()
|
||||||
jwtHandler.getMemberIdFromToken(any())
|
|
||||||
} returns null
|
|
||||||
|
|
||||||
runTest(it) {
|
runGetTest(it) {
|
||||||
statusCode(200)
|
statusCode(200)
|
||||||
body(containsString("<title>Login</title>"))
|
body(containsString("<title>Login</title>"))
|
||||||
}
|
}
|
||||||
@ -135,27 +93,4 @@ class PageControllerTest(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun runTest(endpoint: String, assert: ValidatableResponse.() -> Unit) {
|
|
||||||
setUpMocks()
|
|
||||||
|
|
||||||
Given {
|
|
||||||
port(port)
|
|
||||||
header("Cookie", "accessToken=token")
|
|
||||||
} When {
|
|
||||||
get(endpoint)
|
|
||||||
} Then assert
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setUpMocks() {
|
|
||||||
every { memberService.findMemberById(admin.id) } returns admin
|
|
||||||
|
|
||||||
every { memberService.findMemberById(user.id) } returns user
|
|
||||||
|
|
||||||
every { memberService.findMemberById(null) } throws RoomEscapeException(
|
|
||||||
ErrorType.MEMBER_NOT_FOUND,
|
|
||||||
String.format("[memberId: %d]", null),
|
|
||||||
HttpStatus.BAD_REQUEST
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user