diff --git a/build.gradle b/build.gradle index a96886c68..222cca60e 100644 --- a/build.gradle +++ b/build.gradle @@ -99,6 +99,7 @@ dependencies { // Sentry implementation 'io.sentry:sentry-logback:7.14.0' + implementation 'io.sentry:sentry-openfeign:7.14.0' } tasks.named('test') { diff --git a/src/main/java/com/gdschongik/gdsc/domain/discord/domain/DiscordValidator.java b/src/main/java/com/gdschongik/gdsc/domain/discord/domain/DiscordValidator.java index c8d376a3b..10b6d5c09 100644 --- a/src/main/java/com/gdschongik/gdsc/domain/discord/domain/DiscordValidator.java +++ b/src/main/java/com/gdschongik/gdsc/domain/discord/domain/DiscordValidator.java @@ -32,7 +32,7 @@ public void validateVerifyDiscordCode( } public void validateAdminPermission(Member currentMember) { - if (!currentMember.getManageRole().equals(MemberManageRole.ADMIN)) { + if (currentMember.getManageRole() != MemberManageRole.ADMIN) { throw new CustomException(INVALID_ROLE); } } diff --git a/src/main/java/com/gdschongik/gdsc/domain/member/domain/Member.java b/src/main/java/com/gdschongik/gdsc/domain/member/domain/Member.java index e2c07099b..44e684078 100644 --- a/src/main/java/com/gdschongik/gdsc/domain/member/domain/Member.java +++ b/src/main/java/com/gdschongik/gdsc/domain/member/domain/Member.java @@ -137,7 +137,7 @@ private void validateStatusUpdatable() { * 준회원 승급 가능 여부를 검증합니다. */ private void validateAssociateAvailable() { - if (role.equals(ASSOCIATE)) { + if (isAssociate()) { throw new CustomException(MEMBER_ALREADY_ASSOCIATE); } @@ -152,7 +152,7 @@ private void validateRegularAvailable() { throw new CustomException(MEMBER_ALREADY_REGULAR); } - if (!role.equals(ASSOCIATE)) { + if (!isAssociate()) { throw new CustomException(MEMBER_NOT_ASSOCIATE); } } @@ -335,26 +335,26 @@ public void updateMemberInfo( // 데이터 전달 로직 public boolean isGuest() { - return role.equals(GUEST); + return role == GUEST; } public boolean isAssociate() { - return role.equals(ASSOCIATE); + return role == ASSOCIATE; } public boolean isRegular() { - return role.equals(REGULAR); + return role == REGULAR; } public boolean isAdmin() { - return manageRole.equals(ADMIN); + return manageRole == ADMIN; } public boolean isMentor() { - return studyRole.equals(MENTOR); + return studyRole == MENTOR; } public boolean isStudent() { - return studyRole.equals(STUDENT); + return studyRole == STUDENT; } } diff --git a/src/main/java/com/gdschongik/gdsc/domain/member/domain/MemberStatus.java b/src/main/java/com/gdschongik/gdsc/domain/member/domain/MemberStatus.java index a37e655eb..0864c3602 100644 --- a/src/main/java/com/gdschongik/gdsc/domain/member/domain/MemberStatus.java +++ b/src/main/java/com/gdschongik/gdsc/domain/member/domain/MemberStatus.java @@ -13,10 +13,10 @@ public enum MemberStatus { private final String value; public boolean isDeleted() { - return this.equals(DELETED); + return this == DELETED; } public boolean isForbidden() { - return this.equals(FORBIDDEN); + return this == FORBIDDEN; } } diff --git a/src/main/java/com/gdschongik/gdsc/domain/membership/application/MembershipService.java b/src/main/java/com/gdschongik/gdsc/domain/membership/application/MembershipService.java index e013616f4..c0a0aa474 100644 --- a/src/main/java/com/gdschongik/gdsc/domain/membership/application/MembershipService.java +++ b/src/main/java/com/gdschongik/gdsc/domain/membership/application/MembershipService.java @@ -44,11 +44,6 @@ public void verifyPaymentStatus(String orderNanoId) { findMembershipAndVerifyPayment(membershipId); } - @Transactional - public void verifyPaymentStatus(Long membershipId) { - findMembershipAndVerifyPayment(membershipId); - } - private void findMembershipAndVerifyPayment(Long membershipId) { Membership currentMembership = membershipRepository .findById(membershipId) diff --git a/src/main/java/com/gdschongik/gdsc/domain/membership/domain/Membership.java b/src/main/java/com/gdschongik/gdsc/domain/membership/domain/Membership.java index 7a1e70789..255f152cb 100644 --- a/src/main/java/com/gdschongik/gdsc/domain/membership/domain/Membership.java +++ b/src/main/java/com/gdschongik/gdsc/domain/membership/domain/Membership.java @@ -62,7 +62,7 @@ public static Membership createMembership(Member member, RecruitmentRound recrui // 검증 로직 - public void validateRegularRequirement() { + private void validateRegularRequirement() { if (isRegularRequirementAllSatisfied()) { throw new CustomException(MEMBERSHIP_ALREADY_SATISFIED); } diff --git a/src/main/java/com/gdschongik/gdsc/domain/recruitment/domain/RecruitmentRound.java b/src/main/java/com/gdschongik/gdsc/domain/recruitment/domain/RecruitmentRound.java index 18d3cec87..94e6e1cd7 100644 --- a/src/main/java/com/gdschongik/gdsc/domain/recruitment/domain/RecruitmentRound.java +++ b/src/main/java/com/gdschongik/gdsc/domain/recruitment/domain/RecruitmentRound.java @@ -94,6 +94,6 @@ public void validatePeriodNotStarted() { } public boolean isFirstRound() { - return roundType.equals(RoundType.FIRST); + return roundType == RoundType.FIRST; } } diff --git a/src/main/java/com/gdschongik/gdsc/domain/recruitment/domain/RecruitmentRoundValidator.java b/src/main/java/com/gdschongik/gdsc/domain/recruitment/domain/RecruitmentRoundValidator.java index 42d4ec58a..e23d31589 100644 --- a/src/main/java/com/gdschongik/gdsc/domain/recruitment/domain/RecruitmentRoundValidator.java +++ b/src/main/java/com/gdschongik/gdsc/domain/recruitment/domain/RecruitmentRoundValidator.java @@ -1,6 +1,5 @@ package com.gdschongik.gdsc.domain.recruitment.domain; -import static com.gdschongik.gdsc.domain.recruitment.domain.RoundType.*; import static com.gdschongik.gdsc.global.common.constant.TemporalConstant.*; import static com.gdschongik.gdsc.global.exception.ErrorCode.*; @@ -67,7 +66,7 @@ private void validatePeriodOverlap( // 학년도, 학기, 모집회차가 모두 같은 경우 private void validateRoundOverlap(List recruitmentRounds, RoundType roundType) { recruitmentRounds.stream() - .filter(recruitmentRound -> recruitmentRound.getRoundType().equals(roundType)) + .filter(recruitmentRound -> recruitmentRound.getRoundType() == roundType) .findAny() .ifPresent(ignored -> { throw new CustomException(RECRUITMENT_ROUND_TYPE_OVERLAP); @@ -76,14 +75,14 @@ private void validateRoundOverlap(List recruitmentRounds, Roun // 1차 모집이 없는데 2차 모집을 생성하려고 하는 경우 private void validateRoundOneExist(List recruitmentRounds, RoundType roundType) { - if (roundType.equals(SECOND) && recruitmentRounds.stream().noneMatch(RecruitmentRound::isFirstRound)) { + if (roundType.isSecond() && recruitmentRounds.stream().noneMatch(RecruitmentRound::isFirstRound)) { throw new CustomException(ROUND_ONE_DOES_NOT_EXIST); } } // 1차 모집을 비워둬서는 안되므로, 1차 모집을 2차 모집으로 수정하려고 하는 경우 예외 발생 private void validateRoundOneToTwo(RoundType previousRoundType, RoundType newRoundType) { - if (previousRoundType.equals(FIRST) && newRoundType.equals(SECOND)) { + if (previousRoundType.isFirst() && newRoundType.isSecond()) { throw new CustomException(ROUND_ONE_DOES_NOT_EXIST); } } diff --git a/src/main/java/com/gdschongik/gdsc/domain/recruitment/domain/RoundType.java b/src/main/java/com/gdschongik/gdsc/domain/recruitment/domain/RoundType.java index a70e8a963..f1d377d10 100644 --- a/src/main/java/com/gdschongik/gdsc/domain/recruitment/domain/RoundType.java +++ b/src/main/java/com/gdschongik/gdsc/domain/recruitment/domain/RoundType.java @@ -10,4 +10,12 @@ public enum RoundType { SECOND("2차"); private final String value; + + public boolean isFirst() { + return this == FIRST; + } + + public boolean isSecond() { + return this == SECOND; + } } diff --git a/src/main/java/com/gdschongik/gdsc/domain/study/application/StudentStudyService.java b/src/main/java/com/gdschongik/gdsc/domain/study/application/StudentStudyService.java index d55b9784d..7af5cccfb 100644 --- a/src/main/java/com/gdschongik/gdsc/domain/study/application/StudentStudyService.java +++ b/src/main/java/com/gdschongik/gdsc/domain/study/application/StudentStudyService.java @@ -93,17 +93,15 @@ public void attend(Long studyDetailId, StudyAttendCreateRequest request) { final Study study = studyDetail.getStudy(); final boolean isAlreadyAttended = attendanceRepository.existsByStudentIdAndStudyDetailId(currentMember.getId(), studyDetailId); - final StudyHistory studyHistory = studyHistoryRepository - .findByStudentAndStudy(currentMember, study) - .orElseThrow(() -> new CustomException(STUDY_HISTORY_NOT_FOUND)); + boolean isAppliedToStudy = studyHistoryRepository.existsByStudentAndStudy(currentMember, study); attendanceValidator.validateAttendance( - studyDetail, request.attendanceNumber(), LocalDate.now(), isAlreadyAttended); + studyDetail, request.attendanceNumber(), LocalDate.now(), isAlreadyAttended, isAppliedToStudy); Attendance attendance = Attendance.create(currentMember, studyDetail); attendanceRepository.save(attendance); - log.info("[StudyService] 스터디 출석: attendanceId={}", attendance.getId()); + log.info("[StudyService] 스터디 출석: attendanceId={}, memberId={}", attendance.getId(), currentMember.getId()); } @Transactional(readOnly = true) diff --git a/src/main/java/com/gdschongik/gdsc/domain/study/domain/AttendanceValidator.java b/src/main/java/com/gdschongik/gdsc/domain/study/domain/AttendanceValidator.java index 59532818c..aebade893 100644 --- a/src/main/java/com/gdschongik/gdsc/domain/study/domain/AttendanceValidator.java +++ b/src/main/java/com/gdschongik/gdsc/domain/study/domain/AttendanceValidator.java @@ -8,8 +8,13 @@ @DomainService public class AttendanceValidator { + public void validateAttendance( - StudyDetail studyDetail, String attendanceNumber, LocalDate date, boolean isAlreadyAttended) { + StudyDetail studyDetail, + String attendanceNumber, + LocalDate date, + boolean isAlreadyAttended, + boolean isAppliedToStudy) { // 출석체크 날짜 검증 LocalDate attendanceDay = studyDetail.getAttendanceDay(); if (!attendanceDay.equals(date)) { @@ -25,5 +30,10 @@ public void validateAttendance( if (isAlreadyAttended) { throw new CustomException(STUDY_DETAIL_ALREADY_ATTENDED); } + + // 스터디 신청 여부 검증 + if (!isAppliedToStudy) { + throw new CustomException(STUDY_HISTORY_NOT_FOUND); + } } } diff --git a/src/main/java/com/gdschongik/gdsc/global/common/constant/SentryConstant.java b/src/main/java/com/gdschongik/gdsc/global/common/constant/SentryConstant.java new file mode 100644 index 000000000..ff46df3d1 --- /dev/null +++ b/src/main/java/com/gdschongik/gdsc/global/common/constant/SentryConstant.java @@ -0,0 +1,12 @@ +package com.gdschongik.gdsc.global.common.constant; + +import java.util.List; + +public class SentryConstant { + + private SentryConstant() {} + + public static final String ACTUATOR_KEYWORD = "actuator"; + + public static final List KEYWORDS_TO_IGNORE = List.of(ACTUATOR_KEYWORD); +} diff --git a/src/main/java/com/gdschongik/gdsc/global/config/SentryConfig.java b/src/main/java/com/gdschongik/gdsc/global/config/SentryConfig.java index 7860fe1d7..e9d65580a 100644 --- a/src/main/java/com/gdschongik/gdsc/global/config/SentryConfig.java +++ b/src/main/java/com/gdschongik/gdsc/global/config/SentryConfig.java @@ -3,9 +3,12 @@ import com.gdschongik.gdsc.global.property.DockerProperty; import io.sentry.Sentry; import io.sentry.SentryOptions; +import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.servlet.resource.NoResourceFoundException; @Configuration @RequiredArgsConstructor @@ -13,10 +16,16 @@ public class SentryConfig { private final DockerProperty dockerProperty; + private final List> exceptionsToIgnore = List.of( + NoResourceFoundException.class, // 존재하지 않는 정적 리소스 요청 + MethodArgumentNotValidException.class // @Valid 검증 실패 + ); + @Bean Sentry.OptionsConfiguration customOptionsConfiguration() { return options -> { options.setRelease(convertTagToRelease(dockerProperty.getTag())); + exceptionsToIgnore.forEach(options::addIgnoredExceptionForType); }; } diff --git a/src/main/java/com/gdschongik/gdsc/global/exception/ErrorCode.java b/src/main/java/com/gdschongik/gdsc/global/exception/ErrorCode.java index 069a55f0c..76ac38309 100644 --- a/src/main/java/com/gdschongik/gdsc/global/exception/ErrorCode.java +++ b/src/main/java/com/gdschongik/gdsc/global/exception/ErrorCode.java @@ -116,6 +116,7 @@ public enum ErrorCode { STUDY_DETAIL_ASSIGNMENT_INVALID_UPDATE_DEADLINE(HttpStatus.CONFLICT, "수정하려고 하는 과제의 마감기한은 기존의 마감기한보다 빠르면 안됩니다."), STUDY_DETAIL_ID_INVALID(HttpStatus.CONFLICT, "수정하려는 스터디 상세정보가 서버에 존재하지 않거나 유효하지 않습니다."), STUDY_DETAIL_CURRICULUM_SIZE_MISMATCH(HttpStatus.BAD_REQUEST, "스터디 커리큘럼의 총 개수가 일치하지 않습니다."), + // StudyHistory STUDY_HISTORY_NOT_FOUND(HttpStatus.NOT_FOUND, "존재하지 않는 스터디 수강 기록입니다."), STUDY_HISTORY_DUPLICATE(HttpStatus.CONFLICT, "이미 해당 스터디를 신청했습니다."), diff --git a/src/main/java/com/gdschongik/gdsc/infra/feign/global/config/FeignConfig.java b/src/main/java/com/gdschongik/gdsc/infra/feign/global/config/FeignConfig.java index be4d6dde6..97393198b 100644 --- a/src/main/java/com/gdschongik/gdsc/infra/feign/global/config/FeignConfig.java +++ b/src/main/java/com/gdschongik/gdsc/infra/feign/global/config/FeignConfig.java @@ -3,9 +3,11 @@ import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import feign.Capability; import feign.Logger; import feign.codec.Decoder; import feign.jackson.JacksonDecoder; +import io.sentry.openfeign.SentryCapability; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.cloud.openfeign.FeignFormatterRegistrar; import org.springframework.context.annotation.Bean; @@ -41,4 +43,9 @@ public FeignFormatterRegistrar dateTimeFormatterRegistrar() { registrar.registerFormatters(registry); }; } + + @Bean + public Capability sentryCapability() { + return new SentryCapability(); + } } diff --git a/src/main/java/com/gdschongik/gdsc/infra/sentry/CustomBeforeSendTransactionCallback.java b/src/main/java/com/gdschongik/gdsc/infra/sentry/CustomBeforeSendTransactionCallback.java new file mode 100644 index 000000000..3f9958404 --- /dev/null +++ b/src/main/java/com/gdschongik/gdsc/infra/sentry/CustomBeforeSendTransactionCallback.java @@ -0,0 +1,28 @@ +package com.gdschongik.gdsc.infra.sentry; + +import static com.gdschongik.gdsc.global.common.constant.SentryConstant.*; + +import io.sentry.Hint; +import io.sentry.SentryOptions; +import io.sentry.protocol.SentryTransaction; +import org.jetbrains.annotations.NotNull; +import org.springframework.stereotype.Component; + +@Component +public class CustomBeforeSendTransactionCallback implements SentryOptions.BeforeSendTransactionCallback { + + @Override + public SentryTransaction execute(@NotNull SentryTransaction transaction, @NotNull Hint hint) { + String transactionEndpoint = transaction.getTransaction(); + + if (transactionEndpoint == null) { + return transaction; + } + + if (KEYWORDS_TO_IGNORE.stream().anyMatch(transactionEndpoint::contains)) { + return null; + } + + return transaction; + } +} diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index ccd90cc64..29e676cfa 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -8,5 +8,5 @@ spring: logging: level: - org.springframework.orm.jpa: DEBUG - org.springframework.transaction: DEBUG + com.gdschongik.gdsc.domain.*.api.*: info + com.gdschongik.gdsc.infra.feign: debug diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml index 32b629bc1..400f4fffa 100644 --- a/src/main/resources/application-local.yml +++ b/src/main/resources/application-local.yml @@ -10,11 +10,14 @@ spring: properties: hibernate: format_sql: ${FORMAT_SQL:true} + highlight_sql: ${HIGHLIGHT_SQL:true} defer-datasource-initialization: true open-in-view: false logging: level: - org.springframework.orm.jpa: DEBUG - org.springframework.transaction: DEBUG + com.gdschongik.gdsc.domain.*.api.*: debug + com.gdschongik.gdsc.infra.feign: debug + org.hibernate.orm.jdbc.bind: trace + org.hibernate.SQL: debug org.kohsuke.github: debug diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 9e54f89c9..119c99b78 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -8,5 +8,4 @@ spring: logging: level: - org.springframework.orm.jpa: DEBUG - org.springframework.transaction: DEBUG + com.gdschongik.gdsc.domain.*.api.*: info diff --git a/src/main/resources/application-sentry.yml b/src/main/resources/application-sentry.yml index c25bb6390..1c199cb5d 100644 --- a/src/main/resources/application-sentry.yml +++ b/src/main/resources/application-sentry.yml @@ -10,7 +10,7 @@ sentry: environment: ${spring.profiles.active:local} send-default-pii: true logging: - minimum-event-level: info + minimum-event-level: warn minimum-breadcrumb-level: debug docker: diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 445b022e4..bdbc8e8de 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -17,5 +17,4 @@ spring: logging: level: - com.gdschongik.gdsc.domain.*.api.*: debug - com.gdschongik.gdsc.infra.feign: debug + com.gdschongik.gdsc.domain.*.api.*: info diff --git a/src/test/java/com/gdschongik/gdsc/domain/member/domain/MemberValidatorTest.java b/src/test/java/com/gdschongik/gdsc/domain/member/domain/MemberValidatorTest.java index b05efbfc3..7861dff96 100644 --- a/src/test/java/com/gdschongik/gdsc/domain/member/domain/MemberValidatorTest.java +++ b/src/test/java/com/gdschongik/gdsc/domain/member/domain/MemberValidatorTest.java @@ -1,6 +1,7 @@ package com.gdschongik.gdsc.domain.member.domain; import static com.gdschongik.gdsc.global.common.constant.RecruitmentConstant.*; +import static com.gdschongik.gdsc.global.common.constant.TemporalConstant.*; import static com.gdschongik.gdsc.global.exception.ErrorCode.*; import static org.assertj.core.api.Assertions.*; diff --git a/src/test/java/com/gdschongik/gdsc/domain/membership/application/MembershipServiceTest.java b/src/test/java/com/gdschongik/gdsc/domain/membership/application/MembershipServiceTest.java index 1d940b2c9..6c0f08398 100644 --- a/src/test/java/com/gdschongik/gdsc/domain/membership/application/MembershipServiceTest.java +++ b/src/test/java/com/gdschongik/gdsc/domain/membership/application/MembershipServiceTest.java @@ -1,15 +1,12 @@ package com.gdschongik.gdsc.domain.membership.application; -import static com.gdschongik.gdsc.domain.common.model.RequirementStatus.SATISFIED; import static com.gdschongik.gdsc.domain.member.domain.MemberRole.ASSOCIATE; import static com.gdschongik.gdsc.global.common.constant.RecruitmentConstant.*; +import static com.gdschongik.gdsc.global.common.constant.TemporalConstant.*; import static com.gdschongik.gdsc.global.exception.ErrorCode.*; import static org.assertj.core.api.Assertions.*; import com.gdschongik.gdsc.domain.common.vo.Money; -import com.gdschongik.gdsc.domain.member.domain.Member; -import com.gdschongik.gdsc.domain.membership.dao.MembershipRepository; -import com.gdschongik.gdsc.domain.membership.domain.Membership; import com.gdschongik.gdsc.domain.recruitment.domain.RecruitmentRound; import com.gdschongik.gdsc.domain.recruitment.domain.RoundType; import com.gdschongik.gdsc.global.exception.CustomException; @@ -24,9 +21,6 @@ public class MembershipServiceTest extends IntegrationTest { @Autowired private MembershipService membershipService; - @Autowired - private MembershipRepository membershipRepository; - @Nested class 멤버십_가입신청시 { @Test @@ -73,38 +67,4 @@ class 멤버십_가입신청시 { .doesNotThrowAnyException(); } } - - @Test - void 멤버십_회비납부시_이미_회비납부_했다면_회비납부_실패한다() { - // given - Member member = createMember(); - logoutAndReloginAs(1L, ASSOCIATE); - RecruitmentRound recruitmentRound = createRecruitmentRound(); - Membership membership = createMembership(member, recruitmentRound); - membershipService.verifyPaymentStatus(membership.getId()); - - // when & then - assertThatThrownBy(() -> membershipService.verifyPaymentStatus(membership.getId())) - .isInstanceOf(CustomException.class) - .hasMessage(MEMBERSHIP_ALREADY_SATISFIED.getMessage()); - } - - @Nested - class 정회원_가입조건_인증시도시 { - @Test - void 멤버십_회비납부시_정회원_가입조건중_회비납부_인증상태가_인증_성공한다() { - // given - Member member = createMember(); - logoutAndReloginAs(1L, ASSOCIATE); - RecruitmentRound recruitmentRound = createRecruitmentRound(); - Membership membership = createMembership(member, recruitmentRound); - - // when - membershipService.verifyPaymentStatus(membership.getId()); - membership = membershipRepository.findById(membership.getId()).get(); - - // then - assertThat(membership.getRegularRequirement().getPaymentStatus()).isEqualTo(SATISFIED); - } - } } diff --git a/src/test/java/com/gdschongik/gdsc/domain/membership/domain/MembershipTest.java b/src/test/java/com/gdschongik/gdsc/domain/membership/domain/MembershipTest.java index a9765b309..d92981419 100644 --- a/src/test/java/com/gdschongik/gdsc/domain/membership/domain/MembershipTest.java +++ b/src/test/java/com/gdschongik/gdsc/domain/membership/domain/MembershipTest.java @@ -4,7 +4,7 @@ import static com.gdschongik.gdsc.domain.member.domain.Member.*; import static com.gdschongik.gdsc.global.common.constant.MemberConstant.*; import static com.gdschongik.gdsc.global.common.constant.RecruitmentConstant.*; -import static com.gdschongik.gdsc.global.common.constant.SemesterConstant.*; +import static com.gdschongik.gdsc.global.common.constant.TemporalConstant.*; import static org.assertj.core.api.Assertions.*; import com.gdschongik.gdsc.domain.member.domain.Member; diff --git a/src/test/java/com/gdschongik/gdsc/domain/membership/domain/MembershipValidatorTest.java b/src/test/java/com/gdschongik/gdsc/domain/membership/domain/MembershipValidatorTest.java index 311d0d651..11181fa80 100644 --- a/src/test/java/com/gdschongik/gdsc/domain/membership/domain/MembershipValidatorTest.java +++ b/src/test/java/com/gdschongik/gdsc/domain/membership/domain/MembershipValidatorTest.java @@ -4,7 +4,7 @@ import static com.gdschongik.gdsc.domain.member.domain.Member.*; import static com.gdschongik.gdsc.global.common.constant.MemberConstant.*; import static com.gdschongik.gdsc.global.common.constant.RecruitmentConstant.*; -import static com.gdschongik.gdsc.global.common.constant.SemesterConstant.*; +import static com.gdschongik.gdsc.global.common.constant.TemporalConstant.*; import static com.gdschongik.gdsc.global.exception.ErrorCode.*; import static org.assertj.core.api.Assertions.*; diff --git a/src/test/java/com/gdschongik/gdsc/domain/order/application/OrderServiceTest.java b/src/test/java/com/gdschongik/gdsc/domain/order/application/OrderServiceTest.java index 1da174baf..ede1d1e61 100644 --- a/src/test/java/com/gdschongik/gdsc/domain/order/application/OrderServiceTest.java +++ b/src/test/java/com/gdschongik/gdsc/domain/order/application/OrderServiceTest.java @@ -2,6 +2,7 @@ import static com.gdschongik.gdsc.global.common.constant.OrderConstant.*; import static com.gdschongik.gdsc.global.common.constant.RecruitmentConstant.*; +import static com.gdschongik.gdsc.global.common.constant.TemporalConstant.*; import static com.gdschongik.gdsc.global.exception.ErrorCode.*; import static org.assertj.core.api.Assertions.*; import static org.mockito.Mockito.*; diff --git a/src/test/java/com/gdschongik/gdsc/domain/recruitment/application/AdminRecruitmentServiceTest.java b/src/test/java/com/gdschongik/gdsc/domain/recruitment/application/AdminRecruitmentServiceTest.java index ca1ee1fbc..f70ac4b68 100644 --- a/src/test/java/com/gdschongik/gdsc/domain/recruitment/application/AdminRecruitmentServiceTest.java +++ b/src/test/java/com/gdschongik/gdsc/domain/recruitment/application/AdminRecruitmentServiceTest.java @@ -1,7 +1,7 @@ package com.gdschongik.gdsc.domain.recruitment.application; import static com.gdschongik.gdsc.global.common.constant.RecruitmentConstant.*; -import static com.gdschongik.gdsc.global.common.constant.SemesterConstant.*; +import static com.gdschongik.gdsc.global.common.constant.TemporalConstant.*; import static com.gdschongik.gdsc.global.exception.ErrorCode.*; import static org.assertj.core.api.Assertions.*; diff --git a/src/test/java/com/gdschongik/gdsc/domain/recruitment/domain/RecruitmentRoundValidatorTest.java b/src/test/java/com/gdschongik/gdsc/domain/recruitment/domain/RecruitmentRoundValidatorTest.java index 919593027..0f9c27cd0 100644 --- a/src/test/java/com/gdschongik/gdsc/domain/recruitment/domain/RecruitmentRoundValidatorTest.java +++ b/src/test/java/com/gdschongik/gdsc/domain/recruitment/domain/RecruitmentRoundValidatorTest.java @@ -1,6 +1,7 @@ package com.gdschongik.gdsc.domain.recruitment.domain; import static com.gdschongik.gdsc.global.common.constant.RecruitmentConstant.*; +import static com.gdschongik.gdsc.global.common.constant.TemporalConstant.*; import static com.gdschongik.gdsc.global.exception.ErrorCode.*; import static org.assertj.core.api.Assertions.*; diff --git a/src/test/java/com/gdschongik/gdsc/domain/recruitment/domain/RecruitmentTest.java b/src/test/java/com/gdschongik/gdsc/domain/recruitment/domain/RecruitmentTest.java index 1b276f00e..b8f28b26e 100644 --- a/src/test/java/com/gdschongik/gdsc/domain/recruitment/domain/RecruitmentTest.java +++ b/src/test/java/com/gdschongik/gdsc/domain/recruitment/domain/RecruitmentTest.java @@ -1,7 +1,7 @@ package com.gdschongik.gdsc.domain.recruitment.domain; import static com.gdschongik.gdsc.global.common.constant.RecruitmentConstant.*; -import static com.gdschongik.gdsc.global.common.constant.SemesterConstant.*; +import static com.gdschongik.gdsc.global.common.constant.TemporalConstant.*; import static org.assertj.core.api.Assertions.*; import com.gdschongik.gdsc.domain.recruitment.domain.vo.Period; diff --git a/src/test/java/com/gdschongik/gdsc/domain/study/application/AdminStudyServiceTest.java b/src/test/java/com/gdschongik/gdsc/domain/study/application/AdminStudyServiceTest.java index c70a6b6ee..9cdd8dc1b 100644 --- a/src/test/java/com/gdschongik/gdsc/domain/study/application/AdminStudyServiceTest.java +++ b/src/test/java/com/gdschongik/gdsc/domain/study/application/AdminStudyServiceTest.java @@ -1,7 +1,7 @@ package com.gdschongik.gdsc.domain.study.application; -import static com.gdschongik.gdsc.global.common.constant.RecruitmentConstant.*; import static com.gdschongik.gdsc.global.common.constant.StudyConstant.*; +import static com.gdschongik.gdsc.global.common.constant.TemporalConstant.*; import static org.assertj.core.api.Assertions.*; import com.gdschongik.gdsc.domain.member.domain.Member; diff --git a/src/test/java/com/gdschongik/gdsc/domain/study/domain/AttendanceValidatorTest.java b/src/test/java/com/gdschongik/gdsc/domain/study/domain/AttendanceValidatorTest.java index a394dff05..f66643223 100644 --- a/src/test/java/com/gdschongik/gdsc/domain/study/domain/AttendanceValidatorTest.java +++ b/src/test/java/com/gdschongik/gdsc/domain/study/domain/AttendanceValidatorTest.java @@ -1,10 +1,8 @@ package com.gdschongik.gdsc.domain.study.domain; import static com.gdschongik.gdsc.global.common.constant.StudyConstant.ATTENDANCE_NUMBER; -import static com.gdschongik.gdsc.global.exception.ErrorCode.ATTENDANCE_DATE_INVALID; -import static com.gdschongik.gdsc.global.exception.ErrorCode.ATTENDANCE_NUMBER_MISMATCH; -import static com.gdschongik.gdsc.global.exception.ErrorCode.STUDY_DETAIL_ALREADY_ATTENDED; -import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static com.gdschongik.gdsc.global.exception.ErrorCode.*; +import static org.assertj.core.api.Assertions.*; import com.gdschongik.gdsc.domain.member.domain.Member; import com.gdschongik.gdsc.domain.recruitment.domain.vo.Period; @@ -16,6 +14,7 @@ import org.junit.jupiter.api.Test; public class AttendanceValidatorTest { + FixtureHelper fixtureHelper = new FixtureHelper(); AttendanceValidator attendanceValidator = new AttendanceValidator(); @@ -25,19 +24,19 @@ class 스터디_출석체크시 { @Test void 출석일자가_아니면_실패한다() { // given - Member mentor = fixtureHelper.createAssociateMember(1L); + Member student = fixtureHelper.createRegularMember(1L); LocalDateTime now = LocalDateTime.now(); Period period = Period.createPeriod(now.plusDays(10), now.plusDays(65)); Period applicationPeriod = Period.createPeriod(now.minusDays(10), now.plusDays(5)); - Study study = fixtureHelper.createStudy(mentor, period, applicationPeriod); + Study study = fixtureHelper.createStudy(student, period, applicationPeriod); StudyDetail studyDetail = fixtureHelper.createStudyDetail(study, now, now.plusDays(7)); LocalDate attendanceDay = studyDetail.getAttendanceDay(); // when & then assertThatThrownBy(() -> attendanceValidator.validateAttendance( - studyDetail, ATTENDANCE_NUMBER, attendanceDay.plusDays(1), false)) + studyDetail, ATTENDANCE_NUMBER, attendanceDay.plusDays(1), false, true)) .isInstanceOf(CustomException.class) .hasMessage(ATTENDANCE_DATE_INVALID.getMessage()); } @@ -45,19 +44,19 @@ class 스터디_출석체크시 { @Test void 출석번호가_다르면_실패한다() { // given - Member mentor = fixtureHelper.createAssociateMember(1L); + Member student = fixtureHelper.createRegularMember(1L); LocalDateTime now = LocalDateTime.now(); Period period = Period.createPeriod(now.plusDays(10), now.plusDays(65)); Period applicationPeriod = Period.createPeriod(now.minusDays(10), now.plusDays(5)); - Study study = fixtureHelper.createStudy(mentor, period, applicationPeriod); + Study study = fixtureHelper.createStudy(student, period, applicationPeriod); StudyDetail studyDetail = fixtureHelper.createStudyDetail(study, now, now.plusDays(7)); LocalDate attendanceDay = studyDetail.getAttendanceDay(); // when & then assertThatThrownBy(() -> attendanceValidator.validateAttendance( - studyDetail, ATTENDANCE_NUMBER + 1, attendanceDay, false)) + studyDetail, ATTENDANCE_NUMBER + 1, attendanceDay, false, true)) .isInstanceOf(CustomException.class) .hasMessage(ATTENDANCE_NUMBER_MISMATCH.getMessage()); } @@ -65,21 +64,41 @@ class 스터디_출석체크시 { @Test void 이미_출석했다면_실패한다() { // given - Member mentor = fixtureHelper.createAssociateMember(1L); + Member student = fixtureHelper.createRegularMember(1L); LocalDateTime now = LocalDateTime.now(); Period period = Period.createPeriod(now.plusDays(10), now.plusDays(65)); Period applicationPeriod = Period.createPeriod(now.minusDays(10), now.plusDays(5)); - Study study = fixtureHelper.createStudy(mentor, period, applicationPeriod); + Study study = fixtureHelper.createStudy(student, period, applicationPeriod); StudyDetail studyDetail = fixtureHelper.createStudyDetail(study, now, now.plusDays(7)); LocalDate attendanceDay = studyDetail.getAttendanceDay(); // when & then - assertThatThrownBy(() -> - attendanceValidator.validateAttendance(studyDetail, ATTENDANCE_NUMBER, attendanceDay, true)) + assertThatThrownBy(() -> attendanceValidator.validateAttendance( + studyDetail, ATTENDANCE_NUMBER, attendanceDay, true, true)) .isInstanceOf(CustomException.class) .hasMessage(STUDY_DETAIL_ALREADY_ATTENDED.getMessage()); } + + @Test + void 신청하지_않은_스터디라면_실패한다() { + // given + Member student = fixtureHelper.createRegularMember(1L); + + LocalDateTime now = LocalDateTime.now(); + Period period = Period.createPeriod(now.plusDays(10), now.plusDays(65)); + Period applicationPeriod = Period.createPeriod(now.minusDays(10), now.plusDays(5)); + Study study = fixtureHelper.createStudy(student, period, applicationPeriod); + StudyDetail studyDetail = fixtureHelper.createStudyDetail(study, now, now.plusDays(7)); + + LocalDate attendanceDay = studyDetail.getAttendanceDay(); + + // when & then + assertThatThrownBy(() -> attendanceValidator.validateAttendance( + studyDetail, ATTENDANCE_NUMBER, attendanceDay, false, false)) + .isInstanceOf(CustomException.class) + .hasMessage(STUDY_HISTORY_NOT_FOUND.getMessage()); + } } } diff --git a/src/test/java/com/gdschongik/gdsc/domain/study/domain/StudyTest.java b/src/test/java/com/gdschongik/gdsc/domain/study/domain/StudyTest.java index bd254bbaa..d98bccf2d 100644 --- a/src/test/java/com/gdschongik/gdsc/domain/study/domain/StudyTest.java +++ b/src/test/java/com/gdschongik/gdsc/domain/study/domain/StudyTest.java @@ -5,6 +5,7 @@ import static com.gdschongik.gdsc.global.common.constant.MemberConstant.*; import static com.gdschongik.gdsc.global.common.constant.RecruitmentConstant.*; import static com.gdschongik.gdsc.global.common.constant.StudyConstant.*; +import static com.gdschongik.gdsc.global.common.constant.TemporalConstant.*; import static com.gdschongik.gdsc.global.exception.ErrorCode.*; import static org.assertj.core.api.Assertions.assertThatThrownBy; diff --git a/src/test/java/com/gdschongik/gdsc/global/common/constant/RecruitmentConstant.java b/src/test/java/com/gdschongik/gdsc/global/common/constant/RecruitmentConstant.java index ac8f06584..beae5f5cb 100644 --- a/src/test/java/com/gdschongik/gdsc/global/common/constant/RecruitmentConstant.java +++ b/src/test/java/com/gdschongik/gdsc/global/common/constant/RecruitmentConstant.java @@ -1,6 +1,5 @@ package com.gdschongik.gdsc.global.common.constant; -import com.gdschongik.gdsc.domain.common.model.SemesterType; import com.gdschongik.gdsc.domain.common.vo.Money; import com.gdschongik.gdsc.domain.recruitment.domain.RoundType; import java.math.BigDecimal; @@ -13,8 +12,7 @@ public class RecruitmentConstant { public static final LocalDateTime BETWEEN_START_AND_END_DATE = LocalDateTime.of(2024, 3, 3, 0, 0); public static final LocalDateTime WRONG_END_DATE = LocalDateTime.of(2024, 3, 2, 0, 0); public static final LocalDateTime END_DATE = LocalDateTime.of(2024, 3, 5, 0, 0); - public static final Integer ACADEMIC_YEAR = 2024; - public static final SemesterType SEMESTER_TYPE = SemesterType.FIRST; + public static final Money FEE = Money.from(20000L); public static final BigDecimal FEE_AMOUNT = BigDecimal.valueOf(20000); public static final String FEE_NAME = "2024학년도 1학기 정회원 회비"; diff --git a/src/test/java/com/gdschongik/gdsc/global/common/constant/SemesterConstant.java b/src/test/java/com/gdschongik/gdsc/global/common/constant/SemesterConstant.java deleted file mode 100644 index a95bfd179..000000000 --- a/src/test/java/com/gdschongik/gdsc/global/common/constant/SemesterConstant.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.gdschongik.gdsc.global.common.constant; - -import java.time.LocalDateTime; - -public class SemesterConstant { - public static final LocalDateTime SEMESTER_START_DATE = LocalDateTime.of(2024, 3, 2, 0, 0); - public static final LocalDateTime SEMESTER_END_DATE = LocalDateTime.of(2024, 8, 31, 0, 0); - - private SemesterConstant() {} -} diff --git a/src/test/java/com/gdschongik/gdsc/global/common/constant/TemporalConstant.java b/src/test/java/com/gdschongik/gdsc/global/common/constant/TemporalConstant.java new file mode 100644 index 000000000..c15d6f76d --- /dev/null +++ b/src/test/java/com/gdschongik/gdsc/global/common/constant/TemporalConstant.java @@ -0,0 +1,16 @@ +package com.gdschongik.gdsc.global.common.constant; + +import com.gdschongik.gdsc.domain.common.model.SemesterType; +import java.time.LocalDateTime; + +public class TemporalConstant { + // academic year + public static final Integer ACADEMIC_YEAR = 2024; + + // semester + public static final SemesterType SEMESTER_TYPE = SemesterType.FIRST; + public static final LocalDateTime SEMESTER_START_DATE = LocalDateTime.of(2024, 3, 2, 0, 0); + public static final LocalDateTime SEMESTER_END_DATE = LocalDateTime.of(2024, 8, 31, 0, 0); + + private TemporalConstant() {} +} diff --git a/src/test/java/com/gdschongik/gdsc/helper/FixtureHelper.java b/src/test/java/com/gdschongik/gdsc/helper/FixtureHelper.java index 184dec360..ea1343667 100644 --- a/src/test/java/com/gdschongik/gdsc/helper/FixtureHelper.java +++ b/src/test/java/com/gdschongik/gdsc/helper/FixtureHelper.java @@ -5,8 +5,8 @@ import static com.gdschongik.gdsc.domain.member.domain.MemberStudyRole.MENTOR; import static com.gdschongik.gdsc.global.common.constant.MemberConstant.*; import static com.gdschongik.gdsc.global.common.constant.RecruitmentConstant.*; -import static com.gdschongik.gdsc.global.common.constant.SemesterConstant.*; import static com.gdschongik.gdsc.global.common.constant.StudyConstant.*; +import static com.gdschongik.gdsc.global.common.constant.TemporalConstant.*; import com.gdschongik.gdsc.domain.common.model.SemesterType; import com.gdschongik.gdsc.domain.common.vo.Money; diff --git a/src/test/java/com/gdschongik/gdsc/helper/IntegrationTest.java b/src/test/java/com/gdschongik/gdsc/helper/IntegrationTest.java index 1bfbac94d..dbd8c601b 100644 --- a/src/test/java/com/gdschongik/gdsc/helper/IntegrationTest.java +++ b/src/test/java/com/gdschongik/gdsc/helper/IntegrationTest.java @@ -3,8 +3,8 @@ import static com.gdschongik.gdsc.domain.member.domain.Department.*; import static com.gdschongik.gdsc.global.common.constant.MemberConstant.*; import static com.gdschongik.gdsc.global.common.constant.RecruitmentConstant.*; -import static com.gdschongik.gdsc.global.common.constant.SemesterConstant.*; import static com.gdschongik.gdsc.global.common.constant.StudyConstant.*; +import static com.gdschongik.gdsc.global.common.constant.TemporalConstant.*; import static org.mockito.Mockito.*; import com.gdschongik.gdsc.domain.common.model.SemesterType;