package com.ankurm.ssews; import java.util.List; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.server.LocalServerPort; import static org.assertj.core.api.Assertions.assertThat; /** * {@code .data(object)} and {@code .data(object, APPLICATION_JSON)} are not the same call. * This test prints both results side by side. */ @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = "dashboard.broadcast.enabled=false") class SseDataConversionTest { @LocalServerPort int port; @Test void mediaTypeDecidesTheConverter() throws Exception { List lines; try (SseClient client = new SseClient(port, "/sse/payload")) { lines = client.readToEnd(); } String withJson = dataAfter(lines, "event:with-json"); String noMediaType = dataAfter(lines, "event:no-media-type"); System.out.println("=== SseEmitter.event().data(record, APPLICATION_JSON) ==="); System.out.println(withJson); System.out.println("=== SseEmitter.event().data(record) [no media type] ==="); System.out.println(noMediaType); assertThat(withJson).startsWith("{").contains("\"seq\":7").contains("\"host\":\"node-a\""); // The finding: for a POJO they are IDENTICAL. No media type does not mean toString(). // send() asks the configured HttpMessageConverters which one canWrite(type, null), and // for a record the first (and only) answer is the Jackson converter. System.out.println("=== POJO: identical? " + withJson.equals(noMediaType) + " ==="); assertThat(noMediaType).isEqualTo(withJson); String stringPlain = dataAfter(lines, "event:string-plain"); String stringJson = dataAfter(lines, "event:string-json"); System.out.println("=== String payload, no media type -> " + stringPlain); System.out.println("=== String payload, APPLICATION_JSON -> " + stringJson); // The finding, and it is the opposite of what I expected when writing this test: for a // String the media type argument changes NOTHING. StringHttpMessageConverter supports // MediaType.ALL and sits ahead of the Jackson converter in the list, so it claims the // write even when you ask for application/json. There is no way to make send() quote a // String -- which is good news if you are streaming pre-rendered JSON, and a trap if you // expected a String field to be escaped for you. assertThat(stringPlain).isEqualTo("{\"already\":\"json\"}"); assertThat(stringJson).isEqualTo(stringPlain); } private static String dataAfter(List lines, String eventLine) { int i = lines.indexOf(eventLine); assertThat(i).as("event line %s present", eventLine).isGreaterThanOrEqualTo(0); for (int j = i; j < lines.size(); j++) { if (lines.get(j).startsWith("data:")) { return lines.get(j).substring("data:".length()); } } throw new AssertionError("no data line after " + eventLine); } }