Skip to content

Commit d7497ba

Browse files
authored
feat: 유저 태그 수정 및 삭제 시 이미지 태그 반영 (#135)
* feat: 유저 태그 업데이트 시 이미지 태그 업데이트 * feat: 유저 태그 삭제 시 이미지 태그 삭제 * refactor: BDDMockito로 수정 * feat: 유저 태그 최대 개수 수정
1 parent aad4e48 commit d7497ba

File tree

8 files changed

+237
-5
lines changed

8 files changed

+237
-5
lines changed

capturecat-core/src/main/java/com/capturecat/core/api/user/UserTagController.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import com.capturecat.core.api.user.dto.TagRenameRequest;
1818
import com.capturecat.core.service.auth.LoginUser;
1919
import com.capturecat.core.service.image.TagResponse;
20+
import com.capturecat.core.service.tag.ImageTagService;
2021
import com.capturecat.core.service.user.UserTagService;
2122
import com.capturecat.core.support.response.ApiResponse;
2223
import com.capturecat.core.support.response.CursorResponse;
@@ -27,6 +28,7 @@
2728
public class UserTagController {
2829

2930
private final UserTagService userTagService;
31+
private final ImageTagService imageTagService;
3032

3133
@PostMapping
3234
public ApiResponse<TagResponse> create(@AuthenticationPrincipal LoginUser loginUser, @RequestParam String tagName) {
@@ -44,16 +46,20 @@ public ApiResponse<CursorResponse<TagResponse>> getAll(@AuthenticationPrincipal
4446
}
4547

4648
@PatchMapping
47-
public ApiResponse<TagResponse> update(@AuthenticationPrincipal LoginUser loginUser,
48-
@RequestBody TagRenameRequest request) {
49+
public ApiResponse<TagResponse> update(
50+
@AuthenticationPrincipal LoginUser loginUser,
51+
@RequestBody TagRenameRequest request
52+
) {
4953
TagResponse response = userTagService.update(loginUser, request.currentTagId(), request.newTagName());
54+
imageTagService.update(loginUser, request.currentTagId(), response.id());
5055

5156
return ApiResponse.success(response);
5257
}
5358

5459
@DeleteMapping
5560
public ApiResponse<?> delete(@AuthenticationPrincipal LoginUser loginUser, @RequestParam Long tagId) {
5661
userTagService.delete(loginUser, tagId);
62+
imageTagService.delete(loginUser, tagId);
5763

5864
return ApiResponse.success();
5965
}

capturecat-core/src/main/java/com/capturecat/core/domain/tag/ImageTagRepository.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ public interface ImageTagRepository extends JpaRepository<ImageTag, Long> {
3232
@Query("DELETE FROM ImageTag it WHERE it.tag = :tag AND it.image.user = :user")
3333
void deleteByTagAndUser(Tag tag, User user);
3434

35+
@Modifying(clearAutomatically = true, flushAutomatically = true)
36+
@Query("UPDATE ImageTag it SET it.tag.id = :newTagId WHERE it.tag.id = :oldTagId AND it.image.user.id = :userId")
37+
void updateImageTagsForUser(Long userId, Long oldTagId, Long newTagId);
38+
3539
@Modifying(clearAutomatically = true, flushAutomatically = true)
3640
@Query("DELETE FROM ImageTag it WHERE it.image.id IN (SELECT i.id FROM Image i WHERE i.user.id = :userId)")
3741
void deleteAllTagsByUserId(Long userId);
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package com.capturecat.core.service.tag;
2+
3+
import org.springframework.stereotype.Service;
4+
import org.springframework.transaction.annotation.Transactional;
5+
6+
import lombok.RequiredArgsConstructor;
7+
8+
import com.capturecat.core.domain.tag.ImageTagRepository;
9+
import com.capturecat.core.domain.tag.Tag;
10+
import com.capturecat.core.domain.tag.TagRepository;
11+
import com.capturecat.core.domain.user.User;
12+
import com.capturecat.core.domain.user.UserRepository;
13+
import com.capturecat.core.service.auth.LoginUser;
14+
import com.capturecat.core.support.error.CoreException;
15+
import com.capturecat.core.support.error.ErrorType;
16+
17+
@Service
18+
@RequiredArgsConstructor
19+
public class ImageTagService {
20+
21+
private final ImageTagRepository imageTagRepository;
22+
private final UserRepository userRepository;
23+
private final TagRepository tagRepository;
24+
25+
@Transactional
26+
public void update(LoginUser loginUser, Long oldTagId, Long newTagId) {
27+
User user = userRepository.findByUsername(loginUser.getUsername())
28+
.orElseThrow(() -> new CoreException(ErrorType.USER_NOT_FOUND));
29+
30+
imageTagRepository.updateImageTagsForUser(user.getId(), oldTagId, newTagId);
31+
}
32+
33+
@Transactional
34+
public void delete(LoginUser loginUser, Long tagId) {
35+
User user = userRepository.findByUsername(loginUser.getUsername())
36+
.orElseThrow(() -> new CoreException(ErrorType.USER_NOT_FOUND));
37+
Tag tag = tagRepository.findById(tagId)
38+
.orElseThrow(() -> new CoreException(ErrorType.TAG_NOT_FOUND));
39+
40+
imageTagRepository.deleteByTagAndUser(tag, user);
41+
}
42+
}

capturecat-core/src/main/java/com/capturecat/core/service/user/UserTagService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
@RequiredArgsConstructor
3131
public class UserTagService {
3232

33-
private static final int MAX_USER_TAG_COUNT = 30;
33+
private static final int MAX_USER_TAG_COUNT = 40;
3434

3535
private final UserRepository userRepository;
3636
private final UserTagRepository userTagRepository;

capturecat-core/src/test/java/com/capturecat/core/api/user/UserTagControllerTest.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929

3030
import com.capturecat.core.config.jwt.JwtUtil;
3131
import com.capturecat.core.service.image.TagResponse;
32+
import com.capturecat.core.service.tag.ImageTagService;
3233
import com.capturecat.core.service.user.UserTagService;
3334
import com.capturecat.core.support.response.CursorResponse;
3435
import com.capturecat.test.api.RestDocsTest;
@@ -39,11 +40,13 @@ class UserTagControllerTest extends RestDocsTest {
3940

4041
private UserTagController userTagController;
4142
private UserTagService userTagService;
43+
private ImageTagService imageTagService;
4244

4345
@BeforeEach
4446
void setUp() {
4547
userTagService = mock(UserTagService.class);
46-
userTagController = new UserTagController(userTagService);
48+
imageTagService = mock(ImageTagService.class);
49+
userTagController = new UserTagController(userTagService, imageTagService);
4750
mockMvc = mockController(userTagController);
4851
}
4952

@@ -101,6 +104,7 @@ void setUp() {
101104
void 유저_태그_수정() {
102105
// given
103106
BDDMockito.given(userTagService.update(any(), anyLong(), anyString())).willReturn(new TagResponse(1L, "java"));
107+
BDDMockito.willDoNothing().given(imageTagService).update(any(), anyLong(), anyLong());
104108

105109
// when & then
106110
given()
@@ -126,6 +130,7 @@ void setUp() {
126130
void 유저_태그_삭제() {
127131
// given
128132
BDDMockito.given(userTagService.create(any(), anyString())).willReturn(new TagResponse(1L, "java"));
133+
BDDMockito.willDoNothing().given(imageTagService).delete(any(), anyLong());
129134

130135
// when & then
131136
given()

capturecat-core/src/test/java/com/capturecat/core/domain/tag/ImageTagRepositoryTest.java

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,4 +114,63 @@ class ImageTagRepositoryTest {
114114
assertThat(user2Result).hasSize(1);
115115
assertThat(user2Result.get(0).getImage().getUser()).isEqualTo(user2);
116116
}
117+
118+
@Test
119+
void 지정한_사용자만_태그가_교체된다() {
120+
// given
121+
var user1 = userRepository.save(DummyObject.newUser("user1"));
122+
var user2 = userRepository.save(DummyObject.newUser("user2"));
123+
124+
var image1 = imageRepository.save(DummyObject.newMockUserImage(user1));
125+
var image2 = imageRepository.save(DummyObject.newMockUserImage(user2));
126+
127+
var oldTag = tagRepository.save(TagFixture.createTag("old"));
128+
var newTag = tagRepository.save(TagFixture.createTag("new"));
129+
130+
imageTagRepository.save(new ImageTag(image1, oldTag));
131+
imageTagRepository.save(new ImageTag(image2, oldTag));
132+
133+
entityManager.flush();
134+
entityManager.clear();
135+
136+
// when
137+
imageTagRepository.updateImageTagsForUser(user1.getId(), oldTag.getId(), newTag.getId());
138+
139+
entityManager.flush();
140+
entityManager.clear();
141+
142+
// then
143+
var it1 = imageTagRepository.findByImage(image1).get(0);
144+
var it2 = imageTagRepository.findByImage(image2).get(0);
145+
146+
assertThat(it1.getTag().getId()).isEqualTo(newTag.getId());
147+
assertThat(it2.getTag().getId()).isEqualTo(oldTag.getId());
148+
}
149+
150+
@Test
151+
void 태그가_없는_이미지에는_영향이_없다() {
152+
// given
153+
var user = userRepository.save(DummyObject.newUser("noTagUser"));
154+
var imageWithNoTag = imageRepository.save(DummyObject.newMockUserImage(user));
155+
156+
var oldTag = tagRepository.save(TagFixture.createTag("old"));
157+
var newTag = tagRepository.save(TagFixture.createTag("new"));
158+
159+
var imageWithTag = imageRepository.save(DummyObject.newMockUserImage(user));
160+
imageTagRepository.save(new ImageTag(imageWithTag, oldTag));
161+
162+
entityManager.flush();
163+
entityManager.clear();
164+
165+
// when
166+
imageTagRepository.updateImageTagsForUser(user.getId(), oldTag.getId(), newTag.getId());
167+
168+
entityManager.flush();
169+
entityManager.clear();
170+
171+
// then
172+
assertThat(imageTagRepository.findByImage(imageWithNoTag)).isEmpty();
173+
var updated = imageTagRepository.findByImage(imageWithTag).get(0);
174+
assertThat(updated.getTag().getId()).isEqualTo(newTag.getId());
175+
}
117176
}
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
package com.capturecat.core.service.tag;
2+
3+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
4+
import static org.mockito.ArgumentMatchers.anyLong;
5+
import static org.mockito.ArgumentMatchers.anyString;
6+
import static org.mockito.BDDMockito.given;
7+
import static org.mockito.Mockito.times;
8+
import static org.mockito.Mockito.verify;
9+
import static org.mockito.Mockito.verifyNoInteractions;
10+
11+
import java.util.Optional;
12+
13+
import org.junit.jupiter.api.Test;
14+
import org.junit.jupiter.api.extension.ExtendWith;
15+
import org.mockito.InjectMocks;
16+
import org.mockito.Mock;
17+
import org.mockito.junit.jupiter.MockitoExtension;
18+
19+
import com.capturecat.core.DummyObject;
20+
import com.capturecat.core.domain.tag.ImageTagRepository;
21+
import com.capturecat.core.domain.tag.Tag;
22+
import com.capturecat.core.domain.tag.TagFixture;
23+
import com.capturecat.core.domain.tag.TagRepository;
24+
import com.capturecat.core.domain.user.User;
25+
import com.capturecat.core.domain.user.UserRepository;
26+
import com.capturecat.core.service.auth.LoginUser;
27+
import com.capturecat.core.support.error.CoreException;
28+
29+
@ExtendWith(MockitoExtension.class)
30+
class ImageTagServiceTest {
31+
32+
@Mock
33+
ImageTagRepository imageTagRepository;
34+
35+
@Mock
36+
UserRepository userRepository;
37+
38+
@Mock
39+
TagRepository tagRepository;
40+
41+
@InjectMocks
42+
ImageTagService imageTagService;
43+
44+
@Test
45+
void 업데이트_사용자_존재하면_레포지토리_호출한다() {
46+
// given
47+
User user = DummyObject.newMockUser(123L);
48+
49+
given(userRepository.findByUsername(anyString())).willReturn(Optional.of(user));
50+
51+
// when
52+
imageTagService.update(new LoginUser(user), 10L, 20L);
53+
54+
// then
55+
verify(imageTagRepository, times(1))
56+
.updateImageTagsForUser(user.getId(), 10L, 20L);
57+
}
58+
59+
@Test
60+
void 업데이트_사용자_없으면_CoreException_던진다() {
61+
// given
62+
User user = DummyObject.newUser("noUser");
63+
64+
given(userRepository.findByUsername(anyString())).willReturn(Optional.empty());
65+
66+
// when & then
67+
assertThatThrownBy(() -> imageTagService.update(new LoginUser(user), 1L, 2L))
68+
.isInstanceOf(CoreException.class);
69+
verifyNoInteractions(imageTagRepository);
70+
}
71+
72+
@Test
73+
void 삭제_사용자_태그_존재하면_레포지토리_호출한다() {
74+
// given
75+
User user = DummyObject.newUser("test");
76+
77+
given(userRepository.findByUsername(anyString())).willReturn(Optional.of(user));
78+
79+
Tag tag = TagFixture.createTag(10L, "testTag");
80+
given(tagRepository.findById(anyLong())).willReturn(Optional.of(tag));
81+
82+
// when
83+
imageTagService.delete(new LoginUser(user), tag.getId());
84+
85+
// then
86+
verify(imageTagRepository, times(1)).deleteByTagAndUser(tag, user);
87+
}
88+
89+
@Test
90+
void 삭제_사용자_없으면_CoreException_던진다() {
91+
// given
92+
User user = DummyObject.newUser("noUser");
93+
94+
given(userRepository.findByUsername(anyString())).willReturn(Optional.empty());
95+
96+
// when & then
97+
assertThatThrownBy(() -> imageTagService.delete(new LoginUser(user), 1L))
98+
.isInstanceOf(CoreException.class);
99+
verifyNoInteractions(imageTagRepository, tagRepository);
100+
}
101+
102+
@Test
103+
void 삭제_태그_없으면_CoreException_던진다() {
104+
// given
105+
User user = DummyObject.newUser("test");
106+
107+
given(userRepository.findByUsername(anyString())).willReturn(Optional.of(user));
108+
given(tagRepository.findById(anyLong())).willReturn(Optional.empty());
109+
110+
// when & then
111+
assertThatThrownBy(() -> imageTagService.delete(new LoginUser(user), 2L))
112+
.isInstanceOf(CoreException.class);
113+
verifyNoInteractions(imageTagRepository);
114+
}
115+
}
116+

capturecat-core/src/test/java/com/capturecat/core/service/user/UserTagServiceTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ class UserTagServiceTest {
115115
given(userRepository.findByUsername(anyString())).willReturn(Optional.of(user));
116116
given(tagRegister.registerTagsFor(anyString())).willReturn(tag);
117117
given(userTagRepository.existsByUserAndTag(eq(user), eq(tag))).willReturn(false);
118-
given(userTagRepository.countByUser(eq(user))).willReturn(30L);
118+
given(userTagRepository.countByUser(eq(user))).willReturn(40L);
119119

120120
// when & then
121121
assertThatThrownBy(() -> userTagService.create(new LoginUser(DummyObject.newUser("test")), "java"))

0 commit comments

Comments
 (0)