package com.mydreamjob.auth; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.ValueOperations; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.context.ActiveProfiles; import java.sql.Timestamp; import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneId; import java.util.Set; import java.util.concurrent.atomic.AtomicLong; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; /** Independent S02 acceptance reproduction for persisted timestamp consistency. */ @SpringBootTest @ActiveProfiles("integration-test") class S02IndependentClockAcceptanceTest { private static final AtomicLong PHONE_SEQ = new AtomicLong(9800); private static final Instant CAPTURED_NOW = Instant.parse("2026-01-02T03:04:05Z"); private static final ZoneId ZONE = ZoneId.of("Asia/Shanghai"); @MockBean private RedisTemplate redisTemplate; @Autowired private UserRepository userRepository; @Autowired private AuthSessionService authSessionService; @Autowired private JdbcTemplate jdbcTemplate; private Long userId; @BeforeEach @SuppressWarnings("unchecked") void setUp() { ValueOperations operations = mock(ValueOperations.class); when(redisTemplate.opsForValue()).thenReturn(operations); userId = userRepository.save(User.builder() .phone("12" + String.format("%09d", PHONE_SEQ.incrementAndGet())) .nickname("S02 independent clock") .role(User.Role.STUDENT) .roles(Set.of(User.Role.STUDENT)) .active(true) .verified(true) .build()).getId(); } @AfterEach void tearDown() { if (userId != null) { jdbcTemplate.update("DELETE FROM audit_logs WHERE user_id = ?", userId); jdbcTemplate.update("DELETE FROM auth_tokens WHERE user_id = ?", userId); userRepository.deleteById(userId); } } @Test void persistedTokenCreatedAtMustUseCapturedNow() { AuthSessionService.SessionTokens tokens = authSessionService.createSession( userRepository.findById(userId).orElseThrow(), CAPTURED_NOW, null); Timestamp createdAt = jdbcTemplate.queryForObject( "SELECT created_at FROM auth_tokens WHERE session_id = ?", Timestamp.class, tokens.sessionId()); assertThat(createdAt.toLocalDateTime()) .isEqualTo(LocalDateTime.ofInstant(CAPTURED_NOW, ZONE)); } }