From b972f512073ceecb9fc47f19102a351072855b93 Mon Sep 17 00:00:00 2001 From: Sean Starkey Date: Mon, 1 Jun 2026 17:58:18 -0600 Subject: [PATCH] Add tests for GET /notes --- .../controller/NoteListingControllerTest.java | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 src/test/java/com/seanstarkey/notesvault/controller/NoteListingControllerTest.java diff --git a/src/test/java/com/seanstarkey/notesvault/controller/NoteListingControllerTest.java b/src/test/java/com/seanstarkey/notesvault/controller/NoteListingControllerTest.java new file mode 100644 index 0000000..f709a6e --- /dev/null +++ b/src/test/java/com/seanstarkey/notesvault/controller/NoteListingControllerTest.java @@ -0,0 +1,79 @@ +/** + * NoteListingControllerTest.java + * + * API-level tests for authenticated note listing behavior. + */ +package com.seanstarkey.notesvault.controller; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.HttpHeaders; +import org.springframework.transaction.annotation.Transactional; + +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * Verifies GET /notes through the real Spring MVC, Security, JPA, validation, + * and Flyway-backed H2 application stack. + */ +@SpringBootTest +@AutoConfigureMockMvc +@Transactional +class NoteListingControllerTest extends ControllerApiTestSupport { + + /* + * Test: authenticated users can list owned and shared notes. + * Verifies that GET /notes returns notes owned by the bearer token subject, + * notes explicitly shared with that user as read-only, and no unrelated notes. + */ + @Test + void listNotesReturnsOwnedAndSharedNotesOnly() throws Exception { + register("alice", "secret123"); + register("bob", "secret456"); + register("charlie", "secret789"); + String aliceToken = jwtUtil.generateToken("alice"); + String bobToken = jwtUtil.generateToken("bob"); + String charlieToken = jwtUtil.generateToken("charlie"); + + UUID sharedNoteId = createNote(aliceToken, "Alice Shared", "Bob may read this"); + UUID bobNoteId = createNote(bobToken, "Bob Private", "Bob owns this"); + UUID charlieNoteId = createNote(charlieToken, "Charlie Private", "Bob cannot see this"); + + shareWith(sharedNoteId, "bob"); + + String responseBody = mockMvc.perform(get("/notes") + .header(HttpHeaders.AUTHORIZATION, "Bearer " + bobToken)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$").isArray()) + .andExpect(jsonPath("$.length()").value(2)) + .andReturn() + .getResponse() + .getContentAsString(); + + assertThat(responseBody) + .contains(bobNoteId.toString()) + .contains(sharedNoteId.toString()) + .contains("\"access\":\"OWNER\"") + .contains("\"access\":\"SHARED_READ\"") + .doesNotContain(charlieNoteId.toString()) + .doesNotContain("Charlie Private"); + } + + /* + * Test: note listing requires authentication. + * Verifies that GET /notes is protected by the JWT security chain and + * returns HTTP 401 when no bearer token is supplied. + */ + @Test + void listNotesRequiresBearerToken() throws Exception { + mockMvc.perform(get("/notes")) + .andExpect(status().isUnauthorized()) + .andExpect(jsonPath("$.error").value("Unauthorized")); + } +}