fix(members): guard against removing the last planner from household

removeMember now checks the planner count before deleting a planner
member. Throws ConflictException("Cannot remove the last planner")
when only one planner remains, matching the spec requirement in S4.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-10 20:30:55 +02:00
parent eb5699577b
commit 9d3be84a0c
2 changed files with 25 additions and 0 deletions

View File

@@ -134,6 +134,13 @@ public class HouseholdService {
throw new ConflictException("Planner cannot remove yourself");
}
if ("planner".equals(target.getRole())) {
long plannerCount = householdMemberRepository.countByHouseholdIdAndRole(householdId, "planner");
if (plannerCount <= 1) {
throw new ConflictException("Cannot remove the last planner");
}
}
householdMemberRepository.delete(target);
}

View File

@@ -348,6 +348,24 @@ class HouseholdServiceTest {
.hasMessageContaining("cannot remove yourself");
}
@Test
void removeMemberShouldThrow409WhenRemovingLastPlanner() {
var planner = testUser();
var target = new UserAccount("tom@example.com", "Tom", "hashed");
var household = new Household("Smith family", planner);
var plannerMembership = new HouseholdMember(household, planner, "planner");
var targetMembership = new HouseholdMember(household, target, "planner");
var targetId = UUID.randomUUID();
when(householdMemberRepository.findByUserEmailIgnoreCase("sarah@example.com")).thenReturn(Optional.of(plannerMembership));
when(householdMemberRepository.findByHouseholdIdAndUserId(any(), eq(targetId))).thenReturn(Optional.of(targetMembership));
when(householdMemberRepository.countByHouseholdIdAndRole(any(), eq("planner"))).thenReturn(1L);
assertThatThrownBy(() -> householdService.removeMember("sarah@example.com", targetId))
.isInstanceOf(ConflictException.class)
.hasMessageContaining("last planner");
}
@Test
void removeMemberShouldThrow404WhenTargetNotInHousehold() {
var planner = testUser();