diff --git a/src/test/java/net/javahippie/fitpub/service/InboxProcessorTest.java b/src/test/java/net/javahippie/fitpub/service/InboxProcessorTest.java new file mode 100644 index 0000000..2d4f524 --- /dev/null +++ b/src/test/java/net/javahippie/fitpub/service/InboxProcessorTest.java @@ -0,0 +1,133 @@ +package net.javahippie.fitpub.service; + +import net.javahippie.fitpub.model.entity.Follow; +import net.javahippie.fitpub.model.entity.RemoteActor; +import net.javahippie.fitpub.model.entity.User; +import net.javahippie.fitpub.repository.ActivityRepository; +import net.javahippie.fitpub.repository.CommentRepository; +import net.javahippie.fitpub.repository.FollowRepository; +import net.javahippie.fitpub.repository.LikeRepository; +import net.javahippie.fitpub.repository.RemoteActivityRepository; +import net.javahippie.fitpub.repository.RemoteActorRepository; +import net.javahippie.fitpub.repository.UserRepository; +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; +import org.springframework.test.util.ReflectionTestUtils; + +import java.time.Instant; +import java.util.List; +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.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +@DisplayName("InboxProcessor Tests") +class InboxProcessorTest { + + @Mock + private UserRepository userRepository; + + @Mock + private FollowRepository followRepository; + + @Mock + private FederationService federationService; + + @Mock + private ActivityRepository activityRepository; + + @Mock + private LikeRepository likeRepository; + + @Mock + private CommentRepository commentRepository; + + @Mock + private NotificationService notificationService; + + @Mock + private RemoteActivityRepository remoteActivityRepository; + + @Mock + private RemoteActorRepository remoteActorRepository; + + @InjectMocks + private InboxProcessor inboxProcessor; + + private User localUser; + private String remoteActorUri; + + @BeforeEach + void setUp() { + localUser = User.builder() + .id(UUID.randomUUID()) + .username("JaneDoe") + .email("janedoe@example.com") + .passwordHash("irrelevant") + .publicKey("public-key") + .privateKey("private-key") + .build(); + + remoteActorUri = "https://fitpub.example.com/users/JohnDoe"; + + ReflectionTestUtils.setField(inboxProcessor, "baseUrl", "https://fitpub.example"); + } + + @Test + @DisplayName("Should persist remote activity when published timestamp has fractional seconds but no timezone") + void processCreateRemoteActivity_WithPublishedTimestampWithoutTimezone_ShouldPersistRemoteActivity() { + when(remoteActivityRepository.existsByActivityUri("https://fitpub.example.com/activities/123")) + .thenReturn(false); + when(federationService.fetchRemoteActor(remoteActorUri)).thenReturn(RemoteActor.builder() + .actorUri(remoteActorUri) + .username("JohnDoe") + .domain("fitpub.example.com") + .inboxUrl("https://fitpub.example.com/users/JohnDoe/inbox") + .publicKey("public-key") + .build()); + when(userRepository.findByUsername("JaneDoe")).thenReturn(Optional.of(localUser)); + when(followRepository.findByFollowerIdAndFollowingActorUri(localUser.getId(), remoteActorUri)) + .thenReturn(Optional.of(Follow.builder() + .followerId(localUser.getId()) + .followingActorUri(remoteActorUri) + .status(Follow.FollowStatus.ACCEPTED) + .build())); + + Map note = Map.of( + "id", "https://fitpub.example.com/activities/123", + "type", "Note", + "name", "Lunch Run", + "content", "

Sunny run

", + "published", "2026-05-02T09:24:50.921241", + "to", List.of("https://www.w3.org/ns/activitystreams#Public") + ); + + Map activity = Map.of( + "type", "Create", + "actor", remoteActorUri, + "object", note + ); + + ArgumentCaptor remoteActivityCaptor = + ArgumentCaptor.forClass(net.javahippie.fitpub.model.entity.RemoteActivity.class); + + inboxProcessor.processActivity("JaneDoe", activity); + + verify(remoteActivityRepository).existsByActivityUri("https://fitpub.example.com/activities/123"); + verify(federationService).fetchRemoteActor(remoteActorUri); + verify(remoteActivityRepository).save(remoteActivityCaptor.capture()); + + assertThat(remoteActivityCaptor.getValue().getPublishedAt()) + .isEqualTo(Instant.parse("2026-05-02T09:24:50.921241Z")); + } +}