test: add regression coverage for outgoing published timestamps

Signed-off-by: Marcus Fihlon <marcus@fihlon.swiss>
This commit is contained in:
Marcus Fihlon 2026-05-02 20:19:32 +02:00
parent f5dd624016
commit 2229ba98ff
Signed by: McPringle
GPG key ID: C6B7F469EE363E1F
2 changed files with 139 additions and 2 deletions

View file

@ -0,0 +1,114 @@
package net.javahippie.fitpub.controller;
import com.fasterxml.jackson.databind.ObjectMapper;
import net.javahippie.fitpub.model.entity.Activity;
import net.javahippie.fitpub.model.entity.User;
import net.javahippie.fitpub.repository.ActivityRepository;
import net.javahippie.fitpub.repository.FollowRepository;
import net.javahippie.fitpub.repository.UserRepository;
import net.javahippie.fitpub.security.HttpSignatureValidator;
import net.javahippie.fitpub.service.ActivityImageService;
import net.javahippie.fitpub.service.FederationService;
import net.javahippie.fitpub.service.InboxProcessor;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.http.ResponseEntity;
import org.springframework.test.util.ReflectionTestUtils;
import java.io.File;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
@DisplayName("ActivityPubController Tests")
class ActivityPubControllerTest {
@Mock
private UserRepository userRepository;
@Mock
private ActivityRepository activityRepository;
@Mock
private ActivityImageService activityImageService;
@Mock
private InboxProcessor inboxProcessor;
@Mock
private FollowRepository followRepository;
@Mock
private HttpSignatureValidator signatureValidator;
@Mock
private FederationService federationService;
@Mock
private ObjectMapper objectMapper;
@InjectMocks
private ActivityPubController controller;
private UUID activityId;
private UUID userId;
private Activity activity;
private User user;
private LocalDateTime createdAt;
@BeforeEach
void setUp() {
activityId = UUID.randomUUID();
userId = UUID.randomUUID();
createdAt = LocalDateTime.of(2026, 5, 2, 9, 24, 50, 921_241_000);
ReflectionTestUtils.setField(controller, "baseUrl", "https://fitpub.example");
activity = Activity.builder()
.id(activityId)
.userId(userId)
.activityType(Activity.ActivityType.RUN)
.title("Lunch Run")
.description("Sunny run")
.visibility(Activity.Visibility.PUBLIC)
.totalDistance(BigDecimal.valueOf(5000))
.totalDurationSeconds(1800L)
.createdAt(createdAt)
.build();
user = User.builder()
.id(userId)
.username("JaneDoe")
.email("janedoe@example.com")
.publicKey("public-key")
.privateKey("private-key")
.build();
}
@Test
@DisplayName("Should serialize activity published timestamp with timezone")
void getActivity_ShouldSerializePublishedTimestampWithTimezone() {
when(activityRepository.findById(activityId)).thenReturn(Optional.of(activity));
when(userRepository.findById(userId)).thenReturn(Optional.of(user));
when(activityImageService.getActivityImageFile(activityId)).thenReturn(new File("/definitely/nonexistent-fitpub-test-image"));
ResponseEntity<Map<String, Object>> response = controller.getActivity(activityId);
assertThat(response.getStatusCode().is2xxSuccessful()).isTrue();
assertThat(response.getBody()).isNotNull();
assertThat(response.getBody().get("published"))
.isEqualTo(createdAt.atOffset(ZoneOffset.UTC).toInstant().toString());
}
}

View file

@ -4,6 +4,7 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
@ -15,9 +16,11 @@ import org.springframework.test.util.ReflectionTestUtils;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Optional;
import java.util.UUID;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
@ -56,11 +59,13 @@ class ActivityPostProcessingServiceTest {
private UUID userId;
private Activity testActivity;
private User testUser;
private LocalDateTime createdAt;
@BeforeEach
void setUp() {
activityId = UUID.randomUUID();
userId = UUID.randomUUID();
createdAt = LocalDateTime.of(2026, 5, 2, 9, 24, 50, 921_241_000);
// Set baseUrl via reflection (since it's @Value injected)
ReflectionTestUtils.setField(service, "baseUrl", "https://test.example");
@ -76,8 +81,8 @@ class ActivityPostProcessingServiceTest {
.totalDistance(BigDecimal.valueOf(5000))
.totalDurationSeconds(1800L)
.elevationGain(BigDecimal.valueOf(100))
.startedAt(LocalDateTime.now())
.createdAt(LocalDateTime.now())
.startedAt(createdAt.minusMinutes(30))
.createdAt(createdAt)
.build();
// Create test user
@ -232,6 +237,24 @@ class ActivityPostProcessingServiceTest {
verify(federationService).sendCreateActivity(anyString(), any(), eq(testUser), eq(false));
}
@Test
@DisplayName("Should serialize federation note published timestamp with timezone")
void testPublishToFederationAsync_PublishedTimestampIncludesTimezone() {
when(activityRepository.findById(activityId)).thenReturn(Optional.of(testActivity));
when(userRepository.findById(userId)).thenReturn(Optional.of(testUser));
when(activityImageService.generateActivityImage(testActivity)).thenReturn(null);
doNothing().when(federationService).sendCreateActivity(anyString(), any(), any(), anyBoolean());
@SuppressWarnings("unchecked")
ArgumentCaptor<java.util.Map<String, Object>> noteCaptor = ArgumentCaptor.forClass(java.util.Map.class);
service.publishToFederationAsync(activityId, userId);
verify(federationService).sendCreateActivity(anyString(), noteCaptor.capture(), eq(testUser), eq(true));
assertThat(noteCaptor.getValue().get("published"))
.isEqualTo(createdAt.atOffset(ZoneOffset.UTC).toInstant().toString());
}
@Test
@DisplayName("Should skip federation for PRIVATE activity")
void testPublishToFederationAsync_PrivateActivity() {