From e3afe1b4f2d51d6d2cf50e0ad3c8d79d9ee472be Mon Sep 17 00:00:00 2001 From: Marcel Raddatz Date: Mon, 6 Apr 2026 19:52:09 +0200 Subject: [PATCH] test(shopping): add HTTP-level role guard test and blank customName validation test Co-Authored-By: Claude Sonnet 4.6 --- .../shopping/ShoppingListControllerTest.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/backend/src/test/java/com/recipeapp/shopping/ShoppingListControllerTest.java b/backend/src/test/java/com/recipeapp/shopping/ShoppingListControllerTest.java index 95197ab..c19151c 100644 --- a/backend/src/test/java/com/recipeapp/shopping/ShoppingListControllerTest.java +++ b/backend/src/test/java/com/recipeapp/shopping/ShoppingListControllerTest.java @@ -3,8 +3,10 @@ package com.recipeapp.shopping; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.recipeapp.common.GlobalExceptionHandler; +import com.recipeapp.common.HouseholdRoleInterceptor; import com.recipeapp.recipe.HouseholdResolver; import com.recipeapp.shopping.dto.*; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -12,6 +14,8 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.http.MediaType; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; @@ -49,6 +53,11 @@ class ShoppingListControllerTest { .build(); } + @AfterEach + void clearSecurityContext() { + SecurityContextHolder.clearContext(); + } + @Test void getByWeekStartShouldReturn200() throws Exception { var response = new ShoppingListResponse(LIST_ID, PLAN_ID, Instant.now(), 3, List.of()); @@ -169,4 +178,20 @@ class ShoppingListControllerTest { new AddItemRequest(null, " ", new BigDecimal("1"), "")))) .andExpect(status().isBadRequest()); } + + @Test + void generateFromPlanShouldReturn403ForNonPlanner() throws Exception { + SecurityContextHolder.getContext().setAuthentication( + new UsernamePasswordAuthenticationToken("member@example.com", null)); + when(householdResolver.resolveRole("member@example.com")).thenReturn("member"); + + MockMvc mockMvcWithInterceptor = MockMvcBuilders.standaloneSetup(shoppingListController) + .setControllerAdvice(new GlobalExceptionHandler()) + .addInterceptors(new HouseholdRoleInterceptor(householdResolver)) + .build(); + + mockMvcWithInterceptor.perform(post("/v1/week-plans/{id}/shopping-list", PLAN_ID) + .principal(() -> "member@example.com")) + .andExpect(status().isForbidden()); + } }