mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-11-04 01:59:35 +00:00
Always report lastScoringUpdate in UTC
Use an explicit UTC formatter with an English locale while preserving the existing timestamp format, preventing timezone-only server comparison diffs. Add an endpoint regression test that switches the JVM default time zone back and forth and verifies stable UTC output and epoch milliseconds. Assisted-By: GPT-6 Astra
This commit is contained in:
1 parent
1da818504c
commit
df7c212d07
2 files changed
+90
-2
No files matched your search
+80
@@ -0,0 +1,80 @@
|
||||
package com.sap.sailing.server.gateway.test.jaxrs;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Collections;
|
||||
import java.util.TimeZone;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.json.simple.JSONArray;
|
||||
import org.json.simple.JSONObject;
|
||||
import org.json.simple.JSONValue;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.parallel.ResourceLock;
|
||||
|
||||
import com.sap.sailing.domain.common.LeaderboardNameConstants;
|
||||
import com.sap.sailing.domain.leaderboard.Leaderboard;
|
||||
import com.sap.sailing.domain.leaderboard.LeaderboardGroup;
|
||||
import com.sap.sailing.domain.leaderboard.ScoringScheme;
|
||||
import com.sap.sailing.domain.leaderboard.SettableScoreCorrection;
|
||||
import com.sap.sailing.server.gateway.serialization.LeaderboardGroupConstants;
|
||||
import com.sap.sailing.server.interfaces.RacingEventService;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
import com.sap.sse.rest.StreamingOutputUtil;
|
||||
import com.sap.sse.security.SecurityService;
|
||||
|
||||
public class LeaderboardGroupTimeZoneTest {
|
||||
@Test
|
||||
@ResourceLock("java.util.TimeZone.default")
|
||||
public void testLastScoringUpdateIsUtcRegardlessOfDefaultTimeZone() throws Exception {
|
||||
final TimeZone originalTimeZone = TimeZone.getDefault();
|
||||
try {
|
||||
// Start outside UTC so initialization cannot accidentally capture UTC as the default.
|
||||
TimeZone.setDefault(TimeZone.getTimeZone("Europe/Berlin"));
|
||||
final TimePoint lastUpdate = new MillisecondsTimePoint(Instant.parse("2026-10-09T12:13:04Z").toEpochMilli());
|
||||
final SettableScoreCorrection scoreCorrection = mock(SettableScoreCorrection.class);
|
||||
when(scoreCorrection.getTimePointOfLastCorrectionsValidity()).thenReturn(lastUpdate);
|
||||
final Leaderboard leaderboard = mock(Leaderboard.class);
|
||||
when(leaderboard.getName()).thenReturn("CNR_2026_F36");
|
||||
when(leaderboard.getScoreCorrection()).thenReturn(scoreCorrection);
|
||||
when(leaderboard.getScoringScheme()).thenReturn(mock(ScoringScheme.class));
|
||||
final LeaderboardGroup leaderboardGroup = mock(LeaderboardGroup.class);
|
||||
when(leaderboardGroup.getName()).thenReturn("CNR_2026_GROUP");
|
||||
when(leaderboardGroup.getId()).thenReturn(UUID.fromString("12e275d9-37ef-441d-8376-221d2ef07e04"));
|
||||
when(leaderboardGroup.getLeaderboards()).thenReturn(Collections.singletonList(leaderboard));
|
||||
final RacingEventService service = mock(RacingEventService.class);
|
||||
when(service.getLeaderboardGroupByName(leaderboardGroup.getName())).thenReturn(leaderboardGroup);
|
||||
when(service.getAllEvents()).thenReturn(Collections.emptyList());
|
||||
final SecurityService securityService = mock(SecurityService.class);
|
||||
when(securityService.hasCurrentUserReadPermission(leaderboardGroup)).thenReturn(/* value */ true);
|
||||
when(securityService.hasCurrentUserReadPermission(leaderboard)).thenReturn(/* value */ true);
|
||||
final DummyLeaderboardGroupsResource resource = spy(new DummyLeaderboardGroupsResource());
|
||||
doReturn(service).when(resource).getService();
|
||||
doReturn(securityService).when(resource).getSecurityService();
|
||||
for (final String zoneId : new String[] { "Europe/Berlin", "UTC", "America/Los_Angeles", "UTC",
|
||||
"Asia/Kolkata", "Europe/Berlin", "UTC" }) {
|
||||
TimeZone.setDefault(TimeZone.getTimeZone(zoneId));
|
||||
final Response response = resource.getLeaderboardGroup(leaderboardGroup.getName());
|
||||
assertEquals(Response.Status.OK.getStatusCode(), response.getStatus(), zoneId);
|
||||
final JSONObject json = (JSONObject) JSONValue.parseWithException(
|
||||
StreamingOutputUtil.getEntityAsString(response.getEntity()));
|
||||
final JSONObject jsonLeaderboard = (JSONObject) ((JSONArray) json.get(
|
||||
LeaderboardGroupConstants.LEADERBOARDS)).get(/* index */ 0);
|
||||
assertEquals("Fri Oct 09 12:13:04 UTC 2026",
|
||||
jsonLeaderboard.get(LeaderboardNameConstants.LASTSCORINGUPDATE), zoneId);
|
||||
assertEquals(lastUpdate.asMillis(),
|
||||
jsonLeaderboard.get(LeaderboardNameConstants.LASTSCORINGUPDATE_MILLIS), zoneId);
|
||||
}
|
||||
} finally {
|
||||
TimeZone.setDefault(originalTimeZone);
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
-2
@@ -1,11 +1,14 @@
|
||||
package com.sap.sailing.server.gateway.jaxrs.api;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
@@ -61,6 +64,9 @@ public class LeaderboardGroupsResource extends AbstractSailingServerResource {
|
||||
protected static final String EXISTING_GROUP_ID_QUERY_PARAM = "existingGroupId";
|
||||
protected static final String CREATE_NEW_GROUP_QUERY_PARAM = "createNewGroup";
|
||||
protected static final String NAME_PATH_PARAM = "name";
|
||||
// Preserve the Date.toString() format, using UTC independently of the runtime's time zone and locale.
|
||||
private static final DateTimeFormatter LAST_SCORING_UPDATE_FORMATTER = DateTimeFormatter
|
||||
.ofPattern("EEE MMM dd HH:mm:ss zzz yyyy", Locale.ENGLISH).withZone(ZoneId.of("UTC"));
|
||||
|
||||
@POST
|
||||
@Path("/{"+NAME_PATH_PARAM+"}/migrate")
|
||||
@@ -175,9 +181,11 @@ public class LeaderboardGroupsResource extends AbstractSailingServerResource {
|
||||
SettableScoreCorrection scoreCorrection = leaderboard.getScoreCorrection();
|
||||
if (scoreCorrection != null) {
|
||||
jsonLeaderboard.put(LeaderboardNameConstants.SCORINGCOMMENT, scoreCorrection.getComment());
|
||||
TimePoint lastUpdateTimepoint = scoreCorrection.getTimePointOfLastCorrectionsValidity();
|
||||
final TimePoint lastUpdateTimepoint = scoreCorrection.getTimePointOfLastCorrectionsValidity();
|
||||
jsonLeaderboard.put(LeaderboardNameConstants.LASTSCORINGUPDATE,
|
||||
lastUpdateTimepoint != null ? lastUpdateTimepoint.asDate().toString() : null);
|
||||
lastUpdateTimepoint != null
|
||||
? LAST_SCORING_UPDATE_FORMATTER.format(lastUpdateTimepoint.asDate().toInstant())
|
||||
: null);
|
||||
jsonLeaderboard.put(LeaderboardNameConstants.LASTSCORINGUPDATE_MILLIS,
|
||||
lastUpdateTimepoint != null ? lastUpdateTimepoint.asMillis() : null);
|
||||
} else {
|
||||
|
||||
Reference in new issue
Block a user