From c7f353f01156f9ce1c72b45b358a0a90b8d15a83 Mon Sep 17 00:00:00 2001 From: byeolhaha Date: Wed, 8 Nov 2023 15:57:15 +0900 Subject: [PATCH 01/21] =?UTF-8?q?Style=20:=20'@Column(name=3D)'=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EC=B9=BC=EB=9F=BC=20=EB=84=A4=EC=9D=B4=EB=B0=8D?= =?UTF-8?q?=EC=8A=A4=EB=84=A4=EC=9D=B4=ED=81=AC=EC=8B=9D=20=EB=AA=85?= =?UTF-8?q?=EC=8B=9C=EC=A0=81=EC=9C=BC=EB=A1=9C=20=ED=91=9C=EC=8B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/academy/model/Academy.java | 11 ++++------ .../domain/academy/model/ReviewCount.java | 12 +++++------ .../model/vo/academyinfo/AcademyInfo.java | 14 ++++++------- .../model/vo/academyinfo/PhoneNumber.java | 20 +++++++++---------- .../domain/child/model/Child.java | 2 +- .../domain/member/model/Member.java | 10 +++++----- 6 files changed, 33 insertions(+), 36 deletions(-) diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/model/Academy.java b/src/main/java/org/guzzing/studayserver/domain/academy/model/Academy.java index 41e79f00..fd188a9b 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/model/Academy.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/model/Academy.java @@ -1,11 +1,7 @@ package org.guzzing.studayserver.domain.academy.model; -import jakarta.persistence.Embedded; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.Table; +import jakarta.persistence.*; + import java.util.Objects; import lombok.Getter; import org.guzzing.studayserver.domain.academy.model.vo.Address; @@ -32,6 +28,7 @@ public class Academy extends BaseEntity { @Embedded private Location location; + @Column(name="max_education_fee") private Long maxEducationFee; private Point point; @@ -66,7 +63,7 @@ public String getAcademyName() { } public String getContact() { - return academyInfo.getContact(); + return academyInfo.getPhoneNumber(); } public String getShuttleAvailability() { diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/model/ReviewCount.java b/src/main/java/org/guzzing/studayserver/domain/academy/model/ReviewCount.java index 47032bf7..bc10bece 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/model/ReviewCount.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/model/ReviewCount.java @@ -21,22 +21,22 @@ public class ReviewCount { @GeneratedValue(strategy = GenerationType.IDENTITY) Long id; - @Column(nullable = false) + @Column(nullable = false, name = "kindness_count") private int kindnessCount; - @Column(nullable = false) + @Column(nullable = false,name = "good_facility_count") private int goodFacilityCount; - @Column(nullable = false) + @Column(nullable = false, name="cheap_fee_count") private int cheapFeeCount; - @Column(nullable = false) + @Column(nullable = false, name="good_management_count") private int goodManagementCount; - @Column(nullable = false) + @Column(nullable = false, name="lovely_teaching_count") private int lovelyTeachingCount; - @Column(nullable = false) + @Column(nullable = false, name = "reviewers_count") private int reviewersCount; @OneToOne diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/model/vo/academyinfo/AcademyInfo.java b/src/main/java/org/guzzing/studayserver/domain/academy/model/vo/academyinfo/AcademyInfo.java index aab52bda..499472f8 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/model/vo/academyinfo/AcademyInfo.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/model/vo/academyinfo/AcademyInfo.java @@ -18,7 +18,7 @@ public class AcademyInfo { private String academyName; @Embedded - private PhoneNumber contact; + private PhoneNumber phoneNumber; @Enumerated(value = EnumType.STRING) private ShuttleAvailability shuttle; @@ -26,11 +26,11 @@ public class AcademyInfo { @Column(name = "area_of_expertise", nullable = false) private String areaOfExpertise; - protected AcademyInfo(final String academyName, final String contact, final String shuttle, final String areaOfExpertise) { + protected AcademyInfo(final String academyName, final String phoneNumber, final String shuttle, final String areaOfExpertise) { Assert.isTrue(StringUtils.isNotBlank(academyName), "학원명이 주어지지 않았습니다."); this.academyName = academyName; - this.contact = new PhoneNumber(contact); + this.phoneNumber = new PhoneNumber(phoneNumber); this.shuttle = ShuttleAvailability.getShuttleAvailability(shuttle); this.areaOfExpertise = areaOfExpertise; } @@ -43,8 +43,8 @@ public static AcademyInfo of(final String name, final String contact, final Stri protected AcademyInfo() { } - public String getContact() { - return contact.getContact(); + public String getPhoneNumber() { + return phoneNumber.getPhoneNumber(); } public String getShuttle() { @@ -64,12 +64,12 @@ public boolean equals(Object o) { return false; } AcademyInfo that = (AcademyInfo) o; - return Objects.equals(academyName, that.academyName) && Objects.equals(contact, that.contact) && shuttle == that.shuttle; + return Objects.equals(academyName, that.academyName) && Objects.equals(phoneNumber, that.phoneNumber) && shuttle == that.shuttle; } @Override public int hashCode() { - return Objects.hash(academyName, contact, shuttle); + return Objects.hash(academyName, phoneNumber, shuttle); } } diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/model/vo/academyinfo/PhoneNumber.java b/src/main/java/org/guzzing/studayserver/domain/academy/model/vo/academyinfo/PhoneNumber.java index 6c3b7a09..ef5137c9 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/model/vo/academyinfo/PhoneNumber.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/model/vo/academyinfo/PhoneNumber.java @@ -14,20 +14,20 @@ public class PhoneNumber { @Transient private final String REGEX = "^\\d{2,3}-\\d{3,4}-\\d{3,4}$"; - @Column(name = "contact", nullable = true) - private String contact; + @Column(name = "phone_number", nullable = true) + private String phoneNumber; - public PhoneNumber(final String contact) { - validate(contact); - this.contact = contact; + public PhoneNumber(final String phoneNumber) { + validate(phoneNumber); + this.phoneNumber = phoneNumber; } protected PhoneNumber() { } - private void validate(final String contact) { - if (!contact.isBlank()) { - if (!Pattern.matches(REGEX, contact)) { + private void validate(final String phoneNumber) { + if (!phoneNumber.isBlank()) { + if (!Pattern.matches(REGEX, phoneNumber)) { throw new IllegalArgumentException("올바른 전화번호 형식이 아닙니다."); } } @@ -42,12 +42,12 @@ public boolean equals(Object o) { return false; } PhoneNumber that = (PhoneNumber) o; - return Objects.equals(REGEX, that.REGEX) && Objects.equals(contact, that.contact); + return Objects.equals(REGEX, that.REGEX) && Objects.equals(phoneNumber, that.phoneNumber); } @Override public int hashCode() { - return Objects.hash(REGEX, contact); + return Objects.hash(REGEX, phoneNumber); } } diff --git a/src/main/java/org/guzzing/studayserver/domain/child/model/Child.java b/src/main/java/org/guzzing/studayserver/domain/child/model/Child.java index 409717aa..360d1ca4 100644 --- a/src/main/java/org/guzzing/studayserver/domain/child/model/Child.java +++ b/src/main/java/org/guzzing/studayserver/domain/child/model/Child.java @@ -26,7 +26,7 @@ public class Child { private Long id; @Embedded - @Column(nullable = false) + @Column(nullable = false,name = "nick_name") private NickName nickName; @Enumerated(EnumType.STRING) diff --git a/src/main/java/org/guzzing/studayserver/domain/member/model/Member.java b/src/main/java/org/guzzing/studayserver/domain/member/model/Member.java index 05d8ff90..a1480bc4 100644 --- a/src/main/java/org/guzzing/studayserver/domain/member/model/Member.java +++ b/src/main/java/org/guzzing/studayserver/domain/member/model/Member.java @@ -23,7 +23,7 @@ import org.guzzing.studayserver.domain.member.model.vo.RoleType; @Getter -@Table(name = "members", uniqueConstraints = @UniqueConstraint(columnNames = {"socialId", "memberProvider"})) +@Table(name = "members", uniqueConstraints = @UniqueConstraint(columnNames = {"social_Id", "member_provider"})) @Entity public class Member { @@ -35,22 +35,22 @@ public class Member { private Long id; @Embedded - @Column + @Column(name = "nick_name") private NickName nickName; @Embedded @Column private Email email; - @Column(nullable = false) + @Column(nullable = false, name = "social_id") private String socialId; @Enumerated(EnumType.STRING) - @Column(nullable = false) + @Column(nullable = false, name = "member_provider") private MemberProvider memberProvider; @Enumerated(EnumType.STRING) - @Column(nullable = false) + @Column(nullable = false, name = "role_tyle") private RoleType roleType; @OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY) From f796749bd99d5e4c68a356839d1438d7480d2005 Mon Sep 17 00:00:00 2001 From: byeolhaha Date: Wed, 8 Nov 2023 16:01:19 +0900 Subject: [PATCH 02/21] =?UTF-8?q?Style=20:=20=EC=B9=BC=EB=9F=BC=EB=AA=85?= =?UTF-8?q?=20contact=EC=97=90=EC=84=9C=20phoneNumber=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=EC=9C=BC=EB=A1=9C=20=EC=9D=B8=ED=95=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/academy/repository/AcademiesByLocation.java | 2 +- .../academy/service/dto/result/AcademiesByLocationResult.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/repository/AcademiesByLocation.java b/src/main/java/org/guzzing/studayserver/domain/academy/repository/AcademiesByLocation.java index 4f1a41ee..51efb6c5 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/repository/AcademiesByLocation.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/repository/AcademiesByLocation.java @@ -4,7 +4,7 @@ public record AcademiesByLocation( Long academyId, String academyName, String fullAddress, - String contact, + String phoneNumber, String areaOfExpertise, Double latitude, Double longitude diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/result/AcademiesByLocationResult.java b/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/result/AcademiesByLocationResult.java index 6d38e23f..5fffe8b0 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/result/AcademiesByLocationResult.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/result/AcademiesByLocationResult.java @@ -17,7 +17,7 @@ public static AcademiesByLocationResult from(AcademiesByLocation academiesByLoca academiesByLocation.academyId(), academiesByLocation.academyName(), academiesByLocation.fullAddress(), - academiesByLocation.contact(), + academiesByLocation.phoneNumber(), academiesByLocation.areaOfExpertise(), academiesByLocation.latitude(), academiesByLocation.longitude() From 0b6f2f94ff5830ec5318ad8212ea7fdb3c139b8e Mon Sep 17 00:00:00 2001 From: byeolhaha Date: Wed, 8 Nov 2023 16:03:08 +0900 Subject: [PATCH 03/21] =?UTF-8?q?Feat=20:=20=ED=95=99=EC=9B=90=20=ED=95=84?= =?UTF-8?q?=ED=84=B0=20=EA=B2=80=EC=83=89=20=EC=BF=BC=EB=A6=AC=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20(=ED=9D=AC=EB=A7=9D=EA=B8=88=EC=95=A1,=20=EA=B3=BC?= =?UTF-8?q?=EB=AA=A9,=20=EC=9C=84=EC=B9=98)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/AcademyByFiltering.java | 13 +++++ .../repository/AcademyFilterCondition.java | 9 +++ .../academy/AcademyQueryRepository.java | 4 ++ .../academy/AcademyQueryRepositoryImpl.java | 57 ++++++++++++++++++- .../repository/academy/AcademyRepository.java | 3 + 5 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 src/main/java/org/guzzing/studayserver/domain/academy/repository/AcademyByFiltering.java create mode 100644 src/main/java/org/guzzing/studayserver/domain/academy/repository/AcademyFilterCondition.java diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/repository/AcademyByFiltering.java b/src/main/java/org/guzzing/studayserver/domain/academy/repository/AcademyByFiltering.java new file mode 100644 index 00000000..0baed333 --- /dev/null +++ b/src/main/java/org/guzzing/studayserver/domain/academy/repository/AcademyByFiltering.java @@ -0,0 +1,13 @@ +package org.guzzing.studayserver.domain.academy.repository; + +public record AcademyByFiltering( + Long academyId, + String academyName, + String fullAddress, + String phoneNumber, + String areaOfExpertise, + Double latitude, + Double longitude, + String shuttleAvailable +) { +} diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/repository/AcademyFilterCondition.java b/src/main/java/org/guzzing/studayserver/domain/academy/repository/AcademyFilterCondition.java new file mode 100644 index 00000000..e10ecfdd --- /dev/null +++ b/src/main/java/org/guzzing/studayserver/domain/academy/repository/AcademyFilterCondition.java @@ -0,0 +1,9 @@ +package org.guzzing.studayserver.domain.academy.repository; + +public record AcademyFilterCondition( + String pointFormat, + String areaOfExpertises, + Long desiredMinAmount, + Long desiredMaxAmount +) { +} diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyQueryRepository.java b/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyQueryRepository.java index 9b19e548..e408087c 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyQueryRepository.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyQueryRepository.java @@ -1,9 +1,13 @@ package org.guzzing.studayserver.domain.academy.repository.academy; import org.guzzing.studayserver.domain.academy.repository.AcademiesByLocation; +import org.guzzing.studayserver.domain.academy.repository.AcademyByFiltering; +import org.guzzing.studayserver.domain.academy.repository.AcademyFilterCondition; import java.util.List; public interface AcademyQueryRepository { List findAcademiesByLocation(String pointFormat); + + List filterAcademies(AcademyFilterCondition academyFilterCondition); } diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyQueryRepositoryImpl.java b/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyQueryRepositoryImpl.java index cabbe127..06c864e4 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyQueryRepositoryImpl.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyQueryRepositoryImpl.java @@ -3,9 +3,12 @@ import jakarta.persistence.EntityManager; import jakarta.persistence.Query; import org.guzzing.studayserver.domain.academy.repository.AcademiesByLocation; +import org.guzzing.studayserver.domain.academy.repository.AcademyByFiltering; +import org.guzzing.studayserver.domain.academy.repository.AcademyFilterCondition; import org.hibernate.transform.ResultTransformer; import org.hibernate.type.StandardBasicTypes; + import java.util.List; public class AcademyQueryRepositoryImpl implements AcademyQueryRepository { @@ -18,14 +21,14 @@ public AcademyQueryRepositoryImpl(EntityManager em) { public List findAcademiesByLocation(String pointFormat) { Query query = em.createNativeQuery( - "SELECT a.id AS academyId, a.academy_name AS academyName, a.contact , a.full_address AS fullAddress," + + "SELECT a.id AS academyId, a.academy_name AS academyName, a.phone_number AS phoneNumber, a.full_address AS fullAddress," + " a.area_of_expertise AS areaOfExpertise, a.latitude AS latitude , a.longitude AS longitude FROM academies AS a " + "WHERE MBRContains(ST_LINESTRINGFROMTEXT(" + pointFormat + ", a.point)=1"); List academies = query.unwrap(org.hibernate.query.NativeQuery.class) .addScalar("academyId", StandardBasicTypes.LONG) .addScalar("academyName", StandardBasicTypes.STRING) - .addScalar("contact", StandardBasicTypes.STRING) + .addScalar("phoneNumber", StandardBasicTypes.STRING) .addScalar("fullAddress", StandardBasicTypes.STRING) .addScalar("areaOfExpertise", StandardBasicTypes.STRING) .addScalar("latitude", StandardBasicTypes.DOUBLE) @@ -56,4 +59,54 @@ public List transformList(List collection) { return academies; } + public List filterAcademies(AcademyFilterCondition academyFilterCondition) { + String nativeQuery = "SELECT a.id AS academyId, a.academy_name AS academyName, a.full_address AS fullAddress, " + + "a.phone_number AS phoneNumber, a.area_of_expertise AS areaOfExpertise, a.latitude, a.longitude, a.shuttle AS shuttleAvailable " + + "FROM academies AS a " + + "WHERE MBRContains(ST_LINESTRINGFROMTEXT(" + academyFilterCondition.pointFormat() + ", a.point)=1 "; + + if (academyFilterCondition.areaOfExpertises() != null && !academyFilterCondition.areaOfExpertises().isEmpty()) { + nativeQuery += " AND area_of_expertise IN "+ academyFilterCondition.areaOfExpertises(); + } + + if (academyFilterCondition.desiredMinAmount() != null && academyFilterCondition.desiredMaxAmount() != null) { + nativeQuery += " AND max_education_fee BETWEEN "+academyFilterCondition.desiredMinAmount() +" AND "+academyFilterCondition.desiredMaxAmount(); + } + + Query query = em.createNativeQuery(nativeQuery); + + return query.unwrap(org.hibernate.query.NativeQuery.class) + .addScalar("academyId", StandardBasicTypes.LONG) + .addScalar("academyName", StandardBasicTypes.STRING) + .addScalar("fullAddress", StandardBasicTypes.STRING) + .addScalar("phoneNumber", StandardBasicTypes.STRING) + .addScalar("areaOfExpertise", StandardBasicTypes.STRING) + .addScalar("latitude", StandardBasicTypes.DOUBLE) + .addScalar("longitude", StandardBasicTypes.DOUBLE) + .addScalar("shuttleAvailable", StandardBasicTypes.STRING) // 추가된 부분 + .setResultTransformer( + new ResultTransformer() { + @Override + public Object transformTuple(Object[] tuple, String[] aliases) { + return new AcademyByFiltering( + (Long) tuple[0], + (String) tuple[1], + (String) tuple[2], + (String) tuple[3], + (String) tuple[4], + (Double) tuple[5], + (Double) tuple[6], + (String) tuple[7] // shuttleAvailable 추가된 부분 + ); + } + + @Override + public List transformList(List collection) { + return collection; + } + } + ) + .getResultList(); + } + } diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyRepository.java b/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyRepository.java index a611923d..dab0f477 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyRepository.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyRepository.java @@ -3,6 +3,8 @@ import org.guzzing.studayserver.domain.academy.model.Academy; import org.guzzing.studayserver.domain.academy.repository.AcademiesByLocation; import org.guzzing.studayserver.domain.academy.repository.AcademiesByName; +import org.guzzing.studayserver.domain.academy.repository.AcademyByFiltering; +import org.guzzing.studayserver.domain.academy.repository.AcademyFilterCondition; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; import org.springframework.stereotype.Repository; @@ -20,4 +22,5 @@ public interface AcademyRepository extends AcademyJpaRepository, AcademyQueryRep List findAcademiesByLocation(String pointFormat); + List filterAcademies(AcademyFilterCondition academyFilterCondition); } From aae33ae29b0ce16447fb1a9f813279ebcb66982d Mon Sep 17 00:00:00 2001 From: byeolhaha Date: Wed, 8 Nov 2023 16:05:16 +0900 Subject: [PATCH 04/21] =?UTF-8?q?Feat:=20=ED=95=99=EC=9B=90=20=EA=B2=80?= =?UTF-8?q?=EC=83=89=20=ED=95=84=ED=84=B0=20=EA=B8=B0=EB=8A=A5=20=EC=84=9C?= =?UTF-8?q?=EB=B9=84=EC=8A=A4=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../academy/service/AcademyService.java | 43 +++++++++++++------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/service/AcademyService.java b/src/main/java/org/guzzing/studayserver/domain/academy/service/AcademyService.java index d5b71c18..3d72262a 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/service/AcademyService.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/service/AcademyService.java @@ -5,13 +5,16 @@ import org.guzzing.studayserver.domain.academy.model.Lesson; import org.guzzing.studayserver.domain.academy.model.ReviewCount; import org.guzzing.studayserver.domain.academy.model.vo.Location; + import org.guzzing.studayserver.domain.academy.repository.academy.AcademyRepository; import org.guzzing.studayserver.domain.academy.repository.lesson.LessonRepository; import org.guzzing.studayserver.domain.academy.repository.review.ReviewCountRepository; import org.guzzing.studayserver.domain.academy.service.dto.param.AcademiesByLocationParam; import org.guzzing.studayserver.domain.academy.service.dto.param.AcademiesByNameParam; +import org.guzzing.studayserver.domain.academy.service.dto.param.AcademyFilterParam; import org.guzzing.studayserver.domain.academy.service.dto.result.*; import org.guzzing.studayserver.domain.academy.util.GeometryUtil; +import org.guzzing.studayserver.domain.academy.util.SqlFormatter; import org.guzzing.studayserver.domain.academy.util.model.Direction; import org.springframework.data.domain.PageRequest; import org.springframework.stereotype.Service; @@ -20,7 +23,8 @@ @Service public class AcademyService { - private static final Double DISTANCE = 5.0; + private static final Double DISTANCE = 2.0; + private static final int PAGE_SIZE = 5; private final AcademyRepository academyRepository; @@ -45,23 +49,19 @@ public AcademyGetResult getAcademy(Long academyId) { return AcademyGetResult.from(academy, lessons, reviewCount); } - @Transactional(readOnly = true) + public AcademiesByLocationResults findAcademiesByLocation(AcademiesByLocationParam param) { - Location northEast = GeometryUtil.calculateLocationWithinRadiusInDirection( + Location northEast = calculateLocationWithinRadiusInDirection( param.baseLatitude(), param.baseLongitude(), - Direction.NORTHEAST.getBearing(), - DISTANCE); - Location southWest = GeometryUtil.calculateLocationWithinRadiusInDirection( + Direction.NORTHEAST); + Location southWest = calculateLocationWithinRadiusInDirection( param.baseLatitude(), param.baseLongitude(), - Direction.SOUTHWEST.getBearing(), - DISTANCE); - String diagonal = GeometryUtil.makeDiagonalByLineString(northEast, southWest); + Direction.SOUTHWEST); + String diagonal = SqlFormatter.makeDiagonalByLineString(northEast, southWest); - return AcademiesByLocationResults.to( - academyRepository.findAcademiesByLocation(diagonal) - ); + return AcademiesByLocationResults.to(academyRepository.findAcademiesByLocation(diagonal)); } @Transactional(readOnly = true) @@ -73,4 +73,23 @@ public AcademiesByNameResults findAcademiesByName(AcademiesByNameParam param) { ); } + @Transactional(readOnly = true) + public AcademyFilterResults filterAcademies(AcademyFilterParam param) { + Location northEast = calculateLocationWithinRadiusInDirection( + param.baseLatitude(), + param.baseLongitude(), + Direction.NORTHEAST); + Location southWest = calculateLocationWithinRadiusInDirection( + param.baseLatitude(), + param.baseLongitude(), + Direction.SOUTHWEST); + String diagonal = SqlFormatter.makeDiagonalByLineString(northEast, southWest); + + return AcademyFilterResults.from(academyRepository.filterAcademies(AcademyFilterParam.to(param, diagonal))); + } + + private Location calculateLocationWithinRadiusInDirection(double latitude, double longitude, Direction direction) { + return GeometryUtil.calculateLocationWithinRadiusInDirection(latitude, longitude, direction.getBearing(), DISTANCE); + } + } From 3d417b5f0a92b4306f19d6afe0624bd62b4aad8a Mon Sep 17 00:00:00 2001 From: byeolhaha Date: Wed, 8 Nov 2023 16:25:46 +0900 Subject: [PATCH 05/21] =?UTF-8?q?Feat=20:=20=ED=95=99=EC=9B=90=20=EA=B2=80?= =?UTF-8?q?=EC=83=89=20=ED=95=84=ED=84=B0=20=EA=B8=B0=EB=8A=A5=20=20?= =?UTF-8?q?=EC=84=9C=EB=B9=84=EC=8A=A4=20=EA=B4=80=EB=A0=A8=20dto=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 --- .../service/dto/param/AcademyFilterParam.java | 24 +++++++++++++++ .../dto/result/AcademyFilterResult.java | 29 +++++++++++++++++++ .../dto/result/AcademyFilterResults.java | 17 +++++++++++ 3 files changed, 70 insertions(+) create mode 100644 src/main/java/org/guzzing/studayserver/domain/academy/service/dto/param/AcademyFilterParam.java create mode 100644 src/main/java/org/guzzing/studayserver/domain/academy/service/dto/result/AcademyFilterResult.java create mode 100644 src/main/java/org/guzzing/studayserver/domain/academy/service/dto/result/AcademyFilterResults.java diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/param/AcademyFilterParam.java b/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/param/AcademyFilterParam.java new file mode 100644 index 00000000..5fbcdc6e --- /dev/null +++ b/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/param/AcademyFilterParam.java @@ -0,0 +1,24 @@ +package org.guzzing.studayserver.domain.academy.service.dto.param; + +import org.guzzing.studayserver.domain.academy.repository.AcademyFilterCondition; +import org.guzzing.studayserver.domain.academy.util.SqlFormatter; + +import java.util.List; + +public record AcademyFilterParam( + Double baseLatitude, + Double baseLongitude, + List areaOfExpertises, + Long desiredMinAmount, + Long desiredMaxAmount +) { + public static AcademyFilterCondition to(AcademyFilterParam param, String pointFormat){ + return new AcademyFilterCondition( + pointFormat, + SqlFormatter.makeWhereInString(param.areaOfExpertises), + param.desiredMinAmount, + param.desiredMaxAmount + ); + } + +} diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/result/AcademyFilterResult.java b/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/result/AcademyFilterResult.java new file mode 100644 index 00000000..f1dc72e6 --- /dev/null +++ b/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/result/AcademyFilterResult.java @@ -0,0 +1,29 @@ +package org.guzzing.studayserver.domain.academy.service.dto.result; + +import org.guzzing.studayserver.domain.academy.repository.AcademyByFiltering; + +public record AcademyFilterResult( + Long academyId, + String academyName, + String fullAddress, + String contact, + String areaOfExpertise, + Double latitude, + Double longitude, + String shuttleAvailable +) { + + public static AcademyFilterResult from(AcademyByFiltering academyByFiltering) { + return new AcademyFilterResult( + academyByFiltering.academyId(), + academyByFiltering.academyName(), + academyByFiltering.fullAddress(), + academyByFiltering.phoneNumber(), + academyByFiltering.areaOfExpertise(), + academyByFiltering.latitude(), + academyByFiltering.longitude(), + academyByFiltering.shuttleAvailable() + ); + } + +} diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/result/AcademyFilterResults.java b/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/result/AcademyFilterResults.java new file mode 100644 index 00000000..948a01f4 --- /dev/null +++ b/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/result/AcademyFilterResults.java @@ -0,0 +1,17 @@ +package org.guzzing.studayserver.domain.academy.service.dto.result; + +import org.guzzing.studayserver.domain.academy.repository.AcademyByFiltering; + +import java.util.List; + +public record AcademyFilterResults ( + List academyFilterResults +) { + public static AcademyFilterResults from(List academiesByFiltering) { + return new AcademyFilterResults( + academiesByFiltering.stream() + .map(academyByFiltering -> AcademyFilterResult.from(academyByFiltering)) + .toList() + ); + } +} From b083cbba9466f970fd94884c1abc8c72607e88b4 Mon Sep 17 00:00:00 2001 From: byeolhaha Date: Wed, 8 Nov 2023 16:27:10 +0900 Subject: [PATCH 06/21] =?UTF-8?q?Feat=20:=20List=EB=A1=9C=20=EB=B0=9B?= =?UTF-8?q?=EC=95=84=20where=20in=20=EC=95=88=EC=97=90=20=EB=A7=9E?= =?UTF-8?q?=EB=8A=94=20=ED=98=95=EC=8B=9D=EC=9C=BC=EB=A1=9C=20=EB=B0=94?= =?UTF-8?q?=EA=BE=B8=EB=8A=94=20sqlFormatter=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/academy/util/GeometryUtil.java | 9 ------ .../domain/academy/util/SqlFormatter.java | 31 +++++++++++++++++++ 2 files changed, 31 insertions(+), 9 deletions(-) create mode 100644 src/main/java/org/guzzing/studayserver/domain/academy/util/SqlFormatter.java diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/util/GeometryUtil.java b/src/main/java/org/guzzing/studayserver/domain/academy/util/GeometryUtil.java index 85d0a174..26a469e5 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/util/GeometryUtil.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/util/GeometryUtil.java @@ -7,7 +7,6 @@ public class GeometryUtil { private static final Double EARTH_RADIUS = 6371.01; private static final String LINESTRING_SQL = "'LINESTRING(%f %f, %f %f)')"; - ; public static Location calculateLocationWithinRadiusInDirection(Double baseLatitude, Double baseLongitude, @@ -48,12 +47,4 @@ private static Double normalizeLongitude(Double longitude) { return (longitude + 540) % 360 - 180; } - public static String makeDiagonalByLineString(Location northEast, Location southWest) { - return String.format( - LINESTRING_SQL, - northEast.getLongitude(), northEast.getLatitude(), southWest.getLongitude(), southWest.getLatitude() - ); - - } - } diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/util/SqlFormatter.java b/src/main/java/org/guzzing/studayserver/domain/academy/util/SqlFormatter.java new file mode 100644 index 00000000..6c52eaeb --- /dev/null +++ b/src/main/java/org/guzzing/studayserver/domain/academy/util/SqlFormatter.java @@ -0,0 +1,31 @@ +package org.guzzing.studayserver.domain.academy.util; + +import org.guzzing.studayserver.domain.academy.model.vo.Location; + +import java.util.List; + +public class SqlFormatter { + private static final String LINESTRING_SQL = "'LINESTRING(%f %f, %f %f)')"; + + public static String makeWhereInString(List values) { + StringBuilder builder = new StringBuilder("("); + for (int i = 0; i < values.size(); i++) { + builder.append("'"); + builder.append(values.get(i)); + builder.append("'"); + if (i < values.size() - 1) { + builder.append(", "); + } + } + builder.append(")"); + return builder.toString(); + } + + public static String makeDiagonalByLineString(Location northEast, Location southWest) { + return String.format( + LINESTRING_SQL, + northEast.getLongitude(), northEast.getLatitude(), southWest.getLongitude(), southWest.getLatitude() + ); + + } +} From b248ff8b4e5717d3d98eccce2b0556ad1e6af903 Mon Sep 17 00:00:00 2001 From: byeolhaha Date: Wed, 8 Nov 2023 16:31:58 +0900 Subject: [PATCH 07/21] =?UTF-8?q?Test=20:=20=ED=95=99=EC=9B=90=20=EA=B2=80?= =?UTF-8?q?=EC=83=89=20=ED=95=84=ED=84=B0=20=EC=84=9C=EB=B9=84=EC=8A=A4=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../academy/service/AcademyServiceTest.java | 55 +++++++++++++++++-- .../testutil/fixture/AcademyFixture.java | 43 --------------- .../fixture/academy/AcademyFixture.java | 5 ++ 3 files changed, 55 insertions(+), 48 deletions(-) delete mode 100644 src/test/java/org/guzzing/studayserver/testutil/fixture/AcademyFixture.java diff --git a/src/test/java/org/guzzing/studayserver/domain/academy/service/AcademyServiceTest.java b/src/test/java/org/guzzing/studayserver/domain/academy/service/AcademyServiceTest.java index edb8c16b..871d64b2 100644 --- a/src/test/java/org/guzzing/studayserver/domain/academy/service/AcademyServiceTest.java +++ b/src/test/java/org/guzzing/studayserver/domain/academy/service/AcademyServiceTest.java @@ -9,6 +9,7 @@ import org.guzzing.studayserver.domain.academy.repository.lesson.LessonRepository; import org.guzzing.studayserver.domain.academy.repository.review.ReviewCountRepository; import org.guzzing.studayserver.domain.academy.service.dto.param.AcademiesByNameParam; +import org.guzzing.studayserver.domain.academy.service.dto.param.AcademyFilterParam; import org.guzzing.studayserver.domain.academy.service.dto.result.*; import org.guzzing.studayserver.testutil.fixture.academy.AcademyFixture; import org.junit.jupiter.api.BeforeEach; @@ -19,6 +20,7 @@ import org.springframework.test.context.ActiveProfiles; import org.springframework.transaction.annotation.Transactional; import java.util.List; +import java.util.Random; @Transactional @ActiveProfiles({"oauth", "dev"}) @@ -26,6 +28,8 @@ class AcademyServiceTest { private static final String ACADEMY_NAME_FOR_SEARCH = "코딩"; + private double LATITUDE = 37.4449168; + private double LONGITUDE = 127.1388684; @Autowired private AcademyService academyService; @@ -86,10 +90,7 @@ void findAcademiesByLocation_academiesWithinDistance_equalsSize() { reviewCountRepository.deleteAll(); academyRepository.deleteAll(); - double latitude = 37.4449168; - double longitude = 127.1388684; - - List academies = AcademyFixture.randomAcademiesWithinDistance(latitude, longitude); + List academies = AcademyFixture.randomAcademiesWithinDistance(LATITUDE, LONGITUDE); for (Academy academy : academies) { Academy savedAcademy = academyRepository.save(academy); lessonRepository.save(AcademyFixture.lessonForSunganm(savedAcademy)); @@ -97,7 +98,7 @@ void findAcademiesByLocation_academiesWithinDistance_equalsSize() { } //When - AcademiesByLocationResults academiesByLocations = academyService.findAcademiesByLocation(AcademyFixture.academiesByLocationParam(latitude, longitude)); + AcademiesByLocationResults academiesByLocations = academyService.findAcademiesByLocation(AcademyFixture.academiesByLocationParam(LATITUDE, LONGITUDE)); //Then assertThat(academiesByLocations.academiesByLocationResults().size()).isEqualTo(academies.size()); @@ -125,4 +126,48 @@ void findAcademiesByName_academyName_relatedAcademies() { } } + @Test + @DisplayName("중심 위치 반경 이내에 있는 학원 중에서 교육비가 최소와 최대 사이에 있고 선택한 학원 분류 분야에 해당하는 학원들을 반환한다.") + void filterAcademy_BetweenEducationFeeAndLocationAndInExpertise() { + //Given + lessonRepository.deleteAll(); + reviewCountRepository.deleteAll(); + academyRepository.deleteAll(); + + List academies = AcademyFixture.randomAcademiesWithinDistance(LATITUDE, LONGITUDE); + Long minFee = 10000L; + Long maxFee = 1000000L; + + for (Academy academy : academies) { + Academy savedAcademy = academyRepository.save(academy); + savedAcademy.changeEducationFee(generateRandomAmount(minFee,maxFee)); + + lessonRepository.save(AcademyFixture.lessonForSunganm(savedAcademy)); + reviewCountRepository.save(AcademyFixture.reviewCountDefault(savedAcademy)); + } + AcademyFilterParam academyFilterParam = AcademyFixture.academyFilterParam(LATITUDE, LONGITUDE, minFee, maxFee); + + //When + AcademyFilterResults academyFilterResults = academyService.filterAcademies(academyFilterParam); + + //Then + for (AcademyFilterResult academyFilterResult : academyFilterResults.academyFilterResults()) { + Academy filtedAcademy = academyRepository.getById(academyFilterResult.academyId()); + + assertThat(filtedAcademy.getMaxEducationFee()). + isGreaterThanOrEqualTo(minFee) + .isLessThanOrEqualTo(maxFee); + assertThat(academyFilterParam.areaOfExpertises()).containsExactlyInAnyOrder(academyFilterResult.areaOfExpertise()); + } + } + + private long generateRandomAmount(long min, long max) { + if (min >= max) { + throw new IllegalArgumentException("Min value must be less than max value"); + } + + Random random = new Random(); + return min + random.nextInt((int) (max - min + 1)); + } + } diff --git a/src/test/java/org/guzzing/studayserver/testutil/fixture/AcademyFixture.java b/src/test/java/org/guzzing/studayserver/testutil/fixture/AcademyFixture.java deleted file mode 100644 index 8d75a669..00000000 --- a/src/test/java/org/guzzing/studayserver/testutil/fixture/AcademyFixture.java +++ /dev/null @@ -1,43 +0,0 @@ -package org.guzzing.studayserver.testutil.fixture; - -import java.util.List; -import org.guzzing.studayserver.domain.academy.model.Academy; -import org.guzzing.studayserver.domain.academy.model.Lesson; -import org.guzzing.studayserver.domain.academy.model.ReviewCount; -import org.guzzing.studayserver.domain.academy.model.vo.Address; -import org.guzzing.studayserver.domain.academy.model.vo.Location; -import org.guzzing.studayserver.domain.academy.model.vo.academyinfo.AcademyInfo; -import org.guzzing.studayserver.domain.academy.model.vo.academyinfo.ShuttleAvailability; - -public class AcademyFixture { - - public static List academyInfos() { - return List.of( - AcademyInfo.of("유원우 코딩학원", "000-0000-0000", ShuttleAvailability.AVAILABLE.name(), "예능(대)"), - AcademyInfo.of("박세영 코딩학원", "000-0000-0000", ShuttleAvailability.AVAILABLE.name(), "예능(대)"), - AcademyInfo.of("김별 코딩학원", "000-0000-0000", ShuttleAvailability.AVAILABLE.name(), "예능(대)"), - AcademyInfo.of("김희석보스 코딩학원", "000-0000-0000", ShuttleAvailability.AVAILABLE.name(), "예능(대)") - ); - } - - public static Academy academySuwon() { - return Academy.of(AcademyFixture.academyInfos().get(0), Address.of("경기도 분당구 수원시 망포동"), Location.of(35, 127)); - } - - public static Academy academySungnam() { - return Academy.of(AcademyFixture.academyInfos().get(1), Address.of("경기도 분당구 성남시 망포동"), Location.of(35, 127)); - } - - public static Lesson lessonForSuwon(Academy academy) { - return Lesson.of(academy, "자바와 객체지향", "자바와 객체지향으로 떠나자", "20", "1개월", "100000"); - } - - public static Lesson lessonForSunganm(Academy academy) { - return Lesson.of(academy, "DB에 대해서", "인덱스란 뭘까", "20", "1개월", "100000"); - } - - public static ReviewCount reviewCountDefault(Academy academy) { - return ReviewCount.makeDefaultReviewCount(academy); - } - -} diff --git a/src/test/java/org/guzzing/studayserver/testutil/fixture/academy/AcademyFixture.java b/src/test/java/org/guzzing/studayserver/testutil/fixture/academy/AcademyFixture.java index 24b15867..88887a59 100644 --- a/src/test/java/org/guzzing/studayserver/testutil/fixture/academy/AcademyFixture.java +++ b/src/test/java/org/guzzing/studayserver/testutil/fixture/academy/AcademyFixture.java @@ -8,6 +8,7 @@ import org.guzzing.studayserver.domain.academy.model.vo.academyinfo.AcademyInfo; import org.guzzing.studayserver.domain.academy.model.vo.academyinfo.ShuttleAvailability; import org.guzzing.studayserver.domain.academy.service.dto.param.AcademiesByLocationParam; +import org.guzzing.studayserver.domain.academy.service.dto.param.AcademyFilterParam; import org.locationtech.jts.geom.Point; import java.util.ArrayList; @@ -69,4 +70,8 @@ public static AcademiesByLocationParam academiesByLocationParam(double latitude, return AcademiesByLocationParam.of(latitude, longitude); } + public static AcademyFilterParam academyFilterParam(Double latitude, Double longitude,Long desiredMinAmount, Long desiredMaxAmount) { + return new AcademyFilterParam(latitude,longitude,List.of("예능(대)"),desiredMinAmount, desiredMaxAmount); + } + } From 5c18131a2091d14228893b64fce6b80f977a7976 Mon Sep 17 00:00:00 2001 From: byeolhaha Date: Wed, 8 Nov 2023 18:43:24 +0900 Subject: [PATCH 08/21] =?UTF-8?q?Feat=20:=20=ED=95=99=EC=9B=90=20=EA=B2=80?= =?UTF-8?q?=EC=83=89=20=ED=95=84=ED=84=B0=20controller=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../academy/controller/AcademyController.java | 12 ++++++ .../dto/request/AcademyFilterRequest.java | 39 +++++++++++++++++++ .../dto/response/AcademyFilterResponse.java | 27 +++++++++++++ .../dto/response/AcademyFilterResponses.java | 19 +++++++++ 4 files changed, 97 insertions(+) create mode 100644 src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/request/AcademyFilterRequest.java create mode 100644 src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/response/AcademyFilterResponse.java create mode 100644 src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/response/AcademyFilterResponses.java diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/controller/AcademyController.java b/src/main/java/org/guzzing/studayserver/domain/academy/controller/AcademyController.java index d65bbe8a..ae869b9e 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/controller/AcademyController.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/controller/AcademyController.java @@ -3,12 +3,15 @@ import jakarta.validation.Valid; import org.guzzing.studayserver.domain.academy.controller.dto.request.AcademiesByLocationRequest; import org.guzzing.studayserver.domain.academy.controller.dto.request.AcademiesByNameRequest; +import org.guzzing.studayserver.domain.academy.controller.dto.request.AcademyFilterRequest; import org.guzzing.studayserver.domain.academy.controller.dto.response.AcademiesByLocationResponses; import org.guzzing.studayserver.domain.academy.controller.dto.response.AcademiesByNameResponses; +import org.guzzing.studayserver.domain.academy.controller.dto.response.AcademyFilterResponses; import org.guzzing.studayserver.domain.academy.controller.dto.response.AcademyGetResponse; import org.guzzing.studayserver.domain.academy.service.AcademyService; import org.guzzing.studayserver.domain.academy.service.dto.result.AcademiesByLocationResults; import org.guzzing.studayserver.domain.academy.service.dto.result.AcademiesByNameResults; +import org.guzzing.studayserver.domain.academy.service.dto.result.AcademyFilterResults; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; @@ -53,4 +56,13 @@ public ResponseEntity findByName(@ModelAttribute @Vali .body(AcademiesByNameResponses.from(academiesByNameResults)); } + @GetMapping( + path = "/filter", + produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity filterAcademies(@ModelAttribute @Valid AcademyFilterRequest request) { + AcademyFilterResults academyFilterResults = academyService.filterAcademies(AcademyFilterRequest.to(request)); + + return ResponseEntity.status(HttpStatus.OK) + .body(AcademyFilterResponses.from(academyFilterResults)); + } } diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/request/AcademyFilterRequest.java b/src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/request/AcademyFilterRequest.java new file mode 100644 index 00000000..f1e09745 --- /dev/null +++ b/src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/request/AcademyFilterRequest.java @@ -0,0 +1,39 @@ +package org.guzzing.studayserver.domain.academy.controller.dto.request; + +import jakarta.validation.constraints.DecimalMin; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Positive; +import org.guzzing.studayserver.domain.academy.controller.dto.validation.ValidAreaOfExpertise; +import org.guzzing.studayserver.domain.academy.service.dto.param.AcademyFilterParam; + +import java.util.List; + +public record AcademyFilterRequest( + @NotNull(message = "Latitude cannot be null") + @DecimalMin(value = "-90", message = "Invalid latitude") + Double lat, + + @NotNull(message = "Longitude cannot be null") + @DecimalMin(value = "-180", message = "Invalid longitude") + Double lng, + + + @ValidAreaOfExpertise + List areaOfExpertises, + + @Positive + Long desiredMinAmount, + + @Positive + Long desiredMaxAmount +){ + public static AcademyFilterParam to(AcademyFilterRequest request) { + return new AcademyFilterParam ( + request.lat, + request.lng, + request.areaOfExpertises(), + request.desiredMinAmount, + request.desiredMaxAmount + ); + } +} diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/response/AcademyFilterResponse.java b/src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/response/AcademyFilterResponse.java new file mode 100644 index 00000000..ff988a3e --- /dev/null +++ b/src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/response/AcademyFilterResponse.java @@ -0,0 +1,27 @@ +package org.guzzing.studayserver.domain.academy.controller.dto.response; + +import org.guzzing.studayserver.domain.academy.service.dto.result.AcademyFilterResult; + +public record AcademyFilterResponse( + Long academyId, + String academyName, + String fullAddress, + String contact, + String areaOfExpertise, + Double latitude, + Double longitude, + String shuttleAvailable +) { + public static AcademyFilterResponse from(AcademyFilterResult academyFilterResult) { + return new AcademyFilterResponse( + academyFilterResult.academyId(), + academyFilterResult.academyName(), + academyFilterResult.fullAddress(), + academyFilterResult.contact(), + academyFilterResult.areaOfExpertise(), + academyFilterResult.latitude(), + academyFilterResult.longitude(), + academyFilterResult.shuttleAvailable() + ); + } +} diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/response/AcademyFilterResponses.java b/src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/response/AcademyFilterResponses.java new file mode 100644 index 00000000..c497c2f9 --- /dev/null +++ b/src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/response/AcademyFilterResponses.java @@ -0,0 +1,19 @@ +package org.guzzing.studayserver.domain.academy.controller.dto.response; + +import org.guzzing.studayserver.domain.academy.service.dto.result.AcademyFilterResults; + +import java.util.List; + +public record AcademyFilterResponses( + List academyFilterResponses +) { + public static AcademyFilterResponses from(AcademyFilterResults academyFilterResults) { + return new AcademyFilterResponses( + academyFilterResults.academyFilterResults() + .stream() + .map(academyFilterResult -> AcademyFilterResponse.from(academyFilterResult)) + .toList() + ); + } +} + From 1b89efbf2a0069005b694d8916caf577debbf944 Mon Sep 17 00:00:00 2001 From: byeolhaha Date: Wed, 8 Nov 2023 18:44:04 +0900 Subject: [PATCH 09/21] =?UTF-8?q?Feat:=20=EC=BB=A4=EC=8A=A4=ED=85=80=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D=20=EC=96=B4=EB=85=B8=ED=85=8C=EC=9D=B4?= =?UTF-8?q?=EC=85=98=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/validation/AreaOfExpertise.java | 24 +++++++++++++ .../dto/validation/ValidAreaOfExpertise.java | 18 ++++++++++ .../ValidAreaOfExpertiseValidator.java | 34 +++++++++++++++++++ 3 files changed, 76 insertions(+) create mode 100644 src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/validation/AreaOfExpertise.java create mode 100644 src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/validation/ValidAreaOfExpertise.java create mode 100644 src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/validation/ValidAreaOfExpertiseValidator.java diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/validation/AreaOfExpertise.java b/src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/validation/AreaOfExpertise.java new file mode 100644 index 00000000..0a7db003 --- /dev/null +++ b/src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/validation/AreaOfExpertise.java @@ -0,0 +1,24 @@ +package org.guzzing.studayserver.domain.academy.controller.dto.validation; + +public enum AreaOfExpertise { + + FOREIGN_LANGUAGE("국제화"), + DANCE("기예(대)"), + ETC("기타(대)"), + READING_ROOM("독서실"), + ART_AND_MUSIC("예능(대)"), + HUMANITIES_AND_HISTORY("인문사회(대)"), + TUTORING_SCHOOL("입시.검정 및 보습"), + COMPREHENSIVE("종합(대)"); + + private String kind; + + AreaOfExpertise(String kind) { + this.kind = kind; + } + + public String getAreaOfExpertise() { + return kind; + } + +} diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/validation/ValidAreaOfExpertise.java b/src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/validation/ValidAreaOfExpertise.java new file mode 100644 index 00000000..11c2d278 --- /dev/null +++ b/src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/validation/ValidAreaOfExpertise.java @@ -0,0 +1,18 @@ +package org.guzzing.studayserver.domain.academy.controller.dto.validation; + +import jakarta.validation.Constraint; +import jakarta.validation.Payload; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.FIELD, ElementType.PARAMETER}) +@Retention(RetentionPolicy.RUNTIME) +@Constraint(validatedBy = ValidAreaOfExpertiseValidator.class) +public @interface ValidAreaOfExpertise { + String message() default "유효하지 않은 학원 분야입니다."; + Class[] groups() default {}; + Class[] payload() default {}; +} diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/validation/ValidAreaOfExpertiseValidator.java b/src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/validation/ValidAreaOfExpertiseValidator.java new file mode 100644 index 00000000..efee5d6e --- /dev/null +++ b/src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/validation/ValidAreaOfExpertiseValidator.java @@ -0,0 +1,34 @@ +package org.guzzing.studayserver.domain.academy.controller.dto.validation; + +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; + +import java.util.Arrays; +import java.util.List; + +public class ValidAreaOfExpertiseValidator implements ConstraintValidator> { + + @Override + public void initialize(ValidAreaOfExpertise constraintAnnotation) { + } + + @Override + public boolean isValid(List value, ConstraintValidatorContext context) { + if (value == null || value.isEmpty()) { + return false; + } + + for (String areaOfExpertise : value) { + if (!isValidAreaOfExpertise(areaOfExpertise)) { + return false; + } + } + + return true; + } + + private boolean isValidAreaOfExpertise(String areaOfExpertise) { + return Arrays.stream(AreaOfExpertise.values()) + .anyMatch(enumValue -> enumValue.getAreaOfExpertise().equals(areaOfExpertise)); + } +} From 8ee1dd9a943c4fe47aa420aee2ce3016f1370074 Mon Sep 17 00:00:00 2001 From: byeolhaha Date: Wed, 8 Nov 2023 20:57:29 +0900 Subject: [PATCH 10/21] =?UTF-8?q?Feat=20:=20AccessService=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84(=EC=A1=B4=EC=9E=AC=ED=95=98=EB=8A=94=20Academy?= =?UTF-8?q?=EC=9D=B8=EC=A7=80=20=ED=99=95=EC=9D=B8=20=EC=BF=BC=EB=A6=AC=20?= =?UTF-8?q?=EB=B0=8F=20=EA=B5=90=EC=9C=A1=EB=B9=84=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?=EC=BF=BC=EB=A6=AC)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/academy/repository/AcademyFee.java | 10 ++++++++++ .../repository/academy/AcademyJpaRepository.java | 8 ++++++++ .../academy/repository/academy/AcademyRepository.java | 10 ++++++---- .../academy/service/AcademyAccessServiceImpl.java | 11 +++++++++-- .../like/service/dto/response/AcademyFeeInfo.java | 9 +++++++++ 5 files changed, 42 insertions(+), 6 deletions(-) create mode 100644 src/main/java/org/guzzing/studayserver/domain/academy/repository/AcademyFee.java diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/repository/AcademyFee.java b/src/main/java/org/guzzing/studayserver/domain/academy/repository/AcademyFee.java new file mode 100644 index 00000000..349e3f68 --- /dev/null +++ b/src/main/java/org/guzzing/studayserver/domain/academy/repository/AcademyFee.java @@ -0,0 +1,10 @@ +package org.guzzing.studayserver.domain.academy.repository; + +public interface AcademyFee { + + Long getMaxEducationFee(); + + String getAcademyName(); + + +} diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyJpaRepository.java b/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyJpaRepository.java index 343f67cc..33ec3fdc 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyJpaRepository.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyJpaRepository.java @@ -3,6 +3,7 @@ import jakarta.persistence.EntityNotFoundException; import org.guzzing.studayserver.domain.academy.model.Academy; import org.guzzing.studayserver.domain.academy.repository.AcademiesByName; +import org.guzzing.studayserver.domain.academy.repository.AcademyFee; import org.guzzing.studayserver.global.error.response.ErrorCode; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; @@ -26,4 +27,11 @@ default Academy getById(Long academyId) { nativeQuery = true) Slice findAcademiesByName(@Param("academyName") String academyName, Pageable pageable); + + @Query("SELECT a.maxEducationFee, a.academyInfo.academyName FROM Academy AS a WHERE a.id = :academyId") + AcademyFee findAcademyFeeInfo(@Param("academyId") Long academyId); + + @Query("SELECT CASE WHEN EXISTS (SELECT 1 FROM Academy a WHERE a.id = :academyId) THEN true ELSE false END") + boolean existsByAcademyId(@Param("academyId") Long academyId); + } diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyRepository.java b/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyRepository.java index f894fc49..ff7c7ac8 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyRepository.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyRepository.java @@ -2,12 +2,10 @@ import java.util.List; import org.guzzing.studayserver.domain.academy.model.Academy; -import org.guzzing.studayserver.domain.academy.repository.AcademiesByLocation; -import org.guzzing.studayserver.domain.academy.repository.AcademiesByName; -import org.guzzing.studayserver.domain.academy.repository.AcademyByFiltering; -import org.guzzing.studayserver.domain.academy.repository.AcademyFilterCondition; +import org.guzzing.studayserver.domain.academy.repository.*; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; @Repository @@ -22,4 +20,8 @@ public interface AcademyRepository extends AcademyJpaRepository, AcademyQueryRep List findAcademiesByLocation(String pointFormat); List filterAcademies(AcademyFilterCondition academyFilterCondition); + + AcademyFee findAcademyFeeInfo(Long academyId); + + boolean existsByAcademyId(Long academyId); } diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/service/AcademyAccessServiceImpl.java b/src/main/java/org/guzzing/studayserver/domain/academy/service/AcademyAccessServiceImpl.java index d11d1ddd..56660f51 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/service/AcademyAccessServiceImpl.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/service/AcademyAccessServiceImpl.java @@ -1,5 +1,6 @@ package org.guzzing.studayserver.domain.academy.service; +import org.guzzing.studayserver.domain.academy.repository.academy.AcademyRepository; import org.guzzing.studayserver.domain.like.service.dto.response.AcademyFeeInfo; import org.springframework.stereotype.Service; @@ -7,14 +8,20 @@ public class AcademyAccessServiceImpl implements AcademyAccessService { + private final AcademyRepository academyRepository; + + public AcademyAccessServiceImpl(AcademyRepository academyRepository) { + this.academyRepository = academyRepository; + } + @Override public AcademyFeeInfo findAcademyFeeInfo(Long academyId) { - return null; + return AcademyFeeInfo.to(academyRepository.findAcademyFeeInfo(academyId)); } @Override public boolean existsAcademy(Long academyId) { - return false; + return academyRepository.existsByAcademyId(academyId); } } diff --git a/src/main/java/org/guzzing/studayserver/domain/like/service/dto/response/AcademyFeeInfo.java b/src/main/java/org/guzzing/studayserver/domain/like/service/dto/response/AcademyFeeInfo.java index eb1421d5..3e4e6afb 100644 --- a/src/main/java/org/guzzing/studayserver/domain/like/service/dto/response/AcademyFeeInfo.java +++ b/src/main/java/org/guzzing/studayserver/domain/like/service/dto/response/AcademyFeeInfo.java @@ -1,8 +1,17 @@ package org.guzzing.studayserver.domain.like.service.dto.response; +import org.guzzing.studayserver.domain.academy.repository.AcademyFee; + public record AcademyFeeInfo( String academyName, long expectedFee ) { + public static AcademyFeeInfo to(AcademyFee academyFee) { + return new AcademyFeeInfo( + academyFee.getAcademyName(), + academyFee.getMaxEducationFee() + ); + } + } From 4f8680b0cb2f473bb5833e080fa0211c85185827 Mon Sep 17 00:00:00 2001 From: byeolhaha Date: Thu, 9 Nov 2023 13:20:20 +0900 Subject: [PATCH 11/21] =?UTF-8?q?Refactor=20:=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=EC=9E=90=EA=B0=80=20=EC=A2=8B=EC=95=84=EC=9A=94=EB=A5=BC=20?= =?UTF-8?q?=ED=95=9C=20=ED=95=99=EC=9B=90=EC=9D=B8=EC=A7=80=20true,=20fals?= =?UTF-8?q?e=EB=A1=9C=20=EC=A1=B0=ED=9A=8C=EB=8F=84=EB=A1=9D=20=EC=BF=BC?= =?UTF-8?q?=EB=A6=AC=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../academy/AcademyQueryRepository.java | 4 +-- .../academy/AcademyQueryRepositoryImpl.java | 33 ++++++++++++------- .../repository/academy/AcademyRepository.java | 4 +-- 3 files changed, 26 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyQueryRepository.java b/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyQueryRepository.java index 36c4ca4c..65cb970c 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyQueryRepository.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyQueryRepository.java @@ -7,7 +7,7 @@ public interface AcademyQueryRepository { - List findAcademiesByLocation(String pointFormat); + List findAcademiesByLocation(String pointFormat, Long memberId); - List filterAcademies(AcademyFilterCondition academyFilterCondition); + List filterAcademies(AcademyFilterCondition academyFilterCondition, Long memberId); } diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyQueryRepositoryImpl.java b/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyQueryRepositoryImpl.java index 53e2eb7a..14b8b42c 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyQueryRepositoryImpl.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyQueryRepositoryImpl.java @@ -9,8 +9,6 @@ import org.hibernate.transform.ResultTransformer; import org.hibernate.type.StandardBasicTypes; -import java.util.List; - public class AcademyQueryRepositoryImpl implements AcademyQueryRepository { private final EntityManager em; @@ -19,12 +17,16 @@ public AcademyQueryRepositoryImpl(EntityManager em) { this.em = em; } - public List findAcademiesByLocation(String pointFormat) { + public List findAcademiesByLocation(String pointFormat, Long memberId) { Query query = em.createNativeQuery( "SELECT a.id AS academyId, a.academy_name AS academyName, a.phone_number AS phoneNumber, a.full_address AS fullAddress," + - " a.area_of_expertise AS areaOfExpertise, a.latitude AS latitude , a.longitude AS longitude FROM academies AS a " + - "WHERE MBRContains(ST_LINESTRINGFROMTEXT(" + pointFormat + ", a.point)=1"); + " a.area_of_expertise AS areaOfExpertise, a.latitude AS latitude , a.longitude AS longitude, a.shuttle AS shuttleAvailable," + + " (CASE WHEN r.academy_id IS NOT NULL THEN true ELSE false END) AS isLiked " + + " FROM academies AS a" + + " LEFT JOIN reviews AS r" + + " ON a.id = r.academy_id AND r.member_id = "+memberId + + " WHERE MBRContains(ST_LINESTRINGFROMTEXT(" + pointFormat + ", a.point)=1"); List academies = query.unwrap(org.hibernate.query.NativeQuery.class) .addScalar("academyId", StandardBasicTypes.LONG) @@ -34,6 +36,8 @@ public List findAcademiesByLocation(String pointFormat) { .addScalar("areaOfExpertise", StandardBasicTypes.STRING) .addScalar("latitude", StandardBasicTypes.DOUBLE) .addScalar("longitude", StandardBasicTypes.DOUBLE) + .addScalar("shuttleAvailable", StandardBasicTypes.STRING) + .addScalar("isLiked", StandardBasicTypes.BOOLEAN) .setResultTransformer( new ResultTransformer() { @Override @@ -45,7 +49,9 @@ public Object transformTuple(Object[] tuple, String[] aliases) { (String) tuple[3], (String) tuple[4], (Double) tuple[5], - (Double) tuple[6] + (Double) tuple[6], + (String) tuple[7], + (boolean) tuple[8] ); } @@ -60,11 +66,14 @@ public List transformList(List collection) { return academies; } - public List filterAcademies(AcademyFilterCondition academyFilterCondition) { + public List filterAcademies(AcademyFilterCondition academyFilterCondition, Long memberId) { String nativeQuery = "SELECT a.id AS academyId, a.academy_name AS academyName, a.full_address AS fullAddress, " + - "a.phone_number AS phoneNumber, a.area_of_expertise AS areaOfExpertise, a.latitude, a.longitude, a.shuttle AS shuttleAvailable " + + "a.phone_number AS phoneNumber, a.area_of_expertise AS areaOfExpertise, a.latitude, a.longitude, a.shuttle AS shuttleAvailable, " + + "(CASE WHEN r.academy_id IS NOT NULL THEN true ELSE false END) AS isLiked " + "FROM academies AS a " + - "WHERE MBRContains(ST_LINESTRINGFROMTEXT(" + academyFilterCondition.pointFormat() + ", a.point)=1 "; + "LEFT JOIN reviews AS r " + + "ON a.id = r.academy_id AND r.member_id = "+memberId + + " WHERE MBRContains(ST_LINESTRINGFROMTEXT(" + academyFilterCondition.pointFormat() + ", a.point)=1 "; if (academyFilterCondition.areaOfExpertises() != null && !academyFilterCondition.areaOfExpertises().isEmpty()) { nativeQuery += " AND area_of_expertise IN "+ academyFilterCondition.areaOfExpertises(); @@ -84,7 +93,8 @@ public List filterAcademies(AcademyFilterCondition academyFi .addScalar("areaOfExpertise", StandardBasicTypes.STRING) .addScalar("latitude", StandardBasicTypes.DOUBLE) .addScalar("longitude", StandardBasicTypes.DOUBLE) - .addScalar("shuttleAvailable", StandardBasicTypes.STRING) // 추가된 부분 + .addScalar("shuttleAvailable", StandardBasicTypes.STRING) + .addScalar("isLiked", StandardBasicTypes.BOOLEAN) .setResultTransformer( new ResultTransformer() { @Override @@ -97,7 +107,8 @@ public Object transformTuple(Object[] tuple, String[] aliases) { (String) tuple[4], (Double) tuple[5], (Double) tuple[6], - (String) tuple[7] // shuttleAvailable 추가된 부분 + (String) tuple[7], + (boolean) tuple[8] ); } diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyRepository.java b/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyRepository.java index ff7c7ac8..b19af5ad 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyRepository.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyRepository.java @@ -17,9 +17,9 @@ public interface AcademyRepository extends AcademyJpaRepository, AcademyQueryRep Slice findAcademiesByName(String academyName, Pageable pageable); - List findAcademiesByLocation(String pointFormat); + List findAcademiesByLocation(String pointFormat, Long memberId); - List filterAcademies(AcademyFilterCondition academyFilterCondition); + List filterAcademies(AcademyFilterCondition academyFilterCondition, Long memberId); AcademyFee findAcademyFeeInfo(Long academyId); From 09536d837855a70666175b812b7604b561f2a6ec Mon Sep 17 00:00:00 2001 From: byeolhaha Date: Thu, 9 Nov 2023 13:22:20 +0900 Subject: [PATCH 12/21] =?UTF-8?q?Refactor=20:=20=EC=A2=8B=EC=95=84?= =?UTF-8?q?=EC=9A=94=EB=A5=BC=20=ED=95=9C=20=ED=95=99=EC=9B=90=EC=9D=B8?= =?UTF-8?q?=EC=A7=80=20=ED=91=9C=EC=8B=9C=ED=95=98=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EC=84=9C=EB=B9=84=EC=8A=A4=20=EB=B0=8F=20=EA=B4=80=EB=A0=A8=20?= =?UTF-8?q?dto=20=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../academy/service/AcademyService.java | 55 +++++++++++++------ .../dto/result/AcademiesByLocationResult.java | 8 ++- .../dto/result/AcademyFilterResult.java | 6 +- .../service/dto/result/AcademyGetResult.java | 14 +++-- 4 files changed, 55 insertions(+), 28 deletions(-) diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/service/AcademyService.java b/src/main/java/org/guzzing/studayserver/domain/academy/service/AcademyService.java index 3d72262a..0a2027c6 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/service/AcademyService.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/service/AcademyService.java @@ -1,9 +1,5 @@ package org.guzzing.studayserver.domain.academy.service; -import java.util.List; -import org.guzzing.studayserver.domain.academy.model.Academy; -import org.guzzing.studayserver.domain.academy.model.Lesson; -import org.guzzing.studayserver.domain.academy.model.ReviewCount; import org.guzzing.studayserver.domain.academy.model.vo.Location; import org.guzzing.studayserver.domain.academy.repository.academy.AcademyRepository; @@ -16,6 +12,7 @@ import org.guzzing.studayserver.domain.academy.util.GeometryUtil; import org.guzzing.studayserver.domain.academy.util.SqlFormatter; import org.guzzing.studayserver.domain.academy.util.model.Direction; +import org.guzzing.studayserver.domain.like.service.LikeAccessService; import org.springframework.data.domain.PageRequest; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -33,24 +30,28 @@ public class AcademyService { private final ReviewCountRepository reviewCountRepository; + private final LikeAccessService likeAccessService; + public AcademyService(AcademyRepository academyRepository, LessonRepository lessonRepository, - ReviewCountRepository reviewCountRepository) { + ReviewCountRepository reviewCountRepository, LikeAccessService likeAccessService) { this.academyRepository = academyRepository; this.lessonRepository = lessonRepository; this.reviewCountRepository = reviewCountRepository; + this.likeAccessService = likeAccessService; } + //캐시 이용하기(지금 상황에서는 백오피스가 없기 때문에 3달에 한 번 업데이트 되기 때문에 가능) @Transactional(readOnly = true) - public AcademyGetResult getAcademy(Long academyId) { - Academy academy = academyRepository.getById(academyId); - List lessons = lessonRepository.findAllByAcademyId(academyId); - ReviewCount reviewCount = reviewCountRepository.getByAcademyId(academyId); - - return AcademyGetResult.from(academy, lessons, reviewCount); + public AcademyGetResult getAcademy(Long academyId, Long memberId) { + return AcademyGetResult.from( + academyRepository.getById(academyId), + lessonRepository.findAllByAcademyId(academyId), + reviewCountRepository.getByAcademyId(academyId), + isLiked(academyId, memberId)); } - - public AcademiesByLocationResults findAcademiesByLocation(AcademiesByLocationParam param) { + @Transactional(readOnly = true) + public AcademiesByLocationResults findAcademiesByLocation(AcademiesByLocationParam param, Long memberId) { Location northEast = calculateLocationWithinRadiusInDirection( param.baseLatitude(), param.baseLongitude(), @@ -61,7 +62,7 @@ public AcademiesByLocationResults findAcademiesByLocation(AcademiesByLocationPar Direction.SOUTHWEST); String diagonal = SqlFormatter.makeDiagonalByLineString(northEast, southWest); - return AcademiesByLocationResults.to(academyRepository.findAcademiesByLocation(diagonal)); + return AcademiesByLocationResults.to(academyRepository.findAcademiesByLocation(diagonal, memberId)); } @Transactional(readOnly = true) @@ -74,7 +75,7 @@ public AcademiesByNameResults findAcademiesByName(AcademiesByNameParam param) { } @Transactional(readOnly = true) - public AcademyFilterResults filterAcademies(AcademyFilterParam param) { + public AcademyFilterResults filterAcademies(AcademyFilterParam param, Long memberId) { Location northEast = calculateLocationWithinRadiusInDirection( param.baseLatitude(), param.baseLongitude(), @@ -85,11 +86,29 @@ public AcademyFilterResults filterAcademies(AcademyFilterParam param) { Direction.SOUTHWEST); String diagonal = SqlFormatter.makeDiagonalByLineString(northEast, southWest); - return AcademyFilterResults.from(academyRepository.filterAcademies(AcademyFilterParam.to(param, diagonal))); + return AcademyFilterResults.from( + academyRepository.filterAcademies( + AcademyFilterParam.to( + param, + diagonal), + memberId) + ); + } + + private Location calculateLocationWithinRadiusInDirection( + double latitude, + double longitude, + Direction direction) { + + return GeometryUtil.calculateLocationWithinRadiusInDirection( + latitude, + longitude, + direction.getBearing(), + DISTANCE); } - private Location calculateLocationWithinRadiusInDirection(double latitude, double longitude, Direction direction) { - return GeometryUtil.calculateLocationWithinRadiusInDirection(latitude, longitude, direction.getBearing(), DISTANCE); + private boolean isLiked(Long academyId, Long memberId) { + return likeAccessService.isLiked(academyId, memberId); } } diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/result/AcademiesByLocationResult.java b/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/result/AcademiesByLocationResult.java index 5fffe8b0..05e4231f 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/result/AcademiesByLocationResult.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/result/AcademiesByLocationResult.java @@ -9,7 +9,9 @@ public record AcademiesByLocationResult( String contact, String areaOfExpertise, Double latitude, - Double longitude + Double longitude, + String shuttleAvailable, + boolean isLiked ) { public static AcademiesByLocationResult from(AcademiesByLocation academiesByLocation) { @@ -20,7 +22,9 @@ public static AcademiesByLocationResult from(AcademiesByLocation academiesByLoca academiesByLocation.phoneNumber(), academiesByLocation.areaOfExpertise(), academiesByLocation.latitude(), - academiesByLocation.longitude() + academiesByLocation.longitude(), + academiesByLocation.shuttleAvailable(), + academiesByLocation.isLiked() ); } } diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/result/AcademyFilterResult.java b/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/result/AcademyFilterResult.java index f1dc72e6..278a87c5 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/result/AcademyFilterResult.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/result/AcademyFilterResult.java @@ -10,7 +10,8 @@ public record AcademyFilterResult( String areaOfExpertise, Double latitude, Double longitude, - String shuttleAvailable + String shuttleAvailable, + boolean isLiked ) { public static AcademyFilterResult from(AcademyByFiltering academyByFiltering) { @@ -22,7 +23,8 @@ public static AcademyFilterResult from(AcademyByFiltering academyByFiltering) { academyByFiltering.areaOfExpertise(), academyByFiltering.latitude(), academyByFiltering.longitude(), - academyByFiltering.shuttleAvailable() + academyByFiltering.shuttleAvailable(), + academyByFiltering.isLiked() ); } diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/result/AcademyGetResult.java b/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/result/AcademyGetResult.java index 5cb66317..cf82206f 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/result/AcademyGetResult.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/result/AcademyGetResult.java @@ -14,12 +14,14 @@ public record AcademyGetResult( String updatedDate, String areaOfExpertise, LessonGetResults lessonGetResults, - ReviewPercentGetResult reviewPercentGetResult + ReviewPercentGetResult reviewPercentGetResult, + boolean isLiked ) { - - public static AcademyGetResult from(Academy academy, + public static AcademyGetResult from( + Academy academy, List lessons, - ReviewCount reviewCount) { + ReviewCount reviewCount, + boolean isLiked) { return new AcademyGetResult( academy.getAcademyName(), academy.getContact(), @@ -30,9 +32,9 @@ public static AcademyGetResult from(Academy academy, academy.getAreaOfExpertise(), LessonGetResults.from(lessons), - ReviewPercentGetResult.from(reviewCount) + ReviewPercentGetResult.from(reviewCount), + isLiked ); } - } From 4e629cfc7696a9a4a983bf1a54ea47e51efae361 Mon Sep 17 00:00:00 2001 From: byeolhaha Date: Thu, 9 Nov 2023 13:23:22 +0900 Subject: [PATCH 13/21] =?UTF-8?q?Test=20:=20=ED=9A=8C=EC=9B=90=20=EC=95=84?= =?UTF-8?q?=EC=9D=B4=EB=94=94=EA=B0=80=20=EC=A1=B0=ED=9A=8C=EB=90=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../academy/service/AcademyServiceTest.java | 20 ++++++++++++++++--- .../fixture/member/MemberFixture.java | 12 +++++++++++ 2 files changed, 29 insertions(+), 3 deletions(-) create mode 100644 src/test/java/org/guzzing/studayserver/testutil/fixture/member/MemberFixture.java diff --git a/src/test/java/org/guzzing/studayserver/domain/academy/service/AcademyServiceTest.java b/src/test/java/org/guzzing/studayserver/domain/academy/service/AcademyServiceTest.java index 5b284de7..ad224b1a 100644 --- a/src/test/java/org/guzzing/studayserver/domain/academy/service/AcademyServiceTest.java +++ b/src/test/java/org/guzzing/studayserver/domain/academy/service/AcademyServiceTest.java @@ -11,7 +11,10 @@ import org.guzzing.studayserver.domain.academy.service.dto.param.AcademiesByNameParam; import org.guzzing.studayserver.domain.academy.service.dto.param.AcademyFilterParam; import org.guzzing.studayserver.domain.academy.service.dto.result.*; +import org.guzzing.studayserver.domain.member.model.Member; +import org.guzzing.studayserver.domain.member.repository.MemberRepository; import org.guzzing.studayserver.testutil.fixture.academy.AcademyFixture; +import org.guzzing.studayserver.testutil.fixture.member.MemberFixture; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -45,6 +48,11 @@ class AcademyServiceTest { @Autowired private ReviewCountRepository reviewCountRepository; + @Autowired + private MemberRepository memberRepository; + + private Member savedMember; + private Academy savedAcademyAboutSungnam; private Lesson savedALessonAboutSungnam; @@ -53,6 +61,10 @@ class AcademyServiceTest { @BeforeEach void setUp() { + + Member member = MemberFixture.member(); + savedMember = memberRepository.save(member); + Academy academyAboutSungnam = AcademyFixture.academySungnam(); academyAboutSungnam.changeEducationFee(100000L); savedAcademyAboutSungnam = academyRepository.save(academyAboutSungnam); @@ -68,7 +80,7 @@ void setUp() { @DisplayName("학원 ID로 학원 정보를 조회할 때 학원 정보, 수업 정보, 리뷰를 확인할 수 있다.") void getAcademy_academyId_reviewsAndLessons() { //When - AcademyGetResult academyGetResult = academyService.getAcademy(savedAcademyAboutSungnam.getId()); + AcademyGetResult academyGetResult = academyService.getAcademy(savedAcademyAboutSungnam.getId(),savedMember.getId()); //Then assertThat(academyGetResult.academyName()).isEqualTo(savedAcademyAboutSungnam.getAcademyName()); @@ -100,7 +112,9 @@ void findAcademiesByLocation_academiesWithinDistance_equalsSize() { } //When - AcademiesByLocationResults academiesByLocations = academyService.findAcademiesByLocation(AcademyFixture.academiesByLocationParam(LATITUDE, LONGITUDE)); + AcademiesByLocationResults academiesByLocations = academyService.findAcademiesByLocation( + AcademyFixture.academiesByLocationParam(LATITUDE, LONGITUDE), + savedMember.getId()); //Then assertThat(academiesByLocations.academiesByLocationResults().size()).isEqualTo(academies.size()); @@ -151,7 +165,7 @@ void filterAcademy_BetweenEducationFeeAndLocationAndInExpertise() { AcademyFilterParam academyFilterParam = AcademyFixture.academyFilterParam(LATITUDE, LONGITUDE, minFee, maxFee); //When - AcademyFilterResults academyFilterResults = academyService.filterAcademies(academyFilterParam); + AcademyFilterResults academyFilterResults = academyService.filterAcademies(academyFilterParam, savedMember.getId()); //Then for (AcademyFilterResult academyFilterResult : academyFilterResults.academyFilterResults()) { diff --git a/src/test/java/org/guzzing/studayserver/testutil/fixture/member/MemberFixture.java b/src/test/java/org/guzzing/studayserver/testutil/fixture/member/MemberFixture.java new file mode 100644 index 00000000..2aabb7d1 --- /dev/null +++ b/src/test/java/org/guzzing/studayserver/testutil/fixture/member/MemberFixture.java @@ -0,0 +1,12 @@ +package org.guzzing.studayserver.testutil.fixture.member; + +import org.guzzing.studayserver.domain.child.model.NickName; +import org.guzzing.studayserver.domain.member.model.Member; +import org.guzzing.studayserver.domain.member.model.vo.MemberProvider; +import org.guzzing.studayserver.domain.member.model.vo.RoleType; + +public class MemberFixture { + public static Member member() { + return Member.of(new NickName("나는왕이다"), "12345678", MemberProvider.KAKAO, RoleType.USER); + } +} From da619a4168af6f96597d6066f6963f5fdfbc0ef7 Mon Sep 17 00:00:00 2001 From: byeolhaha Date: Thu, 9 Nov 2023 13:25:10 +0900 Subject: [PATCH 14/21] =?UTF-8?q?Refactor=20:=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=EC=9E=90=EA=B0=80=20=ED=95=99=EC=9B=90=EC=97=90=20=EB=8C=80?= =?UTF-8?q?=ED=95=B4=20=EC=A2=8B=EC=95=84=EC=9A=94=EB=A5=BC=20=ED=96=88?= =?UTF-8?q?=EB=8A=94=EC=A7=80=20=ED=99=95=EC=9D=B8=ED=95=98=EA=B8=B0=20?= =?UTF-8?q?=EC=9C=84=ED=95=B4=EC=84=9C=20'@MemberId'=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../academy/controller/AcademyController.java | 27 ++++++++++++++----- .../response/AcademiesByLocationResponse.java | 9 ++++--- .../dto/response/AcademyGetResponse.java | 7 ++--- 3 files changed, 30 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/controller/AcademyController.java b/src/main/java/org/guzzing/studayserver/domain/academy/controller/AcademyController.java index 96968f94..423ecc9a 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/controller/AcademyController.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/controller/AcademyController.java @@ -12,6 +12,7 @@ import org.guzzing.studayserver.domain.academy.service.dto.result.AcademiesByLocationResults; import org.guzzing.studayserver.domain.academy.service.dto.result.AcademiesByNameResults; import org.guzzing.studayserver.domain.academy.service.dto.result.AcademyFilterResults; +import org.guzzing.studayserver.domain.auth.memberId.MemberId; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; @@ -34,18 +35,23 @@ public AcademyController(AcademyService academyService) { @GetMapping( path = "/{academyId}", produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity getAcademy(@PathVariable Long academyId) { + public ResponseEntity getAcademy( + @PathVariable Long academyId, + @MemberId Long memberId + ) { return ResponseEntity.status(HttpStatus.OK) - .body(AcademyGetResponse.from(academyService.getAcademy(academyId))); + .body(AcademyGetResponse.from(academyService.getAcademy(academyId, memberId))); } @GetMapping( path = "/complexes", produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity findByLocation( - @ModelAttribute @Valid AcademiesByLocationRequest request) { + @ModelAttribute @Valid AcademiesByLocationRequest request, + @MemberId Long memberId + ) { AcademiesByLocationResults academiesByLocation = - academyService.findAcademiesByLocation(AcademiesByLocationRequest.to(request)); + academyService.findAcademiesByLocation(AcademiesByLocationRequest.to(request),memberId); return ResponseEntity.status(HttpStatus.OK) .body(AcademiesByLocationResponses.from(academiesByLocation)); @@ -54,7 +60,9 @@ public ResponseEntity findByLocation( @GetMapping( path = "/search", produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity findByName(@ModelAttribute @Valid AcademiesByNameRequest request) { + public ResponseEntity findByName( + @ModelAttribute @Valid AcademiesByNameRequest request + ) { AcademiesByNameResults academiesByNameResults = academyService.findAcademiesByName( AcademiesByNameRequest.to(request)); @@ -65,8 +73,13 @@ public ResponseEntity findByName(@ModelAttribute @Vali @GetMapping( path = "/filter", produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity filterAcademies(@ModelAttribute @Valid AcademyFilterRequest request) { - AcademyFilterResults academyFilterResults = academyService.filterAcademies(AcademyFilterRequest.to(request)); + public ResponseEntity filterAcademies( + @ModelAttribute @Valid AcademyFilterRequest request, + @MemberId Long memberId + ) { + AcademyFilterResults academyFilterResults = academyService.filterAcademies( + AcademyFilterRequest.to(request), memberId + ); return ResponseEntity.status(HttpStatus.OK) .body(AcademyFilterResponses.from(academyFilterResults)); diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/response/AcademiesByLocationResponse.java b/src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/response/AcademiesByLocationResponse.java index ad540e43..6c605f76 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/response/AcademiesByLocationResponse.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/response/AcademiesByLocationResponse.java @@ -9,9 +9,10 @@ public record AcademiesByLocationResponse( String contact, String areaOfExpertise, Double latitude, - Double longitude + Double longitude, + String shuttleAvailable, + boolean isLiked ) { - public static AcademiesByLocationResponse from(AcademiesByLocationResult academiesByLocationResult) { return new AcademiesByLocationResponse( academiesByLocationResult.academyId(), @@ -20,7 +21,9 @@ public static AcademiesByLocationResponse from(AcademiesByLocationResult academi academiesByLocationResult.contact(), academiesByLocationResult.areaOfExpertise(), academiesByLocationResult.latitude(), - academiesByLocationResult.longitude() + academiesByLocationResult.longitude(), + academiesByLocationResult.shuttleAvailable(), + academiesByLocationResult.isLiked() ); } } diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/response/AcademyGetResponse.java b/src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/response/AcademyGetResponse.java index e4b94836..57142293 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/response/AcademyGetResponse.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/response/AcademyGetResponse.java @@ -11,9 +11,9 @@ public record AcademyGetResponse( String updatedDate, String areaOfExpertise, LessonGetResponses lessonGetResponses, - ReviewPercentGetResponse reviewPercentGetResponse + ReviewPercentGetResponse reviewPercentGetResponse, + boolean isLiked ) { - public static AcademyGetResponse from(AcademyGetResult academyGetResult) { return new AcademyGetResponse( academyGetResult.academyName(), @@ -25,7 +25,8 @@ public static AcademyGetResponse from(AcademyGetResult academyGetResult) { academyGetResult.areaOfExpertise(), LessonGetResponses.from(academyGetResult.lessonGetResults()), - ReviewPercentGetResponse.from(academyGetResult.reviewPercentGetResult()) + ReviewPercentGetResponse.from(academyGetResult.reviewPercentGetResult()), + academyGetResult.isLiked() ); } From 79d4a00676dc554e11cd5a3b8d9d60b120a24416 Mon Sep 17 00:00:00 2001 From: byeolhaha Date: Thu, 9 Nov 2023 13:26:03 +0900 Subject: [PATCH 15/21] =?UTF-8?q?Feat=20:=20=EB=A6=AC=EB=B7=B0=20=EB=8F=84?= =?UTF-8?q?=EB=A9=94=EC=9D=B8=EC=97=90=20=EB=8C=80=ED=95=9C=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=EB=A5=BC=20=EC=9C=84=ED=95=B4=EC=84=9C=20AccessServic?= =?UTF-8?q?e=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/like/service/LikeAccessService.java | 6 ++++++ .../domain/like/service/LikeAccessServiceImpl.java | 11 +++++++++++ 2 files changed, 17 insertions(+) create mode 100644 src/main/java/org/guzzing/studayserver/domain/like/service/LikeAccessService.java create mode 100644 src/main/java/org/guzzing/studayserver/domain/like/service/LikeAccessServiceImpl.java diff --git a/src/main/java/org/guzzing/studayserver/domain/like/service/LikeAccessService.java b/src/main/java/org/guzzing/studayserver/domain/like/service/LikeAccessService.java new file mode 100644 index 00000000..a92a728e --- /dev/null +++ b/src/main/java/org/guzzing/studayserver/domain/like/service/LikeAccessService.java @@ -0,0 +1,6 @@ +package org.guzzing.studayserver.domain.like.service; + +public interface LikeAccessService { + + boolean isLiked(Long academyId, Long memberId); +} diff --git a/src/main/java/org/guzzing/studayserver/domain/like/service/LikeAccessServiceImpl.java b/src/main/java/org/guzzing/studayserver/domain/like/service/LikeAccessServiceImpl.java new file mode 100644 index 00000000..e78c4efb --- /dev/null +++ b/src/main/java/org/guzzing/studayserver/domain/like/service/LikeAccessServiceImpl.java @@ -0,0 +1,11 @@ +package org.guzzing.studayserver.domain.like.service; + +import org.springframework.stereotype.Service; + +@Service +public class LikeAccessServiceImpl implements LikeAccessService { + @Override + public boolean isLiked(Long academyId, Long memberId) { + return false; + } +} From 31f850b8f620f7181675d227600af512a67c2ddb Mon Sep 17 00:00:00 2001 From: byeolhaha Date: Thu, 9 Nov 2023 13:27:02 +0900 Subject: [PATCH 16/21] =?UTF-8?q?Refactor=20:=20=EC=A2=8B=EC=95=84?= =?UTF-8?q?=EC=9A=94=20=ED=95=9C=20=EC=97=AC=EB=B6=80=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/academy/repository/AcademiesByLocation.java | 4 +++- .../domain/academy/repository/AcademyByFiltering.java | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/repository/AcademiesByLocation.java b/src/main/java/org/guzzing/studayserver/domain/academy/repository/AcademiesByLocation.java index 9adb0cf9..d68fac58 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/repository/AcademiesByLocation.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/repository/AcademiesByLocation.java @@ -7,7 +7,9 @@ public record AcademiesByLocation( String phoneNumber, String areaOfExpertise, Double latitude, - Double longitude + Double longitude, + String shuttleAvailable, + boolean isLiked ) { } diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/repository/AcademyByFiltering.java b/src/main/java/org/guzzing/studayserver/domain/academy/repository/AcademyByFiltering.java index 0baed333..a4af9556 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/repository/AcademyByFiltering.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/repository/AcademyByFiltering.java @@ -8,6 +8,7 @@ public record AcademyByFiltering( String areaOfExpertise, Double latitude, Double longitude, - String shuttleAvailable + String shuttleAvailable, + boolean isLiked ) { } From df8fe280a2c253d551bce56b7b883bbbd2e04b54 Mon Sep 17 00:00:00 2001 From: byeolhaha Date: Thu, 9 Nov 2023 13:28:01 +0900 Subject: [PATCH 17/21] =?UTF-8?q?Refactor=20:=20=EC=A2=8B=EC=95=84?= =?UTF-8?q?=EC=9A=94=20=EC=97=AC=EB=B6=80=20dto=EC=97=90=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 --- .../controller/dto/response/AcademyFilterResponse.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/response/AcademyFilterResponse.java b/src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/response/AcademyFilterResponse.java index ff988a3e..25f09d1c 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/response/AcademyFilterResponse.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/response/AcademyFilterResponse.java @@ -10,7 +10,8 @@ public record AcademyFilterResponse( String areaOfExpertise, Double latitude, Double longitude, - String shuttleAvailable + String shuttleAvailable, + boolean isLiked ) { public static AcademyFilterResponse from(AcademyFilterResult academyFilterResult) { return new AcademyFilterResponse( @@ -21,7 +22,8 @@ public static AcademyFilterResponse from(AcademyFilterResult academyFilterResult academyFilterResult.areaOfExpertise(), academyFilterResult.latitude(), academyFilterResult.longitude(), - academyFilterResult.shuttleAvailable() + academyFilterResult.shuttleAvailable(), + academyFilterResult.isLiked() ); } } From c777e3ca9e3ab07c5cda61a2d57eba293b6282b9 Mon Sep 17 00:00:00 2001 From: byeolhaha Date: Thu, 9 Nov 2023 13:36:26 +0900 Subject: [PATCH 18/21] =?UTF-8?q?Style=20:=20=EA=B0=9C=ED=96=89=20?= =?UTF-8?q?=EC=A0=95=EB=A6=AC=20=EB=B0=8F=20Column=20name=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../academy/controller/AcademyController.java | 7 +++---- .../dto/response/AcademiesByLocationResponses.java | 2 +- .../dto/response/AcademiesByNameResponse.java | 1 - .../dto/response/AcademiesByNameResponses.java | 2 -- .../controller/dto/response/LessonGetResponse.java | 1 - .../controller/dto/response/LessonGetResponses.java | 2 +- .../dto/response/ReviewPercentGetResponse.java | 1 - .../dto/validation/ValidAreaOfExpertise.java | 2 ++ .../studayserver/domain/academy/model/Lesson.java | 2 ++ .../domain/academy/model/vo/Address.java | 2 ++ .../domain/academy/model/vo/Location.java | 2 ++ .../academy/model/vo/academyinfo/PhoneNumber.java | 2 ++ .../domain/academy/repository/AcademiesByName.java | 2 -- .../domain/academy/repository/AcademyFee.java | 1 - .../repository/academy/AcademyQueryRepository.java | 1 + .../academy/AcademyQueryRepositoryImpl.java | 10 ++++++---- .../repository/academy/AcademyRepository.java | 1 + .../repository/lesson/LessonJpaRepository.java | 1 + .../academy/repository/lesson/LessonRepository.java | 1 + .../service/dto/param/AcademiesByLocationParam.java | 4 ++-- .../service/dto/param/AcademiesByNameParam.java | 5 +++-- .../service/dto/param/AcademyFilterParam.java | 13 +++++++------ .../dto/result/AcademiesByLocationResult.java | 2 +- .../dto/result/AcademiesByLocationResults.java | 3 ++- .../service/dto/result/AcademiesByNameResult.java | 1 - .../service/dto/result/AcademiesByNameResults.java | 2 +- .../service/dto/result/AcademyFilterResult.java | 1 - .../service/dto/result/AcademyFilterResults.java | 3 ++- .../service/dto/result/AcademyGetResult.java | 4 +++- .../service/dto/result/LessonGetResults.java | 3 ++- .../service/dto/result/ReviewPercentGetResult.java | 2 +- .../domain/academy/util/GeometryUtil.java | 8 +++----- .../domain/academy/util/SqlFormatter.java | 2 +- .../studayserver/domain/member/model/Member.java | 2 +- .../domain/academy/service/AcademyServiceTest.java | 4 ++-- .../testutil/fixture/academy/AcademyFixture.java | 5 +++-- .../fixture/academy/RandomLocationGenerator.java | 2 ++ .../testutil/fixture/member/MemberFixture.java | 1 + 38 files changed, 62 insertions(+), 48 deletions(-) diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/controller/AcademyController.java b/src/main/java/org/guzzing/studayserver/domain/academy/controller/AcademyController.java index 423ecc9a..32728fa4 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/controller/AcademyController.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/controller/AcademyController.java @@ -51,7 +51,7 @@ public ResponseEntity findByLocation( @MemberId Long memberId ) { AcademiesByLocationResults academiesByLocation = - academyService.findAcademiesByLocation(AcademiesByLocationRequest.to(request),memberId); + academyService.findAcademiesByLocation(AcademiesByLocationRequest.to(request), memberId); return ResponseEntity.status(HttpStatus.OK) .body(AcademiesByLocationResponses.from(academiesByLocation)); @@ -76,10 +76,9 @@ public ResponseEntity findByName( public ResponseEntity filterAcademies( @ModelAttribute @Valid AcademyFilterRequest request, @MemberId Long memberId - ) { + ) { AcademyFilterResults academyFilterResults = academyService.filterAcademies( - AcademyFilterRequest.to(request), memberId - ); + AcademyFilterRequest.to(request), memberId); return ResponseEntity.status(HttpStatus.OK) .body(AcademyFilterResponses.from(academyFilterResults)); diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/response/AcademiesByLocationResponses.java b/src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/response/AcademiesByLocationResponses.java index 40b62cef..4ee4cb27 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/response/AcademiesByLocationResponses.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/response/AcademiesByLocationResponses.java @@ -1,6 +1,7 @@ package org.guzzing.studayserver.domain.academy.controller.dto.response; import java.util.List; + import org.guzzing.studayserver.domain.academy.service.dto.result.AcademiesByLocationResults; public record AcademiesByLocationResponses( @@ -15,5 +16,4 @@ public static AcademiesByLocationResponses from(AcademiesByLocationResults acade .toList() ); } - } diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/response/AcademiesByNameResponse.java b/src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/response/AcademiesByNameResponse.java index 044a336f..86415795 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/response/AcademiesByNameResponse.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/response/AcademiesByNameResponse.java @@ -9,7 +9,6 @@ public record AcademiesByNameResponse( Double latitude, Double longitude ) { - public static AcademiesByNameResponse from(AcademiesByNameResult academiesByNameResult) { return new AcademiesByNameResponse( academiesByNameResult.academyId(), diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/response/AcademiesByNameResponses.java b/src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/response/AcademiesByNameResponses.java index 724be7cf..e1a0eda6 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/response/AcademiesByNameResponses.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/response/AcademiesByNameResponses.java @@ -3,11 +3,9 @@ import org.guzzing.studayserver.domain.academy.service.dto.result.AcademiesByNameResults; import org.springframework.data.domain.Slice; - public record AcademiesByNameResponses( Slice academiesByNameResponses ) { - public static AcademiesByNameResponses from(AcademiesByNameResults academiesByNameResults) { return new AcademiesByNameResponses( academiesByNameResults.academiesByNameResults() diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/response/LessonGetResponse.java b/src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/response/LessonGetResponse.java index 1f119df5..c3f76470 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/response/LessonGetResponse.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/response/LessonGetResponse.java @@ -9,7 +9,6 @@ public record LessonGetResponse( String duration, Long totalFee ) { - public static LessonGetResponse from(LessonGetResult lesson) { return new LessonGetResponse( lesson.lessonId(), diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/response/LessonGetResponses.java b/src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/response/LessonGetResponses.java index 48022e74..b7e1c0cc 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/response/LessonGetResponses.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/response/LessonGetResponses.java @@ -1,12 +1,12 @@ package org.guzzing.studayserver.domain.academy.controller.dto.response; import java.util.List; + import org.guzzing.studayserver.domain.academy.service.dto.result.LessonGetResults; public record LessonGetResponses( List lessons ) { - public static LessonGetResponses from(LessonGetResults lessonGetResults) { return new LessonGetResponses( lessonGetResults diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/response/ReviewPercentGetResponse.java b/src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/response/ReviewPercentGetResponse.java index 1aeead0d..bd480610 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/response/ReviewPercentGetResponse.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/response/ReviewPercentGetResponse.java @@ -9,7 +9,6 @@ public record ReviewPercentGetResponse( int goodManagementPercent, int lovelyTeachingPercent ) { - public static ReviewPercentGetResponse from(ReviewPercentGetResult response) { return new ReviewPercentGetResponse( response.kindnessPercent(), diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/validation/ValidAreaOfExpertise.java b/src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/validation/ValidAreaOfExpertise.java index 11c2d278..694e40e8 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/validation/ValidAreaOfExpertise.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/controller/dto/validation/ValidAreaOfExpertise.java @@ -13,6 +13,8 @@ @Constraint(validatedBy = ValidAreaOfExpertiseValidator.class) public @interface ValidAreaOfExpertise { String message() default "유효하지 않은 학원 분야입니다."; + Class[] groups() default {}; + Class[] payload() default {}; } diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/model/Lesson.java b/src/main/java/org/guzzing/studayserver/domain/academy/model/Lesson.java index 784f267f..82c127ca 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/model/Lesson.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/model/Lesson.java @@ -10,7 +10,9 @@ import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; + import java.util.Objects; + import lombok.Getter; import org.springframework.util.Assert; diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/model/vo/Address.java b/src/main/java/org/guzzing/studayserver/domain/academy/model/vo/Address.java index 1ae9611d..24965e9a 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/model/vo/Address.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/model/vo/Address.java @@ -3,7 +3,9 @@ import io.micrometer.common.util.StringUtils; import jakarta.persistence.Column; import jakarta.persistence.Embeddable; + import java.util.Objects; + import lombok.Getter; import org.springframework.util.Assert; diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/model/vo/Location.java b/src/main/java/org/guzzing/studayserver/domain/academy/model/vo/Location.java index dbe322ca..0116c36f 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/model/vo/Location.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/model/vo/Location.java @@ -2,7 +2,9 @@ import jakarta.persistence.Column; import jakarta.persistence.Embeddable; + import java.util.Objects; + import lombok.Getter; @Getter diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/model/vo/academyinfo/PhoneNumber.java b/src/main/java/org/guzzing/studayserver/domain/academy/model/vo/academyinfo/PhoneNumber.java index ef5137c9..934f533a 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/model/vo/academyinfo/PhoneNumber.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/model/vo/academyinfo/PhoneNumber.java @@ -3,8 +3,10 @@ import jakarta.persistence.Column; import jakarta.persistence.Embeddable; import jakarta.persistence.Transient; + import java.util.Objects; import java.util.regex.Pattern; + import lombok.Getter; @Getter diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/repository/AcademiesByName.java b/src/main/java/org/guzzing/studayserver/domain/academy/repository/AcademiesByName.java index 7bb6df49..cd056af6 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/repository/AcademiesByName.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/repository/AcademiesByName.java @@ -13,5 +13,3 @@ public interface AcademiesByName { Double getLatitude(); } - - diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/repository/AcademyFee.java b/src/main/java/org/guzzing/studayserver/domain/academy/repository/AcademyFee.java index 349e3f68..1b4b37c1 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/repository/AcademyFee.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/repository/AcademyFee.java @@ -6,5 +6,4 @@ public interface AcademyFee { String getAcademyName(); - } diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyQueryRepository.java b/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyQueryRepository.java index 65cb970c..6d9e383c 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyQueryRepository.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyQueryRepository.java @@ -1,6 +1,7 @@ package org.guzzing.studayserver.domain.academy.repository.academy; import java.util.List; + import org.guzzing.studayserver.domain.academy.repository.AcademiesByLocation; import org.guzzing.studayserver.domain.academy.repository.AcademyByFiltering; import org.guzzing.studayserver.domain.academy.repository.AcademyFilterCondition; diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyQueryRepositoryImpl.java b/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyQueryRepositoryImpl.java index 14b8b42c..ad59b8d5 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyQueryRepositoryImpl.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyQueryRepositoryImpl.java @@ -2,7 +2,9 @@ import jakarta.persistence.EntityManager; import jakarta.persistence.Query; + import java.util.List; + import org.guzzing.studayserver.domain.academy.repository.AcademiesByLocation; import org.guzzing.studayserver.domain.academy.repository.AcademyByFiltering; import org.guzzing.studayserver.domain.academy.repository.AcademyFilterCondition; @@ -25,7 +27,7 @@ public List findAcademiesByLocation(String pointFormat, Lon " (CASE WHEN r.academy_id IS NOT NULL THEN true ELSE false END) AS isLiked " + " FROM academies AS a" + " LEFT JOIN reviews AS r" + - " ON a.id = r.academy_id AND r.member_id = "+memberId + + " ON a.id = r.academy_id AND r.member_id = " + memberId + " WHERE MBRContains(ST_LINESTRINGFROMTEXT(" + pointFormat + ", a.point)=1"); List academies = query.unwrap(org.hibernate.query.NativeQuery.class) @@ -72,15 +74,15 @@ public List filterAcademies(AcademyFilterCondition academyFi "(CASE WHEN r.academy_id IS NOT NULL THEN true ELSE false END) AS isLiked " + "FROM academies AS a " + "LEFT JOIN reviews AS r " + - "ON a.id = r.academy_id AND r.member_id = "+memberId + + "ON a.id = r.academy_id AND r.member_id = " + memberId + " WHERE MBRContains(ST_LINESTRINGFROMTEXT(" + academyFilterCondition.pointFormat() + ", a.point)=1 "; if (academyFilterCondition.areaOfExpertises() != null && !academyFilterCondition.areaOfExpertises().isEmpty()) { - nativeQuery += " AND area_of_expertise IN "+ academyFilterCondition.areaOfExpertises(); + nativeQuery += " AND area_of_expertise IN " + academyFilterCondition.areaOfExpertises(); } if (academyFilterCondition.desiredMinAmount() != null && academyFilterCondition.desiredMaxAmount() != null) { - nativeQuery += " AND max_education_fee BETWEEN "+academyFilterCondition.desiredMinAmount() +" AND "+academyFilterCondition.desiredMaxAmount(); + nativeQuery += " AND max_education_fee BETWEEN " + academyFilterCondition.desiredMinAmount() + " AND " + academyFilterCondition.desiredMaxAmount(); } Query query = em.createNativeQuery(nativeQuery); diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyRepository.java b/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyRepository.java index b19af5ad..f052c906 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyRepository.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyRepository.java @@ -1,6 +1,7 @@ package org.guzzing.studayserver.domain.academy.repository.academy; import java.util.List; + import org.guzzing.studayserver.domain.academy.model.Academy; import org.guzzing.studayserver.domain.academy.repository.*; import org.springframework.data.domain.Pageable; diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/repository/lesson/LessonJpaRepository.java b/src/main/java/org/guzzing/studayserver/domain/academy/repository/lesson/LessonJpaRepository.java index 15c96199..027980b0 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/repository/lesson/LessonJpaRepository.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/repository/lesson/LessonJpaRepository.java @@ -1,6 +1,7 @@ package org.guzzing.studayserver.domain.academy.repository.lesson; import java.util.List; + import org.guzzing.studayserver.domain.academy.model.Lesson; import org.springframework.data.jpa.repository.JpaRepository; diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/repository/lesson/LessonRepository.java b/src/main/java/org/guzzing/studayserver/domain/academy/repository/lesson/LessonRepository.java index d6298e9d..6000c4e0 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/repository/lesson/LessonRepository.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/repository/lesson/LessonRepository.java @@ -1,6 +1,7 @@ package org.guzzing.studayserver.domain.academy.repository.lesson; import java.util.List; + import org.guzzing.studayserver.domain.academy.model.Lesson; public interface LessonRepository extends LessonJpaRepository { diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/param/AcademiesByLocationParam.java b/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/param/AcademiesByLocationParam.java index ccba306a..7b352745 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/param/AcademiesByLocationParam.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/param/AcademiesByLocationParam.java @@ -4,9 +4,9 @@ public record AcademiesByLocationParam( Double baseLatitude, Double baseLongitude ) { - public static AcademiesByLocationParam of(Double baseLatitude, - Double baseLongitude) { + Double baseLongitude) { return new AcademiesByLocationParam(baseLatitude, baseLongitude); } + } diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/param/AcademiesByNameParam.java b/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/param/AcademiesByNameParam.java index 51168bd1..e9f9e98c 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/param/AcademiesByNameParam.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/param/AcademiesByNameParam.java @@ -4,8 +4,9 @@ public record AcademiesByNameParam( String academyName, int pageNumber ) { - - public static AcademiesByNameParam of(String academyName, int pageNumber) { + public static AcademiesByNameParam of(String academyName, + int pageNumber) { return new AcademiesByNameParam(academyName, pageNumber); } + } diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/param/AcademyFilterParam.java b/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/param/AcademyFilterParam.java index 5fbcdc6e..8caf085a 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/param/AcademyFilterParam.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/param/AcademyFilterParam.java @@ -6,13 +6,14 @@ import java.util.List; public record AcademyFilterParam( - Double baseLatitude, - Double baseLongitude, - List areaOfExpertises, - Long desiredMinAmount, - Long desiredMaxAmount + Double baseLatitude, + Double baseLongitude, + List areaOfExpertises, + Long desiredMinAmount, + Long desiredMaxAmount ) { - public static AcademyFilterCondition to(AcademyFilterParam param, String pointFormat){ + public static AcademyFilterCondition to(AcademyFilterParam param, + String pointFormat) { return new AcademyFilterCondition( pointFormat, SqlFormatter.makeWhereInString(param.areaOfExpertises), diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/result/AcademiesByLocationResult.java b/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/result/AcademiesByLocationResult.java index 05e4231f..b9e1088e 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/result/AcademiesByLocationResult.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/result/AcademiesByLocationResult.java @@ -13,7 +13,6 @@ public record AcademiesByLocationResult( String shuttleAvailable, boolean isLiked ) { - public static AcademiesByLocationResult from(AcademiesByLocation academiesByLocation) { return new AcademiesByLocationResult( academiesByLocation.academyId(), @@ -27,4 +26,5 @@ public static AcademiesByLocationResult from(AcademiesByLocation academiesByLoca academiesByLocation.isLiked() ); } + } diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/result/AcademiesByLocationResults.java b/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/result/AcademiesByLocationResults.java index cad8a0b6..f00a05ef 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/result/AcademiesByLocationResults.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/result/AcademiesByLocationResults.java @@ -1,16 +1,17 @@ package org.guzzing.studayserver.domain.academy.service.dto.result; import java.util.List; + import org.guzzing.studayserver.domain.academy.repository.AcademiesByLocation; public record AcademiesByLocationResults( List academiesByLocationResults ) { - public static AcademiesByLocationResults to(List academiesByLocations) { return new AcademiesByLocationResults( academiesByLocations.stream() .map(AcademiesByLocationResult::from) .toList()); } + } diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/result/AcademiesByNameResult.java b/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/result/AcademiesByNameResult.java index 0aa5dde0..c4531a2b 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/result/AcademiesByNameResult.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/result/AcademiesByNameResult.java @@ -9,7 +9,6 @@ public record AcademiesByNameResult( Double latitude, Double longitude ) { - public static AcademiesByNameResult from(AcademiesByName academiesByName) { return new AcademiesByNameResult( academiesByName.getAcademyId(), diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/result/AcademiesByNameResults.java b/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/result/AcademiesByNameResults.java index 4a8460a2..2e0bffab 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/result/AcademiesByNameResults.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/result/AcademiesByNameResults.java @@ -6,9 +6,9 @@ public record AcademiesByNameResults( Slice academiesByNameResults ) { - public static AcademiesByNameResults to(Slice academiesByNames) { return new AcademiesByNameResults( academiesByNames.map(AcademiesByNameResult::from)); } + } diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/result/AcademyFilterResult.java b/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/result/AcademyFilterResult.java index 278a87c5..5d3bcd45 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/result/AcademyFilterResult.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/result/AcademyFilterResult.java @@ -13,7 +13,6 @@ public record AcademyFilterResult( String shuttleAvailable, boolean isLiked ) { - public static AcademyFilterResult from(AcademyByFiltering academyByFiltering) { return new AcademyFilterResult( academyByFiltering.academyId(), diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/result/AcademyFilterResults.java b/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/result/AcademyFilterResults.java index 948a01f4..fea96518 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/result/AcademyFilterResults.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/result/AcademyFilterResults.java @@ -4,7 +4,7 @@ import java.util.List; -public record AcademyFilterResults ( +public record AcademyFilterResults( List academyFilterResults ) { public static AcademyFilterResults from(List academiesByFiltering) { @@ -14,4 +14,5 @@ public static AcademyFilterResults from(List academiesByFilt .toList() ); } + } diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/result/AcademyGetResult.java b/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/result/AcademyGetResult.java index cf82206f..457b9408 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/result/AcademyGetResult.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/result/AcademyGetResult.java @@ -1,6 +1,7 @@ package org.guzzing.studayserver.domain.academy.service.dto.result; import java.util.List; + import org.guzzing.studayserver.domain.academy.model.Academy; import org.guzzing.studayserver.domain.academy.model.Lesson; import org.guzzing.studayserver.domain.academy.model.ReviewCount; @@ -33,8 +34,9 @@ public static AcademyGetResult from( LessonGetResults.from(lessons), ReviewPercentGetResult.from(reviewCount), + isLiked ); - } + } diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/result/LessonGetResults.java b/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/result/LessonGetResults.java index 67b4189b..9cf0eb78 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/result/LessonGetResults.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/result/LessonGetResults.java @@ -1,12 +1,12 @@ package org.guzzing.studayserver.domain.academy.service.dto.result; import java.util.List; + import org.guzzing.studayserver.domain.academy.model.Lesson; public record LessonGetResults( List lessonGetResults ) { - public static LessonGetResults from(List lessons) { return new LessonGetResults( lessons @@ -14,4 +14,5 @@ public static LessonGetResults from(List lessons) { .map(lesson -> LessonGetResult.from(lesson)) .toList()); } + } diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/result/ReviewPercentGetResult.java b/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/result/ReviewPercentGetResult.java index 47aa4078..ae45b8f0 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/result/ReviewPercentGetResult.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/service/dto/result/ReviewPercentGetResult.java @@ -9,7 +9,6 @@ public record ReviewPercentGetResult( int goodManagementPercent, int lovelyTeachingPercent ) { - public static ReviewPercentGetResult from(ReviewCount reviewCount) { return new ReviewPercentGetResult( reviewCount.makePercent(reviewCount.getKindnessCount()), @@ -19,4 +18,5 @@ public static ReviewPercentGetResult from(ReviewCount reviewCount) { reviewCount.makePercent(reviewCount.getLovelyTeachingCount()) ); } + } diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/util/GeometryUtil.java b/src/main/java/org/guzzing/studayserver/domain/academy/util/GeometryUtil.java index 51a79cfa..e82e6515 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/util/GeometryUtil.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/util/GeometryUtil.java @@ -6,12 +6,10 @@ public class GeometryUtil { private static final Double EARTH_RADIUS = 6371.01; - private static final String LINESTRING_SQL = "'LINESTRING(%f %f, %f %f)')"; - public static Location calculateLocationWithinRadiusInDirection(Double baseLatitude, - Double baseLongitude, - Double bearing, - Double distance) { + Double baseLongitude, + Double bearing, + Double distance) { Double radianLatitude = toRadian(baseLatitude); Double radianLongitude = toRadian(baseLongitude); Double radianAngle = toRadian(bearing); diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/util/SqlFormatter.java b/src/main/java/org/guzzing/studayserver/domain/academy/util/SqlFormatter.java index 6c52eaeb..00ab9627 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/util/SqlFormatter.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/util/SqlFormatter.java @@ -7,7 +7,7 @@ public class SqlFormatter { private static final String LINESTRING_SQL = "'LINESTRING(%f %f, %f %f)')"; - public static String makeWhereInString(List values) { + public static String makeWhereInString(List values) { StringBuilder builder = new StringBuilder("("); for (int i = 0; i < values.size(); i++) { builder.append("'"); diff --git a/src/main/java/org/guzzing/studayserver/domain/member/model/Member.java b/src/main/java/org/guzzing/studayserver/domain/member/model/Member.java index 51e9732e..3c9516a9 100644 --- a/src/main/java/org/guzzing/studayserver/domain/member/model/Member.java +++ b/src/main/java/org/guzzing/studayserver/domain/member/model/Member.java @@ -51,7 +51,7 @@ public class Member { private MemberProvider memberProvider; @Enumerated(EnumType.STRING) - @Column(nullable = false, name = "role_tyle") + @Column(nullable = false, name = "role_type") private RoleType roleType; @OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY) diff --git a/src/test/java/org/guzzing/studayserver/domain/academy/service/AcademyServiceTest.java b/src/test/java/org/guzzing/studayserver/domain/academy/service/AcademyServiceTest.java index ad224b1a..c15968bd 100644 --- a/src/test/java/org/guzzing/studayserver/domain/academy/service/AcademyServiceTest.java +++ b/src/test/java/org/guzzing/studayserver/domain/academy/service/AcademyServiceTest.java @@ -80,7 +80,7 @@ void setUp() { @DisplayName("학원 ID로 학원 정보를 조회할 때 학원 정보, 수업 정보, 리뷰를 확인할 수 있다.") void getAcademy_academyId_reviewsAndLessons() { //When - AcademyGetResult academyGetResult = academyService.getAcademy(savedAcademyAboutSungnam.getId(),savedMember.getId()); + AcademyGetResult academyGetResult = academyService.getAcademy(savedAcademyAboutSungnam.getId(), savedMember.getId()); //Then assertThat(academyGetResult.academyName()).isEqualTo(savedAcademyAboutSungnam.getAcademyName()); @@ -157,7 +157,7 @@ void filterAcademy_BetweenEducationFeeAndLocationAndInExpertise() { for (Academy academy : academies) { Academy savedAcademy = academyRepository.save(academy); - savedAcademy.changeEducationFee(generateRandomAmount(minFee,maxFee)); + savedAcademy.changeEducationFee(generateRandomAmount(minFee, maxFee)); lessonRepository.save(AcademyFixture.lessonForSunganm(savedAcademy)); reviewCountRepository.save(AcademyFixture.reviewCountDefault(savedAcademy)); diff --git a/src/test/java/org/guzzing/studayserver/testutil/fixture/academy/AcademyFixture.java b/src/test/java/org/guzzing/studayserver/testutil/fixture/academy/AcademyFixture.java index 80986c72..f3ecf65f 100644 --- a/src/test/java/org/guzzing/studayserver/testutil/fixture/academy/AcademyFixture.java +++ b/src/test/java/org/guzzing/studayserver/testutil/fixture/academy/AcademyFixture.java @@ -2,6 +2,7 @@ import java.util.ArrayList; import java.util.List; + import org.guzzing.studayserver.domain.academy.model.Academy; import org.guzzing.studayserver.domain.academy.model.Lesson; import org.guzzing.studayserver.domain.academy.model.ReviewCount; @@ -71,8 +72,8 @@ public static AcademiesByLocationParam academiesByLocationParam(double latitude, return AcademiesByLocationParam.of(latitude, longitude); } - public static AcademyFilterParam academyFilterParam(Double latitude, Double longitude,Long desiredMinAmount, Long desiredMaxAmount) { - return new AcademyFilterParam(latitude,longitude,List.of("예능(대)"),desiredMinAmount, desiredMaxAmount); + public static AcademyFilterParam academyFilterParam(Double latitude, Double longitude, Long desiredMinAmount, Long desiredMaxAmount) { + return new AcademyFilterParam(latitude, longitude, List.of("예능(대)"), desiredMinAmount, desiredMaxAmount); } } diff --git a/src/test/java/org/guzzing/studayserver/testutil/fixture/academy/RandomLocationGenerator.java b/src/test/java/org/guzzing/studayserver/testutil/fixture/academy/RandomLocationGenerator.java index 525a9977..5010696a 100644 --- a/src/test/java/org/guzzing/studayserver/testutil/fixture/academy/RandomLocationGenerator.java +++ b/src/test/java/org/guzzing/studayserver/testutil/fixture/academy/RandomLocationGenerator.java @@ -3,6 +3,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Random; + import org.guzzing.studayserver.domain.academy.model.vo.Location; import org.guzzing.studayserver.domain.academy.util.GeometryUtil; @@ -41,4 +42,5 @@ public static List generateRandomLocations(Location centerLocation, in return randomLocations; } + } diff --git a/src/test/java/org/guzzing/studayserver/testutil/fixture/member/MemberFixture.java b/src/test/java/org/guzzing/studayserver/testutil/fixture/member/MemberFixture.java index 2aabb7d1..a825b7d6 100644 --- a/src/test/java/org/guzzing/studayserver/testutil/fixture/member/MemberFixture.java +++ b/src/test/java/org/guzzing/studayserver/testutil/fixture/member/MemberFixture.java @@ -9,4 +9,5 @@ public class MemberFixture { public static Member member() { return Member.of(new NickName("나는왕이다"), "12345678", MemberProvider.KAKAO, RoleType.USER); } + } From 5d607cce5d99c9bafece19c08625df6cd41cf887 Mon Sep 17 00:00:00 2001 From: byeolhaha Date: Thu, 9 Nov 2023 15:02:06 +0900 Subject: [PATCH 19/21] =?UTF-8?q?Chore=20:=20cicid=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci-develop.yml | 2 +- .github/workflows/develop-cicd.yml | 2 +- scripts/deploy.sh | 2 +- .../studayserver/testutil/fixture/member/MemberFixture.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci-develop.yml b/.github/workflows/ci-develop.yml index b11b3193..f6d142dc 100644 --- a/.github/workflows/ci-develop.yml +++ b/.github/workflows/ci-develop.yml @@ -47,7 +47,7 @@ jobs: run: | mkdir -p src/main/resources echo "${{ secrets.APPLICATION_YML_CONTENT }}" > src/main/resources/application.yml - echo "${{ secrets.APPLICATION_COMMERCIAL_YML_CONTENT }}" > src/main/resources/application-commercial.yml + echo "${{ secrets.APPLICATION_PROD_YML_CONTENT }}" > src/main/resources/application-prod.yml echo "${{ secrets.APPLICATION_DEV_YML_CONTENT }}" > src/main/resources/application-dev.yml echo "${{ secrets.APPLICATION_OAUTH_YML_CONTENT }}" > src/main/resources/application-oauth.yml diff --git a/.github/workflows/develop-cicd.yml b/.github/workflows/develop-cicd.yml index afdc7bc2..5fa6f77e 100644 --- a/.github/workflows/develop-cicd.yml +++ b/.github/workflows/develop-cicd.yml @@ -47,7 +47,7 @@ jobs: run: | mkdir -p src/main/resources echo "${{ secrets.APPLICATION_YML_CONTENT }}" > src/main/resources/application.yml - echo "${{ secrets.APPLICATION_COMMERCIAL_YML_CONTENT }}" > src/main/resources/application-commercial.yml + echo "${{ secrets.APPLICATION_PROD_YML_CONTENT }}" > src/main/resources/application-prod.yml echo "${{ secrets.APPLICATION_DEV_YML_CONTENT }}" > src/main/resources/application-dev.yml echo "${{ secrets.APPLICATION_OAUTH_YML_CONTENT }}" > src/main/resources/application-oauth.yml diff --git a/scripts/deploy.sh b/scripts/deploy.sh index d63369fe..61d70d36 100644 --- a/scripts/deploy.sh +++ b/scripts/deploy.sh @@ -19,4 +19,4 @@ else fi echo "> Deploy - $JAR_PATH " -nohup java -jar $JAR_PATH > /dev/null 2> /dev/null < /dev/null & +nohup java -jar -Dspring.profiles.active=prod,oauth $JAR_PATH > /dev/null 2> /dev/null < /dev/null & diff --git a/src/test/java/org/guzzing/studayserver/testutil/fixture/member/MemberFixture.java b/src/test/java/org/guzzing/studayserver/testutil/fixture/member/MemberFixture.java index a825b7d6..e03d7d3c 100644 --- a/src/test/java/org/guzzing/studayserver/testutil/fixture/member/MemberFixture.java +++ b/src/test/java/org/guzzing/studayserver/testutil/fixture/member/MemberFixture.java @@ -9,5 +9,5 @@ public class MemberFixture { public static Member member() { return Member.of(new NickName("나는왕이다"), "12345678", MemberProvider.KAKAO, RoleType.USER); } - + } From 83daf6edacc877c57a1bbeac5c0f210087edf824 Mon Sep 17 00:00:00 2001 From: byeolhaha Date: Thu, 9 Nov 2023 19:03:19 +0900 Subject: [PATCH 20/21] =?UTF-8?q?ReFator=20:=20=EB=A0=88=ED=8C=8C=EC=A7=80?= =?UTF-8?q?=ED=86=A0=EB=A6=AC=20=EA=B5=AC=EC=A1=B0=20=EB=B3=80=EA=B2=BD=20?= =?UTF-8?q?=EB=B0=8F=20=EC=BF=BC=EB=A6=AC=20=EC=A2=8B=EC=95=84=EC=9A=94=20?= =?UTF-8?q?=ED=85=8C=EC=9D=B4=EB=B8=94=20=EC=B0=B8=EC=A1=B0=ED=95=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/academy/model/LessonTest.java | 38 --- .../domain/academy/model/vo/AddressTest.java | 19 -- .../model/vo/academyinfo/AcademyInfoTest.java | 25 -- .../academy/service/AcademyServiceTest.java | 190 ----------- .../controller/ChildRestControllerTest.java | 177 ----------- .../domain/child/model/ChildTest.java | 66 ---- .../domain/child/model/NickNameTest.java | 43 --- .../child/service/ChildServiceTest.java | 295 ------------------ .../controller/LikeRestControllerTest.java | 192 ------------ .../domain/like/service/LikeServiceTest.java | 105 ------- .../controller/MemberRestControllerTest.java | 109 ------- .../domain/member/model/MemberTest.java | 112 ------- .../domain/member/model/vo/EmailTest.java | 54 ---- .../member/service/MemberServiceTest.java | 41 --- .../controller/RegionRestControllerTest.java | 187 ----------- .../region/service/RegionServiceTest.java | 87 ------ .../controller/ReviewRestControllerTest.java | 150 --------- .../review/service/ReviewServiceTest.java | 134 -------- 18 files changed, 2024 deletions(-) delete mode 100644 src/test/java/org/guzzing/studayserver/domain/academy/model/LessonTest.java delete mode 100644 src/test/java/org/guzzing/studayserver/domain/academy/model/vo/AddressTest.java delete mode 100644 src/test/java/org/guzzing/studayserver/domain/academy/model/vo/academyinfo/AcademyInfoTest.java delete mode 100644 src/test/java/org/guzzing/studayserver/domain/academy/service/AcademyServiceTest.java delete mode 100644 src/test/java/org/guzzing/studayserver/domain/child/controller/ChildRestControllerTest.java delete mode 100644 src/test/java/org/guzzing/studayserver/domain/child/model/ChildTest.java delete mode 100644 src/test/java/org/guzzing/studayserver/domain/child/model/NickNameTest.java delete mode 100644 src/test/java/org/guzzing/studayserver/domain/child/service/ChildServiceTest.java delete mode 100644 src/test/java/org/guzzing/studayserver/domain/like/controller/LikeRestControllerTest.java delete mode 100644 src/test/java/org/guzzing/studayserver/domain/like/service/LikeServiceTest.java delete mode 100644 src/test/java/org/guzzing/studayserver/domain/member/controller/MemberRestControllerTest.java delete mode 100644 src/test/java/org/guzzing/studayserver/domain/member/model/MemberTest.java delete mode 100644 src/test/java/org/guzzing/studayserver/domain/member/model/vo/EmailTest.java delete mode 100644 src/test/java/org/guzzing/studayserver/domain/member/service/MemberServiceTest.java delete mode 100644 src/test/java/org/guzzing/studayserver/domain/region/controller/RegionRestControllerTest.java delete mode 100644 src/test/java/org/guzzing/studayserver/domain/region/service/RegionServiceTest.java delete mode 100644 src/test/java/org/guzzing/studayserver/domain/review/controller/ReviewRestControllerTest.java delete mode 100644 src/test/java/org/guzzing/studayserver/domain/review/service/ReviewServiceTest.java diff --git a/src/test/java/org/guzzing/studayserver/domain/academy/model/LessonTest.java b/src/test/java/org/guzzing/studayserver/domain/academy/model/LessonTest.java deleted file mode 100644 index 8b19bfb7..00000000 --- a/src/test/java/org/guzzing/studayserver/domain/academy/model/LessonTest.java +++ /dev/null @@ -1,38 +0,0 @@ -package org.guzzing.studayserver.domain.academy.model; - -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -import org.guzzing.studayserver.testutil.fixture.academy.AcademyFixture; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -class LessonTest { - - @Test - @DisplayName("수업을 등록할 때 학원이 없으면 예외를 던진다.") - void makeLesson_nullAcademy_throwException() { - //Then - assertThatThrownBy( - () -> Lesson.of(null, "자바와 객체지향", "자바와 객체지향으로 떠나자", "20", "1개월", "100000") - ).isInstanceOf(IllegalArgumentException.class); - } - - @Test - @DisplayName("수업 정원이 음수인 경우 예외를 던진다.") - void makeLesson_minusCapacity_throwException() { - //Then - assertThatThrownBy( - () -> Lesson.of(AcademyFixture.academySungnam(), "자바와 객체지향", "자바와 객체지향으로 떠나자", "-100", "1개월", "100000") - ).isInstanceOf(IllegalArgumentException.class); - } - - @Test - @DisplayName("강의료가 음수인 경우 예외를 던진다. ") - void makeLesson_minusTotalFee_throwException() { - //Then - assertThatThrownBy( - () -> Lesson.of(AcademyFixture.academySungnam(), "자바와 객체지향", "자바와 객체지향으로 떠나자", "20", "1개월", "-100000") - ).isInstanceOf(IllegalArgumentException.class); - } - -} diff --git a/src/test/java/org/guzzing/studayserver/domain/academy/model/vo/AddressTest.java b/src/test/java/org/guzzing/studayserver/domain/academy/model/vo/AddressTest.java deleted file mode 100644 index 72463c9a..00000000 --- a/src/test/java/org/guzzing/studayserver/domain/academy/model/vo/AddressTest.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.guzzing.studayserver.domain.academy.model.vo; - -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -import org.guzzing.studayserver.domain.academy.model.vo.academyinfo.AcademyInfo; -import org.guzzing.studayserver.domain.academy.model.vo.academyinfo.ShuttleAvailability; -import org.junit.jupiter.api.Test; - -class AddressTest { - - @Test - void makeInvalidAddress_throwException() { - //Then - assertThatThrownBy( - () -> AcademyInfo.of("박세영 코딩학원", "123456789", ShuttleAvailability.AVAILABLE.name(), "예능(대)") - ).isInstanceOf(IllegalArgumentException.class); - } - -} diff --git a/src/test/java/org/guzzing/studayserver/domain/academy/model/vo/academyinfo/AcademyInfoTest.java b/src/test/java/org/guzzing/studayserver/domain/academy/model/vo/academyinfo/AcademyInfoTest.java deleted file mode 100644 index c3d58f50..00000000 --- a/src/test/java/org/guzzing/studayserver/domain/academy/model/vo/academyinfo/AcademyInfoTest.java +++ /dev/null @@ -1,25 +0,0 @@ -package org.guzzing.studayserver.domain.academy.model.vo.academyinfo; - -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -import org.junit.jupiter.api.Test; - -class AcademyInfoTest { - - @Test - void makeInvalidNameAcademyInfo_throwException() { - //Then - assertThatThrownBy( - () -> AcademyInfo.of("", "000-0000-0000", ShuttleAvailability.AVAILABLE.name(), "예능(대)") - ).isInstanceOf(IllegalArgumentException.class); - } - - @Test - void makeInvalidPhoneNumberAcademyInfo_throwException() { - //Then - assertThatThrownBy( - () -> AcademyInfo.of("박세영 코딩학원", "123445667", ShuttleAvailability.AVAILABLE.name(), "예능(대)") - ).isInstanceOf(IllegalArgumentException.class); - } - -} diff --git a/src/test/java/org/guzzing/studayserver/domain/academy/service/AcademyServiceTest.java b/src/test/java/org/guzzing/studayserver/domain/academy/service/AcademyServiceTest.java deleted file mode 100644 index c15968bd..00000000 --- a/src/test/java/org/guzzing/studayserver/domain/academy/service/AcademyServiceTest.java +++ /dev/null @@ -1,190 +0,0 @@ -package org.guzzing.studayserver.domain.academy.service; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.guzzing.studayserver.domain.academy.model.Academy; -import org.guzzing.studayserver.domain.academy.model.Lesson; -import org.guzzing.studayserver.domain.academy.model.ReviewCount; -import org.guzzing.studayserver.domain.academy.repository.academy.AcademyRepository; -import org.guzzing.studayserver.domain.academy.repository.lesson.LessonRepository; -import org.guzzing.studayserver.domain.academy.repository.review.ReviewCountRepository; -import org.guzzing.studayserver.domain.academy.service.dto.param.AcademiesByNameParam; -import org.guzzing.studayserver.domain.academy.service.dto.param.AcademyFilterParam; -import org.guzzing.studayserver.domain.academy.service.dto.result.*; -import org.guzzing.studayserver.domain.member.model.Member; -import org.guzzing.studayserver.domain.member.repository.MemberRepository; -import org.guzzing.studayserver.testutil.fixture.academy.AcademyFixture; -import org.guzzing.studayserver.testutil.fixture.member.MemberFixture; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.ActiveProfiles; -import org.springframework.transaction.annotation.Transactional; - -import java.util.List; -import java.util.Random; - -@Transactional -@ActiveProfiles({"oauth", "dev"}) -@SpringBootTest -class AcademyServiceTest { - - private static final String ACADEMY_NAME_FOR_SEARCH = "코딩"; - private double LATITUDE = 37.4449168; - private double LONGITUDE = 127.1388684; - - @Autowired - private AcademyService academyService; - - @Autowired - private AcademyRepository academyRepository; - - @Autowired - private LessonRepository lessonRepository; - - @Autowired - private ReviewCountRepository reviewCountRepository; - - @Autowired - private MemberRepository memberRepository; - - private Member savedMember; - - private Academy savedAcademyAboutSungnam; - - private Lesson savedALessonAboutSungnam; - - private ReviewCount savedReviewCountAboutSungnam; - - @BeforeEach - void setUp() { - - Member member = MemberFixture.member(); - savedMember = memberRepository.save(member); - - Academy academyAboutSungnam = AcademyFixture.academySungnam(); - academyAboutSungnam.changeEducationFee(100000L); - savedAcademyAboutSungnam = academyRepository.save(academyAboutSungnam); - - Lesson lessonAboutSungnam = AcademyFixture.lessonForSunganm(savedAcademyAboutSungnam); - savedALessonAboutSungnam = lessonRepository.save(lessonAboutSungnam); - - savedReviewCountAboutSungnam = reviewCountRepository.save( - AcademyFixture.reviewCountDefault(savedAcademyAboutSungnam)); - } - - @Test - @DisplayName("학원 ID로 학원 정보를 조회할 때 학원 정보, 수업 정보, 리뷰를 확인할 수 있다.") - void getAcademy_academyId_reviewsAndLessons() { - //When - AcademyGetResult academyGetResult = academyService.getAcademy(savedAcademyAboutSungnam.getId(), savedMember.getId()); - - //Then - assertThat(academyGetResult.academyName()).isEqualTo(savedAcademyAboutSungnam.getAcademyName()); - assertThat(academyGetResult.contact()).isEqualTo(savedAcademyAboutSungnam.getContact()); - assertThat(academyGetResult.fullAddress()).isEqualTo(savedAcademyAboutSungnam.getFullAddress()); - assertThat(academyGetResult.shuttleAvailability()).isEqualTo(savedAcademyAboutSungnam.getShuttleAvailability().toString()); - assertThat(academyGetResult.expectedFee()).isEqualTo(savedAcademyAboutSungnam.getMaxEducationFee()); - assertThat(academyGetResult.updatedDate()).isEqualTo(savedAcademyAboutSungnam.getUpdatedDate().toString()); - assertThat(academyGetResult.areaOfExpertise()).isEqualTo(savedAcademyAboutSungnam.getAreaOfExpertise()); - assertThat(academyGetResult.lessonGetResults().lessonGetResults()).contains( - LessonGetResult.from(savedALessonAboutSungnam)); - assertThat(academyGetResult.reviewPercentGetResult()).isEqualTo( - ReviewPercentGetResult.from(savedReviewCountAboutSungnam)); - } - - @Test - @DisplayName("사용자의 중심 위치가 주어졌을 때 반경 거리 이내의 학원 목록이 조회된다.") - void findAcademiesByLocation_academiesWithinDistance_equalsSize() { - //Given - lessonRepository.deleteAll(); - reviewCountRepository.deleteAll(); - academyRepository.deleteAll(); - - List academies = AcademyFixture.randomAcademiesWithinDistance(LATITUDE, LONGITUDE); - for (Academy academy : academies) { - Academy savedAcademy = academyRepository.save(academy); - lessonRepository.save(AcademyFixture.lessonForSunganm(savedAcademy)); - reviewCountRepository.save(AcademyFixture.reviewCountDefault(savedAcademy)); - } - - //When - AcademiesByLocationResults academiesByLocations = academyService.findAcademiesByLocation( - AcademyFixture.academiesByLocationParam(LATITUDE, LONGITUDE), - savedMember.getId()); - - //Then - assertThat(academiesByLocations.academiesByLocationResults().size()).isEqualTo(academies.size()); - } - - @Test - @DisplayName("학원 이름(ACADEMY_NAME_FOR_SEARCH)으로 검색하면 자동완성 기능으로 관련 학원들을 보여준다.") - void findAcademiesByName_academyName_relatedAcademies() { - //Given - List academies = AcademyFixture.academies(); - for (Academy academy : academies) { - Academy savedAcademy = academyRepository.save(academy); - lessonRepository.save(AcademyFixture.lessonForSunganm(savedAcademy)); - reviewCountRepository.save(AcademyFixture.reviewCountDefault(savedAcademy)); - } - - //When - AcademiesByNameResults academiesByNameResults = academyService.findAcademiesByName( - AcademiesByNameParam.of(ACADEMY_NAME_FOR_SEARCH, 0) - ); - - //Then - for (AcademiesByNameResult academiesByNameResult : academiesByNameResults.academiesByNameResults()) { - assertThat(academiesByNameResult.academyName()).contains(ACADEMY_NAME_FOR_SEARCH); - } - } - - - @Test - @DisplayName("중심 위치 반경 이내에 있는 학원 중에서 교육비가 최소와 최대 사이에 있고 선택한 학원 분류 분야에 해당하는 학원들을 반환한다.") - void filterAcademy_BetweenEducationFeeAndLocationAndInExpertise() { - //Given - lessonRepository.deleteAll(); - reviewCountRepository.deleteAll(); - academyRepository.deleteAll(); - - List academies = AcademyFixture.randomAcademiesWithinDistance(LATITUDE, LONGITUDE); - Long minFee = 10000L; - Long maxFee = 1000000L; - - for (Academy academy : academies) { - Academy savedAcademy = academyRepository.save(academy); - savedAcademy.changeEducationFee(generateRandomAmount(minFee, maxFee)); - - lessonRepository.save(AcademyFixture.lessonForSunganm(savedAcademy)); - reviewCountRepository.save(AcademyFixture.reviewCountDefault(savedAcademy)); - } - AcademyFilterParam academyFilterParam = AcademyFixture.academyFilterParam(LATITUDE, LONGITUDE, minFee, maxFee); - - //When - AcademyFilterResults academyFilterResults = academyService.filterAcademies(academyFilterParam, savedMember.getId()); - - //Then - for (AcademyFilterResult academyFilterResult : academyFilterResults.academyFilterResults()) { - Academy filtedAcademy = academyRepository.getById(academyFilterResult.academyId()); - - assertThat(filtedAcademy.getMaxEducationFee()). - isGreaterThanOrEqualTo(minFee) - .isLessThanOrEqualTo(maxFee); - assertThat(academyFilterParam.areaOfExpertises()).containsExactlyInAnyOrder(academyFilterResult.areaOfExpertise()); - } - } - - private long generateRandomAmount(long min, long max) { - if (min >= max) { - throw new IllegalArgumentException("Min value must be less than max value"); - } - - Random random = new Random(); - return min + random.nextInt((int) (max - min + 1)); - } - -} diff --git a/src/test/java/org/guzzing/studayserver/domain/child/controller/ChildRestControllerTest.java b/src/test/java/org/guzzing/studayserver/domain/child/controller/ChildRestControllerTest.java deleted file mode 100644 index fea92d1c..00000000 --- a/src/test/java/org/guzzing/studayserver/domain/child/controller/ChildRestControllerTest.java +++ /dev/null @@ -1,177 +0,0 @@ -package org.guzzing.studayserver.domain.child.controller; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.when; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -import com.fasterxml.jackson.databind.ObjectMapper; -import java.util.List; -import java.util.stream.Stream; -import org.guzzing.studayserver.domain.child.controller.request.ChildCreateRequest; -import org.guzzing.studayserver.domain.child.controller.request.ChildModifyRequest; -import org.guzzing.studayserver.domain.child.controller.response.ChildrenFindResponse; -import org.guzzing.studayserver.domain.child.service.ChildService; -import org.guzzing.studayserver.domain.child.service.result.ChildrenFindResult; -import org.guzzing.studayserver.domain.child.service.result.ChildrenFindResult.ChildFindResult; -import org.guzzing.studayserver.testutil.WithMockCustomOAuth2LoginUser; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -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; - -@WebMvcTest(ChildRestController.class) -@AutoConfigureMockMvc(addFilters = false) -class ChildRestControllerTest { - - @Autowired - private MockMvc mockMvc; - - @MockBean - private ChildService childService; - - @Autowired - private ObjectMapper objectMapper; - - @Nested - class Create { - - @DisplayName("아이 생성시 정상 값이면 OK를 반환한다.") - @Test - @WithMockCustomOAuth2LoginUser(memberId = 1L) - void statusIsOk() throws Exception { - // Given - ChildCreateRequest request = new ChildCreateRequest("childName1", "초등학교 1학년"); - Long expectedChildId = 2L; - - // When - when(childService.create(any())).thenReturn(expectedChildId); - - // Then - mockMvc.perform(post("/children") - .contentType(MediaType.APPLICATION_JSON_VALUE) - .content(objectMapper.writeValueAsString(request))) - .andExpect(status().isOk()); - } - - @DisplayName("아이 생성시 잘못된 요청은 400에러를 반환한다.") - @ParameterizedTest - @WithMockCustomOAuth2LoginUser(memberId = 1L) - @MethodSource("provideInvalidRequests") - void statusIsBadRequest(ChildCreateRequest invalidRequest) throws Exception { - // Then - mockMvc.perform(post("/children") - .contentType(MediaType.APPLICATION_JSON_VALUE) - .content(objectMapper.writeValueAsString(invalidRequest))) - .andExpect(status().isBadRequest()); - } - - - private static Stream provideInvalidRequests() { - return Stream.of( - // 빈 닉네임 - Arguments.of(new ChildCreateRequest("", "초등학교 1학년")), - - // 긴 닉네임 - Arguments.of(new ChildCreateRequest("a".repeat(11), "초등학교 1학년")), - - // 빈 학년 - Arguments.of(new ChildCreateRequest("a".repeat(11), "")) - ); - } - } - - @DisplayName("멤버에 할당된 아이들의 정보를 반환한다.") - @WithMockCustomOAuth2LoginUser(memberId = 1L) - @Test - void findChildren_success() throws Exception { - // Given - ChildrenFindResult result = new ChildrenFindResult(List.of( - new ChildFindResult(1L, "Nickname1", "초등학교 1학년", "휴식 중!"), - new ChildFindResult(2L, "Nickname2", "초등학교 2학년", "휴식 중!") - )); - - given(childService.findByMemberId(1L)).willReturn(result); - - ChildrenFindResponse response = ChildrenFindResponse.from(result); - - // When & Then - mockMvc.perform(get("/children") - .contentType(MediaType.APPLICATION_JSON_VALUE) - .content(objectMapper.writeValueAsString(response))) - .andExpect(status().isOk()); - } - - @DisplayName("아이를 삭제한다.") - @WithMockCustomOAuth2LoginUser() - @Test - void delete_success() throws Exception { - // Given - Long existingChildId = 200L; - - // When & Then - mockMvc.perform(delete("/children/{childId}", existingChildId)) - .andExpect(status().isNoContent()); - } - - @Nested - class modify { - - @DisplayName("아이의 정보를 수정한다.") - @WithMockCustomOAuth2LoginUser(memberId = 1L) - @Test - void success() throws Exception { - // Given - Long childId = 100L; - - ChildModifyRequest request = new ChildModifyRequest("아이 닉네임", "초등학교 1학년"); - - given(childService.modify(any())).willReturn(childId); - - // Then - mockMvc.perform(patch("/children/{childId}", childId) - .contentType(MediaType.APPLICATION_JSON_VALUE) - .content(objectMapper.writeValueAsString(request))) - .andExpect(status().isOk()) - .andExpect(content().string(childId.toString())); - } - - @DisplayName("잘못된 요청이 들어오면 예외를 발생시킨다.") - @WithMockCustomOAuth2LoginUser() - @ParameterizedTest - @MethodSource("provideInvalidRequests") - void givenInvalidRequest_throwsException(ChildModifyRequest invalidRequest) throws Exception { - // When & Then - mockMvc.perform(patch("/children/{childId}", 100L) - .contentType(MediaType.APPLICATION_JSON_VALUE) - .content(objectMapper.writeValueAsString(invalidRequest))) - .andExpect(status().isBadRequest()); - } - - private static Stream provideInvalidRequests() { - return Stream.of( - // 빈 닉네임 - Arguments.of(new ChildModifyRequest("", "초등학교 1학년")), - - // 긴 닉네임 - Arguments.of(new ChildModifyRequest("a".repeat(11), "초등학교 1학년")), - - // 빈 학년 - Arguments.of(new ChildModifyRequest("a".repeat(11), "")) - ); - } - } -} diff --git a/src/test/java/org/guzzing/studayserver/domain/child/model/ChildTest.java b/src/test/java/org/guzzing/studayserver/domain/child/model/ChildTest.java deleted file mode 100644 index b893e795..00000000 --- a/src/test/java/org/guzzing/studayserver/domain/child/model/ChildTest.java +++ /dev/null @@ -1,66 +0,0 @@ -package org.guzzing.studayserver.domain.child.model; - -import static org.assertj.core.api.Assertions.assertThatCode; - -import org.assertj.core.api.Assertions; -import org.guzzing.studayserver.domain.member.model.Member; -import org.guzzing.studayserver.domain.member.model.vo.MemberProvider; -import org.guzzing.studayserver.domain.member.model.vo.RoleType; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -class ChildTest { - - - @Nested - class AssignToNewMemberOnly { - - private Child child; - private Member member; - - @BeforeEach - void setUp() { - child = new Child("아이 닉네임", "초등학교 1학년"); - member = Member.of(new NickName("멤버 닉네임"), "123", MemberProvider.KAKAO, RoleType.USER); - } - - @DisplayName("멤버가 없는 아이에게 새 멤버를 할당한다.") - @Test - void whenChildHasNoMember_assignsMember() { - // When & Then - assertThatCode(() -> child.assignToNewMemberOnly(member)) - .doesNotThrowAnyException(); - } - - @DisplayName("이미 멤버가 할당된 아이에게 새 멤버를 할당하려고 하면 예외를 던진다.") - @Test - void givenAssignToNewMemberOnly_WhenChildAlreadyHasMember_ThrowsException() { - // Given - child.assignToNewMemberOnly(member); - - // 실행 & 검증 - Assertions.assertThatThrownBy(() -> child.assignToNewMemberOnly(member)) - .isInstanceOf(IllegalStateException.class) - .hasMessageContaining("이미 멤버가 할당되어 있습니다."); - } - } - - @DisplayName("아이의 정보를 수정한다.") - @Test - void update_success() { - // Given - Child child = new Child("아이 닉네임", "초등학교 1학년"); - - String updatedNickname = "닉네임 수정"; - String updatedGrade = "초등학교 2학년"; - - // When - child.update(updatedNickname, updatedGrade); - - // Then - Assertions.assertThat(child.getNickName()).isEqualTo(updatedNickname); - Assertions.assertThat(child.getGrade()).isEqualTo(updatedGrade); - } -} diff --git a/src/test/java/org/guzzing/studayserver/domain/child/model/NickNameTest.java b/src/test/java/org/guzzing/studayserver/domain/child/model/NickNameTest.java deleted file mode 100644 index 3d0384e8..00000000 --- a/src/test/java/org/guzzing/studayserver/domain/child/model/NickNameTest.java +++ /dev/null @@ -1,43 +0,0 @@ -package org.guzzing.studayserver.domain.child.model; - -import static org.assertj.core.api.Assertions.assertThatCode; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -import java.util.stream.Stream; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; -import org.junit.jupiter.params.provider.ValueSource; - -class NickNameTest { - - @DisplayName("정상 값이라면 객체를 생성한다.") - @ParameterizedTest - @ValueSource(strings = { - "TestNickName", - "AnotherValidName", - "YetAnotherValidName" - }) - void create_success(String nickname) { - // Then - assertThatCode(() -> new NickName(nickname)) - .doesNotThrowAnyException(); - } - - @DisplayName("잘못된 값이라면 예외를 발생시킨다.") - @ParameterizedTest - @MethodSource("provideInvalidNicknames") - void create_failure_throwException(String invalidNickname) { - // Then - assertThatThrownBy(() -> new NickName(invalidNickname)) - .isInstanceOf(IllegalArgumentException.class); - } - - private static Stream provideInvalidNicknames() { - return Stream.of( - null, - " ", - "A".repeat(NickName.NAME_MAX_LENGTH + 1) - ); - } -} diff --git a/src/test/java/org/guzzing/studayserver/domain/child/service/ChildServiceTest.java b/src/test/java/org/guzzing/studayserver/domain/child/service/ChildServiceTest.java deleted file mode 100644 index be13d90c..00000000 --- a/src/test/java/org/guzzing/studayserver/domain/child/service/ChildServiceTest.java +++ /dev/null @@ -1,295 +0,0 @@ -package org.guzzing.studayserver.domain.child.service; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; - -import java.util.List; -import java.util.Optional; -import org.assertj.core.api.Assertions; -import org.guzzing.studayserver.domain.child.model.Child; -import org.guzzing.studayserver.domain.child.model.NickName; -import org.guzzing.studayserver.domain.child.repository.ChildRepository; -import org.guzzing.studayserver.domain.child.service.param.ChildCreateParam; -import org.guzzing.studayserver.domain.child.service.param.ChildDeleteParam; -import org.guzzing.studayserver.domain.child.service.param.ChildModifyParam; -import org.guzzing.studayserver.domain.child.service.result.ChildrenFindResult; -import org.guzzing.studayserver.domain.child.service.result.ChildrenFindResult.ChildFindResult; -import org.guzzing.studayserver.domain.member.model.Member; -import org.guzzing.studayserver.domain.member.model.vo.MemberProvider; -import org.guzzing.studayserver.domain.member.model.vo.RoleType; -import org.guzzing.studayserver.domain.member.repository.MemberRepository; -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.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.test.context.ActiveProfiles; -import org.springframework.transaction.annotation.Transactional; - -@SpringBootTest -@Transactional -@ActiveProfiles({"dev", "oauth"}) -class ChildServiceTest { - - @Autowired - private ChildService childService; - - @MockBean - private ChildRepository childRepository; - - @MockBean - private MemberRepository memberRepository; - - @Nested - class Create { - - @DisplayName("아이 생성 성공") - @Test - void success() { - // Given - Long memberId = 1L; - ChildCreateParam param = new ChildCreateParam("아이 닉네임", "초등학교 1학년", memberId); - Long expectedChildId = 2L; - - Member mockMember = mock(Member.class); - Child mockChild = mock(Child.class); - - given(mockChild.getId()).willReturn(expectedChildId); - given(memberRepository.findById(memberId)).willReturn(Optional.ofNullable(mockMember)); - given(childRepository.save(any(Child.class))).willReturn(mockChild); - - // When - Long savedChildId = childService.create(param); - - // Then - assertThat(savedChildId).isEqualTo(expectedChildId); - verify(childRepository).save(any(Child.class)); - } - - @DisplayName("잘못된 멤버 아이디로 인한 예외를 발생시킨다.") - @Test - void givenInvalidMemberId_throwException() { - // Given - Long invalidMemberId = 999L; - ChildCreateParam param = new ChildCreateParam("아이 닉네임", "초등학교 1학년", invalidMemberId); - - given(memberRepository.findById(invalidMemberId)).willReturn(Optional.empty()); - - // When & Then - assertThatThrownBy(() -> childService.create(param)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("잘못된 멤버 아이디입니다: " + invalidMemberId); - } - - @DisplayName("멤버에게 할당된 아이의 수가 최대치를 넘을 경우 예외를 발생시킨다") - @Test - void whenExceedingMaxChildren_throwException() { - // Given - Long memberId = 1L; - Member member = Member.of(new NickName("멤버 닉네임"), "123", MemberProvider.KAKAO, RoleType.USER); - for (int i = 0; i < Member.CHILDREN_MAX_SIZE; i++) { - member.addChild(mock(Child.class)); - } - - given(memberRepository.findById(memberId)).willReturn(Optional.of(member)); - - ChildCreateParam param = new ChildCreateParam("아이 닉네임", "초등학교 1학년", memberId); - - given(childRepository.save(any())).willReturn(new Child(param.nickname(), param.grade())); - - // When & Then - assertThatThrownBy(() -> childService.create(param)) - .isInstanceOf(IllegalStateException.class) - .hasMessageContaining(String.format("멤버당 아이는 최대 %d까지만 등록할 수 있습니다.", Member.CHILDREN_MAX_SIZE)); - } - - } - - @Nested - class FindByMemberId { - - @DisplayName("멤버의 아이들의 정보를 반환한다.") - @Test - void success() { - // Given - Long memberId = 1L; - - Member member = Member.of(new NickName("멤버 닉네임"), "123", MemberProvider.KAKAO, RoleType.USER); - - Child child1 = new Child("아이 닉네임1", "초등학교 1학년"); - Child child2 = new Child("아이 닉네임2", "초등학교 2학년"); - - Child spyChild1 = spy(child1); - spyChild1.assignToNewMemberOnly(member); - Child spyChild2 = spy(child2); - spyChild2.assignToNewMemberOnly(member); - - Long spyChild1Id = 100L; - Long spyChild2Id = 200L; - given(spyChild1.getId()).willReturn(spyChild1Id); - given(spyChild2.getId()).willReturn(spyChild2Id); - - given(memberRepository.findById(memberId)).willReturn(Optional.of(member)); - - ChildrenFindResult expectedResult = new ChildrenFindResult(List.of( - new ChildFindResult(spyChild1Id, spyChild1.getNickName(), spyChild1.getGrade(), "휴식 중!"), - new ChildFindResult(spyChild2Id, spyChild2.getNickName(), spyChild2.getGrade(), "휴식 중!") - )); - - // When - ChildrenFindResult actualResult = childService.findByMemberId(memberId); - - // Then - assertThat(actualResult).isEqualTo(expectedResult); - } - - @DisplayName("잘못된 멤버 아이디로 인한 예외를 발생시킨다.") - @Test - void givenInvalidMemberId_throwException() { - // Given - Long invalidMemberId = 999L; - - given(memberRepository.findById(invalidMemberId)).willReturn(Optional.empty()); - - // When & Then - assertThatThrownBy(() -> childService.findByMemberId(invalidMemberId)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("잘못된 멤버 아이디입니다: " + invalidMemberId); - } - - } - - @Nested - class Delete { - - @DisplayName("할당된 아이를 삭제한다.") - @Test - void deleteChild() { - // Given - Long memberId = 1L; - Long childId = 20L; - - Member member = Member.of(new NickName("멤버 닉네임"), "123", MemberProvider.KAKAO, RoleType.USER); - - Child child = new Child("아이 닉네임", "초등학교 1학년"); - - Child spyChild = spy(child); - spyChild.assignToNewMemberOnly(member); - given(spyChild.getId()).willReturn(childId); - - given(memberRepository.findById(memberId)).willReturn(Optional.of(member)); - - ChildDeleteParam param = new ChildDeleteParam(memberId, childId); - - // When - childService.delete(param); - - // Then - assertThat(member.getChildren()).isEmpty(); - } - - @DisplayName("멤버에 할당되지 않은 아이일 경우 삭제하지 않는다.") - @Test - void givenChildNotAssignedToMember_doNothing() { - Long memberId = 1L; - Long childId = 20L; - - Member member = Member.of(new NickName("멤버 닉네임"), "123", MemberProvider.KAKAO, RoleType.USER); - Member differentMember = Member.of(new NickName("다른 멤버 닉네임"), "456", MemberProvider.KAKAO, RoleType.USER); - - Child child = new Child("아이 닉네임", "초등학교 1학년"); - Child spyChild = spy(child); - spyChild.assignToNewMemberOnly(differentMember); - given(spyChild.getId()).willReturn(childId); - - given(memberRepository.findById(memberId)).willReturn(Optional.of(member)); - given(childRepository.findById(childId)).willReturn(Optional.of(spyChild)); - - ChildDeleteParam param = new ChildDeleteParam(memberId, childId); - - // When - childService.delete(param); - - // Then - assertThat(differentMember.getChildren()).containsExactly(spyChild); - } - - @DisplayName("잘못된 멤버 아이디인 경우 예외를 발생시킨다.") - @Test - void givenInvalidMemberId_throwsException() { - // Given - Long invalidMemberId = 999L; - - given(memberRepository.findById(invalidMemberId)).willReturn(Optional.empty()); - - ChildDeleteParam param = new ChildDeleteParam(invalidMemberId, 1L); - - // When & Then - Assertions.assertThatThrownBy(() -> childService.delete(param)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("잘못된 멤버 아이디입니다"); - } - } - - @Nested - class Modify { - - @DisplayName("원하는 아이의 정보를 수정한다.") - @Test - void success() { - // Given - ChildModifyParam param = new ChildModifyParam("수정할 아이 닉네임", "초등학교 2학년", 1L, 10L); - - Child mockChild = mock(Child.class); - given(mockChild.getId()).willReturn(param.childId()); - - Member mockMember = mock(Member.class); - given(mockMember.findChild(param.childId())).willReturn(Optional.of(mockChild)); - - given(memberRepository.findById(param.memberId())).willReturn(Optional.of(mockMember)); - - // When - Long updatedChildId = childService.modify(param); - - // Then - assertThat(updatedChildId).isEqualTo(param.childId()); - verify(mockChild).update(param.nickname(), param.grade()); - } - - @DisplayName("존재하지 않는 멤버라면 예외를 발생시킨다.") - @Test - void whenNonExistentMember_throwException() { - // Given - ChildModifyParam param = new ChildModifyParam("수정할 아이 닉네임", "초등학교 2학년", 1L, 10L); - - given(memberRepository.findById(param.memberId())).willReturn(Optional.empty()); - - // When & Then - assertThatThrownBy(() -> childService.modify(param)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("잘못된 멤버 아이디입니다"); - } - - @DisplayName("멤버에 존재하지 않는 아이라면 예외를 발생시킨다.") - @Test - void whenUnassignedChildId_throwsException() { - // Given - ChildModifyParam param = new ChildModifyParam("수정할 아이 닉네임", "초등학교 2학년", 1L, 10L); - - Member member = Member.of(new NickName("멤버 닉네임"), "123", MemberProvider.KAKAO, RoleType.USER); - - given(memberRepository.findById(param.memberId())).willReturn(Optional.of(member)); - - // When & Then - assertThatThrownBy(() -> childService.modify(param)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("찾을 수 없는 아이입니다"); - } - } -} diff --git a/src/test/java/org/guzzing/studayserver/domain/like/controller/LikeRestControllerTest.java b/src/test/java/org/guzzing/studayserver/domain/like/controller/LikeRestControllerTest.java deleted file mode 100644 index 0e3e01c4..00000000 --- a/src/test/java/org/guzzing/studayserver/domain/like/controller/LikeRestControllerTest.java +++ /dev/null @@ -1,192 +0,0 @@ -package org.guzzing.studayserver.domain.like.controller; - -import static org.guzzing.studayserver.testutil.fixture.TestConfig.AUTHORIZATION_HEADER; -import static org.guzzing.studayserver.testutil.fixture.TestConfig.BEARER; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.BDDMockito.given; -import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; -import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName; -import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; -import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; -import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.delete; -import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; -import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint; -import static org.springframework.restdocs.payload.JsonFieldType.ARRAY; -import static org.springframework.restdocs.payload.JsonFieldType.NUMBER; -import static org.springframework.restdocs.payload.JsonFieldType.STRING; -import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; -import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields; -import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; -import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; -import static org.springframework.restdocs.request.RequestDocumentation.pathParameters; -import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -import com.fasterxml.jackson.databind.ObjectMapper; -import org.guzzing.studayserver.domain.academy.service.AcademyAccessService; -import org.guzzing.studayserver.domain.like.controller.dto.request.LikePostRequest; -import org.guzzing.studayserver.domain.like.service.LikeService; -import org.guzzing.studayserver.domain.like.service.dto.request.LikePostParam; -import org.guzzing.studayserver.domain.like.service.dto.response.AcademyFeeInfo; -import org.guzzing.studayserver.domain.like.service.dto.response.LikePostResult; -import org.guzzing.studayserver.domain.member.service.MemberAccessService; -import org.guzzing.studayserver.testutil.WithMockCustomOAuth2LoginUser; -import org.guzzing.studayserver.testutil.fixture.TestConfig; -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.restdocs.AutoConfigureRestDocs; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.test.context.ActiveProfiles; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.ResultActions; -import org.springframework.transaction.annotation.Transactional; - -@AutoConfigureRestDocs -@AutoConfigureMockMvc -@SpringBootTest -@Transactional -@ActiveProfiles(profiles = {"dev", "oauth"}) -class LikeRestControllerTest { - - @Autowired - private MockMvc mockMvc; - - @Autowired - private ObjectMapper objectMapper; - - @Autowired - private TestConfig testConfig; - - @Autowired - private LikeService likeService; - - @MockBean - private AcademyAccessService academyAccessService; - @MockBean - private MemberAccessService memberAccessService; - - private final Long academyId = 1L; - private LikePostParam param; - - @BeforeEach - void setUp() { - Long memberId = 1L; - LikePostRequest request = new LikePostRequest(academyId); - param = LikePostRequest.to(request, memberId); - } - - @Test - @DisplayName("헤더에 JWT 로 들어오는 멤버 아이디와 바디로 전달되는 학원 아이디를 이용해서 좋아요를 등록한다.") - @WithMockCustomOAuth2LoginUser(memberId = 1L) - void createLike_MemberIdAndAcademyId_RegisterLike() throws Exception { - // Given - given(academyAccessService.existsAcademy(any())).willReturn(true); - given(memberAccessService.existsMember(any())).willReturn(true); - - LikePostRequest request = new LikePostRequest(academyId); - String jsonBody = objectMapper.writeValueAsString(request); - - // When - ResultActions perform = mockMvc.perform(post("/likes") - .header(AUTHORIZATION_HEADER, BEARER + testConfig.getJwt()) - .content(jsonBody) - .accept(APPLICATION_JSON_VALUE) - .contentType(APPLICATION_JSON_VALUE)); - - // Then - perform.andDo(print()) - .andExpect(status().isCreated()) - .andExpect(content().contentTypeCompatibleWith(APPLICATION_JSON_VALUE)) - .andExpect(jsonPath("$.likeId").isNumber()) - .andExpect(jsonPath("$.memberId").isNumber()) - .andExpect(jsonPath("$.academyId").value(1L)) - .andDo(document("post-like", - preprocessRequest(prettyPrint()), - preprocessResponse(prettyPrint()), - requestHeaders( - headerWithName("Authorization").description("JWT 토큰 (Bearer)") - ), - requestFields( - fieldWithPath("academyId").type(NUMBER).description("학원 아이디") - ), - responseFields( - fieldWithPath("likeId").type(NUMBER).description("좋아요 아이디"), - fieldWithPath("memberId").type(NUMBER).description("학원 아이디"), - fieldWithPath("academyId").type(NUMBER).description("학원 아이디") - ) - )); - } - - @Test - @DisplayName("등록한 좋아요를 제거한다.") - @WithMockCustomOAuth2LoginUser(memberId = 1L) - void removeLike_LikeId_Remove() throws Exception { - // Given - given(academyAccessService.existsAcademy(any())).willReturn(true); - given(memberAccessService.existsMember(any())).willReturn(true); - - LikePostResult likePostResult = likeService.createLikeOfAcademy(param); - - // When - ResultActions perform = mockMvc.perform(delete("/likes/{likeId}", likePostResult.likeId()) - .header(AUTHORIZATION_HEADER, BEARER + testConfig.getJwt()) - .contentType(APPLICATION_JSON_VALUE) - .accept(APPLICATION_JSON_VALUE)); - - // Then - perform.andDo(print()) - .andExpect(status().isNoContent()) - .andDo(document("delete-like", - preprocessRequest(prettyPrint()), - preprocessResponse(prettyPrint()), - pathParameters( - parameterWithName("likeId").description("좋아요 아이디") - ) - )); - } - - @Test - @DisplayName("좋아요한 학원 비용 정보를 응답받는다.") - @WithMockCustomOAuth2LoginUser(memberId = 1L) - void getAllLikes_MemberId() throws Exception { - // Given - given(academyAccessService.existsAcademy(any())).willReturn(true); - given(memberAccessService.existsMember(any())).willReturn(true); - given(academyAccessService.findAcademyFeeInfo(any())).willReturn(new AcademyFeeInfo("학원명", 100)); - - LikePostResult savedLike = likeService.createLikeOfAcademy(param); - - // When - ResultActions perform = mockMvc.perform(get("/likes") - .header(AUTHORIZATION_HEADER, BEARER + testConfig.getJwt()) - .accept(APPLICATION_JSON_VALUE) - .contentType(APPLICATION_JSON_VALUE)); - - // Then - perform.andDo(print()) - .andExpect(status().isOk()) - .andExpect(content().contentTypeCompatibleWith(APPLICATION_JSON_VALUE)) - .andExpect(jsonPath("$.likeAcademyInfos").isArray()) - .andExpect(jsonPath("$.totalFee").isNumber()) - .andDo(document("get-like", - preprocessRequest(prettyPrint()), - preprocessResponse(prettyPrint()), - responseFields( - fieldWithPath("likeAcademyInfos").type(ARRAY).description("좋아요한 학원 비용 목록"), - fieldWithPath("likeAcademyInfos[].academyName").type(STRING).description("학원명"), - fieldWithPath("likeAcademyInfos[].expectedFee").description("예상 교육비"), - fieldWithPath("totalFee").type(NUMBER).description("총 비용") - ) - )); - } - -} \ No newline at end of file diff --git a/src/test/java/org/guzzing/studayserver/domain/like/service/LikeServiceTest.java b/src/test/java/org/guzzing/studayserver/domain/like/service/LikeServiceTest.java deleted file mode 100644 index fbe5ae46..00000000 --- a/src/test/java/org/guzzing/studayserver/domain/like/service/LikeServiceTest.java +++ /dev/null @@ -1,105 +0,0 @@ -package org.guzzing.studayserver.domain.like.service; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.BDDMockito.given; - -import org.guzzing.studayserver.domain.academy.service.AcademyAccessService; -import org.guzzing.studayserver.domain.like.controller.dto.request.LikePostRequest; -import org.guzzing.studayserver.domain.like.repository.LikeRepository; -import org.guzzing.studayserver.domain.like.service.dto.request.LikePostParam; -import org.guzzing.studayserver.domain.like.service.dto.response.AcademyFeeInfo; -import org.guzzing.studayserver.domain.like.service.dto.response.LikeGetResult; -import org.guzzing.studayserver.domain.like.service.dto.response.LikePostResult; -import org.guzzing.studayserver.domain.member.service.MemberAccessService; -import org.guzzing.studayserver.testutil.WithMockCustomOAuth2LoginUser; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.test.context.ActiveProfiles; -import org.springframework.transaction.annotation.Transactional; - -@ActiveProfiles(profiles = {"dev", "oauth", "test"}) -@SpringBootTest -@Transactional -class LikeServiceTest { - - @Autowired - private LikeService likeService; - - @Autowired - private LikeRepository likeRepository; - - @MockBean - private AcademyAccessService academyAccessService; - @MockBean - private MemberAccessService memberAccessService; - - private final Long memberId = 1L; - private final Long academyId = 1L; - private LikePostParam param; - - @BeforeEach - void setUp() { - LikePostRequest request = new LikePostRequest(academyId); - param = LikePostRequest.to(request, memberId); - } - - @Test - @DisplayName("학원에 대해서 좋아요를 등록한다.") - @WithMockCustomOAuth2LoginUser(memberId = 1L) - void createLikeOfAcademy_WithMemberId() { - // Given - given(academyAccessService.existsAcademy(any())).willReturn(true); - given(memberAccessService.existsMember(any())).willReturn(true); - - // When - LikePostResult result = likeService.createLikeOfAcademy(param); - - // Then - assertThat(result.memberId()).isEqualTo(memberId); - assertThat(result.academyId()).isEqualTo(academyId); - } - - @Test - @DisplayName("학원에 대해 등록한 좋아요를 제거한다.") - @WithMockCustomOAuth2LoginUser(memberId = 1L) - void removeLikeOfAcademy_LikeId_Remove() { - // Given - given(academyAccessService.existsAcademy(any())).willReturn(true); - given(memberAccessService.existsMember(any())).willReturn(true); - - LikePostResult savedLike = likeService.createLikeOfAcademy(param); - - // When - likeService.removeLikeOfAcademy(savedLike.likeId(), memberId); - - // Then - boolean result = likeRepository.existsById(savedLike.likeId()); - - assertThat(result).isFalse(); - } - - @Test - @DisplayName("내가 좋아요한 모든 학원 비용 정보를 조회한다.") - @WithMockCustomOAuth2LoginUser(memberId = 1L) - void findAllLikesOfMember_MemberId_AcademyInfo() { - // Given - given(academyAccessService.findAcademyFeeInfo(any())).willReturn(new AcademyFeeInfo("학원명", 100)); - given(academyAccessService.existsAcademy(any())).willReturn(true); - given(memberAccessService.existsMember(any())).willReturn(true); - - LikePostResult savedLike = likeService.createLikeOfAcademy(param); - - // When - LikeGetResult result = likeService.findAllLikesOfMember(savedLike.memberId()); - - // Then - assertThat(result.likeAcademyInfos()).isNotEmpty(); - assertThat(result.totalFee()).isEqualTo(100); - } - -} \ No newline at end of file diff --git a/src/test/java/org/guzzing/studayserver/domain/member/controller/MemberRestControllerTest.java b/src/test/java/org/guzzing/studayserver/domain/member/controller/MemberRestControllerTest.java deleted file mode 100644 index ca7f3869..00000000 --- a/src/test/java/org/guzzing/studayserver/domain/member/controller/MemberRestControllerTest.java +++ /dev/null @@ -1,109 +0,0 @@ -package org.guzzing.studayserver.domain.member.controller; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.when; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -import com.fasterxml.jackson.databind.ObjectMapper; -import java.util.List; -import java.util.stream.Stream; -import org.guzzing.studayserver.domain.member.controller.request.MemberRegisterRequest; -import org.guzzing.studayserver.domain.member.controller.request.MemberRegisterRequest.MemberAdditionalChildRequest; -import org.guzzing.studayserver.domain.member.service.MemberService; -import org.guzzing.studayserver.testutil.WithMockCustomOAuth2LoginUser; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.test.web.servlet.MockMvc; - -@WebMvcTest(MemberRestController.class) -@AutoConfigureMockMvc(addFilters = false) -class MemberRestControllerTest { - - @Autowired - private MockMvc mockMvc; - - @MockBean - private MemberService memberService; - - @Autowired - private ObjectMapper objectMapper; - - @Nested - class Register { - - @DisplayName("멤버 등록시 정상 값이면 OK를 반환한다.") - @Test - @WithMockCustomOAuth2LoginUser(memberId = 1L) - void statusIsOk() throws Exception { - // Given - MemberRegisterRequest request = new MemberRegisterRequest("nickname", "email@example.com", List.of( - new MemberAdditionalChildRequest("Child1", "중학교 1학년"))); - - // When - when(memberService.register(any())).thenReturn(1L); - - // Then - mockMvc.perform(patch("/members") - .contentType(MediaType.APPLICATION_JSON_VALUE) - .content(objectMapper.writeValueAsString(request))) - .andExpect(status().is(HttpStatus.OK.value())); - } - - @DisplayName("잘못된 Request는 400에러를 반환한다.") - @ParameterizedTest - @WithMockCustomOAuth2LoginUser - @MethodSource("provideInvalidRequests") - void statusIsBadRequest(MemberRegisterRequest invalidRequest) throws Exception { - // Then - mockMvc.perform(patch("/members") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(invalidRequest))) - .andExpect(status().isBadRequest()); - } - - private static Stream provideInvalidRequests() { - return Stream.of( - // 빈 닉네임 - Arguments.of(new MemberRegisterRequest("", "email@example.com", List.of( - new MemberAdditionalChildRequest("Child1", "Grade1") - ))), - - // 잘못된 이메일 - Arguments.of(new MemberRegisterRequest("nickname", "invalid_email_format", List.of( - new MemberAdditionalChildRequest("Child1", "Grade1") - ))), - - // 5명 이상의 아이 - Arguments.of(new MemberRegisterRequest("nickname", "email@example.com", List.of( - new MemberAdditionalChildRequest("Child1", "Grade1"), - new MemberAdditionalChildRequest("Child2", "Grade2"), - new MemberAdditionalChildRequest("Child3", "Grade3"), - new MemberAdditionalChildRequest("Child4", "Grade4"), - new MemberAdditionalChildRequest("Child5", "Grade5"), - new MemberAdditionalChildRequest("Child6", "Grade6") - ))), - - // 빈 아이 닉네임 - Arguments.of(new MemberRegisterRequest("nickname", "email@example.com", List.of( - new MemberAdditionalChildRequest("", "Grade1") - ))), - - // 빈 학년 - Arguments.of(new MemberRegisterRequest("nickname", "email@example.com", List.of( - new MemberAdditionalChildRequest("Child1", "") - ))) - ); - } - } -} diff --git a/src/test/java/org/guzzing/studayserver/domain/member/model/MemberTest.java b/src/test/java/org/guzzing/studayserver/domain/member/model/MemberTest.java deleted file mode 100644 index 82d85aaf..00000000 --- a/src/test/java/org/guzzing/studayserver/domain/member/model/MemberTest.java +++ /dev/null @@ -1,112 +0,0 @@ -package org.guzzing.studayserver.domain.member.model; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.spy; - -import java.util.Optional; -import org.assertj.core.api.Assertions; -import org.guzzing.studayserver.domain.child.model.Child; -import org.guzzing.studayserver.domain.child.model.NickName; -import org.guzzing.studayserver.domain.member.model.vo.MemberProvider; -import org.guzzing.studayserver.domain.member.model.vo.RoleType; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -class MemberTest { - - private Member member; - - @BeforeEach - public void setUp() { - member = Member.of(new NickName("testNick"), "12345", MemberProvider.KAKAO, RoleType.USER); - } - - @DisplayName("업데이트 성공") - @Test - void update_success() { - // Given - String newNickName = "updatedNick"; - String newEmail = "updated@test.com"; - - // When - member.update(newNickName, newEmail); - - // Then - assertThat(member.getNickName()).isEqualTo(newNickName); - assertThat(member.getEmail()).isEqualTo(newEmail); - } - - @DisplayName("아이 추가 성공") - @Test - void addChild_success() { - // Given & When - Child child1 = new Child("child1", "중학교 1학년"); - child1.assignToNewMemberOnly(member); - - Child child2 = new Child("child2", "중학교 2학년"); - child2.assignToNewMemberOnly(member); - - // Then - Assertions.assertThat(member.getChildren()).size().isEqualTo(2); - } - - @DisplayName("아이 추가시 제한된 아이 숫자를 넘긴다면 예외를 발생시킨다.") - @Test - void addChild_failure_exceedsLimit() { - // Given - for (int i = 0; i < 5; i++) { - Child child = new Child("child1", "중학교 1학년"); - child.assignToNewMemberOnly(member); - } - - Child exceededChild = new Child("child1", "중학교 1학년"); - - // When & Then - assertThatThrownBy(() -> exceededChild.assignToNewMemberOnly(member)) - .isInstanceOf(IllegalStateException.class) - .hasMessageContaining(String.format("멤버당 아이는 최대 %d까지만 등록할 수 있습니다.", Member.CHILDREN_MAX_SIZE)); - } - - @Nested - class FindChild { - - @DisplayName("멤버에 아이가 존재한다면 반환한다.") - @Test - void success() { - // Given - Member member = new Member(new NickName("멤버 닉네임"), "123", MemberProvider.KAKAO, RoleType.USER); - - Long childId = 1L; - Child child = new Child("아이 닉네임", "초등학교 1학년"); - - Child spyChild = spy(child); - spyChild.assignToNewMemberOnly(member); - given(spyChild.getId()).willReturn(childId); - - // When - Optional actualChild = member.findChild(childId); - - // Then - assertThat(actualChild).contains(spyChild); - } - - @DisplayName("멤버에 아이가 존재하지 않는다면 empty를 반환한다.") - @Test - void whenNonExistentChildId_returnedEmpty() { - // Given - Member member = new Member(new NickName("멤버 닉네임"), "123", MemberProvider.KAKAO, RoleType.USER); - - Long nonExistentChildId = 1L; - - // When - Optional actualOptionalChild = member.findChild(nonExistentChildId); - - // Then - assertThat(actualOptionalChild).isEmpty(); - } - } -} diff --git a/src/test/java/org/guzzing/studayserver/domain/member/model/vo/EmailTest.java b/src/test/java/org/guzzing/studayserver/domain/member/model/vo/EmailTest.java deleted file mode 100644 index 52d73526..00000000 --- a/src/test/java/org/guzzing/studayserver/domain/member/model/vo/EmailTest.java +++ /dev/null @@ -1,54 +0,0 @@ -package org.guzzing.studayserver.domain.member.model.vo; - -import static org.assertj.core.api.Assertions.assertThatCode; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.NullAndEmptySource; -import org.junit.jupiter.params.provider.ValueSource; - -class EmailTest { - - @DisplayName("정상적인 이메일 형식이라면 객체를 생성한다.") - @ParameterizedTest - @ValueSource(strings = { - "test@example.com", - "firstname.lastname@example.com", - "email@subdomain.example.com", - "username+tag@example.com", - "email@example.co.jp", - "email@example.name" - }) - void create_success(String validEmail) { - // Then - assertThatCode(() -> new Email(validEmail)) - .doesNotThrowAnyException(); - } - - @DisplayName("잘못된 이메일 형식이라면 예외를 발생시킨다.") - - @ParameterizedTest - @NullAndEmptySource - @ValueSource(strings = { - " ", - "plainaddress", - "@missingusername.com", - "username@.com", - "username@.com.com", - "username@example..com", - "username@-example.com", - "username@example#.com" - }) - void create_failure_dueToInvalidFormat(String invalidEmail) { - // Then - assertThatThrownBy(() -> new Email(invalidEmail)) - .isInstanceOf(IllegalArgumentException.class); - } - -} - - - - - diff --git a/src/test/java/org/guzzing/studayserver/domain/member/service/MemberServiceTest.java b/src/test/java/org/guzzing/studayserver/domain/member/service/MemberServiceTest.java deleted file mode 100644 index 51040101..00000000 --- a/src/test/java/org/guzzing/studayserver/domain/member/service/MemberServiceTest.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.guzzing.studayserver.domain.member.service; - -import org.guzzing.studayserver.domain.member.repository.MemberRepository; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.ActiveProfiles; -import org.springframework.transaction.annotation.Transactional; - -@SpringBootTest -@Transactional -@ActiveProfiles({"dev", "oauth"}) -class MemberServiceTest { - - @Autowired - private MemberService memberService; - - @Autowired - private MemberRepository memberRepository; - -// @DisplayName("멤버 등록 성공") -// @Test -// void register_success() { -// // Given -// String memberNickname = "nickname"; -// String memberEmail = "test@email.com"; -// -// Member member = Member.of(new NickName("멤버 닉네임"), "123", MemberProvider.KAKAO, RoleType.USER); -// Member savedMember = memberRepository.save(member); -// -// MemberRegisterParam param = new MemberRegisterParam(savedMember.getId(), memberNickname, memberEmail, List.of( -// new MemberAdditionalChildParam("childNickname1", "초등학교 1학년"), -// new MemberAdditionalChildParam("childNickname2", "초등학교 3학년") -// )); -// -// // When -// memberService.register(param); -// -// // Then -// assertThat(member.getChildren()).size().isEqualTo(2); -// } -} diff --git a/src/test/java/org/guzzing/studayserver/domain/region/controller/RegionRestControllerTest.java b/src/test/java/org/guzzing/studayserver/domain/region/controller/RegionRestControllerTest.java deleted file mode 100644 index 3854ca0d..00000000 --- a/src/test/java/org/guzzing/studayserver/domain/region/controller/RegionRestControllerTest.java +++ /dev/null @@ -1,187 +0,0 @@ -package org.guzzing.studayserver.domain.region.controller; - -import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; -import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; -import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint; -import static org.springframework.restdocs.payload.JsonFieldType.ARRAY; -import static org.springframework.restdocs.payload.JsonFieldType.NUMBER; -import static org.springframework.restdocs.payload.JsonFieldType.STRING; -import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; -import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; -import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; -import static org.springframework.restdocs.request.RequestDocumentation.pathParameters; -import static org.springframework.restdocs.request.RequestDocumentation.queryParameters; -import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -import jakarta.transaction.Transactional; -import org.guzzing.studayserver.domain.region.service.RegionService; -import org.guzzing.studayserver.testutil.WithMockCustomOAuth2LoginUser; -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.restdocs.AutoConfigureRestDocs; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.ActiveProfiles; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.ResultActions; - -@AutoConfigureRestDocs -@AutoConfigureMockMvc -@SpringBootTest -@Transactional -@ActiveProfiles({"dev", "oauth"}) -class RegionRestControllerTest { - - @Autowired - private MockMvc mockMvc; - - @Autowired - private RegionService regionService; - - final String sido = "서울특별시"; - final String sigungu = "테스트구"; - final String upmyeondong = "테스트테스트동"; - final double latitude = 37.5664; - final double longitude = 126.972925; - - @BeforeEach - void setUp() { - regionService.createRegion(sido, sigungu, upmyeondong, latitude, longitude); - } - - @Test - @DisplayName("아무런 파라미터 없이 요청하면 조회 가능한 시도 데이터를 반환한다.") - @WithMockCustomOAuth2LoginUser - void getSubRegions_None_RegionResponse() throws Exception { - // Given & When - ResultActions perform = mockMvc.perform(get("/regions/beopjungdong") - .contentType(APPLICATION_JSON_VALUE) - .accept(APPLICATION_JSON_VALUE)); - - // Then - perform.andDo(print()) - .andExpect(status().isOk()) - .andExpect(content().contentTypeCompatibleWith(APPLICATION_JSON_VALUE)) - .andExpect(jsonPath("$.targetRegion").value("전국")) - .andExpect(jsonPath("$.subRegion").exists()) - .andExpect(jsonPath("$.subRegionCount").isNumber()) - .andDo(document("get-region-beopjungdong-sido", - preprocessRequest(prettyPrint()), - preprocessResponse(prettyPrint()), - responseFields( - fieldWithPath("targetRegion").type(STRING).description("전국"), - fieldWithPath("subRegion").type(ARRAY).description("탐색 가능한 시도 조회 결과 리스트"), - fieldWithPath("subRegionCount").type(NUMBER).description("탐색 가능한 시도 조회 결과 수") - ) - )); - } - - @Test - @DisplayName("시도를 파라미터로 요청하면 해당 시도, 시군구, 개수 데이터를 반환한다.") - @WithMockCustomOAuth2LoginUser - void getSubRegions_Sido_RegionResponse() throws Exception { - // Given & When - ResultActions perform = mockMvc.perform(get("/regions/beopjungdong/{sido}", sido) - .contentType(APPLICATION_JSON_VALUE) - .accept(APPLICATION_JSON_VALUE)); - - // Then - perform.andDo(print()) - .andExpect(status().isOk()) - .andExpect(content().contentTypeCompatibleWith(APPLICATION_JSON_VALUE)) - .andExpect(jsonPath("$.targetRegion").value(sido)) - .andExpect(jsonPath("$.subRegion").isNotEmpty()) - .andExpect(jsonPath("$.subRegionCount").isNumber()) - .andDo(document("get-region-beopjungdong-sigungu", - preprocessRequest(prettyPrint()), - preprocessResponse(prettyPrint()), - pathParameters( - parameterWithName("sido").description("시도") - ), - responseFields( - fieldWithPath("targetRegion").type(STRING).description("탐색한 시도명"), - fieldWithPath("subRegion").type(ARRAY).description("탐색한 시군구 조회 결과 리스트"), - fieldWithPath("subRegionCount").type(NUMBER).description("탐색한 시군구 조회 결과 수") - ) - )); - } - - @Test - @DisplayName("시도, 시군구를 요청 파라미터로 받아 해당 시도군구의 읍면동 데이터를 응답한다.") - @WithMockCustomOAuth2LoginUser - void getSubRegions_SidoAndSigungu_RegionResponse() throws Exception { - // Given & When - ResultActions perform = mockMvc.perform(get("/regions/beopjungdong/{sido}/{sigungu}", sido, sigungu) - .contentType(APPLICATION_JSON_VALUE) - .accept(APPLICATION_JSON_VALUE)); - - // Then - perform.andDo(print()) - .andExpect(status().isOk()) - .andExpect(content().contentTypeCompatibleWith(APPLICATION_JSON_VALUE)) - .andExpect(jsonPath("$.targetRegion").value(sido + " " + sigungu)) - .andExpect(jsonPath("$.subRegion").isNotEmpty()) - .andExpect(jsonPath("$.subRegionCount").isNumber()) - .andDo(document("get-region-beopjungdong-upmyeondong", - preprocessRequest(prettyPrint()), - preprocessResponse(prettyPrint()), - pathParameters( - parameterWithName("sido").description("시도"), - parameterWithName("sigungu").description("시군구") - ), - responseFields( - fieldWithPath("targetRegion").type(STRING).description("탐색한 시도군구명"), - fieldWithPath("subRegion").type(ARRAY).description("탐색한 읍면동 조회 결과 리스트"), - fieldWithPath("subRegionCount").type(NUMBER).description("탐색한 읍면동 조회 결과 수") - ) - )); - } - - @Test - @DisplayName("시도, 시군구, 읍면동 데이터를 요청받아, 해당하는 위경도 데이터를 응답한다.") - @WithMockCustomOAuth2LoginUser - void getLocation_AllAddress_RegionLocationResponse() throws Exception { - // Given & When - ResultActions perform = mockMvc.perform(get("/regions/location") - .param("sido", sido) - .param("sigungu", sigungu) - .param("upmyeondong", upmyeondong) - .contentType(APPLICATION_JSON_VALUE) - .accept(APPLICATION_JSON_VALUE)); - - // Then - perform.andDo(print()) - .andExpect(status().isOk()) - .andExpect(content().contentTypeCompatibleWith(APPLICATION_JSON_VALUE)) - .andExpect(jsonPath("$.sido").value(sido)) - .andExpect(jsonPath("$.sigungu").value(sigungu)) - .andExpect(jsonPath("$.upmyeondong").value(upmyeondong)) - .andExpect(jsonPath("$.latitude").value(latitude)) - .andExpect(jsonPath("$.longitude").value(longitude)) - .andDo(document("get-region-location", - preprocessRequest(prettyPrint()), - preprocessResponse(prettyPrint()), - queryParameters( - parameterWithName("sido").description("시도"), - parameterWithName("sigungu").description("시군구"), - parameterWithName("upmyeondong").description("읍면동") - ), - responseFields( - fieldWithPath("sido").type(STRING).description("조회된 시도"), - fieldWithPath("sigungu").type(STRING).description("조회된 시군구"), - fieldWithPath("upmyeondong").type(STRING).description("조회된 읍면동"), - fieldWithPath("latitude").type(NUMBER).description("조회된 위도"), - fieldWithPath("longitude").type(NUMBER).description("조회된 경도") - ) - )); - } - -} \ No newline at end of file diff --git a/src/test/java/org/guzzing/studayserver/domain/region/service/RegionServiceTest.java b/src/test/java/org/guzzing/studayserver/domain/region/service/RegionServiceTest.java deleted file mode 100644 index 79f20282..00000000 --- a/src/test/java/org/guzzing/studayserver/domain/region/service/RegionServiceTest.java +++ /dev/null @@ -1,87 +0,0 @@ -package org.guzzing.studayserver.domain.region.service; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.guzzing.studayserver.domain.region.service.dto.beopjungdong.SidoResult; -import org.guzzing.studayserver.domain.region.service.dto.beopjungdong.SigunguResult; -import org.guzzing.studayserver.domain.region.service.dto.beopjungdong.UpmyeondongResult; -import org.guzzing.studayserver.domain.region.service.dto.location.RegionResult; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.ActiveProfiles; -import org.springframework.transaction.annotation.Transactional; - -@SpringBootTest -@Transactional -@ActiveProfiles({"dev", "oauth"}) -class RegionServiceTest { - - @Autowired - private RegionService regionService; - - private RegionResult savedRegion; - - final String sido = "서울특별시"; - final String sigungu = "테스트구"; - final String upmyeondong = "테스트테스트동"; - - @BeforeEach - void setUp() { - final double latitude = 37.5664; - final double longitude = 126.972925; - - savedRegion = regionService.createRegion(sido, sigungu, upmyeondong, latitude, longitude); - } - - @Test - @DisplayName("시도를 받아 해당 시도의 시군구를 반환한다.") - void findSigungusBySido_Sido_SigunguResult() { - // Given & When - SigunguResult result = regionService.findSigungusBySido(sido); - - // Then - assertThat(result.sido()).isEqualTo(sido); - assertThat(result.sigunguCount()).isPositive(); - } - - @Test - @DisplayName("시도, 시군구를 받아 해당 시도군구의 읍면동을 반환한다.") - void findUpmyeondongBySidoAndSigungu_SidoAndSigungu_UpmyeondongResult() { - // Given & When - UpmyeondongResult result = regionService.findUpmyeondongBySidoAndSigungu(sido, sigungu); - - // Then - assertThat(result.sido()).isEqualTo(sido); - assertThat(result.sigungu()).isEqualTo(sigungu); - assertThat(result.upmyeondongCount()).isPositive(); - } - - @Test - @DisplayName("조회 가능한 시도를 반환한다.") - void findSido_None_SidoResult() { - // Given & When - SidoResult result = regionService.findSido(); - - // Then - assertThat(result.nation()).isEqualTo("전국"); - assertThat(result.sidos()).isNotEmpty(); - assertThat(result.sidoCount()).isEqualTo(2); - } - - @Test - @DisplayName("시도, 시군구, 읍면동 데이터를 요청받아, 해당하는 위경도를 반환한다.") - void findLocation_AllAddress_RegionResult() { - // Given & When - RegionResult result = regionService.findLocation(sido, sigungu, upmyeondong); - - // Then - assertThat(result.sido()).isEqualTo(sido); - assertThat(result.upmyeondong()).isEqualTo(upmyeondong); - assertThat(result.latitude()).isLessThanOrEqualTo(40.0); - assertThat(result.longtigute()).isLessThanOrEqualTo(130.0); - } - -} \ No newline at end of file diff --git a/src/test/java/org/guzzing/studayserver/domain/review/controller/ReviewRestControllerTest.java b/src/test/java/org/guzzing/studayserver/domain/review/controller/ReviewRestControllerTest.java deleted file mode 100644 index 2576a910..00000000 --- a/src/test/java/org/guzzing/studayserver/domain/review/controller/ReviewRestControllerTest.java +++ /dev/null @@ -1,150 +0,0 @@ -package org.guzzing.studayserver.domain.review.controller; - -import static org.guzzing.studayserver.testutil.fixture.TestConfig.AUTHORIZATION_HEADER; -import static org.guzzing.studayserver.testutil.fixture.TestConfig.BEARER; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.BDDMockito.given; -import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; -import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName; -import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; -import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; -import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; -import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint; -import static org.springframework.restdocs.payload.JsonFieldType.BOOLEAN; -import static org.springframework.restdocs.payload.JsonFieldType.NUMBER; -import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; -import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields; -import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; -import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; -import static org.springframework.restdocs.request.RequestDocumentation.queryParameters; -import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -import com.fasterxml.jackson.databind.ObjectMapper; -import org.guzzing.studayserver.domain.academy.service.AcademyAccessService; -import org.guzzing.studayserver.domain.member.service.MemberAccessService; -import org.guzzing.studayserver.domain.review.controller.dto.request.ReviewPostRequest; -import org.guzzing.studayserver.testutil.WithMockCustomOAuth2LoginUser; -import org.guzzing.studayserver.testutil.fixture.ReviewFixture; -import org.guzzing.studayserver.testutil.fixture.TestConfig; -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.restdocs.AutoConfigureRestDocs; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.test.context.ActiveProfiles; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.ResultActions; -import org.springframework.transaction.annotation.Transactional; - -@AutoConfigureMockMvc -@AutoConfigureRestDocs -@ActiveProfiles(value = {"dev", "oauth"}) -@SpringBootTest -@Transactional -class ReviewRestControllerTest { - - @Autowired - private MockMvc mockMvc; - - @Autowired - private TestConfig testConfig; - @Autowired - private ObjectMapper objectMapper; - - @MockBean - private MemberAccessService memberAccessService; - @MockBean - private AcademyAccessService academyAccessService; - - @Test - @DisplayName("리뷰 타입이 3개 이하고, 해당 학원에 대해서 등록한 학원이 없다면 리뷰를 등록한다.") - @WithMockCustomOAuth2LoginUser(memberId = 1L) - void registerReview_Success() throws Exception { - // Given - given(memberAccessService.existsMember(any())).willReturn(true); - given(academyAccessService.existsAcademy(any())).willReturn(true); - - ReviewPostRequest request = ReviewFixture.makeReviewPostRequest(true); - String jsonBody = objectMapper.writeValueAsString(request); - - // When - ResultActions perform = mockMvc.perform(post("/reviews") - .header(AUTHORIZATION_HEADER, BEARER + testConfig.getJwt()) - .content(jsonBody) - .accept(APPLICATION_JSON_VALUE) - .contentType(APPLICATION_JSON_VALUE)); - - // Then - perform.andDo(print()) - .andExpect(status().isCreated()) - .andExpect(content().contentTypeCompatibleWith(APPLICATION_JSON_VALUE)) - .andExpect(jsonPath("$.reviewId").isNumber()) - .andExpect(jsonPath("$.academyId").isNumber()) - .andDo(document("post-review", - preprocessRequest(prettyPrint()), - preprocessResponse(prettyPrint()), - requestHeaders( - headerWithName("Authorization").description("JWT 토큰 (Bearer)") - ), - requestFields( - fieldWithPath("academyId").type(NUMBER).description("학원 아이디"), - fieldWithPath("kindness").type(BOOLEAN).description("친절해요 리뷰 선택 여부"), - fieldWithPath("cheapFee").type(BOOLEAN).description("수강료가 싸요 리뷰 선택 여부"), - fieldWithPath("goodFacility").type(BOOLEAN).description("시설이 좋아요 리뷰 선택 여부"), - fieldWithPath("goodManagement").type(BOOLEAN).description("관리가 좋아요 리뷰 선택 여부"), - fieldWithPath("lovelyTeaching").type(BOOLEAN).description("가르침이 사랑스러워요 리뷰 선택 여부"), - fieldWithPath("shuttleAvailability").type(BOOLEAN).description("셔틀을 운행해요 리뷰 선택 여부") - ), - responseFields( - fieldWithPath("reviewId").type(NUMBER).description("리뷰 아이디"), - fieldWithPath("academyId").type(NUMBER).description("학원 아이디") - ) - )); - } - - @Test - @DisplayName("리뷰를 등록한 적 없다면 리뷰 등록 가능함을 응답한다.") - @WithMockCustomOAuth2LoginUser(memberId = 1L) - void getReviewable_NotExistsReview_Reviewable() throws Exception { - // Given - given(memberAccessService.existsMember(any())).willReturn(true); - given(academyAccessService.existsAcademy(any())).willReturn(true); - - // When - ResultActions perform = mockMvc.perform(get("/reviews/reviewable") - .param("academyId", String.valueOf(1L)) - .header(AUTHORIZATION_HEADER, BEARER + testConfig.getJwt()) - .accept(APPLICATION_JSON_VALUE) - .contentType(APPLICATION_JSON_VALUE)); - - // Then - perform.andDo(print()) - .andExpect(status().isOk()) - .andExpect(content().contentTypeCompatibleWith(APPLICATION_JSON_VALUE)) - .andExpect(jsonPath("$.academyId").value(1L)) - .andExpect(jsonPath("$.reviewable").value(true)) - .andDo(document("get-reviewable", - preprocessRequest(prettyPrint()), - preprocessResponse(prettyPrint()), - requestHeaders( - headerWithName("Authorization").description("JWT 토큰 (Bearer)") - ), - queryParameters( - parameterWithName("academyId").description("학원 아이디") - ), - responseFields( - fieldWithPath("academyId").type(NUMBER).description("학원 아이디"), - fieldWithPath("reviewable").type(BOOLEAN).description("리뷰 등록 가능 여부") - ) - )); - } - -} \ No newline at end of file diff --git a/src/test/java/org/guzzing/studayserver/domain/review/service/ReviewServiceTest.java b/src/test/java/org/guzzing/studayserver/domain/review/service/ReviewServiceTest.java deleted file mode 100644 index e479432a..00000000 --- a/src/test/java/org/guzzing/studayserver/domain/review/service/ReviewServiceTest.java +++ /dev/null @@ -1,134 +0,0 @@ -package org.guzzing.studayserver.domain.review.service; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.guzzing.studayserver.domain.review.model.ReviewType.CHEAP_FEE; -import static org.guzzing.studayserver.domain.review.model.ReviewType.GOOD_FACILITY; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.BDDMockito.given; - -import java.util.Map; -import org.guzzing.studayserver.domain.academy.service.AcademyAccessService; -import org.guzzing.studayserver.domain.member.service.MemberAccessService; -import org.guzzing.studayserver.domain.review.model.ReviewType; -import org.guzzing.studayserver.domain.review.service.dto.request.ReviewPostParam; -import org.guzzing.studayserver.domain.review.service.dto.response.ReviewPostResult; -import org.guzzing.studayserver.domain.review.service.dto.response.ReviewableResult; -import org.guzzing.studayserver.global.exception.ReviewException; -import org.guzzing.studayserver.testutil.WithMockCustomOAuth2LoginUser; -import org.guzzing.studayserver.testutil.fixture.ReviewFixture; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.test.context.ActiveProfiles; -import org.springframework.transaction.annotation.Transactional; - -@ActiveProfiles(value = {"dev", "oauth"}) -@SpringBootTest -@Transactional -class ReviewServiceTest { - - @Autowired - private ReviewService reviewService; - - @MockBean - private AcademyAccessService academyAccessService; - @MockBean - private MemberAccessService memberAccessService; - - @Test - @DisplayName("해당 학원에 리뷰를 남긴 적이 없으면 리뷰를 등록한다.") - @WithMockCustomOAuth2LoginUser(memberId = 1L) - void createReviewOfAcademy_NotReviewYet_RegisterReview() { - // Given - given(memberAccessService.existsMember(any())).willReturn(true); - given(academyAccessService.existsAcademy(any())).willReturn(true); - - boolean isValid = true; - ReviewPostParam param = ReviewFixture.makeReviewPostParam(isValid); - Map validReviewMap = ReviewFixture.makeValidReviewMap(); - - // When - ReviewPostResult result = reviewService.createReviewOfAcademy(param); - - // Then - assertThat(result).satisfies(entry -> { - assertThat(entry.memberId()).isEqualTo(ReviewFixture.memberId); - assertThat(entry.academyId()).isEqualTo(ReviewFixture.academyId); - assertThat(entry.cheapFee()).isEqualTo(validReviewMap.get(CHEAP_FEE)); - assertThat(entry.goodFacility()).isEqualTo(validReviewMap.get(GOOD_FACILITY)); - }); - } - - @Test - @DisplayName("해당 학원에 리뷰를 남겼다면 리뷰 등록에 실패한다.") - @WithMockCustomOAuth2LoginUser(memberId = 1L) - void createReviewOfAcademy_Reviewed_Fail() { - // Given - given(memberAccessService.existsMember(any())).willReturn(true); - given(academyAccessService.existsAcademy(any())).willReturn(true); - - boolean isValid = true; - ReviewPostParam param = ReviewFixture.makeReviewPostParam(isValid); - - reviewService.createReviewOfAcademy(param); - - // When & Then - assertThatThrownBy(() -> reviewService.createReviewOfAcademy(param)) - .isInstanceOf(ReviewException.class) - .hasMessage("이미 리뷰를 남겼습니다."); - } - - @Test - @DisplayName("리뷰를 3 항목 초과로 남겼다면 리뷰 등록에 실패한다.") - @WithMockCustomOAuth2LoginUser(memberId = 1L) - void createReviewOfAcademy_GreaterThanThreeReivewTypes_Fail() { - // Given - given(memberAccessService.existsMember(any())).willReturn(true); - given(academyAccessService.existsAcademy(any())).willReturn(true); - - boolean isValid = false; - ReviewPostParam param = ReviewFixture.makeReviewPostParam(isValid); - - // When & Then - assertThatThrownBy(() -> reviewService.createReviewOfAcademy(param)) - .isInstanceOf(ReviewException.class) - .hasMessage("리뷰는 3개까지만 가능합니다."); - } - - @Test - @DisplayName("해당 학원에 리뷰를 남긴 적 없으면 리뷰 등록 가능하다.") - @WithMockCustomOAuth2LoginUser(memberId = 1L) - void isReviewableToAcademy_NotExistsReview_Reviewable() { - // Given - given(memberAccessService.existsMember(any())).willReturn(true); - given(academyAccessService.existsAcademy(any())).willReturn(true); - - // When & Then - ReviewableResult result = reviewService.getReviewableToAcademy(100L, 100L); - - assertThat(result.reviewable()).isTrue(); - } - - @Test - @DisplayName("해당 학원에 리뷰를 남겼다면 리뷰 등록 불가하다.") - @WithMockCustomOAuth2LoginUser(memberId = 1L) - void isReviewableToAcademy_ExistsReview_NotReviewable() { - // Given - given(memberAccessService.existsMember(any())).willReturn(true); - given(academyAccessService.existsAcademy(any())).willReturn(true); - - boolean isValid = true; - ReviewPostParam param = ReviewFixture.makeReviewPostParam(isValid); - - reviewService.createReviewOfAcademy(param); - - // When & Then - ReviewableResult result = reviewService.getReviewableToAcademy(param.memberId(), param.academyId()); - - assertThat(result.reviewable()).isFalse(); - } - -} \ No newline at end of file From 6e55616d2503196f70c8ec2faec81fc19ece64fb Mon Sep 17 00:00:00 2001 From: byeolhaha Date: Thu, 9 Nov 2023 19:04:17 +0900 Subject: [PATCH 21/21] =?UTF-8?q?ReFator=20:=20=EB=A0=88=ED=8C=8C=EC=A7=80?= =?UTF-8?q?=ED=86=A0=EB=A6=AC=20=EA=B5=AC=EC=A1=B0=20=EB=B3=80=EA=B2=BD=20?= =?UTF-8?q?=EB=B0=8F=20=EC=BF=BC=EB=A6=AC=20=EC=A2=8B=EC=95=84=EC=9A=94=20?= =?UTF-8?q?=ED=85=8C=EC=9D=B4=EB=B8=94=20=EC=B0=B8=EC=A1=B0=ED=95=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/deploy.sh | 2 +- .../academy/AcademyJpaRepository.java | 2 +- .../academy/AcademyQueryRepositoryImpl.java | 12 +- .../repository/academy/AcademyRepository.java | 5 +- .../lesson/LessonJpaRepository.java | 2 +- .../repository/lesson/LessonRepository.java | 4 +- .../review/ReviewCountJpaRepository.java | 2 +- .../review/ReviewCountRepository.java | 4 +- .../domain/academy/model/LessonTest.java | 38 +++ .../domain/academy/model/vo/AddressTest.java | 19 ++ .../model/vo/academyinfo/AcademyInfoTest.java | 25 ++ .../academy/service/AcademyServiceTest.java | 190 +++++++++++ .../controller/ChildRestControllerTest.java | 177 +++++++++++ .../domain/child/model/ChildTest.java | 66 ++++ .../domain/child/model/NickNameTest.java | 43 +++ .../child/service/ChildServiceTest.java | 295 ++++++++++++++++++ .../controller/LikeRestControllerTest.java | 192 ++++++++++++ .../domain/like/service/LikeServiceTest.java | 105 +++++++ .../controller/MemberRestControllerTest.java | 109 +++++++ .../domain/member/model/MemberTest.java | 112 +++++++ .../domain/member/model/vo/EmailTest.java | 54 ++++ .../member/service/MemberServiceTest.java | 41 +++ .../controller/RegionRestControllerTest.java | 187 +++++++++++ .../region/service/RegionServiceTest.java | 87 ++++++ .../controller/ReviewRestControllerTest.java | 150 +++++++++ .../review/service/ReviewServiceTest.java | 134 ++++++++ 26 files changed, 2043 insertions(+), 14 deletions(-) create mode 100644 src/test/java/org/guzzing/studayserver/domain/academy/model/LessonTest.java create mode 100644 src/test/java/org/guzzing/studayserver/domain/academy/model/vo/AddressTest.java create mode 100644 src/test/java/org/guzzing/studayserver/domain/academy/model/vo/academyinfo/AcademyInfoTest.java create mode 100644 src/test/java/org/guzzing/studayserver/domain/academy/service/AcademyServiceTest.java create mode 100644 src/test/java/org/guzzing/studayserver/domain/child/controller/ChildRestControllerTest.java create mode 100644 src/test/java/org/guzzing/studayserver/domain/child/model/ChildTest.java create mode 100644 src/test/java/org/guzzing/studayserver/domain/child/model/NickNameTest.java create mode 100644 src/test/java/org/guzzing/studayserver/domain/child/service/ChildServiceTest.java create mode 100644 src/test/java/org/guzzing/studayserver/domain/like/controller/LikeRestControllerTest.java create mode 100644 src/test/java/org/guzzing/studayserver/domain/like/service/LikeServiceTest.java create mode 100644 src/test/java/org/guzzing/studayserver/domain/member/controller/MemberRestControllerTest.java create mode 100644 src/test/java/org/guzzing/studayserver/domain/member/model/MemberTest.java create mode 100644 src/test/java/org/guzzing/studayserver/domain/member/model/vo/EmailTest.java create mode 100644 src/test/java/org/guzzing/studayserver/domain/member/service/MemberServiceTest.java create mode 100644 src/test/java/org/guzzing/studayserver/domain/region/controller/RegionRestControllerTest.java create mode 100644 src/test/java/org/guzzing/studayserver/domain/region/service/RegionServiceTest.java create mode 100644 src/test/java/org/guzzing/studayserver/domain/review/controller/ReviewRestControllerTest.java create mode 100644 src/test/java/org/guzzing/studayserver/domain/review/service/ReviewServiceTest.java diff --git a/scripts/deploy.sh b/scripts/deploy.sh index 61d70d36..d63369fe 100644 --- a/scripts/deploy.sh +++ b/scripts/deploy.sh @@ -19,4 +19,4 @@ else fi echo "> Deploy - $JAR_PATH " -nohup java -jar -Dspring.profiles.active=prod,oauth $JAR_PATH > /dev/null 2> /dev/null < /dev/null & +nohup java -jar $JAR_PATH > /dev/null 2> /dev/null < /dev/null & diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyJpaRepository.java b/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyJpaRepository.java index 33ec3fdc..6541aaea 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyJpaRepository.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyJpaRepository.java @@ -11,7 +11,7 @@ import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; -public interface AcademyJpaRepository extends JpaRepository { +public interface AcademyJpaRepository extends JpaRepository, AcademyQueryRepository, AcademyRepository { default Academy getById(Long academyId) { return findById(academyId) diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyQueryRepositoryImpl.java b/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyQueryRepositoryImpl.java index ad59b8d5..5bbfe463 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyQueryRepositoryImpl.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyQueryRepositoryImpl.java @@ -24,10 +24,10 @@ public List findAcademiesByLocation(String pointFormat, Lon Query query = em.createNativeQuery( "SELECT a.id AS academyId, a.academy_name AS academyName, a.phone_number AS phoneNumber, a.full_address AS fullAddress," + " a.area_of_expertise AS areaOfExpertise, a.latitude AS latitude , a.longitude AS longitude, a.shuttle AS shuttleAvailable," + - " (CASE WHEN r.academy_id IS NOT NULL THEN true ELSE false END) AS isLiked " + + " (CASE WHEN l.academy_id IS NOT NULL THEN true ELSE false END) AS isLiked " + " FROM academies AS a" + - " LEFT JOIN reviews AS r" + - " ON a.id = r.academy_id AND r.member_id = " + memberId + + " LEFT JOIN likes AS l" + + " ON a.id = l.academy_id AND l.member_id = " + memberId + " WHERE MBRContains(ST_LINESTRINGFROMTEXT(" + pointFormat + ", a.point)=1"); List academies = query.unwrap(org.hibernate.query.NativeQuery.class) @@ -71,10 +71,10 @@ public List transformList(List collection) { public List filterAcademies(AcademyFilterCondition academyFilterCondition, Long memberId) { String nativeQuery = "SELECT a.id AS academyId, a.academy_name AS academyName, a.full_address AS fullAddress, " + "a.phone_number AS phoneNumber, a.area_of_expertise AS areaOfExpertise, a.latitude, a.longitude, a.shuttle AS shuttleAvailable, " + - "(CASE WHEN r.academy_id IS NOT NULL THEN true ELSE false END) AS isLiked " + + "(CASE WHEN l.academy_id IS NOT NULL THEN true ELSE false END) AS isLiked " + "FROM academies AS a " + - "LEFT JOIN reviews AS r " + - "ON a.id = r.academy_id AND r.member_id = " + memberId + + "LEFT JOIN likes AS l " + + "ON a.id = l.academy_id AND l.member_id = " + memberId + " WHERE MBRContains(ST_LINESTRINGFROMTEXT(" + academyFilterCondition.pointFormat() + ", a.point)=1 "; if (academyFilterCondition.areaOfExpertises() != null && !academyFilterCondition.areaOfExpertises().isEmpty()) { diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyRepository.java b/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyRepository.java index f052c906..634cff9b 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyRepository.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyRepository.java @@ -9,8 +9,7 @@ import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; -@Repository -public interface AcademyRepository extends AcademyJpaRepository, AcademyQueryRepository { +public interface AcademyRepository { Academy getById(Long academyId); @@ -25,4 +24,6 @@ public interface AcademyRepository extends AcademyJpaRepository, AcademyQueryRep AcademyFee findAcademyFeeInfo(Long academyId); boolean existsByAcademyId(Long academyId); + + void deleteAll(); } diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/repository/lesson/LessonJpaRepository.java b/src/main/java/org/guzzing/studayserver/domain/academy/repository/lesson/LessonJpaRepository.java index 027980b0..5cecc731 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/repository/lesson/LessonJpaRepository.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/repository/lesson/LessonJpaRepository.java @@ -5,7 +5,7 @@ import org.guzzing.studayserver.domain.academy.model.Lesson; import org.springframework.data.jpa.repository.JpaRepository; -public interface LessonJpaRepository extends JpaRepository { +public interface LessonJpaRepository extends JpaRepository, LessonRepository { List findAllByAcademyId(Long academyId); diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/repository/lesson/LessonRepository.java b/src/main/java/org/guzzing/studayserver/domain/academy/repository/lesson/LessonRepository.java index 6000c4e0..99a86b13 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/repository/lesson/LessonRepository.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/repository/lesson/LessonRepository.java @@ -4,10 +4,12 @@ import org.guzzing.studayserver.domain.academy.model.Lesson; -public interface LessonRepository extends LessonJpaRepository { +public interface LessonRepository { List findAllByAcademyId(Long academyId); Lesson save(Lesson lesson); + void deleteAll(); + } diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/repository/review/ReviewCountJpaRepository.java b/src/main/java/org/guzzing/studayserver/domain/academy/repository/review/ReviewCountJpaRepository.java index c2ee629d..2e5bb4a7 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/repository/review/ReviewCountJpaRepository.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/repository/review/ReviewCountJpaRepository.java @@ -4,7 +4,7 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; -public interface ReviewCountJpaRepository extends JpaRepository { +public interface ReviewCountJpaRepository extends JpaRepository, ReviewCountRepository { @Query("select rc from ReviewCount rc where rc.academy.id =:academyId") ReviewCount getByAcademyId(Long academyId); diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/repository/review/ReviewCountRepository.java b/src/main/java/org/guzzing/studayserver/domain/academy/repository/review/ReviewCountRepository.java index a1bb40b5..b56e3fe1 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/repository/review/ReviewCountRepository.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/repository/review/ReviewCountRepository.java @@ -2,9 +2,11 @@ import org.guzzing.studayserver.domain.academy.model.ReviewCount; -public interface ReviewCountRepository extends ReviewCountJpaRepository { +public interface ReviewCountRepository { ReviewCount getByAcademyId(Long academyId); ReviewCount save(ReviewCount reviewCount); + + void deleteAll(); } diff --git a/src/test/java/org/guzzing/studayserver/domain/academy/model/LessonTest.java b/src/test/java/org/guzzing/studayserver/domain/academy/model/LessonTest.java new file mode 100644 index 00000000..8b19bfb7 --- /dev/null +++ b/src/test/java/org/guzzing/studayserver/domain/academy/model/LessonTest.java @@ -0,0 +1,38 @@ +package org.guzzing.studayserver.domain.academy.model; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import org.guzzing.studayserver.testutil.fixture.academy.AcademyFixture; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class LessonTest { + + @Test + @DisplayName("수업을 등록할 때 학원이 없으면 예외를 던진다.") + void makeLesson_nullAcademy_throwException() { + //Then + assertThatThrownBy( + () -> Lesson.of(null, "자바와 객체지향", "자바와 객체지향으로 떠나자", "20", "1개월", "100000") + ).isInstanceOf(IllegalArgumentException.class); + } + + @Test + @DisplayName("수업 정원이 음수인 경우 예외를 던진다.") + void makeLesson_minusCapacity_throwException() { + //Then + assertThatThrownBy( + () -> Lesson.of(AcademyFixture.academySungnam(), "자바와 객체지향", "자바와 객체지향으로 떠나자", "-100", "1개월", "100000") + ).isInstanceOf(IllegalArgumentException.class); + } + + @Test + @DisplayName("강의료가 음수인 경우 예외를 던진다. ") + void makeLesson_minusTotalFee_throwException() { + //Then + assertThatThrownBy( + () -> Lesson.of(AcademyFixture.academySungnam(), "자바와 객체지향", "자바와 객체지향으로 떠나자", "20", "1개월", "-100000") + ).isInstanceOf(IllegalArgumentException.class); + } + +} diff --git a/src/test/java/org/guzzing/studayserver/domain/academy/model/vo/AddressTest.java b/src/test/java/org/guzzing/studayserver/domain/academy/model/vo/AddressTest.java new file mode 100644 index 00000000..72463c9a --- /dev/null +++ b/src/test/java/org/guzzing/studayserver/domain/academy/model/vo/AddressTest.java @@ -0,0 +1,19 @@ +package org.guzzing.studayserver.domain.academy.model.vo; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import org.guzzing.studayserver.domain.academy.model.vo.academyinfo.AcademyInfo; +import org.guzzing.studayserver.domain.academy.model.vo.academyinfo.ShuttleAvailability; +import org.junit.jupiter.api.Test; + +class AddressTest { + + @Test + void makeInvalidAddress_throwException() { + //Then + assertThatThrownBy( + () -> AcademyInfo.of("박세영 코딩학원", "123456789", ShuttleAvailability.AVAILABLE.name(), "예능(대)") + ).isInstanceOf(IllegalArgumentException.class); + } + +} diff --git a/src/test/java/org/guzzing/studayserver/domain/academy/model/vo/academyinfo/AcademyInfoTest.java b/src/test/java/org/guzzing/studayserver/domain/academy/model/vo/academyinfo/AcademyInfoTest.java new file mode 100644 index 00000000..c3d58f50 --- /dev/null +++ b/src/test/java/org/guzzing/studayserver/domain/academy/model/vo/academyinfo/AcademyInfoTest.java @@ -0,0 +1,25 @@ +package org.guzzing.studayserver.domain.academy.model.vo.academyinfo; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import org.junit.jupiter.api.Test; + +class AcademyInfoTest { + + @Test + void makeInvalidNameAcademyInfo_throwException() { + //Then + assertThatThrownBy( + () -> AcademyInfo.of("", "000-0000-0000", ShuttleAvailability.AVAILABLE.name(), "예능(대)") + ).isInstanceOf(IllegalArgumentException.class); + } + + @Test + void makeInvalidPhoneNumberAcademyInfo_throwException() { + //Then + assertThatThrownBy( + () -> AcademyInfo.of("박세영 코딩학원", "123445667", ShuttleAvailability.AVAILABLE.name(), "예능(대)") + ).isInstanceOf(IllegalArgumentException.class); + } + +} diff --git a/src/test/java/org/guzzing/studayserver/domain/academy/service/AcademyServiceTest.java b/src/test/java/org/guzzing/studayserver/domain/academy/service/AcademyServiceTest.java new file mode 100644 index 00000000..c15968bd --- /dev/null +++ b/src/test/java/org/guzzing/studayserver/domain/academy/service/AcademyServiceTest.java @@ -0,0 +1,190 @@ +package org.guzzing.studayserver.domain.academy.service; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.guzzing.studayserver.domain.academy.model.Academy; +import org.guzzing.studayserver.domain.academy.model.Lesson; +import org.guzzing.studayserver.domain.academy.model.ReviewCount; +import org.guzzing.studayserver.domain.academy.repository.academy.AcademyRepository; +import org.guzzing.studayserver.domain.academy.repository.lesson.LessonRepository; +import org.guzzing.studayserver.domain.academy.repository.review.ReviewCountRepository; +import org.guzzing.studayserver.domain.academy.service.dto.param.AcademiesByNameParam; +import org.guzzing.studayserver.domain.academy.service.dto.param.AcademyFilterParam; +import org.guzzing.studayserver.domain.academy.service.dto.result.*; +import org.guzzing.studayserver.domain.member.model.Member; +import org.guzzing.studayserver.domain.member.repository.MemberRepository; +import org.guzzing.studayserver.testutil.fixture.academy.AcademyFixture; +import org.guzzing.studayserver.testutil.fixture.member.MemberFixture; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Random; + +@Transactional +@ActiveProfiles({"oauth", "dev"}) +@SpringBootTest +class AcademyServiceTest { + + private static final String ACADEMY_NAME_FOR_SEARCH = "코딩"; + private double LATITUDE = 37.4449168; + private double LONGITUDE = 127.1388684; + + @Autowired + private AcademyService academyService; + + @Autowired + private AcademyRepository academyRepository; + + @Autowired + private LessonRepository lessonRepository; + + @Autowired + private ReviewCountRepository reviewCountRepository; + + @Autowired + private MemberRepository memberRepository; + + private Member savedMember; + + private Academy savedAcademyAboutSungnam; + + private Lesson savedALessonAboutSungnam; + + private ReviewCount savedReviewCountAboutSungnam; + + @BeforeEach + void setUp() { + + Member member = MemberFixture.member(); + savedMember = memberRepository.save(member); + + Academy academyAboutSungnam = AcademyFixture.academySungnam(); + academyAboutSungnam.changeEducationFee(100000L); + savedAcademyAboutSungnam = academyRepository.save(academyAboutSungnam); + + Lesson lessonAboutSungnam = AcademyFixture.lessonForSunganm(savedAcademyAboutSungnam); + savedALessonAboutSungnam = lessonRepository.save(lessonAboutSungnam); + + savedReviewCountAboutSungnam = reviewCountRepository.save( + AcademyFixture.reviewCountDefault(savedAcademyAboutSungnam)); + } + + @Test + @DisplayName("학원 ID로 학원 정보를 조회할 때 학원 정보, 수업 정보, 리뷰를 확인할 수 있다.") + void getAcademy_academyId_reviewsAndLessons() { + //When + AcademyGetResult academyGetResult = academyService.getAcademy(savedAcademyAboutSungnam.getId(), savedMember.getId()); + + //Then + assertThat(academyGetResult.academyName()).isEqualTo(savedAcademyAboutSungnam.getAcademyName()); + assertThat(academyGetResult.contact()).isEqualTo(savedAcademyAboutSungnam.getContact()); + assertThat(academyGetResult.fullAddress()).isEqualTo(savedAcademyAboutSungnam.getFullAddress()); + assertThat(academyGetResult.shuttleAvailability()).isEqualTo(savedAcademyAboutSungnam.getShuttleAvailability().toString()); + assertThat(academyGetResult.expectedFee()).isEqualTo(savedAcademyAboutSungnam.getMaxEducationFee()); + assertThat(academyGetResult.updatedDate()).isEqualTo(savedAcademyAboutSungnam.getUpdatedDate().toString()); + assertThat(academyGetResult.areaOfExpertise()).isEqualTo(savedAcademyAboutSungnam.getAreaOfExpertise()); + assertThat(academyGetResult.lessonGetResults().lessonGetResults()).contains( + LessonGetResult.from(savedALessonAboutSungnam)); + assertThat(academyGetResult.reviewPercentGetResult()).isEqualTo( + ReviewPercentGetResult.from(savedReviewCountAboutSungnam)); + } + + @Test + @DisplayName("사용자의 중심 위치가 주어졌을 때 반경 거리 이내의 학원 목록이 조회된다.") + void findAcademiesByLocation_academiesWithinDistance_equalsSize() { + //Given + lessonRepository.deleteAll(); + reviewCountRepository.deleteAll(); + academyRepository.deleteAll(); + + List academies = AcademyFixture.randomAcademiesWithinDistance(LATITUDE, LONGITUDE); + for (Academy academy : academies) { + Academy savedAcademy = academyRepository.save(academy); + lessonRepository.save(AcademyFixture.lessonForSunganm(savedAcademy)); + reviewCountRepository.save(AcademyFixture.reviewCountDefault(savedAcademy)); + } + + //When + AcademiesByLocationResults academiesByLocations = academyService.findAcademiesByLocation( + AcademyFixture.academiesByLocationParam(LATITUDE, LONGITUDE), + savedMember.getId()); + + //Then + assertThat(academiesByLocations.academiesByLocationResults().size()).isEqualTo(academies.size()); + } + + @Test + @DisplayName("학원 이름(ACADEMY_NAME_FOR_SEARCH)으로 검색하면 자동완성 기능으로 관련 학원들을 보여준다.") + void findAcademiesByName_academyName_relatedAcademies() { + //Given + List academies = AcademyFixture.academies(); + for (Academy academy : academies) { + Academy savedAcademy = academyRepository.save(academy); + lessonRepository.save(AcademyFixture.lessonForSunganm(savedAcademy)); + reviewCountRepository.save(AcademyFixture.reviewCountDefault(savedAcademy)); + } + + //When + AcademiesByNameResults academiesByNameResults = academyService.findAcademiesByName( + AcademiesByNameParam.of(ACADEMY_NAME_FOR_SEARCH, 0) + ); + + //Then + for (AcademiesByNameResult academiesByNameResult : academiesByNameResults.academiesByNameResults()) { + assertThat(academiesByNameResult.academyName()).contains(ACADEMY_NAME_FOR_SEARCH); + } + } + + + @Test + @DisplayName("중심 위치 반경 이내에 있는 학원 중에서 교육비가 최소와 최대 사이에 있고 선택한 학원 분류 분야에 해당하는 학원들을 반환한다.") + void filterAcademy_BetweenEducationFeeAndLocationAndInExpertise() { + //Given + lessonRepository.deleteAll(); + reviewCountRepository.deleteAll(); + academyRepository.deleteAll(); + + List academies = AcademyFixture.randomAcademiesWithinDistance(LATITUDE, LONGITUDE); + Long minFee = 10000L; + Long maxFee = 1000000L; + + for (Academy academy : academies) { + Academy savedAcademy = academyRepository.save(academy); + savedAcademy.changeEducationFee(generateRandomAmount(minFee, maxFee)); + + lessonRepository.save(AcademyFixture.lessonForSunganm(savedAcademy)); + reviewCountRepository.save(AcademyFixture.reviewCountDefault(savedAcademy)); + } + AcademyFilterParam academyFilterParam = AcademyFixture.academyFilterParam(LATITUDE, LONGITUDE, minFee, maxFee); + + //When + AcademyFilterResults academyFilterResults = academyService.filterAcademies(academyFilterParam, savedMember.getId()); + + //Then + for (AcademyFilterResult academyFilterResult : academyFilterResults.academyFilterResults()) { + Academy filtedAcademy = academyRepository.getById(academyFilterResult.academyId()); + + assertThat(filtedAcademy.getMaxEducationFee()). + isGreaterThanOrEqualTo(minFee) + .isLessThanOrEqualTo(maxFee); + assertThat(academyFilterParam.areaOfExpertises()).containsExactlyInAnyOrder(academyFilterResult.areaOfExpertise()); + } + } + + private long generateRandomAmount(long min, long max) { + if (min >= max) { + throw new IllegalArgumentException("Min value must be less than max value"); + } + + Random random = new Random(); + return min + random.nextInt((int) (max - min + 1)); + } + +} diff --git a/src/test/java/org/guzzing/studayserver/domain/child/controller/ChildRestControllerTest.java b/src/test/java/org/guzzing/studayserver/domain/child/controller/ChildRestControllerTest.java new file mode 100644 index 00000000..fea92d1c --- /dev/null +++ b/src/test/java/org/guzzing/studayserver/domain/child/controller/ChildRestControllerTest.java @@ -0,0 +1,177 @@ +package org.guzzing.studayserver.domain.child.controller; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.List; +import java.util.stream.Stream; +import org.guzzing.studayserver.domain.child.controller.request.ChildCreateRequest; +import org.guzzing.studayserver.domain.child.controller.request.ChildModifyRequest; +import org.guzzing.studayserver.domain.child.controller.response.ChildrenFindResponse; +import org.guzzing.studayserver.domain.child.service.ChildService; +import org.guzzing.studayserver.domain.child.service.result.ChildrenFindResult; +import org.guzzing.studayserver.domain.child.service.result.ChildrenFindResult.ChildFindResult; +import org.guzzing.studayserver.testutil.WithMockCustomOAuth2LoginUser; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +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; + +@WebMvcTest(ChildRestController.class) +@AutoConfigureMockMvc(addFilters = false) +class ChildRestControllerTest { + + @Autowired + private MockMvc mockMvc; + + @MockBean + private ChildService childService; + + @Autowired + private ObjectMapper objectMapper; + + @Nested + class Create { + + @DisplayName("아이 생성시 정상 값이면 OK를 반환한다.") + @Test + @WithMockCustomOAuth2LoginUser(memberId = 1L) + void statusIsOk() throws Exception { + // Given + ChildCreateRequest request = new ChildCreateRequest("childName1", "초등학교 1학년"); + Long expectedChildId = 2L; + + // When + when(childService.create(any())).thenReturn(expectedChildId); + + // Then + mockMvc.perform(post("/children") + .contentType(MediaType.APPLICATION_JSON_VALUE) + .content(objectMapper.writeValueAsString(request))) + .andExpect(status().isOk()); + } + + @DisplayName("아이 생성시 잘못된 요청은 400에러를 반환한다.") + @ParameterizedTest + @WithMockCustomOAuth2LoginUser(memberId = 1L) + @MethodSource("provideInvalidRequests") + void statusIsBadRequest(ChildCreateRequest invalidRequest) throws Exception { + // Then + mockMvc.perform(post("/children") + .contentType(MediaType.APPLICATION_JSON_VALUE) + .content(objectMapper.writeValueAsString(invalidRequest))) + .andExpect(status().isBadRequest()); + } + + + private static Stream provideInvalidRequests() { + return Stream.of( + // 빈 닉네임 + Arguments.of(new ChildCreateRequest("", "초등학교 1학년")), + + // 긴 닉네임 + Arguments.of(new ChildCreateRequest("a".repeat(11), "초등학교 1학년")), + + // 빈 학년 + Arguments.of(new ChildCreateRequest("a".repeat(11), "")) + ); + } + } + + @DisplayName("멤버에 할당된 아이들의 정보를 반환한다.") + @WithMockCustomOAuth2LoginUser(memberId = 1L) + @Test + void findChildren_success() throws Exception { + // Given + ChildrenFindResult result = new ChildrenFindResult(List.of( + new ChildFindResult(1L, "Nickname1", "초등학교 1학년", "휴식 중!"), + new ChildFindResult(2L, "Nickname2", "초등학교 2학년", "휴식 중!") + )); + + given(childService.findByMemberId(1L)).willReturn(result); + + ChildrenFindResponse response = ChildrenFindResponse.from(result); + + // When & Then + mockMvc.perform(get("/children") + .contentType(MediaType.APPLICATION_JSON_VALUE) + .content(objectMapper.writeValueAsString(response))) + .andExpect(status().isOk()); + } + + @DisplayName("아이를 삭제한다.") + @WithMockCustomOAuth2LoginUser() + @Test + void delete_success() throws Exception { + // Given + Long existingChildId = 200L; + + // When & Then + mockMvc.perform(delete("/children/{childId}", existingChildId)) + .andExpect(status().isNoContent()); + } + + @Nested + class modify { + + @DisplayName("아이의 정보를 수정한다.") + @WithMockCustomOAuth2LoginUser(memberId = 1L) + @Test + void success() throws Exception { + // Given + Long childId = 100L; + + ChildModifyRequest request = new ChildModifyRequest("아이 닉네임", "초등학교 1학년"); + + given(childService.modify(any())).willReturn(childId); + + // Then + mockMvc.perform(patch("/children/{childId}", childId) + .contentType(MediaType.APPLICATION_JSON_VALUE) + .content(objectMapper.writeValueAsString(request))) + .andExpect(status().isOk()) + .andExpect(content().string(childId.toString())); + } + + @DisplayName("잘못된 요청이 들어오면 예외를 발생시킨다.") + @WithMockCustomOAuth2LoginUser() + @ParameterizedTest + @MethodSource("provideInvalidRequests") + void givenInvalidRequest_throwsException(ChildModifyRequest invalidRequest) throws Exception { + // When & Then + mockMvc.perform(patch("/children/{childId}", 100L) + .contentType(MediaType.APPLICATION_JSON_VALUE) + .content(objectMapper.writeValueAsString(invalidRequest))) + .andExpect(status().isBadRequest()); + } + + private static Stream provideInvalidRequests() { + return Stream.of( + // 빈 닉네임 + Arguments.of(new ChildModifyRequest("", "초등학교 1학년")), + + // 긴 닉네임 + Arguments.of(new ChildModifyRequest("a".repeat(11), "초등학교 1학년")), + + // 빈 학년 + Arguments.of(new ChildModifyRequest("a".repeat(11), "")) + ); + } + } +} diff --git a/src/test/java/org/guzzing/studayserver/domain/child/model/ChildTest.java b/src/test/java/org/guzzing/studayserver/domain/child/model/ChildTest.java new file mode 100644 index 00000000..b893e795 --- /dev/null +++ b/src/test/java/org/guzzing/studayserver/domain/child/model/ChildTest.java @@ -0,0 +1,66 @@ +package org.guzzing.studayserver.domain.child.model; + +import static org.assertj.core.api.Assertions.assertThatCode; + +import org.assertj.core.api.Assertions; +import org.guzzing.studayserver.domain.member.model.Member; +import org.guzzing.studayserver.domain.member.model.vo.MemberProvider; +import org.guzzing.studayserver.domain.member.model.vo.RoleType; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +class ChildTest { + + + @Nested + class AssignToNewMemberOnly { + + private Child child; + private Member member; + + @BeforeEach + void setUp() { + child = new Child("아이 닉네임", "초등학교 1학년"); + member = Member.of(new NickName("멤버 닉네임"), "123", MemberProvider.KAKAO, RoleType.USER); + } + + @DisplayName("멤버가 없는 아이에게 새 멤버를 할당한다.") + @Test + void whenChildHasNoMember_assignsMember() { + // When & Then + assertThatCode(() -> child.assignToNewMemberOnly(member)) + .doesNotThrowAnyException(); + } + + @DisplayName("이미 멤버가 할당된 아이에게 새 멤버를 할당하려고 하면 예외를 던진다.") + @Test + void givenAssignToNewMemberOnly_WhenChildAlreadyHasMember_ThrowsException() { + // Given + child.assignToNewMemberOnly(member); + + // 실행 & 검증 + Assertions.assertThatThrownBy(() -> child.assignToNewMemberOnly(member)) + .isInstanceOf(IllegalStateException.class) + .hasMessageContaining("이미 멤버가 할당되어 있습니다."); + } + } + + @DisplayName("아이의 정보를 수정한다.") + @Test + void update_success() { + // Given + Child child = new Child("아이 닉네임", "초등학교 1학년"); + + String updatedNickname = "닉네임 수정"; + String updatedGrade = "초등학교 2학년"; + + // When + child.update(updatedNickname, updatedGrade); + + // Then + Assertions.assertThat(child.getNickName()).isEqualTo(updatedNickname); + Assertions.assertThat(child.getGrade()).isEqualTo(updatedGrade); + } +} diff --git a/src/test/java/org/guzzing/studayserver/domain/child/model/NickNameTest.java b/src/test/java/org/guzzing/studayserver/domain/child/model/NickNameTest.java new file mode 100644 index 00000000..3d0384e8 --- /dev/null +++ b/src/test/java/org/guzzing/studayserver/domain/child/model/NickNameTest.java @@ -0,0 +1,43 @@ +package org.guzzing.studayserver.domain.child.model; + +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.util.stream.Stream; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; + +class NickNameTest { + + @DisplayName("정상 값이라면 객체를 생성한다.") + @ParameterizedTest + @ValueSource(strings = { + "TestNickName", + "AnotherValidName", + "YetAnotherValidName" + }) + void create_success(String nickname) { + // Then + assertThatCode(() -> new NickName(nickname)) + .doesNotThrowAnyException(); + } + + @DisplayName("잘못된 값이라면 예외를 발생시킨다.") + @ParameterizedTest + @MethodSource("provideInvalidNicknames") + void create_failure_throwException(String invalidNickname) { + // Then + assertThatThrownBy(() -> new NickName(invalidNickname)) + .isInstanceOf(IllegalArgumentException.class); + } + + private static Stream provideInvalidNicknames() { + return Stream.of( + null, + " ", + "A".repeat(NickName.NAME_MAX_LENGTH + 1) + ); + } +} diff --git a/src/test/java/org/guzzing/studayserver/domain/child/service/ChildServiceTest.java b/src/test/java/org/guzzing/studayserver/domain/child/service/ChildServiceTest.java new file mode 100644 index 00000000..be13d90c --- /dev/null +++ b/src/test/java/org/guzzing/studayserver/domain/child/service/ChildServiceTest.java @@ -0,0 +1,295 @@ +package org.guzzing.studayserver.domain.child.service; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +import java.util.List; +import java.util.Optional; +import org.assertj.core.api.Assertions; +import org.guzzing.studayserver.domain.child.model.Child; +import org.guzzing.studayserver.domain.child.model.NickName; +import org.guzzing.studayserver.domain.child.repository.ChildRepository; +import org.guzzing.studayserver.domain.child.service.param.ChildCreateParam; +import org.guzzing.studayserver.domain.child.service.param.ChildDeleteParam; +import org.guzzing.studayserver.domain.child.service.param.ChildModifyParam; +import org.guzzing.studayserver.domain.child.service.result.ChildrenFindResult; +import org.guzzing.studayserver.domain.child.service.result.ChildrenFindResult.ChildFindResult; +import org.guzzing.studayserver.domain.member.model.Member; +import org.guzzing.studayserver.domain.member.model.vo.MemberProvider; +import org.guzzing.studayserver.domain.member.model.vo.RoleType; +import org.guzzing.studayserver.domain.member.repository.MemberRepository; +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.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.transaction.annotation.Transactional; + +@SpringBootTest +@Transactional +@ActiveProfiles({"dev", "oauth"}) +class ChildServiceTest { + + @Autowired + private ChildService childService; + + @MockBean + private ChildRepository childRepository; + + @MockBean + private MemberRepository memberRepository; + + @Nested + class Create { + + @DisplayName("아이 생성 성공") + @Test + void success() { + // Given + Long memberId = 1L; + ChildCreateParam param = new ChildCreateParam("아이 닉네임", "초등학교 1학년", memberId); + Long expectedChildId = 2L; + + Member mockMember = mock(Member.class); + Child mockChild = mock(Child.class); + + given(mockChild.getId()).willReturn(expectedChildId); + given(memberRepository.findById(memberId)).willReturn(Optional.ofNullable(mockMember)); + given(childRepository.save(any(Child.class))).willReturn(mockChild); + + // When + Long savedChildId = childService.create(param); + + // Then + assertThat(savedChildId).isEqualTo(expectedChildId); + verify(childRepository).save(any(Child.class)); + } + + @DisplayName("잘못된 멤버 아이디로 인한 예외를 발생시킨다.") + @Test + void givenInvalidMemberId_throwException() { + // Given + Long invalidMemberId = 999L; + ChildCreateParam param = new ChildCreateParam("아이 닉네임", "초등학교 1학년", invalidMemberId); + + given(memberRepository.findById(invalidMemberId)).willReturn(Optional.empty()); + + // When & Then + assertThatThrownBy(() -> childService.create(param)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("잘못된 멤버 아이디입니다: " + invalidMemberId); + } + + @DisplayName("멤버에게 할당된 아이의 수가 최대치를 넘을 경우 예외를 발생시킨다") + @Test + void whenExceedingMaxChildren_throwException() { + // Given + Long memberId = 1L; + Member member = Member.of(new NickName("멤버 닉네임"), "123", MemberProvider.KAKAO, RoleType.USER); + for (int i = 0; i < Member.CHILDREN_MAX_SIZE; i++) { + member.addChild(mock(Child.class)); + } + + given(memberRepository.findById(memberId)).willReturn(Optional.of(member)); + + ChildCreateParam param = new ChildCreateParam("아이 닉네임", "초등학교 1학년", memberId); + + given(childRepository.save(any())).willReturn(new Child(param.nickname(), param.grade())); + + // When & Then + assertThatThrownBy(() -> childService.create(param)) + .isInstanceOf(IllegalStateException.class) + .hasMessageContaining(String.format("멤버당 아이는 최대 %d까지만 등록할 수 있습니다.", Member.CHILDREN_MAX_SIZE)); + } + + } + + @Nested + class FindByMemberId { + + @DisplayName("멤버의 아이들의 정보를 반환한다.") + @Test + void success() { + // Given + Long memberId = 1L; + + Member member = Member.of(new NickName("멤버 닉네임"), "123", MemberProvider.KAKAO, RoleType.USER); + + Child child1 = new Child("아이 닉네임1", "초등학교 1학년"); + Child child2 = new Child("아이 닉네임2", "초등학교 2학년"); + + Child spyChild1 = spy(child1); + spyChild1.assignToNewMemberOnly(member); + Child spyChild2 = spy(child2); + spyChild2.assignToNewMemberOnly(member); + + Long spyChild1Id = 100L; + Long spyChild2Id = 200L; + given(spyChild1.getId()).willReturn(spyChild1Id); + given(spyChild2.getId()).willReturn(spyChild2Id); + + given(memberRepository.findById(memberId)).willReturn(Optional.of(member)); + + ChildrenFindResult expectedResult = new ChildrenFindResult(List.of( + new ChildFindResult(spyChild1Id, spyChild1.getNickName(), spyChild1.getGrade(), "휴식 중!"), + new ChildFindResult(spyChild2Id, spyChild2.getNickName(), spyChild2.getGrade(), "휴식 중!") + )); + + // When + ChildrenFindResult actualResult = childService.findByMemberId(memberId); + + // Then + assertThat(actualResult).isEqualTo(expectedResult); + } + + @DisplayName("잘못된 멤버 아이디로 인한 예외를 발생시킨다.") + @Test + void givenInvalidMemberId_throwException() { + // Given + Long invalidMemberId = 999L; + + given(memberRepository.findById(invalidMemberId)).willReturn(Optional.empty()); + + // When & Then + assertThatThrownBy(() -> childService.findByMemberId(invalidMemberId)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("잘못된 멤버 아이디입니다: " + invalidMemberId); + } + + } + + @Nested + class Delete { + + @DisplayName("할당된 아이를 삭제한다.") + @Test + void deleteChild() { + // Given + Long memberId = 1L; + Long childId = 20L; + + Member member = Member.of(new NickName("멤버 닉네임"), "123", MemberProvider.KAKAO, RoleType.USER); + + Child child = new Child("아이 닉네임", "초등학교 1학년"); + + Child spyChild = spy(child); + spyChild.assignToNewMemberOnly(member); + given(spyChild.getId()).willReturn(childId); + + given(memberRepository.findById(memberId)).willReturn(Optional.of(member)); + + ChildDeleteParam param = new ChildDeleteParam(memberId, childId); + + // When + childService.delete(param); + + // Then + assertThat(member.getChildren()).isEmpty(); + } + + @DisplayName("멤버에 할당되지 않은 아이일 경우 삭제하지 않는다.") + @Test + void givenChildNotAssignedToMember_doNothing() { + Long memberId = 1L; + Long childId = 20L; + + Member member = Member.of(new NickName("멤버 닉네임"), "123", MemberProvider.KAKAO, RoleType.USER); + Member differentMember = Member.of(new NickName("다른 멤버 닉네임"), "456", MemberProvider.KAKAO, RoleType.USER); + + Child child = new Child("아이 닉네임", "초등학교 1학년"); + Child spyChild = spy(child); + spyChild.assignToNewMemberOnly(differentMember); + given(spyChild.getId()).willReturn(childId); + + given(memberRepository.findById(memberId)).willReturn(Optional.of(member)); + given(childRepository.findById(childId)).willReturn(Optional.of(spyChild)); + + ChildDeleteParam param = new ChildDeleteParam(memberId, childId); + + // When + childService.delete(param); + + // Then + assertThat(differentMember.getChildren()).containsExactly(spyChild); + } + + @DisplayName("잘못된 멤버 아이디인 경우 예외를 발생시킨다.") + @Test + void givenInvalidMemberId_throwsException() { + // Given + Long invalidMemberId = 999L; + + given(memberRepository.findById(invalidMemberId)).willReturn(Optional.empty()); + + ChildDeleteParam param = new ChildDeleteParam(invalidMemberId, 1L); + + // When & Then + Assertions.assertThatThrownBy(() -> childService.delete(param)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("잘못된 멤버 아이디입니다"); + } + } + + @Nested + class Modify { + + @DisplayName("원하는 아이의 정보를 수정한다.") + @Test + void success() { + // Given + ChildModifyParam param = new ChildModifyParam("수정할 아이 닉네임", "초등학교 2학년", 1L, 10L); + + Child mockChild = mock(Child.class); + given(mockChild.getId()).willReturn(param.childId()); + + Member mockMember = mock(Member.class); + given(mockMember.findChild(param.childId())).willReturn(Optional.of(mockChild)); + + given(memberRepository.findById(param.memberId())).willReturn(Optional.of(mockMember)); + + // When + Long updatedChildId = childService.modify(param); + + // Then + assertThat(updatedChildId).isEqualTo(param.childId()); + verify(mockChild).update(param.nickname(), param.grade()); + } + + @DisplayName("존재하지 않는 멤버라면 예외를 발생시킨다.") + @Test + void whenNonExistentMember_throwException() { + // Given + ChildModifyParam param = new ChildModifyParam("수정할 아이 닉네임", "초등학교 2학년", 1L, 10L); + + given(memberRepository.findById(param.memberId())).willReturn(Optional.empty()); + + // When & Then + assertThatThrownBy(() -> childService.modify(param)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("잘못된 멤버 아이디입니다"); + } + + @DisplayName("멤버에 존재하지 않는 아이라면 예외를 발생시킨다.") + @Test + void whenUnassignedChildId_throwsException() { + // Given + ChildModifyParam param = new ChildModifyParam("수정할 아이 닉네임", "초등학교 2학년", 1L, 10L); + + Member member = Member.of(new NickName("멤버 닉네임"), "123", MemberProvider.KAKAO, RoleType.USER); + + given(memberRepository.findById(param.memberId())).willReturn(Optional.of(member)); + + // When & Then + assertThatThrownBy(() -> childService.modify(param)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("찾을 수 없는 아이입니다"); + } + } +} diff --git a/src/test/java/org/guzzing/studayserver/domain/like/controller/LikeRestControllerTest.java b/src/test/java/org/guzzing/studayserver/domain/like/controller/LikeRestControllerTest.java new file mode 100644 index 00000000..0e3e01c4 --- /dev/null +++ b/src/test/java/org/guzzing/studayserver/domain/like/controller/LikeRestControllerTest.java @@ -0,0 +1,192 @@ +package org.guzzing.studayserver.domain.like.controller; + +import static org.guzzing.studayserver.testutil.fixture.TestConfig.AUTHORIZATION_HEADER; +import static org.guzzing.studayserver.testutil.fixture.TestConfig.BEARER; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; +import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName; +import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.delete; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint; +import static org.springframework.restdocs.payload.JsonFieldType.ARRAY; +import static org.springframework.restdocs.payload.JsonFieldType.NUMBER; +import static org.springframework.restdocs.payload.JsonFieldType.STRING; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; +import static org.springframework.restdocs.request.RequestDocumentation.pathParameters; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.guzzing.studayserver.domain.academy.service.AcademyAccessService; +import org.guzzing.studayserver.domain.like.controller.dto.request.LikePostRequest; +import org.guzzing.studayserver.domain.like.service.LikeService; +import org.guzzing.studayserver.domain.like.service.dto.request.LikePostParam; +import org.guzzing.studayserver.domain.like.service.dto.response.AcademyFeeInfo; +import org.guzzing.studayserver.domain.like.service.dto.response.LikePostResult; +import org.guzzing.studayserver.domain.member.service.MemberAccessService; +import org.guzzing.studayserver.testutil.WithMockCustomOAuth2LoginUser; +import org.guzzing.studayserver.testutil.fixture.TestConfig; +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.restdocs.AutoConfigureRestDocs; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.transaction.annotation.Transactional; + +@AutoConfigureRestDocs +@AutoConfigureMockMvc +@SpringBootTest +@Transactional +@ActiveProfiles(profiles = {"dev", "oauth"}) +class LikeRestControllerTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private ObjectMapper objectMapper; + + @Autowired + private TestConfig testConfig; + + @Autowired + private LikeService likeService; + + @MockBean + private AcademyAccessService academyAccessService; + @MockBean + private MemberAccessService memberAccessService; + + private final Long academyId = 1L; + private LikePostParam param; + + @BeforeEach + void setUp() { + Long memberId = 1L; + LikePostRequest request = new LikePostRequest(academyId); + param = LikePostRequest.to(request, memberId); + } + + @Test + @DisplayName("헤더에 JWT 로 들어오는 멤버 아이디와 바디로 전달되는 학원 아이디를 이용해서 좋아요를 등록한다.") + @WithMockCustomOAuth2LoginUser(memberId = 1L) + void createLike_MemberIdAndAcademyId_RegisterLike() throws Exception { + // Given + given(academyAccessService.existsAcademy(any())).willReturn(true); + given(memberAccessService.existsMember(any())).willReturn(true); + + LikePostRequest request = new LikePostRequest(academyId); + String jsonBody = objectMapper.writeValueAsString(request); + + // When + ResultActions perform = mockMvc.perform(post("/likes") + .header(AUTHORIZATION_HEADER, BEARER + testConfig.getJwt()) + .content(jsonBody) + .accept(APPLICATION_JSON_VALUE) + .contentType(APPLICATION_JSON_VALUE)); + + // Then + perform.andDo(print()) + .andExpect(status().isCreated()) + .andExpect(content().contentTypeCompatibleWith(APPLICATION_JSON_VALUE)) + .andExpect(jsonPath("$.likeId").isNumber()) + .andExpect(jsonPath("$.memberId").isNumber()) + .andExpect(jsonPath("$.academyId").value(1L)) + .andDo(document("post-like", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + requestHeaders( + headerWithName("Authorization").description("JWT 토큰 (Bearer)") + ), + requestFields( + fieldWithPath("academyId").type(NUMBER).description("학원 아이디") + ), + responseFields( + fieldWithPath("likeId").type(NUMBER).description("좋아요 아이디"), + fieldWithPath("memberId").type(NUMBER).description("학원 아이디"), + fieldWithPath("academyId").type(NUMBER).description("학원 아이디") + ) + )); + } + + @Test + @DisplayName("등록한 좋아요를 제거한다.") + @WithMockCustomOAuth2LoginUser(memberId = 1L) + void removeLike_LikeId_Remove() throws Exception { + // Given + given(academyAccessService.existsAcademy(any())).willReturn(true); + given(memberAccessService.existsMember(any())).willReturn(true); + + LikePostResult likePostResult = likeService.createLikeOfAcademy(param); + + // When + ResultActions perform = mockMvc.perform(delete("/likes/{likeId}", likePostResult.likeId()) + .header(AUTHORIZATION_HEADER, BEARER + testConfig.getJwt()) + .contentType(APPLICATION_JSON_VALUE) + .accept(APPLICATION_JSON_VALUE)); + + // Then + perform.andDo(print()) + .andExpect(status().isNoContent()) + .andDo(document("delete-like", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + pathParameters( + parameterWithName("likeId").description("좋아요 아이디") + ) + )); + } + + @Test + @DisplayName("좋아요한 학원 비용 정보를 응답받는다.") + @WithMockCustomOAuth2LoginUser(memberId = 1L) + void getAllLikes_MemberId() throws Exception { + // Given + given(academyAccessService.existsAcademy(any())).willReturn(true); + given(memberAccessService.existsMember(any())).willReturn(true); + given(academyAccessService.findAcademyFeeInfo(any())).willReturn(new AcademyFeeInfo("학원명", 100)); + + LikePostResult savedLike = likeService.createLikeOfAcademy(param); + + // When + ResultActions perform = mockMvc.perform(get("/likes") + .header(AUTHORIZATION_HEADER, BEARER + testConfig.getJwt()) + .accept(APPLICATION_JSON_VALUE) + .contentType(APPLICATION_JSON_VALUE)); + + // Then + perform.andDo(print()) + .andExpect(status().isOk()) + .andExpect(content().contentTypeCompatibleWith(APPLICATION_JSON_VALUE)) + .andExpect(jsonPath("$.likeAcademyInfos").isArray()) + .andExpect(jsonPath("$.totalFee").isNumber()) + .andDo(document("get-like", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + responseFields( + fieldWithPath("likeAcademyInfos").type(ARRAY).description("좋아요한 학원 비용 목록"), + fieldWithPath("likeAcademyInfos[].academyName").type(STRING).description("학원명"), + fieldWithPath("likeAcademyInfos[].expectedFee").description("예상 교육비"), + fieldWithPath("totalFee").type(NUMBER).description("총 비용") + ) + )); + } + +} \ No newline at end of file diff --git a/src/test/java/org/guzzing/studayserver/domain/like/service/LikeServiceTest.java b/src/test/java/org/guzzing/studayserver/domain/like/service/LikeServiceTest.java new file mode 100644 index 00000000..fbe5ae46 --- /dev/null +++ b/src/test/java/org/guzzing/studayserver/domain/like/service/LikeServiceTest.java @@ -0,0 +1,105 @@ +package org.guzzing.studayserver.domain.like.service; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; + +import org.guzzing.studayserver.domain.academy.service.AcademyAccessService; +import org.guzzing.studayserver.domain.like.controller.dto.request.LikePostRequest; +import org.guzzing.studayserver.domain.like.repository.LikeRepository; +import org.guzzing.studayserver.domain.like.service.dto.request.LikePostParam; +import org.guzzing.studayserver.domain.like.service.dto.response.AcademyFeeInfo; +import org.guzzing.studayserver.domain.like.service.dto.response.LikeGetResult; +import org.guzzing.studayserver.domain.like.service.dto.response.LikePostResult; +import org.guzzing.studayserver.domain.member.service.MemberAccessService; +import org.guzzing.studayserver.testutil.WithMockCustomOAuth2LoginUser; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.transaction.annotation.Transactional; + +@ActiveProfiles(profiles = {"dev", "oauth", "test"}) +@SpringBootTest +@Transactional +class LikeServiceTest { + + @Autowired + private LikeService likeService; + + @Autowired + private LikeRepository likeRepository; + + @MockBean + private AcademyAccessService academyAccessService; + @MockBean + private MemberAccessService memberAccessService; + + private final Long memberId = 1L; + private final Long academyId = 1L; + private LikePostParam param; + + @BeforeEach + void setUp() { + LikePostRequest request = new LikePostRequest(academyId); + param = LikePostRequest.to(request, memberId); + } + + @Test + @DisplayName("학원에 대해서 좋아요를 등록한다.") + @WithMockCustomOAuth2LoginUser(memberId = 1L) + void createLikeOfAcademy_WithMemberId() { + // Given + given(academyAccessService.existsAcademy(any())).willReturn(true); + given(memberAccessService.existsMember(any())).willReturn(true); + + // When + LikePostResult result = likeService.createLikeOfAcademy(param); + + // Then + assertThat(result.memberId()).isEqualTo(memberId); + assertThat(result.academyId()).isEqualTo(academyId); + } + + @Test + @DisplayName("학원에 대해 등록한 좋아요를 제거한다.") + @WithMockCustomOAuth2LoginUser(memberId = 1L) + void removeLikeOfAcademy_LikeId_Remove() { + // Given + given(academyAccessService.existsAcademy(any())).willReturn(true); + given(memberAccessService.existsMember(any())).willReturn(true); + + LikePostResult savedLike = likeService.createLikeOfAcademy(param); + + // When + likeService.removeLikeOfAcademy(savedLike.likeId(), memberId); + + // Then + boolean result = likeRepository.existsById(savedLike.likeId()); + + assertThat(result).isFalse(); + } + + @Test + @DisplayName("내가 좋아요한 모든 학원 비용 정보를 조회한다.") + @WithMockCustomOAuth2LoginUser(memberId = 1L) + void findAllLikesOfMember_MemberId_AcademyInfo() { + // Given + given(academyAccessService.findAcademyFeeInfo(any())).willReturn(new AcademyFeeInfo("학원명", 100)); + given(academyAccessService.existsAcademy(any())).willReturn(true); + given(memberAccessService.existsMember(any())).willReturn(true); + + LikePostResult savedLike = likeService.createLikeOfAcademy(param); + + // When + LikeGetResult result = likeService.findAllLikesOfMember(savedLike.memberId()); + + // Then + assertThat(result.likeAcademyInfos()).isNotEmpty(); + assertThat(result.totalFee()).isEqualTo(100); + } + +} \ No newline at end of file diff --git a/src/test/java/org/guzzing/studayserver/domain/member/controller/MemberRestControllerTest.java b/src/test/java/org/guzzing/studayserver/domain/member/controller/MemberRestControllerTest.java new file mode 100644 index 00000000..ca7f3869 --- /dev/null +++ b/src/test/java/org/guzzing/studayserver/domain/member/controller/MemberRestControllerTest.java @@ -0,0 +1,109 @@ +package org.guzzing.studayserver.domain.member.controller; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.List; +import java.util.stream.Stream; +import org.guzzing.studayserver.domain.member.controller.request.MemberRegisterRequest; +import org.guzzing.studayserver.domain.member.controller.request.MemberRegisterRequest.MemberAdditionalChildRequest; +import org.guzzing.studayserver.domain.member.service.MemberService; +import org.guzzing.studayserver.testutil.WithMockCustomOAuth2LoginUser; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; + +@WebMvcTest(MemberRestController.class) +@AutoConfigureMockMvc(addFilters = false) +class MemberRestControllerTest { + + @Autowired + private MockMvc mockMvc; + + @MockBean + private MemberService memberService; + + @Autowired + private ObjectMapper objectMapper; + + @Nested + class Register { + + @DisplayName("멤버 등록시 정상 값이면 OK를 반환한다.") + @Test + @WithMockCustomOAuth2LoginUser(memberId = 1L) + void statusIsOk() throws Exception { + // Given + MemberRegisterRequest request = new MemberRegisterRequest("nickname", "email@example.com", List.of( + new MemberAdditionalChildRequest("Child1", "중학교 1학년"))); + + // When + when(memberService.register(any())).thenReturn(1L); + + // Then + mockMvc.perform(patch("/members") + .contentType(MediaType.APPLICATION_JSON_VALUE) + .content(objectMapper.writeValueAsString(request))) + .andExpect(status().is(HttpStatus.OK.value())); + } + + @DisplayName("잘못된 Request는 400에러를 반환한다.") + @ParameterizedTest + @WithMockCustomOAuth2LoginUser + @MethodSource("provideInvalidRequests") + void statusIsBadRequest(MemberRegisterRequest invalidRequest) throws Exception { + // Then + mockMvc.perform(patch("/members") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(invalidRequest))) + .andExpect(status().isBadRequest()); + } + + private static Stream provideInvalidRequests() { + return Stream.of( + // 빈 닉네임 + Arguments.of(new MemberRegisterRequest("", "email@example.com", List.of( + new MemberAdditionalChildRequest("Child1", "Grade1") + ))), + + // 잘못된 이메일 + Arguments.of(new MemberRegisterRequest("nickname", "invalid_email_format", List.of( + new MemberAdditionalChildRequest("Child1", "Grade1") + ))), + + // 5명 이상의 아이 + Arguments.of(new MemberRegisterRequest("nickname", "email@example.com", List.of( + new MemberAdditionalChildRequest("Child1", "Grade1"), + new MemberAdditionalChildRequest("Child2", "Grade2"), + new MemberAdditionalChildRequest("Child3", "Grade3"), + new MemberAdditionalChildRequest("Child4", "Grade4"), + new MemberAdditionalChildRequest("Child5", "Grade5"), + new MemberAdditionalChildRequest("Child6", "Grade6") + ))), + + // 빈 아이 닉네임 + Arguments.of(new MemberRegisterRequest("nickname", "email@example.com", List.of( + new MemberAdditionalChildRequest("", "Grade1") + ))), + + // 빈 학년 + Arguments.of(new MemberRegisterRequest("nickname", "email@example.com", List.of( + new MemberAdditionalChildRequest("Child1", "") + ))) + ); + } + } +} diff --git a/src/test/java/org/guzzing/studayserver/domain/member/model/MemberTest.java b/src/test/java/org/guzzing/studayserver/domain/member/model/MemberTest.java new file mode 100644 index 00000000..82d85aaf --- /dev/null +++ b/src/test/java/org/guzzing/studayserver/domain/member/model/MemberTest.java @@ -0,0 +1,112 @@ +package org.guzzing.studayserver.domain.member.model; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.spy; + +import java.util.Optional; +import org.assertj.core.api.Assertions; +import org.guzzing.studayserver.domain.child.model.Child; +import org.guzzing.studayserver.domain.child.model.NickName; +import org.guzzing.studayserver.domain.member.model.vo.MemberProvider; +import org.guzzing.studayserver.domain.member.model.vo.RoleType; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +class MemberTest { + + private Member member; + + @BeforeEach + public void setUp() { + member = Member.of(new NickName("testNick"), "12345", MemberProvider.KAKAO, RoleType.USER); + } + + @DisplayName("업데이트 성공") + @Test + void update_success() { + // Given + String newNickName = "updatedNick"; + String newEmail = "updated@test.com"; + + // When + member.update(newNickName, newEmail); + + // Then + assertThat(member.getNickName()).isEqualTo(newNickName); + assertThat(member.getEmail()).isEqualTo(newEmail); + } + + @DisplayName("아이 추가 성공") + @Test + void addChild_success() { + // Given & When + Child child1 = new Child("child1", "중학교 1학년"); + child1.assignToNewMemberOnly(member); + + Child child2 = new Child("child2", "중학교 2학년"); + child2.assignToNewMemberOnly(member); + + // Then + Assertions.assertThat(member.getChildren()).size().isEqualTo(2); + } + + @DisplayName("아이 추가시 제한된 아이 숫자를 넘긴다면 예외를 발생시킨다.") + @Test + void addChild_failure_exceedsLimit() { + // Given + for (int i = 0; i < 5; i++) { + Child child = new Child("child1", "중학교 1학년"); + child.assignToNewMemberOnly(member); + } + + Child exceededChild = new Child("child1", "중학교 1학년"); + + // When & Then + assertThatThrownBy(() -> exceededChild.assignToNewMemberOnly(member)) + .isInstanceOf(IllegalStateException.class) + .hasMessageContaining(String.format("멤버당 아이는 최대 %d까지만 등록할 수 있습니다.", Member.CHILDREN_MAX_SIZE)); + } + + @Nested + class FindChild { + + @DisplayName("멤버에 아이가 존재한다면 반환한다.") + @Test + void success() { + // Given + Member member = new Member(new NickName("멤버 닉네임"), "123", MemberProvider.KAKAO, RoleType.USER); + + Long childId = 1L; + Child child = new Child("아이 닉네임", "초등학교 1학년"); + + Child spyChild = spy(child); + spyChild.assignToNewMemberOnly(member); + given(spyChild.getId()).willReturn(childId); + + // When + Optional actualChild = member.findChild(childId); + + // Then + assertThat(actualChild).contains(spyChild); + } + + @DisplayName("멤버에 아이가 존재하지 않는다면 empty를 반환한다.") + @Test + void whenNonExistentChildId_returnedEmpty() { + // Given + Member member = new Member(new NickName("멤버 닉네임"), "123", MemberProvider.KAKAO, RoleType.USER); + + Long nonExistentChildId = 1L; + + // When + Optional actualOptionalChild = member.findChild(nonExistentChildId); + + // Then + assertThat(actualOptionalChild).isEmpty(); + } + } +} diff --git a/src/test/java/org/guzzing/studayserver/domain/member/model/vo/EmailTest.java b/src/test/java/org/guzzing/studayserver/domain/member/model/vo/EmailTest.java new file mode 100644 index 00000000..52d73526 --- /dev/null +++ b/src/test/java/org/guzzing/studayserver/domain/member/model/vo/EmailTest.java @@ -0,0 +1,54 @@ +package org.guzzing.studayserver.domain.member.model.vo; + +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullAndEmptySource; +import org.junit.jupiter.params.provider.ValueSource; + +class EmailTest { + + @DisplayName("정상적인 이메일 형식이라면 객체를 생성한다.") + @ParameterizedTest + @ValueSource(strings = { + "test@example.com", + "firstname.lastname@example.com", + "email@subdomain.example.com", + "username+tag@example.com", + "email@example.co.jp", + "email@example.name" + }) + void create_success(String validEmail) { + // Then + assertThatCode(() -> new Email(validEmail)) + .doesNotThrowAnyException(); + } + + @DisplayName("잘못된 이메일 형식이라면 예외를 발생시킨다.") + + @ParameterizedTest + @NullAndEmptySource + @ValueSource(strings = { + " ", + "plainaddress", + "@missingusername.com", + "username@.com", + "username@.com.com", + "username@example..com", + "username@-example.com", + "username@example#.com" + }) + void create_failure_dueToInvalidFormat(String invalidEmail) { + // Then + assertThatThrownBy(() -> new Email(invalidEmail)) + .isInstanceOf(IllegalArgumentException.class); + } + +} + + + + + diff --git a/src/test/java/org/guzzing/studayserver/domain/member/service/MemberServiceTest.java b/src/test/java/org/guzzing/studayserver/domain/member/service/MemberServiceTest.java new file mode 100644 index 00000000..51040101 --- /dev/null +++ b/src/test/java/org/guzzing/studayserver/domain/member/service/MemberServiceTest.java @@ -0,0 +1,41 @@ +package org.guzzing.studayserver.domain.member.service; + +import org.guzzing.studayserver.domain.member.repository.MemberRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.transaction.annotation.Transactional; + +@SpringBootTest +@Transactional +@ActiveProfiles({"dev", "oauth"}) +class MemberServiceTest { + + @Autowired + private MemberService memberService; + + @Autowired + private MemberRepository memberRepository; + +// @DisplayName("멤버 등록 성공") +// @Test +// void register_success() { +// // Given +// String memberNickname = "nickname"; +// String memberEmail = "test@email.com"; +// +// Member member = Member.of(new NickName("멤버 닉네임"), "123", MemberProvider.KAKAO, RoleType.USER); +// Member savedMember = memberRepository.save(member); +// +// MemberRegisterParam param = new MemberRegisterParam(savedMember.getId(), memberNickname, memberEmail, List.of( +// new MemberAdditionalChildParam("childNickname1", "초등학교 1학년"), +// new MemberAdditionalChildParam("childNickname2", "초등학교 3학년") +// )); +// +// // When +// memberService.register(param); +// +// // Then +// assertThat(member.getChildren()).size().isEqualTo(2); +// } +} diff --git a/src/test/java/org/guzzing/studayserver/domain/region/controller/RegionRestControllerTest.java b/src/test/java/org/guzzing/studayserver/domain/region/controller/RegionRestControllerTest.java new file mode 100644 index 00000000..3854ca0d --- /dev/null +++ b/src/test/java/org/guzzing/studayserver/domain/region/controller/RegionRestControllerTest.java @@ -0,0 +1,187 @@ +package org.guzzing.studayserver.domain.region.controller; + +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint; +import static org.springframework.restdocs.payload.JsonFieldType.ARRAY; +import static org.springframework.restdocs.payload.JsonFieldType.NUMBER; +import static org.springframework.restdocs.payload.JsonFieldType.STRING; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; +import static org.springframework.restdocs.request.RequestDocumentation.pathParameters; +import static org.springframework.restdocs.request.RequestDocumentation.queryParameters; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import jakarta.transaction.Transactional; +import org.guzzing.studayserver.domain.region.service.RegionService; +import org.guzzing.studayserver.testutil.WithMockCustomOAuth2LoginUser; +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.restdocs.AutoConfigureRestDocs; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; + +@AutoConfigureRestDocs +@AutoConfigureMockMvc +@SpringBootTest +@Transactional +@ActiveProfiles({"dev", "oauth"}) +class RegionRestControllerTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private RegionService regionService; + + final String sido = "서울특별시"; + final String sigungu = "테스트구"; + final String upmyeondong = "테스트테스트동"; + final double latitude = 37.5664; + final double longitude = 126.972925; + + @BeforeEach + void setUp() { + regionService.createRegion(sido, sigungu, upmyeondong, latitude, longitude); + } + + @Test + @DisplayName("아무런 파라미터 없이 요청하면 조회 가능한 시도 데이터를 반환한다.") + @WithMockCustomOAuth2LoginUser + void getSubRegions_None_RegionResponse() throws Exception { + // Given & When + ResultActions perform = mockMvc.perform(get("/regions/beopjungdong") + .contentType(APPLICATION_JSON_VALUE) + .accept(APPLICATION_JSON_VALUE)); + + // Then + perform.andDo(print()) + .andExpect(status().isOk()) + .andExpect(content().contentTypeCompatibleWith(APPLICATION_JSON_VALUE)) + .andExpect(jsonPath("$.targetRegion").value("전국")) + .andExpect(jsonPath("$.subRegion").exists()) + .andExpect(jsonPath("$.subRegionCount").isNumber()) + .andDo(document("get-region-beopjungdong-sido", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + responseFields( + fieldWithPath("targetRegion").type(STRING).description("전국"), + fieldWithPath("subRegion").type(ARRAY).description("탐색 가능한 시도 조회 결과 리스트"), + fieldWithPath("subRegionCount").type(NUMBER).description("탐색 가능한 시도 조회 결과 수") + ) + )); + } + + @Test + @DisplayName("시도를 파라미터로 요청하면 해당 시도, 시군구, 개수 데이터를 반환한다.") + @WithMockCustomOAuth2LoginUser + void getSubRegions_Sido_RegionResponse() throws Exception { + // Given & When + ResultActions perform = mockMvc.perform(get("/regions/beopjungdong/{sido}", sido) + .contentType(APPLICATION_JSON_VALUE) + .accept(APPLICATION_JSON_VALUE)); + + // Then + perform.andDo(print()) + .andExpect(status().isOk()) + .andExpect(content().contentTypeCompatibleWith(APPLICATION_JSON_VALUE)) + .andExpect(jsonPath("$.targetRegion").value(sido)) + .andExpect(jsonPath("$.subRegion").isNotEmpty()) + .andExpect(jsonPath("$.subRegionCount").isNumber()) + .andDo(document("get-region-beopjungdong-sigungu", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + pathParameters( + parameterWithName("sido").description("시도") + ), + responseFields( + fieldWithPath("targetRegion").type(STRING).description("탐색한 시도명"), + fieldWithPath("subRegion").type(ARRAY).description("탐색한 시군구 조회 결과 리스트"), + fieldWithPath("subRegionCount").type(NUMBER).description("탐색한 시군구 조회 결과 수") + ) + )); + } + + @Test + @DisplayName("시도, 시군구를 요청 파라미터로 받아 해당 시도군구의 읍면동 데이터를 응답한다.") + @WithMockCustomOAuth2LoginUser + void getSubRegions_SidoAndSigungu_RegionResponse() throws Exception { + // Given & When + ResultActions perform = mockMvc.perform(get("/regions/beopjungdong/{sido}/{sigungu}", sido, sigungu) + .contentType(APPLICATION_JSON_VALUE) + .accept(APPLICATION_JSON_VALUE)); + + // Then + perform.andDo(print()) + .andExpect(status().isOk()) + .andExpect(content().contentTypeCompatibleWith(APPLICATION_JSON_VALUE)) + .andExpect(jsonPath("$.targetRegion").value(sido + " " + sigungu)) + .andExpect(jsonPath("$.subRegion").isNotEmpty()) + .andExpect(jsonPath("$.subRegionCount").isNumber()) + .andDo(document("get-region-beopjungdong-upmyeondong", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + pathParameters( + parameterWithName("sido").description("시도"), + parameterWithName("sigungu").description("시군구") + ), + responseFields( + fieldWithPath("targetRegion").type(STRING).description("탐색한 시도군구명"), + fieldWithPath("subRegion").type(ARRAY).description("탐색한 읍면동 조회 결과 리스트"), + fieldWithPath("subRegionCount").type(NUMBER).description("탐색한 읍면동 조회 결과 수") + ) + )); + } + + @Test + @DisplayName("시도, 시군구, 읍면동 데이터를 요청받아, 해당하는 위경도 데이터를 응답한다.") + @WithMockCustomOAuth2LoginUser + void getLocation_AllAddress_RegionLocationResponse() throws Exception { + // Given & When + ResultActions perform = mockMvc.perform(get("/regions/location") + .param("sido", sido) + .param("sigungu", sigungu) + .param("upmyeondong", upmyeondong) + .contentType(APPLICATION_JSON_VALUE) + .accept(APPLICATION_JSON_VALUE)); + + // Then + perform.andDo(print()) + .andExpect(status().isOk()) + .andExpect(content().contentTypeCompatibleWith(APPLICATION_JSON_VALUE)) + .andExpect(jsonPath("$.sido").value(sido)) + .andExpect(jsonPath("$.sigungu").value(sigungu)) + .andExpect(jsonPath("$.upmyeondong").value(upmyeondong)) + .andExpect(jsonPath("$.latitude").value(latitude)) + .andExpect(jsonPath("$.longitude").value(longitude)) + .andDo(document("get-region-location", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + queryParameters( + parameterWithName("sido").description("시도"), + parameterWithName("sigungu").description("시군구"), + parameterWithName("upmyeondong").description("읍면동") + ), + responseFields( + fieldWithPath("sido").type(STRING).description("조회된 시도"), + fieldWithPath("sigungu").type(STRING).description("조회된 시군구"), + fieldWithPath("upmyeondong").type(STRING).description("조회된 읍면동"), + fieldWithPath("latitude").type(NUMBER).description("조회된 위도"), + fieldWithPath("longitude").type(NUMBER).description("조회된 경도") + ) + )); + } + +} \ No newline at end of file diff --git a/src/test/java/org/guzzing/studayserver/domain/region/service/RegionServiceTest.java b/src/test/java/org/guzzing/studayserver/domain/region/service/RegionServiceTest.java new file mode 100644 index 00000000..79f20282 --- /dev/null +++ b/src/test/java/org/guzzing/studayserver/domain/region/service/RegionServiceTest.java @@ -0,0 +1,87 @@ +package org.guzzing.studayserver.domain.region.service; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.guzzing.studayserver.domain.region.service.dto.beopjungdong.SidoResult; +import org.guzzing.studayserver.domain.region.service.dto.beopjungdong.SigunguResult; +import org.guzzing.studayserver.domain.region.service.dto.beopjungdong.UpmyeondongResult; +import org.guzzing.studayserver.domain.region.service.dto.location.RegionResult; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.transaction.annotation.Transactional; + +@SpringBootTest +@Transactional +@ActiveProfiles({"dev", "oauth"}) +class RegionServiceTest { + + @Autowired + private RegionService regionService; + + private RegionResult savedRegion; + + final String sido = "서울특별시"; + final String sigungu = "테스트구"; + final String upmyeondong = "테스트테스트동"; + + @BeforeEach + void setUp() { + final double latitude = 37.5664; + final double longitude = 126.972925; + + savedRegion = regionService.createRegion(sido, sigungu, upmyeondong, latitude, longitude); + } + + @Test + @DisplayName("시도를 받아 해당 시도의 시군구를 반환한다.") + void findSigungusBySido_Sido_SigunguResult() { + // Given & When + SigunguResult result = regionService.findSigungusBySido(sido); + + // Then + assertThat(result.sido()).isEqualTo(sido); + assertThat(result.sigunguCount()).isPositive(); + } + + @Test + @DisplayName("시도, 시군구를 받아 해당 시도군구의 읍면동을 반환한다.") + void findUpmyeondongBySidoAndSigungu_SidoAndSigungu_UpmyeondongResult() { + // Given & When + UpmyeondongResult result = regionService.findUpmyeondongBySidoAndSigungu(sido, sigungu); + + // Then + assertThat(result.sido()).isEqualTo(sido); + assertThat(result.sigungu()).isEqualTo(sigungu); + assertThat(result.upmyeondongCount()).isPositive(); + } + + @Test + @DisplayName("조회 가능한 시도를 반환한다.") + void findSido_None_SidoResult() { + // Given & When + SidoResult result = regionService.findSido(); + + // Then + assertThat(result.nation()).isEqualTo("전국"); + assertThat(result.sidos()).isNotEmpty(); + assertThat(result.sidoCount()).isEqualTo(2); + } + + @Test + @DisplayName("시도, 시군구, 읍면동 데이터를 요청받아, 해당하는 위경도를 반환한다.") + void findLocation_AllAddress_RegionResult() { + // Given & When + RegionResult result = regionService.findLocation(sido, sigungu, upmyeondong); + + // Then + assertThat(result.sido()).isEqualTo(sido); + assertThat(result.upmyeondong()).isEqualTo(upmyeondong); + assertThat(result.latitude()).isLessThanOrEqualTo(40.0); + assertThat(result.longtigute()).isLessThanOrEqualTo(130.0); + } + +} \ No newline at end of file diff --git a/src/test/java/org/guzzing/studayserver/domain/review/controller/ReviewRestControllerTest.java b/src/test/java/org/guzzing/studayserver/domain/review/controller/ReviewRestControllerTest.java new file mode 100644 index 00000000..2576a910 --- /dev/null +++ b/src/test/java/org/guzzing/studayserver/domain/review/controller/ReviewRestControllerTest.java @@ -0,0 +1,150 @@ +package org.guzzing.studayserver.domain.review.controller; + +import static org.guzzing.studayserver.testutil.fixture.TestConfig.AUTHORIZATION_HEADER; +import static org.guzzing.studayserver.testutil.fixture.TestConfig.BEARER; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; +import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName; +import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint; +import static org.springframework.restdocs.payload.JsonFieldType.BOOLEAN; +import static org.springframework.restdocs.payload.JsonFieldType.NUMBER; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; +import static org.springframework.restdocs.request.RequestDocumentation.queryParameters; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.guzzing.studayserver.domain.academy.service.AcademyAccessService; +import org.guzzing.studayserver.domain.member.service.MemberAccessService; +import org.guzzing.studayserver.domain.review.controller.dto.request.ReviewPostRequest; +import org.guzzing.studayserver.testutil.WithMockCustomOAuth2LoginUser; +import org.guzzing.studayserver.testutil.fixture.ReviewFixture; +import org.guzzing.studayserver.testutil.fixture.TestConfig; +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.restdocs.AutoConfigureRestDocs; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.transaction.annotation.Transactional; + +@AutoConfigureMockMvc +@AutoConfigureRestDocs +@ActiveProfiles(value = {"dev", "oauth"}) +@SpringBootTest +@Transactional +class ReviewRestControllerTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private TestConfig testConfig; + @Autowired + private ObjectMapper objectMapper; + + @MockBean + private MemberAccessService memberAccessService; + @MockBean + private AcademyAccessService academyAccessService; + + @Test + @DisplayName("리뷰 타입이 3개 이하고, 해당 학원에 대해서 등록한 학원이 없다면 리뷰를 등록한다.") + @WithMockCustomOAuth2LoginUser(memberId = 1L) + void registerReview_Success() throws Exception { + // Given + given(memberAccessService.existsMember(any())).willReturn(true); + given(academyAccessService.existsAcademy(any())).willReturn(true); + + ReviewPostRequest request = ReviewFixture.makeReviewPostRequest(true); + String jsonBody = objectMapper.writeValueAsString(request); + + // When + ResultActions perform = mockMvc.perform(post("/reviews") + .header(AUTHORIZATION_HEADER, BEARER + testConfig.getJwt()) + .content(jsonBody) + .accept(APPLICATION_JSON_VALUE) + .contentType(APPLICATION_JSON_VALUE)); + + // Then + perform.andDo(print()) + .andExpect(status().isCreated()) + .andExpect(content().contentTypeCompatibleWith(APPLICATION_JSON_VALUE)) + .andExpect(jsonPath("$.reviewId").isNumber()) + .andExpect(jsonPath("$.academyId").isNumber()) + .andDo(document("post-review", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + requestHeaders( + headerWithName("Authorization").description("JWT 토큰 (Bearer)") + ), + requestFields( + fieldWithPath("academyId").type(NUMBER).description("학원 아이디"), + fieldWithPath("kindness").type(BOOLEAN).description("친절해요 리뷰 선택 여부"), + fieldWithPath("cheapFee").type(BOOLEAN).description("수강료가 싸요 리뷰 선택 여부"), + fieldWithPath("goodFacility").type(BOOLEAN).description("시설이 좋아요 리뷰 선택 여부"), + fieldWithPath("goodManagement").type(BOOLEAN).description("관리가 좋아요 리뷰 선택 여부"), + fieldWithPath("lovelyTeaching").type(BOOLEAN).description("가르침이 사랑스러워요 리뷰 선택 여부"), + fieldWithPath("shuttleAvailability").type(BOOLEAN).description("셔틀을 운행해요 리뷰 선택 여부") + ), + responseFields( + fieldWithPath("reviewId").type(NUMBER).description("리뷰 아이디"), + fieldWithPath("academyId").type(NUMBER).description("학원 아이디") + ) + )); + } + + @Test + @DisplayName("리뷰를 등록한 적 없다면 리뷰 등록 가능함을 응답한다.") + @WithMockCustomOAuth2LoginUser(memberId = 1L) + void getReviewable_NotExistsReview_Reviewable() throws Exception { + // Given + given(memberAccessService.existsMember(any())).willReturn(true); + given(academyAccessService.existsAcademy(any())).willReturn(true); + + // When + ResultActions perform = mockMvc.perform(get("/reviews/reviewable") + .param("academyId", String.valueOf(1L)) + .header(AUTHORIZATION_HEADER, BEARER + testConfig.getJwt()) + .accept(APPLICATION_JSON_VALUE) + .contentType(APPLICATION_JSON_VALUE)); + + // Then + perform.andDo(print()) + .andExpect(status().isOk()) + .andExpect(content().contentTypeCompatibleWith(APPLICATION_JSON_VALUE)) + .andExpect(jsonPath("$.academyId").value(1L)) + .andExpect(jsonPath("$.reviewable").value(true)) + .andDo(document("get-reviewable", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + requestHeaders( + headerWithName("Authorization").description("JWT 토큰 (Bearer)") + ), + queryParameters( + parameterWithName("academyId").description("학원 아이디") + ), + responseFields( + fieldWithPath("academyId").type(NUMBER).description("학원 아이디"), + fieldWithPath("reviewable").type(BOOLEAN).description("리뷰 등록 가능 여부") + ) + )); + } + +} \ No newline at end of file diff --git a/src/test/java/org/guzzing/studayserver/domain/review/service/ReviewServiceTest.java b/src/test/java/org/guzzing/studayserver/domain/review/service/ReviewServiceTest.java new file mode 100644 index 00000000..e479432a --- /dev/null +++ b/src/test/java/org/guzzing/studayserver/domain/review/service/ReviewServiceTest.java @@ -0,0 +1,134 @@ +package org.guzzing.studayserver.domain.review.service; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.guzzing.studayserver.domain.review.model.ReviewType.CHEAP_FEE; +import static org.guzzing.studayserver.domain.review.model.ReviewType.GOOD_FACILITY; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; + +import java.util.Map; +import org.guzzing.studayserver.domain.academy.service.AcademyAccessService; +import org.guzzing.studayserver.domain.member.service.MemberAccessService; +import org.guzzing.studayserver.domain.review.model.ReviewType; +import org.guzzing.studayserver.domain.review.service.dto.request.ReviewPostParam; +import org.guzzing.studayserver.domain.review.service.dto.response.ReviewPostResult; +import org.guzzing.studayserver.domain.review.service.dto.response.ReviewableResult; +import org.guzzing.studayserver.global.exception.ReviewException; +import org.guzzing.studayserver.testutil.WithMockCustomOAuth2LoginUser; +import org.guzzing.studayserver.testutil.fixture.ReviewFixture; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.transaction.annotation.Transactional; + +@ActiveProfiles(value = {"dev", "oauth"}) +@SpringBootTest +@Transactional +class ReviewServiceTest { + + @Autowired + private ReviewService reviewService; + + @MockBean + private AcademyAccessService academyAccessService; + @MockBean + private MemberAccessService memberAccessService; + + @Test + @DisplayName("해당 학원에 리뷰를 남긴 적이 없으면 리뷰를 등록한다.") + @WithMockCustomOAuth2LoginUser(memberId = 1L) + void createReviewOfAcademy_NotReviewYet_RegisterReview() { + // Given + given(memberAccessService.existsMember(any())).willReturn(true); + given(academyAccessService.existsAcademy(any())).willReturn(true); + + boolean isValid = true; + ReviewPostParam param = ReviewFixture.makeReviewPostParam(isValid); + Map validReviewMap = ReviewFixture.makeValidReviewMap(); + + // When + ReviewPostResult result = reviewService.createReviewOfAcademy(param); + + // Then + assertThat(result).satisfies(entry -> { + assertThat(entry.memberId()).isEqualTo(ReviewFixture.memberId); + assertThat(entry.academyId()).isEqualTo(ReviewFixture.academyId); + assertThat(entry.cheapFee()).isEqualTo(validReviewMap.get(CHEAP_FEE)); + assertThat(entry.goodFacility()).isEqualTo(validReviewMap.get(GOOD_FACILITY)); + }); + } + + @Test + @DisplayName("해당 학원에 리뷰를 남겼다면 리뷰 등록에 실패한다.") + @WithMockCustomOAuth2LoginUser(memberId = 1L) + void createReviewOfAcademy_Reviewed_Fail() { + // Given + given(memberAccessService.existsMember(any())).willReturn(true); + given(academyAccessService.existsAcademy(any())).willReturn(true); + + boolean isValid = true; + ReviewPostParam param = ReviewFixture.makeReviewPostParam(isValid); + + reviewService.createReviewOfAcademy(param); + + // When & Then + assertThatThrownBy(() -> reviewService.createReviewOfAcademy(param)) + .isInstanceOf(ReviewException.class) + .hasMessage("이미 리뷰를 남겼습니다."); + } + + @Test + @DisplayName("리뷰를 3 항목 초과로 남겼다면 리뷰 등록에 실패한다.") + @WithMockCustomOAuth2LoginUser(memberId = 1L) + void createReviewOfAcademy_GreaterThanThreeReivewTypes_Fail() { + // Given + given(memberAccessService.existsMember(any())).willReturn(true); + given(academyAccessService.existsAcademy(any())).willReturn(true); + + boolean isValid = false; + ReviewPostParam param = ReviewFixture.makeReviewPostParam(isValid); + + // When & Then + assertThatThrownBy(() -> reviewService.createReviewOfAcademy(param)) + .isInstanceOf(ReviewException.class) + .hasMessage("리뷰는 3개까지만 가능합니다."); + } + + @Test + @DisplayName("해당 학원에 리뷰를 남긴 적 없으면 리뷰 등록 가능하다.") + @WithMockCustomOAuth2LoginUser(memberId = 1L) + void isReviewableToAcademy_NotExistsReview_Reviewable() { + // Given + given(memberAccessService.existsMember(any())).willReturn(true); + given(academyAccessService.existsAcademy(any())).willReturn(true); + + // When & Then + ReviewableResult result = reviewService.getReviewableToAcademy(100L, 100L); + + assertThat(result.reviewable()).isTrue(); + } + + @Test + @DisplayName("해당 학원에 리뷰를 남겼다면 리뷰 등록 불가하다.") + @WithMockCustomOAuth2LoginUser(memberId = 1L) + void isReviewableToAcademy_ExistsReview_NotReviewable() { + // Given + given(memberAccessService.existsMember(any())).willReturn(true); + given(academyAccessService.existsAcademy(any())).willReturn(true); + + boolean isValid = true; + ReviewPostParam param = ReviewFixture.makeReviewPostParam(isValid); + + reviewService.createReviewOfAcademy(param); + + // When & Then + ReviewableResult result = reviewService.getReviewableToAcademy(param.memberId(), param.academyId()); + + assertThat(result.reviewable()).isFalse(); + } + +} \ No newline at end of file