1 package fr.tiogars.chat.domains.task.controllers;
2
3 import static org.mockito.Mockito.verifyNoInteractions;
4 import static org.mockito.Mockito.when;
5 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
6 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
7 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
8 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
9 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
10
11 import java.nio.charset.StandardCharsets;
12 import java.util.List;
13 import java.util.Optional;
14
15 import org.junit.jupiter.api.Test;
16 import org.junit.jupiter.params.ParameterizedTest;
17 import org.junit.jupiter.params.provider.ValueSource;
18 import org.springframework.beans.factory.annotation.Autowired;
19 import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc;
20 import org.springframework.boot.webmvc.test.autoconfigure.WebMvcTest;
21 import org.springframework.http.MediaType;
22 import org.springframework.http.HttpStatus;
23 import org.springframework.security.core.userdetails.UserDetailsService;
24 import org.springframework.test.context.bean.override.mockito.MockitoBean;
25 import org.springframework.test.web.servlet.MockMvc;
26 import org.springframework.web.server.ResponseStatusException;
27
28 import fr.tiogars.chat.domains.auth.services.JwtTokenService;
29 import fr.tiogars.chat.domains.task.services.TaskPrintService;
30 import fr.tiogars.chat.domains.task.services.TasksPrintAllService;
31 import fr.tiogars.chat.domains.task.services.TasksPrintSelectedService;
32
33 @WebMvcTest(TaskPrintController.class)
34 @AutoConfigureMockMvc(addFilters = false)
35 class TaskPrintControllerTest {
36
37 private static final byte[] PDF = "%PDF-1.7\nTest PDF bytes".getBytes(StandardCharsets.UTF_8);
38
39 @Autowired
40 private MockMvc mvc;
41
42 @MockitoBean
43 private TaskPrintService printService;
44
45 @MockitoBean
46 private TasksPrintAllService printAllService;
47
48 @MockitoBean
49 private TasksPrintSelectedService printSelectedService;
50
51 @MockitoBean
52 private JwtTokenService jwtTokenService;
53
54 @MockitoBean
55 private UserDetailsService userDetailsService;
56
57 @Test
58 void print_should_returnInlinePdfForCurrentRecord() throws Exception {
59 when(printService.print(42L)).thenReturn(Optional.of(PDF));
60
61 mvc.perform(get("/api/v1/task/42/print.pdf"))
62 .andExpect(status().isOk())
63 .andExpect(content().contentType(MediaType.APPLICATION_PDF))
64 .andExpect(content().bytes(PDF))
65 .andExpect(header().string("Content-Disposition", "inline; filename=\"task-42.pdf\""))
66 .andExpect(header().string("Cache-Control", "no-store"));
67 }
68
69 @Test
70 void printAll_should_returnInlinePdf() throws Exception {
71 when(printAllService.printAll()).thenReturn(PDF);
72
73 mvc.perform(get("/api/v1/task/print.pdf"))
74 .andExpect(status().isOk())
75 .andExpect(content().contentType(MediaType.APPLICATION_PDF))
76 .andExpect(content().bytes(PDF))
77 .andExpect(header().string("Content-Disposition", "inline; filename=\"tasks.pdf\""));
78 }
79
80 @Test
81 void printSelected_should_returnInlinePdfForRequestedIds() throws Exception {
82 when(printSelectedService.printSelected(List.of(1L, 2L))).thenReturn(Optional.of(PDF));
83
84 mvc.perform(post("/api/v1/task/print-selected.pdf")
85 .contentType(MediaType.APPLICATION_JSON).content("{\"ids\":[1,2]}"))
86 .andExpect(status().isOk())
87 .andExpect(content().contentType(MediaType.APPLICATION_PDF))
88 .andExpect(content().bytes(PDF))
89 .andExpect(header().string("Content-Disposition", "inline; filename=\"tasks-selected.pdf\""));
90 }
91
92 @ParameterizedTest
93 @ValueSource(strings = {"{}", "{\"ids\":null}", "{\"ids\":[]}", "{\"ids\":[null]}",
94 "{\"ids\":[0]}", "{\"ids\":[-1]}"})
95 void printSelected_should_rejectInvalidSelections(String body) throws Exception {
96 mvc.perform(post("/api/v1/task/print-selected.pdf")
97 .contentType(MediaType.APPLICATION_JSON).content(body))
98 .andExpect(status().isBadRequest());
99
100 verifyNoInteractions(printSelectedService);
101 }
102
103 @Test
104 void printSelected_should_rejectOversizedSelection() throws Exception {
105 String body = "{\"ids\":[" + String.join(",", java.util.Collections.nCopies(1001, "1")) + "]}";
106 mvc.perform(post("/api/v1/task/print-selected.pdf")
107 .contentType(MediaType.APPLICATION_JSON).content(body))
108 .andExpect(status().isBadRequest());
109 verifyNoInteractions(printSelectedService);
110 }
111
112 @Test
113 void print_should_returnNotFoundWhenCurrentRecordIsMissing() throws Exception {
114 when(printService.print(99L)).thenReturn(Optional.empty());
115
116 mvc.perform(get("/api/v1/task/99/print.pdf")).andExpect(status().isNotFound());
117 }
118
119 @Test
120 void printSelected_should_returnNotFoundWhenAnySelectedRecordIsMissing() throws Exception {
121 when(printSelectedService.printSelected(List.of(1L, 99L))).thenReturn(Optional.empty());
122
123 mvc.perform(post("/api/v1/task/print-selected.pdf")
124 .contentType(MediaType.APPLICATION_JSON).content("{\"ids\":[1,99]}"))
125 .andExpect(status().isNotFound());
126 }
127
128 @Test
129 void print_should_reportUnsupportedCharactersAsUnprocessableContent() throws Exception {
130 when(printService.print(42L)).thenThrow(new ResponseStatusException(
131 HttpStatus.UNPROCESSABLE_CONTENT, "Unsupported PDF characters."));
132
133 mvc.perform(get("/api/v1/task/42/print.pdf"))
134 .andExpect(status().is(HttpStatus.UNPROCESSABLE_CONTENT.value()));
135 }
136 }