/*
* 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.URI;
import java.util.Date;
import java.util.concurrent.ExecutionException;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.mockito.ArgumentCaptor;
import org.springframework.http.HttpHeaders;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.util.concurrent.ListenableFutureCallback;
import org.springframework.util.concurrent.SettableListenableFuture;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.sockjs.frame.Jackson2SockJsMessageCodec;
import org.springframework.web.socket.sockjs.transport.TransportType;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
/**
* Unit tests for {@link DefaultTransportRequest}.
*
* @author Rossen Stoyanchev
*/
public class DefaultTransportRequestTests {
private static final Jackson2SockJsMessageCodec CODEC = new Jackson2SockJsMessageCodec();
private SettableListenableFuture<WebSocketSession> connectFuture;
private ListenableFutureCallback<WebSocketSession> connectCallback;
private TestTransport webSocketTransport;
private TestTransport xhrTransport;
@Rule
public final ExpectedException thrown = ExpectedException.none();
@SuppressWarnings("unchecked")
@Before
public void setup() throws Exception {
this.connectCallback = mock(ListenableFutureCallback.class);
this.connectFuture = new SettableListenableFuture<>();
this.connectFuture.addCallback(this.connectCallback);
this.webSocketTransport = new TestTransport("WebSocketTestTransport");
this.xhrTransport = new TestTransport("XhrTestTransport");
}
@Test
public void connect() throws Exception {
DefaultTransportRequest request = createTransportRequest(this.webSocketTransport, TransportType.WEBSOCKET);
request.connect(null, this.connectFuture);
WebSocketSession session = mock(WebSocketSession.class);
this.webSocketTransport.getConnectCallback().onSuccess(session);
assertSame(session, this.connectFuture.get());
}
@Test
public void fallbackAfterTransportError() throws Exception {
DefaultTransportRequest request1 = createTransportRequest(this.webSocketTransport, TransportType.WEBSOCKET);
DefaultTransportRequest request2 = createTransportRequest(this.xhrTransport, TransportType.XHR_STREAMING);
request1.setFallbackRequest(request2);
request1.connect(null, this.connectFuture);
// Transport error => fallback
this.webSocketTransport.getConnectCallback().onFailure(new IOException("Fake exception 1"));
assertFalse(this.connectFuture.isDone());
assertTrue(this.xhrTransport.invoked());
// Transport error => no more fallback
this.xhrTransport.getConnectCallback().onFailure(new IOException("Fake exception 2"));
assertTrue(this.connectFuture.isDone());
this.thrown.expect(ExecutionException.class);
this.thrown.expectMessage("Fake exception 2");
this.connectFuture.get();
}
@Test
public void fallbackAfterTimeout() throws Exception {
TaskScheduler scheduler = mock(TaskScheduler.class);
Runnable sessionCleanupTask = mock(Runnable.class);
DefaultTransportRequest request1 = createTransportRequest(this.webSocketTransport, TransportType.WEBSOCKET);
DefaultTransportRequest request2 = createTransportRequest(this.xhrTransport, TransportType.XHR_STREAMING);
request1.setFallbackRequest(request2);
request1.setTimeoutScheduler(scheduler);
request1.addTimeoutTask(sessionCleanupTask);
request1.connect(null, this.connectFuture);
assertTrue(this.webSocketTransport.invoked());
assertFalse(this.xhrTransport.invoked());
// Get and invoke the scheduled timeout task
ArgumentCaptor<Runnable> taskCaptor = ArgumentCaptor.forClass(Runnable.class);
verify(scheduler).schedule(taskCaptor.capture(), any(Date.class));
verifyNoMoreInteractions(scheduler);
taskCaptor.getValue().run();
assertTrue(this.xhrTransport.invoked());
verify(sessionCleanupTask).run();
}
protected DefaultTransportRequest createTransportRequest(Transport transport, TransportType type) throws Exception {
SockJsUrlInfo urlInfo = new SockJsUrlInfo(new URI("http://example.com"));
return new DefaultTransportRequest(urlInfo, new HttpHeaders(), new HttpHeaders(), transport, type, CODEC);
}
}