/*
* Copyright 2002-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.socket.sockjs.client;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.URI;
import java.util.List;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.util.concurrent.SettableListenableFuture;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketExtension;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.sockjs.frame.Jackson2SockJsMessageCodec;
import org.springframework.web.socket.sockjs.frame.SockJsFrame;
import org.springframework.web.socket.sockjs.transport.TransportType;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import static org.mockito.BDDMockito.*;
/**
* Unit tests for
* {@link org.springframework.web.socket.sockjs.client.AbstractClientSockJsSession}.
*
* @author Rossen Stoyanchev
*/
public class ClientSockJsSessionTests {
private static final Jackson2SockJsMessageCodec CODEC = new Jackson2SockJsMessageCodec();
private TestClientSockJsSession session;
private WebSocketHandler handler;
private SettableListenableFuture<WebSocketSession> connectFuture;
@Rule
public final ExpectedException thrown = ExpectedException.none();
@Before
public void setup() throws Exception {
SockJsUrlInfo urlInfo = new SockJsUrlInfo(new URI("http://example.com"));
Transport transport = mock(Transport.class);
TransportRequest request = new DefaultTransportRequest(urlInfo, null, null, transport, TransportType.XHR, CODEC);
this.handler = mock(WebSocketHandler.class);
this.connectFuture = new SettableListenableFuture<>();
this.session = new TestClientSockJsSession(request, this.handler, this.connectFuture);
}
@Test
public void handleFrameOpen() throws Exception {
assertThat(this.session.isOpen(), is(false));
this.session.handleFrame(SockJsFrame.openFrame().getContent());
assertThat(this.session.isOpen(), is(true));
assertTrue(this.connectFuture.isDone());
assertThat(this.connectFuture.get(), sameInstance(this.session));
verify(this.handler).afterConnectionEstablished(this.session);
verifyNoMoreInteractions(this.handler);
}
@Test
public void handleFrameOpenWhenStatusNotNew() throws Exception {
this.session.handleFrame(SockJsFrame.openFrame().getContent());
assertThat(this.session.isOpen(), is(true));
this.session.handleFrame(SockJsFrame.openFrame().getContent());
assertThat(this.session.disconnectStatus, equalTo(new CloseStatus(1006, "Server lost session")));
}
@Test
public void handleFrameOpenWithWebSocketHandlerException() throws Exception {
willThrow(new IllegalStateException("Fake error")).given(this.handler).afterConnectionEstablished(this.session);
this.session.handleFrame(SockJsFrame.openFrame().getContent());
assertThat(this.session.isOpen(), is(true));
}
@Test
public void handleFrameMessage() throws Exception {
this.session.handleFrame(SockJsFrame.openFrame().getContent());
this.session.handleFrame(SockJsFrame.messageFrame(CODEC, "foo", "bar").getContent());
verify(this.handler).afterConnectionEstablished(this.session);
verify(this.handler).handleMessage(this.session, new TextMessage("foo"));
verify(this.handler).handleMessage(this.session, new TextMessage("bar"));
verifyNoMoreInteractions(this.handler);
}
@Test
public void handleFrameMessageWhenNotOpen() throws Exception {
this.session.handleFrame(SockJsFrame.openFrame().getContent());
this.session.close();
reset(this.handler);
this.session.handleFrame(SockJsFrame.messageFrame(CODEC, "foo", "bar").getContent());
verifyNoMoreInteractions(this.handler);
}
@Test
public void handleFrameMessageWithBadData() throws Exception {
this.session.handleFrame(SockJsFrame.openFrame().getContent());
this.session.handleFrame("a['bad data");
assertThat(this.session.isOpen(), equalTo(false));
assertThat(this.session.disconnectStatus, equalTo(CloseStatus.BAD_DATA));
verify(this.handler).afterConnectionEstablished(this.session);
verifyNoMoreInteractions(this.handler);
}
@Test
public void handleFrameMessageWithWebSocketHandlerException() throws Exception {
this.session.handleFrame(SockJsFrame.openFrame().getContent());
willThrow(new IllegalStateException("Fake error")).given(this.handler).handleMessage(this.session, new TextMessage("foo"));
willThrow(new IllegalStateException("Fake error")).given(this.handler).handleMessage(this.session, new TextMessage("bar"));
this.session.handleFrame(SockJsFrame.messageFrame(CODEC, "foo", "bar").getContent());
assertThat(this.session.isOpen(), equalTo(true));
verify(this.handler).afterConnectionEstablished(this.session);
verify(this.handler).handleMessage(this.session, new TextMessage("foo"));
verify(this.handler).handleMessage(this.session, new TextMessage("bar"));
verifyNoMoreInteractions(this.handler);
}
@Test
public void handleFrameClose() throws Exception {
this.session.handleFrame(SockJsFrame.openFrame().getContent());
this.session.handleFrame(SockJsFrame.closeFrame(1007, "").getContent());
assertThat(this.session.isOpen(), equalTo(false));
assertThat(this.session.disconnectStatus, equalTo(new CloseStatus(1007, "")));
verify(this.handler).afterConnectionEstablished(this.session);
verifyNoMoreInteractions(this.handler);
}
@Test
public void handleTransportError() throws Exception {
final IllegalStateException ex = new IllegalStateException("Fake error");
this.session.handleTransportError(ex);
verify(this.handler).handleTransportError(this.session, ex);
verifyNoMoreInteractions(this.handler);
}
@Test
public void afterTransportClosed() throws Exception {
this.session.handleFrame(SockJsFrame.openFrame().getContent());
this.session.afterTransportClosed(CloseStatus.SERVER_ERROR);
assertThat(this.session.isOpen(), equalTo(false));
verify(this.handler).afterConnectionEstablished(this.session);
verify(this.handler).afterConnectionClosed(this.session, CloseStatus.SERVER_ERROR);
verifyNoMoreInteractions(this.handler);
}
@Test
public void close() throws Exception {
this.session.handleFrame(SockJsFrame.openFrame().getContent());
this.session.close();
assertThat(this.session.isOpen(), equalTo(false));
assertThat(this.session.disconnectStatus, equalTo(CloseStatus.NORMAL));
verify(this.handler).afterConnectionEstablished(this.session);
verifyNoMoreInteractions(this.handler);
}
@Test
public void closeWithStatus() throws Exception {
this.session.handleFrame(SockJsFrame.openFrame().getContent());
this.session.close(new CloseStatus(3000, "reason"));
assertThat(this.session.disconnectStatus, equalTo(new CloseStatus(3000, "reason")));
}
@Test
public void closeWithNullStatus() throws Exception {
this.session.handleFrame(SockJsFrame.openFrame().getContent());
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Invalid close status");
this.session.close(null);
}
@Test
public void closeWithStatusOutOfRange() throws Exception {
this.session.handleFrame(SockJsFrame.openFrame().getContent());
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Invalid close status");
this.session.close(new CloseStatus(2999, "reason"));
}
@Test
public void timeoutTask() {
this.session.getTimeoutTask().run();
assertThat(this.session.disconnectStatus, equalTo(new CloseStatus(2007, "Transport timed out")));
}
@Test
public void send() throws Exception {
this.session.handleFrame(SockJsFrame.openFrame().getContent());
this.session.sendMessage(new TextMessage("foo"));
assertThat(this.session.sentMessage, equalTo(new TextMessage("[\"foo\"]")));
}
private static class TestClientSockJsSession extends AbstractClientSockJsSession {
private TextMessage sentMessage;
private CloseStatus disconnectStatus;
protected TestClientSockJsSession(TransportRequest request, WebSocketHandler handler,
SettableListenableFuture<WebSocketSession> connectFuture) {
super(request, handler, connectFuture);
}
@Override
protected void sendInternal(TextMessage textMessage) throws IOException {
this.sentMessage = textMessage;
}
@Override
protected void disconnect(CloseStatus status) throws IOException {
this.disconnectStatus = status;
}
@Override
public InetSocketAddress getLocalAddress() {
return null;
}
@Override
public InetSocketAddress getRemoteAddress() {
return null;
}
@Override
public String getAcceptedProtocol() {
return null;
}
@Override
public void setTextMessageSizeLimit(int messageSizeLimit) {
}
@Override
public int getTextMessageSizeLimit() {
return 0;
}
@Override
public void setBinaryMessageSizeLimit(int messageSizeLimit) {
}
@Override
public int getBinaryMessageSizeLimit() {
return 0;
}
@Override
public List<WebSocketExtension> getExtensions() {
return null;
}
}
}