From e4edc8b5ad249f911a20574d76d04d6be899315f Mon Sep 17 00:00:00 2001 From: Vinod Kumar Date: Mon, 13 Jul 2026 11:53:01 +0530 Subject: [PATCH] Add SSE support to MockClient --- .../java/kong/unirest/core/MockClient.java | 87 ++++++++++++++++++- .../src/test/java/kong/tests/SseMockTest.java | 70 +++++++++++++++ 2 files changed, 156 insertions(+), 1 deletion(-) create mode 100644 unirest-modules-mocks/src/test/java/kong/tests/SseMockTest.java diff --git a/unirest-modules-mocks/src/main/java/kong/unirest/core/MockClient.java b/unirest-modules-mocks/src/main/java/kong/unirest/core/MockClient.java index b742bda22..68fb47098 100644 --- a/unirest-modules-mocks/src/main/java/kong/unirest/core/MockClient.java +++ b/unirest-modules-mocks/src/main/java/kong/unirest/core/MockClient.java @@ -144,7 +144,29 @@ public CompletableFuture sse(SseRequest request, SseHandler handler) { @Override public Stream sse(SseRequest request) { - return Stream.empty(); + HttpRequest httpRequest = new MockSseHttpRequest(config.get(), request); + RawResponse response = findExpectation(httpRequest).exchange(httpRequest, config.get()); + return parseSseEvents(response.getContentAsString()); + } + + private Stream parseSseEvents(String body) { + if (body == null || body.isEmpty()) { + return Stream.empty(); + } + + List events = new ArrayList<>(); + SseEventBuilder builder = new SseEventBuilder(config.get()); + + for (String line : body.split("\\R", -1)) { + if (line.isEmpty()) { + builder.addEvent(events); + } else if (!line.startsWith(":")) { + builder.accept(line); + } + } + + builder.addEvent(events); + return events.stream(); } public SocketSet serversSocket() { @@ -224,4 +246,67 @@ public ExpectedResponse defaultResponse() { this.defaultResponse = new Invocation(); return this.defaultResponse.thenReturn(); } + + private static class MockSseHttpRequest extends BaseRequest { + private MockSseHttpRequest(Config config, SseRequest request) { + super(config, HttpMethod.GET, request.getUrl()); + this.headers.putAll(request.getHeaders()); + } + } + + private static class SseEventBuilder { + + private final Config config; + private String id = ""; + private String event = ""; + private final StringBuilder data = new StringBuilder(); + + private SseEventBuilder(Config config) { + this.config = config; + } + + private void accept(String line) { + int separator = line.indexOf(':'); + String field = separator >= 0 ? line.substring(0, separator) : line; + String value = separator >= 0 ? line.substring(separator + 1) : ""; + + if (value.startsWith(" ")) { + value = value.substring(1); + } + + accept(field, value); + } + + private void accept(String field, String value) { + switch (field) { + case "id": + id = value; + break; + case "event": + event = value; + break; + case "data": + data.append(value).append('\n'); + break; + default: + break; + } + } + + private void addEvent(List events) { + if (data.length() == 0) { + return; + } + + data.setLength(data.length() - 1); + events.add(new Event(id, event, data.toString(), config)); + reset(); + } + + private void reset() { + id = ""; + event = ""; + data.setLength(0); + } + } } diff --git a/unirest-modules-mocks/src/test/java/kong/tests/SseMockTest.java b/unirest-modules-mocks/src/test/java/kong/tests/SseMockTest.java new file mode 100644 index 000000000..6c5e5062c --- /dev/null +++ b/unirest-modules-mocks/src/test/java/kong/tests/SseMockTest.java @@ -0,0 +1,70 @@ +/** + * The MIT License + * + * Copyright for portions of unirest-java are held by Kong Inc (c) 2013. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package kong.tests; + +import kong.unirest.core.HttpMethod; +import kong.unirest.core.Unirest; +import kong.unirest.core.java.Event; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.stream.Collectors; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class SseMockTest extends Base { + + @Test + void shouldReturnMockedSseEventsFromExpectedResponse() { + client.expect(HttpMethod.GET, "http://events") + .thenReturn( + "id: 1\n" + + "event: greeting\n" + + "data: hello\n" + + "\n" + + "id: 2\n" + + "event: greeting\n" + + "data: world\n" + + "\n") + .withHeader("Content-Type", "text/event-stream"); + + List events = Unirest.sse("http://events") + .connect() + .collect(Collectors.toList()); + + assertEquals(2, events.size()); + + assertEquals("1", events.get(0).id()); + assertEquals("greeting", events.get(0).event()); + assertEquals("hello", events.get(0).data()); + + assertEquals("2", events.get(1).id()); + assertEquals("greeting", events.get(1).event()); + assertEquals("world", events.get(1).data()); + + client.verifyAll(); + } +} \ No newline at end of file