/*
* #%L
* Wisdom-Framework
* %%
* Copyright (C) 2013 - 2014 Wisdom Framework
* %%
* 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.
* #L%
*/
package org.wisdom.router;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.NullNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.junit.Test;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.wisdom.api.DefaultController;
import org.wisdom.api.annotations.*;
import org.wisdom.api.concurrent.ManagedExecutorService;
import org.wisdom.api.content.ParameterConverter;
import org.wisdom.api.content.ParameterFactory;
import org.wisdom.api.http.websockets.WebSocketDispatcher;
import org.wisdom.content.converters.ParamConverterEngine;
import java.nio.charset.Charset;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Callable;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.entry;
import static org.mockito.Mockito.*;
/**
* Check the WebSocket Router
*/
public class WebSocketRouterTest {
@Test
public void testRegistrationAndUnregistration() {
WebSocketRouter router = new WebSocketRouter();
final WebSocketDispatcher dispatcher = mock(WebSocketDispatcher.class);
router.dispatchers = new WebSocketDispatcher[]{dispatcher};
router.bindDispatcher(dispatcher);
verify(dispatcher, times(1)).register(router);
router.stop();
verify(dispatcher, times(1)).unregister(router);
}
@Test
public void testControllerBindingWithNoAnnotation() {
WebSocketRouter router = new WebSocketRouter();
final WebSocketDispatcher dispatcher = mock(WebSocketDispatcher.class);
router.dispatchers = new WebSocketDispatcher[]{dispatcher};
router.converter = new ParamConverterEngine(
Collections.<ParameterConverter>emptyList(),
Collections.<ParameterFactory>emptyList());
final DefaultController controller = new DefaultController() {
public void foo() {
}
};
router.bindController(controller);
assertThat(router.closes).isEmpty();
assertThat(router.opens).isEmpty();
assertThat(router.listeners).isEmpty();
router.unbindController(controller);
}
String message;
@Test
public void testControllerWithOnMessageAnnotation() {
WebSocketRouter router = new WebSocketRouter();
final WebSocketDispatcher dispatcher = mock(WebSocketDispatcher.class);
router.dispatchers = new WebSocketDispatcher[]{dispatcher};
router.executor = mock(ManagedExecutorService.class);
router.converter = new ParamConverterEngine(
Collections.<ParameterConverter>emptyList(),
Collections.<ParameterFactory>emptyList());
doAnswer(new Answer() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
((Callable<Void>) invocation.getArguments()[0]).call();
return null;
}
}).when(router.executor).submit(any(Callable.class));
final DefaultController controller = new DefaultController() {
@OnMessage("/ws")
public void foo(@Body String message) {
WebSocketRouterTest.this.message = message;
}
};
router.bindController(controller);
assertThat(router.closes).isEmpty();
assertThat(router.opens).isEmpty();
assertThat(router.listeners).hasSize(1);
assertThat(router.listeners.iterator().next().check()).isTrue();
assertThat(router.listeners.iterator().next().getController()).isEqualTo(controller);
router.received("/ws", "client", "hello".getBytes(Charset.defaultCharset()));
assertThat(message).isEqualTo("hello");
router.unbindController(controller);
}
@Test
public void testControllerWithOnMessageAnnotationWithParameters() {
WebSocketRouter router = new WebSocketRouter();
router.converter = new ParamConverterEngine(
Collections.<ParameterConverter>emptyList(),
Collections.<ParameterFactory>emptyList());
final WebSocketDispatcher dispatcher = mock(WebSocketDispatcher.class);
router.dispatchers = new WebSocketDispatcher[]{dispatcher};
router.executor = mock(ManagedExecutorService.class);
doAnswer(new Answer() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
((Callable<Void>) invocation.getArguments()[0]).call();
return null;
}
}).when(router.executor).submit(any(Callable.class));
final Map<String, String> results = new HashMap<>();
final DefaultController controller = new DefaultController() {
@OnMessage("/ws/{name}")
public void foo(@Parameter("name") String name, @Body String message, @Parameter("client") String client) {
results.put("message", message);
results.put("name", name);
results.put("client", client);
}
};
router.bindController(controller);
assertThat(router.closes).isEmpty();
assertThat(router.opens).isEmpty();
assertThat(router.listeners).hasSize(1);
assertThat(router.listeners.iterator().next().check()).isTrue();
assertThat(router.listeners.iterator().next().getController()).isEqualTo(controller);
router.received("/ws/foo", "client", "hello".getBytes(Charset.defaultCharset()));
assertThat(results)
.contains(entry("message", "hello"))
.contains(entry("client", "client"))
.contains(entry("name", "foo"));
results.clear();
router.received("/ws", "client", "hello".getBytes(Charset.defaultCharset()));
// Should not have been received.
assertThat(results).isEmpty();
router.unbindController(controller);
}
@Test
public void testControllerWithOpenAndCloseAnnotations() {
WebSocketRouter router = new WebSocketRouter();
final WebSocketDispatcher dispatcher = mock(WebSocketDispatcher.class);
router.dispatchers = new WebSocketDispatcher[]{dispatcher};
final Map<String, String> results = new HashMap<>();
final DefaultController controller = new DefaultController() {
@Opened("/ws")
public void open() {
results.put("open", "true");
}
@Opened("/ws")
public void openWithClient(@Parameter("client") String client) {
results.put("open-client", client);
}
@Closed("/ws")
public void close() {
results.put("close", "true");
}
@Closed("/ws")
public void closeWithClient(@Parameter("client") String client) {
results.put("close-client", client);
}
};
router.bindController(controller);
assertThat(router.closes).hasSize(2);
assertThat(router.opens).hasSize(2);
assertThat(router.listeners).hasSize(0);
router.opened("/ws", "id");
assertThat(results)
.contains(entry("open", "true"))
.contains(entry("open-client", "id"))
.doesNotContainKey("close")
.doesNotContainKey("close-client");
router.closed("/ws", "id");
assertThat(results)
.contains(entry("open", "true"))
.contains(entry("open-client", "id"))
.contains(entry("close", "true"))
.contains(entry("close-client", "id"));
router.unbindController(controller);
}
@Test
public void testSend() {
WebSocketRouter router = new WebSocketRouter();
final WebSocketDispatcher dispatcher = mock(WebSocketDispatcher.class);
router.dispatchers = new WebSocketDispatcher[]{dispatcher};
router.send("/ws", "client", "hello");
verify(dispatcher, times(1)).send("/ws", "client", "hello");
router.send("/ws", "client", "hello".getBytes(Charset.defaultCharset()));
verify(dispatcher, times(1)).send("/ws", "client", "hello".getBytes(Charset.defaultCharset()));
router.send("/ws", "client", (String) null);
// should not be sent.
verify(dispatcher, times(0)).send("/ws", "client", (String) null);
router.send("/ws", null, "hello");
// should not be sent.
verify(dispatcher, times(0)).send("/ws", null, "hello");
router.send("/ws", "client", (byte[]) null);
// should not be sent.
verify(dispatcher, times(0)).send("/ws", "client", (byte[]) null);
router.send("/ws", null, "hello".getBytes(Charset.defaultCharset()));
// should not be sent.
verify(dispatcher, times(0)).send("/ws", null, "hello".getBytes(Charset.defaultCharset()));
}
@Test
public void testSendJson() {
ObjectMapper mapper = new ObjectMapper();
ObjectNode object = mapper.createObjectNode();
object.put("message", "hello");
ArrayNode array = mapper.createArrayNode();
array.add(object).add(mapper.createObjectNode().put("name", "wisdom"));
WebSocketRouter router = new WebSocketRouter();
final WebSocketDispatcher dispatcher = mock(WebSocketDispatcher.class);
router.dispatchers = new WebSocketDispatcher[]{dispatcher};
router.send("/ws", "client", object);
verify(dispatcher, times(1)).send("/ws", "client", object.toString());
router.send("/ws", "client", array);
verify(dispatcher, times(1)).send("/ws", "client", array.toString());
router.send("/ws", "client", (JsonNode) null);
// should not be sent.
verify(dispatcher, times(1)).send("/ws", "client", NullNode.getInstance().toString());
}
@Test
public void testPublish() {
WebSocketRouter router = new WebSocketRouter();
final WebSocketDispatcher dispatcher = mock(WebSocketDispatcher.class);
router.dispatchers = new WebSocketDispatcher[]{dispatcher};
router.publish("/ws", "hello");
verify(dispatcher, times(1)).publish("/ws", "hello");
router.publish("/ws", "hello".getBytes(Charset.defaultCharset()));
verify(dispatcher, times(1)).publish("/ws", "hello".getBytes(Charset.defaultCharset()));
router.publish("/ws", (String) null);
// should not be sent.
verify(dispatcher, times(0)).publish("/ws", (String) null);
router.publish("/ws", (byte[]) null);
// should not be sent.
verify(dispatcher, times(0)).publish("/ws", (byte[]) null);
}
@Test
public void testPublishJson() {
ObjectMapper mapper = new ObjectMapper();
ObjectNode object = mapper.createObjectNode();
object.put("message", "hello");
ArrayNode array = mapper.createArrayNode();
array.add(object).add(mapper.createObjectNode().put("name", "wisdom"));
WebSocketRouter router = new WebSocketRouter();
final WebSocketDispatcher dispatcher = mock(WebSocketDispatcher.class);
router.dispatchers = new WebSocketDispatcher[]{dispatcher};
router.publish("/ws", object);
verify(dispatcher, times(1)).publish("/ws", object.toString());
router.publish("/ws", array);
verify(dispatcher, times(1)).publish("/ws", array.toString());
router.publish("/ws", (JsonNode) null);
// should not be sent.
verify(dispatcher, times(1)).publish("/ws", NullNode.getInstance().toString());
}
}