Merge branch 'refs/heads/24-fix-achievement-date' into sattelgeschichten

# Conflicts:
#	src/test/java/net/javahippie/fitpub/service/ActivitySummaryServiceTest.java
This commit is contained in:
Marcus Fihlon 2026-04-29 17:08:56 +02:00
commit 88ac213214
Signed by: McPringle
GPG key ID: C6B7F469EE363E1F
3 changed files with 128 additions and 10 deletions

View file

@ -53,6 +53,12 @@ public interface ActivityRepository extends JpaRepository<Activity, UUID> {
LocalDateTime endDate LocalDateTime endDate
); );
boolean existsByUserIdAndStartedAtBetween(
UUID userId,
LocalDateTime startDate,
LocalDateTime endDate
);
/** /**
* Find all public activities for a user. * Find all public activities for a user.
* *

View file

@ -239,26 +239,67 @@ public class ActivitySummaryService {
/** /**
* Get current week summary. * Get current week summary.
*/ */
@Transactional(readOnly = true) @Transactional
public ActivitySummary getCurrentWeekSummary(UUID userId) { public ActivitySummary getCurrentWeekSummary(UUID userId) {
LocalDate weekStart = LocalDate.now().with(TemporalAdjusters.previousOrSame(java.time.DayOfWeek.MONDAY)); LocalDate weekStart = LocalDate.now().with(TemporalAdjusters.previousOrSame(java.time.DayOfWeek.MONDAY));
return activitySummaryRepository.findByUserIdAndPeriodTypeAndPeriodStart( LocalDate weekEndExclusive = weekStart.plusDays(7);
return getOrBuildCurrentSummary(
userId, userId,
ActivitySummary.PeriodType.WEEK, ActivitySummary.PeriodType.WEEK,
weekStart weekStart,
).orElse(null); weekEndExclusive,
() -> updateWeeklySummary(userId, weekStart)
);
} }
/** /**
* Get current month summary. * Get current month summary.
*/ */
@Transactional(readOnly = true) @Transactional
public ActivitySummary getCurrentMonthSummary(UUID userId) { public ActivitySummary getCurrentMonthSummary(UUID userId) {
LocalDate monthStart = LocalDate.now().with(TemporalAdjusters.firstDayOfMonth()); LocalDate monthStart = LocalDate.now().with(TemporalAdjusters.firstDayOfMonth());
return activitySummaryRepository.findByUserIdAndPeriodTypeAndPeriodStart( LocalDate monthEndExclusive = monthStart.plusMonths(1);
return getOrBuildCurrentSummary(
userId, userId,
ActivitySummary.PeriodType.MONTH, ActivitySummary.PeriodType.MONTH,
monthStart monthStart,
monthEndExclusive,
() -> updateMonthlySummary(userId, monthStart)
);
}
private ActivitySummary getOrBuildCurrentSummary(
UUID userId,
ActivitySummary.PeriodType periodType,
LocalDate periodStart,
LocalDate periodEndExclusive,
Runnable rebuildAction
) {
ActivitySummary existingSummary = activitySummaryRepository.findByUserIdAndPeriodTypeAndPeriodStart(
userId,
periodType,
periodStart
).orElse(null);
if (existingSummary != null) {
return existingSummary;
}
boolean hasActivitiesInPeriod = activityRepository.existsByUserIdAndStartedAtBetween(
userId,
periodStart.atStartOfDay(),
periodEndExclusive.atStartOfDay()
);
if (!hasActivitiesInPeriod) {
return null;
}
rebuildAction.run();
return activitySummaryRepository.findByUserIdAndPeriodTypeAndPeriodStart(
userId,
periodType,
periodStart
).orElse(null); ).orElse(null);
} }
} }

View file

@ -22,6 +22,9 @@ import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.argThat; import static org.mockito.Mockito.argThat;
@ -144,4 +147,72 @@ class ActivitySummaryServiceTest {
summary.getActivityCount() == 1 summary.getActivityCount() == 1
)); ));
} }
@Test
@DisplayName("Should rebuild current month summary on demand when activities exist but summary is missing")
void getCurrentMonthSummary_RebuildsMissingSummary() {
LocalDate monthStart = LocalDate.now().withDayOfMonth(1);
LocalDateTime startDateTime = monthStart.atStartOfDay();
LocalDateTime endDateTime = monthStart.plusMonths(1).atStartOfDay();
Activity activity = Activity.builder()
.id(UUID.randomUUID())
.userId(userId)
.activityType(Activity.ActivityType.RUN)
.startedAt(startDateTime.plusDays(2).plusHours(7))
.endedAt(startDateTime.plusDays(2).plusHours(8))
.totalDistance(BigDecimal.valueOf(10000))
.totalDurationSeconds(3600L)
.elevationGain(BigDecimal.valueOf(150))
.build();
ActivitySummary rebuiltSummary = ActivitySummary.builder()
.userId(userId)
.periodType(ActivitySummary.PeriodType.MONTH)
.periodStart(monthStart)
.periodEnd(monthStart.plusMonths(1).minusDays(1))
.activityCount(1)
.build();
when(activitySummaryRepository.findByUserIdAndPeriodTypeAndPeriodStart(
userId,
ActivitySummary.PeriodType.MONTH,
monthStart
)).thenReturn(Optional.empty(), Optional.of(rebuiltSummary));
when(activityRepository.existsByUserIdAndStartedAtBetween(userId, startDateTime, endDateTime))
.thenReturn(true);
when(activityRepository.findByUserIdAndStartedAtBetweenOrderByStartedAtDesc(userId, startDateTime, endDateTime))
.thenReturn(List.of(activity));
when(personalRecordRepository.countByUserIdAndDateRange(userId, startDateTime, endDateTime)).thenReturn(0L);
when(achievementRepository.countByUserIdAndActivityStartedDateRange(userId, startDateTime, endDateTime)).thenReturn(0L);
when(activitySummaryRepository.save(org.mockito.ArgumentMatchers.any(ActivitySummary.class)))
.thenAnswer(invocation -> invocation.getArgument(0));
ActivitySummary result = activitySummaryService.getCurrentMonthSummary(userId);
assertNotNull(result);
assertEquals(1, result.getActivityCount());
verify(activityRepository).existsByUserIdAndStartedAtBetween(userId, startDateTime, endDateTime);
}
@Test
@DisplayName("Should return null for current month summary when no activities exist in the period")
void getCurrentMonthSummary_ReturnsNullWhenNoActivitiesExist() {
LocalDate monthStart = LocalDate.now().withDayOfMonth(1);
LocalDateTime startDateTime = monthStart.atStartOfDay();
LocalDateTime endDateTime = monthStart.plusMonths(1).atStartOfDay();
when(activitySummaryRepository.findByUserIdAndPeriodTypeAndPeriodStart(
userId,
ActivitySummary.PeriodType.MONTH,
monthStart
)).thenReturn(Optional.empty());
when(activityRepository.existsByUserIdAndStartedAtBetween(userId, startDateTime, endDateTime))
.thenReturn(false);
ActivitySummary result = activitySummaryService.getCurrentMonthSummary(userId);
assertNull(result);
verify(activityRepository).existsByUserIdAndStartedAtBetween(userId, startDateTime, endDateTime);
}
} }