fix(#221): resolve inherited color on child tags in document responses
Colors are stored only on root-level tags. DocumentService now calls TagService.resolveEffectiveColors() before returning search results and single-document responses, so child tags carry their parent's color when serialised to JSON. Parent tags are batch-loaded in a single query. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -14,6 +14,7 @@ import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -233,6 +234,75 @@ class TagServiceTest {
|
||||
assertThat(tree.get(0).children().get(0).id()).isEqualTo(childId);
|
||||
}
|
||||
|
||||
// ─── resolveEffectiveColors ───────────────────────────────────────────────
|
||||
|
||||
@Test
|
||||
void resolveEffectiveColors_doesNothing_whenCollectionIsEmpty() {
|
||||
tagService.resolveEffectiveColors(List.of());
|
||||
verifyNoInteractions(tagRepository);
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveEffectiveColors_doesNothing_whenAllTagsHaveOwnColor() {
|
||||
Tag tag = Tag.builder().id(UUID.randomUUID()).name("Root").color("sage").build();
|
||||
|
||||
tagService.resolveEffectiveColors(List.of(tag));
|
||||
|
||||
verify(tagRepository, never()).findAllById(any());
|
||||
assertThat(tag.getColor()).isEqualTo("sage");
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveEffectiveColors_doesNothing_whenTagHasNoParentAndNoColor() {
|
||||
Tag tag = Tag.builder().id(UUID.randomUUID()).name("Root").build();
|
||||
|
||||
tagService.resolveEffectiveColors(List.of(tag));
|
||||
|
||||
verify(tagRepository, never()).findAllById(any());
|
||||
assertThat(tag.getColor()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveEffectiveColors_setsParentColor_onChildTagWithNoColor() {
|
||||
UUID parentId = UUID.randomUUID();
|
||||
Tag parent = Tag.builder().id(parentId).name("Parent").color("sage").build();
|
||||
Tag child = Tag.builder().id(UUID.randomUUID()).name("Child").parentId(parentId).build();
|
||||
when(tagRepository.findAllById(Set.of(parentId))).thenReturn(List.of(parent));
|
||||
|
||||
tagService.resolveEffectiveColors(List.of(child));
|
||||
|
||||
assertThat(child.getColor()).isEqualTo("sage");
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveEffectiveColors_leavesChildUnchanged_whenParentHasNoColor() {
|
||||
UUID parentId = UUID.randomUUID();
|
||||
Tag parent = Tag.builder().id(parentId).name("Parent").build();
|
||||
Tag child = Tag.builder().id(UUID.randomUUID()).name("Child").parentId(parentId).build();
|
||||
when(tagRepository.findAllById(Set.of(parentId))).thenReturn(List.of(parent));
|
||||
|
||||
tagService.resolveEffectiveColors(List.of(child));
|
||||
|
||||
assertThat(child.getColor()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveEffectiveColors_batchLoadsParents_inOneQuery() {
|
||||
UUID parentId1 = UUID.randomUUID();
|
||||
UUID parentId2 = UUID.randomUUID();
|
||||
Tag parent1 = Tag.builder().id(parentId1).name("P1").color("sage").build();
|
||||
Tag parent2 = Tag.builder().id(parentId2).name("P2").color("sienna").build();
|
||||
Tag child1 = Tag.builder().id(UUID.randomUUID()).name("C1").parentId(parentId1).build();
|
||||
Tag child2 = Tag.builder().id(UUID.randomUUID()).name("C2").parentId(parentId2).build();
|
||||
when(tagRepository.findAllById(Set.of(parentId1, parentId2))).thenReturn(List.of(parent1, parent2));
|
||||
|
||||
tagService.resolveEffectiveColors(List.of(child1, child2));
|
||||
|
||||
verify(tagRepository, times(1)).findAllById(any());
|
||||
assertThat(child1.getColor()).isEqualTo("sage");
|
||||
assertThat(child2.getColor()).isEqualTo("sienna");
|
||||
}
|
||||
|
||||
// ─── delete ───────────────────────────────────────────────────────────────
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user