-
Notifications
You must be signed in to change notification settings - Fork 1
prod #388
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
prod #388
Changes from all commits
10ef4d5
fb2b5f8
83a88a2
3e8ca30
0627278
20e381b
ec371ba
c2905fc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -13,7 +13,7 @@ public static String formatExamNumber(Integer roundCode, Integer areaCode, Integ | |||||
| } | ||||||
|
|
||||||
| return String.format( | ||||||
| "%d%02d%02d%04d", | ||||||
| "%d%d%02d%04d", | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 수험 번호 형식 문자열에서 이 변경으로 인해 수험 번호의 전체 길이가 가변적으로 변할 수 있습니다. 만약 시스템의 다른 부분에서 수험 번호가 고정 길이라고 가정하고 있다면, 이 변경이 예기치 않은 문제를 일으킬 수 있습니다. 이 변경이 의도된 것이며 관련된 모든 시스템에 영향이 없는지 다시 한번 확인해 보시는 것을 권장합니다.
Suggested change
|
||||||
| roundCode, | ||||||
| areaCode, | ||||||
| schoolCode, | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,110 @@ | ||
| package life.mosu.mosuserver.application.profile; | ||
|
|
||
| import static org.assertj.core.api.Assertions.assertThat; | ||
| import static org.junit.jupiter.api.Assertions.assertEquals; | ||
| import static org.junit.jupiter.api.Assertions.assertTrue; | ||
|
|
||
| import java.time.LocalDate; | ||
| import java.util.List; | ||
| import java.util.concurrent.CopyOnWriteArrayList; | ||
| import java.util.concurrent.CountDownLatch; | ||
| import java.util.concurrent.ExecutorService; | ||
| import java.util.concurrent.Executors; | ||
| import java.util.concurrent.TimeUnit; | ||
| import life.mosu.mosuserver.domain.profile.entity.Education; | ||
| import life.mosu.mosuserver.domain.profile.entity.Grade; | ||
| import life.mosu.mosuserver.domain.profile.repository.ProfileJpaRepository; | ||
| import life.mosu.mosuserver.domain.user.entity.AuthProvider; | ||
| import life.mosu.mosuserver.domain.user.entity.UserJpaEntity; | ||
| import life.mosu.mosuserver.domain.user.entity.UserRole; | ||
| import life.mosu.mosuserver.domain.user.repository.UserJpaRepository; | ||
| import life.mosu.mosuserver.global.exception.CustomRuntimeException; | ||
| import life.mosu.mosuserver.presentation.profile.dto.SchoolInfoRequest; | ||
| import life.mosu.mosuserver.presentation.profile.dto.SignUpProfileRequest; | ||
| 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; | ||
|
|
||
| @SpringBootTest | ||
| class ProfileServiceConcurrencyTest { | ||
|
|
||
| @Autowired | ||
| private ProfileService profileService; | ||
|
|
||
| @Autowired | ||
| private UserJpaRepository userRepository; | ||
|
|
||
| @Autowired | ||
| private ProfileJpaRepository profileJpaRepository; | ||
|
|
||
| private UserJpaEntity testUser; | ||
| private SignUpProfileRequest request; | ||
|
|
||
| @BeforeEach | ||
| void setUp() { | ||
| profileJpaRepository.deleteAllInBatch(); | ||
| userRepository.deleteAllInBatch(); | ||
|
|
||
| testUser = UserJpaEntity.builder() | ||
| .loginId("[email protected]") | ||
| .userRole(UserRole.ROLE_PENDING) | ||
| .phoneNumber("010-1234-5678") | ||
| .name("김영숙") | ||
| .provider(AuthProvider.KAKAO) | ||
| .birth(LocalDate.of(2007, 1, 1)) | ||
| .build(); | ||
| userRepository.save(testUser); | ||
|
|
||
| request = new SignUpProfileRequest( | ||
| "김영숙", | ||
| LocalDate.of(2007, 1, 1), | ||
| "여자", | ||
| "010-1234-5678", | ||
| "[email protected]", | ||
| Education.ENROLLED, | ||
| new SchoolInfoRequest("test school", "12345", "test street"), | ||
| Grade.HIGH_1 | ||
| ); | ||
| } | ||
|
|
||
| @Test | ||
| @DisplayName("동일한 사용자에 대한 프로필 등록이 동시에 요청되면 하나는 성공하고 하나는 Unique 제약조건 위반 예외를 던진다") | ||
| void registerProfile_concurrency_test() throws InterruptedException { | ||
| // given | ||
| int threadCount = 2; | ||
| ExecutorService executorService = Executors.newFixedThreadPool(threadCount); | ||
| CountDownLatch latch = new CountDownLatch(threadCount); | ||
| List<Exception> exceptions = new CopyOnWriteArrayList<>(); | ||
|
|
||
| // when | ||
| for (int i = 0; i < threadCount; i++) { | ||
| executorService.submit(() -> { | ||
| try { | ||
| latch.countDown(); | ||
| latch.await(); | ||
|
|
||
| profileService.registerProfile(testUser.getId(), request); | ||
| } catch (InterruptedException e) { | ||
| Thread.currentThread().interrupt(); | ||
| } catch (Exception e) { | ||
| exceptions.add(e); | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| executorService.shutdown(); | ||
| assertTrue(executorService.awaitTermination(5, TimeUnit.SECONDS), | ||
| "스레드 풀이 시간 내에 종료되지 않았습니다."); | ||
|
|
||
| // then | ||
| long profileCount = profileJpaRepository.count(); | ||
| assertEquals(1, profileCount, "경쟁 조건으로 인해 프로필이 중복 생성되거나 생성되지 않았습니다."); | ||
|
|
||
| assertThat(exceptions).hasSize(1); | ||
|
|
||
| Exception thrownException = exceptions.getFirst(); | ||
| assertThat(thrownException).isInstanceOf(CustomRuntimeException.class); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
새로운 동시성 테스트 파일(
ProfileServiceConcurrencyTest.java)을 테스트 소스셋에서 제외하고 있습니다. 이 테스트는ProfileService.registerProfile의 중요한 동시성 문제를 검증하기 위해 추가된 것으로 보입니다. 이 테스트를 제외하면 해당 로직의 동시성 안전성을 보장할 수 없게 됩니다.만약 테스트가 CI/CD 파이프라인에서 실행하기에 너무 오래 걸리거나 불안정하다면, 빌드 스크립트에서 완전히 제외하는 대신
@Disabled어노테이션을 사용하여 테스트 클래스나 메서드 수준에서 비활성화하고, 그 이유를 주석으로 명시하는 것이 좋습니다. 이렇게 하면 다른 개발자들이 테스트의 존재와 비활성화된 이유를 명확히 알 수 있습니다.가장 좋은 방법은 테스트를 수정하여 안정적이고 빠르게 실행되도록 한 후, 테스트 셋에 다시 포함시키는 것입니다. 동시성 버그는 재현하기 어렵고 심각한 문제를 일으킬 수 있으므로, 이를 검증하는 테스트는 매우 중요합니다.