FIRMessagingRmqManagerTest.m 13 KB

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