feat(documents): add GET /api/documents/density endpoint (#385)
Authenticated read endpoint backing the timeline density widget. Optional from/to LocalDate query params narrow the aggregation. Response carries Cache-Control: private, max-age=300 so repeated browse sessions skip the aggregation query (per Tobias' devops review). No @RequirePermission needed — inherits the global anyRequest().authenticated() rule. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1240,4 +1240,60 @@ class DocumentControllerTest {
|
||||
.andExpect(jsonPath("$.errors[0].message").value(
|
||||
org.hamcrest.Matchers.containsString("not found")));
|
||||
}
|
||||
|
||||
// ─── GET /api/documents/density ───────────────────────────────────────────
|
||||
|
||||
@Test
|
||||
void density_returns401_whenUnauthenticated() throws Exception {
|
||||
mockMvc.perform(get("/api/documents/density"))
|
||||
.andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser
|
||||
void density_returns200_withResultBody_whenAuthenticated() throws Exception {
|
||||
when(documentService.getDensity(any(), any())).thenReturn(
|
||||
new DocumentDensityResult(
|
||||
List.of(new MonthBucket("1915-08", 2), new MonthBucket("1915-09", 1)),
|
||||
java.time.LocalDate.of(1915, 8, 3),
|
||||
java.time.LocalDate.of(1915, 9, 1)));
|
||||
|
||||
mockMvc.perform(get("/api/documents/density"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.buckets").isArray())
|
||||
.andExpect(jsonPath("$.buckets[0].month").value("1915-08"))
|
||||
.andExpect(jsonPath("$.buckets[0].count").value(2))
|
||||
.andExpect(jsonPath("$.minDate").value("1915-08-03"))
|
||||
.andExpect(jsonPath("$.maxDate").value("1915-09-01"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser
|
||||
void density_emitsPrivateCacheControlHeader() throws Exception {
|
||||
when(documentService.getDensity(any(), any())).thenReturn(
|
||||
new DocumentDensityResult(List.of(), null, null));
|
||||
|
||||
mockMvc.perform(get("/api/documents/density"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(header().string("Cache-Control",
|
||||
org.hamcrest.Matchers.containsString("max-age=300")))
|
||||
.andExpect(header().string("Cache-Control",
|
||||
org.hamcrest.Matchers.containsString("private")));
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser
|
||||
void density_passesFromAndToParametersToService() throws Exception {
|
||||
when(documentService.getDensity(any(), any())).thenReturn(
|
||||
new DocumentDensityResult(List.of(), null, null));
|
||||
|
||||
mockMvc.perform(get("/api/documents/density")
|
||||
.param("from", "1914-01-01")
|
||||
.param("to", "1918-12-31"))
|
||||
.andExpect(status().isOk());
|
||||
|
||||
verify(documentService).getDensity(
|
||||
java.time.LocalDate.of(1914, 1, 1),
|
||||
java.time.LocalDate.of(1918, 12, 31));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user