|  |  |  |  | // | 
					
						
							|  |  |  |  | //  Copyright (c) 2017 Open Whisper Systems. All rights reserved. | 
					
						
							|  |  |  |  | // | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | #import <XCTest/XCTest.h> | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | #import "Cryptography.h" | 
					
						
							|  |  |  |  | #import "TSThread.h" | 
					
						
							|  |  |  |  | #import "TSContactThread.h" | 
					
						
							|  |  |  |  | #import "TSGroupThread.h" | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | #import "TSStorageManager.h" | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | #import "TSIncomingMessage.h" | 
					
						
							|  |  |  |  | #import "TSMessage.h" | 
					
						
							|  |  |  |  | #import "TSOutgoingMessage.h" | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | @interface TSMessageStorageTests : XCTestCase | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | @property TSContactThread *thread; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | @end | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | @implementation TSMessageStorageTests | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | - (void)setUp | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |     [super setUp]; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     [[TSStorageManager sharedManager].dbReadWriteConnection | 
					
						
							|  |  |  |  |         readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { | 
					
						
							|  |  |  |  |             self.thread = [TSContactThread getOrCreateThreadWithContactId:@"aStupidId" transaction:transaction]; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             [self.thread saveWithTransaction:transaction]; | 
					
						
							|  |  |  |  |         }]; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     TSStorageManager *manager = [TSStorageManager sharedManager]; | 
					
						
							|  |  |  |  |     [manager purgeCollection:[TSMessage collection]]; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | - (void)tearDown | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |     // Put teardown code here. This method is called after the invocation of each test method in the class. | 
					
						
							|  |  |  |  |     [super tearDown]; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | - (void)testIncrementalMessageNumbers | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |     __block NSInteger messageInt; | 
					
						
							|  |  |  |  |     NSString *body = @"I don't see myself as a hero because what I'm doing is self-interested: I don't want to live in a world where there's no privacy and therefore no room for intellectual exploration and creativity."; | 
					
						
							|  |  |  |  |     [[TSStorageManager sharedManager].newDatabaseConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { | 
					
						
							|  |  |  |  |          | 
					
						
							|  |  |  |  |         NSString* messageId; | 
					
						
							|  |  |  |  |          | 
					
						
							|  |  |  |  |         for (uint64_t i = 0; i<50; i++) { | 
					
						
							|  |  |  |  |             TSOutgoingMessage *newMessage = | 
					
						
							|  |  |  |  |                 [[TSOutgoingMessage alloc] initWithTimestamp:i inThread:self.thread messageBody:body]; | 
					
						
							|  |  |  |  |             [newMessage saveWithTransaction:transaction]; | 
					
						
							|  |  |  |  |             if (i == 0) { | 
					
						
							|  |  |  |  |                 messageId = newMessage.uniqueId; | 
					
						
							|  |  |  |  |              } | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  |          | 
					
						
							|  |  |  |  |         messageInt = [messageId integerValue]; | 
					
						
							|  |  |  |  |          | 
					
						
							|  |  |  |  |         for (NSInteger i = messageInt; i < messageInt+50; i++) { | 
					
						
							|  |  |  |  |             TSOutgoingMessage *message = | 
					
						
							|  |  |  |  |                 [TSOutgoingMessage fetchObjectWithUniqueID:[@(i) stringValue] transaction:transaction]; | 
					
						
							|  |  |  |  |             XCTAssert(message != nil); | 
					
						
							|  |  |  |  |             XCTAssert(message.body == body); | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  |     }]; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     [[TSStorageManager sharedManager].newDatabaseConnection | 
					
						
							|  |  |  |  |         readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { | 
					
						
							|  |  |  |  |             TSOutgoingMessage *deletedmessage = | 
					
						
							|  |  |  |  |                 [TSOutgoingMessage fetchObjectWithUniqueID:[@(messageInt + 49) stringValue]]; | 
					
						
							|  |  |  |  |             [deletedmessage removeWithTransaction:transaction]; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             uint64_t uniqueNewTimestamp = 985439854983; | 
					
						
							|  |  |  |  |             TSOutgoingMessage *newMessage = | 
					
						
							|  |  |  |  |                 [[TSOutgoingMessage alloc] initWithTimestamp:uniqueNewTimestamp inThread:self.thread messageBody:body]; | 
					
						
							|  |  |  |  |             [newMessage saveWithTransaction:transaction]; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             TSOutgoingMessage *retrieved = | 
					
						
							|  |  |  |  |                 [TSOutgoingMessage fetchObjectWithUniqueID:[@(messageInt + 50) stringValue] transaction:transaction]; | 
					
						
							|  |  |  |  |             XCTAssertEqual(uniqueNewTimestamp, retrieved.timestamp); | 
					
						
							|  |  |  |  |         }]; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | - (void)testStoreIncomingMessage | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |     __block NSString *messageId; | 
					
						
							|  |  |  |  |     uint64_t timestamp = 666; | 
					
						
							|  |  |  |  |      | 
					
						
							|  |  |  |  |     NSString *body = @"A child born today will grow up with no conception of privacy at all. They’ll never know what it means to have a private moment to themselves an unrecorded, unanalyzed thought. And that’s a problem because privacy matters; privacy is what allows us to determine who we are and who we want to be."; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     TSIncomingMessage *newMessage = [[TSIncomingMessage alloc] initWithTimestamp:timestamp | 
					
						
							|  |  |  |  |                                                                         inThread:self.thread | 
					
						
							|  |  |  |  |                                                                         authorId:[self.thread contactIdentifier] | 
					
						
							|  |  |  |  |                                                                   sourceDeviceId:1 | 
					
						
							|  |  |  |  |                                                                      messageBody:body]; | 
					
						
							|  |  |  |  |     [[TSStorageManager sharedManager].newDatabaseConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { | 
					
						
							|  |  |  |  |         [newMessage saveWithTransaction:transaction]; | 
					
						
							|  |  |  |  |         messageId = newMessage.uniqueId; | 
					
						
							|  |  |  |  |     }]; | 
					
						
							|  |  |  |  |      | 
					
						
							|  |  |  |  |     TSIncomingMessage *fetchedMessage = [TSIncomingMessage fetchObjectWithUniqueID:messageId]; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     XCTAssertEqualObjects(body, fetchedMessage.body); | 
					
						
							|  |  |  |  |     XCTAssertFalse(fetchedMessage.hasAttachments); | 
					
						
							|  |  |  |  |     XCTAssertEqual(timestamp, fetchedMessage.timestamp); | 
					
						
							|  |  |  |  |     XCTAssertFalse(fetchedMessage.wasRead); | 
					
						
							|  |  |  |  |     XCTAssertEqualObjects(self.thread.uniqueId, fetchedMessage.uniqueThreadId); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | - (void)testMessagesDeletedOnThreadDeletion | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |     NSString *body = @"A child born today will grow up with no conception of privacy at all. They’ll never know what it means to have a private moment to themselves an unrecorded, unanalyzed thought. And that’s a problem because privacy matters; privacy is what allows us to determine who we are and who we want to be."; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     NSMutableArray<TSIncomingMessage *> *messages = [NSMutableArray new]; | 
					
						
							|  |  |  |  |     for (int i = 0; i < 10; i++) { | 
					
						
							|  |  |  |  |         TSIncomingMessage *newMessage = [[TSIncomingMessage alloc] initWithTimestamp:i | 
					
						
							|  |  |  |  |                                                                             inThread:self.thread | 
					
						
							|  |  |  |  |                                                                             authorId:[self.thread contactIdentifier] | 
					
						
							|  |  |  |  |                                                                       sourceDeviceId:1 | 
					
						
							|  |  |  |  |                                                                          messageBody:body]; | 
					
						
							|  |  |  |  |         [messages addObject:newMessage]; | 
					
						
							|  |  |  |  |         [newMessage save]; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     for (TSIncomingMessage *message in messages) { | 
					
						
							|  |  |  |  |         TSIncomingMessage *fetchedMessage = [TSIncomingMessage fetchObjectWithUniqueID:message.uniqueId]; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         XCTAssertEqualObjects(fetchedMessage.body, body, @"Body of incoming message recovered"); | 
					
						
							|  |  |  |  |         XCTAssertEqual(0, fetchedMessage.attachmentIds.count, @"attachments are nil"); | 
					
						
							|  |  |  |  |         XCTAssertEqualObjects(fetchedMessage.uniqueId, message.uniqueId, @"Unique identifier is accurate"); | 
					
						
							|  |  |  |  |         XCTAssertFalse(fetchedMessage.wasRead, @"Message should originally be unread"); | 
					
						
							|  |  |  |  |         XCTAssertEqualObjects( | 
					
						
							|  |  |  |  |             fetchedMessage.uniqueThreadId, self.thread.uniqueId, @"Isn't stored in the right thread!"); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     [self.thread remove]; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     for (TSIncomingMessage *message in messages) { | 
					
						
							|  |  |  |  |         TSIncomingMessage *fetchedMessage = [TSIncomingMessage fetchObjectWithUniqueID:message.uniqueId]; | 
					
						
							|  |  |  |  |         XCTAssertNil(fetchedMessage, @"Message should be deleted!"); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | - (void)testGroupMessagesDeletedOnThreadDeletion | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |     NSString *body = @"A child born today will grow up with no conception of privacy at all. They’ll never know what it means to have a private moment to themselves an unrecorded, unanalyzed thought. And that’s a problem because privacy matters; privacy is what allows us to determine who we are and who we want to be."; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     __block TSGroupThread *thread; | 
					
						
							|  |  |  |  |     [[TSStorageManager sharedManager].dbReadWriteConnection readWriteWithBlock:^( | 
					
						
							|  |  |  |  |         YapDatabaseReadWriteTransaction *transaction) { | 
					
						
							|  |  |  |  |         thread = [TSGroupThread getOrCreateThreadWithGroupModel:[[TSGroupModel alloc] initWithTitle:@"fdsfsd" | 
					
						
							|  |  |  |  |                                                                                           memberIds:[@[] mutableCopy] | 
					
						
							|  |  |  |  |                                                                                               image:nil | 
					
						
							|  |  |  |  |                                                                                             groupId:[NSData data]] | 
					
						
							|  |  |  |  |                                                     transaction:transaction]; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         [thread saveWithTransaction:transaction]; | 
					
						
							|  |  |  |  |     }]; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     TSStorageManager *manager         = [TSStorageManager sharedManager]; | 
					
						
							|  |  |  |  |     [manager purgeCollection:[TSMessage collection]]; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     NSMutableArray<TSIncomingMessage *> *messages = [NSMutableArray new]; | 
					
						
							|  |  |  |  |     for (uint64_t i = 0; i < 10; i++) { | 
					
						
							|  |  |  |  |         TSIncomingMessage *newMessage = [[TSIncomingMessage alloc] initWithTimestamp:i | 
					
						
							|  |  |  |  |                                                                             inThread:thread | 
					
						
							|  |  |  |  |                                                                             authorId:@"Ed" | 
					
						
							|  |  |  |  |                                                                       sourceDeviceId:1 | 
					
						
							|  |  |  |  |                                                                          messageBody:body]; | 
					
						
							|  |  |  |  |         [newMessage save]; | 
					
						
							|  |  |  |  |         [messages addObject:newMessage]; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     for (TSIncomingMessage *message in messages) { | 
					
						
							|  |  |  |  |         TSIncomingMessage *fetchedMessage = [TSIncomingMessage fetchObjectWithUniqueID:message.uniqueId]; | 
					
						
							|  |  |  |  |         XCTAssertNotNil(fetchedMessage); | 
					
						
							|  |  |  |  |         XCTAssertEqualObjects(fetchedMessage.body, body, @"Body of incoming message recovered"); | 
					
						
							|  |  |  |  |         XCTAssertEqual(0, fetchedMessage.attachmentIds.count, @"attachments are empty"); | 
					
						
							|  |  |  |  |         XCTAssertEqualObjects(fetchedMessage.uniqueId, message.uniqueId, @"Unique identifier is accurate"); | 
					
						
							|  |  |  |  |         XCTAssertFalse(fetchedMessage.wasRead, @"Message should originally be unread"); | 
					
						
							|  |  |  |  |         XCTAssertEqualObjects(fetchedMessage.uniqueThreadId, thread.uniqueId, @"Isn't stored in the right thread!"); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     [thread remove]; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     for (TSIncomingMessage *message in messages) { | 
					
						
							|  |  |  |  |         TSIncomingMessage *fetchedMessage = [TSIncomingMessage fetchObjectWithUniqueID:message.uniqueId]; | 
					
						
							|  |  |  |  |         XCTAssertNil(fetchedMessage, @"Message should be deleted!"); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | @end |