Public Access
feat(user): add welcome-seen-at timestamp for welcome page tracking
Null means the welcome page was not seen yet. Existing users are backfilled at migration time so only newly registered users see the page.
This commit is contained in:
1 parent
2b2f45b9af
commit
6b6ac44551
12 files changed
+105
-1
No files matched your search
@@ -33,9 +33,11 @@ import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.test.context.bean.override.mockito.MockitoBean;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.never;
|
||||
@@ -381,6 +383,40 @@ class UserControllerTest extends AbstractWebMvcTest {
|
||||
.andExpect(jsonPath("$.settings.favoritePlatform").value("PS5"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getCurrentUser_SendsWelcomeSeenAtNullForANewUser() throws Exception {
|
||||
SecurityContextHolder.getContext().setAuthentication(
|
||||
new UsernamePasswordAuthenticationToken(new UserPrincipal("1", null), null, Collections.emptyList())
|
||||
);
|
||||
when(userService.getUser(1L)).thenReturn(User.builder().email("test@example.com")
|
||||
.settings(UserSettings.builder().language("en").theme("dark").build()).build());
|
||||
mockMvc.perform(get("/api/users/me").accept(MediaType.APPLICATION_JSON))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.settings.welcomeSeenAt").value(nullValue()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getCurrentUser_SendsWelcomeSeenAtAsIsoInstant() throws Exception {
|
||||
SecurityContextHolder.getContext().setAuthentication(
|
||||
new UsernamePasswordAuthenticationToken(new UserPrincipal("1", null), null, Collections.emptyList())
|
||||
);
|
||||
when(userService.getUser(1L)).thenReturn(User.builder().email("test@example.com")
|
||||
.settings(UserSettings.builder().language("en").theme("dark")
|
||||
.welcomeSeenAt(Instant.parse("2026-10-01T10:00:00Z")).build()).build());
|
||||
mockMvc.perform(get("/api/users/me").accept(MediaType.APPLICATION_JSON))
|
||||
.andExpect(jsonPath("$.settings.welcomeSeenAt").value("2026-10-01T10:00:00Z"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void markWelcomeSeen_Returns204() throws Exception {
|
||||
SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(
|
||||
new UserPrincipal("1", null), null, Collections.emptyList()));
|
||||
|
||||
mockMvc.perform(put("/api/users/settings/welcome-seen")).andExpect(status().isNoContent());
|
||||
|
||||
verify(userService).markWelcomeSeen(1L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getCurrentUser_NeverChangedDashboard_SendsNullTiles() throws Exception {
|
||||
String email = "test@example.com";
|
||||
|
||||
@@ -14,7 +14,9 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.jpa.test.autoconfigure.TestEntityManager;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -374,6 +376,28 @@ class UserRepositoryTest extends AbstractPersistenceTest {
|
||||
assertEquals(profile, findByEmail("longtile@example.com").orElseThrow().getSettings().getProfileTiles());
|
||||
}
|
||||
|
||||
@Test
|
||||
void markWelcomeSeen_IsNullForANewRowAndSurvivesASettingsSave() {
|
||||
UserEntity userEntity = persisted(UserEntity.builder()
|
||||
.email("welcome@example.com")
|
||||
.password("password")
|
||||
.enabled(true)
|
||||
.registrationDate(LocalDateTime.now())
|
||||
.build());
|
||||
assertNull(findByEmail("welcome@example.com").orElseThrow().getSettings().getWelcomeSeenAt());
|
||||
|
||||
Instant seen = Instant.now().truncatedTo(ChronoUnit.MILLIS);
|
||||
userRepository.update(userEntity.getId(), user -> user.getSettings().setWelcomeSeenAt(seen));
|
||||
entityManager.flush();
|
||||
entityManager.clear();
|
||||
assertEquals(seen, findByEmail("welcome@example.com").orElseThrow().getSettings().getWelcomeSeenAt());
|
||||
|
||||
updateSettings(userEntity.getId(), null, UserSettings.builder().language("de").theme("dark").build());
|
||||
entityManager.flush();
|
||||
entityManager.clear();
|
||||
assertEquals(seen, findByEmail("welcome@example.com").orElseThrow().getSettings().getWelcomeSeenAt());
|
||||
}
|
||||
|
||||
private void updateSettings(long userId, String displayName, UserSettings settings) {
|
||||
userRepository.update(userId, user -> {
|
||||
user.setDisplayName(displayName);
|
||||
|
||||
@@ -19,6 +19,8 @@ import org.mockito.InOrder;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
@@ -26,6 +28,7 @@ import java.util.function.Consumer;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
@@ -192,6 +195,14 @@ class UserServiceTest extends AbstractUnitTest {
|
||||
assertEquals("Switch", appliedChanges(1).getFirst().getSettings().getFavoritePlatform());
|
||||
}
|
||||
|
||||
@Test
|
||||
void markWelcomeSeen_StampsNow() {
|
||||
userService.markWelcomeSeen(1L);
|
||||
|
||||
Instant stamp = appliedChanges(1).getFirst().getSettings().getWelcomeSeenAt();
|
||||
assertTrue(Duration.between(stamp, Instant.now()).abs().getSeconds() < 5);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs each change user 1 got on a fresh user with default settings.
|
||||
*/
|
||||
|
||||
Reference in new issue
Block a user