Extracted common subscriptions directory interface and switched to CTrie inspired implementation

This commit is contained in:
andsel
2017-07-29 17:10:01 +02:00
parent c81fab62e6
commit d7023ae81b
20 changed files with 1129 additions and 98 deletions
@@ -17,9 +17,7 @@
package io.moquette.spi;
import io.moquette.persistence.MemoryStorageService;
import io.moquette.spi.impl.subscriptions.Subscription;
import io.moquette.spi.impl.subscriptions.SubscriptionsDirectory;
import io.moquette.spi.impl.subscriptions.Topic;
import io.moquette.spi.impl.subscriptions.*;
import org.junit.Before;
import org.junit.Test;
@@ -34,11 +32,11 @@ public class ClientSessionTest {
ClientSession session1;
ClientSession session2;
ISessionsStore sessionsStore;
SubscriptionsDirectory store;
ISubscriptionsDirectory store;
@Before
public void setUp() {
store = new SubscriptionsDirectory();
store = new CTrieSubscriptionDirectory();
MemoryStorageService storageService = new MemoryStorageService(null, null);
this.sessionsStore = storageService.sessionsStore();
store.init(sessionsStore);
@@ -22,9 +22,7 @@ import io.moquette.spi.IMessagesStore;
import io.moquette.spi.ISessionsStore;
import io.moquette.spi.ISubscriptionsStore.ClientTopicCouple;
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.moquette.spi.impl.subscriptions.*;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.embedded.EmbeddedChannel;
@@ -56,7 +54,7 @@ abstract class AbstractProtocolProcessorCommonUtils {
IMessagesStore m_messagesStore;
ISessionsStore m_sessionStore;
SubscriptionsDirectory subscriptions;
ISubscriptionsDirectory subscriptions;
MockAuthenticator m_mockAuthenticator;
protected void initializeProcessorAndSubsystems() {
@@ -77,7 +75,7 @@ abstract class AbstractProtocolProcessorCommonUtils {
users.put(TEST_USER, TEST_PWD);
m_mockAuthenticator = new MockAuthenticator(clientIds, users);
subscriptions = new SubscriptionsDirectory();
subscriptions = new CTrieSubscriptionDirectory();
subscriptions.init(memStorage.sessionsStore());
m_processor = new ProtocolProcessor();
m_processor.init(subscriptions, m_messagesStore, m_sessionStore, m_mockAuthenticator, true,
@@ -22,6 +22,7 @@ import io.moquette.server.netty.NettyUtils;
import io.moquette.spi.IMessagesStore;
import io.moquette.spi.IMessagesStore.StoredMessage;
import io.moquette.spi.impl.security.PermitAllAuthorizator;
import io.moquette.spi.impl.subscriptions.ISubscriptionsDirectory;
import io.moquette.spi.impl.subscriptions.Subscription;
import io.moquette.spi.impl.subscriptions.SubscriptionsDirectory;
import io.moquette.spi.impl.subscriptions.Topic;
@@ -68,7 +69,7 @@ public class ProtocolProcessorTest extends AbstractProtocolProcessorCommonUtils
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() {
ISubscriptionsDirectory subs = new SubscriptionsDirectory() {
@Override
public List<Subscription> matches(Topic topic) {
@@ -104,7 +105,7 @@ public class ProtocolProcessorTest extends AbstractProtocolProcessorCommonUtils
AT_MOST_ONCE);
// subscriptions.matches(topic) redefine the method to return true
SubscriptionsDirectory subs = new SubscriptionsDirectory() {
ISubscriptionsDirectory subs = new SubscriptionsDirectory() {
@Override
public List<Subscription> matches(Topic topic) {
@@ -228,7 +229,7 @@ public class ProtocolProcessorTest extends AbstractProtocolProcessorCommonUtils
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() {
ISubscriptionsDirectory subs = new SubscriptionsDirectory() {
@Override
public List<Subscription> matches(Topic topic) {
@@ -258,7 +259,7 @@ public class ProtocolProcessorTest extends AbstractProtocolProcessorCommonUtils
@Test
public void testRepublishAndConsumePersistedMessages_onReconnect() {
SubscriptionsDirectory subs = mock(SubscriptionsDirectory.class);
ISubscriptionsDirectory subs = mock(SubscriptionsDirectory.class);
List<Subscription> emptySubs = Collections.emptyList();
when(subs.matches(any(Topic.class))).thenReturn(emptySubs);
@@ -282,7 +283,7 @@ public class ProtocolProcessorTest extends AbstractProtocolProcessorCommonUtils
// create an inactive session for Subscriber
m_sessionStore.createNewSession("Subscriber", false);
SubscriptionsDirectory mockedSubscriptions = mock(SubscriptionsDirectory.class);
ISubscriptionsDirectory mockedSubscriptions = mock(SubscriptionsDirectory.class);
Subscription inactiveSub = new Subscription("Subscriber", new Topic("/topic"), MqttQoS.AT_LEAST_ONCE);
List<Subscription> inactiveSubscriptions = Collections.singletonList(inactiveSub);
when(mockedSubscriptions.matches(eq(new Topic("/topic")))).thenReturn(inactiveSubscriptions);
@@ -22,9 +22,7 @@ import io.moquette.spi.ClientSession;
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.moquette.spi.impl.subscriptions.*;
import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.handler.codec.mqtt.*;
import org.junit.Before;
@@ -47,7 +45,7 @@ public class ProtocolProcessor_CONNECT_Test {
IMessagesStore m_messagesStore;
ISessionsStore m_sessionStore;
SubscriptionsDirectory subscriptions;
ISubscriptionsDirectory subscriptions;
MockAuthenticator m_mockAuthenticator;
@Before
@@ -63,7 +61,7 @@ public class ProtocolProcessor_CONNECT_Test {
m_mockAuthenticator = new MockAuthenticator(singleton(FAKE_CLIENT_ID), singletonMap(TEST_USER, TEST_PWD));
subscriptions = new SubscriptionsDirectory();
subscriptions = new CTrieSubscriptionDirectory();
subscriptions.init(m_sessionStore);
m_processor = new ProtocolProcessor();
m_processor.init(subscriptions, m_messagesStore, m_sessionStore, m_mockAuthenticator, true,
@@ -0,0 +1,285 @@
/*
* 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.subscriptions;
import io.moquette.persistence.MemoryStorageService;
import io.moquette.spi.ClientSession;
import io.moquette.spi.ISessionsStore;
import io.moquette.spi.ISubscriptionsStore;
import io.moquette.spi.ISubscriptionsStore.ClientTopicCouple;
import io.netty.handler.codec.mqtt.MqttQoS;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import static io.moquette.spi.impl.subscriptions.CTrieSubscriptionDirectoryTest.clientSubOnTopic;
import static io.moquette.spi.impl.subscriptions.Topic.asTopic;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class CTrieSubscriptionDirectoryMatchingTest {
private CTrieSubscriptionDirectory sut;
private ISessionsStore sessionsStore;
private ISubscriptionsStore subscriptionsStore;
private MemoryStorageService storageService;
@Before
public void setUp() {
sut = new CTrieSubscriptionDirectory();
this.storageService = new MemoryStorageService(null, null);
this.sessionsStore = storageService.sessionsStore();
sut.init(sessionsStore);
this.subscriptionsStore = this.sessionsStore.subscriptionStore();
}
@Test
public void testMatchSimple() {
ClientTopicCouple slashSub = clientSubOnTopic("TempSensor1", "/");
sut.add(slashSub);
assertThat(sut.recursiveMatch(asTopic("finance"), sut.root)).isEmpty();
ClientTopicCouple slashFinanceSub = clientSubOnTopic("TempSensor1", "/finance");
sut.add(slashFinanceSub);
assertThat(sut.recursiveMatch(asTopic("finance"), sut.root)).isEmpty();
assertThat(sut.recursiveMatch(asTopic("/finance"), sut.root)).contains(slashFinanceSub);
assertThat(sut.recursiveMatch(asTopic("/"), sut.root)).contains(slashSub);
}
@Test
public void testMatchSimpleMulti() {
ClientTopicCouple anySub = clientSubOnTopic("TempSensor1", "#");
sut.add(anySub);
assertThat(sut.recursiveMatch(asTopic("finance"), sut.root)).contains(anySub);
ClientTopicCouple financeAnySub = clientSubOnTopic("TempSensor1", "finance/#");
sut.add(financeAnySub);
assertThat(sut.recursiveMatch(asTopic("finance"), sut.root)).containsExactlyInAnyOrder(financeAnySub, anySub);
}
@Test
public void testMatchingDeepMulti_one_layer() {
ClientTopicCouple anySub = clientSubOnTopic("AllSensor1", "#");
ClientTopicCouple financeAnySub = clientSubOnTopic("FinanceSensor", "finance/#");
sut.add(anySub);
sut.add(financeAnySub);
// Verify
assertThat(sut.recursiveMatch(asTopic("finance/stock"), sut.root)).containsExactlyInAnyOrder(financeAnySub, anySub);
assertThat(sut.recursiveMatch(asTopic("finance/stock/ibm"), sut.root)).containsExactlyInAnyOrder(financeAnySub, anySub);
System.out.println(sut.dumpTree());
}
@Test
public void testMatchingDeepMulti_two_layer() {
ClientTopicCouple financeAnySub = clientSubOnTopic("FinanceSensor", "finance/stock/#");
sut.add(financeAnySub);
// Verify
assertThat(sut.recursiveMatch(asTopic("finance/stock/ibm"), sut.root)).containsExactly(financeAnySub);
}
@Test
public void testMatchSimpleSingle() {
ClientTopicCouple anySub = clientSubOnTopic("AnySensor", "+");
sut.add(anySub);
assertThat(sut.recursiveMatch(asTopic("finance"), sut.root)).containsExactly(anySub);
ClientTopicCouple financeOne = clientSubOnTopic("AnySensor", "finance/+");
sut.add(financeOne);
assertThat(sut.recursiveMatch(asTopic("finance/stock"), sut.root)).containsExactly(financeOne);
}
@Test
public void testMatchManySingle() {
ClientTopicCouple manySub = clientSubOnTopic("AnySensor", "+/+");
sut.add(manySub);
// verify
assertThat(sut.recursiveMatch(asTopic("/finance"), sut.root)).contains(manySub);
}
@Test
public void testMatchSlashSingle() {
ClientTopicCouple slashPlusSub = clientSubOnTopic("AnySensor", "/+");
sut.add(slashPlusSub);
ClientTopicCouple anySub = clientSubOnTopic("AnySensor", "+");
sut.add(anySub);
// Verify
assertThat(sut.recursiveMatch(asTopic("/finance"), sut.root)).containsOnly(slashPlusSub);
assertThat(sut.recursiveMatch(asTopic("/finance"), sut.root)).doesNotContain(anySub);
}
@Test
public void testMatchManyDeepSingle() {
ClientTopicCouple slashPlusSub = clientSubOnTopic("FinanceSensor1", "/finance/+/ibm");
sut.add(slashPlusSub);
ClientTopicCouple slashPlusDeepSub = clientSubOnTopic("FinanceSensor2", "/+/stock/+");
sut.add(slashPlusDeepSub);
// Verify
assertThat(sut.recursiveMatch(asTopic("/finance/stock/ibm"), sut.root))
.containsExactlyInAnyOrder(slashPlusSub, slashPlusDeepSub);
}
@Test
public void testMatchSimpleMulti_allTheTree() {
ClientTopicCouple sub = clientSubOnTopic("AnySensor1", "#");
sut.add(sub);
assertThat(sut.recursiveMatch(asTopic("finance"), sut.root)).isNotEmpty();
assertThat(sut.recursiveMatch(asTopic("finance/ibm"), sut.root)).isNotEmpty();
}
@Test
public void rogerLightTopicMatches() {
assertMatch("foo/bar", "foo/bar");
assertMatch("foo/bar", "foo/bar");
assertMatch("foo/+", "foo/bar");
assertMatch("foo/+/baz", "foo/bar/baz");
assertMatch("foo/+/#", "foo/bar/baz");
assertMatch("#", "foo/bar/baz");
assertNotMatch("foo/bar", "foo");
assertNotMatch("foo/+", "foo/bar/baz");
assertNotMatch("foo/+/baz", "foo/bar/bar");
assertNotMatch("foo/+/#", "fo2/bar/baz");
assertMatch("#", "/foo/bar");
assertMatch("/#", "/foo/bar");
assertNotMatch("/#", "foo/bar");
assertMatch("foo//bar", "foo//bar");
assertMatch("foo//+", "foo//bar");
assertMatch("foo/+/+/baz", "foo///baz");
assertMatch("foo/bar/+", "foo/bar/");
}
private void assertMatch(String s, String t) {
sut = new CTrieSubscriptionDirectory();
MemoryStorageService memStore = new MemoryStorageService(null, null);
ISessionsStore aSessionsStore = memStore.sessionsStore();
sut.init(aSessionsStore);
ClientTopicCouple sub = clientSubOnTopic("AnySensor1", s);
sut.add(sub);
assertThat(sut.recursiveMatch(asTopic(t), sut.root)).isNotEmpty();
}
private void assertNotMatch(String subscription, String topic) {
sut = new CTrieSubscriptionDirectory();
MemoryStorageService memStore = new MemoryStorageService(null, null);
ISessionsStore aSessionsStore = memStore.sessionsStore();
sut.init(aSessionsStore);
ClientTopicCouple sub = clientSubOnTopic("AnySensor1", subscription);
sut.add(sub);
assertThat(sut.recursiveMatch(asTopic(topic), sut.root)).isEmpty();
}
@Test
public void testOverlappingSubscriptions() {
Subscription sub = new Subscription("Sensor1", asTopic("a/+"), MqttQoS.AT_MOST_ONCE);
ClientTopicCouple genericSub = sub.asClientTopicCouple();
this.subscriptionsStore.addNewSubscription(sub);
storageService.sessionsStore().createNewSession("Sensor1", false);
sut.add(genericSub);
Subscription sub2 = new Subscription("Sensor1", asTopic("a/b"), MqttQoS.AT_MOST_ONCE);
ClientTopicCouple specificSub = sub2.asClientTopicCouple();
this.subscriptionsStore.addNewSubscription(sub2);
sut.add(specificSub);
// Verify
assertThat(sut.match(asTopic("a/b")).size()).isEqualTo(1);
}
@Test
public void removeSubscription_withDifferentClients_subscribedSameTopic() {
ClientTopicCouple slashSub = clientSubOnTopic("Sensor1", "/topic");
sut.add(slashSub);
ClientTopicCouple slashSub2 = clientSubOnTopic("Sensor2", "/topic");
sut.add(slashSub2);
// Exercise
sut.removeSubscription(asTopic("/topic"), slashSub2.clientID);
// Verify
ClientTopicCouple remainedSubscription = sut.recursiveMatch(asTopic("/topic"), sut.root).iterator().next();
assertThat(remainedSubscription.clientID).isEqualTo(slashSub.clientID);
assertEquals(slashSub.clientID, remainedSubscription.clientID);
}
@Test
public void removeSubscription_sameClients_subscribedSameTopic() {
ClientTopicCouple slashSub = clientSubOnTopic("Sensor1", "/topic");
sut.add(slashSub);
// Exercise
sut.removeSubscription(asTopic("/topic"), slashSub.clientID);
// Verify
final Set<ClientTopicCouple> matchingSubscriptions = sut.recursiveMatch(asTopic("/topic"), sut.root);
assertThat(matchingSubscriptions).isEmpty();
}
/*
* Test for Issue #49
*/
@Test
public void duplicatedSubscriptionsWithDifferentQos() {
ClientSession session2 = sessionsStore.createNewSession("client2", true);
Subscription client2Sub = new Subscription("client2", asTopic("client/test/b"), MqttQoS.AT_MOST_ONCE);
session2.subscribe(client2Sub);
this.sut.add(client2Sub.asClientTopicCouple());
ClientSession session1 = sessionsStore.createNewSession("client1", true);
Subscription client1SubQoS0 = new Subscription("client1", asTopic("client/test/b"), MqttQoS.AT_MOST_ONCE);
session1.subscribe(client1SubQoS0);
this.sut.add(client1SubQoS0.asClientTopicCouple());
Subscription client1SubQoS2 = new Subscription("client1", asTopic("client/test/b"), MqttQoS.EXACTLY_ONCE);
session1.subscribe(client1SubQoS2);
this.sut.add(client1SubQoS2.asClientTopicCouple());
// Verify
Set<Subscription> subscriptions = this.sut.match(asTopic("client/test/b"));
assertThat(subscriptions).contains(client1SubQoS2);
assertThat(subscriptions).contains(client2Sub);
final Optional<Subscription> matchingClient1Sub = subscriptions
.stream()
.filter(s -> s.equals(client1SubQoS0))
.findFirst();
assertTrue(matchingClient1Sub.isPresent());
Subscription client1Sub = matchingClient1Sub.get();
assertThat(client1SubQoS0.getRequestedQos()).isNotEqualTo(client1Sub.getRequestedQos());
// client1SubQoS2 should override client1SubQoS0
assertThat(client1Sub.getRequestedQos()).isEqualTo(client1SubQoS2.getRequestedQos());
}
}
@@ -0,0 +1,144 @@
/*
* 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.subscriptions;
import io.moquette.persistence.MemoryStorageService;
import io.moquette.spi.ISessionsStore;
import io.moquette.spi.ISubscriptionsStore.ClientTopicCouple;
import org.junit.Before;
import org.junit.Test;
import java.util.Optional;
import java.util.Set;
import static io.moquette.spi.impl.subscriptions.Topic.asTopic;
import static org.junit.Assert.*;
import static org.assertj.core.api.Assertions.assertThat;
public class CTrieSubscriptionDirectoryTest {
private CTrieSubscriptionDirectory sut;
@Before
public void setUp() {
sut = new CTrieSubscriptionDirectory();
MemoryStorageService memStore = new MemoryStorageService(null, null);
ISessionsStore aSessionsStore = memStore.sessionsStore();
sut.init(aSessionsStore);
}
@Test
public void testAddOnSecondLayerWithEmptyTokenOnEmptyTree() {
//Exercise
sut.add(clientSubOnTopic("TempSensor1", "/"));
//Verify
final Optional<CNode> matchedNode = sut.lookup(asTopic("/"));
assertTrue("Node on path / must be present", matchedNode.isPresent());
//verify structure, only root INode and the first CNode should be present
assertThat(this.sut.root.mainNode().subscriptions).isEmpty();
assertThat(this.sut.root.mainNode().allChildren()).isNotEmpty();
INode firstLayer = this.sut.root.mainNode().allChildren().get(0);
assertThat(firstLayer.mainNode().subscriptions).isEmpty();
assertThat(firstLayer.mainNode().allChildren()).isNotEmpty();
INode secondLayer = firstLayer.mainNode().allChildren().get(0);
assertThat(secondLayer.mainNode().subscriptions).isNotEmpty();
assertThat(secondLayer.mainNode().allChildren()).isEmpty();
}
@Test
public void testAddFirstLayerNodeOnEmptyTree() {
//Exercise
sut.add(clientSubOnTopic("TempSensor1", "/temp"));
//Verify
final Optional<CNode> matchedNode = sut.lookup(asTopic("/temp"));
assertTrue("Node on path /temp must be present", matchedNode.isPresent());
assertFalse(matchedNode.get().subscriptions.isEmpty());
}
@Test
public void testLookup() {
final ClientTopicCouple existingSubscription = clientSubOnTopic("TempSensor1", "/temp");
sut.add(existingSubscription);
//Exercise
final Optional<CNode> matchedNode = sut.lookup(asTopic("/humidity"));
//Verify
assertFalse("Node on path /humidity can't be present", matchedNode.isPresent());
}
@Test
public void testAddNewSubscriptionOnExistingNode() {
final ClientTopicCouple existingSubscription = clientSubOnTopic("TempSensor1", "/temp");
sut.add(existingSubscription);
//Exercise
final ClientTopicCouple newSubscription = clientSubOnTopic("TempSensor2", "/temp");
sut.add(newSubscription);
//Verify
final Optional<CNode> matchedNode = sut.lookup(asTopic("/temp"));
assertTrue("Node on path /temp must be present", matchedNode.isPresent());
final Set<ClientTopicCouple> subscriptions = matchedNode.get().subscriptions;
assertTrue(subscriptions.contains(newSubscription));
}
@Test
public void testAddNewDeepNodes() {
sut.add(clientSubOnTopic("TempSensorRM", "/italy/roma/temp"));
sut.add(clientSubOnTopic("TempSensorFI", "/italy/firenze/temp"));
sut.add(clientSubOnTopic("HumSensorFI", "/italy/roma/humidity"));
final ClientTopicCouple happinessSensor = clientSubOnTopic("HappinessSensor", "/italy/happiness");
sut.add(happinessSensor);
//Verify
final Optional<CNode> matchedNode = sut.lookup(asTopic("/italy/happiness"));
assertTrue("Node on path /italy/happiness must be present", matchedNode.isPresent());
final Set<ClientTopicCouple> subscriptions = matchedNode.get().subscriptions;
assertTrue(subscriptions.contains(happinessSensor));
}
static ClientTopicCouple clientSubOnTopic(String clientID, String topicName) {
return new ClientTopicCouple(clientID, asTopic(topicName));
}
@Test
public void givenTreeWithSomeNodeWhenRemoveContainedSubscriptionThenNodeIsUpdated() {
sut.add(clientSubOnTopic("TempSensor1", "/temp"));
//Exercise
sut.removeSubscription(asTopic("/temp"), "TempSensor1");
//Verify
final Optional<CNode> matchedNode = sut.lookup(asTopic("/temp"));
assertFalse("Node on path /temp can't be present", matchedNode.isPresent());
}
@Test
public void testMatchSubscriptionNoWildcards() {
sut.add(clientSubOnTopic("TempSensor1", "/temp"));
//Exercise
final Set<ClientTopicCouple> matchingSubs = sut.recursiveMatch(asTopic("/temp"), sut.root);
//Verify
final ClientTopicCouple expectedMatchingsub = new ClientTopicCouple("TempSensor1", asTopic("/temp"));
assertThat(matchingSubs).contains(expectedMatchingsub);
}
}
@@ -234,34 +234,6 @@ public class SubscriptionsDirectoryTest {
assertThat(store.matches(topic)).isEmpty();
}
@Test
public void testRemoveClientSubscriptions_existingClientID() {
String cliendID = "FAKE_CLID_1";
Subscription sub = new Subscription(cliendID, new Topic("finance/#"), MqttQoS.AT_MOST_ONCE);
this.subscriptionsStore.addNewSubscription(sub);
store.add(sub.asClientTopicCouple());
// Exercise
store.removeForClient(cliendID);
// Verify
assertThat(store.size()).isZero();
}
@Test
public void testRemoveClientSubscriptions_notexistingClientID() {
String clientID = "FAKE_CLID_1";
Subscription s = new Subscription(clientID, new Topic("finance/#"), MqttQoS.AT_MOST_ONCE);
this.subscriptionsStore.addNewSubscription(s);
store.add(s.asClientTopicCouple());
// Exercise
store.removeForClient("FAKE_CLID_2");
// Verify
assertThat(store.size()).isEqualTo(1);
}
@Test
public void testOverlappingSubscriptions() {
Subscription genericSub = new Subscription("FAKE_CLI_ID_1", new Topic("a/+"), MqttQoS.EXACTLY_ONCE);
@@ -277,7 +249,7 @@ public class SubscriptionsDirectoryTest {
@Test
public void removeSubscription_withDifferentClients_subscribedSameTopic() {
SubscriptionsDirectory aStore = new SubscriptionsDirectory();
ISubscriptionsDirectory aStore = new SubscriptionsDirectory();
MemoryStorageService memStore = new MemoryStorageService(null, null);
ISessionsStore sessionsStore = memStore.sessionsStore();
aStore.init(sessionsStore);
@@ -19,6 +19,8 @@ import org.assertj.core.api.AbstractAssert;
import org.assertj.core.api.Assertions;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class TopicTest {
@Test
@@ -38,7 +40,7 @@ public class TopicTest {
}
@Test
public void testValidtionProcess() {
public void testValidationProcess() {
// TopicMultiInTheMiddle
assertThat(new Topic("finance/#/closingprice")).isInValid();
@@ -114,6 +116,13 @@ public class TopicTest {
assertThat(new Topic("foo/bar/")).matches("foo/bar/+");
}
@Test
public void exceptHeadToken() {
assertEquals(Topic.asTopic("token"), Topic.asTopic("/token").exceptHeadToken());
assertEquals(Topic.asTopic("a/b"), Topic.asTopic("/a/b").exceptHeadToken());
}
public static TopicAssert assertThat(Topic topic) {
return new TopicAssert(topic);
}