|  |  |  | // | 
					
						
							|  |  |  | //  Copyright (c) 2017 Open Whisper Systems. All rights reserved. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #import "Cryptography.h" | 
					
						
							|  |  |  | #import "OWSDisappearingMessagesConfiguration.h" | 
					
						
							|  |  |  | #import "OWSError.h" | 
					
						
							|  |  |  | #import "OWSFakeContactsManager.h" | 
					
						
							|  |  |  | #import "OWSFakeContactsUpdater.h" | 
					
						
							|  |  |  | #import "OWSFakeNetworkManager.h" | 
					
						
							|  |  |  | #import "OWSMessageSender.h" | 
					
						
							|  |  |  | #import "OWSUploadingService.h" | 
					
						
							|  |  |  | #import "TSAccountManager.h" | 
					
						
							|  |  |  | #import "TSContactThread.h" | 
					
						
							|  |  |  | #import "TSGroupModel.h" | 
					
						
							|  |  |  | #import "TSGroupThread.h" | 
					
						
							|  |  |  | #import "TSMessagesManager.h" | 
					
						
							|  |  |  | #import "TSNetworkManager.h" | 
					
						
							|  |  |  | #import "TSOutgoingMessage.h" | 
					
						
							|  |  |  | #import "TSStorageManager.h" | 
					
						
							|  |  |  | #import <AxolotlKit/AxolotlExceptions.h> | 
					
						
							|  |  |  | #import <AxolotlKit/SessionBuilder.h> | 
					
						
							|  |  |  | #import <XCTest/XCTest.h> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | NS_ASSUME_NONNULL_BEGIN | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @interface OWSMessageSender (Testing) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @property (nonatomic) OWSUploadingService *uploadingService; | 
					
						
							|  |  |  | @property (nonatomic) ContactsUpdater *contactsUpdater; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Private Methods to test | 
					
						
							|  |  |  | - (NSArray<SignalRecipient *> *)getRecipients:(NSArray<NSString *> *)identifiers error:(NSError **)error; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @implementation OWSMessageSender (Testing) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | - (NSArray<NSDictionary *> *)deviceMessages:(TSOutgoingMessage *)message | 
					
						
							|  |  |  |                                forRecipient:(SignalRecipient *)recipient | 
					
						
							|  |  |  |                                    inThread:(TSThread *)thread | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     NSLog(@"[OWSFakeMessagesManager] Faking deviceMessages."); | 
					
						
							|  |  |  |     return @[]; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | - (void)setContactsUpdater:(ContactsUpdater *)contactsUpdater | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     _contactsUpdater = contactsUpdater; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | - (ContactsUpdater *)contactsUpdater | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return _contactsUpdater; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | - (void)setUploadingService:(OWSUploadingService *)uploadingService | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     _uploadingService = uploadingService; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | - (OWSUploadingService *)uploadingService | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return _uploadingService; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @interface OWSFakeUploadingService : OWSUploadingService | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @property (nonatomic, readonly) BOOL shouldSucceed; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @implementation OWSFakeUploadingService | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | - (instancetype)initWithSuccess:(BOOL)flag | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     self = [super initWithNetworkManager:[OWSFakeNetworkManager new]]; | 
					
						
							|  |  |  |     if (!self) { | 
					
						
							|  |  |  |         return self; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     _shouldSucceed = flag; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return self; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | - (void)uploadAttachmentStream:(TSAttachmentStream *)attachmentStream | 
					
						
							|  |  |  |                        message:(TSOutgoingMessage *)outgoingMessage | 
					
						
							|  |  |  |                        success:(void (^)())successHandler | 
					
						
							|  |  |  |                        failure:(void (^)(NSError *error))failureHandler | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (self.shouldSucceed) { | 
					
						
							|  |  |  |         successHandler(); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         NSError *error = OWSErrorMakeFailedToSendOutgoingMessageError(); | 
					
						
							|  |  |  |         [error setIsRetryable:NO]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         failureHandler(error); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @interface OWSFakeURLSessionDataTask : NSURLSessionDataTask | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @property (copy) NSHTTPURLResponse *response; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | - (instancetype)initWithStatusCode:(long)statusCode; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @implementation OWSFakeURLSessionDataTask | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @synthesize response = _response; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | - (instancetype)initWithStatusCode:(long)statusCode | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     self = [super init]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!self) { | 
					
						
							|  |  |  |         return self; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     NSURL *fakeURL = [NSURL URLWithString:@"http://127.0.0.1"]; | 
					
						
							|  |  |  |     _response = [[NSHTTPURLResponse alloc] initWithURL:fakeURL statusCode:statusCode HTTPVersion:nil headerFields:nil]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return self; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @interface OWSMessageSenderFakeNetworkManager : OWSFakeNetworkManager | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | - (instancetype)init; | 
					
						
							|  |  |  | - (instancetype)initWithSuccess:(BOOL)shouldSucceed NS_DESIGNATED_INITIALIZER; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @property (nonatomic, readonly) BOOL shouldSucceed; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @implementation OWSMessageSenderFakeNetworkManager | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | - (instancetype)initWithSuccess:(BOOL)shouldSucceed | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     self = [self init]; | 
					
						
							|  |  |  |     if (!self) { | 
					
						
							|  |  |  |         return self; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     _shouldSucceed = shouldSucceed; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return self; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | - (void)makeRequest:(TSRequest *)request | 
					
						
							|  |  |  |             success:(void (^)(NSURLSessionDataTask *task, id responseObject))success | 
					
						
							|  |  |  |             failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if ([request isKindOfClass:[TSSubmitMessageRequest class]]) { | 
					
						
							|  |  |  |         if (self.shouldSucceed) { | 
					
						
							|  |  |  |             success([NSURLSessionDataTask new], @{}); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             NSError *error | 
					
						
							|  |  |  |                 = OWSErrorWithCodeDescription(OWSErrorCodeFailedToSendOutgoingMessage, @"fake error description"); | 
					
						
							|  |  |  |             OWSFakeURLSessionDataTask *task = [[OWSFakeURLSessionDataTask alloc] initWithStatusCode:500]; | 
					
						
							|  |  |  |             failure(task, error); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         [super makeRequest:request success:success failure:failure]; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @interface TSAccountManager (Testing) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | - (void)storeLocalNumber:(NSString *)localNumber; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @interface OWSMessageSenderTest : XCTestCase | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @property (nonatomic) TSThread *thread; | 
					
						
							|  |  |  | @property (nonatomic) TSOutgoingMessage *expiringMessage; | 
					
						
							|  |  |  | @property (nonatomic) TSOutgoingMessage *unexpiringMessage; | 
					
						
							|  |  |  | @property (nonatomic) OWSMessageSenderFakeNetworkManager *networkManager; | 
					
						
							|  |  |  | @property (nonatomic) OWSMessageSender *successfulMessageSender; | 
					
						
							|  |  |  | @property (nonatomic) OWSMessageSender *unsuccessfulMessageSender; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @implementation OWSMessageSenderTest | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | - (void)setUp | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     [super setUp]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Hack to make sure we don't explode when sending sync message. | 
					
						
							|  |  |  |     [[TSAccountManager sharedInstance] storeLocalNumber:@"+13231231234"]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     self.thread = [[TSContactThread alloc] initWithUniqueId:@"fake-thread-id"]; | 
					
						
							|  |  |  |     [self.thread save]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     self.unexpiringMessage = [[TSOutgoingMessage alloc] initWithTimestamp:1 | 
					
						
							|  |  |  |                                                                  inThread:self.thread | 
					
						
							|  |  |  |                                                               messageBody:@"outgoing message" | 
					
						
							|  |  |  |                                                             attachmentIds:[NSMutableArray new] | 
					
						
							|  |  |  |                                                          expiresInSeconds:0]; | 
					
						
							|  |  |  |     [self.unexpiringMessage save]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     self.expiringMessage = [[TSOutgoingMessage alloc] initWithTimestamp:1 | 
					
						
							|  |  |  |                                                                inThread:self.thread | 
					
						
							|  |  |  |                                                             messageBody:@"outgoing message" | 
					
						
							|  |  |  |                                                           attachmentIds:[NSMutableArray new] | 
					
						
							|  |  |  |                                                        expiresInSeconds:30]; | 
					
						
							|  |  |  |     [self.expiringMessage save]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     TSStorageManager *storageManager = [TSStorageManager sharedManager]; | 
					
						
							|  |  |  |     OWSFakeContactsManager *contactsManager = [OWSFakeContactsManager new]; | 
					
						
							|  |  |  |     OWSFakeContactsUpdater *contactsUpdater = [OWSFakeContactsUpdater new]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Successful Sending | 
					
						
							|  |  |  |     TSNetworkManager *successfulNetworkManager = [[OWSMessageSenderFakeNetworkManager alloc] initWithSuccess:YES]; | 
					
						
							|  |  |  |     self.successfulMessageSender = [[OWSMessageSender alloc] initWithNetworkManager:successfulNetworkManager | 
					
						
							|  |  |  |                                                                      storageManager:storageManager | 
					
						
							|  |  |  |                                                                     contactsManager:contactsManager | 
					
						
							|  |  |  |                                                                     contactsUpdater:contactsUpdater]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Unsuccessful Sending | 
					
						
							|  |  |  |     TSNetworkManager *unsuccessfulNetworkManager = [[OWSMessageSenderFakeNetworkManager alloc] initWithSuccess:NO]; | 
					
						
							|  |  |  |     self.unsuccessfulMessageSender = [[OWSMessageSender alloc] initWithNetworkManager:unsuccessfulNetworkManager | 
					
						
							|  |  |  |                                                                        storageManager:storageManager | 
					
						
							|  |  |  |                                                                       contactsManager:contactsManager | 
					
						
							|  |  |  |                                                                       contactsUpdater:contactsUpdater]; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | - (void)testExpiringMessageTimerStartsOnSuccessWhenDisappearingMessagesEnabled | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     [[[OWSDisappearingMessagesConfiguration alloc] initWithThreadId:self.thread.uniqueId enabled:YES durationSeconds:10] | 
					
						
							|  |  |  |         save]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     OWSMessageSender *messageSender = self.successfulMessageSender; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Sanity Check | 
					
						
							|  |  |  |     XCTAssertEqual(0, self.expiringMessage.expiresAt); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     XCTestExpectation *messageStartedExpiration = [self expectationWithDescription:@"messageStartedExpiration"]; | 
					
						
							|  |  |  |     [messageSender sendMessage:self.expiringMessage | 
					
						
							|  |  |  |         success:^() { | 
					
						
							|  |  |  |             //FIXME remove sleep hack in favor of expiringMessage completion handler | 
					
						
							|  |  |  |             sleep(2); | 
					
						
							|  |  |  |             if (self.expiringMessage.expiresAt > 0) { | 
					
						
							|  |  |  |                 [messageStartedExpiration fulfill]; | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 XCTFail(@"Message expiration was supposed to start."); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         failure:^(NSError *error) { | 
					
						
							|  |  |  |             XCTFail(@"Message failed to send"); | 
					
						
							|  |  |  |         }]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     [self waitForExpectationsWithTimeout:5 | 
					
						
							|  |  |  |                                  handler:^(NSError *error) { | 
					
						
							|  |  |  |                                      NSLog(@"Expiration timer not set in time."); | 
					
						
							|  |  |  |                                  }]; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | - (void)testExpiringMessageTimerDoesNotStartsWhenDisabled | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     [[[OWSDisappearingMessagesConfiguration alloc] initWithThreadId:self.thread.uniqueId enabled:NO durationSeconds:10] | 
					
						
							|  |  |  |         save]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     OWSMessageSender *messageSender = self.successfulMessageSender; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     XCTestExpectation *messageDidNotStartExpiration = [self expectationWithDescription:@"messageDidNotStartExpiration"]; | 
					
						
							|  |  |  |     [messageSender sendMessage:self.unexpiringMessage | 
					
						
							|  |  |  |         success:^() { | 
					
						
							|  |  |  |             if (self.unexpiringMessage.isExpiringMessage || self.unexpiringMessage.expiresAt > 0) { | 
					
						
							|  |  |  |                 XCTFail(@"Message expiration was not supposed to start."); | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 [messageDidNotStartExpiration fulfill]; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         failure:^(NSError *error) { | 
					
						
							|  |  |  |             XCTFail(@"Message failed to send"); | 
					
						
							|  |  |  |         }]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     [self waitForExpectationsWithTimeout:5 | 
					
						
							|  |  |  |                                  handler:^(NSError *error) { | 
					
						
							|  |  |  |                                      NSLog(@"Expiration timer not set in time."); | 
					
						
							|  |  |  |                                  }]; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | - (void)testExpiringMessageTimerDoesNotStartsOnFailure | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     OWSMessageSender *messageSender = self.unsuccessfulMessageSender; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Sanity Check | 
					
						
							|  |  |  |     XCTAssertEqual(0, self.expiringMessage.expiresAt); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     XCTestExpectation *messageDidNotStartExpiration = [self expectationWithDescription:@"messageStartedExpiration"]; | 
					
						
							|  |  |  |     [messageSender sendMessage:self.expiringMessage | 
					
						
							|  |  |  |         success:^() { | 
					
						
							|  |  |  |             XCTFail(@"Message sending was supposed to fail."); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         failure:^(NSError *error) { | 
					
						
							|  |  |  |             if (self.expiringMessage.expiresAt == 0) { | 
					
						
							|  |  |  |                 [messageDidNotStartExpiration fulfill]; | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 XCTFail(@"Message expiration was not supposed to start."); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         }]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     [self waitForExpectationsWithTimeout:5 handler:nil]; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | - (void)testTextMessageIsMarkedAsSentOnSuccess | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     OWSMessageSender *messageSender = self.successfulMessageSender; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     TSOutgoingMessage *message = [[TSOutgoingMessage alloc] initWithTimestamp:1 | 
					
						
							|  |  |  |                                                                      inThread:self.thread | 
					
						
							|  |  |  |                                                                   messageBody:@"We want punks in the palace."]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     XCTestExpectation *markedAsSent = [self expectationWithDescription:@"markedAsSent"]; | 
					
						
							|  |  |  |     [messageSender sendMessage:message | 
					
						
							|  |  |  |         success:^() { | 
					
						
							|  |  |  |             if (message.messageState == TSOutgoingMessageStateSentToService) { | 
					
						
							|  |  |  |                 [markedAsSent fulfill]; | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 XCTFail(@"Unexpected message state"); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         failure:^(NSError *error) { | 
					
						
							|  |  |  |             XCTFail(@"sendMessage should succeed."); | 
					
						
							|  |  |  |         }]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     [self waitForExpectationsWithTimeout:5 handler:nil]; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | - (void)testMediaMessageIsMarkedAsSentOnSuccess | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     OWSMessageSender *messageSender = self.successfulMessageSender; | 
					
						
							|  |  |  |     messageSender.uploadingService = [[OWSFakeUploadingService alloc] initWithSuccess:YES]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     TSOutgoingMessage *message = [[TSOutgoingMessage alloc] initWithTimestamp:1 | 
					
						
							|  |  |  |                                                                      inThread:self.thread | 
					
						
							|  |  |  |                                                                   messageBody:@"We want punks in the palace."]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     XCTestExpectation *markedAsSent = [self expectationWithDescription:@"markedAsSent"]; | 
					
						
							|  |  |  |     [messageSender sendAttachmentData:[NSData new] | 
					
						
							|  |  |  |         contentType:@"image/gif" | 
					
						
							|  |  |  |         sourceFilename:nil | 
					
						
							|  |  |  |         inMessage:message | 
					
						
							|  |  |  |         success:^() { | 
					
						
							|  |  |  |             if (message.messageState == TSOutgoingMessageStateSentToService) { | 
					
						
							|  |  |  |                 [markedAsSent fulfill]; | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 XCTFail(@"Unexpected message state"); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         failure:^(NSError *error) { | 
					
						
							|  |  |  |             XCTFail(@"sendMessage should succeed."); | 
					
						
							|  |  |  |         }]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     [self waitForExpectationsWithTimeout:5 handler:nil]; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | - (void)testTextMessageIsMarkedAsUnsentOnFailure | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     OWSMessageSender *messageSender = self.unsuccessfulMessageSender; | 
					
						
							|  |  |  |     messageSender.uploadingService = [[OWSFakeUploadingService alloc] initWithSuccess:YES]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     TSOutgoingMessage *message = [[TSOutgoingMessage alloc] initWithTimestamp:1 | 
					
						
							|  |  |  |                                                                      inThread:self.thread | 
					
						
							|  |  |  |                                                                   messageBody:@"We want punks in the palace."]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     XCTestExpectation *markedAsUnsent = [self expectationWithDescription:@"markedAsUnsent"]; | 
					
						
							|  |  |  |     [messageSender sendMessage:message | 
					
						
							|  |  |  |         success:^() { | 
					
						
							|  |  |  |             XCTFail(@"sendMessage should fail."); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         failure:^(NSError *error) { | 
					
						
							|  |  |  |             if (message.messageState == TSOutgoingMessageStateUnsent) { | 
					
						
							|  |  |  |                 [markedAsUnsent fulfill]; | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 XCTFail(@"Unexpected message state"); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         }]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     [self waitForExpectationsWithTimeout:5 handler:nil]; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | - (void)testMediaMessageIsMarkedAsUnsentOnFailureToSend | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     OWSMessageSender *messageSender = self.unsuccessfulMessageSender; | 
					
						
							|  |  |  |     // Assume that upload will go well, but that failure happens elsewhere in message sender. | 
					
						
							|  |  |  |     messageSender.uploadingService = [[OWSFakeUploadingService alloc] initWithSuccess:YES]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     TSOutgoingMessage *message = [[TSOutgoingMessage alloc] initWithTimestamp:1 | 
					
						
							|  |  |  |                                                                      inThread:self.thread | 
					
						
							|  |  |  |                                                                   messageBody:@"We want punks in the palace."]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     XCTestExpectation *markedAsUnsent = [self expectationWithDescription:@"markedAsUnsent"]; | 
					
						
							|  |  |  |     [messageSender sendAttachmentData:[NSData new] | 
					
						
							|  |  |  |         contentType:@"image/gif" | 
					
						
							|  |  |  |         sourceFilename:nil | 
					
						
							|  |  |  |         inMessage:message | 
					
						
							|  |  |  |         success:^{ | 
					
						
							|  |  |  |             XCTFail(@"sendMessage should fail."); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         failure:^(NSError *_Nonnull error) { | 
					
						
							|  |  |  |             if (message.messageState == TSOutgoingMessageStateUnsent) { | 
					
						
							|  |  |  |                 [markedAsUnsent fulfill]; | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 XCTFail(@"Unexpected message state"); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         }]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     [self waitForExpectationsWithTimeout:5 handler:nil]; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | - (void)testMediaMessageIsMarkedAsUnsentOnFailureToUpload | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     OWSMessageSender *messageSender = self.successfulMessageSender; | 
					
						
							|  |  |  |     // Assume that upload fails, but other sending stuff would succeed. | 
					
						
							|  |  |  |     messageSender.uploadingService = [[OWSFakeUploadingService alloc] initWithSuccess:NO]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     TSOutgoingMessage *message = [[TSOutgoingMessage alloc] initWithTimestamp:1 | 
					
						
							|  |  |  |                                                                      inThread:self.thread | 
					
						
							|  |  |  |                                                                   messageBody:@"We want punks in the palace."]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     XCTestExpectation *markedAsUnsent = [self expectationWithDescription:@"markedAsUnsent"]; | 
					
						
							|  |  |  |     [messageSender sendAttachmentData:[NSData new] | 
					
						
							|  |  |  |         contentType:@"image/gif" | 
					
						
							|  |  |  |         sourceFilename:nil | 
					
						
							|  |  |  |         inMessage:message | 
					
						
							|  |  |  |         success:^{ | 
					
						
							|  |  |  |             XCTFail(@"sendMessage should fail."); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         failure:^(NSError *_Nonnull error) { | 
					
						
							|  |  |  |             if (message.messageState == TSOutgoingMessageStateUnsent) { | 
					
						
							|  |  |  |                 [markedAsUnsent fulfill]; | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 XCTFail(@"Unexpected message state"); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         }]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     [self waitForExpectationsWithTimeout:5 handler:nil]; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | - (void)testGroupSend | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     OWSMessageSender *messageSender = self.successfulMessageSender; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     NSData *groupIdData = [Cryptography generateRandomBytes:32]; | 
					
						
							|  |  |  |     SignalRecipient *successfulRecipient = | 
					
						
							|  |  |  |         [[SignalRecipient alloc] initWithTextSecureIdentifier:@"successful-recipient-id" relay:nil]; | 
					
						
							|  |  |  |     SignalRecipient *successfulRecipient2 = | 
					
						
							|  |  |  |         [[SignalRecipient alloc] initWithTextSecureIdentifier:@"successful-recipient-id2" relay:nil]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     TSGroupModel *groupModel = [[TSGroupModel alloc] | 
					
						
							|  |  |  |         initWithTitle:@"group title" | 
					
						
							|  |  |  |             memberIds:[@[ successfulRecipient.uniqueId, successfulRecipient2.uniqueId ] mutableCopy] | 
					
						
							|  |  |  |                 image:nil | 
					
						
							|  |  |  |               groupId:groupIdData]; | 
					
						
							|  |  |  |     TSGroupThread *groupThread = [TSGroupThread getOrCreateThreadWithGroupModel:groupModel]; | 
					
						
							|  |  |  |     TSOutgoingMessage *message = [[TSOutgoingMessage alloc] initWithTimestamp:1 | 
					
						
							|  |  |  |                                                                      inThread:groupThread | 
					
						
							|  |  |  |                                                                   messageBody:@"We want punks in the palace."]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     XCTestExpectation *markedAsSent = [self expectationWithDescription:@"markedAsSent"]; | 
					
						
							|  |  |  |     [messageSender sendMessage:message | 
					
						
							|  |  |  |         success:^{ | 
					
						
							|  |  |  |             if (message.messageState == TSOutgoingMessageStateSentToService) { | 
					
						
							|  |  |  |                 [markedAsSent fulfill]; | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 XCTFail(@"Unexpected message state"); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         failure:^(NSError *_Nonnull error) { | 
					
						
							|  |  |  |             XCTFail(@"sendMessage should not fail."); | 
					
						
							|  |  |  |         }]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     [self waitForExpectationsWithTimeout:5 handler:nil]; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | - (void)testGetRecipients | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     SignalRecipient *recipient = [[SignalRecipient alloc] initWithTextSecureIdentifier:@"fake-recipient-id" relay:nil]; | 
					
						
							|  |  |  |     [recipient save]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     OWSMessageSender *messageSender = self.successfulMessageSender; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // At the time of writing this test, the ContactsUpdater was relying on global singletons. So if this test | 
					
						
							|  |  |  |     // later fails due to network access that could be why. | 
					
						
							|  |  |  |     messageSender.contactsUpdater = [ContactsUpdater sharedUpdater]; | 
					
						
							|  |  |  |     NSError *error; | 
					
						
							|  |  |  |     NSArray<SignalRecipient *> *recipients = [messageSender getRecipients:@[ recipient.uniqueId ] error:&error]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     XCTAssertNil(error); | 
					
						
							|  |  |  |     XCTAssertEqualObjects(recipient, recipients.firstObject); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | NS_ASSUME_NONNULL_END |