]> git.basschouten.com Git - openhab-addons.git/blob
e2b129317c04af2c44b7b3187d92074637dbcf34
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
7  * This program and the accompanying materials are made available under the
8  * terms of the Eclipse Public License 2.0 which is available at
9  * http://www.eclipse.org/legal/epl-2.0
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.mielecloud.internal.webservice.sse;
14
15 import static org.mockito.ArgumentMatchers.*;
16 import static org.mockito.Mockito.*;
17
18 import java.io.ByteArrayInputStream;
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.nio.charset.StandardCharsets;
22 import java.util.concurrent.TimeoutException;
23 import java.util.function.Consumer;
24
25 import org.eclipse.jdt.annotation.NonNullByDefault;
26 import org.eclipse.jdt.annotation.Nullable;
27 import org.junit.jupiter.api.Test;
28 import org.junit.jupiter.api.extension.ExtendWith;
29 import org.mockito.Mock;
30 import org.mockito.junit.jupiter.MockitoExtension;
31 import org.openhab.binding.mielecloud.internal.webservice.exception.MieleWebserviceDisconnectSseException;
32
33 /**
34  * @author Björn Lange - Initial contribution
35  */
36 @ExtendWith(MockitoExtension.class)
37 @NonNullByDefault
38 public class SseStreamParserTest {
39     @Mock
40     @NonNullByDefault({})
41     private Consumer<ServerSentEvent> serverSentEventCallback;
42
43     @Mock
44     @NonNullByDefault({})
45     private Consumer<@Nullable Throwable> streamClosedCallback;
46
47     private InputStream getInputStreamReadingUtf8Data(String data) {
48         return new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8));
49     }
50
51     @Test
52     public void whenNoEventIsProvidedThenTheStreamClosedCallbackIsInvoked() {
53         // given:
54         InputStream inputStream = getInputStreamReadingUtf8Data("");
55
56         SseStreamParser parser = new SseStreamParser(inputStream, serverSentEventCallback, streamClosedCallback);
57
58         // when:
59         parser.parseAndDispatchEvents();
60
61         // then:
62         verify(streamClosedCallback).accept(null);
63         verifyNoMoreInteractions(streamClosedCallback);
64         verifyNoInteractions(serverSentEventCallback);
65     }
66
67     @Test
68     public void whenNoEventAndOnlyWhitespaceIsProvidedThenTheStreamClosedCallbackIsInvoked() {
69         // given:
70         InputStream inputStream = getInputStreamReadingUtf8Data("\r\n");
71
72         SseStreamParser parser = new SseStreamParser(inputStream, serverSentEventCallback, streamClosedCallback);
73
74         // when:
75         parser.parseAndDispatchEvents();
76
77         // then:
78         verify(streamClosedCallback).accept(null);
79         verifyNoMoreInteractions(streamClosedCallback);
80         verifyNoInteractions(serverSentEventCallback);
81     }
82
83     @Test
84     public void whenAnEventIsProvidedThenItIsPassedToTheCallback() {
85         // given:
86         InputStream inputStream = getInputStreamReadingUtf8Data("event: ping\r\ndata: pong\r\n");
87
88         SseStreamParser parser = new SseStreamParser(inputStream, serverSentEventCallback, streamClosedCallback);
89
90         // when:
91         parser.parseAndDispatchEvents();
92
93         // then:
94         verify(streamClosedCallback).accept(null);
95         verify(serverSentEventCallback).accept(new ServerSentEvent("ping", "pong"));
96         verifyNoMoreInteractions(streamClosedCallback, serverSentEventCallback);
97     }
98
99     @Test
100     public void whenALineWithInvalidKeyIsProvidedThenItIsIgnored() {
101         // given:
102         InputStream inputStream = getInputStreamReadingUtf8Data("name: ping\r\n");
103
104         SseStreamParser parser = new SseStreamParser(inputStream, serverSentEventCallback, streamClosedCallback);
105
106         // when:
107         parser.parseAndDispatchEvents();
108
109         // then:
110         verify(streamClosedCallback).accept(null);
111         verifyNoMoreInteractions(streamClosedCallback);
112         verifyNoInteractions(serverSentEventCallback);
113     }
114
115     @Test
116     public void whenDataWithoutEventIsProvidedThenItIsIgnored() {
117         // given:
118         InputStream inputStream = getInputStreamReadingUtf8Data("data: ping\r\n");
119
120         SseStreamParser parser = new SseStreamParser(inputStream, serverSentEventCallback, streamClosedCallback);
121
122         // when:
123         parser.parseAndDispatchEvents();
124
125         // then:
126         verify(streamClosedCallback).accept(null);
127         verifyNoMoreInteractions(streamClosedCallback);
128         verifyNoInteractions(serverSentEventCallback);
129     }
130
131     @Test
132     public void whenTheEventStreamBreaksThenTheStreamClosedCallbackIsNotifiedWithTheCause() throws IOException {
133         // given:
134         InputStream inputStream = mock(InputStream.class);
135         TimeoutException timeoutException = new TimeoutException();
136         when(inputStream.read(any(), anyInt(), anyInt())).thenThrow(new IOException(timeoutException));
137
138         SseStreamParser parser = new SseStreamParser(inputStream, serverSentEventCallback, streamClosedCallback);
139
140         // when:
141         parser.parseAndDispatchEvents();
142
143         // then:
144         verify(streamClosedCallback).accept(timeoutException);
145         verifyNoMoreInteractions(streamClosedCallback);
146         verifyNoInteractions(serverSentEventCallback);
147     }
148
149     @Test
150     public void whenTheEventStreamBreaksBecauseOfAnSseDisconnectThenTheStreamCloseCallbackIsNotNotifiedToPreventSseReconnect()
151             throws IOException {
152         // given:
153         InputStream inputStream = mock(InputStream.class);
154         when(inputStream.read(any(), anyInt(), anyInt()))
155                 .thenThrow(new IOException(new MieleWebserviceDisconnectSseException()));
156
157         SseStreamParser parser = new SseStreamParser(inputStream, serverSentEventCallback, streamClosedCallback);
158
159         // when:
160         parser.parseAndDispatchEvents();
161
162         // then:
163         verifyNoInteractions(streamClosedCallback, serverSentEventCallback);
164     }
165
166     @Test
167     public void whenTheEventStreamBreaksAndTheResourceCleanupFailsThenItIsIgnored() throws IOException {
168         // given:
169         InputStream inputStream = mock(InputStream.class);
170         when(inputStream.read(any(), anyInt(), anyInt()))
171                 .thenThrow(new IOException(new MieleWebserviceDisconnectSseException()));
172         doThrow(new IOException()).when(inputStream).close();
173
174         SseStreamParser parser = new SseStreamParser(inputStream, serverSentEventCallback, streamClosedCallback);
175
176         // when:
177         parser.parseAndDispatchEvents();
178
179         // then:
180         verifyNoInteractions(streamClosedCallback, serverSentEventCallback);
181     }
182 }