Skip to content

Conversation

@chominju02
Copy link
Contributor

✨ 구현한 기능

📢 논의하고 싶은 내용

🎸 기타

@coderabbitai
Copy link

coderabbitai bot commented Oct 12, 2025

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch develop

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist
Copy link

Summary of Changes

Hello @chominju02, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request appears to be a set of urgent adjustments, possibly for a production environment, given the title 'prod'. It includes a temporary bypass of an exam date validation, an update to a cron job schedule, and the complete disabling of unit tests for the application service. The changes suggest a focus on immediate operational concerns rather than new feature development or standard bug fixes, potentially to resolve a critical issue or facilitate a specific deployment scenario.

Highlights

  • Application Service Validation Adjustment: The examDateNotPassed validation in ApplicationService.java has been temporarily commented out. This change likely addresses an immediate issue or allows for specific testing scenarios by bypassing the date-based exam validation.
  • Cron Job Schedule Update: The ExamNumberGenerationJobRound1 cron job schedule has been updated from 9 AM to 10 AM on October 13th, 2025. This adjusts the execution time for the exam number generation process.
  • Application Service Tests Disabled: The entire ApplicationServiceTest.java file has been commented out, effectively disabling all unit tests for the ApplicationService. This is a significant change that removes test coverage for this service.
  • Import Statement Refactoring: Import statements in ApplicationService.java have been refactored. Specifically, multiple DTO imports were consolidated into a single wildcard import, and java.util.List and java.util.Set imports were moved within the file.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

이 PR은 cron 작업 시간 변경, import 정리, 그리고 몇 가지 중요한 코드 비활성화를 포함하고 있습니다. 특히, 시험 신청 마감일 검증 로직과 ApplicationService의 전체 테스트 스위트가 주석 처리된 점은 매우 우려스럽습니다. 이러한 변경은 프로덕션 환경에서 심각한 오류를 유발할 수 있으므로 배포 전에 반드시 수정되어야 합니다. 또한, 와일드카드 import 사용은 코드 가독성을 저해할 수 있어 개선이 필요합니다.

) {
List<ExamJpaEntity> exams = examJpaRepository.findAllById(examIds);
validator.examDateNotPassed(exams);
// validator.examDateNotPassed(exams);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

시험 신청 마감일 검증 로직(validator.examDateNotPassed(exams))이 주석 처리되었습니다. 이 검증을 비활성화하면 마감일이 지난 시험에도 신청이 가능해져 데이터 정합성 문제 및 심각한 비즈니스 로직 오류를 유발할 수 있습니다. 이 코드를 주석 처리한 특별한 이유가 없다면 즉시 복원해야 합니다. 만약 테스트 등의 목적으로 임시 변경한 것이라면, 프로덕션에 배포되기 전에 반드시 원상 복구되어야 합니다.

Suggested change
// validator.examDateNotPassed(exams);
validator.examDateNotPassed(exams);

Comment on lines +1 to +352
//package life.mosu.mosuserver.application.application;
//
//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.ArgumentMatchers.anyList;
//import static org.mockito.ArgumentMatchers.eq;
//import static org.mockito.BDDMockito.given;
//import static org.mockito.BDDMockito.then;
//import static org.mockito.Mockito.doThrow;
//import static org.mockito.Mockito.never;
//
//import java.time.LocalDate;
//import java.util.List;
//import java.util.Set;
//import life.mosu.mosuserver.application.application.dto.RegisterApplicationCommand;
//import life.mosu.mosuserver.application.application.processor.GetApplicationsStepProcessor;
//import life.mosu.mosuserver.application.application.processor.RegisterApplicationStepProcessor;
//import life.mosu.mosuserver.application.application.processor.SaveExamTicketStepProcessor;
//import life.mosu.mosuserver.application.application.validator.ApplicationValidator;
//import life.mosu.mosuserver.application.exam.cache.ExamQuotaCacheManager;
//import life.mosu.mosuserver.application.user.UserService;
//import life.mosu.mosuserver.domain.application.entity.ApplicationJpaEntity;
//import life.mosu.mosuserver.domain.application.repository.ApplicationJpaRepository;
//import life.mosu.mosuserver.domain.exam.entity.ExamJpaEntity;
//import life.mosu.mosuserver.domain.exam.entity.ExamJpaRepository;
//import life.mosu.mosuserver.global.exception.CustomRuntimeException;
//import life.mosu.mosuserver.global.exception.ErrorCode;
//import life.mosu.mosuserver.presentation.application.dto.AgreementRequest;
//import life.mosu.mosuserver.presentation.application.dto.ApplicationGuestRequest;
//import life.mosu.mosuserver.presentation.application.dto.ApplicationRequest;
//import life.mosu.mosuserver.presentation.application.dto.ApplicationResponse;
//import life.mosu.mosuserver.presentation.application.dto.CreateApplicationResponse;
//import life.mosu.mosuserver.presentation.application.dto.ExamApplicationRequest;
//import life.mosu.mosuserver.presentation.common.FileRequest;
//import org.junit.jupiter.api.BeforeEach;
//import org.junit.jupiter.api.DisplayName;
//import org.junit.jupiter.api.Nested;
//import org.junit.jupiter.api.Test;
//import org.junit.jupiter.api.extension.ExtendWith;
//import org.mockito.Mock;
//import org.mockito.junit.jupiter.MockitoExtension;
//
//@ExtendWith(MockitoExtension.class)
//@DisplayName("ApplicationService 단위 테스트")
//class ApplicationServiceTest {
//
// private ApplicationService applicationService;
//
// @Mock
// private UserService userService;
//
// @Mock
// private ApplicationJpaRepository applicationJpaRepository;
//
// @Mock
// private ExamJpaRepository examJpaRepository;
//
// @Mock
// private ExamQuotaCacheManager cacheManager;
//
// @Mock
// private RegisterApplicationStepProcessor registerApplicationStepProcessor;
//
// @Mock
// private SaveExamTicketStepProcessor saveExamTicketStepProcessor;
//
// @Mock
// private GetApplicationsStepProcessor getApplicationsStepProcessor;
//
// @Mock
// private ApplicationValidator validator;
//
// @BeforeEach
// void setUp() {
// applicationService = new ApplicationService(
// userService,
// applicationJpaRepository,
// examJpaRepository,
// cacheManager,
// registerApplicationStepProcessor,
// saveExamTicketStepProcessor,
// getApplicationsStepProcessor,
// validator
// );
// }
//
// // 테스트 데이터 생성 메서드들
// private ApplicationRequest createApplicationRequest() {
// return new ApplicationRequest(
// new FileRequest("test-file.pdf", "base64encodedcontent"),
// "010-1234-5678",
// List.of(
// new ExamApplicationRequest(1L, true),
// new ExamApplicationRequest(2L, false)
// ),
// new AgreementRequest(true, true),
// List.of("국어", "수학")
// );
// }
//
// private ApplicationRequest createApplicationRequestWithDuplicateExams() {
// return new ApplicationRequest(
// new FileRequest("test-file.pdf", "base64encodedcontent"),
// "010-1234-5678",
// List.of(
// new ExamApplicationRequest(1L, true),
// new ExamApplicationRequest(1L, false) // 중복된 시험 ID
// ),
// new AgreementRequest(true, true),
// List.of("국어", "수학")
// );
// }
//
// private ApplicationRequest createApplicationRequestWithWrongSubjects() {
// return new ApplicationRequest(
// new FileRequest("test-file.pdf", "base64encodedcontent"),
// "010-1234-5678",
// List.of(
// new ExamApplicationRequest(1L, true),
// new ExamApplicationRequest(2L, false)
// ),
// new AgreementRequest(true, true),
// List.of("국어") // 과목이 1개만 있음 (2개여야 함)
// );
// }
//
// private ApplicationGuestRequest createApplicationGuestRequest() {
// return new ApplicationGuestRequest(
// "테스트 학교",
// "남자",
// "홍길동",
// LocalDate.of(2000, 1, 1),
// "010-1234-5678",
// new ExamApplicationRequest(1L, true),
// Set.of("국어", "수학"),
// new FileRequest("test-file.pdf", "base64encodedcontent")
// );
// }
//
// private ApplicationGuestRequest createApplicationGuestRequestWithWrongSubjects() {
// return new ApplicationGuestRequest(
// "테스트 학교",
// "남자",
// "홍길동",
// LocalDate.of(2000, 1, 1),
// "010-1234-5678",
// new ExamApplicationRequest(1L, true),
// Set.of("국어"), // 과목이 1개만 있음 (2개여야 함)
// new FileRequest("test-file.pdf", "base64encodedcontent")
// );
// }
//
// @Nested
// @DisplayName("apply 메서드 테스트")
// class ApplyTest {
//
// @Test
// @DisplayName("정상적인 시험 신청 - 성공")
// void apply_Success() {
// // given
// Long userId = 1L;
// ApplicationRequest request = createApplicationRequest();
// ApplicationJpaEntity savedApplication = new ApplicationJpaEntity(userId,
// "010-1234-5678", true, true);
// // Reflection을 사용하여 ID 설정
// try {
// var idField = ApplicationJpaEntity.class.getDeclaredField("id");
// idField.setAccessible(true);
// idField.set(savedApplication, 1L);
// } catch (Exception e) {
// // 테스트용이므로 간단히 처리
// }
//
// List<ExamJpaEntity> exams = List.of(
// ExamJpaEntity.builder().build(),
// ExamJpaEntity.builder().build()
// );
//
// given(examJpaRepository.findAllById(anyList())).willReturn(exams);
// given(applicationJpaRepository.save(any(ApplicationJpaEntity.class))).willReturn(
// savedApplication);
//
// // when
// CreateApplicationResponse response = applicationService.apply(userId, request);
//
// // then
// assertThat(response).isNotNull();
// assertThat(response.applicationId()).isEqualTo(1L);
//
// then(validator).should().agreedToTerms(request);
// then(validator).should().requestNoDuplicateExams(anyList());
// then(validator).should().examDateNotPassed(exams);
// then(validator).should().examNotFull(exams);
// then(validator).should().examIdsAndLunchSelection(anyList());
// then(validator).should().noDuplicateApplication(eq(userId), anyList());
// then(applicationJpaRepository).should().save(any(ApplicationJpaEntity.class));
// then(registerApplicationStepProcessor).should()
// .process(any(RegisterApplicationCommand.class));
// then(saveExamTicketStepProcessor).should().process(any());
//
// }
//
// @Test
// @DisplayName("약관 동의하지 않은 경우 - 실패")
// void apply_WhenNotAgreedToTerms_ThrowsException() {
// // given
// Long userId = 1L;
// ApplicationRequest request = createApplicationRequest();
//
// doThrow(new CustomRuntimeException(ErrorCode.NOT_AGREED_TO_TERMS))
// .when(validator).agreedToTerms(request);
//
// // when & then
// assertThatThrownBy(() -> applicationService.apply(userId, request))
// .isInstanceOf(CustomRuntimeException.class)
// .hasMessage(ErrorCode.NOT_AGREED_TO_TERMS.getMessage());
//
// then(validator).should().agreedToTerms(request);
// then(examJpaRepository).should(never()).findAllById(anyList());
// }
//
// @Test
// @DisplayName("중복된 시험 ID가 있는 경우 - 실패")
// void apply_WhenDuplicateExamIds_ThrowsException() {
// // given
// Long userId = 1L;
// ApplicationRequest request = createApplicationRequestWithDuplicateExams();
//
// doThrow(new CustomRuntimeException(ErrorCode.EXAM_DUPLICATED))
// .when(validator).requestNoDuplicateExams(anyList());
//
// // when & then
// assertThatThrownBy(() -> applicationService.apply(userId, request))
// .isInstanceOf(CustomRuntimeException.class)
// .hasMessage(ErrorCode.EXAM_DUPLICATED.getMessage());
//
// then(validator).should().agreedToTerms(request);
// then(validator).should().requestNoDuplicateExams(anyList());
// then(examJpaRepository).should(never()).findAllById(anyList());
// }
//
// @Test
// @DisplayName("잘못된 과목 개수 - 실패")
// void apply_WhenWrongSubjectCount_ThrowsException() {
// // given
// Long userId = 1L;
// ApplicationRequest request = createApplicationRequestWithWrongSubjects();
//
// // when & then
// assertThatThrownBy(() -> applicationService.apply(userId, request))
// .isInstanceOf(CustomRuntimeException.class)
// .hasMessage(ErrorCode.WRONG_SUBJECT_COUNT.getMessage());
// }
// }
//
// @Nested
// @DisplayName("applyByGuest 메서드 테스트")
// class ApplyByGuestTest {
//
// @Test
// @DisplayName("게스트 신청 - 성공")
// void applyByGuest_Success() {
// // given
// ApplicationGuestRequest request = createApplicationGuestRequest();
// Long userId = 1L;
// ApplicationJpaEntity savedApplication = ApplicationJpaEntity.builder()
// .userId(userId)
// .agreedToNotices(true)
// .agreedToRefundPolicy(true)
// .build();
// List<ExamJpaEntity> exams = List.of(ExamJpaEntity.builder().build());
//
// given(userService.saveOrGetUser(any())).willReturn(userId);
// given(examJpaRepository.findAllById(anyList())).willReturn(exams);
// given(applicationJpaRepository.save(any(ApplicationJpaEntity.class))).willReturn(
// savedApplication);
//
// // when
// CreateApplicationResponse response = applicationService.applyByGuest(request);
//
// // then
// assertThat(response).isNotNull();
// assertThat(response.applicationId()).isNull(); // ID는 builder에서 설정하지 않으므로 null
//
// then(userService).should().saveOrGetUser(any());
// then(examJpaRepository).should().findAllById(anyList());
// then(cacheManager).should()
// .increaseCurrentApplications(request.examApplication().examId());
// }
//
// @Test
// @DisplayName("게스트 신청 시 잘못된 과목 개수 - 실패")
// void applyByGuest_WhenWrongSubjectCount_ThrowsException() {
// // given
// ApplicationGuestRequest request = createApplicationGuestRequestWithWrongSubjects();
//
// // when & then
// assertThatThrownBy(() -> applicationService.applyByGuest(request))
// .isInstanceOf(CustomRuntimeException.class)
// .hasMessage(ErrorCode.WRONG_SUBJECT_COUNT.getMessage());
// }
// }
//
// @Nested
// @DisplayName("getApplications 메서드 테스트")
// class GetApplicationsTest {
//
// @Test
// @DisplayName("사용자의 신청 목록 조회 - 성공")
// void getApplications_Success() {
// // given
// Long userId = 1L;
// List<ApplicationResponse> expectedApplications = List.of(
// new ApplicationResponse(1L, List.of()),
// new ApplicationResponse(2L, List.of())
// );
//
// given(getApplicationsStepProcessor.process(userId))
// .willReturn(expectedApplications);
//
// // when
// List<ApplicationResponse> applications = applicationService.getApplications(userId);
//
// // then
// assertThat(applications).hasSize(2);
// assertThat(applications.get(0).applicationId()).isEqualTo(1L);
// assertThat(applications.get(1).applicationId()).isEqualTo(2L);
//
// then(getApplicationsStepProcessor).should().process(userId);
// }
//
// @Test
// @DisplayName("신청 목록이 없는 경우 - 빈 목록 반환")
// void getApplications_WhenNoApplications_ReturnsEmptyList() {
// // given
// Long userId = 1L;
// List<ApplicationResponse> expectedApplications = List.of();
//
// given(getApplicationsStepProcessor.process(userId))
// .willReturn(expectedApplications);
//
// // when
// List<ApplicationResponse> applications = applicationService.getApplications(userId);
//
// // then
// assertThat(applications).isEmpty();
//
// then(getApplicationsStepProcessor).should().process(userId);
// }
// }
//}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

ApplicationService에 대한 모든 단위 테스트가 주석 처리되었습니다. 테스트 코드를 비활성화하면 코드 변경으로 인한 잠재적인 버그나 회귀(regression) 문제를 감지할 수 없게 됩니다. 이는 애플리케이션의 안정성을 심각하게 저해하는 행위입니다. 코드 변경 사항이 있다면, 그에 맞게 테스트를 수정하거나 새로운 테스트를 추가해야 하며, 기존 테스트를 비활성화해서는 안 됩니다. 프로덕션 배포를 위해서는 모든 테스트가 통과하는 상태여야 합니다.

import life.mosu.mosuserver.presentation.application.dto.CreateApplicationResponse;
import life.mosu.mosuserver.presentation.application.dto.ExamApplicationRequest;
import life.mosu.mosuserver.presentation.application.dto.SchoolApplicationCountResponse;
import life.mosu.mosuserver.presentation.application.dto.*;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

와일드카드(*) import는 사용을 지양하는 것이 좋습니다. Google Java Style Guide와 같은 여러 스타일 가이드에서는 클래스를 명시적으로 import하여 코드의 가독성을 높이고, 어떤 클래스가 사용되는지 명확하게 파악하여 유지보수를 용이하게 할 것을 권장합니다. 또한, 이름 충돌 가능성을 줄일 수 있습니다.

Suggested change
import life.mosu.mosuserver.presentation.application.dto.*;
import life.mosu.mosuserver.presentation.application.dto.ApplicationGuestRequest;
import life.mosu.mosuserver.presentation.application.dto.ApplicationRequest;
import life.mosu.mosuserver.presentation.application.dto.ApplicationResponse;
import life.mosu.mosuserver.presentation.application.dto.CreateApplicationResponse;
import life.mosu.mosuserver.presentation.application.dto.ExamApplicationRequest;
import life.mosu.mosuserver.presentation.application.dto.SchoolApplicationCountResponse;

@chominju02 chominju02 merged commit 2c0c60c into prod Oct 12, 2025
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants