feat(#221): add V39 migration for tag hierarchy and colors
Adds parent_id FK (ON DELETE SET NULL), self-reference check constraint, parent_id index, and nullable color column to the tag table. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
-- Add self-referencing parent FK for tag hierarchy (adjacency list model).
|
||||
-- ON DELETE SET NULL: deleting a parent promotes its children to root level.
|
||||
ALTER TABLE tag ADD COLUMN parent_id UUID REFERENCES tag(id) ON DELETE SET NULL;
|
||||
ALTER TABLE tag ADD CONSTRAINT chk_tag_no_self_reference CHECK (parent_id != id);
|
||||
CREATE INDEX idx_tag_parent_id ON tag(parent_id);
|
||||
|
||||
-- Optional color token (e.g. "sage", "teal") for root-level tags.
|
||||
-- Validated against the allowed palette in TagService before save.
|
||||
ALTER TABLE tag ADD COLUMN color VARCHAR(20);
|
||||
@@ -168,8 +168,63 @@ class MigrationIntegrationTest {
|
||||
assertThat(rows).isEqualTo(1);
|
||||
}
|
||||
|
||||
// ─── V39: tag hierarchy — parent_id FK + self-reference check + color ──────
|
||||
|
||||
@Test
|
||||
void v39_parentId_allowsNull() {
|
||||
UUID tagId = createTag("TagWithoutParent");
|
||||
|
||||
Integer count = jdbc.queryForObject(
|
||||
"SELECT COUNT(*) FROM tag WHERE id = ? AND parent_id IS NULL", Integer.class, tagId);
|
||||
assertThat(count).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void v39_selfReferenceCheck_rejectsSelfAsParent() {
|
||||
UUID tagId = createTag("SelfRef");
|
||||
|
||||
assertThatThrownBy(() ->
|
||||
jdbc.update("UPDATE tag SET parent_id = id WHERE id = ?", tagId)
|
||||
).isInstanceOf(DataIntegrityViolationException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void v39_parentId_acceptsValidParent() {
|
||||
UUID parent = createTag("Parent");
|
||||
UUID child = createTag("Child");
|
||||
|
||||
int rows = jdbc.update("UPDATE tag SET parent_id = ? WHERE id = ?", parent, child);
|
||||
assertThat(rows).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void v39_color_allowsNull() {
|
||||
UUID tagId = createTag("ColorlessTag");
|
||||
|
||||
Integer count = jdbc.queryForObject(
|
||||
"SELECT COUNT(*) FROM tag WHERE id = ? AND color IS NULL", Integer.class, tagId);
|
||||
assertThat(count).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void v39_color_storesTokenName() {
|
||||
UUID tagId = createTag("ColoredTag");
|
||||
|
||||
int rows = jdbc.update("UPDATE tag SET color = 'sage' WHERE id = ?", tagId);
|
||||
String stored = jdbc.queryForObject("SELECT color FROM tag WHERE id = ?", String.class, tagId);
|
||||
|
||||
assertThat(rows).isEqualTo(1);
|
||||
assertThat(stored).isEqualTo("sage");
|
||||
}
|
||||
|
||||
// ─── helpers ─────────────────────────────────────────────────────────────
|
||||
|
||||
private UUID createTag(String name) {
|
||||
UUID id = UUID.randomUUID();
|
||||
jdbc.update("INSERT INTO tag (id, name) VALUES (?, ?)", id, name);
|
||||
return id;
|
||||
}
|
||||
|
||||
private UUID createDocument() {
|
||||
Document doc = documentRepository.save(Document.builder()
|
||||
.title("Testdokument")
|
||||
|
||||
Reference in New Issue
Block a user