feat(#248): add TagService.mergeTags() with validateNotSelf/validateNotDescendant/transferDocuments helpers
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -302,6 +302,79 @@ class TagServiceTest {
|
||||
assertThat(child2.getColor()).isEqualTo("sienna");
|
||||
}
|
||||
|
||||
// ─── mergeTags ────────────────────────────────────────────────────────────
|
||||
|
||||
@Test
|
||||
void mergeTags_throwsBadRequest_whenSelfMerge() {
|
||||
UUID id = UUID.randomUUID();
|
||||
|
||||
assertThatThrownBy(() -> tagService.mergeTags(id, id))
|
||||
.isInstanceOf(DomainException.class)
|
||||
.extracting(e -> ((DomainException) e).getCode())
|
||||
.isEqualTo(ErrorCode.TAG_MERGE_SELF);
|
||||
}
|
||||
|
||||
@Test
|
||||
void mergeTags_throwsNotFound_whenSourceMissing() {
|
||||
UUID sourceId = UUID.randomUUID();
|
||||
UUID targetId = UUID.randomUUID();
|
||||
when(tagRepository.findById(sourceId)).thenReturn(Optional.empty());
|
||||
|
||||
assertThatThrownBy(() -> tagService.mergeTags(sourceId, targetId))
|
||||
.isInstanceOf(DomainException.class)
|
||||
.extracting(e -> ((DomainException) e).getCode())
|
||||
.isEqualTo(ErrorCode.TAG_NOT_FOUND);
|
||||
}
|
||||
|
||||
@Test
|
||||
void mergeTags_throwsNotFound_whenTargetMissing() {
|
||||
UUID sourceId = UUID.randomUUID();
|
||||
UUID targetId = UUID.randomUUID();
|
||||
Tag source = Tag.builder().id(sourceId).name("Source").build();
|
||||
when(tagRepository.findById(sourceId)).thenReturn(Optional.of(source));
|
||||
when(tagRepository.findById(targetId)).thenReturn(Optional.empty());
|
||||
|
||||
assertThatThrownBy(() -> tagService.mergeTags(sourceId, targetId))
|
||||
.isInstanceOf(DomainException.class)
|
||||
.extracting(e -> ((DomainException) e).getCode())
|
||||
.isEqualTo(ErrorCode.TAG_NOT_FOUND);
|
||||
}
|
||||
|
||||
@Test
|
||||
void mergeTags_throwsBadRequest_whenTargetIsDescendantOfSource() {
|
||||
UUID sourceId = UUID.randomUUID();
|
||||
UUID targetId = UUID.randomUUID();
|
||||
Tag source = Tag.builder().id(sourceId).name("Parent").build();
|
||||
Tag target = Tag.builder().id(targetId).name("Child").parentId(sourceId).build();
|
||||
when(tagRepository.findById(sourceId)).thenReturn(Optional.of(source));
|
||||
when(tagRepository.findById(targetId)).thenReturn(Optional.of(target));
|
||||
when(tagRepository.findDescendantIds(sourceId)).thenReturn(List.of(sourceId, targetId));
|
||||
|
||||
assertThatThrownBy(() -> tagService.mergeTags(sourceId, targetId))
|
||||
.isInstanceOf(DomainException.class)
|
||||
.extracting(e -> ((DomainException) e).getCode())
|
||||
.isEqualTo(ErrorCode.TAG_MERGE_INVALID_TARGET);
|
||||
}
|
||||
|
||||
@Test
|
||||
void mergeTags_reassignsDocumentsReparentsChildrenAndDeletesSource() {
|
||||
UUID sourceId = UUID.randomUUID();
|
||||
UUID targetId = UUID.randomUUID();
|
||||
Tag source = Tag.builder().id(sourceId).name("Source").build();
|
||||
Tag target = Tag.builder().id(targetId).name("Target").build();
|
||||
when(tagRepository.findById(sourceId)).thenReturn(Optional.of(source));
|
||||
when(tagRepository.findById(targetId)).thenReturn(Optional.of(target));
|
||||
when(tagRepository.findDescendantIds(sourceId)).thenReturn(List.of(sourceId));
|
||||
|
||||
Tag result = tagService.mergeTags(sourceId, targetId);
|
||||
|
||||
verify(tagRepository).reassignDocumentTags(sourceId, targetId);
|
||||
verify(tagRepository).deleteDocumentTagsByTagId(sourceId);
|
||||
verify(tagRepository).reparentChildren(sourceId, targetId);
|
||||
verify(tagRepository).deleteById(sourceId);
|
||||
assertThat(result).isEqualTo(target);
|
||||
}
|
||||
|
||||
// ─── delete ───────────────────────────────────────────────────────────────
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user