From f439b25aa0f68350e0e33f8a691154339553c759 Mon Sep 17 00:00:00 2001 From: erlend Date: Tue, 24 Jun 2025 09:55:37 +0200 Subject: [PATCH 1/2] Always return default instance instead of null when mapping to proto --- .../common/ProtobufStandardMappings.java | 251 +++++++++--------- .../abt/mapstruct/common/Timestamps.java | 6 +- .../mapstruct/ProtobufStandardMappings.java | 10 +- .../mapstruct/ProtobufStandardMappings.java | 2 +- .../ProtobufStandardMappingsTest.java | 30 ++- 5 files changed, 156 insertions(+), 143 deletions(-) diff --git a/support-core/src/main/java/no/entur/abt/mapstruct/common/ProtobufStandardMappings.java b/support-core/src/main/java/no/entur/abt/mapstruct/common/ProtobufStandardMappings.java index 24cb783..b76ac3a 100644 --- a/support-core/src/main/java/no/entur/abt/mapstruct/common/ProtobufStandardMappings.java +++ b/support-core/src/main/java/no/entur/abt/mapstruct/common/ProtobufStandardMappings.java @@ -23,6 +23,15 @@ * #L% */ +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.util.GregorianCalendar; +import java.util.TimeZone; + import com.google.protobuf.BoolValue; import com.google.protobuf.ByteString; import com.google.protobuf.BytesValue; @@ -35,133 +44,121 @@ import com.google.protobuf.UInt32Value; import com.google.protobuf.UInt64Value; -import java.time.Instant; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.time.OffsetDateTime; -import java.time.ZoneOffset; -import java.util.GregorianCalendar; -import java.util.TimeZone; - public interface ProtobufStandardMappings { - default ByteString mapByteString(byte[] array) { - if (array == null) { - return ByteString.EMPTY; - } - return ByteString.copyFrom(array); - } - - default byte[] mapByteString(ByteString in) { - if (in != null && !in.isEmpty()) { - return in.toByteArray(); - } - - return null; - } - - default ByteString mapByteStringToString(String string) { - return ByteString.copyFromUtf8(string); - } - - default String mapStringToByteString(ByteString in) { - if (in != null && !in.isEmpty()) { - return in.toStringUtf8(); - } - - return null; - } - - default com.google.type.Date mapLocalDate(LocalDate t) { - return com.google.type.Date.newBuilder().setYear(t.getYear()).setMonth(t.getMonthValue()).setDay(t.getDayOfMonth()).build(); - } - - default LocalDate mapDate(com.google.type.Date t) { - return LocalDate.of(t.getYear(), t.getMonth(), t.getDay()); - } - - default com.google.type.TimeOfDay mapLocalTime(LocalTime t) { - return com.google.type.TimeOfDay.newBuilder().setHours(t.getHour()).setMinutes(t.getMinute()).setSeconds(t.getSecond()).setNanos(t.getNano()).build(); - } - - default LocalTime mapTimeOfDay(com.google.type.TimeOfDay t) { - return LocalTime.of(t.getHours(), t.getMinutes(), t.getSeconds(), t.getNanos()); - } - - default Timestamp map(LocalDateTime i) { - if (i == null) { - return null; - } - - TimeZone systemDefault = TimeZone.getDefault(); - - int offset = systemDefault.getOffset(GregorianCalendar.AD, i.getYear(), i.getMonthValue() - 1, i.getDayOfMonth(), i.getDayOfWeek().getValue(), - i.getNano() / 1000); - - return Timestamp.newBuilder().setSeconds(i.toEpochSecond(ZoneOffset.ofTotalSeconds(offset / 1000))).setNanos(i.getNano()).build(); - } - - default Timestamp map(OffsetDateTime in) { - return Timestamp.newBuilder().setSeconds(in.toEpochSecond()).setNanos(0).build(); - } - - default float map(FloatValue f) { - return f.getValue(); - } - - default double map(DoubleValue f) { - return f.getValue(); - } - - default int map(Int32Value f) { - return f.getValue(); - } - - default long map(Int64Value f) { - return f.getValue(); - } - - default int map(UInt32Value f) { - return f.getValue(); - } - - default long map(UInt64Value f) { - return f.getValue(); - } - - default String map(StringValue f) { - return f.getValue(); - } - - default boolean map(BoolValue f) { - return f.getValue(); - } - - default ByteString map(BytesValue f) { - return f.getValue(); - } - - default Instant mapToInstant(Timestamp t) { - if (t == null) { - return null; - } - - Timestamp sanitized = Timestamps.sanitize(t); - - if (sanitized != null) { - return Instant.ofEpochSecond(sanitized.getSeconds(), sanitized.getNanos()); - } else { - return null; - } - } - - default Timestamp mapToTimestamp(Instant i) { - if (i == null) { - return null; - } - - Timestamp t = Timestamp.newBuilder().setSeconds(i.getEpochSecond()).setNanos(i.getNano()).build(); - return Timestamps.sanitize(t); - } + default ByteString mapByteString(byte[] array) { + if (array == null) { + return ByteString.EMPTY; + } + return ByteString.copyFrom(array); + } + + default byte[] mapByteString(ByteString in) { + if (in != null && !in.isEmpty()) { + return in.toByteArray(); + } + + return null; + } + + default ByteString mapByteStringToString(String string) { + return ByteString.copyFromUtf8(string != null ? string : ""); + } + + default String mapStringToByteString(ByteString in) { + if (in != null && !in.isEmpty()) { + return in.toStringUtf8(); + } + + return null; + } + + default com.google.type.Date mapLocalDate(LocalDate t) { + return t != null ? com.google.type.Date.newBuilder().setYear(t.getYear()).setMonth(t.getMonthValue()).setDay(t.getDayOfMonth()).build() + : com.google.type.Date.getDefaultInstance(); + } + + default LocalDate mapDate(com.google.type.Date t) { + return LocalDate.of(t.getYear(), t.getMonth(), t.getDay()); + } + + default com.google.type.TimeOfDay mapLocalTime(LocalTime t) { + return t != null + ? com.google.type.TimeOfDay.newBuilder().setHours(t.getHour()).setMinutes(t.getMinute()).setSeconds(t.getSecond()).setNanos(t.getNano()).build() + : com.google.type.TimeOfDay.getDefaultInstance(); + } + + default LocalTime mapTimeOfDay(com.google.type.TimeOfDay t) { + return LocalTime.of(t.getHours(), t.getMinutes(), t.getSeconds(), t.getNanos()); + } + + default Timestamp map(LocalDateTime i) { + if (i == null) { + return Timestamp.getDefaultInstance(); + } + + TimeZone systemDefault = TimeZone.getDefault(); + + int offset = systemDefault.getOffset(GregorianCalendar.AD, i.getYear(), i.getMonthValue() - 1, i.getDayOfMonth(), i.getDayOfWeek().getValue(), + i.getNano() / 1000); + + return Timestamp.newBuilder().setSeconds(i.toEpochSecond(ZoneOffset.ofTotalSeconds(offset / 1000))).setNanos(i.getNano()).build(); + } + + default Timestamp map(OffsetDateTime in) { + return in != null ? Timestamp.newBuilder().setSeconds(in.toEpochSecond()).setNanos(0).build() : Timestamp.getDefaultInstance(); + } + + default float map(FloatValue f) { + return f.getValue(); + } + + default double map(DoubleValue f) { + return f.getValue(); + } + + default int map(Int32Value f) { + return f.getValue(); + } + + default long map(Int64Value f) { + return f.getValue(); + } + + default int map(UInt32Value f) { + return f.getValue(); + } + + default long map(UInt64Value f) { + return f.getValue(); + } + + default String map(StringValue f) { + return f.getValue(); + } + + default boolean map(BoolValue f) { + return f.getValue(); + } + + default ByteString map(BytesValue f) { + return f.getValue(); + } + + default Instant mapToInstant(Timestamp t) { + if (t == null || Timestamp.getDefaultInstance().equals(t)) { + return null; + } + Timestamp sanitized = Timestamps.sanitize(t); + return Instant.ofEpochSecond(sanitized.getSeconds(), sanitized.getNanos()); + } + + default Timestamp mapToTimestamp(Instant i) { + if (i == null) { + return Timestamp.getDefaultInstance(); + } + + Timestamp t = Timestamp.newBuilder().setSeconds(i.getEpochSecond()).setNanos(i.getNano()).build(); + return Timestamps.sanitize(t); + } } diff --git a/support-core/src/main/java/no/entur/abt/mapstruct/common/Timestamps.java b/support-core/src/main/java/no/entur/abt/mapstruct/common/Timestamps.java index 09aa764..977f6b8 100644 --- a/support-core/src/main/java/no/entur/abt/mapstruct/common/Timestamps.java +++ b/support-core/src/main/java/no/entur/abt/mapstruct/common/Timestamps.java @@ -23,10 +23,11 @@ * #L% */ -import com.google.protobuf.Timestamp; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.protobuf.Timestamp; + public class Timestamps { private static final Logger LOGGER = LoggerFactory.getLogger(Timestamps.class);; @@ -41,9 +42,6 @@ public class Timestamps { * Sanitize Timestamps outside legal range where possible. */ public static Timestamp sanitize(Timestamp t) { - if (t.getSeconds() == 0 && t.getNanos() == 0) { - return null; // Assuming null for epoch, cannot differentiate - } if (t.getNanos() < 0 || t.getNanos() >= NANOS_PER_SECOND) { throw new IllegalArgumentException(String.format( "Timestamp is not valid. See proto definition for valid values. Seconds (%s) must be in range [-62,135,596,800, +253,402,300,799]. Nanos (%s) must be in range [0, +999,999,999].", diff --git a/support-lite/src/main/java/no/entur/abt/mapstruct/ProtobufStandardMappings.java b/support-lite/src/main/java/no/entur/abt/mapstruct/ProtobufStandardMappings.java index 29a53f2..63723f7 100644 --- a/support-lite/src/main/java/no/entur/abt/mapstruct/ProtobufStandardMappings.java +++ b/support-lite/src/main/java/no/entur/abt/mapstruct/ProtobufStandardMappings.java @@ -48,11 +48,8 @@ default Long toEpochMilliseconds(Timestamp instance) { return instant == null ? null : instant.toEpochMilli(); } - default Timestamp fromEpochMilliseconds(Long instance) { - if (instance == null) { - return null; - } - Instant instant = Instant.ofEpochMilli(instance); + default Timestamp fromEpochMilliseconds(Long millis) { + Instant instant = Instant.ofEpochMilli(millis != null ? millis : 0L); return mapToTimestamp(instant); } @@ -61,6 +58,9 @@ default Duration mapDuration(com.google.protobuf.Duration t) { } default com.google.protobuf.Duration mapDuration(Duration t) { + if (t == null) { + return com.google.protobuf.Duration.getDefaultInstance(); + } long seconds = t.getSeconds(); int nanos = t.getNano(); diff --git a/support-standard/src/main/java/no/entur/abt/mapstruct/ProtobufStandardMappings.java b/support-standard/src/main/java/no/entur/abt/mapstruct/ProtobufStandardMappings.java index 95630c5..c758487 100644 --- a/support-standard/src/main/java/no/entur/abt/mapstruct/ProtobufStandardMappings.java +++ b/support-standard/src/main/java/no/entur/abt/mapstruct/ProtobufStandardMappings.java @@ -67,7 +67,7 @@ default com.google.protobuf.Duration mapDuration(Duration t) { if (t != null) { return Durations.fromNanos(t.toNanos()); } else { - return null; + return com.google.protobuf.Duration.getDefaultInstance(); } } diff --git a/support-standard/src/test/java/no/entur/abt/mapstruct/ProtobufStandardMappingsTest.java b/support-standard/src/test/java/no/entur/abt/mapstruct/ProtobufStandardMappingsTest.java index 9a5f893..fc2408a 100644 --- a/support-standard/src/test/java/no/entur/abt/mapstruct/ProtobufStandardMappingsTest.java +++ b/support-standard/src/test/java/no/entur/abt/mapstruct/ProtobufStandardMappingsTest.java @@ -23,10 +23,8 @@ * #L% */ -import com.google.protobuf.Timestamp; -import com.google.protobuf.util.Durations; -import no.entur.abt.mapstruct.common.Timestamps; -import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; import java.time.Duration; import java.time.Instant; @@ -35,8 +33,12 @@ import java.time.temporal.ChronoUnit; import java.util.concurrent.TimeUnit; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; +import org.junit.jupiter.api.Test; + +import com.google.protobuf.Timestamp; +import com.google.protobuf.util.Durations; + +import no.entur.abt.mapstruct.common.Timestamps; public class ProtobufStandardMappingsTest { @@ -101,6 +103,11 @@ public void mapInstantToTimestamp_whenValueIsTooSmallForRangeForTimestamp_thenMa assertEquals(Timestamps.MIN_VALUE, MAPPER.mapToTimestamp(Instant.now().minus(Integer.MAX_VALUE, ChronoUnit.DAYS))); } + @Test + public void mapToInstant_whenEpoch_thenReturnDefaultTimestamp() { + assertEquals(Timestamp.getDefaultInstance(), MAPPER.mapToTimestamp(Instant.ofEpochSecond(0))); + } + @Test public void mapPositiveDuration() { Duration duration = Duration.of(3, ChronoUnit.NANOS); @@ -137,4 +144,15 @@ public void mapNegativeDuration_fromProto() { Durations.checkValid(pbDuration); assertEquals(pbDuration, MAPPER.mapDuration(duration)); } + + @Test + public void mapDurationToProto_whenNull_thenReturnDefaultDuration() { + assertEquals(com.google.protobuf.Duration.getDefaultInstance(), MAPPER.mapDuration((Duration) null)); + } + + @Test + public void mapLocalDate_whenNull_thenReturnDefaultLocalDate() { + assertEquals(com.google.type.Date.getDefaultInstance(), MAPPER.mapLocalDate(null)); + } + } From 2ebbd2ff058d9a845e44ce0e9f10bdd485d2a2a0 Mon Sep 17 00:00:00 2001 From: erlend Date: Wed, 25 Jun 2025 11:25:02 +0200 Subject: [PATCH 2/2] More tests --- .../ProtobufStandardMappingsTest.java | 259 ++++++++++-------- 1 file changed, 143 insertions(+), 116 deletions(-) diff --git a/support-standard/src/test/java/no/entur/abt/mapstruct/ProtobufStandardMappingsTest.java b/support-standard/src/test/java/no/entur/abt/mapstruct/ProtobufStandardMappingsTest.java index fc2408a..a062009 100644 --- a/support-standard/src/test/java/no/entur/abt/mapstruct/ProtobufStandardMappingsTest.java +++ b/support-standard/src/test/java/no/entur/abt/mapstruct/ProtobufStandardMappingsTest.java @@ -23,136 +23,163 @@ * #L% */ -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; +import com.google.protobuf.ByteString; +import com.google.protobuf.Timestamp; +import com.google.protobuf.util.Durations; +import com.google.type.TimeOfDay; +import no.entur.abt.mapstruct.common.Timestamps; +import org.junit.jupiter.api.Test; import java.time.Duration; import java.time.Instant; import java.time.LocalDateTime; +import java.time.OffsetDateTime; import java.time.ZoneId; import java.time.temporal.ChronoUnit; import java.util.concurrent.TimeUnit; -import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; -import com.google.protobuf.Timestamp; -import com.google.protobuf.util.Durations; +public class ProtobufStandardMappingsTest { -import no.entur.abt.mapstruct.common.Timestamps; + no.entur.abt.mapstruct.ProtobufStandardMappings MAPPER = no.entur.abt.mapstruct.ProtobufStandardMappings.INSTANCE; -public class ProtobufStandardMappingsTest { + @Test + public void testMapLocalDateToTimestampSummertime() { + LocalDateTime l = LocalDateTime.of(2000, 6, 1, 12, 0); + + Timestamp timestamp = MAPPER.map(l); + Instant instant = MAPPER.mapToInstant(timestamp); - no.entur.abt.mapstruct.ProtobufStandardMappings MAPPER = no.entur.abt.mapstruct.ProtobufStandardMappings.INSTANCE; + LocalDateTime back = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); + + assertEquals(l, back); + } - @Test - public void testMapLocalDateToTimestampSummertime() { - LocalDateTime l = LocalDateTime.of(2000, 6, 1, 12, 0); + @Test + public void testMapLocalDateToTimestampWintertime() { + LocalDateTime l = LocalDateTime.of(2000, 2, 1, 12, 0); - Timestamp timestamp = MAPPER.map(l); - Instant instant = MAPPER.mapToInstant(timestamp); + Timestamp timestamp = MAPPER.map(l); + Instant instant = MAPPER.mapToInstant(timestamp); + + LocalDateTime back = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); + + assertEquals(l, back); + } + + @Test + public void mapToInstant_whenSecondsAndNanosIs0_thenMapToNull() { + assertNull(MAPPER.mapToInstant(Timestamp.newBuilder().build())); + } + + @Test + public void mapToInstant_whenSecondsAndNanosIsNull_thenMapToNull() { + assertNull(MAPPER.mapToInstant(null)); + } + + @Test + public void mapToInstant_whenNanosIsSet_thenMapToInstant() { + assertEquals(3000, MAPPER.mapToInstant(Timestamp.newBuilder().setNanos(3000).build()).getNano()); + } + + @Test + public void mapToInstant_whenValueIsTooLargeForRangeForTimestamp_thenMapFromMaxValidTimestamp() { + assertEquals(MAPPER.mapToInstant(Timestamps.MAX_VALUE), MAPPER.mapToInstant(Timestamp.newBuilder().setSeconds(Long.MAX_VALUE).build())); + } + + @Test + public void mapToInstant_whenValueIsTooSmallForRangeForTimestamp_thenMapFromMinValidTimestamp() { + assertEquals(MAPPER.mapToInstant(Timestamps.MIN_VALUE), MAPPER.mapToInstant(Timestamp.newBuilder().setSeconds(-Long.MAX_VALUE).build())); + } + + @Test + public void mapInstantToTimestamp_whenValueIsTooLargeForRangeForTimestamp_thenMapToMaxValidTimestamp() { + assertEquals(Timestamps.MAX_VALUE, MAPPER.mapToTimestamp(Instant.now().plus(Integer.MAX_VALUE, ChronoUnit.DAYS))); + } + + @Test + public void mapInstantToTimestamp_whenValueIsTooSmallForRangeForTimestamp_thenMapToMinValidTimestamp() { + assertEquals(Timestamps.MIN_VALUE, MAPPER.mapToTimestamp(Instant.now().minus(Integer.MAX_VALUE, ChronoUnit.DAYS))); + } + + @Test + public void mapToInstant_whenEpoch_thenReturnDefaultTimestamp() { + assertEquals(Timestamp.getDefaultInstance(), MAPPER.mapToTimestamp(Instant.ofEpochSecond(0))); + } + + @Test + public void mapPositiveDuration() { + Duration duration = Duration.of(3, ChronoUnit.NANOS); + + com.google.protobuf.Duration pbDuration = MAPPER.mapDuration(duration); + Durations.checkValid(pbDuration); + assertEquals(duration, MAPPER.mapDuration(pbDuration)); + } + + @Test + public void mapNegativeDurationToProto_whenSecondsAreNegativeAndNanoPositive() { + Duration duration = Duration.ofSeconds(-3, 2); + + com.google.protobuf.Duration pbDuration = MAPPER.mapDuration(duration); + Durations.checkValid(pbDuration); + assertEquals(duration, MAPPER.mapDuration(pbDuration)); + } + + @Test + public void mapNegativeDurationToProto_whenSecondsArePositiveAndNanoNegative() { + // Duration.ofSeconds accepts negative values. Will still be stored as positive values in Duration + Duration duration = Duration.ofSeconds(3, -(TimeUnit.SECONDS.toNanos(1) - 2)); + + com.google.protobuf.Duration pbDuration = MAPPER.mapDuration(duration); + Durations.checkValid(pbDuration); + assertEquals(duration, MAPPER.mapDuration(pbDuration)); + } + + @Test + public void mapNegativeDuration_fromProto() { + com.google.protobuf.Duration pbDuration = com.google.protobuf.Duration.newBuilder().setSeconds(-10).setNanos(-5).build(); + + Duration duration = MAPPER.mapDuration(pbDuration); + Durations.checkValid(pbDuration); + assertEquals(pbDuration, MAPPER.mapDuration(duration)); + } + + @Test + public void mapDurationToProto_whenNull_thenReturnDefaultInstance() { + assertEquals(com.google.protobuf.Duration.getDefaultInstance(), MAPPER.mapDuration((Duration) null)); + } + + @Test + public void mapLocalDateToProto_whenNull_thenReturnDefaultInstance() { + assertEquals(com.google.type.Date.getDefaultInstance(), MAPPER.mapLocalDate(null)); + } + + @Test + public void mapLocalDateTimeToProto_whenNull_thenReturnDefaultInstance() { + assertEquals(Timestamp.getDefaultInstance(), MAPPER.map((LocalDateTime) null)); + } + + @Test + public void mapOffsetDateTimeToProto_whenNull_thenReturnDefaultInstance() { + assertEquals(Timestamp.getDefaultInstance(), MAPPER.map((OffsetDateTime) null)); + } + + @Test + public void mapInstantToProto_whenNull_thenReturnDefaultInstance() { + assertEquals(Timestamp.getDefaultInstance(), MAPPER.mapToTimestamp(null)); + } + + @Test + public void mapLocalTimeToProto_whenNull_thenReturnDefaultInstance() { + assertEquals(TimeOfDay.getDefaultInstance(), MAPPER.mapLocalTime(null)); + } + + @Test + public void mapByteArrayToProto_whenNull_thenReturnEmpty() { + assertEquals(ByteString.empty(), MAPPER.mapByteString((byte[]) null)); + } - LocalDateTime back = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); - - assertEquals(l, back); - } - - @Test - public void testMapLocalDateToTimestampWintertime() { - LocalDateTime l = LocalDateTime.of(2000, 2, 1, 12, 0); - - Timestamp timestamp = MAPPER.map(l); - Instant instant = MAPPER.mapToInstant(timestamp); - - LocalDateTime back = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); - - assertEquals(l, back); - } - - @Test - public void mapToInstant_whenSecondsAndNanosIs0_thenMapToNull() { - assertNull(MAPPER.mapToInstant(Timestamp.newBuilder().build())); - } - - @Test - public void mapToInstant_whenSecondsAndNanosIsNull_thenMapToNull() { - assertNull(MAPPER.mapToInstant(null)); - } - - @Test - public void mapToInstant_whenNanosIsSet_thenMapToInstant() { - assertEquals(3000, MAPPER.mapToInstant(Timestamp.newBuilder().setNanos(3000).build()).getNano()); - } - - @Test - public void mapToInstant_whenValueIsTooLargeForRangeForTimestamp_thenMapFromMaxValidTimestamp() { - assertEquals(MAPPER.mapToInstant(Timestamps.MAX_VALUE), MAPPER.mapToInstant(Timestamp.newBuilder().setSeconds(Long.MAX_VALUE).build())); - } - - @Test - public void mapToInstant_whenValueIsTooSmallForRangeForTimestamp_thenMapFromMinValidTimestamp() { - assertEquals(MAPPER.mapToInstant(Timestamps.MIN_VALUE), MAPPER.mapToInstant(Timestamp.newBuilder().setSeconds(-Long.MAX_VALUE).build())); - } - - @Test - public void mapInstantToTimestamp_whenValueIsTooLargeForRangeForTimestamp_thenMapToMaxValidTimestamp() { - assertEquals(Timestamps.MAX_VALUE, MAPPER.mapToTimestamp(Instant.now().plus(Integer.MAX_VALUE, ChronoUnit.DAYS))); - } - - @Test - public void mapInstantToTimestamp_whenValueIsTooSmallForRangeForTimestamp_thenMapToMinValidTimestamp() { - assertEquals(Timestamps.MIN_VALUE, MAPPER.mapToTimestamp(Instant.now().minus(Integer.MAX_VALUE, ChronoUnit.DAYS))); - } - - @Test - public void mapToInstant_whenEpoch_thenReturnDefaultTimestamp() { - assertEquals(Timestamp.getDefaultInstance(), MAPPER.mapToTimestamp(Instant.ofEpochSecond(0))); - } - - @Test - public void mapPositiveDuration() { - Duration duration = Duration.of(3, ChronoUnit.NANOS); - - com.google.protobuf.Duration pbDuration = MAPPER.mapDuration(duration); - Durations.checkValid(pbDuration); - assertEquals(duration, MAPPER.mapDuration(pbDuration)); - } - - @Test - public void mapNegativeDurationToProto_whenSecondsAreNegativeAndNanoPositive() { - Duration duration = Duration.ofSeconds(-3, 2); - - com.google.protobuf.Duration pbDuration = MAPPER.mapDuration(duration); - Durations.checkValid(pbDuration); - assertEquals(duration, MAPPER.mapDuration(pbDuration)); - } - - @Test - public void mapNegativeDurationToProto_whenSecondsArePositiveAndNanoNegative() { - // Duration.ofSeconds accepts negative values. Will still be stored as positive values in Duration - Duration duration = Duration.ofSeconds(3, -(TimeUnit.SECONDS.toNanos(1) - 2)); - - com.google.protobuf.Duration pbDuration = MAPPER.mapDuration(duration); - Durations.checkValid(pbDuration); - assertEquals(duration, MAPPER.mapDuration(pbDuration)); - } - - @Test - public void mapNegativeDuration_fromProto() { - com.google.protobuf.Duration pbDuration = com.google.protobuf.Duration.newBuilder().setSeconds(-10).setNanos(-5).build(); - - Duration duration = MAPPER.mapDuration(pbDuration); - Durations.checkValid(pbDuration); - assertEquals(pbDuration, MAPPER.mapDuration(duration)); - } - - @Test - public void mapDurationToProto_whenNull_thenReturnDefaultDuration() { - assertEquals(com.google.protobuf.Duration.getDefaultInstance(), MAPPER.mapDuration((Duration) null)); - } - - @Test - public void mapLocalDate_whenNull_thenReturnDefaultLocalDate() { - assertEquals(com.google.type.Date.getDefaultInstance(), MAPPER.mapLocalDate(null)); - } }