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.service.ReservationService;
|
||||
import roomescape.reservation.service.ReservationWithPaymentService;
|
||||
import roomescape.system.auth.annotation.Admin;
|
||||
import roomescape.system.auth.annotation.LoginRequired;
|
||||
import roomescape.system.auth.annotation.MemberId;
|
||||
import roomescape.system.auth.web.support.Admin;
|
||||
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;
|
||||
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.ReservationTimesResponse;
|
||||
import roomescape.reservation.service.ReservationTimeService;
|
||||
import roomescape.system.auth.annotation.Admin;
|
||||
import roomescape.system.auth.annotation.LoginRequired;
|
||||
import roomescape.system.auth.web.support.Admin;
|
||||
import roomescape.system.auth.web.support.LoginRequired;
|
||||
import roomescape.system.dto.response.ErrorResponse;
|
||||
import roomescape.system.dto.response.RoomEscapeApiResponse;
|
||||
|
||||
|
||||
@ -15,7 +15,7 @@ import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import roomescape.member.domain.Member;
|
||||
import roomescape.member.infrastructure.persistence.Member;
|
||||
import roomescape.system.exception.ErrorType;
|
||||
import roomescape.system.exception.RoomEscapeException;
|
||||
import roomescape.theme.domain.Theme;
|
||||
|
||||
@ -5,7 +5,7 @@ import java.time.LocalDate;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
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.ReservationStatus;
|
||||
import roomescape.theme.dto.ThemeResponse;
|
||||
|
||||
@ -9,8 +9,8 @@ import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import roomescape.member.domain.Member;
|
||||
import roomescape.member.service.MemberService;
|
||||
import roomescape.member.business.MemberService;
|
||||
import roomescape.member.infrastructure.persistence.Member;
|
||||
import roomescape.reservation.domain.Reservation;
|
||||
import roomescape.reservation.domain.ReservationStatus;
|
||||
import roomescape.reservation.domain.ReservationTime;
|
||||
@ -147,7 +147,7 @@ public class ReservationService {
|
||||
ReservationStatus status) {
|
||||
ReservationTime time = reservationTimeService.findTimeById(timeId);
|
||||
Theme theme = themeService.findThemeById(themeId);
|
||||
Member member = memberService.findMemberById(memberId);
|
||||
Member member = memberService.findById(memberId);
|
||||
|
||||
validateDateAndTime(date, time);
|
||||
return new Reservation(date, time, theme, member, status);
|
||||
@ -213,7 +213,7 @@ public class ReservationService {
|
||||
}
|
||||
|
||||
private void validateIsMemberAdmin(Long memberId) {
|
||||
Member member = memberService.findMemberById(memberId);
|
||||
Member member = memberService.findById(memberId);
|
||||
if (member.isAdmin()) {
|
||||
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.WebMvcConfigurer;
|
||||
|
||||
import roomescape.system.auth.interceptor.AdminInterceptor;
|
||||
import roomescape.system.auth.interceptor.LoginInterceptor;
|
||||
import roomescape.system.auth.resolver.MemberIdResolver;
|
||||
import roomescape.system.auth.web.support.AdminInterceptor;
|
||||
import roomescape.system.auth.web.support.LoginInterceptor;
|
||||
import roomescape.system.auth.web.support.MemberIdResolver;
|
||||
|
||||
@Configuration
|
||||
public class WebMvcConfig implements WebMvcConfigurer {
|
||||
|
||||
@ -43,6 +43,7 @@ public enum ErrorType {
|
||||
|
||||
// 500 Internal Server Error,
|
||||
INTERNAL_SERVER_ERROR("서버 내부에서 에러가 발생하였습니다."),
|
||||
UNEXPECTED_ERROR("예상치 못한 에러가 발생하였습니다. 잠시 후 다시 시도해주세요."),
|
||||
|
||||
// Payment Error
|
||||
PAYMENT_ERROR("결제(취소)에 실패했습니다. 결제(취소) 정보를 확인해주세요."),
|
||||
|
||||
@ -21,8 +21,8 @@ import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import roomescape.system.auth.annotation.Admin;
|
||||
import roomescape.system.auth.annotation.LoginRequired;
|
||||
import roomescape.system.auth.web.support.Admin;
|
||||
import roomescape.system.auth.web.support.LoginRequired;
|
||||
import roomescape.system.dto.response.ErrorResponse;
|
||||
import roomescape.system.dto.response.RoomEscapeApiResponse;
|
||||
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.PathVariable
|
||||
import org.springframework.web.bind.annotation.RequestMapping
|
||||
import roomescape.system.auth.annotation.Admin
|
||||
import roomescape.system.auth.annotation.LoginRequired
|
||||
import roomescape.system.auth.web.support.Admin
|
||||
import roomescape.system.auth.web.support.LoginRequired
|
||||
|
||||
@Controller
|
||||
class AuthPageController {
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
package roomescape.common
|
||||
|
||||
import roomescape.member.domain.Member
|
||||
import roomescape.member.domain.Role
|
||||
import roomescape.member.infrastructure.persistence.Member
|
||||
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
|
||||
|
||||
object MemberFixture {
|
||||
@ -16,5 +18,24 @@ object MemberFixture {
|
||||
): Member = Member(id, name, "$account@email.com", password, role)
|
||||
|
||||
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 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.NullSource;
|
||||
|
||||
import roomescape.member.domain.Member;
|
||||
import roomescape.member.domain.Role;
|
||||
import roomescape.member.infrastructure.persistence.Member;
|
||||
import roomescape.member.infrastructure.persistence.Role;
|
||||
import roomescape.reservation.domain.Reservation;
|
||||
import roomescape.reservation.domain.ReservationStatus;
|
||||
import roomescape.reservation.domain.ReservationTime;
|
||||
@ -30,7 +30,7 @@ class PaymentTest {
|
||||
LocalDate now = LocalDate.now();
|
||||
ReservationTime reservationTime = new ReservationTime(LocalTime.now());
|
||||
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);
|
||||
}
|
||||
|
||||
@ -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.ExecutionPhase;
|
||||
|
||||
import roomescape.member.domain.Member;
|
||||
import roomescape.member.domain.Role;
|
||||
import roomescape.member.domain.repository.MemberRepository;
|
||||
import roomescape.member.infrastructure.persistence.Member;
|
||||
import roomescape.member.infrastructure.persistence.MemberRepository;
|
||||
import roomescape.member.infrastructure.persistence.Role;
|
||||
import roomescape.payment.domain.repository.CanceledPaymentRepository;
|
||||
import roomescape.payment.dto.request.PaymentCancelRequest;
|
||||
import roomescape.payment.dto.response.PaymentResponse;
|
||||
@ -54,7 +54,7 @@ class PaymentServiceTest {
|
||||
LocalDateTime localDateTime = LocalDateTime.now().plusHours(1L);
|
||||
LocalDate date = localDateTime.toLocalDate();
|
||||
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"));
|
||||
Reservation reservation = reservationRepository.save(new Reservation(date, time, theme, member,
|
||||
ReservationStatus.CONFIRMED));
|
||||
@ -75,7 +75,7 @@ class PaymentServiceTest {
|
||||
LocalDateTime localDateTime = LocalDateTime.now().plusHours(1L);
|
||||
LocalDate date = localDateTime.toLocalDate();
|
||||
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"));
|
||||
Reservation reservation = reservationRepository.save(new Reservation(date, time, theme, member,
|
||||
ReservationStatus.CONFIRMED));
|
||||
@ -111,7 +111,7 @@ class PaymentServiceTest {
|
||||
LocalDateTime localDateTime = LocalDateTime.now().plusHours(1L);
|
||||
LocalDate date = localDateTime.toLocalDate();
|
||||
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"));
|
||||
Reservation reservation = reservationRepository.save(new Reservation(date, time, theme, member,
|
||||
ReservationStatus.CONFIRMED));
|
||||
|
||||
@ -2,7 +2,6 @@ package roomescape.reservation.controller;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ -33,9 +32,9 @@ import org.springframework.test.context.jdbc.Sql.ExecutionPhase;
|
||||
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;
|
||||
import roomescape.member.infrastructure.persistence.Member;
|
||||
import roomescape.member.infrastructure.persistence.MemberRepository;
|
||||
import roomescape.member.infrastructure.persistence.Role;
|
||||
import roomescape.payment.client.TossPaymentClient;
|
||||
import roomescape.payment.domain.CanceledPayment;
|
||||
import roomescape.payment.domain.Payment;
|
||||
@ -124,12 +123,12 @@ public class ReservationControllerTest {
|
||||
@DisplayName("대기중인 예약을 취소한다.")
|
||||
void cancelWaiting() {
|
||||
// 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");
|
||||
|
||||
ReservationTime reservationTime = reservationTimeRepository.save(new ReservationTime(LocalTime.of(17, 30)));
|
||||
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
|
||||
reservationRepository.save(new Reservation(LocalDate.now().plusDays(1), reservationTime, theme, member1,
|
||||
@ -151,12 +150,14 @@ public class ReservationControllerTest {
|
||||
@DisplayName("회원은 자신이 아닌 다른 회원의 예약을 취소할 수 없다.")
|
||||
void cantCancelOtherMembersWaiting() {
|
||||
// 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");
|
||||
|
||||
ReservationTime reservationTime = reservationTimeRepository.save(new ReservationTime(LocalTime.of(17, 30)));
|
||||
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
|
||||
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)));
|
||||
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
|
||||
reservationRepository.save(
|
||||
@ -206,7 +207,7 @@ public class ReservationControllerTest {
|
||||
@DisplayName("예약 취소는 관리자만 할 수 있다.")
|
||||
void canRemoveMyReservation() {
|
||||
// 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());
|
||||
|
||||
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)));
|
||||
Theme theme = themeRepository.save(new Theme("테마명", "설명", "썸네일URL"));
|
||||
Member confirmedMember = memberRepository.save(new Member("name1", "email@email.com", "password", Role.MEMBER));
|
||||
Member waitingMember = memberRepository.save(new Member("name1", "email1@email.com", "password", Role.MEMBER));
|
||||
Member confirmedMember = memberRepository.save(
|
||||
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(
|
||||
new Reservation(LocalDate.now(), reservationTime, theme, confirmedMember, ReservationStatus.CONFIRMED));
|
||||
@ -252,12 +255,13 @@ public class ReservationControllerTest {
|
||||
@DisplayName("본인의 예약이 아니더라도 관리자 권한이 있으면 예약 정보를 삭제할 수 있다.")
|
||||
void readReservationsSizeAfterPostAndDelete() {
|
||||
// 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());
|
||||
|
||||
ReservationTime reservationTime = reservationTimeRepository.save(new ReservationTime(LocalTime.of(17, 30)));
|
||||
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(
|
||||
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 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");
|
||||
|
||||
// when : 예약은 2개, 예약 대기는 1개 조회되어야 한다.
|
||||
@ -371,7 +375,7 @@ public class ReservationControllerTest {
|
||||
|
||||
// when
|
||||
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));
|
||||
|
||||
// then
|
||||
@ -391,7 +395,7 @@ public class ReservationControllerTest {
|
||||
LocalDate date = LocalDate.now().plusDays(1);
|
||||
Theme theme = themeRepository.save(new Theme("테마명", "설명", "썸네일URL"));
|
||||
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(
|
||||
new Reservation(date, time, theme, member, ReservationStatus.CONFIRMED));
|
||||
@ -421,7 +425,7 @@ public class ReservationControllerTest {
|
||||
LocalDate date = localDateTime.toLocalDate();
|
||||
ReservationTime time = reservationTimeRepository.save(new ReservationTime(localDateTime.toLocalTime()));
|
||||
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());
|
||||
|
||||
// when : 이전 날짜의 예약을 추가하여 결제 승인 이후 DB 저장 과정에서 예외를 발생시킨다.
|
||||
@ -514,8 +518,8 @@ public class ReservationControllerTest {
|
||||
LocalDate date = localDateTime.toLocalDate();
|
||||
ReservationTime time = reservationTimeRepository.save(new ReservationTime(localDateTime.toLocalTime()));
|
||||
Theme theme = themeRepository.save(new Theme("테마명", "설명", "썸네일URL"));
|
||||
Member member = memberRepository.save(new Member("name", "email@email.com", "password", Role.MEMBER));
|
||||
Member member1 = memberRepository.save(new Member("name1", "email1@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(null, "name1", "email1@email.com", "password", Role.MEMBER));
|
||||
|
||||
String accessToken = getAccessTokenCookieByLogin(member.getEmail(), member.getPassword());
|
||||
reservationRepository.save(new Reservation(date, time, theme, member1, ReservationStatus.CONFIRMED));
|
||||
@ -540,7 +544,7 @@ public class ReservationControllerTest {
|
||||
LocalDate date = localDateTime.toLocalDate();
|
||||
ReservationTime time = reservationTimeRepository.save(new ReservationTime(localDateTime.toLocalTime()));
|
||||
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");
|
||||
Reservation waiting = reservationRepository.save(
|
||||
@ -585,7 +589,7 @@ public class ReservationControllerTest {
|
||||
LocalDate date = localDateTime.toLocalDate();
|
||||
ReservationTime time = reservationTimeRepository.save(new ReservationTime(localDateTime.toLocalTime()));
|
||||
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");
|
||||
|
||||
@ -601,7 +605,7 @@ public class ReservationControllerTest {
|
||||
}
|
||||
|
||||
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(
|
||||
"email", email,
|
||||
|
||||
@ -21,9 +21,9 @@ import org.springframework.test.context.jdbc.Sql.ExecutionPhase;
|
||||
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;
|
||||
import roomescape.member.infrastructure.persistence.Member;
|
||||
import roomescape.member.infrastructure.persistence.MemberRepository;
|
||||
import roomescape.member.infrastructure.persistence.Role;
|
||||
import roomescape.reservation.domain.Reservation;
|
||||
import roomescape.reservation.domain.ReservationStatus;
|
||||
import roomescape.reservation.domain.ReservationTime;
|
||||
@ -199,7 +199,7 @@ public class ReservationTimeControllerTest {
|
||||
}
|
||||
|
||||
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(
|
||||
"email", email,
|
||||
@ -225,7 +225,7 @@ public class ReservationTimeControllerTest {
|
||||
ReservationTime reservationTime2 = reservationTimeRepository.save(new ReservationTime(LocalTime.of(17, 30)));
|
||||
ReservationTime reservationTime3 = reservationTimeRepository.save(new ReservationTime(LocalTime.of(18, 30)));
|
||||
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(
|
||||
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.MethodSource;
|
||||
|
||||
import roomescape.member.domain.Member;
|
||||
import roomescape.member.domain.Role;
|
||||
import roomescape.member.infrastructure.persistence.Member;
|
||||
import roomescape.member.infrastructure.persistence.Role;
|
||||
import roomescape.system.exception.RoomEscapeException;
|
||||
import roomescape.theme.domain.Theme;
|
||||
|
||||
@ -34,17 +34,17 @@ public class ReservationTest {
|
||||
Arguments.of(null,
|
||||
new ReservationTime(LocalTime.now().plusHours(1)),
|
||||
new Theme("테마명", "설명", "썸네일URI"),
|
||||
new Member("name", "email@email.com", "password", Role.MEMBER)),
|
||||
new Member(null, "name", "email@email.com", "password", Role.MEMBER)),
|
||||
Arguments.of(
|
||||
LocalDate.now(),
|
||||
null,
|
||||
new Theme("테마명", "설명", "썸네일URI"),
|
||||
new Member("name", "email@email.com", "password", Role.MEMBER)),
|
||||
new Member(null, "name", "email@email.com", "password", Role.MEMBER)),
|
||||
Arguments.of(
|
||||
LocalDate.now(),
|
||||
new ReservationTime(LocalTime.now().plusHours(1)),
|
||||
null,
|
||||
new Member("name", "email@email.com", "password", Role.MEMBER)),
|
||||
new Member(null, "name", "email@email.com", "password", Role.MEMBER)),
|
||||
Arguments.of(
|
||||
LocalDate.now(),
|
||||
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.data.jpa.domain.Specification;
|
||||
|
||||
import roomescape.member.domain.Member;
|
||||
import roomescape.member.domain.Role;
|
||||
import roomescape.member.domain.repository.MemberRepository;
|
||||
import roomescape.member.infrastructure.persistence.Member;
|
||||
import roomescape.member.infrastructure.persistence.MemberRepository;
|
||||
import roomescape.member.infrastructure.persistence.Role;
|
||||
import roomescape.reservation.domain.Reservation;
|
||||
import roomescape.reservation.domain.ReservationStatus;
|
||||
import roomescape.reservation.domain.ReservationTime;
|
||||
@ -50,7 +50,7 @@ class ReservationSearchSpecificationTest {
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
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()));
|
||||
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.ExecutionPhase;
|
||||
|
||||
import roomescape.member.domain.Member;
|
||||
import roomescape.member.domain.Role;
|
||||
import roomescape.member.domain.repository.MemberRepository;
|
||||
import roomescape.member.service.MemberService;
|
||||
import roomescape.member.business.MemberService;
|
||||
import roomescape.member.infrastructure.persistence.Member;
|
||||
import roomescape.member.infrastructure.persistence.MemberRepository;
|
||||
import roomescape.member.infrastructure.persistence.Role;
|
||||
import roomescape.reservation.domain.Reservation;
|
||||
import roomescape.reservation.domain.ReservationStatus;
|
||||
import roomescape.reservation.domain.ReservationTime;
|
||||
@ -53,8 +53,8 @@ class ReservationServiceTest {
|
||||
// given
|
||||
ReservationTime reservationTime = reservationTimeRepository.save(new ReservationTime(LocalTime.of(12, 30)));
|
||||
Theme theme = themeRepository.save(new Theme("테마명", "설명", "썸네일URL"));
|
||||
Member member1 = memberRepository.save(new Member("name", "email@email.com", "password", Role.MEMBER));
|
||||
Member member2 = memberRepository.save(new Member("name2", "email2@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(null, "name2", "email2@email.com", "password", Role.MEMBER));
|
||||
LocalDate date = LocalDate.now().plusDays(1L);
|
||||
|
||||
// when
|
||||
@ -75,7 +75,7 @@ class ReservationServiceTest {
|
||||
// given
|
||||
ReservationTime reservationTime = reservationTimeRepository.save(new ReservationTime(LocalTime.of(12, 30)));
|
||||
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);
|
||||
|
||||
// when
|
||||
@ -95,8 +95,8 @@ class ReservationServiceTest {
|
||||
// given
|
||||
ReservationTime reservationTime = reservationTimeRepository.save(new ReservationTime(LocalTime.of(12, 30)));
|
||||
Theme theme = themeRepository.save(new Theme("테마명", "설명", "썸네일URL"));
|
||||
Member member = memberRepository.save(new Member("name", "email@email.com", "password", Role.MEMBER));
|
||||
Member member1 = memberRepository.save(new Member("name1", "email1@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(null, "name1", "email1@email.com", "password", Role.MEMBER));
|
||||
LocalDate date = LocalDate.now().plusDays(1L);
|
||||
|
||||
// when
|
||||
@ -119,7 +119,7 @@ class ReservationServiceTest {
|
||||
// given
|
||||
ReservationTime reservationTime = reservationTimeRepository.save(new ReservationTime(LocalTime.of(12, 30)));
|
||||
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);
|
||||
|
||||
// when & then
|
||||
@ -136,7 +136,7 @@ class ReservationServiceTest {
|
||||
LocalDateTime beforeTime = LocalDateTime.now().minusHours(1L).withNano(0);
|
||||
ReservationTime reservationTime = reservationTimeRepository.save(new ReservationTime(beforeTime.toLocalTime()));
|
||||
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
|
||||
assertThatThrownBy(() -> reservationService.addReservation(
|
||||
@ -180,9 +180,9 @@ class ReservationServiceTest {
|
||||
// given
|
||||
ReservationTime reservationTime = reservationTimeRepository.save(new ReservationTime(LocalTime.of(12, 30)));
|
||||
Theme theme = themeRepository.save(new Theme("테마명", "설명", "썸네일URL"));
|
||||
Member admin = memberRepository.save(new Member("admin", "admin@email.com", "password", Role.ADMIN));
|
||||
Member member = memberRepository.save(new Member("name", "email@email.com", "password", Role.MEMBER));
|
||||
Member member1 = memberRepository.save(new Member("name1", "email1@email.com", "password", Role.MEMBER));
|
||||
Member admin = memberRepository.save(new Member(null, "admin", "admin@email.com", "password", Role.ADMIN));
|
||||
Member member = memberRepository.save(new Member(null, "name", "email@email.com", "password", Role.MEMBER));
|
||||
Member member1 = memberRepository.save(new Member(null, "name1", "email1@email.com", "password", Role.MEMBER));
|
||||
|
||||
reservationService.addReservation(
|
||||
new ReservationRequest(LocalDate.now().plusDays(1L), reservationTime.getId(), theme.getId(),
|
||||
@ -203,8 +203,8 @@ class ReservationServiceTest {
|
||||
// given
|
||||
ReservationTime reservationTime = reservationTimeRepository.save(new ReservationTime(LocalTime.of(12, 30)));
|
||||
Theme theme = themeRepository.save(new Theme("테마명", "설명", "썸네일URL"));
|
||||
Member admin = memberRepository.save(new Member("admin", "admin@email.com", "password", Role.ADMIN));
|
||||
Member member = memberRepository.save(new Member("name", "email@email.com", "password", Role.MEMBER));
|
||||
Member admin = memberRepository.save(new Member(null, "admin", "admin@email.com", "password", Role.ADMIN));
|
||||
Member member = memberRepository.save(new Member(null, "name", "email@email.com", "password", Role.MEMBER));
|
||||
|
||||
// when
|
||||
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.ExecutionPhase;
|
||||
|
||||
import roomescape.member.domain.Member;
|
||||
import roomescape.member.domain.Role;
|
||||
import roomescape.member.domain.repository.MemberRepository;
|
||||
import roomescape.member.infrastructure.persistence.Member;
|
||||
import roomescape.member.infrastructure.persistence.MemberRepository;
|
||||
import roomescape.member.infrastructure.persistence.Role;
|
||||
import roomescape.reservation.domain.Reservation;
|
||||
import roomescape.reservation.domain.ReservationStatus;
|
||||
import roomescape.reservation.domain.ReservationTime;
|
||||
@ -75,7 +75,7 @@ class ReservationTimeServiceTest {
|
||||
ReservationTime reservationTime = reservationTimeRepository.save(
|
||||
new ReservationTime(localDateTime.toLocalTime()));
|
||||
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
|
||||
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.ExecutionPhase;
|
||||
|
||||
import roomescape.member.domain.Member;
|
||||
import roomescape.member.domain.Role;
|
||||
import roomescape.member.domain.repository.MemberRepository;
|
||||
import roomescape.member.infrastructure.persistence.Member;
|
||||
import roomescape.member.infrastructure.persistence.MemberRepository;
|
||||
import roomescape.member.infrastructure.persistence.Role;
|
||||
import roomescape.payment.domain.repository.CanceledPaymentRepository;
|
||||
import roomescape.payment.domain.repository.PaymentRepository;
|
||||
import roomescape.payment.dto.request.PaymentCancelRequest;
|
||||
@ -57,7 +57,7 @@ class ReservationWithPaymentServiceTest {
|
||||
LocalDateTime localDateTime = LocalDateTime.now().plusDays(1L).withNano(0);
|
||||
LocalDate date = localDateTime.toLocalDate();
|
||||
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"));
|
||||
ReservationRequest reservationRequest = new ReservationRequest(date, time.getId(), theme.getId(), "payment-key",
|
||||
"order-id", 10000L, "NORMAL");
|
||||
@ -92,7 +92,7 @@ class ReservationWithPaymentServiceTest {
|
||||
LocalDateTime localDateTime = LocalDateTime.now().plusDays(1L).withNano(0);
|
||||
LocalDate date = localDateTime.toLocalDate();
|
||||
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"));
|
||||
ReservationRequest reservationRequest = new ReservationRequest(date, time.getId(), theme.getId(), "payment-key",
|
||||
"order-id", 10000L, "NORMAL");
|
||||
@ -119,7 +119,7 @@ class ReservationWithPaymentServiceTest {
|
||||
LocalDateTime localDateTime = LocalDateTime.now().plusHours(1L);
|
||||
LocalDate date = localDateTime.toLocalDate();
|
||||
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"));
|
||||
|
||||
Reservation saved = reservationRepository.save(
|
||||
@ -140,7 +140,7 @@ class ReservationWithPaymentServiceTest {
|
||||
LocalDateTime localDateTime = LocalDateTime.now().plusDays(1L).withNano(0);
|
||||
LocalDate date = localDateTime.toLocalDate();
|
||||
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"));
|
||||
ReservationRequest reservationRequest = new ReservationRequest(date, time.getId(), theme.getId(), "payment-key",
|
||||
"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.http.ContentType;
|
||||
import io.restassured.http.Header;
|
||||
import roomescape.member.domain.Member;
|
||||
import roomescape.member.domain.Role;
|
||||
import roomescape.member.domain.repository.MemberRepository;
|
||||
import roomescape.member.infrastructure.persistence.Member;
|
||||
import roomescape.member.infrastructure.persistence.MemberRepository;
|
||||
import roomescape.member.infrastructure.persistence.Role;
|
||||
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||
@Sql(scripts = "/truncate.sql", executionPhase = ExecutionPhase.BEFORE_TEST_METHOD)
|
||||
@ -165,7 +165,7 @@ class ThemeControllerTest {
|
||||
}
|
||||
|
||||
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(
|
||||
"email", email,
|
||||
|
||||
@ -13,10 +13,10 @@ import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.test.context.jdbc.Sql;
|
||||
|
||||
import roomescape.member.domain.Member;
|
||||
import roomescape.member.domain.Role;
|
||||
import roomescape.member.domain.repository.MemberRepository;
|
||||
import roomescape.member.service.MemberService;
|
||||
import roomescape.member.business.MemberService;
|
||||
import roomescape.member.infrastructure.persistence.Member;
|
||||
import roomescape.member.infrastructure.persistence.MemberRepository;
|
||||
import roomescape.member.infrastructure.persistence.Role;
|
||||
import roomescape.reservation.dto.request.ReservationRequest;
|
||||
import roomescape.reservation.dto.request.ReservationTimeRequest;
|
||||
import roomescape.reservation.dto.response.ReservationTimeResponse;
|
||||
@ -152,7 +152,7 @@ class ThemeServiceTest {
|
||||
ReservationTimeResponse time = reservationTimeService.addTime(
|
||||
new ReservationTimeRequest(dateTime.toLocalTime()));
|
||||
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(
|
||||
new ReservationRequest(dateTime.toLocalDate(), time.id(), theme.getId(), "paymentKey", "orderId", 1000L,
|
||||
"NORMAL"), member.getId());
|
||||
|
||||
@ -1,66 +1,34 @@
|
||||
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.springframework.boot.test.context.SpringBootTest
|
||||
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()
|
||||
import roomescape.common.RoomescapeApiTest
|
||||
|
||||
class PageControllerTest() : RoomescapeApiTest() {
|
||||
|
||||
init {
|
||||
listOf("/", "/login").forEach {
|
||||
given("GET $it 요청은") {
|
||||
`when`("로그인 및 권한 여부와 관계없이 성공한다.") {
|
||||
then("비회원") {
|
||||
runTest(it) {
|
||||
setUpNotLoggedIn()
|
||||
|
||||
runGetTest(it) {
|
||||
statusCode(200)
|
||||
}
|
||||
}
|
||||
|
||||
then("회원") {
|
||||
every {
|
||||
jwtHandler.getMemberIdFromToken(any())
|
||||
} returns user.id
|
||||
setUpUser()
|
||||
|
||||
runTest(it) {
|
||||
runGetTest(it) {
|
||||
statusCode(200)
|
||||
}
|
||||
}
|
||||
|
||||
then("관리자") {
|
||||
every {
|
||||
jwtHandler.getMemberIdFromToken(any())
|
||||
} returns admin.id
|
||||
setUpAdmin()
|
||||
|
||||
runTest(it) {
|
||||
runGetTest(it) {
|
||||
statusCode(200)
|
||||
}
|
||||
}
|
||||
@ -71,24 +39,20 @@ class PageControllerTest(
|
||||
listOf("/admin", "/admin/reservation", "/admin/time", "/admin/theme", "/admin/waiting").forEach {
|
||||
given("GET $it 요청을") {
|
||||
`when`("관리자가 보내면") {
|
||||
every {
|
||||
jwtHandler.getMemberIdFromToken(any())
|
||||
} returns admin.id
|
||||
setUpAdmin()
|
||||
|
||||
then("성공한다.") {
|
||||
runTest(it) {
|
||||
runGetTest(it) {
|
||||
statusCode(200)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
`when`("회원이 보내면") {
|
||||
every {
|
||||
jwtHandler.getMemberIdFromToken(any())
|
||||
} returns user.id
|
||||
setUpUser()
|
||||
|
||||
then("로그인 페이지로 이동한다.") {
|
||||
runTest(it) {
|
||||
runGetTest(it) {
|
||||
statusCode(200)
|
||||
body(containsString("<title>Login</title>"))
|
||||
}
|
||||
@ -101,20 +65,16 @@ class PageControllerTest(
|
||||
given("GET $it 요청을") {
|
||||
`when`("로그인 된 회원이 보내면 성공한다.") {
|
||||
then("회원") {
|
||||
every {
|
||||
jwtHandler.getMemberIdFromToken(any())
|
||||
} returns user.id
|
||||
setUpUser()
|
||||
|
||||
runTest(it) {
|
||||
runGetTest(it) {
|
||||
statusCode(200)
|
||||
}
|
||||
}
|
||||
then("관리자") {
|
||||
every {
|
||||
jwtHandler.getMemberIdFromToken(any())
|
||||
} returns admin.id
|
||||
setUpAdmin()
|
||||
|
||||
runTest(it) {
|
||||
runGetTest(it) {
|
||||
statusCode(200)
|
||||
}
|
||||
}
|
||||
@ -122,11 +82,9 @@ class PageControllerTest(
|
||||
|
||||
`when`("로그인 없이 보내면") {
|
||||
then("로그인 페이지로 이동한다.") {
|
||||
every {
|
||||
jwtHandler.getMemberIdFromToken(any())
|
||||
} returns null
|
||||
setUpNotLoggedIn()
|
||||
|
||||
runTest(it) {
|
||||
runGetTest(it) {
|
||||
statusCode(200)
|
||||
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