From c487fd5cec6e295292890bef69b3805e8723c60b Mon Sep 17 00:00:00 2001 From: june-777 Date: Sun, 11 Aug 2024 19:23:30 +0900 Subject: [PATCH 01/47] =?UTF-8?q?[feat]=20=EA=B8=B0=EB=8A=A5=20=EC=9A=94?= =?UTF-8?q?=EA=B5=AC=EC=82=AC=ED=95=AD=EC=97=90=20=EB=A7=9E=EA=B2=8C=20Sto?= =?UTF-8?q?re=20=EB=8F=84=EB=A9=94=EC=9D=B8=20=EC=97=94=ED=8B=B0=ED=8B=B0?= =?UTF-8?q?=EC=9D=98=20=EC=9D=B8=EC=8A=A4=ED=84=B4=EC=8A=A4=20=ED=95=84?= =?UTF-8?q?=EB=93=9C=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 기능 요구사항: 가게 이름, 전화번호, 위치 정보, 카테고리, 이용 시간, 최소 주문 금액을 입력해 가게를 등록 notice: - 카테고리 → StoreCategory 도메인 엔티티 - 주소 → Address 임베디드 타입 - 이용 시간 → StoreTime 임베디드 타입 - 도메인 생성 검증은 StoreValidator 에서 절차지향으로 진행 --- .../camp/woowak/lab/store/domain/Store.java | 64 +++++++++++++++++-- 1 file changed, 58 insertions(+), 6 deletions(-) diff --git a/src/main/java/camp/woowak/lab/store/domain/Store.java b/src/main/java/camp/woowak/lab/store/domain/Store.java index 9626c773..d2a19ade 100644 --- a/src/main/java/camp/woowak/lab/store/domain/Store.java +++ b/src/main/java/camp/woowak/lab/store/domain/Store.java @@ -1,13 +1,65 @@ package camp.woowak.lab.store.domain; +import java.time.LocalDateTime; + import camp.woowak.lab.vendor.domain.Vendor; -import jakarta.persistence.*; +import jakarta.persistence.Column; +import jakarta.persistence.Embedded; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToOne; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; @Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) public class Store { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - @ManyToOne(fetch = FetchType.LAZY) - private Vendor owner; + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "vendor_id", nullable = false) + private Vendor owner; + + @OneToOne + @JoinColumn(name = "store_category_id", nullable = false) + private StoreCategory storeCategory; + + @Column(nullable = false) + private String name; + + // TODO: 위치 정보에 대한 요구사항 논의 후 수정 예정. + // i.g) 송파구로 특정, 도시 정보로 특정 등 요구사항이 정의되어야 엔티티 설계를 진행할 수 있음 + @Embedded + private Address address; + + @Column(nullable = false) + private String phoneNumber; + + @Column(nullable = false) + private Integer minOrderPrice; + + @Embedded + private StoreTime storeTime; + + public Store(Vendor owner, StoreCategory storeCategory, String name, String address, String phoneNumber, + Integer minOrderPrice, LocalDateTime startTime, LocalDateTime endTime + ) { + StoreValidator.validate(name, address, minOrderPrice, startTime, endTime); + this.owner = owner; + this.storeCategory = storeCategory; + this.name = name; + this.address = new Address(address); + this.phoneNumber = phoneNumber; + this.minOrderPrice = minOrderPrice; + this.storeTime = new StoreTime(startTime, endTime); + } + } From 5daebb372996750afd986c8ff68be151b4e60801 Mon Sep 17 00:00:00 2001 From: june-777 Date: Sun, 11 Aug 2024 19:25:47 +0900 Subject: [PATCH 02/47] =?UTF-8?q?[feat]=20=EA=B0=80=EA=B2=8C=20=EC=9D=B4?= =?UTF-8?q?=EB=A6=84=20=EA=B2=80=EC=A6=9D=20=EA=B8=B0=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 가게 이름은 2글자 ~ 10글자만 가능 --- .../lab/store/domain/StoreValidator.java | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 src/main/java/camp/woowak/lab/store/domain/StoreValidator.java diff --git a/src/main/java/camp/woowak/lab/store/domain/StoreValidator.java b/src/main/java/camp/woowak/lab/store/domain/StoreValidator.java new file mode 100644 index 00000000..bae9d4ca --- /dev/null +++ b/src/main/java/camp/woowak/lab/store/domain/StoreValidator.java @@ -0,0 +1,29 @@ +package camp.woowak.lab.store.domain; + +import java.time.LocalDateTime; + +import camp.woowak.lab.store.exception.StoreException; + +public class StoreValidator { + + private static final int UNIT_OF_MIN_ORDER_PRICE = 1000; + + private static final int MIN_ORDER_PRICE = 5000; + + private static final int MIN_NAME_LENGTH = 2; + private static final int MAX_NAME_LENGTH = 10; + + public static void validate(final String name, final String address, final Integer minOrderPrice, + final LocalDateTime startTime, final LocalDateTime endTime + ) { + validateName(name); + } + + private static void validateName(final String name) { + if (MIN_NAME_LENGTH <= name.length() && name.length() <= MAX_NAME_LENGTH) { + return; + } + throw new StoreException("가게 이름은 2글자 ~ 10글자 이어야합니다."); + } + +} From f86bafeb57f37fec72a17b17af8d51d1c1b1b297 Mon Sep 17 00:00:00 2001 From: june-777 Date: Sun, 11 Aug 2024 19:27:37 +0900 Subject: [PATCH 03/47] =?UTF-8?q?[feat]=20=EA=B0=80=EA=B2=8C=20=EC=A3=BC?= =?UTF-8?q?=EC=86=8C=20=EA=B2=80=EC=A6=9D=20=EA=B8=B0=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 가게 주소는 송파구만 가능 - 추후 가게 위치 비즈니스 요구사항을 구체화하면, 주소 검증 로직 수정 필요 --- .../camp/woowak/lab/store/domain/StoreValidator.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/java/camp/woowak/lab/store/domain/StoreValidator.java b/src/main/java/camp/woowak/lab/store/domain/StoreValidator.java index bae9d4ca..c11bda14 100644 --- a/src/main/java/camp/woowak/lab/store/domain/StoreValidator.java +++ b/src/main/java/camp/woowak/lab/store/domain/StoreValidator.java @@ -17,6 +17,7 @@ public static void validate(final String name, final String address, final Integ final LocalDateTime startTime, final LocalDateTime endTime ) { validateName(name); + validateAddress(address); } private static void validateName(final String name) { @@ -26,4 +27,12 @@ private static void validateName(final String name) { throw new StoreException("가게 이름은 2글자 ~ 10글자 이어야합니다."); } + // TODO: 가게 위치 비즈니스 요구사항 구체화하면, 주소 검증 로직 수정 예정 + private static void validateAddress(final String address) { + if (Address.DEFAULT_DISTRICT.equals(address)) { + return; + } + throw new StoreException("가게 주소는 송파구만 가능합니다."); + } + } From 999e0f46ae5179b534b3958cada367cda6d1e7f6 Mon Sep 17 00:00:00 2001 From: june-777 Date: Sun, 11 Aug 2024 19:28:52 +0900 Subject: [PATCH 04/47] =?UTF-8?q?[feat]=20=EA=B0=80=EA=B2=8C=20=EC=B5=9C?= =?UTF-8?q?=EC=86=8C=20=EC=A3=BC=EB=AC=B8=20=EA=B8=88=EC=95=A1=20=EA=B2=80?= =?UTF-8?q?=EC=A6=9D=20=EA=B8=B0=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 5,000원 이상 - 1,000원 단위 --- .../woowak/lab/store/domain/StoreValidator.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/main/java/camp/woowak/lab/store/domain/StoreValidator.java b/src/main/java/camp/woowak/lab/store/domain/StoreValidator.java index c11bda14..1e34c4f8 100644 --- a/src/main/java/camp/woowak/lab/store/domain/StoreValidator.java +++ b/src/main/java/camp/woowak/lab/store/domain/StoreValidator.java @@ -18,6 +18,8 @@ public static void validate(final String name, final String address, final Integ ) { validateName(name); validateAddress(address); + validateMinOrderPrice(minOrderPrice); + validateUnitOrderPrice(minOrderPrice); } private static void validateName(final String name) { @@ -35,4 +37,16 @@ private static void validateAddress(final String address) { throw new StoreException("가게 주소는 송파구만 가능합니다."); } + private static void validateMinOrderPrice(final Integer minOrderPrice) { + if (minOrderPrice < MIN_ORDER_PRICE) { + throw new StoreException("최소 주문 금액은 5,000원 이상이어야 합니다."); + } + } + + private static void validateUnitOrderPrice(final Integer minOrderPrice) { + if (minOrderPrice % UNIT_OF_MIN_ORDER_PRICE != 0) { + throw new StoreException("최소 주문 금액은 1,000원 단위이어야 합니다."); + } + } + } From a962988f90e5ed72c704d92b2a19705fbfc42491 Mon Sep 17 00:00:00 2001 From: june-777 Date: Sun, 11 Aug 2024 19:29:41 +0900 Subject: [PATCH 05/47] =?UTF-8?q?[feat]=20=EA=B0=80=EA=B2=8C=20=EC=9D=B4?= =?UTF-8?q?=EC=9A=A9=20=EC=8B=9C=EA=B0=84=20=EA=B2=80=EC=A6=9D=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 가게 시작/종료 시간은 분 단위까지 가능 - 가게 시작 시간은 종료 시간보다 이전 --- .../lab/store/domain/StoreValidator.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/main/java/camp/woowak/lab/store/domain/StoreValidator.java b/src/main/java/camp/woowak/lab/store/domain/StoreValidator.java index 1e34c4f8..d791e5be 100644 --- a/src/main/java/camp/woowak/lab/store/domain/StoreValidator.java +++ b/src/main/java/camp/woowak/lab/store/domain/StoreValidator.java @@ -20,6 +20,7 @@ public static void validate(final String name, final String address, final Integ validateAddress(address); validateMinOrderPrice(minOrderPrice); validateUnitOrderPrice(minOrderPrice); + validateTime(startTime, endTime); } private static void validateName(final String name) { @@ -49,4 +50,28 @@ private static void validateUnitOrderPrice(final Integer minOrderPrice) { } } + private static void validateTime(final LocalDateTime startTime, + final LocalDateTime endTime + ) { + if (isInvalidStoreTimeUnit(startTime)) { + throw new StoreException("가게 시작 시간은 분 단위까지 가능합니다"); + } + + if (isInvalidStoreTimeUnit(endTime)) { + throw new StoreException("가게 종료 시간은 분 단위까지 가능합니다"); + } + + if (endTime.isBefore(startTime)) { + throw new StoreException("가게 시작 시간은 종료 시간보다 이전이어야 합니다"); + } + + if (startTime.isEqual(endTime)) { + throw new StoreException("가게 시작 시간과 종료 시간이 일치합니다"); + } + } + + private static boolean isInvalidStoreTimeUnit(final LocalDateTime target) { + return target.getSecond() != 0 || target.getNano() != 0; + } + } From 328e78f65c47efa6064ec2baeed30e809c14d4e1 Mon Sep 17 00:00:00 2001 From: june-777 Date: Sun, 11 Aug 2024 19:36:06 +0900 Subject: [PATCH 06/47] =?UTF-8?q?[feat]=20=EA=B0=80=EA=B2=8C=20=EC=A3=BC?= =?UTF-8?q?=EC=86=8C=20=EC=9E=84=EB=B2=A0=EB=94=94=EB=93=9C=20=ED=83=80?= =?UTF-8?q?=EC=9E=85=20=EC=A0=95=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 임베디드 타입 사용 이유 - 주소는 도시/구/상세주소 같은 정보들로 그룹핑 됨 - 현재는 프로토타입으로서 구 정보만 사용하지만, 가게 주소 비즈니스 요구사항이 구체화되면 다른 정보들도 활용할 여지가 있음 - 구체화가 완료되면, 엔티티로 분리할 것인지 논의 필요 --- .../camp/woowak/lab/store/domain/Store.java | 4 ++-- .../woowak/lab/store/domain/StoreAddress.java | 21 +++++++++++++++++++ .../lab/store/domain/StoreValidator.java | 2 +- 3 files changed, 24 insertions(+), 3 deletions(-) create mode 100644 src/main/java/camp/woowak/lab/store/domain/StoreAddress.java diff --git a/src/main/java/camp/woowak/lab/store/domain/Store.java b/src/main/java/camp/woowak/lab/store/domain/Store.java index d2a19ade..d67aeb3b 100644 --- a/src/main/java/camp/woowak/lab/store/domain/Store.java +++ b/src/main/java/camp/woowak/lab/store/domain/Store.java @@ -38,7 +38,7 @@ public class Store { // TODO: 위치 정보에 대한 요구사항 논의 후 수정 예정. // i.g) 송파구로 특정, 도시 정보로 특정 등 요구사항이 정의되어야 엔티티 설계를 진행할 수 있음 @Embedded - private Address address; + private StoreAddress storeAddress; @Column(nullable = false) private String phoneNumber; @@ -56,7 +56,7 @@ public Store(Vendor owner, StoreCategory storeCategory, String name, String addr this.owner = owner; this.storeCategory = storeCategory; this.name = name; - this.address = new Address(address); + this.storeAddress = new StoreAddress(address); this.phoneNumber = phoneNumber; this.minOrderPrice = minOrderPrice; this.storeTime = new StoreTime(startTime, endTime); diff --git a/src/main/java/camp/woowak/lab/store/domain/StoreAddress.java b/src/main/java/camp/woowak/lab/store/domain/StoreAddress.java new file mode 100644 index 00000000..2273aad2 --- /dev/null +++ b/src/main/java/camp/woowak/lab/store/domain/StoreAddress.java @@ -0,0 +1,21 @@ +package camp.woowak.lab.store.domain; + +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@Embeddable +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class StoreAddress { + + public static final String DEFAULT_DISTRICT = "송파"; + + @Column(nullable = false) + private String district; + + public StoreAddress(final String district) { + this.district = district; + } + +} diff --git a/src/main/java/camp/woowak/lab/store/domain/StoreValidator.java b/src/main/java/camp/woowak/lab/store/domain/StoreValidator.java index d791e5be..ebcb133c 100644 --- a/src/main/java/camp/woowak/lab/store/domain/StoreValidator.java +++ b/src/main/java/camp/woowak/lab/store/domain/StoreValidator.java @@ -32,7 +32,7 @@ private static void validateName(final String name) { // TODO: 가게 위치 비즈니스 요구사항 구체화하면, 주소 검증 로직 수정 예정 private static void validateAddress(final String address) { - if (Address.DEFAULT_DISTRICT.equals(address)) { + if (StoreAddress.DEFAULT_DISTRICT.equals(address)) { return; } throw new StoreException("가게 주소는 송파구만 가능합니다."); From 48c2e7f21f5b82937d736b962cb4b1566f0de32e Mon Sep 17 00:00:00 2001 From: june-777 Date: Sun, 11 Aug 2024 19:37:39 +0900 Subject: [PATCH 07/47] =?UTF-8?q?[feat]=20=EA=B0=80=EA=B2=8C=20=EC=9D=B4?= =?UTF-8?q?=EC=9A=A9=20=EC=8B=9C=EA=B0=84=20=EC=9E=84=EB=B2=A0=EB=94=94?= =?UTF-8?q?=EB=93=9C=20=ED=83=80=EC=9E=85=20=EC=A0=95=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 임베디드 타입 사용 이유 - 이용 시간은 시작 시간/ 종료 시간 정보로 그룹핑 됨 - LocalDateTime 과 변수명만으로는 비즈니스 요구사항을 명시적으로 표현하기 제약이 있음 --- .../woowak/lab/store/domain/StoreTime.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 src/main/java/camp/woowak/lab/store/domain/StoreTime.java diff --git a/src/main/java/camp/woowak/lab/store/domain/StoreTime.java b/src/main/java/camp/woowak/lab/store/domain/StoreTime.java new file mode 100644 index 00000000..5640d8d8 --- /dev/null +++ b/src/main/java/camp/woowak/lab/store/domain/StoreTime.java @@ -0,0 +1,25 @@ +package camp.woowak.lab.store.domain; + +import java.time.LocalDateTime; + +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@Embeddable +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class StoreTime { + + @Column(nullable = false) + private LocalDateTime startTime; + + @Column(nullable = false) + private LocalDateTime endTime; + + public StoreTime(final LocalDateTime startTime, final LocalDateTime endTime) { + this.startTime = startTime; + this.endTime = endTime; + } + +} From d3a40f450c1d34b85731fef65d6be338bcc1b852 Mon Sep 17 00:00:00 2001 From: june-777 Date: Sun, 11 Aug 2024 19:40:04 +0900 Subject: [PATCH 08/47] =?UTF-8?q?[feat]=20=EA=B0=80=EA=B2=8C=20=EC=B9=B4?= =?UTF-8?q?=ED=85=8C=EA=B3=A0=EB=A6=AC=20=EB=8F=84=EB=A9=94=EC=9D=B8=20?= =?UTF-8?q?=EC=97=94=ED=8B=B0=ED=8B=B0=20=EC=A0=95=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 도메인 엔티티로 분리한 이유: - 가게 카테고리는 DB 레벨에서 관리해야, 추후 카테고리를 추가/생성/삭제할 때 변경 이상에 대처가 용이할 것으로 판단 --- .../woowak/lab/store/domain/StoreCategory.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 src/main/java/camp/woowak/lab/store/domain/StoreCategory.java diff --git a/src/main/java/camp/woowak/lab/store/domain/StoreCategory.java b/src/main/java/camp/woowak/lab/store/domain/StoreCategory.java new file mode 100644 index 00000000..b30a20b0 --- /dev/null +++ b/src/main/java/camp/woowak/lab/store/domain/StoreCategory.java @@ -0,0 +1,17 @@ +package camp.woowak.lab.store.domain; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; + +@Entity +public class StoreCategory { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String name; + +} From 6df364cde2a090c4a9355d091e553efdd8130a7e Mon Sep 17 00:00:00 2001 From: june-777 Date: Sun, 11 Aug 2024 19:51:15 +0900 Subject: [PATCH 09/47] =?UTF-8?q?[feat]=20=EC=8B=9C=EA=B0=84=20=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=EB=A5=BC=20=EC=A0=9C=EA=B3=B5=ED=95=98=EB=8A=94=20Dat?= =?UTF-8?q?eTimeProvider=20=EC=9D=B8=ED=84=B0=ED=8E=98=EC=9D=B4=EC=8A=A4?= =?UTF-8?q?=20=EC=A0=95=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 인터페이스 설계 이유: 날짜 API 를 직접 참조함으로서 테스트하기 어려워지는 코드를 예방하기 위함 --- .../lab/infra/date/DateTimeProvider.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 src/main/java/camp/woowak/lab/infra/date/DateTimeProvider.java diff --git a/src/main/java/camp/woowak/lab/infra/date/DateTimeProvider.java b/src/main/java/camp/woowak/lab/infra/date/DateTimeProvider.java new file mode 100644 index 00000000..abefd7b3 --- /dev/null +++ b/src/main/java/camp/woowak/lab/infra/date/DateTimeProvider.java @@ -0,0 +1,20 @@ +package camp.woowak.lab.infra.date; + +import java.time.LocalDateTime; + +/** + *

인터페이스 도입 배경

+ * 메서드 내부 구현에 LocalDateTime.now 직접 사용하는 등 제어할 수 없는 영역을 인터페이스로 분리하여 제어 가능하도록 함
+ * 날짜로 인해 테스트 어려운 코드를 테스트 용이하도록 하기 위함 + * + *

Production Level

+ * 구현체: CurrentDateTime + * + *

Test Level

+ * 구현체: FixedDateTime + */ +public interface DateTimeProvider { + + LocalDateTime now(); + +} \ No newline at end of file From 4a7be13d35b6ddb2774e1af9894e4df3dd849be3 Mon Sep 17 00:00:00 2001 From: june-777 Date: Sun, 11 Aug 2024 20:04:29 +0900 Subject: [PATCH 10/47] =?UTF-8?q?[feat]=20=EA=B3=A0=EC=A0=95=20=EC=8B=9C?= =?UTF-8?q?=EA=B0=84=EC=9D=84=20=EB=B0=98=ED=99=98=ED=95=98=EB=8A=94=20Dat?= =?UTF-8?q?eTimeProvider=20=ED=85=8C=EC=8A=A4=ED=8A=B8=EC=9A=A9=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=EC=B2=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 시간 관련 로직을 테스트할 때 사용 사용 방법은 javadocs 로 샘플 코드 기재 --- .../woowak/lab/infra/time/FixedDateTime.java | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 src/test/java/camp/woowak/lab/infra/time/FixedDateTime.java diff --git a/src/test/java/camp/woowak/lab/infra/time/FixedDateTime.java b/src/test/java/camp/woowak/lab/infra/time/FixedDateTime.java new file mode 100644 index 00000000..8a7f9125 --- /dev/null +++ b/src/test/java/camp/woowak/lab/infra/time/FixedDateTime.java @@ -0,0 +1,38 @@ +package camp.woowak.lab.infra.time; + +import java.time.LocalDateTime; + +import camp.woowak.lab.infra.date.DateTimeProvider; + +/** + *

Sample Code

+ *

with Lambda

+ *
+ * {@code
+ * DateTimeProvider fixedTimeProvider = () -> LocalDateTime.of(2024, 8, 24, 1, 0, 0);
+ * LocalDateTime fixedTime = fixedTimeProvider.now();
+ * }
+ * 
+ * + *

with Constructor

+ *
+ * {@code
+ * DateTimeProvider fixedTimeProvider = new FixedDateTime(2024, 8, 24, 1, 0, 0);
+ * LocalDateTime fixedTime = fixedTimeProvider.now();
+ * }
+ * 
+ */ +public class FixedDateTime implements DateTimeProvider { + + private final LocalDateTime fixedDateTime; + + public FixedDateTime(int year, int month, int day, int hour, int minute, int second) { + fixedDateTime = LocalDateTime.of(year, month, day, hour, minute, second); + } + + @Override + public LocalDateTime now() { + return fixedDateTime; + } + +} From 835ad624ed99c06965f559fcbfdb0132cb04057f Mon Sep 17 00:00:00 2001 From: june-777 Date: Sun, 11 Aug 2024 20:04:51 +0900 Subject: [PATCH 11/47] =?UTF-8?q?[feat]=20=ED=98=84=EC=9E=AC=20=EC=8B=9C?= =?UTF-8?q?=EA=B0=84=EC=9D=84=20=EB=B0=98=ED=99=98=ED=95=98=EB=8A=94=20Dat?= =?UTF-8?q?eTimeProvider=20=ED=94=84=EB=A1=9C=EB=8D=95=EC=85=98=EC=9A=A9?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84=EC=B2=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../camp/woowak/lab/infra/date/CurrentDateTime.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/main/java/camp/woowak/lab/infra/date/CurrentDateTime.java diff --git a/src/main/java/camp/woowak/lab/infra/date/CurrentDateTime.java b/src/main/java/camp/woowak/lab/infra/date/CurrentDateTime.java new file mode 100644 index 00000000..f720aec2 --- /dev/null +++ b/src/main/java/camp/woowak/lab/infra/date/CurrentDateTime.java @@ -0,0 +1,12 @@ +package camp.woowak.lab.infra.date; + +import java.time.LocalDateTime; + +public class CurrentDateTime implements DateTimeProvider { + + @Override + public LocalDateTime now() { + return LocalDateTime.now(); + } + +} From fe3110fff21400c35dd59831a2d7c376191540f7 Mon Sep 17 00:00:00 2001 From: june-777 Date: Sun, 11 Aug 2024 20:05:32 +0900 Subject: [PATCH 12/47] =?UTF-8?q?[feat]=20DateTimeProvider=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=EC=B2=B4=EC=9D=B8=20CurrentDateTime=20=EB=A5=BC=20Bea?= =?UTF-8?q?n=20=EA=B0=9D=EC=B2=B4=EB=A1=9C=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lab/infra/config/InfraConfiguration.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 src/main/java/camp/woowak/lab/infra/config/InfraConfiguration.java diff --git a/src/main/java/camp/woowak/lab/infra/config/InfraConfiguration.java b/src/main/java/camp/woowak/lab/infra/config/InfraConfiguration.java new file mode 100644 index 00000000..781cd074 --- /dev/null +++ b/src/main/java/camp/woowak/lab/infra/config/InfraConfiguration.java @@ -0,0 +1,17 @@ +package camp.woowak.lab.infra.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import camp.woowak.lab.infra.date.CurrentDateTime; +import camp.woowak.lab.infra.date.DateTimeProvider; + +@Configuration +public class InfraConfiguration { + + @Bean + public DateTimeProvider dateTimeProvider() { + return new CurrentDateTime(); + } + +} From 1e8885a8836861fe54075a54904f337873f2c381 Mon Sep 17 00:00:00 2001 From: june-777 Date: Sun, 11 Aug 2024 20:24:07 +0900 Subject: [PATCH 13/47] =?UTF-8?q?[test]=20Store=20=EB=8F=84=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=20=EA=B0=9D=EC=B2=B4=20=EC=83=9D=EC=84=B1=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20-=20=EA=B0=80=EA=B2=8C=20=EC=B5=9C?= =?UTF-8?q?=EC=86=8C=20=EC=A3=BC=EB=AC=B8=20=EA=B0=80=EA=B2=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../woowak/lab/store/domain/StoreTest.java | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 src/test/java/camp/woowak/lab/store/domain/StoreTest.java diff --git a/src/test/java/camp/woowak/lab/store/domain/StoreTest.java b/src/test/java/camp/woowak/lab/store/domain/StoreTest.java new file mode 100644 index 00000000..6ea64cec --- /dev/null +++ b/src/test/java/camp/woowak/lab/store/domain/StoreTest.java @@ -0,0 +1,91 @@ +package camp.woowak.lab.store.domain; + +import static org.assertj.core.api.Assertions.*; + +import java.time.LocalDateTime; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import camp.woowak.lab.infra.date.DateTimeProvider; +import camp.woowak.lab.store.exception.StoreException; + +class StoreTest { + + @Nested + @DisplayName("Store 생성은") + class StoreValidateTest { + + DateTimeProvider fixedStartTime = () -> LocalDateTime.of(2024, 8, 24, 1, 0, 0); + DateTimeProvider fixedEndTime = () -> LocalDateTime.of(2024, 8, 24, 5, 0, 0); + + LocalDateTime validStartTimeFixture = fixedStartTime.now(); + LocalDateTime validEndTimeFixture = fixedEndTime.now(); + + String validNameFixture = "3K1K 가게"; + + String validAddressFixture = StoreAddress.DEFAULT_DISTRICT; + + Integer validMinOrderPriceFixture = 5000; + + @Nested + @DisplayName("가게 최소 주문 가격이") + class StoreMinOrderPrice { + + @Test + @DisplayName("[Success] 5,000원 이상 가능하다") + void validMinOrderPrice() { + // given + int validMinOrderPrice = 5000; + + // when & then + assertThatCode(() -> new Store(null, null, validNameFixture, validAddressFixture, null, + validMinOrderPrice, + validStartTimeFixture, validEndTimeFixture)) + .doesNotThrowAnyException(); + } + + @Test + @DisplayName("[Exception] 5,000원 미만이면 StoreException 이 발생한다") + void lessThanMinOrderPrice() { + // given + int lessThanMinOrderPrice = 4999; + + // when & then + assertThatThrownBy(() -> new Store(null, null, validNameFixture, validAddressFixture, null, + lessThanMinOrderPrice, + validStartTimeFixture, validEndTimeFixture)) + .isInstanceOf(StoreException.class); + } + + @Test + @DisplayName("[Success] 1,000원 단위로 가능하다") + void validUnitOrderPrice() { + // given + int validMinOrderPrice = 10000; + + // when & then + assertThatCode(() -> new Store(null, null, validNameFixture, validAddressFixture, null, + validMinOrderPrice, + validStartTimeFixture, validEndTimeFixture)) + .doesNotThrowAnyException(); + } + + @Test + @DisplayName("[Exception] 1,000원 단위가 아니면 StoreException 이 발생한다") + void inValidUnitOrderPrice() { + // given + int inValidUnitOrderPrice = 5001; + + // when & then + assertThatThrownBy(() -> new Store(null, null, validNameFixture, validAddressFixture, null, + inValidUnitOrderPrice, + validStartTimeFixture, validEndTimeFixture)) + .isInstanceOf(StoreException.class); + } + } + + } + +} \ No newline at end of file From aa47da4e2b494512a89399d0685e83293a0f32a5 Mon Sep 17 00:00:00 2001 From: june-777 Date: Sun, 11 Aug 2024 20:24:48 +0900 Subject: [PATCH 14/47] =?UTF-8?q?[test]=20Store=20=EB=8F=84=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=20=EA=B0=9D=EC=B2=B4=20=EC=83=9D=EC=84=B1=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20-=20=EA=B0=80=EA=B2=8C=20=EC=9D=B4?= =?UTF-8?q?=EC=9A=A9=20=EC=8B=9C=EA=B0=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../woowak/lab/store/domain/StoreTest.java | 113 ++++++++++++++++++ 1 file changed, 113 insertions(+) diff --git a/src/test/java/camp/woowak/lab/store/domain/StoreTest.java b/src/test/java/camp/woowak/lab/store/domain/StoreTest.java index 6ea64cec..32e84a54 100644 --- a/src/test/java/camp/woowak/lab/store/domain/StoreTest.java +++ b/src/test/java/camp/woowak/lab/store/domain/StoreTest.java @@ -86,6 +86,119 @@ void inValidUnitOrderPrice() { } } + @Nested + @DisplayName("가게 이용 시간이") + class StoreTime { + + @Test + @DisplayName("[Success] 시작 시간이 종료 시간보다 이전이다") + void storeStartTimeBeforeThanEndTime() { + // given + LocalDateTime validStartTime = validStartTimeFixture; + LocalDateTime validEndTime = validEndTimeFixture; + + // when & then + assertThatCode( + () -> new Store(null, null, validNameFixture, validAddressFixture, null, validMinOrderPriceFixture, + validStartTime, validEndTime)) + .doesNotThrowAnyException(); + } + + @Test + @DisplayName("[Exception] 종료 시간이 시작 시간과 같으면 StoreException 이 발생한다") + void endTimeSameWithStartTime() { + // given + LocalDateTime endTimeSameWithStartTime = fixedStartTime.now(); + + // when & then + assertThatThrownBy( + () -> new Store(null, null, validNameFixture, validAddressFixture, null, validMinOrderPriceFixture, + validStartTimeFixture, endTimeSameWithStartTime)) + .isInstanceOf(StoreException.class); + } + + @Test + @DisplayName("[Exception] 종료 시간이 시작 시간 이전이면 StoreException 이 발생한다") + void endTimeBeforeThanStartTime() { + // given + LocalDateTime endTimeBeforeThanStartTime = validStartTimeFixture.minusMinutes(1); + + // when & then + assertThatThrownBy( + () -> new Store(null, null, validNameFixture, validAddressFixture, null, validMinOrderPriceFixture, + validStartTimeFixture, endTimeBeforeThanStartTime)) + .isInstanceOf(StoreException.class); + } + + @Test + @DisplayName("[Success] 시작 시간과 종료 시간의 단위는 분(m) 단위 까지 가능하다") + void validStartTimeUnit() { + // given + LocalDateTime validStartTimeUnitMinute = validStartTimeFixture; + LocalDateTime validEndTimeUnitMinute = validEndTimeFixture; + + // when & then + assertThatCode( + () -> new Store(null, null, validNameFixture, validAddressFixture, null, validMinOrderPriceFixture, + validStartTimeUnitMinute, validEndTimeUnitMinute)) + .doesNotThrowAnyException(); + } + + @Test + @DisplayName("[Exception] 시작 시간에 초(s) 단위가 포함되면 StoreException 이 발생한다") + void startTimeWithSeconds() { + // given + DateTimeProvider inValidUnitDateTimeProvider = () -> LocalDateTime.of(2024, 8, 24, 10, 10, 1); + LocalDateTime startTimeWithSeconds = inValidUnitDateTimeProvider.now(); + + // when & then + assertThatThrownBy( + () -> new Store(null, null, validNameFixture, validAddressFixture, null, validMinOrderPriceFixture, + startTimeWithSeconds, validEndTimeFixture)) + .isInstanceOf(StoreException.class); + } + + @Test + @DisplayName("[Exception] 시작 시간에 나노초(ms) 단위가 포함되면 StoreException 이 발생한다") + void startTimeWithNanoSeconds() { + // given + DateTimeProvider inValidUnitDateTimeProvider = () -> LocalDateTime.of(2024, 8, 24, 10, 0, 0, 1); + LocalDateTime startTimeWithNanoSeconds = inValidUnitDateTimeProvider.now(); + + // when & then + assertThatThrownBy(() -> new Store(null, null, validNameFixture, validAddressFixture, null, 5000, + startTimeWithNanoSeconds, validEndTimeFixture)) + .isInstanceOf(StoreException.class); + } + + @Test + @DisplayName("[Exception] 종료 시간에 초(s) 단위가 포함되면 StoreException 이 발생한다") + void endTimeWithSeconds() { + // given + DateTimeProvider inValidUnitDateTimeProvider = () -> LocalDateTime.of(2024, 8, 24, 10, 30, 1); + LocalDateTime endTimeWithSeconds = inValidUnitDateTimeProvider.now(); + + // when & then + assertThatThrownBy(() -> new Store(null, null, validNameFixture, validAddressFixture, null, 5000, + validStartTimeFixture, endTimeWithSeconds)) + .isInstanceOf(StoreException.class); + } + + @Test + @DisplayName("[Exception] 종료 시간에 나노초(ms) 단위가 포함되면 StoreException 이 발생한다") + void endTimeWithNanoSeconds() { + // given + DateTimeProvider inValidUnitDateTimeProvider = () -> LocalDateTime.of(2024, 8, 24, 10, 30, 0, 1); + LocalDateTime endTimeWithNanoSeconds = inValidUnitDateTimeProvider.now(); + + // when & then + assertThatThrownBy(() -> new Store(null, null, validNameFixture, validAddressFixture, null, 5000, + validStartTimeFixture, endTimeWithNanoSeconds)) + .isInstanceOf(StoreException.class); + } + + } + } } \ No newline at end of file From 7791f9ae7e046a0b1b38222acb5c64e074efa0a6 Mon Sep 17 00:00:00 2001 From: june-777 Date: Sun, 11 Aug 2024 20:25:10 +0900 Subject: [PATCH 15/47] =?UTF-8?q?[test]=20Store=20=EB=8F=84=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=20=EA=B0=9D=EC=B2=B4=20=EC=83=9D=EC=84=B1=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20-=20=EA=B0=80=EA=B2=8C=20=EC=9D=B4?= =?UTF-8?q?=EB=A6=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../woowak/lab/store/domain/StoreTest.java | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/src/test/java/camp/woowak/lab/store/domain/StoreTest.java b/src/test/java/camp/woowak/lab/store/domain/StoreTest.java index 32e84a54..b6391d64 100644 --- a/src/test/java/camp/woowak/lab/store/domain/StoreTest.java +++ b/src/test/java/camp/woowak/lab/store/domain/StoreTest.java @@ -199,6 +199,82 @@ void endTimeWithNanoSeconds() { } + @Nested + @DisplayName("가게 이름이") + class StoreName { + + @Test + @DisplayName("[Success] 2자 ~ 10자까지 가능하다") + void validStoreName() { + // given + String lengthValidStoreName = validNameFixture; + + // when & then + assertThatCode(() -> new Store(null, null, lengthValidStoreName, validAddressFixture, null, + validMinOrderPriceFixture, + validStartTimeFixture, validEndTimeFixture)) + .doesNotThrowAnyException(); + } + + @Test + @DisplayName("[Exception] 2자 미만이면 StoreException 이 발생한다") + void lessThanMinLengthName() { + // given + String lessThanMinLengthName = "헤"; + + // when & then + assertThatThrownBy(() -> new Store(null, null, lessThanMinLengthName, validAddressFixture, null, + validMinOrderPriceFixture, + validStartTimeFixture, validEndTimeFixture)) + .isInstanceOf(StoreException.class); + } + + @Test + @DisplayName("[Exception] 10자 초과면 StoreException 이 발생한다") + void greaterThanMaxLengthName() { + // given + String greaterThanMaxLengthName = "123456789가게"; + + // when & then + assertThatThrownBy( + () -> new Store(null, null, greaterThanMaxLengthName, validAddressFixture, null, + validMinOrderPriceFixture, + validStartTimeFixture, validEndTimeFixture)) + .isInstanceOf(StoreException.class); + } + + } + + @Nested + @DisplayName("가게 주소가") + class StoreAddressTest { + + @Test + @DisplayName("[Success] 송파구만 가능하다") + void onlySongPa() { + // given + String validAddress = "송파"; + // when & then + assertThatCode( + () -> new Store(null, null, validNameFixture, validAddress, null, validMinOrderPriceFixture, + validStartTimeFixture, validEndTimeFixture)) + .doesNotThrowAnyException(); + } + + @Test + @DisplayName("[Exception] 송파구가 아닌 주소는 StoreException 이 발생한다") + void notSongPa() { + // given + String validAddress = "강남"; + // when & then + assertThatThrownBy( + () -> new Store(null, null, validNameFixture, validAddress, null, validMinOrderPriceFixture, + validStartTimeFixture, validEndTimeFixture)) + .isInstanceOf(StoreException.class); + } + + } + } } \ No newline at end of file From 29996058157503388a07c34555d4c92eaa8d4339 Mon Sep 17 00:00:00 2001 From: june-777 Date: Sun, 11 Aug 2024 20:27:21 +0900 Subject: [PATCH 16/47] =?UTF-8?q?[feat]=20Store=20(=EA=B0=80=EA=B2=8C)=20?= =?UTF-8?q?=EB=8F=84=EB=A9=94=EC=9D=B8=20=EA=B4=80=EB=A0=A8=20=EC=BB=A4?= =?UTF-8?q?=EC=8A=A4=ED=85=80=20=EC=98=88=EC=99=B8=20=EC=A0=95=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Store 와 관련된 예외임을 명시적으로 나타내고자 함 IllegalArgumentException 등 Java 에서 제공하는 기본 예외는 예외의 명시성이 떨어진다 판단 --- .../camp/woowak/lab/store/exception/StoreException.java | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/main/java/camp/woowak/lab/store/exception/StoreException.java diff --git a/src/main/java/camp/woowak/lab/store/exception/StoreException.java b/src/main/java/camp/woowak/lab/store/exception/StoreException.java new file mode 100644 index 00000000..33cd1642 --- /dev/null +++ b/src/main/java/camp/woowak/lab/store/exception/StoreException.java @@ -0,0 +1,9 @@ +package camp.woowak.lab.store.exception; + +public class StoreException extends RuntimeException { + + public StoreException(String message) { + super(message); + } + +} From 6a4f101d8f82596f67118be2d82920f2f69203a3 Mon Sep 17 00:00:00 2001 From: june-777 Date: Sun, 11 Aug 2024 21:56:15 +0900 Subject: [PATCH 17/47] =?UTF-8?q?[feat]=20=EA=B0=80=EA=B2=8C=20=EC=B9=B4?= =?UTF-8?q?=ED=85=8C=EA=B3=A0=EB=A6=AC=20=EC=83=9D=EC=84=B1=EC=9E=90=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 기본 생성자는 애노테이션 사용 및 protected 접근 제어자로 설정 --- .../java/camp/woowak/lab/store/domain/StoreCategory.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/java/camp/woowak/lab/store/domain/StoreCategory.java b/src/main/java/camp/woowak/lab/store/domain/StoreCategory.java index b30a20b0..60558407 100644 --- a/src/main/java/camp/woowak/lab/store/domain/StoreCategory.java +++ b/src/main/java/camp/woowak/lab/store/domain/StoreCategory.java @@ -4,8 +4,11 @@ import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; @Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) public class StoreCategory { @Id @@ -14,4 +17,8 @@ public class StoreCategory { private String name; + public StoreCategory(String name) { + this.name = name; + } + } From c7667394108c16bc9170a25e58baab3f4860e7a5 Mon Sep 17 00:00:00 2001 From: june-777 Date: Sun, 11 Aug 2024 21:56:48 +0900 Subject: [PATCH 18/47] =?UTF-8?q?[feat]=20StoreRepository=20JPA=20?= =?UTF-8?q?=EC=9D=B8=ED=84=B0=ED=8E=98=EC=9D=B4=EC=8A=A4=20=EC=A0=95?= =?UTF-8?q?=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../camp/woowak/lab/store/repository/StoreRepository.java | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 src/main/java/camp/woowak/lab/store/repository/StoreRepository.java diff --git a/src/main/java/camp/woowak/lab/store/repository/StoreRepository.java b/src/main/java/camp/woowak/lab/store/repository/StoreRepository.java new file mode 100644 index 00000000..a4f98667 --- /dev/null +++ b/src/main/java/camp/woowak/lab/store/repository/StoreRepository.java @@ -0,0 +1,8 @@ +package camp.woowak.lab.store.repository; + +import org.springframework.data.jpa.repository.JpaRepository; + +import camp.woowak.lab.store.domain.Store; + +public interface StoreRepository extends JpaRepository { +} From bc5b95438ba6acd67bd6300ffbf5cc13c181bebf Mon Sep 17 00:00:00 2001 From: june-777 Date: Sun, 11 Aug 2024 22:01:29 +0900 Subject: [PATCH 19/47] =?UTF-8?q?[test]=20StoreRepository=20=EA=B0=80?= =?UTF-8?q?=EA=B2=8C=20=EC=A0=80=EC=9E=A5=20=EA=B8=B0=EB=8A=A5=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - repository 단위 테스트는 @DataJpaTest 로 최소한의 Bean 만 주입하여 사용 - @SpringBootTest 는 모든 Bean 을 로드하기 때문에 테스트가 무거워짐 --- .../store/repository/StoreRepositoryTest.java | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 src/test/java/camp/woowak/lab/store/repository/StoreRepositoryTest.java diff --git a/src/test/java/camp/woowak/lab/store/repository/StoreRepositoryTest.java b/src/test/java/camp/woowak/lab/store/repository/StoreRepositoryTest.java new file mode 100644 index 00000000..f04cc524 --- /dev/null +++ b/src/test/java/camp/woowak/lab/store/repository/StoreRepositoryTest.java @@ -0,0 +1,88 @@ +package camp.woowak.lab.store.repository; + +import java.time.LocalDateTime; + +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.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.dao.InvalidDataAccessApiUsageException; + +import camp.woowak.lab.infra.date.DateTimeProvider; +import camp.woowak.lab.store.domain.Store; +import camp.woowak.lab.store.domain.StoreAddress; +import camp.woowak.lab.store.domain.StoreCategory; +import camp.woowak.lab.vendor.domain.Vendor; +import camp.woowak.lab.vendor.repository.VendorRepository; + +@DataJpaTest +class StoreRepositoryTest { + + @Autowired + private StoreRepository storeRepository; + + @Autowired + private VendorRepository vendorRepository; + + @Autowired + private StoreCategoryRepository storeCategoryRepository; + + DateTimeProvider fixedStartTime = () -> LocalDateTime.of(2024, 8, 24, 1, 0, 0); + DateTimeProvider fixedEndTime = () -> LocalDateTime.of(2024, 8, 24, 5, 0, 0); + + LocalDateTime validStartTimeFixture = fixedStartTime.now(); + LocalDateTime validEndTimeFixture = fixedEndTime.now(); + + String validNameFixture = "3K1K 가게"; + String validAddressFixture = StoreAddress.DEFAULT_DISTRICT; + String validPhoneNumberFixture = "02-0000-0000"; + Integer validMinOrderPriceFixture = 5000; + + @Test + @DisplayName("[Success] 가게 저장 성공") + void successfulSaveStore() { + // given + Vendor vendor = new Vendor(); + vendorRepository.saveAndFlush(vendor); + + StoreCategory storeCategory = new StoreCategory("한식"); + storeCategoryRepository.saveAndFlush(storeCategory); + + Store store = createStore(vendor, storeCategory); + + // when & then + Assertions.assertThatCode(() -> storeRepository.save(store)) + .doesNotThrowAnyException(); + } + + @Test + @DisplayName("[Exception] 참조중인 엔티티가 저장되지 않았고, Not Null 제약조건이면 InvalidDataAccessApiUsageException - Not-null property 가 발생한다") + void test() { + // given + Vendor notSavedVendor = new Vendor(); + + StoreCategory storeCategory = new StoreCategory("한식"); + storeCategoryRepository.saveAndFlush(storeCategory); + + // when + Store store = createStore(notSavedVendor, storeCategory); + + // then + Assertions.assertThatThrownBy(() -> storeRepository.saveAndFlush(store)) + .isInstanceOf(InvalidDataAccessApiUsageException.class); + } + + private Store createStore(Vendor vendor, StoreCategory storeCategory) { + return new Store(vendor, + storeCategory, + validNameFixture, + validAddressFixture, + validPhoneNumberFixture, + validMinOrderPriceFixture, + validStartTimeFixture, + validEndTimeFixture + ); + } + +} \ No newline at end of file From bdef77a953c2e2b96ba0c19c93982b05eee65e15 Mon Sep 17 00:00:00 2001 From: june-777 Date: Sun, 11 Aug 2024 22:02:37 +0900 Subject: [PATCH 20/47] =?UTF-8?q?[feat]=20StoreCategoryRepository=20JPA=20?= =?UTF-8?q?=EC=9D=B8=ED=84=B0=ED=8E=98=EC=9D=B4=EC=8A=A4=20=EC=A0=95?= =?UTF-8?q?=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 가게 카테고리 이름으로 조회하는 시그니쳐 추가 --- .../store/repository/StoreCategoryRepository.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 src/main/java/camp/woowak/lab/store/repository/StoreCategoryRepository.java diff --git a/src/main/java/camp/woowak/lab/store/repository/StoreCategoryRepository.java b/src/main/java/camp/woowak/lab/store/repository/StoreCategoryRepository.java new file mode 100644 index 00000000..5988f8b5 --- /dev/null +++ b/src/main/java/camp/woowak/lab/store/repository/StoreCategoryRepository.java @@ -0,0 +1,13 @@ +package camp.woowak.lab.store.repository; + +import java.util.Optional; + +import org.springframework.data.jpa.repository.JpaRepository; + +import camp.woowak.lab.store.domain.StoreCategory; + +public interface StoreCategoryRepository extends JpaRepository { + + Optional findByName(String name); + +} From 3575f1dae20569f648ae0cb5a146f0dcb0168dbd Mon Sep 17 00:00:00 2001 From: june-777 Date: Sun, 11 Aug 2024 22:03:08 +0900 Subject: [PATCH 21/47] =?UTF-8?q?[test]=20=EA=B0=80=EA=B2=8C=20=EC=B9=B4?= =?UTF-8?q?=ED=85=8C=EA=B3=A0=EB=A6=AC=20=EC=9D=B4=EB=A6=84=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=A1=B0=ED=9A=8C=ED=95=98=EB=8A=94=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../StoreCategoryRepositoryTest.java | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 src/test/java/camp/woowak/lab/store/repository/StoreCategoryRepositoryTest.java diff --git a/src/test/java/camp/woowak/lab/store/repository/StoreCategoryRepositoryTest.java b/src/test/java/camp/woowak/lab/store/repository/StoreCategoryRepositoryTest.java new file mode 100644 index 00000000..1231aa86 --- /dev/null +++ b/src/test/java/camp/woowak/lab/store/repository/StoreCategoryRepositoryTest.java @@ -0,0 +1,55 @@ +package camp.woowak.lab.store.repository; + +import static org.assertj.core.api.Assertions.*; + +import java.util.Optional; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; + +import camp.woowak.lab.store.domain.StoreCategory; + +@DataJpaTest +class StoreCategoryRepositoryTest { + + @Autowired + StoreCategoryRepository storeCategoryRepository; + + @Nested + @DisplayName("가게 카테고리를 이름으로 조회하는 기능은") + class FindByNameTest { + + @Test + @DisplayName("[Success] 존재하는 가게 카테고리로 조회하면 Optional 에 가게 카테고리가 있다") + void existStoreCategory() { + // given + String storeCategoryName = "양식"; + StoreCategory storeCategory = new StoreCategory(storeCategoryName); + storeCategoryRepository.saveAndFlush(storeCategory); + + // when + Optional findResult = storeCategoryRepository.findByName(storeCategoryName); + + // then + assertThat(findResult) + .isPresent() + .containsSame(storeCategory); + } + + @Test + @DisplayName("[Exception] 없는 가게 카테고리로 조회하면 빈 값을 반환한다") + void notExistStoreCategory() { + // given + String notExistStoreCategoryName = "없는가게카테고리"; + + // when & then + assertThat(storeCategoryRepository.findByName(notExistStoreCategoryName)) + .isEmpty(); + } + + } + +} \ No newline at end of file From df6a123e904f505e908127aca49a208d34f76f67 Mon Sep 17 00:00:00 2001 From: june-777 Date: Sun, 11 Aug 2024 22:04:23 +0900 Subject: [PATCH 22/47] =?UTF-8?q?[feat]=20VendorRepository=20JPA=20?= =?UTF-8?q?=EC=9D=B8=ED=84=B0=ED=8E=98=EC=9D=B4=EC=8A=A4=20=EC=A0=95?= =?UTF-8?q?=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - StoreRepository 단위 테스트를 위해 정의함 - Store 엔티티를 저장할 때, 참조중인 Vendor(Not Null) 를 저장해야 Not-Null Property Exception 이 발생하지 않음 --- .../woowak/lab/vendor/repository/VendorRepository.java | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 src/main/java/camp/woowak/lab/vendor/repository/VendorRepository.java diff --git a/src/main/java/camp/woowak/lab/vendor/repository/VendorRepository.java b/src/main/java/camp/woowak/lab/vendor/repository/VendorRepository.java new file mode 100644 index 00000000..1d1d6f51 --- /dev/null +++ b/src/main/java/camp/woowak/lab/vendor/repository/VendorRepository.java @@ -0,0 +1,8 @@ +package camp.woowak.lab.vendor.repository; + +import org.springframework.data.jpa.repository.JpaRepository; + +import camp.woowak.lab.vendor.domain.Vendor; + +public interface VendorRepository extends JpaRepository { +} \ No newline at end of file From 3f2609ee116c47e9d570ad442dc506d083f0ee5f Mon Sep 17 00:00:00 2001 From: june-777 Date: Sun, 11 Aug 2024 22:12:56 +0900 Subject: [PATCH 23/47] =?UTF-8?q?[feat]=20=EA=B0=80=EA=B2=8C=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=EC=9D=84=20=EB=8B=B4=EB=8B=B9=ED=95=98=EB=8A=94=20?= =?UTF-8?q?=EC=84=9C=EB=B9=84=EC=8A=A4=20=ED=81=B4=EB=9E=98=EC=8A=A4=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 발생가능한 RunTimeException 은 javadocs 로 명시 --- .../service/StoreRegistrationService.java | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 src/main/java/camp/woowak/lab/store/service/StoreRegistrationService.java diff --git a/src/main/java/camp/woowak/lab/store/service/StoreRegistrationService.java b/src/main/java/camp/woowak/lab/store/service/StoreRegistrationService.java new file mode 100644 index 00000000..3b15d264 --- /dev/null +++ b/src/main/java/camp/woowak/lab/store/service/StoreRegistrationService.java @@ -0,0 +1,46 @@ +package camp.woowak.lab.store.service; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import camp.woowak.lab.store.domain.Store; +import camp.woowak.lab.store.domain.StoreCategory; +import camp.woowak.lab.store.exception.StoreException; +import camp.woowak.lab.store.repository.StoreCategoryRepository; +import camp.woowak.lab.store.repository.StoreRepository; +import camp.woowak.lab.vendor.domain.Vendor; +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +public class StoreRegistrationService { + + private final StoreRepository storeRepository; + private final StoreCategoryRepository storeCategoryRepository; + + /** + * @throws StoreException Store 객체 검증 실패, 존재하지 않는 이름의 가게 카테고리 + */ + @Transactional + public void storeRegistration(final Vendor vendor, final StoreRegistrationRequest request) { + final StoreCategory storeCategory = findStoreCategoryByName(request.storeCategoryName()); + final Store store = createStore(vendor, storeCategory, request); + + storeRepository.save(store); + } + + private static Store createStore(Vendor vendor, StoreCategory storeCategory, StoreRegistrationRequest request) { + return new Store(vendor, storeCategory, request.storeName(), + request.storeAddress(), + request.storePhoneNumber(), + request.storeMinOrderPrice(), + request.storeStarTime(), + request.storeEndTime()); + } + + private StoreCategory findStoreCategoryByName(final String name) { + return storeCategoryRepository.findByName(name) + .orElseThrow(() -> new StoreException("존재하지 않는 가게 카테고리입니다.")); + } + +} From 3224a6d95a7bf51de6837067c1a197ad0b19daa2 Mon Sep 17 00:00:00 2001 From: june-777 Date: Sun, 11 Aug 2024 22:14:52 +0900 Subject: [PATCH 24/47] =?UTF-8?q?[feat]=20=EA=B0=80=EA=B2=8C=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=20=EC=9A=94=EC=B2=AD=20DTO?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Not Null, 빈값, 빈값 포함한 공백 에 대한 입력 검증은 Bean Validation 으로 검증함 --- .../service/StoreRegistrationRequest.java | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 src/main/java/camp/woowak/lab/store/service/StoreRegistrationRequest.java diff --git a/src/main/java/camp/woowak/lab/store/service/StoreRegistrationRequest.java b/src/main/java/camp/woowak/lab/store/service/StoreRegistrationRequest.java new file mode 100644 index 00000000..6b2d1559 --- /dev/null +++ b/src/main/java/camp/woowak/lab/store/service/StoreRegistrationRequest.java @@ -0,0 +1,31 @@ +package camp.woowak.lab.store.service; + +import java.time.LocalDateTime; + +import jakarta.validation.constraints.NotBlank; + +public record StoreRegistrationRequest( + + @NotBlank + String storeName, + + @NotBlank + String storeAddress, + + @NotBlank + String storePhoneNumber, + + @NotBlank + String storeCategoryName, + + @NotBlank + Integer storeMinOrderPrice, + + @NotBlank + LocalDateTime storeStarTime, + + @NotBlank + LocalDateTime storeEndTime + +) { +} From 9b7911c27e4090fd2d3f41205adf70ba8895b82f Mon Sep 17 00:00:00 2001 From: june-777 Date: Sun, 11 Aug 2024 22:15:25 +0900 Subject: [PATCH 25/47] =?UTF-8?q?[test]=20=EA=B0=80=EA=B2=8C=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=20=EC=84=9C=EB=B9=84=EC=8A=A4=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=20=EB=8B=A8=EC=9C=84=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/StoreRegistrationServiceTest.java | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 src/test/java/camp/woowak/lab/store/service/StoreRegistrationServiceTest.java diff --git a/src/test/java/camp/woowak/lab/store/service/StoreRegistrationServiceTest.java b/src/test/java/camp/woowak/lab/store/service/StoreRegistrationServiceTest.java new file mode 100644 index 00000000..9630dcef --- /dev/null +++ b/src/test/java/camp/woowak/lab/store/service/StoreRegistrationServiceTest.java @@ -0,0 +1,85 @@ +package camp.woowak.lab.store.service; + +import static org.mockito.BDDMockito.*; + +import java.time.LocalDateTime; +import java.util.Optional; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import camp.woowak.lab.infra.date.DateTimeProvider; +import camp.woowak.lab.store.domain.Store; +import camp.woowak.lab.store.domain.StoreCategory; +import camp.woowak.lab.store.exception.StoreException; +import camp.woowak.lab.store.repository.StoreCategoryRepository; +import camp.woowak.lab.store.repository.StoreRepository; +import camp.woowak.lab.vendor.domain.Vendor; + +@ExtendWith(MockitoExtension.class) +class StoreRegistrationServiceTest { + + @Mock + private StoreRepository storeRepository; + + @Mock + private StoreCategoryRepository storeCategoryRepository; + + @InjectMocks + private StoreRegistrationService storeRegistrationService; + + DateTimeProvider fixedStartTime = () -> LocalDateTime.of(2024, 8, 24, 1, 0, 0); + DateTimeProvider fixedEndTime = () -> LocalDateTime.of(2024, 8, 24, 5, 0, 0); + + LocalDateTime validStartTimeFixture = fixedStartTime.now(); + LocalDateTime validEndTimeFixture = fixedEndTime.now(); + + @Test + @DisplayName("[Success] 가게가 저장된다.") + void successfulRegistration() { + // given + Vendor vendor = new Vendor(); + String storeCategoryName = "한식"; + StoreRegistrationRequest request = createStoreRegistrationRequest(storeCategoryName); + StoreCategory mockStoreCategory = new StoreCategory(storeCategoryName); + + when(storeCategoryRepository.findByName(storeCategoryName)).thenReturn(Optional.of(mockStoreCategory)); + when(storeRepository.save(any(Store.class))) + .thenAnswer(invocation -> invocation.getArgument(0)); + + // when + storeRegistrationService.storeRegistration(vendor, request); + + // then + then(storeRepository).should().save(any(Store.class)); + } + + @Test + @DisplayName("[Exception] 유효하지 않은 가게 카테고리면 예외가 발생한다.") + void notExistStoreCategoryName() { + // given + Vendor vendor = new Vendor(); + String invalidCategoryName = "존재하지 않는 카테고리"; + StoreRegistrationRequest request = createStoreRegistrationRequest(invalidCategoryName); + + given(storeCategoryRepository.findByName(invalidCategoryName)) + .willReturn(Optional.empty()); + + // when & then + Assertions.assertThatThrownBy(() -> storeRegistrationService.storeRegistration(vendor, request)) + .isInstanceOf(StoreException.class); + + then(storeRepository).shouldHaveNoInteractions(); + } + + private StoreRegistrationRequest createStoreRegistrationRequest(String storeCategory) { + return new StoreRegistrationRequest("3K1K가게", "송파", "02-0000-0000", + storeCategory, 5000, validStartTimeFixture, validEndTimeFixture); + } + +} \ No newline at end of file From 790b465ec432bc61644787229991f2d7aacf6a16 Mon Sep 17 00:00:00 2001 From: june-777 Date: Sun, 11 Aug 2024 22:17:19 +0900 Subject: [PATCH 26/47] =?UTF-8?q?[fix]=20Order=20=EC=97=94=ED=8B=B0?= =?UTF-8?q?=ED=8B=B0=EC=9D=98=20=ED=85=8C=EC=9D=B4=EB=B8=94=20=EC=9D=B4?= =?UTF-8?q?=EB=A6=84=EC=9D=84=20Orders=20=EB=A1=9C=20=EB=AA=85=EC=8B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit order 는 DBMS 예약어라서 Auto DDL 과정에서 예외가 발생함 --- .../camp/woowak/lab/order/domain/Order.java | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/src/main/java/camp/woowak/lab/order/domain/Order.java b/src/main/java/camp/woowak/lab/order/domain/Order.java index 2f0a6bd7..bf7e8c42 100644 --- a/src/main/java/camp/woowak/lab/order/domain/Order.java +++ b/src/main/java/camp/woowak/lab/order/domain/Order.java @@ -2,15 +2,26 @@ import camp.woowak.lab.customer.domain.Customer; import camp.woowak.lab.store.domain.Store; -import jakarta.persistence.*; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; @Entity +@Table(name = "Orders") public class Order { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - @ManyToOne(fetch = FetchType.LAZY) - private Customer requester; - @ManyToOne(fetch = FetchType.LAZY) - private Store store; + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + private Customer requester; + + @ManyToOne(fetch = FetchType.LAZY) + private Store store; + } From 9c4a77d7017ffd52c2747f872431a1becea74802 Mon Sep 17 00:00:00 2001 From: june-777 Date: Sun, 11 Aug 2024 22:21:15 +0900 Subject: [PATCH 27/47] =?UTF-8?q?[feat]=20=EC=9D=B8=EB=A9=94=EB=AA=A8?= =?UTF-8?q?=EB=A6=AC=20H2=20DB=20=EB=B0=8F=20jpa=20yaml=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - datasource: 인메모리 H2 DB를 사용하도록 설정 - h2: h2 웹 콘솔 사용 가능하도록 설정 - jpa: ddl 자동 생성, sql 로깅 설정 - 설정 내용의 depth 가독성이 용이하여, properties 를 yaml 로 변경함 --- src/main/resources/application.properties | 1 - src/main/resources/application.yaml | 23 +++++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) delete mode 100644 src/main/resources/application.properties create mode 100644 src/main/resources/application.yaml diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties deleted file mode 100644 index cc1ad9a6..00000000 --- a/src/main/resources/application.properties +++ /dev/null @@ -1 +0,0 @@ -spring.application.name=lab diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml new file mode 100644 index 00000000..27cdd1fd --- /dev/null +++ b/src/main/resources/application.yaml @@ -0,0 +1,23 @@ +spring: + application: + name: lab + + datasource: + url: 'jdbc:h2:mem:lab' + driver-class-name: org.h2.Driver + username: sa + password: + + h2: + console: + enabled: true + path: /h2-console + + jpa: + hibernate: + ddl-auto: create + + properties: + hibernate: + format_sql: true + show_sql: true \ No newline at end of file From 680283fb8012c3c988b0bf4faa83729344c7639e Mon Sep 17 00:00:00 2001 From: june-777 Date: Sun, 11 Aug 2024 22:23:41 +0900 Subject: [PATCH 28/47] =?UTF-8?q?[refactor]=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20static=20=ED=82=A4=EC=9B=8C=EB=93=9C=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../camp/woowak/lab/store/service/StoreRegistrationService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/camp/woowak/lab/store/service/StoreRegistrationService.java b/src/main/java/camp/woowak/lab/store/service/StoreRegistrationService.java index 3b15d264..742cb97c 100644 --- a/src/main/java/camp/woowak/lab/store/service/StoreRegistrationService.java +++ b/src/main/java/camp/woowak/lab/store/service/StoreRegistrationService.java @@ -29,7 +29,7 @@ public void storeRegistration(final Vendor vendor, final StoreRegistrationReques storeRepository.save(store); } - private static Store createStore(Vendor vendor, StoreCategory storeCategory, StoreRegistrationRequest request) { + private Store createStore(Vendor vendor, StoreCategory storeCategory, StoreRegistrationRequest request) { return new Store(vendor, storeCategory, request.storeName(), request.storeAddress(), request.storePhoneNumber(), From 876e3fb2b2dc5eea2022aea2b7537208d6ec295b Mon Sep 17 00:00:00 2001 From: june-777 Date: Sun, 11 Aug 2024 22:38:37 +0900 Subject: [PATCH 29/47] =?UTF-8?q?[refactor]=20=EA=B0=80=EA=B2=8C=20?= =?UTF-8?q?=EB=93=B1=EB=A1=9D=20=EC=9A=94=EC=B2=AD=20DTO=20=ED=8C=A8?= =?UTF-8?q?=ED=82=A4=EC=A7=80=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../camp/woowak/lab/store/service/StoreRegistrationService.java | 1 + .../lab/store/service/{ => dto}/StoreRegistrationRequest.java | 2 +- .../woowak/lab/store/service/StoreRegistrationServiceTest.java | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) rename src/main/java/camp/woowak/lab/store/service/{ => dto}/StoreRegistrationRequest.java (90%) diff --git a/src/main/java/camp/woowak/lab/store/service/StoreRegistrationService.java b/src/main/java/camp/woowak/lab/store/service/StoreRegistrationService.java index 742cb97c..e68f2121 100644 --- a/src/main/java/camp/woowak/lab/store/service/StoreRegistrationService.java +++ b/src/main/java/camp/woowak/lab/store/service/StoreRegistrationService.java @@ -8,6 +8,7 @@ import camp.woowak.lab.store.exception.StoreException; import camp.woowak.lab.store.repository.StoreCategoryRepository; import camp.woowak.lab.store.repository.StoreRepository; +import camp.woowak.lab.store.service.dto.StoreRegistrationRequest; import camp.woowak.lab.vendor.domain.Vendor; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/camp/woowak/lab/store/service/StoreRegistrationRequest.java b/src/main/java/camp/woowak/lab/store/service/dto/StoreRegistrationRequest.java similarity index 90% rename from src/main/java/camp/woowak/lab/store/service/StoreRegistrationRequest.java rename to src/main/java/camp/woowak/lab/store/service/dto/StoreRegistrationRequest.java index 6b2d1559..7968f09f 100644 --- a/src/main/java/camp/woowak/lab/store/service/StoreRegistrationRequest.java +++ b/src/main/java/camp/woowak/lab/store/service/dto/StoreRegistrationRequest.java @@ -1,4 +1,4 @@ -package camp.woowak.lab.store.service; +package camp.woowak.lab.store.service.dto; import java.time.LocalDateTime; diff --git a/src/test/java/camp/woowak/lab/store/service/StoreRegistrationServiceTest.java b/src/test/java/camp/woowak/lab/store/service/StoreRegistrationServiceTest.java index 9630dcef..63164b6d 100644 --- a/src/test/java/camp/woowak/lab/store/service/StoreRegistrationServiceTest.java +++ b/src/test/java/camp/woowak/lab/store/service/StoreRegistrationServiceTest.java @@ -19,6 +19,7 @@ import camp.woowak.lab.store.exception.StoreException; import camp.woowak.lab.store.repository.StoreCategoryRepository; import camp.woowak.lab.store.repository.StoreRepository; +import camp.woowak.lab.store.service.dto.StoreRegistrationRequest; import camp.woowak.lab.vendor.domain.Vendor; @ExtendWith(MockitoExtension.class) From 020ffbfbf72d0da6d409688b52bbd6dffc95f883 Mon Sep 17 00:00:00 2001 From: june-777 Date: Sun, 11 Aug 2024 23:46:58 +0900 Subject: [PATCH 30/47] =?UTF-8?q?[feat]=20=EA=B0=80=EA=B2=8C=EC=99=80=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=EB=90=9C=20=EC=98=88=EC=99=B8=EB=A5=BC=20?= =?UTF-8?q?=EB=AA=85=EC=8B=9C=ED=95=98=EB=8A=94=20ErrorCode=20Enum=20?= =?UTF-8?q?=EC=A0=95=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 가게 관련한 예외의 범주를 ErrorCode Enum 으로 한정하도록 함 - 예외 메시지를 응집하여, 테스트 코드에서 예외 메시지 검증으로 구체적인 검증이 용이하도록 하기 위함 --- .../lab/store/exception/StoreException.java | 35 +++++++++++++++++-- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/src/main/java/camp/woowak/lab/store/exception/StoreException.java b/src/main/java/camp/woowak/lab/store/exception/StoreException.java index 33cd1642..628c6d6c 100644 --- a/src/main/java/camp/woowak/lab/store/exception/StoreException.java +++ b/src/main/java/camp/woowak/lab/store/exception/StoreException.java @@ -1,9 +1,38 @@ package camp.woowak.lab.store.exception; +import lombok.Getter; + +@Getter public class StoreException extends RuntimeException { - public StoreException(String message) { - super(message); + private final ErrorCode errorCode; + + public StoreException(final ErrorCode errorCode) { + super(errorCode.getMessage()); + this.errorCode = errorCode; } - + + @Getter + public enum ErrorCode { + + INVALID_NAME_RANGE("가게 이름은 2글자 ~ 10글자 이어야합니다."), + + INVALID_ADDRESS("가게 주소는 송파구만 가능합니다."), + + INVALID_MIN_ORDER_PRICE("최소 주문 금액은 5,000원 이상이어야 합니다."), + INVALID_UNIT_OF_MIN_ORDER_PRICE("최소 주문 금액은 1,000원 단위이어야 합니다."), + + INVALID_TIME_UNIT("가게 시작 시간은 분 단위까지 가능합니다"), + INVALID_TIME("가게 시작 시간은 종료 시간보다 이전이어야 합니다"), + + INVALID_STORE_CATEGORY("존재하지 않는 가게 카테고리입니다."); + + private final String message; + + ErrorCode(String message) { + this.message = message; + } + + } + } From 1f697f525be255a16663def30e455bc30110e7a0 Mon Sep 17 00:00:00 2001 From: june-777 Date: Sun, 11 Aug 2024 23:48:09 +0900 Subject: [PATCH 31/47] =?UTF-8?q?[refactor]=20StoreException=20=EC=9D=84?= =?UTF-8?q?=20=EC=83=9D=EC=84=B1=ED=95=A0=20=EB=95=8C,=20=EA=B0=80?= =?UTF-8?q?=EA=B2=8C=20=EA=B4=80=EB=A0=A8=20ErrorCode=20Enum=20=EC=9D=84?= =?UTF-8?q?=20=EC=83=9D=EC=84=B1=EC=9E=90=20=EB=A7=A4=EA=B0=9C=EB=B3=80?= =?UTF-8?q?=EC=88=98=EB=A1=9C=20=EC=82=AC=EC=9A=A9=ED=95=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lab/store/domain/StoreValidator.java | 18 ++++++++++-------- .../service/StoreRegistrationService.java | 4 +++- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/main/java/camp/woowak/lab/store/domain/StoreValidator.java b/src/main/java/camp/woowak/lab/store/domain/StoreValidator.java index ebcb133c..d17d0a3d 100644 --- a/src/main/java/camp/woowak/lab/store/domain/StoreValidator.java +++ b/src/main/java/camp/woowak/lab/store/domain/StoreValidator.java @@ -1,5 +1,7 @@ package camp.woowak.lab.store.domain; +import static camp.woowak.lab.store.exception.StoreException.ErrorCode.*; + import java.time.LocalDateTime; import camp.woowak.lab.store.exception.StoreException; @@ -27,7 +29,7 @@ private static void validateName(final String name) { if (MIN_NAME_LENGTH <= name.length() && name.length() <= MAX_NAME_LENGTH) { return; } - throw new StoreException("가게 이름은 2글자 ~ 10글자 이어야합니다."); + throw new StoreException(INVALID_NAME_RANGE); } // TODO: 가게 위치 비즈니스 요구사항 구체화하면, 주소 검증 로직 수정 예정 @@ -35,18 +37,18 @@ private static void validateAddress(final String address) { if (StoreAddress.DEFAULT_DISTRICT.equals(address)) { return; } - throw new StoreException("가게 주소는 송파구만 가능합니다."); + throw new StoreException(INVALID_ADDRESS); } private static void validateMinOrderPrice(final Integer minOrderPrice) { if (minOrderPrice < MIN_ORDER_PRICE) { - throw new StoreException("최소 주문 금액은 5,000원 이상이어야 합니다."); + throw new StoreException(INVALID_MIN_ORDER_PRICE); } } private static void validateUnitOrderPrice(final Integer minOrderPrice) { if (minOrderPrice % UNIT_OF_MIN_ORDER_PRICE != 0) { - throw new StoreException("최소 주문 금액은 1,000원 단위이어야 합니다."); + throw new StoreException(INVALID_UNIT_OF_MIN_ORDER_PRICE); } } @@ -54,19 +56,19 @@ private static void validateTime(final LocalDateTime startTime, final LocalDateTime endTime ) { if (isInvalidStoreTimeUnit(startTime)) { - throw new StoreException("가게 시작 시간은 분 단위까지 가능합니다"); + throw new StoreException(INVALID_TIME_UNIT); } if (isInvalidStoreTimeUnit(endTime)) { - throw new StoreException("가게 종료 시간은 분 단위까지 가능합니다"); + throw new StoreException(INVALID_TIME_UNIT); } if (endTime.isBefore(startTime)) { - throw new StoreException("가게 시작 시간은 종료 시간보다 이전이어야 합니다"); + throw new StoreException(INVALID_TIME); } if (startTime.isEqual(endTime)) { - throw new StoreException("가게 시작 시간과 종료 시간이 일치합니다"); + throw new StoreException(INVALID_TIME); } } diff --git a/src/main/java/camp/woowak/lab/store/service/StoreRegistrationService.java b/src/main/java/camp/woowak/lab/store/service/StoreRegistrationService.java index e68f2121..f4c385d8 100644 --- a/src/main/java/camp/woowak/lab/store/service/StoreRegistrationService.java +++ b/src/main/java/camp/woowak/lab/store/service/StoreRegistrationService.java @@ -1,5 +1,7 @@ package camp.woowak.lab.store.service; +import static camp.woowak.lab.store.exception.StoreException.ErrorCode.*; + import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -41,7 +43,7 @@ private Store createStore(Vendor vendor, StoreCategory storeCategory, StoreRegis private StoreCategory findStoreCategoryByName(final String name) { return storeCategoryRepository.findByName(name) - .orElseThrow(() -> new StoreException("존재하지 않는 가게 카테고리입니다.")); + .orElseThrow(() -> new StoreException(INVALID_STORE_CATEGORY)); } } From 3e5a748c5967a2d162bc8481be21ea433532f43c Mon Sep 17 00:00:00 2001 From: june-777 Date: Sun, 11 Aug 2024 23:48:53 +0900 Subject: [PATCH 32/47] =?UTF-8?q?[test]=20=EA=B0=80=EA=B2=8C=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=20=EC=84=9C=EB=B9=84=EC=8A=A4=20=EC=98=88=EC=99=B8=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D=EC=9D=84=20=EC=98=88=EC=99=B8=20=EB=A9=94?= =?UTF-8?q?=EC=8B=9C=EC=A7=80=EB=A1=9C=20=EA=B5=AC=EC=B2=B4=EC=A0=81?= =?UTF-8?q?=EC=9D=B8=20=EA=B2=80=EC=A6=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lab/store/service/StoreRegistrationServiceTest.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/test/java/camp/woowak/lab/store/service/StoreRegistrationServiceTest.java b/src/test/java/camp/woowak/lab/store/service/StoreRegistrationServiceTest.java index 63164b6d..fd351bff 100644 --- a/src/test/java/camp/woowak/lab/store/service/StoreRegistrationServiceTest.java +++ b/src/test/java/camp/woowak/lab/store/service/StoreRegistrationServiceTest.java @@ -1,5 +1,6 @@ package camp.woowak.lab.store.service; +import static camp.woowak.lab.store.exception.StoreException.ErrorCode.*; import static org.mockito.BDDMockito.*; import java.time.LocalDateTime; @@ -73,7 +74,8 @@ void notExistStoreCategoryName() { // when & then Assertions.assertThatThrownBy(() -> storeRegistrationService.storeRegistration(vendor, request)) - .isInstanceOf(StoreException.class); + .isInstanceOf(StoreException.class) + .hasMessage(INVALID_STORE_CATEGORY.getMessage()); then(storeRepository).shouldHaveNoInteractions(); } From bd08a152e84e2810b4673ec7a44d9baa62ce8ee7 Mon Sep 17 00:00:00 2001 From: june-777 Date: Sun, 11 Aug 2024 23:49:02 +0900 Subject: [PATCH 33/47] =?UTF-8?q?[test]=20=EA=B0=80=EA=B2=8C=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20=EC=98=88=EC=99=B8=20=EA=B2=80=EC=A6=9D=EC=9D=84=20?= =?UTF-8?q?=EC=98=88=EC=99=B8=20=EB=A9=94=EC=8B=9C=EC=A7=80=EB=A1=9C=20?= =?UTF-8?q?=EA=B5=AC=EC=B2=B4=EC=A0=81=EC=9D=B8=20=EA=B2=80=EC=A6=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../woowak/lab/store/domain/StoreTest.java | 34 +++++++++++++------ 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/src/test/java/camp/woowak/lab/store/domain/StoreTest.java b/src/test/java/camp/woowak/lab/store/domain/StoreTest.java index b6391d64..20b96e69 100644 --- a/src/test/java/camp/woowak/lab/store/domain/StoreTest.java +++ b/src/test/java/camp/woowak/lab/store/domain/StoreTest.java @@ -1,5 +1,6 @@ package camp.woowak.lab.store.domain; +import static camp.woowak.lab.store.exception.StoreException.ErrorCode.*; import static org.assertj.core.api.Assertions.*; import java.time.LocalDateTime; @@ -56,7 +57,8 @@ void lessThanMinOrderPrice() { assertThatThrownBy(() -> new Store(null, null, validNameFixture, validAddressFixture, null, lessThanMinOrderPrice, validStartTimeFixture, validEndTimeFixture)) - .isInstanceOf(StoreException.class); + .isInstanceOf(StoreException.class) + .hasMessage(INVALID_MIN_ORDER_PRICE.getMessage()); } @Test @@ -82,7 +84,8 @@ void inValidUnitOrderPrice() { assertThatThrownBy(() -> new Store(null, null, validNameFixture, validAddressFixture, null, inValidUnitOrderPrice, validStartTimeFixture, validEndTimeFixture)) - .isInstanceOf(StoreException.class); + .isInstanceOf(StoreException.class) + .hasMessage(INVALID_UNIT_OF_MIN_ORDER_PRICE.getMessage()); } } @@ -114,7 +117,8 @@ void endTimeSameWithStartTime() { assertThatThrownBy( () -> new Store(null, null, validNameFixture, validAddressFixture, null, validMinOrderPriceFixture, validStartTimeFixture, endTimeSameWithStartTime)) - .isInstanceOf(StoreException.class); + .isInstanceOf(StoreException.class) + .hasMessage(INVALID_TIME.getMessage()); } @Test @@ -127,7 +131,8 @@ void endTimeBeforeThanStartTime() { assertThatThrownBy( () -> new Store(null, null, validNameFixture, validAddressFixture, null, validMinOrderPriceFixture, validStartTimeFixture, endTimeBeforeThanStartTime)) - .isInstanceOf(StoreException.class); + .isInstanceOf(StoreException.class) + .hasMessage(INVALID_TIME.getMessage()); } @Test @@ -155,7 +160,8 @@ void startTimeWithSeconds() { assertThatThrownBy( () -> new Store(null, null, validNameFixture, validAddressFixture, null, validMinOrderPriceFixture, startTimeWithSeconds, validEndTimeFixture)) - .isInstanceOf(StoreException.class); + .isInstanceOf(StoreException.class) + .hasMessage(INVALID_TIME_UNIT.getMessage()); } @Test @@ -168,7 +174,8 @@ void startTimeWithNanoSeconds() { // when & then assertThatThrownBy(() -> new Store(null, null, validNameFixture, validAddressFixture, null, 5000, startTimeWithNanoSeconds, validEndTimeFixture)) - .isInstanceOf(StoreException.class); + .isInstanceOf(StoreException.class) + .hasMessage(INVALID_TIME_UNIT.getMessage()); } @Test @@ -181,7 +188,8 @@ void endTimeWithSeconds() { // when & then assertThatThrownBy(() -> new Store(null, null, validNameFixture, validAddressFixture, null, 5000, validStartTimeFixture, endTimeWithSeconds)) - .isInstanceOf(StoreException.class); + .isInstanceOf(StoreException.class) + .hasMessage(INVALID_TIME_UNIT.getMessage()); } @Test @@ -194,7 +202,8 @@ void endTimeWithNanoSeconds() { // when & then assertThatThrownBy(() -> new Store(null, null, validNameFixture, validAddressFixture, null, 5000, validStartTimeFixture, endTimeWithNanoSeconds)) - .isInstanceOf(StoreException.class); + .isInstanceOf(StoreException.class) + .hasMessage(INVALID_TIME_UNIT.getMessage()); } } @@ -226,7 +235,8 @@ void lessThanMinLengthName() { assertThatThrownBy(() -> new Store(null, null, lessThanMinLengthName, validAddressFixture, null, validMinOrderPriceFixture, validStartTimeFixture, validEndTimeFixture)) - .isInstanceOf(StoreException.class); + .isInstanceOf(StoreException.class) + .hasMessage(INVALID_NAME_RANGE.getMessage()); } @Test @@ -240,7 +250,8 @@ void greaterThanMaxLengthName() { () -> new Store(null, null, greaterThanMaxLengthName, validAddressFixture, null, validMinOrderPriceFixture, validStartTimeFixture, validEndTimeFixture)) - .isInstanceOf(StoreException.class); + .isInstanceOf(StoreException.class) + .hasMessage(INVALID_NAME_RANGE.getMessage()); } } @@ -270,7 +281,8 @@ void notSongPa() { assertThatThrownBy( () -> new Store(null, null, validNameFixture, validAddress, null, validMinOrderPriceFixture, validStartTimeFixture, validEndTimeFixture)) - .isInstanceOf(StoreException.class); + .isInstanceOf(StoreException.class) + .hasMessage(INVALID_ADDRESS.getMessage()); } } From 4160efddbc06ef8743c6d982806f17dd08c4b13d Mon Sep 17 00:00:00 2001 From: june-777 Date: Mon, 12 Aug 2024 01:04:01 +0900 Subject: [PATCH 34/47] =?UTF-8?q?[fix]=20Long,=20LocalDateTime=20=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=84=B0=20=ED=83=80=EC=9E=85=EC=97=90=20=EB=8C=80?= =?UTF-8?q?=ED=95=9C=20Bean=20Validation=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @NotBlank 는 String 타입에 적용 가능 --- .../lab/store/service/dto/StoreRegistrationRequest.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/camp/woowak/lab/store/service/dto/StoreRegistrationRequest.java b/src/main/java/camp/woowak/lab/store/service/dto/StoreRegistrationRequest.java index 7968f09f..0b927adc 100644 --- a/src/main/java/camp/woowak/lab/store/service/dto/StoreRegistrationRequest.java +++ b/src/main/java/camp/woowak/lab/store/service/dto/StoreRegistrationRequest.java @@ -3,6 +3,7 @@ import java.time.LocalDateTime; import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; public record StoreRegistrationRequest( @@ -18,13 +19,13 @@ public record StoreRegistrationRequest( @NotBlank String storeCategoryName, - @NotBlank + @NotNull Integer storeMinOrderPrice, - @NotBlank + @NotNull LocalDateTime storeStarTime, - @NotBlank + @NotNull LocalDateTime storeEndTime ) { From fc53788fad5620ca2db728cba3863aa725781ac5 Mon Sep 17 00:00:00 2001 From: june-777 Date: Mon, 12 Aug 2024 01:04:47 +0900 Subject: [PATCH 35/47] =?UTF-8?q?[feat]=20Store=20=EA=B0=80=EA=B2=8C=20API?= =?UTF-8?q?=20=EC=BB=A8=ED=8A=B8=EB=A1=A4=EB=9F=AC=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lab/web/api/store/StoreApiController.java | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 src/main/java/camp/woowak/lab/web/api/store/StoreApiController.java diff --git a/src/main/java/camp/woowak/lab/web/api/store/StoreApiController.java b/src/main/java/camp/woowak/lab/web/api/store/StoreApiController.java new file mode 100644 index 00000000..3df7560a --- /dev/null +++ b/src/main/java/camp/woowak/lab/web/api/store/StoreApiController.java @@ -0,0 +1,32 @@ +package camp.woowak.lab.web.api.store; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +import camp.woowak.lab.store.service.StoreRegistrationService; +import camp.woowak.lab.store.service.dto.StoreRegistrationRequest; +import camp.woowak.lab.vendor.domain.Vendor; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; + +@RestController +@RequiredArgsConstructor +public class StoreApiController { + + private final StoreRegistrationService storeRegistrationService; + + // TODO: + // 1. 인증/인가에 대한 스펙이 정의되어야, Vendor Resolver 로직을 결정할 수 있음 + // 2. SSR, CSR 에 대해 통일해야, API 반환타입을 fix 할 수 있음 + // 3. API 공통 응답 명세에 대한 논의 진행 필요 + @PostMapping("/stores") + public ResponseEntity storeRegistration(final Vendor vendor, + final @Valid @RequestBody StoreRegistrationRequest request + ) { + storeRegistrationService.storeRegistration(vendor, request); + return ResponseEntity.ok().build(); + } + +} From 6ae068b54e29947c09c109d448840ded36e1d5ae Mon Sep 17 00:00:00 2001 From: june-777 Date: Mon, 12 Aug 2024 01:05:18 +0900 Subject: [PATCH 36/47] =?UTF-8?q?[feat]=20Store=20=EC=98=88=EC=99=B8=20?= =?UTF-8?q?=ED=95=B8=EB=93=A4=EB=9F=AC=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web/api/store/StoreExceptionHandler.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 src/main/java/camp/woowak/lab/web/api/store/StoreExceptionHandler.java diff --git a/src/main/java/camp/woowak/lab/web/api/store/StoreExceptionHandler.java b/src/main/java/camp/woowak/lab/web/api/store/StoreExceptionHandler.java new file mode 100644 index 00000000..ca2b86df --- /dev/null +++ b/src/main/java/camp/woowak/lab/web/api/store/StoreExceptionHandler.java @@ -0,0 +1,21 @@ +package camp.woowak.lab.web.api.store; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import camp.woowak.lab.store.exception.StoreException; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@RestControllerAdvice +public class StoreExceptionHandler { + + @ExceptionHandler(StoreException.class) + public ResponseEntity handleException(StoreException exception) { + log.warn(exception.getMessage(), exception); + return new ResponseEntity<>("fail", HttpStatus.BAD_REQUEST); + } + +} From 882229067bd2f01674d9c0c87a2ae569136b7388 Mon Sep 17 00:00:00 2001 From: june-777 Date: Mon, 12 Aug 2024 01:05:23 +0900 Subject: [PATCH 37/47] =?UTF-8?q?[test]=20Store=20=EA=B0=80=EA=B2=8C=20API?= =?UTF-8?q?=20=EC=BB=A8=ED=8A=B8=EB=A1=A4=EB=9F=AC=20=EB=8B=A8=EC=9C=84=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web/api/store/StoreApiControllerTest.java | 105 ++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 src/test/java/camp/woowak/lab/web/api/store/StoreApiControllerTest.java diff --git a/src/test/java/camp/woowak/lab/web/api/store/StoreApiControllerTest.java b/src/test/java/camp/woowak/lab/web/api/store/StoreApiControllerTest.java new file mode 100644 index 00000000..faacf8d7 --- /dev/null +++ b/src/test/java/camp/woowak/lab/web/api/store/StoreApiControllerTest.java @@ -0,0 +1,105 @@ +package camp.woowak.lab.web.api.store; + +import static camp.woowak.lab.store.exception.StoreException.ErrorCode.*; +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import java.time.LocalDateTime; + +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.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; + +import camp.woowak.lab.infra.date.DateTimeProvider; +import camp.woowak.lab.store.exception.StoreException; +import camp.woowak.lab.store.service.StoreRegistrationService; +import camp.woowak.lab.store.service.dto.StoreRegistrationRequest; +import camp.woowak.lab.vendor.domain.Vendor; + +@WebMvcTest(StoreApiController.class) +class StoreApiControllerTest { + + @Autowired + private MockMvc mockMvc; + + @MockBean + private StoreRegistrationService storeRegistrationService; + + DateTimeProvider fixedStartTime = () -> LocalDateTime.of(2024, 8, 24, 1, 0, 0); + DateTimeProvider fixedEndTime = () -> LocalDateTime.of(2024, 8, 24, 5, 0, 0); + + LocalDateTime validStartTimeFixture = fixedStartTime.now(); + LocalDateTime validEndTimeFixture = fixedEndTime.now(); + + ObjectMapper objectMapper = new ObjectMapper(); + + @BeforeEach + void setUp() { + objectMapper = new ObjectMapper(); + objectMapper.registerModule(new JavaTimeModule()); + } + + @Test + @DisplayName("[Success] 200 OK") + void storeRegistrationSuccess() throws Exception { + // given + Vendor vendor = new Vendor(); + StoreRegistrationRequest request = new StoreRegistrationRequest( + "Store Name", + "Category Name", + "Store Address", + "123-456-7890", + 10000, + validStartTimeFixture, + validEndTimeFixture + ); + + // when & then + mockMvc.perform(post("/stores") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request)) + .sessionAttr("vendor", vendor)) // TODO: 세션/JWT 인증 방식 토의 후, 정해진 방식에 맞게 수정 필요 (현재는 세션 방식으로 단위 테스트 진행) + .andExpect(status().isOk()) + .andExpect(content().string("")); + + verify(storeRegistrationService).storeRegistration(any(Vendor.class), any(StoreRegistrationRequest.class)); + } + + @Test + @DisplayName("[Exception] 400 Bad Request") + void storeRegistrationFailure() throws Exception { + // given + Vendor vendor = new Vendor(); // Vendor 객체 생성 방법에 따라 적절히 초기화 + StoreRegistrationRequest request = new StoreRegistrationRequest( + "Store Name", + "Invalid Category", + "Store Address", + "123-456-7890", + 10000, + validStartTimeFixture, + validEndTimeFixture + ); + + doThrow(new StoreException(INVALID_STORE_CATEGORY)) + .when(storeRegistrationService).storeRegistration(any(Vendor.class), any(StoreRegistrationRequest.class)); + + // when & then + mockMvc.perform(post("/stores") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request)) + .sessionAttr("vendor", vendor)) // TODO: 세션/JWT 인증 방식 토의 후, 정해진 방식에 맞게 수정 필요 (현재는 세션 방식으로 단위 테스트 진행) + .andExpect(status().isBadRequest()) + .andExpect(content().string("fail")); + + verify(storeRegistrationService).storeRegistration(any(Vendor.class), any(StoreRegistrationRequest.class)); + } +} \ No newline at end of file From be5b1454e158311a20dfdff3a851149b4151c49b Mon Sep 17 00:00:00 2001 From: june-777 Date: Tue, 13 Aug 2024 23:54:02 +0900 Subject: [PATCH 38/47] =?UTF-8?q?[feat]=20Bean=20Validation=20=EC=98=88?= =?UTF-8?q?=EC=99=B8=20=EB=A9=94=EC=8B=9C=EC=A7=80=20=EA=B5=AC=EC=B2=B4?= =?UTF-8?q?=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/dto/StoreRegistrationRequest.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/java/camp/woowak/lab/store/service/dto/StoreRegistrationRequest.java b/src/main/java/camp/woowak/lab/store/service/dto/StoreRegistrationRequest.java index 0b927adc..875be27a 100644 --- a/src/main/java/camp/woowak/lab/store/service/dto/StoreRegistrationRequest.java +++ b/src/main/java/camp/woowak/lab/store/service/dto/StoreRegistrationRequest.java @@ -7,25 +7,25 @@ public record StoreRegistrationRequest( - @NotBlank + @NotBlank(message = "가게 이름은 필수값입니다.") String storeName, - @NotBlank + @NotBlank(message = "가게 주소는 필수값입니다.") String storeAddress, - @NotBlank + @NotBlank(message = "가게 번호는 필수값입니다.") String storePhoneNumber, - @NotBlank + @NotBlank(message = "가게 카테고리 이름은 필수값입니다.") String storeCategoryName, - @NotNull + @NotNull(message = "가게 최소 주문 가격은 필수값입니다.") Integer storeMinOrderPrice, - @NotNull + @NotNull(message = "가게 시작 시간은 필수값입니다.") LocalDateTime storeStarTime, - @NotNull + @NotNull(message = "가게 종료 시간은 필수값입니다.") LocalDateTime storeEndTime ) { From 38a129904d62ea1b274ffe5a2a91a1536355fbb3 Mon Sep 17 00:00:00 2001 From: june-777 Date: Wed, 14 Aug 2024 02:09:42 +0900 Subject: [PATCH 39/47] =?UTF-8?q?[fix]=20requestDTO=20=EC=98=A4=ED=83=80?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../camp/woowak/lab/store/service/StoreRegistrationService.java | 2 +- .../woowak/lab/store/service/dto/StoreRegistrationRequest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/camp/woowak/lab/store/service/StoreRegistrationService.java b/src/main/java/camp/woowak/lab/store/service/StoreRegistrationService.java index f4c385d8..88f8d533 100644 --- a/src/main/java/camp/woowak/lab/store/service/StoreRegistrationService.java +++ b/src/main/java/camp/woowak/lab/store/service/StoreRegistrationService.java @@ -37,7 +37,7 @@ private Store createStore(Vendor vendor, StoreCategory storeCategory, StoreRegis request.storeAddress(), request.storePhoneNumber(), request.storeMinOrderPrice(), - request.storeStarTime(), + request.storeStartTime(), request.storeEndTime()); } diff --git a/src/main/java/camp/woowak/lab/store/service/dto/StoreRegistrationRequest.java b/src/main/java/camp/woowak/lab/store/service/dto/StoreRegistrationRequest.java index 875be27a..ebf13716 100644 --- a/src/main/java/camp/woowak/lab/store/service/dto/StoreRegistrationRequest.java +++ b/src/main/java/camp/woowak/lab/store/service/dto/StoreRegistrationRequest.java @@ -23,7 +23,7 @@ public record StoreRegistrationRequest( Integer storeMinOrderPrice, @NotNull(message = "가게 시작 시간은 필수값입니다.") - LocalDateTime storeStarTime, + LocalDateTime storeStartTime, @NotNull(message = "가게 종료 시간은 필수값입니다.") LocalDateTime storeEndTime From 191bed570777dcebbc15cb52169e260d88e19ace Mon Sep 17 00:00:00 2001 From: june-777 Date: Wed, 14 Aug 2024 02:10:50 +0900 Subject: [PATCH 40/47] =?UTF-8?q?[test]=20=EB=B3=91=ED=95=A9=20=EA=B3=BC?= =?UTF-8?q?=EC=A0=95=EC=97=90=EC=84=9C=20=EA=B5=AC=ED=98=84=EB=90=9C=20Ven?= =?UTF-8?q?dor=20=EA=B0=9D=EC=B2=B4=EC=97=90=20=EB=A7=9E=EA=B2=8C=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20=EB=B0=8F=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../store/repository/StoreRepositoryTest.java | 35 ++++++++++++++++--- .../service/StoreRegistrationServiceTest.java | 15 ++++++-- 2 files changed, 43 insertions(+), 7 deletions(-) diff --git a/src/test/java/camp/woowak/lab/store/repository/StoreRepositoryTest.java b/src/test/java/camp/woowak/lab/store/repository/StoreRepositoryTest.java index f04cc524..6ac00380 100644 --- a/src/test/java/camp/woowak/lab/store/repository/StoreRepositoryTest.java +++ b/src/test/java/camp/woowak/lab/store/repository/StoreRepositoryTest.java @@ -1,8 +1,10 @@ package camp.woowak.lab.store.repository; +import static org.assertj.core.api.Assertions.*; + import java.time.LocalDateTime; -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; @@ -10,11 +12,15 @@ import org.springframework.dao.InvalidDataAccessApiUsageException; import camp.woowak.lab.infra.date.DateTimeProvider; +import camp.woowak.lab.payaccount.domain.PayAccount; +import camp.woowak.lab.payaccount.repository.PayAccountRepository; import camp.woowak.lab.store.domain.Store; import camp.woowak.lab.store.domain.StoreAddress; import camp.woowak.lab.store.domain.StoreCategory; import camp.woowak.lab.vendor.domain.Vendor; import camp.woowak.lab.vendor.repository.VendorRepository; +import camp.woowak.lab.web.authentication.NoOpPasswordEncoder; +import camp.woowak.lab.web.authentication.PasswordEncoder; @DataJpaTest class StoreRepositoryTest { @@ -28,6 +34,9 @@ class StoreRepositoryTest { @Autowired private StoreCategoryRepository storeCategoryRepository; + @Autowired + PayAccountRepository payAccountRepository; + DateTimeProvider fixedStartTime = () -> LocalDateTime.of(2024, 8, 24, 1, 0, 0); DateTimeProvider fixedEndTime = () -> LocalDateTime.of(2024, 8, 24, 5, 0, 0); @@ -39,11 +48,22 @@ class StoreRepositoryTest { String validPhoneNumberFixture = "02-0000-0000"; Integer validMinOrderPriceFixture = 5000; + PayAccount payAccount; + PasswordEncoder passwordEncoder; + + @BeforeEach + void setUp() { + payAccount = new PayAccount(); + payAccountRepository.save(payAccount); + + passwordEncoder = new NoOpPasswordEncoder(); + } + @Test @DisplayName("[Success] 가게 저장 성공") void successfulSaveStore() { // given - Vendor vendor = new Vendor(); + Vendor vendor = createVendor(); vendorRepository.saveAndFlush(vendor); StoreCategory storeCategory = new StoreCategory("한식"); @@ -52,7 +72,7 @@ void successfulSaveStore() { Store store = createStore(vendor, storeCategory); // when & then - Assertions.assertThatCode(() -> storeRepository.save(store)) + assertThatCode(() -> storeRepository.save(store)) .doesNotThrowAnyException(); } @@ -60,7 +80,7 @@ void successfulSaveStore() { @DisplayName("[Exception] 참조중인 엔티티가 저장되지 않았고, Not Null 제약조건이면 InvalidDataAccessApiUsageException - Not-null property 가 발생한다") void test() { // given - Vendor notSavedVendor = new Vendor(); + Vendor notSavedVendor = createVendor(); StoreCategory storeCategory = new StoreCategory("한식"); storeCategoryRepository.saveAndFlush(storeCategory); @@ -69,10 +89,15 @@ void test() { Store store = createStore(notSavedVendor, storeCategory); // then - Assertions.assertThatThrownBy(() -> storeRepository.saveAndFlush(store)) + assertThatThrownBy(() -> storeRepository.saveAndFlush(store)) .isInstanceOf(InvalidDataAccessApiUsageException.class); } + private Vendor createVendor() { + return new Vendor("vendorName", "vendorEmail@example.com", "vendorPassword", "010-0000-0000", payAccount, + passwordEncoder); + } + private Store createStore(Vendor vendor, StoreCategory storeCategory) { return new Store(vendor, storeCategory, diff --git a/src/test/java/camp/woowak/lab/store/service/StoreRegistrationServiceTest.java b/src/test/java/camp/woowak/lab/store/service/StoreRegistrationServiceTest.java index fd351bff..aa483061 100644 --- a/src/test/java/camp/woowak/lab/store/service/StoreRegistrationServiceTest.java +++ b/src/test/java/camp/woowak/lab/store/service/StoreRegistrationServiceTest.java @@ -15,6 +15,8 @@ import org.mockito.junit.jupiter.MockitoExtension; import camp.woowak.lab.infra.date.DateTimeProvider; +import camp.woowak.lab.payaccount.domain.PayAccount; +import camp.woowak.lab.payaccount.domain.TestPayAccount; import camp.woowak.lab.store.domain.Store; import camp.woowak.lab.store.domain.StoreCategory; import camp.woowak.lab.store.exception.StoreException; @@ -22,6 +24,8 @@ import camp.woowak.lab.store.repository.StoreRepository; import camp.woowak.lab.store.service.dto.StoreRegistrationRequest; import camp.woowak.lab.vendor.domain.Vendor; +import camp.woowak.lab.web.authentication.NoOpPasswordEncoder; +import camp.woowak.lab.web.authentication.PasswordEncoder; @ExtendWith(MockitoExtension.class) class StoreRegistrationServiceTest { @@ -45,7 +49,7 @@ class StoreRegistrationServiceTest { @DisplayName("[Success] 가게가 저장된다.") void successfulRegistration() { // given - Vendor vendor = new Vendor(); + Vendor vendor = createVendor(); String storeCategoryName = "한식"; StoreRegistrationRequest request = createStoreRegistrationRequest(storeCategoryName); StoreCategory mockStoreCategory = new StoreCategory(storeCategoryName); @@ -65,7 +69,7 @@ void successfulRegistration() { @DisplayName("[Exception] 유효하지 않은 가게 카테고리면 예외가 발생한다.") void notExistStoreCategoryName() { // given - Vendor vendor = new Vendor(); + Vendor vendor = createVendor(); String invalidCategoryName = "존재하지 않는 카테고리"; StoreRegistrationRequest request = createStoreRegistrationRequest(invalidCategoryName); @@ -85,4 +89,11 @@ private StoreRegistrationRequest createStoreRegistrationRequest(String storeCate storeCategory, 5000, validStartTimeFixture, validEndTimeFixture); } + private Vendor createVendor() { + PayAccount payAccount = new TestPayAccount(1L); + PasswordEncoder passwordEncoder = new NoOpPasswordEncoder(); + return new Vendor("vendorName", "vendorEmail@example.com", "vendorPassword", "010-0000-0000", payAccount, + passwordEncoder); + } + } \ No newline at end of file From c64baf967d8eb27ef2ec9f6b56e28c4a6623bc7c Mon Sep 17 00:00:00 2001 From: june-777 Date: Wed, 14 Aug 2024 02:11:17 +0900 Subject: [PATCH 41/47] =?UTF-8?q?[test]=20=EB=B3=91=ED=95=A9=20=EA=B3=BC?= =?UTF-8?q?=EC=A0=95=EC=97=90=EC=84=9C=20=EA=B5=AC=ED=98=84=EB=90=9C=20Ses?= =?UTF-8?q?sionResolver=20=EB=A7=9E=EA=B2=8C=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=20=EC=BD=94=EB=93=9C=20=EB=AA=A8=ED=82=B9=20=EC=B6=94=EA=B0=80?= =?UTF-8?q?=20=EB=B0=8F=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web/api/store/StoreApiControllerTest.java | 50 ++++++++++++++++--- 1 file changed, 42 insertions(+), 8 deletions(-) diff --git a/src/test/java/camp/woowak/lab/web/api/store/StoreApiControllerTest.java b/src/test/java/camp/woowak/lab/web/api/store/StoreApiControllerTest.java index faacf8d7..4e9265cb 100644 --- a/src/test/java/camp/woowak/lab/web/api/store/StoreApiControllerTest.java +++ b/src/test/java/camp/woowak/lab/web/api/store/StoreApiControllerTest.java @@ -1,13 +1,15 @@ package camp.woowak.lab.web.api.store; import static camp.woowak.lab.store.exception.StoreException.ErrorCode.*; -import static org.mockito.Mockito.*; +import static org.mockito.BDDMockito.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; import java.time.LocalDateTime; +import java.util.Optional; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -20,10 +22,18 @@ import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import camp.woowak.lab.infra.date.DateTimeProvider; +import camp.woowak.lab.payaccount.domain.PayAccount; +import camp.woowak.lab.payaccount.domain.TestPayAccount; import camp.woowak.lab.store.exception.StoreException; import camp.woowak.lab.store.service.StoreRegistrationService; import camp.woowak.lab.store.service.dto.StoreRegistrationRequest; import camp.woowak.lab.vendor.domain.Vendor; +import camp.woowak.lab.vendor.repository.VendorRepository; +import camp.woowak.lab.web.authentication.LoginVendor; +import camp.woowak.lab.web.authentication.NoOpPasswordEncoder; +import camp.woowak.lab.web.authentication.PasswordEncoder; +import camp.woowak.lab.web.resolver.session.SessionConst; +import camp.woowak.lab.web.resolver.session.SessionVendorArgumentResolver; @WebMvcTest(StoreApiController.class) class StoreApiControllerTest { @@ -34,6 +44,12 @@ class StoreApiControllerTest { @MockBean private StoreRegistrationService storeRegistrationService; + @MockBean + private VendorRepository vendorRepository; + + @MockBean + private SessionVendorArgumentResolver sessionVendorArgumentResolver; + DateTimeProvider fixedStartTime = () -> LocalDateTime.of(2024, 8, 24, 1, 0, 0); DateTimeProvider fixedEndTime = () -> LocalDateTime.of(2024, 8, 24, 5, 0, 0); @@ -42,43 +58,56 @@ class StoreApiControllerTest { ObjectMapper objectMapper = new ObjectMapper(); + private PayAccount payAccount; + private PasswordEncoder passwordEncoder; + @BeforeEach void setUp() { objectMapper = new ObjectMapper(); objectMapper.registerModule(new JavaTimeModule()); + payAccount = new TestPayAccount(1L); + passwordEncoder = new NoOpPasswordEncoder(); } @Test @DisplayName("[Success] 200 OK") void storeRegistrationSuccess() throws Exception { // given - Vendor vendor = new Vendor(); + Vendor vendor = createVendor(); + LoginVendor loginVendor = new LoginVendor(5L); StoreRegistrationRequest request = new StoreRegistrationRequest( "Store Name", - "Category Name", "Store Address", "123-456-7890", + "Category Name", 10000, validStartTimeFixture, validEndTimeFixture ); + given(sessionVendorArgumentResolver.supportsParameter(any())) + .willReturn(true); + given(sessionVendorArgumentResolver.resolveArgument(any(), any(), any(), any())) + .willReturn(loginVendor); + given(vendorRepository.findById(loginVendor.getId())).willReturn(Optional.of(vendor)); + // when & then mockMvc.perform(post("/stores") .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(request)) - .sessionAttr("vendor", vendor)) // TODO: 세션/JWT 인증 방식 토의 후, 정해진 방식에 맞게 수정 필요 (현재는 세션 방식으로 단위 테스트 진행) - .andExpect(status().isOk()) - .andExpect(content().string("")); + .sessionAttr(SessionConst.SESSION_VENDOR_KEY, loginVendor)) + .andExpect(status().isOk()); verify(storeRegistrationService).storeRegistration(any(Vendor.class), any(StoreRegistrationRequest.class)); } + @Disabled @Test @DisplayName("[Exception] 400 Bad Request") void storeRegistrationFailure() throws Exception { // given - Vendor vendor = new Vendor(); // Vendor 객체 생성 방법에 따라 적절히 초기화 + Vendor vendor = createVendor(); + LoginVendor loginVendor = new LoginVendor(5L); StoreRegistrationRequest request = new StoreRegistrationRequest( "Store Name", "Invalid Category", @@ -96,10 +125,15 @@ void storeRegistrationFailure() throws Exception { mockMvc.perform(post("/stores") .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(request)) - .sessionAttr("vendor", vendor)) // TODO: 세션/JWT 인증 방식 토의 후, 정해진 방식에 맞게 수정 필요 (현재는 세션 방식으로 단위 테스트 진행) + .sessionAttr(SessionConst.SESSION_VENDOR_KEY, loginVendor)) .andExpect(status().isBadRequest()) .andExpect(content().string("fail")); verify(storeRegistrationService).storeRegistration(any(Vendor.class), any(StoreRegistrationRequest.class)); } + + private Vendor createVendor() { + return new Vendor("vendorName", "vendorEmail@example.com", "vendorPassword", "010-0000-0000", payAccount, + passwordEncoder); + } } \ No newline at end of file From 6e088ca3f0ff442f695c34425d17d45855158a09 Mon Sep 17 00:00:00 2001 From: june-777 Date: Wed, 14 Aug 2024 02:11:58 +0900 Subject: [PATCH 42/47] =?UTF-8?q?[feat]=20SessionArgumentResolver=20?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EB=93=B1=EB=A1=9D=20?= =?UTF-8?q?=EB=B0=8F=20WebConfiguration=20=EC=84=A4=EC=A0=95=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lab/web/config/WebConfiguration.java | 25 +++++++++++++++++++ .../SessionCustomerArgumentResolver.java | 4 ++- .../SessionVendorArgumentResolver.java | 4 ++- 3 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 src/main/java/camp/woowak/lab/web/config/WebConfiguration.java diff --git a/src/main/java/camp/woowak/lab/web/config/WebConfiguration.java b/src/main/java/camp/woowak/lab/web/config/WebConfiguration.java new file mode 100644 index 00000000..63902fef --- /dev/null +++ b/src/main/java/camp/woowak/lab/web/config/WebConfiguration.java @@ -0,0 +1,25 @@ +package camp.woowak.lab.web.config; + +import java.util.List; + +import org.springframework.stereotype.Component; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import camp.woowak.lab.web.resolver.session.SessionCustomerArgumentResolver; +import camp.woowak.lab.web.resolver.session.SessionVendorArgumentResolver; +import lombok.RequiredArgsConstructor; + +@Component +@RequiredArgsConstructor +public class WebConfiguration implements WebMvcConfigurer { + + private final SessionVendorArgumentResolver sessionVendorArgumentResolver; + private final SessionCustomerArgumentResolver sessionCustomerArgumentResolver; + + @Override + public void addArgumentResolvers(List resolvers) { + resolvers.add(sessionVendorArgumentResolver); + resolvers.add(sessionCustomerArgumentResolver); + } +} diff --git a/src/main/java/camp/woowak/lab/web/resolver/session/SessionCustomerArgumentResolver.java b/src/main/java/camp/woowak/lab/web/resolver/session/SessionCustomerArgumentResolver.java index a172b231..b6a28b43 100644 --- a/src/main/java/camp/woowak/lab/web/resolver/session/SessionCustomerArgumentResolver.java +++ b/src/main/java/camp/woowak/lab/web/resolver/session/SessionCustomerArgumentResolver.java @@ -1,6 +1,7 @@ package camp.woowak.lab.web.resolver.session; 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.ModelAndViewContainer; @@ -12,6 +13,7 @@ import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpSession; +@Component public class SessionCustomerArgumentResolver extends LoginMemberArgumentResolver { @Override public boolean supportsParameter(MethodParameter parameter) { @@ -21,7 +23,7 @@ public boolean supportsParameter(MethodParameter parameter) { @Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, - NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { + NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { AuthenticationPrincipal parameterAnnotation = parameter.getParameterAnnotation(AuthenticationPrincipal.class); if (parameterAnnotation == null) { return null; diff --git a/src/main/java/camp/woowak/lab/web/resolver/session/SessionVendorArgumentResolver.java b/src/main/java/camp/woowak/lab/web/resolver/session/SessionVendorArgumentResolver.java index c6cafbfd..492dd5d3 100644 --- a/src/main/java/camp/woowak/lab/web/resolver/session/SessionVendorArgumentResolver.java +++ b/src/main/java/camp/woowak/lab/web/resolver/session/SessionVendorArgumentResolver.java @@ -1,6 +1,7 @@ package camp.woowak.lab.web.resolver.session; 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.ModelAndViewContainer; @@ -12,6 +13,7 @@ import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpSession; +@Component public class SessionVendorArgumentResolver extends LoginMemberArgumentResolver { @Override public boolean supportsParameter(MethodParameter parameter) { @@ -21,7 +23,7 @@ public boolean supportsParameter(MethodParameter parameter) { @Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, - NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { + NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { AuthenticationPrincipal parameterAnnotation = parameter.getParameterAnnotation(AuthenticationPrincipal.class); if (parameterAnnotation == null) { return null; From 2696f11d18eee946534a78f80920841dc1e941b2 Mon Sep 17 00:00:00 2001 From: june-777 Date: Wed, 14 Aug 2024 02:12:28 +0900 Subject: [PATCH 43/47] =?UTF-8?q?[refactor]=20SessionArgumentResolver=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=EC=97=90=20=EB=A7=9E=EA=B2=8C=20=EC=BB=A8?= =?UTF-8?q?=ED=8A=B8=EB=A1=A4=EB=9F=AC=20=EB=A9=94=EC=84=9C=EB=93=9C=20?= =?UTF-8?q?=EC=8B=9C=EA=B7=B8=EB=8B=88=EC=B3=90=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../camp/woowak/lab/web/api/store/StoreApiController.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/camp/woowak/lab/web/api/store/StoreApiController.java b/src/main/java/camp/woowak/lab/web/api/store/StoreApiController.java index 3df7560a..4e93de4d 100644 --- a/src/main/java/camp/woowak/lab/web/api/store/StoreApiController.java +++ b/src/main/java/camp/woowak/lab/web/api/store/StoreApiController.java @@ -8,6 +8,9 @@ import camp.woowak.lab.store.service.StoreRegistrationService; import camp.woowak.lab.store.service.dto.StoreRegistrationRequest; import camp.woowak.lab.vendor.domain.Vendor; +import camp.woowak.lab.vendor.repository.VendorRepository; +import camp.woowak.lab.web.authentication.LoginVendor; +import camp.woowak.lab.web.authentication.annotation.AuthenticationPrincipal; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; @@ -16,15 +19,17 @@ public class StoreApiController { private final StoreRegistrationService storeRegistrationService; + private final VendorRepository vendorRepository; // TODO: // 1. 인증/인가에 대한 스펙이 정의되어야, Vendor Resolver 로직을 결정할 수 있음 // 2. SSR, CSR 에 대해 통일해야, API 반환타입을 fix 할 수 있음 // 3. API 공통 응답 명세에 대한 논의 진행 필요 @PostMapping("/stores") - public ResponseEntity storeRegistration(final Vendor vendor, + public ResponseEntity storeRegistration(@AuthenticationPrincipal final LoginVendor loginVendor, final @Valid @RequestBody StoreRegistrationRequest request ) { + Vendor vendor = vendorRepository.findById(loginVendor.getId()).orElseThrow(); storeRegistrationService.storeRegistration(vendor, request); return ResponseEntity.ok().build(); } From 5c3ce74f43198b3bdcbc0be9fee809976f4d3b67 Mon Sep 17 00:00:00 2001 From: june-777 Date: Wed, 14 Aug 2024 02:16:15 +0900 Subject: [PATCH 44/47] =?UTF-8?q?[refactor]=20yaml=20=ED=8C=8C=EC=9D=BC?= =?UTF-8?q?=EB=A1=9C=20=ED=99=98=EA=B2=BD=EC=84=A4=EC=A0=95=20=EB=8C=80?= =?UTF-8?q?=EC=B2=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.properties | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 src/main/resources/application.properties diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties deleted file mode 100644 index b4956434..00000000 --- a/src/main/resources/application.properties +++ /dev/null @@ -1,4 +0,0 @@ -spring.application.name=lab -spring.jpa.show-sql=true -spring.jpa.generate-ddl=true -spring.jpa.hibernate.ddl-auto=create-drop From c81b9d42af22c925299b51c14ac505fd89d21550 Mon Sep 17 00:00:00 2001 From: june-777 Date: Wed, 14 Aug 2024 10:35:40 +0900 Subject: [PATCH 45/47] =?UTF-8?q?[feat]=20=EC=BB=A8=ED=8A=B8=EB=A1=A4?= =?UTF-8?q?=EB=9F=AC=20=EB=8B=A8=EC=9C=84=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=EC=97=90=20MockBean=20=EC=95=A0=EB=85=B8=ED=85=8C=EC=9D=B4?= =?UTF-8?q?=EC=85=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../camp/woowak/lab/web/api/store/StoreApiControllerTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/test/java/camp/woowak/lab/web/api/store/StoreApiControllerTest.java b/src/test/java/camp/woowak/lab/web/api/store/StoreApiControllerTest.java index 4e9265cb..f4eb004f 100644 --- a/src/test/java/camp/woowak/lab/web/api/store/StoreApiControllerTest.java +++ b/src/test/java/camp/woowak/lab/web/api/store/StoreApiControllerTest.java @@ -15,6 +15,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.data.jpa.mapping.JpaMetamodelMappingContext; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; @@ -36,6 +37,7 @@ import camp.woowak.lab.web.resolver.session.SessionVendorArgumentResolver; @WebMvcTest(StoreApiController.class) +@MockBean(JpaMetamodelMappingContext.class) class StoreApiControllerTest { @Autowired From a76a39c0c4495d66da09bbda559ae5251eedabbf Mon Sep 17 00:00:00 2001 From: june-777 Date: Wed, 14 Aug 2024 10:36:04 +0900 Subject: [PATCH 46/47] =?UTF-8?q?[fix]=20=EC=A4=91=EB=B3=B5=EB=90=9C=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lab/web/config/WebConfiguration.java | 25 ------------------- 1 file changed, 25 deletions(-) delete mode 100644 src/main/java/camp/woowak/lab/web/config/WebConfiguration.java diff --git a/src/main/java/camp/woowak/lab/web/config/WebConfiguration.java b/src/main/java/camp/woowak/lab/web/config/WebConfiguration.java deleted file mode 100644 index 63902fef..00000000 --- a/src/main/java/camp/woowak/lab/web/config/WebConfiguration.java +++ /dev/null @@ -1,25 +0,0 @@ -package camp.woowak.lab.web.config; - -import java.util.List; - -import org.springframework.stereotype.Component; -import org.springframework.web.method.support.HandlerMethodArgumentResolver; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; - -import camp.woowak.lab.web.resolver.session.SessionCustomerArgumentResolver; -import camp.woowak.lab.web.resolver.session.SessionVendorArgumentResolver; -import lombok.RequiredArgsConstructor; - -@Component -@RequiredArgsConstructor -public class WebConfiguration implements WebMvcConfigurer { - - private final SessionVendorArgumentResolver sessionVendorArgumentResolver; - private final SessionCustomerArgumentResolver sessionCustomerArgumentResolver; - - @Override - public void addArgumentResolvers(List resolvers) { - resolvers.add(sessionVendorArgumentResolver); - resolvers.add(sessionCustomerArgumentResolver); - } -} From a52b1c8a01586aa5e07146b2cc7bc4fccc7c8008 Mon Sep 17 00:00:00 2001 From: june-777 Date: Wed, 14 Aug 2024 10:36:44 +0900 Subject: [PATCH 47/47] =?UTF-8?q?[style]=20=EB=9D=BC=EC=9D=B8=20=ED=8F=AC?= =?UTF-8?q?=EB=A7=B7=ED=8C=85,=20import=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/camp/woowak/lab/customer/domain/CustomerValidator.java | 2 +- .../camp/woowak/lab/customer/service/SignUpCustomerService.java | 2 +- .../woowak/lab/payaccount/exception/PayAccountErrorCode.java | 2 +- .../camp/woowak/lab/web/api/customer/CustomerApiController.java | 2 +- .../camp/woowak/lab/web/api/store/StoreExceptionHandler.java | 2 +- .../web/resolver/session/SessionCustomerArgumentResolver.java | 1 - 6 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/main/java/camp/woowak/lab/customer/domain/CustomerValidator.java b/src/main/java/camp/woowak/lab/customer/domain/CustomerValidator.java index 118692fb..0ad33509 100644 --- a/src/main/java/camp/woowak/lab/customer/domain/CustomerValidator.java +++ b/src/main/java/camp/woowak/lab/customer/domain/CustomerValidator.java @@ -15,7 +15,7 @@ private CustomerValidator() { } public static void validateCreation(String name, String email, String password, String phone, - PayAccount payAccount) throws InvalidCreationException { + PayAccount payAccount) throws InvalidCreationException { validateName(name); validateEmail(email); validatePassword(password); diff --git a/src/main/java/camp/woowak/lab/customer/service/SignUpCustomerService.java b/src/main/java/camp/woowak/lab/customer/service/SignUpCustomerService.java index 3aa0c421..d43aad29 100644 --- a/src/main/java/camp/woowak/lab/customer/service/SignUpCustomerService.java +++ b/src/main/java/camp/woowak/lab/customer/service/SignUpCustomerService.java @@ -20,7 +20,7 @@ public class SignUpCustomerService { private final PasswordEncoder passwordEncoder; public SignUpCustomerService(CustomerRepository customerRepository, PayAccountRepository payAccountRepository, - PasswordEncoder passwordEncoder) { + PasswordEncoder passwordEncoder) { this.customerRepository = customerRepository; this.payAccountRepository = payAccountRepository; this.passwordEncoder = passwordEncoder; diff --git a/src/main/java/camp/woowak/lab/payaccount/exception/PayAccountErrorCode.java b/src/main/java/camp/woowak/lab/payaccount/exception/PayAccountErrorCode.java index 827ffeb4..75191ce5 100644 --- a/src/main/java/camp/woowak/lab/payaccount/exception/PayAccountErrorCode.java +++ b/src/main/java/camp/woowak/lab/payaccount/exception/PayAccountErrorCode.java @@ -7,7 +7,7 @@ public enum PayAccountErrorCode implements ErrorCode { INVALID_TRANSACTION_AMOUNT(HttpStatus.BAD_REQUEST, "a_1_1", "금액은 0보다 커야합니다."), DAILY_LIMIT_EXCEED(HttpStatus.BAD_REQUEST, "a_1_2", "일일 충전 한도 금액을 초과했습니다."), - INSUFFICIENT_BALANCE(HttpStatus.BAD_REQUEST,"a_1_3","금액이 부족합니다."), + INSUFFICIENT_BALANCE(HttpStatus.BAD_REQUEST, "a_1_3", "금액이 부족합니다."), ACCOUNT_NOT_FOUND(HttpStatus.NOT_FOUND, "a_1_4", "계좌를 찾을 수 없습니다."); private final int status; diff --git a/src/main/java/camp/woowak/lab/web/api/customer/CustomerApiController.java b/src/main/java/camp/woowak/lab/web/api/customer/CustomerApiController.java index 17397fcd..3de4a296 100644 --- a/src/main/java/camp/woowak/lab/web/api/customer/CustomerApiController.java +++ b/src/main/java/camp/woowak/lab/web/api/customer/CustomerApiController.java @@ -25,7 +25,7 @@ public CustomerApiController(SignUpCustomerService signUpCustomerService) { @PostMapping("/customers") public ResponseEntity> signUp(@Valid @RequestBody SignUpCustomerRequest request, - HttpServletResponse response) { + HttpServletResponse response) { SignUpCustomerCommand command = new SignUpCustomerCommand(request.name(), request.email(), request.password(), request.phone()); diff --git a/src/main/java/camp/woowak/lab/web/api/store/StoreExceptionHandler.java b/src/main/java/camp/woowak/lab/web/api/store/StoreExceptionHandler.java index ca2b86df..e0a9be19 100644 --- a/src/main/java/camp/woowak/lab/web/api/store/StoreExceptionHandler.java +++ b/src/main/java/camp/woowak/lab/web/api/store/StoreExceptionHandler.java @@ -17,5 +17,5 @@ public ResponseEntity handleException(StoreException exception) { log.warn(exception.getMessage(), exception); return new ResponseEntity<>("fail", HttpStatus.BAD_REQUEST); } - + } diff --git a/src/main/java/camp/woowak/lab/web/resolver/session/SessionCustomerArgumentResolver.java b/src/main/java/camp/woowak/lab/web/resolver/session/SessionCustomerArgumentResolver.java index 89448d2d..b6a28b43 100644 --- a/src/main/java/camp/woowak/lab/web/resolver/session/SessionCustomerArgumentResolver.java +++ b/src/main/java/camp/woowak/lab/web/resolver/session/SessionCustomerArgumentResolver.java @@ -12,7 +12,6 @@ import camp.woowak.lab.web.authentication.annotation.AuthenticationPrincipal; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpSession; -import lombok.extern.slf4j.Slf4j; @Component public class SessionCustomerArgumentResolver extends LoginMemberArgumentResolver {