diff --git a/src/main/java/io/getstream/chat/java/models/Message.java b/src/main/java/io/getstream/chat/java/models/Message.java index f0cd39ca..81da6a05 100644 --- a/src/main/java/io/getstream/chat/java/models/Message.java +++ b/src/main/java/io/getstream/chat/java/models/Message.java @@ -513,8 +513,11 @@ public static class MessageRequestObject { @JsonProperty("html") private String html; + // @Singular builds an empty map, not null. Sending {} fails scheduled sends on servers + // before chat#19652, which stored this legacy field as invalid JSON. @Singular @Nullable + @JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonProperty("reaction_scores") private Map reactionScores; diff --git a/src/test/java/io/getstream/chat/java/ScheduledMessageIntegrationTest.java b/src/test/java/io/getstream/chat/java/ScheduledMessageIntegrationTest.java index 776a305c..e16fc732 100644 --- a/src/test/java/io/getstream/chat/java/ScheduledMessageIntegrationTest.java +++ b/src/test/java/io/getstream/chat/java/ScheduledMessageIntegrationTest.java @@ -11,17 +11,24 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; /** - * Runs against a real app. The scheduled messages endpoints are behind a per-app flag, so these - * tests run only when STREAM_SCHEDULED_MESSAGES_ENABLED=true says the flag is on for the app of - * STREAM_KEY. + * Runs against a real app. The scheduled messages endpoints are behind a per-app flag, which is on + * for the app of STREAM_KEY that CI uses. */ -@EnabledIfEnvironmentVariable(named = "STREAM_SCHEDULED_MESSAGES_ENABLED", matches = "true") public class ScheduledMessageIntegrationTest extends BasicTest { - private static final long ONE_HOUR = 60 * 60 * 1000L; + private static final long ONE_MINUTE = 60 * 1000L; + private static final long ONE_HOUR = 60 * ONE_MINUTE; + + /** + * Returns a time offsetMillis from now, floored to a whole minute. The server rounds a future + * scheduled_at up to the next whole UTC minute, so only a whole-minute time is stored unchanged. + */ + private static Date wholeMinuteFromNow(long offsetMillis) { + long t = System.currentTimeMillis() + offsetMillis; + return new Date(t - t % ONE_MINUTE); + } private static MessageSendResponse schedule(String text, Date scheduledAt) { return Assertions.assertDoesNotThrow( @@ -40,7 +47,7 @@ private static MessageSendResponse schedule(String text, Date scheduledAt) { @Test void whenSchedulingAMessage_thenResponseCarriesTheScheduledMessage() { String text = UUID.randomUUID().toString(); - Date scheduledAt = new Date(System.currentTimeMillis() + ONE_HOUR); + Date scheduledAt = wholeMinuteFromNow(ONE_HOUR); MessageSendResponse response = schedule(text, scheduledAt); @@ -50,7 +57,7 @@ void whenSchedulingAMessage_thenResponseCarriesTheScheduledMessage() { Assertions.assertEquals(testChannel.getType() + ":" + testChannel.getId(), sm.getCid()); Assertions.assertEquals(testUserRequestObject.getId(), sm.getUser().getId()); Assertions.assertEquals(text, sm.getMessage().getText()); - Assertions.assertEquals(scheduledAt.getTime() / 1000, sm.getScheduledAt().getTime() / 1000); + Assertions.assertEquals(scheduledAt, sm.getScheduledAt()); } @DisplayName("Can query a user's scheduled messages in a channel") @@ -83,7 +90,7 @@ void whenUpdatingAScheduledMessage_thenTheChangeIsReturned() { ScheduledMessage sm = schedule(UUID.randomUUID().toString(), new Date(System.currentTimeMillis() + ONE_HOUR)) .getScheduledMessage(); - Date newTime = new Date(System.currentTimeMillis() + 2 * ONE_HOUR); + Date newTime = wholeMinuteFromNow(2 * ONE_HOUR); ScheduledMessage updated = Assertions.assertDoesNotThrow( @@ -96,7 +103,7 @@ void whenUpdatingAScheduledMessage_thenTheChangeIsReturned() { Assertions.assertEquals(sm.getId(), updated.getId()); Assertions.assertEquals("edited", updated.getMessage().getText()); - Assertions.assertEquals(newTime.getTime() / 1000, updated.getScheduledAt().getTime() / 1000); + Assertions.assertEquals(newTime, updated.getScheduledAt()); } @DisplayName("Can cancel a scheduled message") diff --git a/src/test/java/io/getstream/chat/java/ScheduledMessageTest.java b/src/test/java/io/getstream/chat/java/ScheduledMessageTest.java index f8ae418a..652f5fe3 100644 --- a/src/test/java/io/getstream/chat/java/ScheduledMessageTest.java +++ b/src/test/java/io/getstream/chat/java/ScheduledMessageTest.java @@ -63,6 +63,19 @@ void whenSendingWithScheduledAt_thenBodyCarriesIt() throws Exception { Assertions.assertTrue(body.contains("\"scheduled_at\":" + SCHEDULED_AT_JSON), body); } + @DisplayName("Send omits reaction_scores when none are set") + @Test + void whenSendingWithoutReactionScores_thenBodyOmitsThem() throws Exception { + String body = + MAPPER.writeValueAsString( + Message.send("messaging", "general") + .message(MessageRequestObject.builder().text("hi").userId("gandalf").build()) + .scheduledAt(SCHEDULED_AT) + .internalBuild()); + + Assertions.assertFalse(body.contains("\"reaction_scores\""), body); + } + @DisplayName("Send response reads scheduled_message") @Test void whenSendResponseHasScheduledMessage_thenGetterReturnsIt() throws Exception {