refactor(dashboard): remove page field from DashboardResumeDTO; rename pages to totalBlocks
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,18 @@
|
|||||||
|
package org.raddatz.familienarchiv.dashboard;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import jakarta.annotation.Nullable;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public record DashboardResumeDTO(
|
||||||
|
@Schema(requiredMode = Schema.RequiredMode.REQUIRED) UUID documentId,
|
||||||
|
@Schema(requiredMode = Schema.RequiredMode.REQUIRED) String title,
|
||||||
|
@Schema(requiredMode = Schema.RequiredMode.REQUIRED) String caption,
|
||||||
|
@Schema(requiredMode = Schema.RequiredMode.REQUIRED) String excerpt,
|
||||||
|
@Schema(requiredMode = Schema.RequiredMode.REQUIRED) int totalBlocks,
|
||||||
|
@Schema(requiredMode = Schema.RequiredMode.REQUIRED) int pct,
|
||||||
|
@Nullable String thumbnailUrl,
|
||||||
|
@Schema(requiredMode = Schema.RequiredMode.REQUIRED) List<ActivityActorDTO> collaborators
|
||||||
|
) {}
|
||||||
@@ -53,16 +53,6 @@ public class DashboardService {
|
|||||||
long reviewedBlocks = blocks.stream().filter(TranscriptionBlock::isReviewed).count();
|
long reviewedBlocks = blocks.stream().filter(TranscriptionBlock::isReviewed).count();
|
||||||
int pct = totalBlocks > 0 ? (int) (reviewedBlocks * 100L / totalBlocks) : 0;
|
int pct = totalBlocks > 0 ? (int) (reviewedBlocks * 100L / totalBlocks) : 0;
|
||||||
|
|
||||||
Set<Integer> pageNumbers = new HashSet<>();
|
|
||||||
for (TranscriptionBlock b : blocks) {
|
|
||||||
pageNumbers.add(b.getSortOrder());
|
|
||||||
}
|
|
||||||
int maxPage = blocks.stream().mapToInt(TranscriptionBlock::getSortOrder).max().orElse(1);
|
|
||||||
int totalPages = Math.max(1, (int) blocks.stream()
|
|
||||||
.mapToInt(TranscriptionBlock::getSortOrder)
|
|
||||||
.distinct().count());
|
|
||||||
int currentPage = 1;
|
|
||||||
|
|
||||||
String caption = buildCaption(doc);
|
String caption = buildCaption(doc);
|
||||||
|
|
||||||
List<UUID> collaboratorIds = blocks.stream()
|
List<UUID> collaboratorIds = blocks.stream()
|
||||||
@@ -85,7 +75,7 @@ public class DashboardService {
|
|||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
return new DashboardResumeDTO(docId, doc.getTitle(), caption, excerpt,
|
return new DashboardResumeDTO(docId, doc.getTitle(), caption, excerpt,
|
||||||
currentPage, totalPages, pct, null, collaborators);
|
totalBlocks, pct, null, collaborators);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DashboardPulseDTO getPulse(UUID userId) {
|
public DashboardPulseDTO getPulse(UUID userId) {
|
||||||
|
|||||||
@@ -0,0 +1,130 @@
|
|||||||
|
package org.raddatz.familienarchiv.dashboard;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.raddatz.familienarchiv.config.SecurityConfig;
|
||||||
|
import org.raddatz.familienarchiv.model.AppUser;
|
||||||
|
import org.raddatz.familienarchiv.security.PermissionAspect;
|
||||||
|
import org.raddatz.familienarchiv.service.CustomUserDetailsService;
|
||||||
|
import org.raddatz.familienarchiv.service.UserService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.autoconfigure.aop.AopAutoConfiguration;
|
||||||
|
import org.springframework.boot.webmvc.test.autoconfigure.WebMvcTest;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
|
import org.springframework.security.test.context.support.WithMockUser;
|
||||||
|
import org.springframework.test.context.bean.override.mockito.MockitoBean;
|
||||||
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyInt;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
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;
|
||||||
|
|
||||||
|
@WebMvcTest(DashboardController.class)
|
||||||
|
@Import({SecurityConfig.class, PermissionAspect.class, AopAutoConfiguration.class})
|
||||||
|
class DashboardControllerTest {
|
||||||
|
|
||||||
|
@Autowired MockMvc mockMvc;
|
||||||
|
|
||||||
|
@MockitoBean DashboardService dashboardService;
|
||||||
|
@MockitoBean UserService userService;
|
||||||
|
@MockitoBean CustomUserDetailsService customUserDetailsService;
|
||||||
|
|
||||||
|
// ─── Security ────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void resume_returns401_whenUnauthenticated() throws Exception {
|
||||||
|
mockMvc.perform(get("/api/dashboard/resume"))
|
||||||
|
.andExpect(status().isUnauthorized());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithMockUser
|
||||||
|
void resume_returns403_whenUserHasNoPermissions() throws Exception {
|
||||||
|
mockMvc.perform(get("/api/dashboard/resume"))
|
||||||
|
.andExpect(status().isForbidden());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void pulse_returns401_whenUnauthenticated() throws Exception {
|
||||||
|
mockMvc.perform(get("/api/dashboard/pulse"))
|
||||||
|
.andExpect(status().isUnauthorized());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithMockUser
|
||||||
|
void pulse_returns403_whenUserHasNoPermissions() throws Exception {
|
||||||
|
mockMvc.perform(get("/api/dashboard/pulse"))
|
||||||
|
.andExpect(status().isForbidden());
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── GET /api/dashboard/resume ────────────────────────────────────────────
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithMockUser(authorities = "READ_ALL")
|
||||||
|
void resume_returnsNull_whenNoInProgressTranscription() throws Exception {
|
||||||
|
UUID userId = UUID.randomUUID();
|
||||||
|
when(userService.findByEmail(any())).thenReturn(
|
||||||
|
AppUser.builder().id(userId).email("u@test.com").password("pw").build());
|
||||||
|
when(dashboardService.getResume(userId)).thenReturn(null);
|
||||||
|
|
||||||
|
mockMvc.perform(get("/api/dashboard/resume"))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$").doesNotExist());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithMockUser(authorities = "READ_ALL")
|
||||||
|
void resume_returnsDocument_whenUserHasInProgressTranscription() throws Exception {
|
||||||
|
UUID userId = UUID.randomUUID();
|
||||||
|
UUID docId = UUID.randomUUID();
|
||||||
|
when(userService.findByEmail(any())).thenReturn(
|
||||||
|
AppUser.builder().id(userId).email("u@test.com").password("pw").build());
|
||||||
|
|
||||||
|
DashboardResumeDTO resume = new DashboardResumeDTO(docId, "Brief an Frieda",
|
||||||
|
"Frieda an Wilhelm · 1923", "Liebe Frieda…", 4, 38, null, List.of());
|
||||||
|
when(dashboardService.getResume(userId)).thenReturn(resume);
|
||||||
|
|
||||||
|
mockMvc.perform(get("/api/dashboard/resume"))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.documentId").value(docId.toString()))
|
||||||
|
.andExpect(jsonPath("$.pct").value(38));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── GET /api/dashboard/pulse ─────────────────────────────────────────────
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithMockUser(authorities = "READ_ALL")
|
||||||
|
void pulse_returnsWeekStats() throws Exception {
|
||||||
|
UUID userId = UUID.randomUUID();
|
||||||
|
when(userService.findByEmail(any())).thenReturn(
|
||||||
|
AppUser.builder().id(userId).email("u@test.com").password("pw").build());
|
||||||
|
|
||||||
|
DashboardPulseDTO pulse = new DashboardPulseDTO(86, 23, 9, 47, 4, List.of());
|
||||||
|
when(dashboardService.getPulse(userId)).thenReturn(pulse);
|
||||||
|
|
||||||
|
mockMvc.perform(get("/api/dashboard/pulse"))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.pages").value(86))
|
||||||
|
.andExpect(jsonPath("$.annotated").value(23));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── GET /api/dashboard/activity ─────────────────────────────────────────
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithMockUser(authorities = "READ_ALL")
|
||||||
|
void activity_returnsDeduplicated_feed() throws Exception {
|
||||||
|
UUID userId = UUID.randomUUID();
|
||||||
|
when(userService.findByEmail(any())).thenReturn(
|
||||||
|
AppUser.builder().id(userId).email("u@test.com").password("pw").build());
|
||||||
|
when(dashboardService.getActivity(any(UUID.class), anyInt())).thenReturn(List.of());
|
||||||
|
|
||||||
|
mockMvc.perform(get("/api/dashboard/activity"))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$").isArray());
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user