Moved tests from integration to unit and extracted and simplified common test part in ProtocolProcessor unit test

This commit is contained in:
andsel
2017-06-29 16:59:56 +02:00
parent 83be327bd3
commit 03c4874a50
8 changed files with 463 additions and 477 deletions
@@ -86,8 +86,6 @@ public class ClientSession {
private final ISubscriptionsStore subscriptionsStore;
private volatile boolean cleanSession;
// private BlockingQueue<AbstractMessage> m_queueToPublish = new
// ArrayBlockingQueue<>(Constants.MAX_MESSAGE_QUEUE);
private final OutboundFlightZone outboundFlightZone;
private final InboundFlightZone inboundFlightZone;
@@ -115,15 +113,15 @@ public class ClientSession {
@Override
public String toString() {
return "ClientSession{clientID='" + clientID + '\'' + "}";
return "ClientSession{clientID='" + clientID + "'}";
}
public boolean subscribe(Subscription newSubscription) {
LOG.info("Adding new subscription. ClientId={}, topics={}, qos={}", newSubscription.getClientId(),
LOG.info("Adding new subscription. CId={}, topics={}, qos={}", newSubscription.getClientId(),
newSubscription.getTopicFilter(), newSubscription.getRequestedQos());
boolean validTopic = newSubscription.getTopicFilter().isValid();
if (!validTopic) {
LOG.warn("The topic filter is not valid. ClientId={}, topics={}", newSubscription.getClientId(),
LOG.warn("The topic filter is not valid. CId={}, topics={}", newSubscription.getClientId(),
newSubscription.getTopicFilter());
// send SUBACK with 0x80 for this topic filter
return false;
@@ -133,7 +131,7 @@ public class ClientSession {
// update the selected subscriptions if not present or if has a greater qos
if (existingSub == null || existingSub.getRequestedQos().value() < newSubscription.getRequestedQos().value()) {
if (existingSub != null) {
LOG.info("Subscription already existed with a lower QoS value. It will be updated. ClientId={}, " +
LOG.info("Subscription already existed with a lower QoS value. It will be updated. CId={}, " +
"topics={}, existingQos={}, newQos={}", newSubscription.getClientId(),
newSubscription.getTopicFilter(), existingSub.getRequestedQos(), newSubscription.getRequestedQos());
subscriptions.remove(newSubscription);
@@ -145,7 +143,7 @@ public class ClientSession {
}
public void unsubscribeFrom(Topic topicFilter) {
LOG.info("Removing subscription. ClientID={}, topics={}", clientID, topicFilter);
LOG.info("Removing subscription. CId={}, topics={}", clientID, topicFilter);
subscriptionsStore.removeSubscription(topicFilter, clientID);
Set<Subscription> subscriptionsToRemove = new HashSet<>();
for (Subscription sub : this.subscriptions) {
@@ -158,7 +156,7 @@ public class ClientSession {
public void disconnect() {
if (this.cleanSession) {
LOG.info("Client disconnected. Removing its subscriptions. ClientId={}", clientID);
LOG.info("Client disconnected. Removing its subscriptions. CId={}", clientID);
// cleanup topic subscriptions
cleanSession();
}
@@ -191,7 +189,7 @@ public class ClientSession {
* @return the packetID for the message in flight.
* */
public int inFlightAckWaiting(IMessagesStore.StoredMessage msg) {
LOG.debug("Adding message ot inflight zone. ClientId={}", clientID);
LOG.debug("Adding message ot inflight zone. CId={}", clientID);
int messageId = ClientSession.this.nextPacketId();
outboundFlightZone.waitingAck(messageId, msg);
return messageId;
@@ -224,6 +222,7 @@ public class ClientSession {
}
public Set<Subscription> getSubscriptions() {
//TODO WARN this doesn'load anything from DB
return subscriptions;
}
@@ -35,18 +35,19 @@ import io.netty.handler.codec.mqtt.*;
import io.netty.handler.timeout.IdleStateHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.ByteBuffer;
import java.util.*;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import static io.moquette.server.ConnectionDescriptor.ConnectionState.*;
import static io.moquette.spi.impl.InternalRepublisher.createPublishForQos;
import static io.moquette.spi.impl.Utils.messageId;
import static io.moquette.spi.impl.Utils.readBytesAndRewind;
import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.*;
import static io.netty.handler.codec.mqtt.MqttMessageIdVariableHeader.from;
import static io.netty.handler.codec.mqtt.MqttQoS.*;
import static io.moquette.server.ConnectionDescriptor.ConnectionState.*;
/**
* Class responsible to handle the logic of MQTT protocol it's the director of the protocol
@@ -653,7 +654,7 @@ public class ProtocolProcessor {
}
if (!existingDescriptor.close()) {
LOG.info("The connection has been closed. CId={}", clientID);
LOG.info("Connection has been closed. CId={}", clientID);
return;
}
@@ -664,7 +665,7 @@ public class ProtocolProcessor {
return;
}
LOG.info("The DISCONNECT message has been processed. CId={}", clientID);
LOG.info("DISCONNECT message has been processed. CId={}", clientID);
}
private boolean removeSubscriptions(ConnectionDescriptor descriptor, String clientID) {
@@ -1,295 +0,0 @@
/*
* Copyright (c) 2012-2017 The original author or authors
* ------------------------------------------------------
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*/
package io.moquette.server;
import io.moquette.server.config.IConfig;
import io.moquette.server.config.MemoryConfig;
import io.netty.buffer.Unpooled;
import io.netty.handler.codec.mqtt.MqttMessageBuilders;
import io.netty.handler.codec.mqtt.MqttPublishMessage;
import io.netty.handler.codec.mqtt.MqttQoS;
import org.eclipse.paho.client.mqttv3.*;
import org.eclipse.paho.client.mqttv3.persist.MqttDefaultFilePersistence;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.util.Properties;
import static org.junit.Assert.*;
public class ServerIntegrationEmbeddedPublishTest {
private static final Logger LOG = LoggerFactory.getLogger(ServerIntegrationEmbeddedPublishTest.class);
static MqttClientPersistence s_dataStore;
static MqttClientPersistence s_pubDataStore;
Server m_server;
IMqttClient m_subscriber;
MessageCollector m_callback;
IConfig m_config;
protected void startServer() throws IOException {
m_server = new Server();
final Properties configProps = IntegrationUtils.prepareTestProperties();
m_config = new MemoryConfig(configProps);
m_server.startServer(m_config);
}
@BeforeClass
public static void beforeTests() {
String tmpDir = System.getProperty("java.io.tmpdir");
s_dataStore = new MqttDefaultFilePersistence(tmpDir);
s_pubDataStore = new MqttDefaultFilePersistence(tmpDir + File.separator + "publisher");
}
@Before
public void setUp() throws Exception {
startServer();
m_subscriber = new MqttClient("tcp://localhost:1883", "TestClient", s_dataStore);
m_callback = new MessageCollector();
m_subscriber.setCallback(m_callback);
}
@After
public void tearDown() throws Exception {
if (m_subscriber != null && m_subscriber.isConnected()) {
m_subscriber.disconnect();
}
m_server.stopServer();
}
private void subscribeToWithQos(String topic, int qos) throws Exception {
m_subscriber.connect();
m_subscriber.subscribe(topic, qos);
}
private void internalPublishToWithQosAndRetained(String topic, MqttQoS qos, boolean retained) {
MqttPublishMessage message = MqttMessageBuilders.publish().topicName(topic).retained(retained).qos(qos)
.payload(Unpooled.copiedBuffer("Hello world MQTT!!".getBytes())).build();
m_server.internalPublish(message, "INTRPUBL");
}
private void connectNoCleanSession() throws Exception {
MqttConnectOptions opts = new MqttConnectOptions();
opts.setCleanSession(false);
m_subscriber.connect(opts);
}
private void subscribeToWithQosAndNoCleanSession(String topic, int qos) throws Exception {
connectNoCleanSession();
m_subscriber.subscribe(topic, qos);
}
private void verifyNoMessageIsReceived() throws Exception {
MqttMessage msg = m_callback.getMessageImmediate();
assertNull(msg);
}
private void verifyMessageIsReceivedSuccessfully() throws Exception {
// check in 2 seconds
MqttMessage msg = m_callback.waitMessage(2);
assertNotNull(msg);
assertEquals("Hello world MQTT!!", new String(msg.getPayload()));
}
@Test
public void testClientSubscribeAfterNotRetainedQoS0IsSent() throws Exception {
LOG.info("*** testClientSubscribeAfterNotRetainedQoS0IsSent ***");
// Exercise
internalPublishToWithQosAndRetained("/topic", MqttQoS.AT_MOST_ONCE, false);
subscribeToWithQos("/topic", 0);
// Verify
verifyNoMessageIsReceived();
}
@Test
public void testClientSubscribeBeforeNotRetainedQoS0IsSent() throws Exception {
LOG.info("*** testClientSubscribeBeforeNotRetainedQoS0 ***");
subscribeToWithQos("/topic", 0);
// Exercise
internalPublishToWithQosAndRetained("/topic", MqttQoS.AT_MOST_ONCE, false);
// Verify
verifyMessageIsReceivedSuccessfully();
}
@Test
public void testClientSubscribeBeforeRetainedQoS0IsSent() throws Exception {
LOG.info("*** testClientSubscribeBeforeRetainedQoS0IsSent ***");
subscribeToWithQos("/topic", 0);
// Exercise
internalPublishToWithQosAndRetained("/topic", MqttQoS.AT_MOST_ONCE, true);
// Verify
verifyMessageIsReceivedSuccessfully();
}
@Test
public void testClientSubscribeAfterRetainedQoS0IsSent() throws Exception {
LOG.info("*** testClientSubscribeAfterRetainedQoS0IsSent ***");
// Exercise
internalPublishToWithQosAndRetained("/topic", MqttQoS.AT_MOST_ONCE, true);
// LOG.info("** post internalPublish **");
subscribeToWithQos("/topic", 0);
// Verify
verifyNoMessageIsReceived();
}
@Test
public void testClientSubscribeBeforeNotRetainedQoS1IsSent() throws Exception {
LOG.info("*** testClientSubscribeBeforeNotRetainedQoS1IsSent ***");
subscribeToWithQos("/topic", 1);
// Exercise
internalPublishToWithQosAndRetained("/topic", MqttQoS.AT_LEAST_ONCE, false);
// Verify
verifyMessageIsReceivedSuccessfully();
}
@Test
public void testClientSubscribeAfterNotRetainedQoS1IsSent() throws Exception {
LOG.info("*** testClientSubscribeAfterNotRetainedQoS1IsSent ***");
// Exercise
internalPublishToWithQosAndRetained("/topic", MqttQoS.AT_LEAST_ONCE, false);
subscribeToWithQos("/topic", 1);
// Verify
verifyNoMessageIsReceived();
}
@Test
public void testClientSubscribeBeforeRetainedQoS1IsSent() throws Exception {
LOG.info("*** testClientSubscribeBeforeRetainedQoS1IsSent ***");
subscribeToWithQos("/topic", 1);
// Exercise
internalPublishToWithQosAndRetained("/topic", MqttQoS.AT_LEAST_ONCE, true);
// Verify
verifyMessageIsReceivedSuccessfully();
}
@Test
public void testClientSubscribeAfterRetainedQoS1IsSent() throws Exception {
LOG.info("*** testClientSubscribeAfterRetainedQoS0IsSent ***");
// Exercise
internalPublishToWithQosAndRetained("/topic", MqttQoS.AT_LEAST_ONCE, true);
subscribeToWithQos("/topic", MqttQoS.AT_LEAST_ONCE.value());
LOG.info("** After subscribe **");
// Verify
verifyMessageIsReceivedSuccessfully();
LOG.info("** Post verify **");
}
@Test
public void testClientSubscribeBeforeNotRetainedQoS2IsSent() throws Exception {
LOG.info("*** testClientSubscribeBeforeNotRetainedQoS2IsSent ***");
subscribeToWithQos("/topic", 2);
// Exercise
internalPublishToWithQosAndRetained("/topic", MqttQoS.EXACTLY_ONCE, false);
// Verify
verifyMessageIsReceivedSuccessfully();
}
@Test
public void testClientSubscribeAfterNotRetainedQoS2IsSent() throws Exception {
LOG.info("*** testClientSubscribeAfterNotRetainedQoS2IsSent ***");
// Exercise
internalPublishToWithQosAndRetained("/topic", MqttQoS.EXACTLY_ONCE, false);
subscribeToWithQos("/topic", 2);
// Verify
verifyNoMessageIsReceived();
}
@Test
public void testClientSubscribeBeforeRetainedQoS2IsSent() throws Exception {
LOG.info("*** testClientSubscribeBeforeRetainedQoS2IsSent ***");
subscribeToWithQos("/topic", 2);
// Exercise
internalPublishToWithQosAndRetained("/topic", MqttQoS.EXACTLY_ONCE, true);
// Verify
verifyMessageIsReceivedSuccessfully();
}
@Test
public void testClientSubscribeAfterRetainedQoS2IsSent() throws Exception {
LOG.info("*** testClientSubscribeAfterRetainedQoS2IsSent ***");
// Exercise
internalPublishToWithQosAndRetained("/topic", MqttQoS.EXACTLY_ONCE, true);
subscribeToWithQos("/topic", 2);
// Verify
verifyMessageIsReceivedSuccessfully();
}
@Test
public void testClientSubscribeAfterDisconnected() throws Exception {
LOG.info("*** testClientSubscribeAfterDisconnected ***");
subscribeToWithQos("foo", 0);
m_subscriber.disconnect();
internalPublishToWithQosAndRetained("foo", MqttQoS.AT_MOST_ONCE, false);
// verifyMessageIsReceivedSuccessfully();
// m_subscriber.receive(2, TimeUnit.MILLISECONDS);
MqttMessage message = m_callback.waitMessage(1);
assertTrue(message == null);
}
@Test
public void testClientSubscribeWithoutCleanSession() throws Exception {
LOG.info("*** testClientSubscribeWithoutCleanSession ***");
subscribeToWithQosAndNoCleanSession("foo", 1);
m_subscriber.disconnect();
assertTrue(m_server.getSubscriptions().size() == 1);
connectNoCleanSession();
assertTrue(m_server.getSubscriptions().size() == 1);
internalPublishToWithQosAndRetained("foo", MqttQoS.AT_MOST_ONCE, false);
verifyMessageIsReceivedSuccessfully();
}
}
@@ -30,7 +30,7 @@ import java.util.Properties;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
public class ServerRestartIntegrationTest {
public class ServerIntegrationRestartTest {
static MqttClientPersistence s_dataStore;
static MqttClientPersistence s_pubDataStore;
@@ -0,0 +1,213 @@
/*
* Copyright (c) 2012-2017 The original author or authors
* ------------------------------------------------------
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*/
package io.moquette.spi.impl;
import io.moquette.interception.InterceptHandler;
import io.moquette.persistence.MemoryStorageService;
import io.moquette.server.netty.NettyUtils;
import io.moquette.spi.IMessagesStore;
import io.moquette.spi.ISessionsStore;
import io.moquette.spi.impl.security.PermitAllAuthorizator;
import io.moquette.spi.impl.subscriptions.Subscription;
import io.moquette.spi.impl.subscriptions.SubscriptionsDirectory;
import io.moquette.spi.impl.subscriptions.Topic;
import io.netty.buffer.Unpooled;
import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.handler.codec.mqtt.*;
import java.util.*;
import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.CONNECTION_ACCEPTED;
import static io.netty.handler.codec.mqtt.MqttQoS.AT_MOST_ONCE;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
abstract class AbstractProtocolProcessorCommonUtils {
static final String FAKE_CLIENT_ID = "FAKE_123";
static final String FAKE_CLIENT_ID2 = "FAKE_456";
static final String TEST_USER = "fakeuser";
static final String TEST_PWD = "fakepwd";
static final List<InterceptHandler> EMPTY_OBSERVERS = Collections.emptyList();
static final BrokerInterceptor NO_OBSERVERS_INTERCEPTOR = new BrokerInterceptor(EMPTY_OBSERVERS);
public static final String HELLO_WORLD_MQTT = "Hello world MQTT!!";
EmbeddedChannel m_channel;
ProtocolProcessor m_processor;
IMessagesStore m_messagesStore;
ISessionsStore m_sessionStore;
SubscriptionsDirectory subscriptions;
MockAuthenticator m_mockAuthenticator;
protected void initializeProcessorAndSubsystems() {
m_channel = new EmbeddedChannel();
NettyUtils.clientID(m_channel, FAKE_CLIENT_ID);
NettyUtils.cleanSession(m_channel, false);
// sleep to let the messaging batch processor to process the initEvent
MemoryStorageService memStorage = new MemoryStorageService(null, null);
m_messagesStore = memStorage.messagesStore();
m_sessionStore = memStorage.sessionsStore();
// m_messagesStore.initStore();
Set<String> clientIds = new HashSet<>();
clientIds.add(FAKE_CLIENT_ID);
clientIds.add(FAKE_CLIENT_ID2);
Map<String, String> users = new HashMap<>();
users.put(TEST_USER, TEST_PWD);
m_mockAuthenticator = new MockAuthenticator(clientIds, users);
subscriptions = new SubscriptionsDirectory();
subscriptions.init(memStorage.sessionsStore());
m_processor = new ProtocolProcessor();
m_processor.init(subscriptions, m_messagesStore, m_sessionStore, m_mockAuthenticator, true,
new PermitAllAuthorizator(), NO_OBSERVERS_INTERCEPTOR);
}
protected void verifyNoPublishIsReceived() {
assertNull("No out messages from the processor", m_channel.readOutbound());
}
protected void subscribe(String topic, MqttQoS desiredQos) {
MqttSubscribeMessage subscribe = MqttMessageBuilders.subscribe()
.addSubscription(desiredQos, topic)
.messageId(1)
.build();
this.m_processor.processSubscribe(m_channel, subscribe);
MqttSubAckMessage subAck = m_channel.readOutbound();
assertEquals(desiredQos.value(), (int) subAck.payload().grantedQoSLevels().get(0));
Subscription expectedSubscription = new Subscription(FAKE_CLIENT_ID, new Topic(topic), desiredQos);
assertTrue(subscriptions.contains(expectedSubscription));
}
protected MqttSubAckMessage subscribeWithoutVerify(String topic, MqttQoS desiredQos) {
MqttSubscribeMessage subscribe = MqttMessageBuilders.subscribe()
.addSubscription(desiredQos, topic)
.messageId(1)
.build();
this.m_processor.processSubscribe(m_channel, subscribe);
return m_channel.readOutbound();
}
protected void subscribeAndNotReadResponse(String topic, MqttQoS desiredQos) {
MqttSubscribeMessage subscribe = MqttMessageBuilders.subscribe()
.addSubscription(desiredQos, topic)
.messageId(1)
.build();
this.m_processor.processSubscribe(m_channel, subscribe);
}
protected void unsubscribe(String topic) {
MqttUnsubscribeMessage msg = MqttMessageBuilders.unsubscribe()
.addTopicFilter(topic)
.messageId(1)
.build();
m_processor.processUnsubscribe(m_channel, msg);
}
protected void internalPublishNotRetainedTo(String topic) {
internalPublishTo(topic, AT_MOST_ONCE, false);
}
protected void internalPublishRetainedTo(String topic) {
internalPublishTo(topic, AT_MOST_ONCE, true);
}
protected void internalPublishTo(String topic, MqttQoS qos, boolean retained) {
MqttPublishMessage publish = MqttMessageBuilders.publish()
.topicName(topic)
.retained(retained)
.qos(qos)
.payload(Unpooled.copiedBuffer(HELLO_WORLD_MQTT.getBytes())).build();
this.m_processor.internalPublish(publish, "INTRPUBL");
}
protected void publishToAs(String clientId, String topic, MqttQoS qos, boolean retained) {
NettyUtils.userName(m_channel, clientId);
MqttPublishMessage publish = MqttMessageBuilders.publish()
.topicName(topic)
.retained(retained)
.qos(qos)
.payload(Unpooled.copiedBuffer(HELLO_WORLD_MQTT.getBytes())).build();
this.m_processor.processPublish(m_channel, publish);
}
protected void publishToAs(String clientId, String topic, MqttQoS qos, int messageId, boolean retained) {
NettyUtils.userName(m_channel, clientId);
MqttPublishMessage publish = MqttMessageBuilders.publish()
.topicName(topic)
.retained(retained)
.messageId(messageId)
.qos(qos)
.payload(Unpooled.copiedBuffer(HELLO_WORLD_MQTT.getBytes())).build();
this.m_processor.processPublish(m_channel, publish);
}
protected void connect() {
connectAsClient(FAKE_CLIENT_ID);
}
protected void connectAsClient(String clientId) {
MqttConnectMessage connectMessage = MqttMessageBuilders.connect()
.clientId(clientId)
.build();
this.m_processor.processConnect(m_channel, connectMessage);
MqttConnAckMessage connAck = m_channel.readOutbound();
assertEquals("Connect must be accepted", CONNECTION_ACCEPTED, connAck.variableHeader().connectReturnCode());
}
protected void connect_v3_1() {
connect_v3_1_asClient(FAKE_CLIENT_ID);
}
protected void connect_v3_1_asClient(String clientID) {
MqttConnectMessage connectMessage = MqttMessageBuilders.connect()
.clientId(clientID)
.cleanSession(true)
.protocolVersion(MqttVersion.MQTT_3_1)
.build();
this.m_processor.processConnect(m_channel, connectMessage);
MqttConnAckMessage connAck = m_channel.readOutbound();
assertEquals("Connect must be accepted", CONNECTION_ACCEPTED, connAck.variableHeader().connectReturnCode());
}
protected void connectNoCleanSession() {
MqttConnectMessage connectMessage = MqttMessageBuilders.connect()
.clientId(FAKE_CLIENT_ID)
.cleanSession(false)
.build();
this.m_processor.processConnect(m_channel, connectMessage);
MqttConnAckMessage connAck = m_channel.readOutbound();
assertEquals("Connect must be accepted", CONNECTION_ACCEPTED, connAck.variableHeader().connectReturnCode());
}
protected void disconnect() throws InterruptedException {
this.m_processor.processDisconnect(this.m_channel);
}
protected void verifyPublishIsReceived() {
final MqttPublishMessage publishReceived = m_channel.readOutbound();
String payloadMessage = new String(publishReceived.payload().array());
assertEquals("Sent and received payload must be identical", HELLO_WORLD_MQTT, payloadMessage);
}
}
@@ -21,7 +21,6 @@ import io.moquette.persistence.MemoryStorageService;
import io.moquette.server.netty.NettyUtils;
import io.moquette.spi.IMessagesStore;
import io.moquette.spi.IMessagesStore.StoredMessage;
import io.moquette.spi.ISessionsStore;
import io.moquette.spi.impl.security.PermitAllAuthorizator;
import io.moquette.spi.impl.subscriptions.Subscription;
import io.moquette.spi.impl.subscriptions.SubscriptionsDirectory;
@@ -32,14 +31,17 @@ import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.handler.codec.mqtt.*;
import org.junit.Before;
import org.junit.Test;
import java.util.*;
import java.util.concurrent.BlockingQueue;
import static io.moquette.spi.impl.NettyChannelAssertions.assertConnAckAccepted;
import static io.moquette.spi.impl.ProtocolProcessor.lowerQosToTheSubscriptionDesired;
import static io.netty.handler.codec.mqtt.MqttQoS.AT_LEAST_ONCE;
import static io.netty.handler.codec.mqtt.MqttQoS.AT_MOST_ONCE;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
public class ProtocolProcessorTest {
public class ProtocolProcessorTest extends AbstractProtocolProcessorCommonUtils {
static final String FAKE_CLIENT_ID = "FAKE_123";
static final String FAKE_CLIENT_ID2 = "FAKE_456";
@@ -55,52 +57,15 @@ public class ProtocolProcessorTest {
static final List<InterceptHandler> EMPTY_OBSERVERS = Collections.emptyList();
static final BrokerInterceptor NO_OBSERVERS_INTERCEPTOR = new BrokerInterceptor(EMPTY_OBSERVERS);
EmbeddedChannel m_channel;
MqttConnectMessage connMsg;
ProtocolProcessor m_processor;
IMessagesStore m_messagesStore;
ISessionsStore m_sessionStore;
SubscriptionsDirectory subscriptions;
MockAuthenticator m_mockAuthenticator;
@Before
public void setUp() throws InterruptedException {
/*
* connMsg = new ConnectMessage(); connMsg.setProtocolVersion((byte) 0x03);
*
* connMsg = MqttMessageBuilders.connect() .protocolVersion(MqttVersion.MQTT_3_1)
* .clientId(FAKE_CLIENT_ID) .cleanSession(true) .build();
*/
m_channel = new EmbeddedChannel();
NettyUtils.clientID(m_channel, FAKE_CLIENT_ID);
NettyUtils.cleanSession(m_channel, false);
// sleep to let the messaging batch processor to process the initEvent
Thread.sleep(300);
MemoryStorageService memStorage = new MemoryStorageService(null, null);
m_messagesStore = memStorage.messagesStore();
m_sessionStore = memStorage.sessionsStore();
// m_messagesStore.initStore();
Set<String> clientIds = new HashSet<>();
clientIds.add(FAKE_CLIENT_ID);
clientIds.add(FAKE_CLIENT_ID2);
Map<String, String> users = new HashMap<>();
users.put(TEST_USER, TEST_PWD);
m_mockAuthenticator = new MockAuthenticator(clientIds, users);
subscriptions = new SubscriptionsDirectory();
subscriptions.init(memStorage.sessionsStore());
m_processor = new ProtocolProcessor();
m_processor.init(subscriptions, m_messagesStore, m_sessionStore, m_mockAuthenticator, true,
new PermitAllAuthorizator(), NO_OBSERVERS_INTERCEPTOR);
initializeProcessorAndSubsystems();
}
@Test
public void testPublishToItself() throws InterruptedException {
final Subscription subscription = new Subscription(FAKE_CLIENT_ID, new Topic(FAKE_TOPIC), MqttQoS.AT_MOST_ONCE);
final Subscription subscription = new Subscription(FAKE_CLIENT_ID, new Topic(FAKE_TOPIC), AT_MOST_ONCE);
// subscriptions.matches(topic) redefine the method to return true
SubscriptionsDirectory subs = new SubscriptionsDirectory() {
@@ -118,37 +83,25 @@ public class ProtocolProcessorTest {
// simulate a connect that register a clientID to an IoSession
MemoryStorageService storageService = new MemoryStorageService(null, null);
subs.init(storageService.sessionsStore());
m_processor.init(
subs,
m_messagesStore,
m_sessionStore,
null,
true,
new PermitAllAuthorizator(),
m_processor.init(subs, m_messagesStore, m_sessionStore, null, true, new PermitAllAuthorizator(),
NO_OBSERVERS_INTERCEPTOR);
MqttConnectMessage connectMessage = MqttMessageBuilders.connect().protocolVersion(MqttVersion.MQTT_3_1)
.clientId(FAKE_CLIENT_ID).cleanSession(true).build();
m_processor.processConnect(m_channel, connectMessage);
connect_v3_1();
// Exercise
MqttPublishMessage msg = MqttMessageBuilders.publish().topicName(FAKE_TOPIC).qos(MqttQoS.AT_MOST_ONCE)
.retained(false).payload(Unpooled.copiedBuffer("Hello".getBytes())).build();
NettyUtils.userName(m_channel, "FakeCLI");
m_processor.processPublish(m_channel, msg);
publishToAs("FakeCLI", FAKE_TOPIC, AT_MOST_ONCE, false);
// Verify
assertNotNull(m_channel.readOutbound());
// TODO check received message attributes
verifyPublishIsReceived();
}
@Test
public void testPublishToMultipleSubscribers() throws InterruptedException {
final Subscription subscription = new Subscription(FAKE_CLIENT_ID, new Topic(FAKE_TOPIC), MqttQoS.AT_MOST_ONCE);
final Subscription subscription = new Subscription(FAKE_CLIENT_ID, new Topic(FAKE_TOPIC), AT_MOST_ONCE);
final Subscription subscriptionClient2 = new Subscription(
FAKE_CLIENT_ID2,
new Topic(FAKE_TOPIC),
MqttQoS.AT_MOST_ONCE);
AT_MOST_ONCE);
// subscriptions.matches(topic) redefine the method to return true
SubscriptionsDirectory subs = new SubscriptionsDirectory() {
@@ -183,7 +136,7 @@ public class ProtocolProcessorTest {
assertConnAckAccepted(secondReceiverChannel);
// Exercise
MqttPublishMessage msg = MqttMessageBuilders.publish().topicName(FAKE_TOPIC).qos(MqttQoS.AT_MOST_ONCE)
MqttPublishMessage msg = MqttMessageBuilders.publish().topicName(FAKE_TOPIC).qos(AT_MOST_ONCE)
.retained(false).payload(Unpooled.copiedBuffer("Hello".getBytes())).build();
NettyUtils.userName(m_channel, "FakeCLI");
m_processor.processPublish(m_channel, msg);
@@ -204,17 +157,10 @@ public class ProtocolProcessorTest {
@Test
public void testSubscribe() {
MqttSubscribeMessage msg = MqttMessageBuilders.subscribe().addSubscription(MqttQoS.AT_MOST_ONCE, FAKE_TOPIC)
.messageId(10).build();
// Exercise
m_sessionStore.createNewSession(FAKE_CLIENT_ID, false);
m_processor.processSubscribe(m_channel, msg);
connect();
// Verify
assertTrue(m_channel.readOutbound() instanceof MqttSubAckMessage);
Subscription expectedSubscription = new Subscription(FAKE_CLIENT_ID, new Topic(FAKE_TOPIC),
MqttQoS.AT_MOST_ONCE);
assertTrue(subscriptions.contains(expectedSubscription));
// Exercise & verify
subscribe(FAKE_TOPIC, AT_MOST_ONCE);
}
@Test
@@ -229,17 +175,11 @@ public class ProtocolProcessorTest {
m_processor.init(subscriptions, m_messagesStore, m_sessionStore, m_mockAuthenticator, true, mockAuthorizator,
NO_OBSERVERS_INTERCEPTOR);
// Exercise
MqttSubscribeMessage msg = MqttMessageBuilders.subscribe().addSubscription(MqttQoS.AT_MOST_ONCE, FAKE_TOPIC)
.messageId(10).build();
connect();
m_sessionStore.createNewSession(FAKE_CLIENT_ID, false);
m_processor.processSubscribe(m_channel, msg);
//Exercise
MqttSubAckMessage subAckMsg = subscribeWithoutVerify(FAKE_TOPIC, AT_MOST_ONCE);
// Verify
Object ackMsg = m_channel.readOutbound();
assertTrue(ackMsg instanceof MqttSubAckMessage);
MqttSubAckMessage subAckMsg = (MqttSubAckMessage) ackMsg;
verifyFailureQos(subAckMsg);
}
@@ -251,63 +191,41 @@ public class ProtocolProcessorTest {
@Test
public void testDoubleSubscribe() {
MqttSubscribeMessage msg = MqttMessageBuilders.subscribe().addSubscription(MqttQoS.AT_MOST_ONCE, FAKE_TOPIC)
.messageId(10).build();
m_sessionStore.createNewSession(FAKE_CLIENT_ID, false);
connect();
assertEquals(0, subscriptions.size());
m_processor.processSubscribe(m_channel, msg);
subscribe(FAKE_TOPIC, AT_MOST_ONCE);
assertEquals(1, subscriptions.size());
//Exercise
m_processor.processSubscribe(m_channel, msg);
// Verify
assertEquals(1, subscriptions.size());
Subscription expectedSubscription = new Subscription(FAKE_CLIENT_ID, new Topic(FAKE_TOPIC),
MqttQoS.AT_MOST_ONCE);
assertTrue(subscriptions.contains(expectedSubscription));
//Exercise & verify
subscribe(FAKE_TOPIC, AT_MOST_ONCE);
}
@Test
public void testSubscribeWithBadFormattedTopic() {
MqttSubscribeMessage msg = MqttMessageBuilders.subscribe().addSubscription(MqttQoS.AT_MOST_ONCE, BAD_FORMATTED_TOPIC)
.messageId(10).build();
m_sessionStore.createNewSession(FAKE_CLIENT_ID, false);
connect();
assertEquals(0, subscriptions.size());
// Exercise
m_processor.processSubscribe(m_channel, msg);
//Exercise
MqttSubAckMessage subAckMsg = subscribeWithoutVerify(BAD_FORMATTED_TOPIC, AT_MOST_ONCE);
// Verify
assertEquals(0, subscriptions.size());
Object recvSubAckMessage = m_channel.readOutbound();
assertTrue(recvSubAckMessage instanceof MqttSubAckMessage);
verifyFailureQos((MqttSubAckMessage) recvSubAckMessage);
verifyFailureQos(subAckMsg);
}
/*
* Check topicFilter is a valid MQTT topic filter (issue 68)
*/
@Test
public void testUnsubscribeWithBadFormattedTopic() {
MqttUnsubscribeMessage msg = MqttMessageBuilders.unsubscribe().addTopicFilter(BAD_FORMATTED_TOPIC).messageId(1)
.build();
// Exercise
m_processor.processUnsubscribe(m_channel, msg);
unsubscribe(BAD_FORMATTED_TOPIC);
// Verify
assertFalse("If client unsubscribe with bad topic than channel must be closed", m_channel.isOpen());
assertFalse("If client unsubscribe with bad topic than channel must be closed, (issue 68)", m_channel.isOpen());
}
@Test
public void testPublishOfRetainedMessage_afterNewSubscription() throws Exception {
// simulate a connect that register a clientID to an IoSession
final Subscription subscription =
new Subscription(FAKE_PUBLISHER_ID, new Topic(FAKE_TOPIC), MqttQoS.AT_MOST_ONCE);
new Subscription(FAKE_PUBLISHER_ID, new Topic(FAKE_TOPIC), AT_MOST_ONCE);
// subscriptions.matches(topic) redefine the method to return true
SubscriptionsDirectory subs = new SubscriptionsDirectory() {
@@ -327,29 +245,15 @@ public class ProtocolProcessorTest {
// simulate a connect that register a clientID to an IoSession
m_processor.init(subs, m_messagesStore, m_sessionStore, null, true, new PermitAllAuthorizator(),
NO_OBSERVERS_INTERCEPTOR);
MqttConnectMessage connectMessage = MqttMessageBuilders.connect().clientId(FAKE_PUBLISHER_ID)
.protocolVersion(MqttVersion.MQTT_3_1).cleanSession(true).build();
m_processor.processConnect(m_channel, connectMessage);
assertConnAckAccepted(m_channel);
MqttPublishMessage pubmsg = MqttMessageBuilders.publish().topicName(FAKE_TOPIC).qos(MqttQoS.AT_MOST_ONCE)
.payload(Unpooled.copiedBuffer("Hello".getBytes())).retained(true).build();
NettyUtils.clientID(m_channel, FAKE_PUBLISHER_ID);
m_processor.processPublish(m_channel, pubmsg);
connect_v3_1_asClient(FAKE_PUBLISHER_ID);
publishToAs(FAKE_PUBLISHER_ID, FAKE_TOPIC, AT_MOST_ONCE, true);
NettyUtils.cleanSession(m_channel, false);
// Exercise
MqttSubscribeMessage msg = MqttMessageBuilders.subscribe().messageId(10).addSubscription(MqttQoS.AT_MOST_ONCE, "#")
.build();
m_processor.processSubscribe(m_channel, msg);
subscribeAndNotReadResponse("#", AT_MOST_ONCE);
// Verify
// wait the latch
Object pubMessage = m_channel.readOutbound();
assertNotNull(pubMessage);
assertTrue(pubMessage instanceof MqttPublishMessage);
assertEquals(FAKE_TOPIC, ((MqttPublishMessage) pubMessage).variableHeader().topicName());
verifyPublishIsReceived();
}
@Test
@@ -366,10 +270,7 @@ public class ProtocolProcessorTest {
m_processor.init(subs, m_messagesStore, m_sessionStore, null, true, new PermitAllAuthorizator(),
NO_OBSERVERS_INTERCEPTOR);
MqttConnectMessage connectMessage = MqttMessageBuilders.connect().clientId(FAKE_PUBLISHER_ID)
.protocolVersion(MqttVersion.MQTT_3_1).build();
m_processor.processConnect(m_channel, connectMessage);
connect_v3_1_asClient(FAKE_PUBLISHER_ID);
// Verify no messages are still stored
Queue<StoredMessage> messages = m_sessionStore.queue(FAKE_PUBLISHER_ID);
@@ -390,14 +291,10 @@ public class ProtocolProcessorTest {
NO_OBSERVERS_INTERCEPTOR);
// Exercise
MqttPublishMessage msg = MqttMessageBuilders.publish().topicName("/topic").qos(MqttQoS.AT_MOST_ONCE)
.payload(Unpooled.copiedBuffer("Hello".getBytes())).retained(true).build();
connectAsClient("Publisher");
publishToAs("Publisher", "/topic", AT_MOST_ONCE, true);
NettyUtils.clientID(m_channel, "Publisher");
m_processor.processPublish(m_channel, msg);
// Verify no message is received
assertNull(m_channel.readOutbound());
verifyNoPublishIsReceived();
}
/**
@@ -407,24 +304,18 @@ public class ProtocolProcessorTest {
@Test
public void testCleanRetainedStoreAfterAQoS0AndRetainedTrue() {
// force a connect
connMsg = MqttMessageBuilders.connect().protocolVersion(MqttVersion.MQTT_3_1).clientId("Publisher")
.cleanSession(true).build();
m_processor.processConnect(m_channel, connMsg);
connect_v3_1_asClient("Publisher");
// prepare and existing retained store
NettyUtils.clientID(m_channel, "Publisher");
MqttPublishMessage msg = MqttMessageBuilders.publish().topicName(FAKE_TOPIC).qos(MqttQoS.AT_LEAST_ONCE)
.payload(Unpooled.copiedBuffer("Hello".getBytes())).retained(true).messageId(100).build();
m_processor.processPublish(m_channel, msg);
publishToAs("Publisher", FAKE_TOPIC, AT_LEAST_ONCE, 100, true);
Collection<IMessagesStore.StoredMessage> messages = m_messagesStore
.searchMatching(key -> key.match(new Topic(FAKE_TOPIC)));
assertFalse(messages.isEmpty());
// Exercise
MqttPublishMessage cleanPubMsg = MqttMessageBuilders.publish().topicName(FAKE_TOPIC).qos(MqttQoS.AT_MOST_ONCE)
.payload(Unpooled.copiedBuffer("Hello".getBytes())).retained(true).build();
m_processor.processPublish(m_channel, cleanPubMsg);
// send a message that clean the previous retained publish
publishToAs("Publisher", FAKE_TOPIC, AT_MOST_ONCE, true);
// Verify
messages = m_messagesStore.searchMatching(key -> key.match(new Topic(FAKE_TOPIC)));
@@ -58,8 +58,6 @@ public class ProtocolProcessor_CONNECT_Test {
m_session = new EmbeddedChannel();
// sleep to let the messaging batch processor to process the initEvent
Thread.sleep(300);
MemoryStorageService memStorage = new MemoryStorageService(null, null);
m_messagesStore = memStorage.messagesStore();
m_sessionStore = memStorage.sessionsStore();
@@ -84,18 +82,6 @@ public class ProtocolProcessor_CONNECT_Test {
ProtocolProcessorTest.NO_OBSERVERS_INTERCEPTOR);
}
// @Test
// public void testHandleConnect_BadProtocol() {
// connMsg.setProtocolVersion((byte) 0x02);
//
// //Exercise
// m_processor.processConnect(m_session, connMsg);
//
// //Verify
// assertEqualsConnAck(UNNACEPTABLE_PROTOCOL_VERSION, m_session.readOutbound());
// assertFalse("Connection should be closed by the broker.", m_session.isOpen());
// }
@Test
public void testConnect_badClientID() {
connMsg.clientId("extremely_long_clientID_greater_than_23").build();
@@ -0,0 +1,191 @@
/*
* Copyright (c) 2012-2017 The original author or authors
* ------------------------------------------------------
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*/
package io.moquette.spi.impl;
import io.moquette.server.netty.NettyUtils;
import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.handler.codec.mqtt.MqttQoS;
import org.junit.Before;
import org.junit.Test;
import static io.netty.handler.codec.mqtt.MqttQoS.*;
import static org.junit.Assert.assertEquals;
public class ProtocolProcessor_InternalPublish_Test extends AbstractProtocolProcessorCommonUtils {
@Before
public void setUp() throws InterruptedException {
initializeProcessorAndSubsystems();
connect();
}
@Test
public void testClientSubscribeAfterNotRetainedQoS0IsSent() {
// Exercise
final String topic = "/topic";
internalPublishNotRetainedTo(topic);
subscribe(topic, AT_MOST_ONCE);
// Verify
verifyNoPublishIsReceived();
}
@Test
public void testClientSubscribeAfterRetainedQoS0IsSent() {
// Exercise
final String topic = "/topic";
internalPublishRetainedTo(topic);
subscribe(topic, AT_MOST_ONCE);
// Verify
verifyNoPublishIsReceived();
}
@Test
public void testClientSubscribeBeforeNotRetainedQoS0IsSent() {
subscribe("/topic", AT_MOST_ONCE);
// Exercise
internalPublishNotRetainedTo("/topic");
// Verify
//verifyMessageIsReceivedSuccessfully();
verifyPublishIsReceived();
}
@Test
public void testClientSubscribeBeforeRetainedQoS0IsSent() {
subscribe("/topic", AT_MOST_ONCE);
// Exercise
internalPublishRetainedTo("/topic");
// Verify
verifyPublishIsReceived();
}
@Test
public void testClientSubscribeBeforeNotRetainedQoS1IsSent() {
subscribe("/topic", AT_LEAST_ONCE);
// Exercise
internalPublishTo("/topic", AT_LEAST_ONCE, false);
// Verify
verifyPublishIsReceived();
}
@Test
public void testClientSubscribeAfterNotRetainedQoS1IsSent() {
// Exercise
internalPublishTo("/topic", AT_LEAST_ONCE, false);
subscribe("/topic", AT_LEAST_ONCE);
// Verify
verifyNoPublishIsReceived();
}
@Test
public void testClientSubscribeBeforeRetainedQoS1IsSent() {
subscribe("/topic", AT_LEAST_ONCE);
// Exercise
internalPublishTo("/topic", AT_LEAST_ONCE, true);
// Verify
verifyPublishIsReceived();
}
@Test
public void testClientSubscribeAfterRetainedQoS1IsSent() {
// Exercise
internalPublishTo("/topic", AT_LEAST_ONCE, true);
subscribe("/topic", AT_LEAST_ONCE);
// Verify
verifyPublishIsReceived();
}
@Test
public void testClientSubscribeBeforeNotRetainedQoS2IsSent() {
subscribe("/topic", EXACTLY_ONCE);
// Exercise
internalPublishTo("/topic", EXACTLY_ONCE, false);
// Verify
verifyPublishIsReceived();
}
@Test
public void testClientSubscribeAfterNotRetainedQoS2IsSent() {
// Exercise
internalPublishTo("/topic", EXACTLY_ONCE, false);
subscribe("/topic", EXACTLY_ONCE);
// Verify
verifyNoPublishIsReceived();
}
@Test
public void testClientSubscribeBeforeRetainedQoS2IsSent() {
subscribe("/topic", EXACTLY_ONCE);
// Exercise
internalPublishTo("/topic", EXACTLY_ONCE, true);
// Verify
verifyPublishIsReceived();
}
@Test
public void testClientSubscribeAfterRetainedQoS2IsSent() {
// Exercise
internalPublishTo("/topic", EXACTLY_ONCE, true);
subscribe("/topic", EXACTLY_ONCE);
// Verify
verifyPublishIsReceived();
}
@Test
public void testClientSubscribeAfterDisconnected() throws InterruptedException {
subscribe("foo", AT_MOST_ONCE);
disconnect();
internalPublishTo("foo", AT_MOST_ONCE, false);
verifyNoPublishIsReceived();
}
@Test
public void testClientSubscribeWithoutCleanSession() throws Exception {
subscribe("foo", AT_MOST_ONCE);
disconnect();
assertEquals(1, m_processor.getSessionsStore().subscriptionStore().getSubscriptions().size());
m_channel = new EmbeddedChannel();
NettyUtils.clientID(m_channel, FAKE_CLIENT_ID);
connectNoCleanSession();
assertEquals(1, m_processor.getSessionsStore().subscriptionStore().getSubscriptions().size());
internalPublishTo("foo", MqttQoS.AT_MOST_ONCE, false);
verifyPublishIsReceived();
}
}