-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathProfileServiceConcurrencyTest.java
More file actions
110 lines (93 loc) · 4.14 KB
/
ProfileServiceConcurrencyTest.java
File metadata and controls
110 lines (93 loc) · 4.14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
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("testUser@example.com")
.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",
"test@example.com",
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);
}
}