From 9d3be84a0cb7baccc0b0570da4becf6866517517 Mon Sep 17 00:00:00 2001 From: Marcel Raddatz Date: Fri, 10 Apr 2026 20:30:55 +0200 Subject: [PATCH] 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 --- .../recipeapp/household/HouseholdService.java | 7 +++++++ .../household/HouseholdServiceTest.java | 18 ++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/backend/src/main/java/com/recipeapp/household/HouseholdService.java b/backend/src/main/java/com/recipeapp/household/HouseholdService.java index 9328d50..bcf207c 100644 --- a/backend/src/main/java/com/recipeapp/household/HouseholdService.java +++ b/backend/src/main/java/com/recipeapp/household/HouseholdService.java @@ -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); } diff --git a/backend/src/test/java/com/recipeapp/household/HouseholdServiceTest.java b/backend/src/test/java/com/recipeapp/household/HouseholdServiceTest.java index 213825d..6545566 100644 --- a/backend/src/test/java/com/recipeapp/household/HouseholdServiceTest.java +++ b/backend/src/test/java/com/recipeapp/household/HouseholdServiceTest.java @@ -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();