feat(stammbaum): family network — graph, badge, edit card, /stammbaum page (#358) #360
@@ -0,0 +1,6 @@
|
||||
-- Symmetric SPOUSE_OF: enforce only one row per unordered pair, mirroring the
|
||||
-- SIBLING_OF index added in V54.
|
||||
CREATE UNIQUE INDEX unique_spouse_pair ON person_relationships (
|
||||
LEAST(person_id, related_person_id),
|
||||
GREATEST(person_id, related_person_id)
|
||||
) WHERE relation_type = 'SPOUSE_OF';
|
||||
@@ -117,6 +117,20 @@ class RelationshipServiceIntegrationTest {
|
||||
assertThat(relationshipRepository.findById(created.id())).isPresent();
|
||||
}
|
||||
|
||||
@Test
|
||||
void addRelationship_throws_409_when_reverse_SPOUSE_OF_pair_already_exists() {
|
||||
// V55 enforces symmetric uniqueness for SPOUSE_OF. Inserting (alice, bob, SPOUSE_OF)
|
||||
// and then (bob, alice, SPOUSE_OF) must be rejected, just like reverse SIBLING_OF.
|
||||
relationshipService.addRelationship(alice.getId(),
|
||||
new CreateRelationshipRequest(bob.getId(), "SPOUSE_OF", null, null, null));
|
||||
|
||||
var reverse = new CreateRelationshipRequest(alice.getId(), "SPOUSE_OF", null, null, null);
|
||||
assertThatThrownBy(() -> relationshipService.addRelationship(bob.getId(), reverse))
|
||||
.isInstanceOf(DomainException.class)
|
||||
.extracting("code")
|
||||
.isEqualTo(ErrorCode.DUPLICATE_RELATIONSHIP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void deleteRelationship_succeeds_for_symmetric_type_from_either_side() {
|
||||
// alice SPOUSE_OF bob. Bob deletes from his side.
|
||||
|
||||
Reference in New Issue
Block a user