mirror of https://github.com/oxen-io/session-ios
				
				
				
			
			You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			547 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			Objective-C
		
	
			
		
		
	
	
			547 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			Objective-C
		
	
| //
 | |
| //  Copyright (c) 2019 Open Whisper Systems. All rights reserved.
 | |
| //
 | |
| 
 | |
| #import "OWSMessageSender.h"
 | |
| #import "NSError+MessageSending.h"
 | |
| #import "OWSDisappearingMessagesConfiguration.h"
 | |
| #import "OWSError.h"
 | |
| #import "OWSFakeNetworkManager.h"
 | |
| #import "OWSPrimaryStorage.h"
 | |
| #import "OWSUploadOperation.h"
 | |
| #import "SSKBaseTestObjC.h"
 | |
| #import "TSAccountManager.h"
 | |
| #import "TSContactThread.h"
 | |
| #import "TSGroupModel.h"
 | |
| #import "TSGroupThread.h"
 | |
| #import "TSNetworkManager.h"
 | |
| #import "TSOutgoingMessage.h"
 | |
| #import "TSRequest.h"
 | |
| #import <SessionAxolotlKit/AxolotlExceptions.h>
 | |
| #import <SessionAxolotlKit/SessionBuilder.h>
 | |
| #import <SessionCoreKit/Cryptography.h>
 | |
| 
 | |
| NS_ASSUME_NONNULL_BEGIN
 | |
| 
 | |
| #ifdef BROKEN_TESTS
 | |
| 
 | |
| @interface OWSUploadOperation (Testing)
 | |
| 
 | |
| - (instancetype)initWithAttachmentId:(NSString *)attachmentId
 | |
|                         dbConnection:(YapDatabaseConnection *)dbConnection
 | |
|                       networkManager:(TSNetworkManager *)networkManager;
 | |
| 
 | |
| @end
 | |
| 
 | |
| #pragma mark -
 | |
| 
 | |
| @interface OWSMessageSender (Testing)
 | |
| 
 | |
| @property (nonatomic) OWSUploadOperation *uploadingService;
 | |
| 
 | |
| - (void)sendMessageToService:(TSOutgoingMessage *)message
 | |
|                      success:(void (^)(void))successHandler
 | |
|                      failure:(RetryableFailureHandler)failureHandler;
 | |
| 
 | |
| @end
 | |
| 
 | |
| #pragma mark -
 | |
| 
 | |
| @implementation OWSMessageSender (Testing)
 | |
| 
 | |
| - (NSArray<NSDictionary *> *)deviceMessages:(TSOutgoingMessage *)message
 | |
|                                forRecipient:(SignalRecipient *)recipient
 | |
|                                    inThread:(TSThread *)thread
 | |
| {
 | |
|     OWSLogInfo(@"[OWSFakeMessagesManager] Faking deviceMessages.");
 | |
|     return @[];
 | |
| }
 | |
| 
 | |
| - (void)setUploadingService:(OWSUploadingService *)uploadingService
 | |
| {
 | |
|     _uploadingService = uploadingService;
 | |
| }
 | |
| 
 | |
| - (OWSUploadingService *)uploadingService
 | |
| {
 | |
|     return _uploadingService;
 | |
| }
 | |
| 
 | |
| @end
 | |
| 
 | |
| #pragma mark -
 | |
| 
 | |
| @interface OWSFakeUploadingService : OWSUploadOperation
 | |
| 
 | |
| @property (nonatomic, readonly) BOOL shouldSucceed;
 | |
| 
 | |
| @end
 | |
| 
 | |
| #pragma mark -
 | |
| 
 | |
| @implementation OWSFakeUploadingService
 | |
| 
 | |
| - (instancetype)initWithAttachmentId:(NSString *)attachmentId
 | |
|                         dbConnection:(YapDatabaseConnection *)dbConnection
 | |
|                        shouldSucceed:(BOOL)shouldSucceed
 | |
| {
 | |
|     self =
 | |
|         [super initWithAttachmentId:attachmentId dbConnection:dbConnection networkManager:[OWSFakeNetworkManager new]];
 | |
|     if (!self) {
 | |
|         return self;
 | |
|     }
 | |
| 
 | |
|     _shouldSucceed = shouldSucceed;
 | |
| 
 | |
|     return self;
 | |
| }
 | |
| 
 | |
| - (void)uploadAttachmentStream:(TSAttachmentStream *)attachmentStream
 | |
|                        message:(TSOutgoingMessage *)outgoingMessage
 | |
|                        success:(void (^)(void))successHandler
 | |
|                        failure:(void (^)(NSError *error))failureHandler
 | |
| {
 | |
|     if (self.shouldSucceed) {
 | |
|         successHandler();
 | |
|     } else {
 | |
|         NSError *error = OWSErrorMakeFailedToSendOutgoingMessageError();
 | |
|         [error setIsRetryable:NO];
 | |
| 
 | |
|         failureHandler(error);
 | |
|     }
 | |
| }
 | |
| 
 | |
| @end
 | |
| 
 | |
| #pragma mark -
 | |
| 
 | |
| @interface OWSFakeURLSessionDataTask : NSURLSessionDataTask
 | |
| 
 | |
| @property (copy) NSHTTPURLResponse *response;
 | |
| 
 | |
| - (instancetype)initWithStatusCode:(long)statusCode;
 | |
| 
 | |
| @end
 | |
| 
 | |
| #pragma mark -
 | |
| 
 | |
| @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
 | |
| 
 | |
| #pragma mark -
 | |
| 
 | |
| @interface OWSMessageSenderFakeNetworkManager : OWSFakeNetworkManager
 | |
| 
 | |
| - (instancetype)init;
 | |
| - (instancetype)initWithSuccess:(BOOL)shouldSucceed NS_DESIGNATED_INITIALIZER;
 | |
| 
 | |
| @property (nonatomic, readonly) BOOL shouldSucceed;
 | |
| 
 | |
| @end
 | |
| 
 | |
| #pragma mark -
 | |
| 
 | |
| @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
 | |
| {
 | |
|     BOOL isSubmitMessageRequest
 | |
|         = ([request.HTTPMethod isEqualToString:@"PUT"] && [request.URL.path hasPrefix:textSecureMessagesAPI]);
 | |
| 
 | |
|     if (isSubmitMessageRequest) {
 | |
|         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
 | |
| 
 | |
| #pragma mark -
 | |
| 
 | |
| @interface TSAccountManager (Testing)
 | |
| 
 | |
| - (void)storeLocalNumber:(NSString *)localNumber;
 | |
| 
 | |
| @end
 | |
| 
 | |
| #pragma mark -
 | |
| 
 | |
| @interface OWSMessageSenderTest : SSKBaseTestObjC
 | |
| 
 | |
| @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
 | |
| 
 | |
| #pragma mark -
 | |
| 
 | |
| @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] initOutgoingMessageWithTimestamp:1
 | |
|                                                                                 inThread:self.thread
 | |
|                                                                              messageBody:@"outgoing message"
 | |
|                                                                            attachmentIds:[NSMutableArray new]
 | |
|                                                                         expiresInSeconds:0
 | |
|                                                                          expireStartedAt:0
 | |
|                                                                           isVoiceMessage:NO
 | |
|                                                                         groupMetaMessage:TSGroupMetaMessageUnspecified
 | |
|                                                                            quotedMessage:nil
 | |
|                                                                             contactShare:nil
 | |
|                                                                              linkPreview:nil];
 | |
|     [self.unexpiringMessage save];
 | |
| 
 | |
|     self.expiringMessage = [[TSOutgoingMessage alloc] initOutgoingMessageWithTimestamp:1
 | |
|                                                                               inThread:self.thread
 | |
|                                                                            messageBody:@"outgoing message"
 | |
|                                                                          attachmentIds:[NSMutableArray new]
 | |
|                                                                       expiresInSeconds:30
 | |
|                                                                        expireStartedAt:0
 | |
|                                                                         isVoiceMessage:NO
 | |
|                                                                       groupMetaMessage:TSGroupMetaMessageUnspecified
 | |
|                                                                          quotedMessage:nil
 | |
|                                                                           contactShare:nil
 | |
|                                                                            linkPreview:nil];
 | |
|     [self.expiringMessage save];
 | |
| 
 | |
|     OWSPrimaryStorage *storageManager = [OWSPrimaryStorage sharedManager];
 | |
|     OWSFakeContactsManager *contactsManager = [OWSFakeContactsManager new];
 | |
| 
 | |
|     // Successful Sending
 | |
|     TSNetworkManager *successfulNetworkManager = [[OWSMessageSenderFakeNetworkManager alloc] initWithSuccess:YES];
 | |
|     self.successfulMessageSender = [[OWSMessageSender alloc] initWithNetworkManager:successfulNetworkManager
 | |
|                                                                      primaryStorage:primaryStorage
 | |
|                                                                     contactsManager:contactsManager];
 | |
| 
 | |
|     // Unsuccessful Sending
 | |
|     TSNetworkManager *unsuccessfulNetworkManager = [[OWSMessageSenderFakeNetworkManager alloc] initWithSuccess:NO];
 | |
|     self.unsuccessfulMessageSender = [[OWSMessageSender alloc] initWithNetworkManager:unsuccessfulNetworkManager
 | |
|                                                                        primaryStorage:primaryStorage
 | |
|                                                                       contactsManager:contactsManager];
 | |
| }
 | |
| 
 | |
| - (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) {
 | |
|                                      OWSLogInfo(@"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 sendMessageToService: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) {
 | |
|                                      OWSLogInfo(@"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 sendMessageToService: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 outgoingMessageInThread:self.thread
 | |
|                                                                 messageBody:@"We want punks in the palace."
 | |
|                                                                attachmentId:nil];
 | |
| 
 | |
|     XCTestExpectation *markedAsSent = [self expectationWithDescription:@"markedAsSent"];
 | |
|     [messageSender sendMessageToService:message
 | |
|         success:^() {
 | |
|             if (message.messageState == TSOutgoingMessageStateSent) {
 | |
|                 [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;
 | |
| 
 | |
|     NSError *error;
 | |
|     NSArray<SignalRecipient *> *recipients = [messageSender getRecipients:@[ recipient.uniqueId ] error:&error];
 | |
| 
 | |
|     XCTAssertNil(error);
 | |
|     XCTAssertEqualObjects(recipient, recipients.firstObject);
 | |
| }
 | |
| 
 | |
| @end
 | |
| 
 | |
| #endif
 | |
| 
 | |
| NS_ASSUME_NONNULL_END
 |