1+ package com .back .domain .notification .repository ;
2+
3+ import com .back .domain .notification .entity .NotificationSetting ;
4+ import com .back .domain .notification .entity .NotificationSettingType ;
5+ import com .back .domain .user .entity .User ;
6+ import com .back .domain .user .repository .UserRepository ;
7+ import com .back .global .config .QueryDslTestConfig ;
8+ import jakarta .persistence .EntityManager ;
9+ import org .junit .jupiter .api .BeforeEach ;
10+ import org .junit .jupiter .api .DisplayName ;
11+ import org .junit .jupiter .api .Nested ;
12+ import org .junit .jupiter .api .Test ;
13+ import org .springframework .beans .factory .annotation .Autowired ;
14+ import org .springframework .boot .test .autoconfigure .jdbc .AutoConfigureTestDatabase ;
15+ import org .springframework .boot .test .autoconfigure .orm .jpa .DataJpaTest ;
16+ import org .springframework .context .annotation .Import ;
17+ import org .springframework .dao .DataIntegrityViolationException ;
18+
19+ import java .util .List ;
20+ import java .util .Optional ;
21+
22+ import static org .assertj .core .api .Assertions .assertThat ;
23+ import static org .junit .jupiter .api .Assertions .assertThrows ;
24+
25+ @ DataJpaTest
26+ @ Import (QueryDslTestConfig .class )
27+ @ AutoConfigureTestDatabase (replace = AutoConfigureTestDatabase .Replace .NONE )
28+ class NotificationSettingRepositoryTest {
29+
30+ @ Autowired
31+ private NotificationSettingRepository notificationSettingRepository ;
32+
33+ @ Autowired
34+ private UserRepository userRepository ;
35+
36+ @ Autowired
37+ private EntityManager entityManager ;
38+
39+ private User userWithSettings ;
40+ private User userWithoutSettings ;
41+
42+ @ BeforeEach
43+ void setUp () {
44+ userWithSettings =
User .
builder ().
email (
"[email protected] " ).
username (
"유저1" ).
build ();
45+ userWithoutSettings =
User .
builder ().
email (
"[email protected] " ).
username (
"유저2" ).
build ();
46+ userRepository .saveAll (List .of (userWithSettings , userWithoutSettings ));
47+
48+ NotificationSetting commentSetting = NotificationSetting .create (
49+ userWithSettings ,
50+ NotificationSettingType .POST_COMMENT
51+ );
52+
53+ NotificationSetting likeSetting = NotificationSetting .create (
54+ userWithSettings ,
55+ NotificationSettingType .POST_LIKE ,
56+ false
57+ );
58+
59+ notificationSettingRepository .saveAll (List .of (commentSetting , likeSetting ));
60+
61+ entityManager .flush ();
62+ entityManager .clear ();
63+ }
64+
65+ @ Nested
66+ @ DisplayName ("알림 설정 조회 테스트" )
67+ class FindNotificationSettingsTest {
68+
69+ @ Test
70+ @ DisplayName ("특정 사용자의 모든 알림 설정 조회 성공" )
71+ void t1 () {
72+ // when
73+ List <NotificationSetting > settings = notificationSettingRepository .findAllByUserId (userWithSettings .getId ());
74+
75+ // then: Enum 타입이 정확한지 확인
76+ assertThat (settings ).hasSize (2 );
77+ assertThat (settings )
78+ .extracting (NotificationSetting ::getType )
79+ .containsExactlyInAnyOrder (NotificationSettingType .POST_COMMENT , NotificationSettingType .POST_LIKE );
80+ }
81+
82+ @ Test
83+ @ DisplayName ("특정 사용자의 특정 타입 알림 설정 조회 성공" )
84+ void t2 () {
85+ // when
86+ Optional <NotificationSetting > settingOpt = notificationSettingRepository .findByUserIdAndType (
87+ userWithSettings .getId (),
88+ NotificationSettingType .POST_COMMENT
89+ );
90+
91+ // then
92+ assertThat (settingOpt ).isPresent ();
93+ assertThat (settingOpt .get ().getType ()).isEqualTo (NotificationSettingType .POST_COMMENT );
94+ }
95+
96+ @ Test
97+ @ DisplayName ("존재하지 않는 타입 조회 시 빈 Optional 반환" )
98+ void t3 () {
99+ // when
100+ Optional <NotificationSetting > settingOpt = notificationSettingRepository .findByUserIdAndType (
101+ userWithSettings .getId (),
102+ NotificationSettingType .SYSTEM // 저장하지 않은 타입
103+ );
104+
105+ // then
106+ assertThat (settingOpt ).isEmpty ();
107+ }
108+
109+ @ Test
110+ @ DisplayName ("특정 사용자의 활성화된 알림 설정만 조회 성공" )
111+ void t4 () {
112+ // when
113+ List <NotificationSetting > enabledSettings = notificationSettingRepository .findEnabledSettingsByUserId (userWithSettings .getId ());
114+
115+ // then: setUp에서 활성화한 POST_COMMENT만 조회되어야 함
116+ assertThat (enabledSettings ).hasSize (1 );
117+ assertThat (enabledSettings .get (0 ).getType ()).isEqualTo (NotificationSettingType .POST_COMMENT );
118+ assertThat (enabledSettings .get (0 ).isEnabled ()).isTrue ();
119+ }
120+ }
121+
122+ @ Nested
123+ @ DisplayName ("알림 설정 변경 및 영속성 테스트" )
124+ class ChangePersistenceTest {
125+
126+ @ Test
127+ @ DisplayName ("toggle 메서드 호출 후 변경사항 DB에 반영" )
128+ void t1 () {
129+ // given: POST_COMMENT 설정(enabled=true)을 조회
130+ NotificationSetting setting = notificationSettingRepository .findByUserIdAndType (
131+ userWithSettings .getId (),
132+ NotificationSettingType .POST_COMMENT
133+ ).orElseThrow ();
134+ assertThat (setting .isEnabled ()).isTrue ();
135+
136+ // when: toggle 메서드 호출
137+ setting .toggle ();
138+ entityManager .flush (); // 변경 감지(Dirty Checking)를 통해 UPDATE 쿼리 실행
139+ entityManager .clear (); // 영속성 컨텍스트 초기화
140+
141+ // then: 다시 조회했을 때 enabled 상태가 false로 변경되어 있어야 함
142+ NotificationSetting updatedSetting = notificationSettingRepository .findById (setting .getId ()).orElseThrow ();
143+ assertThat (updatedSetting .isEnabled ()).isFalse ();
144+ }
145+ }
146+
147+ @ Nested
148+ @ DisplayName ("데이터 제약 조건 테스트" )
149+ class ConstraintTest {
150+
151+ @ Test
152+ @ DisplayName ("동일한 사용자와 타입으로 중복 설정 저장 시 예외 발생" )
153+ void t1 () {
154+ // given: 이미 POST_COMMENT 타입 설정이 존재하는 사용자
155+ // when: 동일한 타입으로 새로운 설정을 생성
156+ NotificationSetting duplicateSetting = NotificationSetting .create (
157+ userWithSettings ,
158+ NotificationSettingType .POST_COMMENT
159+ );
160+
161+ // then: Unique 제약 조건 위반으로 DataIntegrityViolationException이 발생해야 함
162+ assertThrows (DataIntegrityViolationException .class , () -> {
163+ notificationSettingRepository .saveAndFlush (duplicateSetting );
164+ });
165+ }
166+ }
167+ }
0 commit comments