FIRMessagingRmqManagerTest.m 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. /*
  2. * Copyright 2017 Google
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #import <XCTest/XCTest.h>
  17. #import "Firebase/Messaging/FIRMessagingPersistentSyncMessage.h"
  18. #import "Firebase/Messaging/FIRMessagingRmqManager.h"
  19. #import "Firebase/Messaging/FIRMessagingUtilities.h"
  20. #import "Firebase/Messaging/Protos/GtalkCore.pbobjc.h"
  21. static NSString *const kRmqDatabaseName = @"rmq-test-db";
  22. static NSString *const kRmqDataMessageCategory = @"com.google.gcm-rmq-test";
  23. @interface FIRMessagingRmqManager (ExposedForTest)
  24. - (void)removeDatabase;
  25. @end
  26. @interface FIRMessagingRmqManagerTest : XCTestCase
  27. @property(nonatomic, readwrite, strong) FIRMessagingRmqManager *rmqManager;
  28. @end
  29. @implementation FIRMessagingRmqManagerTest
  30. - (void)setUp {
  31. [super setUp];
  32. // Make sure we start off with a clean state each time
  33. _rmqManager = [[FIRMessagingRmqManager alloc] initWithDatabaseName:kRmqDatabaseName];
  34. }
  35. - (void)tearDown {
  36. [self.rmqManager removeDatabase];
  37. [super tearDown];
  38. }
  39. /**
  40. * Add s2d messages with different RMQ-ID's to the RMQ. Fetch the messages
  41. * and verify that all messages were successfully saved.
  42. */
  43. - (void)testSavingS2dMessages {
  44. NSArray *messageIDs = @[ @"message1", @"message2", @"123456" ];
  45. for (NSString *messageID in messageIDs) {
  46. [self.rmqManager saveS2dMessageWithRmqId:messageID];
  47. }
  48. NSArray *rmqMessages = [self.rmqManager unackedS2dRmqIds];
  49. XCTAssertEqual(messageIDs.count, rmqMessages.count);
  50. for (NSString *messageID in rmqMessages) {
  51. XCTAssertTrue([messageIDs containsObject:messageID]);
  52. }
  53. }
  54. /**
  55. * Add s2d messages with different RMQ-ID's to the RMQ. Delete some of the
  56. * messages stored, assuming we received a server ACK for them. The remaining
  57. * messages should be fetched successfully.
  58. */
  59. - (void)testDeletingS2dMessages {
  60. NSArray *addMessages = @[ @"message1", @"message2", @"message3", @"message4"];
  61. for (NSString *messageID in addMessages) {
  62. [self.rmqManager saveS2dMessageWithRmqId:messageID];
  63. }
  64. NSArray *removeMessages = @[ addMessages[1], addMessages[3] ];
  65. [self.rmqManager removeS2dIds:removeMessages];
  66. NSArray *remainingMessages = [self.rmqManager unackedS2dRmqIds];
  67. XCTAssertEqual(2, remainingMessages.count);
  68. XCTAssertTrue([remainingMessages containsObject:addMessages[0]]);
  69. XCTAssertTrue([remainingMessages containsObject:addMessages[2]]);
  70. }
  71. /**
  72. * Test deleting a s2d message that is not in the persistent store. This shouldn't
  73. * crash or alter the valid contents of the RMQ store.
  74. */
  75. - (void)testDeletingInvalidS2dMessage {
  76. NSString *validMessageID = @"validMessage123";
  77. [self.rmqManager saveS2dMessageWithRmqId:validMessageID];
  78. NSString *invalidMessageID = @"invalidMessage123";
  79. [self.rmqManager removeS2dIds:@[invalidMessageID]];
  80. NSArray *remainingMessages = [self.rmqManager unackedS2dRmqIds];
  81. XCTAssertEqual(1, remainingMessages.count);
  82. XCTAssertEqualObjects(validMessageID, remainingMessages[0]);
  83. }
  84. /**
  85. * Test loading the RMQ-ID for d2s messages when there are no outgoing messages in the RMQ.
  86. */
  87. - (void)testLoadRmqIDWithNoD2sMessages {
  88. [self.rmqManager loadRmqId];
  89. XCTAssertEqual(-1, [self maxRmqIDInRmqStoreForD2SMessages]);
  90. }
  91. /**
  92. * Test that outgoing RMQ messages are correctly saved
  93. */
  94. - (void)testOutgoingRmqWithValidMessages {
  95. NSString *from = @"rmq-test";
  96. [self.rmqManager loadRmqId];
  97. GtalkDataMessageStanza *message1 = [self dataMessageWithMessageID:@"message1"
  98. from:from
  99. data:nil];
  100. NSError *error = nil;
  101. // should successfully save the message to RMQ
  102. XCTAssertTrue([self.rmqManager saveRmqMessage:message1 error:&error]);
  103. XCTAssertNil(error);
  104. GtalkDataMessageStanza *message2 = [self dataMessageWithMessageID:@"message2"
  105. from:from
  106. data:nil];
  107. // should successfully save the second message to RMQ
  108. XCTAssertTrue([self.rmqManager saveRmqMessage:message2 error:&error]);
  109. XCTAssertNil(error);
  110. // message1 should have RMQ-ID = 2, message2 = 3
  111. XCTAssertEqual(3, [self maxRmqIDInRmqStoreForD2SMessages]);
  112. [self.rmqManager scanWithRmqMessageHandler:nil
  113. dataMessageHandler:^(int64_t rmqId, GtalkDataMessageStanza *stanza) {
  114. if (rmqId == 2) {
  115. XCTAssertEqualObjects(@"message1", stanza.id_p);
  116. } else if (rmqId == 3) {
  117. XCTAssertEqualObjects(@"message2", stanza.id_p);
  118. } else {
  119. XCTFail(@"Invalid RmqID %lld for s2d message", rmqId);
  120. }
  121. }];
  122. }
  123. /**
  124. * Test that an outgoing message with different properties is correctly saved to the RMQ.
  125. */
  126. - (void)testOutgoingDataMessageIsCorrectlySaved {
  127. NSString *from = @"rmq-test";
  128. NSString *messageID = @"message123";
  129. NSString *to = @"to-senderID-123";
  130. int32_t ttl = 2400;
  131. NSString *registrationToken = @"registration-token";
  132. NSDictionary *data = @{
  133. @"hello" : @"world",
  134. @"count" : @"2",
  135. };
  136. [self.rmqManager loadRmqId];
  137. GtalkDataMessageStanza *message = [self dataMessageWithMessageID:messageID
  138. from:from
  139. data:data];
  140. [message setTo:to];
  141. [message setTtl:ttl];
  142. [message setRegId:registrationToken];
  143. NSError *error = nil;
  144. // should successfully save the message to RMQ
  145. XCTAssertTrue([self.rmqManager saveRmqMessage:message error:&error]);
  146. XCTAssertNil(error);
  147. [self.rmqManager scanWithRmqMessageHandler:nil
  148. dataMessageHandler:^(int64_t rmqId, GtalkDataMessageStanza *stanza) {
  149. XCTAssertEqualObjects(from, stanza.from);
  150. XCTAssertEqualObjects(messageID, stanza.id_p);
  151. XCTAssertEqualObjects(to, stanza.to);
  152. XCTAssertEqualObjects(registrationToken, stanza.regId);
  153. XCTAssertEqual(ttl, stanza.ttl);
  154. NSMutableDictionary *d = [NSMutableDictionary dictionary];
  155. for (GtalkAppData *appData in stanza.appDataArray) {
  156. d[appData.key] = appData.value;
  157. }
  158. XCTAssertTrue([data isEqualToDictionary:d]);
  159. }];
  160. }
  161. /**
  162. * Test D2S messages being deleted from RMQ.
  163. */
  164. - (void)testDeletingD2SMessagesFromRMQ {
  165. NSString *message1 = @"message123";
  166. NSString *ackedMessage = @"message234";
  167. NSString *from = @"from-rmq-test";
  168. GtalkDataMessageStanza *stanza1 = [self dataMessageWithMessageID:message1 from:from data:nil];
  169. GtalkDataMessageStanza *stanza2 = [self dataMessageWithMessageID:ackedMessage
  170. from:from
  171. data:nil];
  172. NSError *error = nil;
  173. XCTAssertTrue([self.rmqManager saveRmqMessage:stanza1 error:&error]);
  174. XCTAssertNil(error);
  175. XCTAssertTrue([self.rmqManager saveRmqMessage:stanza2 error:&error]);
  176. XCTAssertNil(error);
  177. __block int64_t ackedMessageRmqID = -1;
  178. [self.rmqManager scanWithRmqMessageHandler:nil
  179. dataMessageHandler:^(int64_t rmqId, GtalkDataMessageStanza *stanza) {
  180. if ([stanza.id_p isEqualToString:ackedMessage]) {
  181. ackedMessageRmqID = rmqId;
  182. }
  183. }];
  184. // should be a valid RMQ ID
  185. XCTAssertTrue(ackedMessageRmqID > 0);
  186. // delete the acked message
  187. NSString *rmqIDString = [NSString stringWithFormat:@"%lld", ackedMessageRmqID];
  188. XCTAssertEqual(1, [self.rmqManager removeRmqMessagesWithRmqIds:@[rmqIDString]]);
  189. // should only have one message in the d2s RMQ
  190. [self.rmqManager scanWithRmqMessageHandler:nil
  191. dataMessageHandler:^(int64_t rmqId, GtalkDataMessageStanza *stanza) {
  192. // the acked message was queued later so should have
  193. // rmqID = ackedMessageRMQID - 1
  194. XCTAssertEqual(ackedMessageRmqID - 1, rmqId);
  195. XCTAssertEqual(message1, stanza2.id_p);
  196. }];
  197. }
  198. /**
  199. * Test saving a sync message to SYNC_RMQ.
  200. */
  201. - (void)testSavingSyncMessage {
  202. NSString *rmqID = @"fake-rmq-id-1";
  203. int64_t expirationTime = FIRMessagingCurrentTimestampInSeconds() + 1;
  204. XCTAssertTrue([self.rmqManager saveSyncMessageWithRmqID:rmqID
  205. expirationTime:expirationTime
  206. apnsReceived:YES
  207. mcsReceived:NO
  208. error:nil]);
  209. FIRMessagingPersistentSyncMessage *persistentMessage = [self.rmqManager querySyncMessageWithRmqID:rmqID];
  210. XCTAssertEqual(persistentMessage.expirationTime, expirationTime);
  211. XCTAssertTrue(persistentMessage.apnsReceived);
  212. XCTAssertFalse(persistentMessage.mcsReceived);
  213. }
  214. /**
  215. * Test updating a sync message initially received via MCS, now being received via APNS.
  216. */
  217. - (void)testUpdateMessageReceivedViaAPNS {
  218. NSString *rmqID = @"fake-rmq-id-1";
  219. int64_t expirationTime = FIRMessagingCurrentTimestampInSeconds() + 1;
  220. XCTAssertTrue([self.rmqManager saveSyncMessageWithRmqID:rmqID
  221. expirationTime:expirationTime
  222. apnsReceived:NO
  223. mcsReceived:YES
  224. error:nil]);
  225. // Message was now received via APNS
  226. XCTAssertTrue([self.rmqManager updateSyncMessageViaAPNSWithRmqID:rmqID error:nil]);
  227. FIRMessagingPersistentSyncMessage *persistentMessage = [self.rmqManager querySyncMessageWithRmqID:rmqID];
  228. XCTAssertTrue(persistentMessage.apnsReceived);
  229. XCTAssertTrue(persistentMessage.mcsReceived);
  230. }
  231. /**
  232. * Test updating a sync message initially received via APNS, now being received via MCS.
  233. */
  234. - (void)testUpdateMessageReceivedViaMCS {
  235. NSString *rmqID = @"fake-rmq-id-1";
  236. int64_t expirationTime = FIRMessagingCurrentTimestampInSeconds() + 1;
  237. XCTAssertTrue([self.rmqManager saveSyncMessageWithRmqID:rmqID
  238. expirationTime:expirationTime
  239. apnsReceived:YES
  240. mcsReceived:NO
  241. error:nil]);
  242. // Message was now received via APNS
  243. XCTAssertTrue([self.rmqManager updateSyncMessageViaMCSWithRmqID:rmqID error:nil]);
  244. FIRMessagingPersistentSyncMessage *persistentMessage = [self.rmqManager querySyncMessageWithRmqID:rmqID];
  245. XCTAssertTrue(persistentMessage.apnsReceived);
  246. XCTAssertTrue(persistentMessage.mcsReceived);
  247. }
  248. /**
  249. * Test deleting sync messages from SYNC_RMQ.
  250. */
  251. - (void)testDeleteSyncMessage {
  252. NSString *rmqID = @"fake-rmq-id-1";
  253. int64_t expirationTime = FIRMessagingCurrentTimestampInSeconds() + 1;
  254. XCTAssertTrue([self.rmqManager saveSyncMessageWithRmqID:rmqID
  255. expirationTime:expirationTime
  256. apnsReceived:YES
  257. mcsReceived:NO
  258. error:nil]);
  259. XCTAssertNotNil([self.rmqManager querySyncMessageWithRmqID:rmqID]);
  260. // should successfully delete the message
  261. XCTAssertTrue([self.rmqManager deleteSyncMessageWithRmqID:rmqID]);
  262. XCTAssertNil([self.rmqManager querySyncMessageWithRmqID:rmqID]);
  263. }
  264. #pragma mark - Private Helpers
  265. - (GtalkDataMessageStanza *)dataMessageWithMessageID:(NSString *)messageID
  266. from:(NSString *)from
  267. data:(NSDictionary *)data {
  268. GtalkDataMessageStanza *stanza = [[GtalkDataMessageStanza alloc] init];
  269. [stanza setId_p:messageID];
  270. [stanza setFrom:from];
  271. [stanza setCategory:kRmqDataMessageCategory];
  272. for (NSString *key in data) {
  273. NSString *val = data[key];
  274. GtalkAppData *appData = [[GtalkAppData alloc] init];
  275. [appData setKey:key];
  276. [appData setValue:val];
  277. [[stanza appDataArray] addObject:appData];
  278. }
  279. return stanza;
  280. }
  281. - (int64_t)maxRmqIDInRmqStoreForD2SMessages {
  282. __block int64_t maxRmqID = -1;
  283. [self.rmqManager scanWithRmqMessageHandler:^(int64_t rmqId, int8_t tag, NSData *data) {
  284. if (rmqId > maxRmqID) {
  285. maxRmqID = rmqId;
  286. }
  287. }
  288. dataMessageHandler:nil];
  289. return maxRmqID;
  290. }
  291. @end