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.
512 lines
18 KiB
Objective-C
512 lines
18 KiB
Objective-C
//
|
|
// 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
|