feat(stats): add totalStories to StatsDTO via GeschichteService.countPublished()

Addresses @Elicit review concern: stories stat tile was permanently showing
"—" because StatsDTO had no published-story count. Now wired end-to-end.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-05-07 22:16:34 +02:00
parent 5fd4c6425c
commit bd8a20fee0
5 changed files with 28 additions and 4 deletions

View File

@@ -1,7 +1,12 @@
package org.raddatz.familienarchiv.dashboard; package org.raddatz.familienarchiv.dashboard;
import io.swagger.v3.oas.annotations.media.Schema;
/** /**
* Aggregate counts for the dashboard/persons stats bar. * Aggregate counts for the dashboard/persons stats bar.
*/ */
public record StatsDTO(long totalPersons, long totalDocuments) { public record StatsDTO(
@Schema(requiredMode = Schema.RequiredMode.REQUIRED) long totalPersons,
@Schema(requiredMode = Schema.RequiredMode.REQUIRED) long totalDocuments,
@Schema(requiredMode = Schema.RequiredMode.REQUIRED) long totalStories) {
} }

View File

@@ -2,6 +2,7 @@ package org.raddatz.familienarchiv.dashboard;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.raddatz.familienarchiv.document.DocumentService; import org.raddatz.familienarchiv.document.DocumentService;
import org.raddatz.familienarchiv.geschichte.GeschichteService;
import org.raddatz.familienarchiv.person.PersonService; import org.raddatz.familienarchiv.person.PersonService;
import org.raddatz.familienarchiv.dashboard.StatsDTO; import org.raddatz.familienarchiv.dashboard.StatsDTO;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@@ -12,8 +13,9 @@ public class StatsService {
private final PersonService personService; private final PersonService personService;
private final DocumentService documentService; private final DocumentService documentService;
private final GeschichteService geschichteService;
public StatsDTO getStats() { public StatsDTO getStats() {
return new StatsDTO(personService.count(), documentService.count()); return new StatsDTO(personService.count(), documentService.count(), geschichteService.countPublished());
} }
} }

View File

@@ -56,6 +56,10 @@ public class GeschichteService {
// ─── Read API ──────────────────────────────────────────────────────────── // ─── Read API ────────────────────────────────────────────────────────────
public long countPublished() {
return geschichteRepository.count(GeschichteSpecifications.hasStatus(GeschichteStatus.PUBLISHED));
}
public Geschichte getById(UUID id) { public Geschichte getById(UUID id) {
Geschichte g = geschichteRepository.findById(id) Geschichte g = geschichteRepository.findById(id)
.orElseThrow(() -> DomainException.notFound( .orElseThrow(() -> DomainException.notFound(

View File

@@ -44,7 +44,7 @@ class StatsControllerTest {
@Test @Test
@WithMockUser(authorities = "READ_ALL") @WithMockUser(authorities = "READ_ALL")
void getStats_returns200_withCorrectCounts() throws Exception { void getStats_returns200_withCorrectCounts() throws Exception {
when(statsService.getStats()).thenReturn(new StatsDTO(4L, 12L)); when(statsService.getStats()).thenReturn(new StatsDTO(4L, 12L, 2L));
mockMvc.perform(get("/api/stats")) mockMvc.perform(get("/api/stats"))
.andExpect(status().isOk()) .andExpect(status().isOk())
@@ -55,7 +55,7 @@ class StatsControllerTest {
@Test @Test
@WithMockUser(authorities = "READ_ALL") @WithMockUser(authorities = "READ_ALL")
void getStats_returns200_withZeroCounts() throws Exception { void getStats_returns200_withZeroCounts() throws Exception {
when(statsService.getStats()).thenReturn(new StatsDTO(0L, 0L)); when(statsService.getStats()).thenReturn(new StatsDTO(0L, 0L, 0L));
mockMvc.perform(get("/api/stats")) mockMvc.perform(get("/api/stats"))
.andExpect(status().isOk()) .andExpect(status().isOk())

View File

@@ -7,6 +7,7 @@ import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
import org.raddatz.familienarchiv.document.DocumentService; import org.raddatz.familienarchiv.document.DocumentService;
import org.raddatz.familienarchiv.dashboard.StatsDTO; import org.raddatz.familienarchiv.dashboard.StatsDTO;
import org.raddatz.familienarchiv.geschichte.GeschichteService;
import org.raddatz.familienarchiv.person.PersonService; import org.raddatz.familienarchiv.person.PersonService;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@@ -17,6 +18,7 @@ class StatsServiceTest {
@Mock PersonService personService; @Mock PersonService personService;
@Mock DocumentService documentService; @Mock DocumentService documentService;
@Mock GeschichteService geschichteService;
@InjectMocks StatsService statsService; @InjectMocks StatsService statsService;
@Test @Test
@@ -30,6 +32,17 @@ class StatsServiceTest {
assertThat(stats.totalDocuments()).isEqualTo(12L); assertThat(stats.totalDocuments()).isEqualTo(12L);
} }
@Test
void getStats_includes_totalStories() {
when(personService.count()).thenReturn(3L);
when(documentService.count()).thenReturn(7L);
when(geschichteService.countPublished()).thenReturn(5L);
StatsDTO stats = statsService.getStats();
assertThat(stats.totalStories()).isEqualTo(5L);
}
@Test @Test
void getStats_returnsZero_whenNoEntities() { void getStats_returnsZero_whenNoEntities() {
when(personService.count()).thenReturn(0L); when(personService.count()).thenReturn(0L);