From 1529cc6cf0a9b9e7207cee5eea78e86e1751d25e Mon Sep 17 00:00:00 2001
From: Sandro Weber <webers@in.tum.de>
Date: Tue, 6 Sep 2022 19:18:31 +0200
Subject: [PATCH] fixed unsubscribe, added test

---
 .../__tests__/mqtt-client-service.test.js     | 72 +++++++++++++++++++
 src/services/mqtt-client-service.js           | 36 ++++++----
 2 files changed, 93 insertions(+), 15 deletions(-)
 create mode 100644 src/services/__tests__/mqtt-client-service.test.js

diff --git a/src/services/__tests__/mqtt-client-service.test.js b/src/services/__tests__/mqtt-client-service.test.js
new file mode 100644
index 0000000..3f384cd
--- /dev/null
+++ b/src/services/__tests__/mqtt-client-service.test.js
@@ -0,0 +1,72 @@
+/**
+ * @jest-environment jsdom
+*/
+import '@testing-library/jest-dom';
+
+import MqttClientService from '../mqtt-client-service';
+
+let subscribeTopicAndValidate = (topic, callback) => {
+  let token = MqttClientService.instance.subscribeToTopic(topic, callback);
+  expect(token).toBeDefined();
+  expect(token.topic).toBe(topic);
+  expect(token.callback).toBe(callback);
+  expect(MqttClientService.instance.subTokensMap.get(topic).includes(token)).toBeTruthy();
+
+  return token;
+};
+
+let unsubscribeAndValidate = (token) => {
+  MqttClientService.instance.unsubscribe(token);
+  expect(MqttClientService.instance.subTokensMap.get(token.topic).includes(token)).toBeFalsy();
+};
+
+test('sub/unsub', async () => {
+  let topicA = 'topic/A';
+  let topicB = 'topic/B';
+
+  let sub1Callback = jest.fn();
+  let sub1Token = subscribeTopicAndValidate(topicA, sub1Callback);
+  let sub2Callback = jest.fn();
+  let sub2Token = subscribeTopicAndValidate(topicA, sub2Callback);
+  let sub3Callback = jest.fn();
+  let sub3Token = subscribeTopicAndValidate(topicB, sub3Callback);
+
+  expect(MqttClientService.instance.subTokensMap.get(topicA).length).toBe(2);
+  expect(MqttClientService.instance.subTokensMap.get(topicB).length).toBe(1);
+
+  MqttClientService.instance.onMessage(topicA, {});
+  MqttClientService.instance.onMessage(topicB, {});
+  expect(sub1Token.callback).toHaveBeenCalledTimes(1);
+  expect(sub2Token.callback).toHaveBeenCalledTimes(1);
+  expect(sub3Token.callback).toHaveBeenCalledTimes(1);
+
+  unsubscribeAndValidate(sub1Token);
+  expect(MqttClientService.instance.subTokensMap.get(topicA).length).toBe(1);
+  expect(MqttClientService.instance.subTokensMap.get(topicB).length).toBe(1);
+
+  MqttClientService.instance.onMessage(topicA, {});
+  MqttClientService.instance.onMessage(topicB, {});
+  expect(sub1Token.callback).toHaveBeenCalledTimes(1);
+  expect(sub2Token.callback).toHaveBeenCalledTimes(2);
+  expect(sub3Token.callback).toHaveBeenCalledTimes(2);
+
+  unsubscribeAndValidate(sub2Token);
+  expect(MqttClientService.instance.subTokensMap.get(topicA).length).toBe(0);
+  expect(MqttClientService.instance.subTokensMap.get(topicB).length).toBe(1);
+
+  MqttClientService.instance.onMessage(topicA, {});
+  MqttClientService.instance.onMessage(topicB, {});
+  expect(sub1Token.callback).toHaveBeenCalledTimes(1);
+  expect(sub2Token.callback).toHaveBeenCalledTimes(2);
+  expect(sub3Token.callback).toHaveBeenCalledTimes(3);
+
+  unsubscribeAndValidate(sub3Token);
+  expect(MqttClientService.instance.subTokensMap.get(topicA).length).toBe(0);
+  expect(MqttClientService.instance.subTokensMap.get(topicB).length).toBe(0);
+
+  MqttClientService.instance.onMessage(topicA, {});
+  MqttClientService.instance.onMessage(topicB, {});
+  expect(sub1Token.callback).toHaveBeenCalledTimes(1);
+  expect(sub2Token.callback).toHaveBeenCalledTimes(2);
+  expect(sub3Token.callback).toHaveBeenCalledTimes(3);
+});
diff --git a/src/services/mqtt-client-service.js b/src/services/mqtt-client-service.js
index cd91d1c..06b2193 100644
--- a/src/services/mqtt-client-service.js
+++ b/src/services/mqtt-client-service.js
@@ -47,15 +47,18 @@ export default class MqttClientService extends EventEmitter {
   }
 
   onMessage(topic, payload, packet) {
+    if (typeof payload === 'undefined') {
+      return;
+    }
+
     //console.info('MQTT message: [topic, payload, packet]');
     //console.info([topic, payload, packet]);
     //Now we see which callbacks have been assigned for a topic
-    if (typeof this.subTokensMap.get(topic) !== 'undefined') {
-      for (var token in this.subTokensMap.get(topic)){
-        if (typeof token.callback === 'function' && payload !== 'undefined') {
-          //Deserializatin of Data must happen here
-          token.callback(payload);
-        }
+    let subTokens = this.subTokensMap.get(topic);
+    if (typeof subTokens !== 'undefined') {
+      for (var token of subTokens) {
+        //Deserializatin of Data must happen here
+        token.callback(payload);
       };
     };
 
@@ -95,22 +98,25 @@ export default class MqttClientService extends EventEmitter {
         [token]
       );
     }
-    console.info('You have been subscribed to topic ' + topic);
-    console.info(this.subTokensMap);
+    //console.info('You have been subscribed to topic ' + topic);
+    //console.info(this.subTokensMap);
     return token;
   }
 
   unsubscribe(unsubToken) {
     if (this.subTokensMap.has(unsubToken.topic)){
-      this.subTokensMap.get(unsubToken.topic).forEach(token => {
-        if (token === unsubToken){
-          this.subTokensMap.get(unsubToken.topic).pop(token);
-          console.info('You have been unsubscribed from topic ' + unsubToken.topic);
-        };
-      });
+      let tokens = this.subTokensMap.get(unsubToken.topic);
+      let index = tokens.indexOf(unsubToken);
+      if (index !== -1) {
+        tokens.splice(index, 1);
+        //console.info('You have been unsubscribed from topic ' + unsubToken.topic);
+      }
+      else {
+        console.warn('Your provided token could not be found in the subscription list');
+      }
     }
     else{
-      console.info('The topic ' + unsubToken.topic + ' was not found');
+      console.warn('The topic ' + unsubToken.topic + ' was not found');
     }
   }
 
-- 
GitLab