diff --git a/Example/TSKitiOSTestApp/TSKitiOSTestApp.xcodeproj/project.pbxproj b/Example/TSKitiOSTestApp/TSKitiOSTestApp.xcodeproj/project.pbxproj index 175c9ef48..fe621f784 100644 --- a/Example/TSKitiOSTestApp/TSKitiOSTestApp.xcodeproj/project.pbxproj +++ b/Example/TSKitiOSTestApp/TSKitiOSTestApp.xcodeproj/project.pbxproj @@ -12,6 +12,9 @@ 450E3C9A1D96DD2600BF4EB6 /* OWSDisappearingMessagesJobTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 450E3C991D96DD2600BF4EB6 /* OWSDisappearingMessagesJobTest.m */; }; 452EE6CF1D4A754C00E934BA /* TSThreadTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 452EE6CE1D4A754C00E934BA /* TSThreadTest.m */; }; 452EE6D51D4AC43300E934BA /* OWSOrphanedDataCleanerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 452EE6D41D4AC43300E934BA /* OWSOrphanedDataCleanerTest.m */; }; + 453E1FCF1DA8313100DDD7B7 /* OWSMessageSenderTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 453E1FCE1DA8313100DDD7B7 /* OWSMessageSenderTest.m */; }; + 453E1FD81DA83E1000DDD7B7 /* OWSFakeContactsManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 453E1FD71DA83E1000DDD7B7 /* OWSFakeContactsManager.m */; }; + 453E1FDB1DA83EFB00DDD7B7 /* OWSFakeContactsUpdater.m in Sources */ = {isa = PBXBuildFile; fileRef = 453E1FDA1DA83EFB00DDD7B7 /* OWSFakeContactsUpdater.m */; }; 454021ED1D960ABF00F2126D /* OWSDisappearingMessageFinderTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 454021EC1D960ABF00F2126D /* OWSDisappearingMessageFinderTest.m */; }; 45458B751CC342B600A02153 /* SignedPreKeyDeletionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 45458B6A1CC342B600A02153 /* SignedPreKeyDeletionTests.m */; }; 45458B761CC342B600A02153 /* TSAttachmentsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 45458B6C1CC342B600A02153 /* TSAttachmentsTest.m */; }; @@ -21,6 +24,7 @@ 45458B7A1CC342B600A02153 /* TSStorageSignedPreKeyStore.m in Sources */ = {isa = PBXBuildFile; fileRef = 45458B711CC342B600A02153 /* TSStorageSignedPreKeyStore.m */; }; 45458B7B1CC342B600A02153 /* CryptographyTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 45458B731CC342B600A02153 /* CryptographyTests.m */; }; 45458B7C1CC342B600A02153 /* MessagePaddingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 45458B741CC342B600A02153 /* MessagePaddingTests.m */; }; + 45731A6C1DA87AA1007E22AA /* TSOutgoingMessageTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 45731A6B1DA87AA1007E22AA /* TSOutgoingMessageTest.m */; }; 459850C11D22C6F2006FFEDB /* PhoneNumberTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 459850C01D22C6F2006FFEDB /* PhoneNumberTest.m */; }; 45A856AC1D220BFF0056CD4D /* TSAttributesTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 45A856AB1D220BFF0056CD4D /* TSAttributesTest.m */; }; 45B700971D9841E400269FFD /* OWSDisappearingMessagesConfigurationTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 45B700961D9841E400269FFD /* OWSDisappearingMessagesConfigurationTest.m */; }; @@ -56,6 +60,11 @@ 450E3C991D96DD2600BF4EB6 /* OWSDisappearingMessagesJobTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSDisappearingMessagesJobTest.m; path = ../../../tests/Messages/OWSDisappearingMessagesJobTest.m; sourceTree = ""; }; 452EE6CE1D4A754C00E934BA /* TSThreadTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSThreadTest.m; path = ../../../tests/Contacts/TSThreadTest.m; sourceTree = ""; }; 452EE6D41D4AC43300E934BA /* OWSOrphanedDataCleanerTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSOrphanedDataCleanerTest.m; sourceTree = ""; }; + 453E1FCE1DA8313100DDD7B7 /* OWSMessageSenderTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSMessageSenderTest.m; path = ../../../tests/Messages/OWSMessageSenderTest.m; sourceTree = ""; }; + 453E1FD61DA83E1000DDD7B7 /* OWSFakeContactsManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSFakeContactsManager.h; path = ../../../tests/TestSupport/Fakes/OWSFakeContactsManager.h; sourceTree = ""; }; + 453E1FD71DA83E1000DDD7B7 /* OWSFakeContactsManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSFakeContactsManager.m; path = ../../../tests/TestSupport/Fakes/OWSFakeContactsManager.m; sourceTree = ""; }; + 453E1FD91DA83EFB00DDD7B7 /* OWSFakeContactsUpdater.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSFakeContactsUpdater.h; path = ../../../tests/TestSupport/Fakes/OWSFakeContactsUpdater.h; sourceTree = ""; }; + 453E1FDA1DA83EFB00DDD7B7 /* OWSFakeContactsUpdater.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSFakeContactsUpdater.m; path = ../../../tests/TestSupport/Fakes/OWSFakeContactsUpdater.m; sourceTree = ""; }; 454021EC1D960ABF00F2126D /* OWSDisappearingMessageFinderTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSDisappearingMessageFinderTest.m; path = ../../../tests/Messages/OWSDisappearingMessageFinderTest.m; sourceTree = ""; }; 45458B6A1CC342B600A02153 /* SignedPreKeyDeletionTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SignedPreKeyDeletionTests.m; sourceTree = ""; }; 45458B6C1CC342B600A02153 /* TSAttachmentsTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSAttachmentsTest.m; sourceTree = ""; }; @@ -65,6 +74,7 @@ 45458B711CC342B600A02153 /* TSStorageSignedPreKeyStore.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSStorageSignedPreKeyStore.m; sourceTree = ""; }; 45458B731CC342B600A02153 /* CryptographyTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CryptographyTests.m; sourceTree = ""; }; 45458B741CC342B600A02153 /* MessagePaddingTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MessagePaddingTests.m; sourceTree = ""; }; + 45731A6B1DA87AA1007E22AA /* TSOutgoingMessageTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSOutgoingMessageTest.m; path = ../../../tests/Messages/Interactions/TSOutgoingMessageTest.m; sourceTree = ""; }; 459850C01D22C6F2006FFEDB /* PhoneNumberTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PhoneNumberTest.m; path = ../../../tests/Contacts/PhoneNumberTest.m; sourceTree = ""; }; 459FE0DA1D4AD49E00E1071A /* TSKitiOSTestApp-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "TSKitiOSTestApp-Prefix.pch"; sourceTree = ""; }; 45A856AB1D220BFF0056CD4D /* TSAttributesTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSAttributesTest.m; sourceTree = ""; }; @@ -111,6 +121,25 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 453E1FD41DA83DD500DDD7B7 /* TestSupport */ = { + isa = PBXGroup; + children = ( + 453E1FD51DA83DDC00DDD7B7 /* Fakes */, + ); + name = TestSupport; + sourceTree = ""; + }; + 453E1FD51DA83DDC00DDD7B7 /* Fakes */ = { + isa = PBXGroup; + children = ( + 453E1FD61DA83E1000DDD7B7 /* OWSFakeContactsManager.h */, + 453E1FD71DA83E1000DDD7B7 /* OWSFakeContactsManager.m */, + 453E1FD91DA83EFB00DDD7B7 /* OWSFakeContactsUpdater.h */, + 453E1FDA1DA83EFB00DDD7B7 /* OWSFakeContactsUpdater.m */, + ); + name = Fakes; + sourceTree = ""; + }; 45458B691CC342B600A02153 /* Account */ = { isa = PBXGroup; children = ( @@ -178,6 +207,7 @@ 45C6A0981D2F0264007D8AC0 /* Interactions */, 45046FDF1D95A6130015EFF2 /* TSMessagesManagerTest.m */, 454021EC1D960ABF00F2126D /* OWSDisappearingMessageFinderTest.m */, + 453E1FCE1DA8313100DDD7B7 /* OWSMessageSenderTest.m */, ); name = Messages; sourceTree = ""; @@ -186,6 +216,7 @@ isa = PBXGroup; children = ( 45C6A0991D2F029B007D8AC0 /* TSMessageTest.m */, + 45731A6B1DA87AA1007E22AA /* TSOutgoingMessageTest.m */, ); name = Interactions; sourceTree = ""; @@ -268,6 +299,7 @@ B6273DED1C13A2E500738558 /* TSKitiOSTestAppTests */ = { isa = PBXGroup; children = ( + 453E1FD41DA83DD500DDD7B7 /* TestSupport */, 45458B691CC342B600A02153 /* Account */, 45458B6B1CC342B600A02153 /* Attachments */, 459850BF1D22C6C4006FFEDB /* Contacts */, @@ -503,13 +535,17 @@ 452EE6CF1D4A754C00E934BA /* TSThreadTest.m in Sources */, 45458B761CC342B600A02153 /* TSAttachmentsTest.m in Sources */, 45C6A09A1D2F029B007D8AC0 /* TSMessageTest.m in Sources */, + 453E1FCF1DA8313100DDD7B7 /* OWSMessageSenderTest.m in Sources */, 459850C11D22C6F2006FFEDB /* PhoneNumberTest.m in Sources */, 45458B7A1CC342B600A02153 /* TSStorageSignedPreKeyStore.m in Sources */, + 453E1FDB1DA83EFB00DDD7B7 /* OWSFakeContactsUpdater.m in Sources */, + 453E1FD81DA83E1000DDD7B7 /* OWSFakeContactsManager.m in Sources */, 454021ED1D960ABF00F2126D /* OWSDisappearingMessageFinderTest.m in Sources */, 45458B771CC342B600A02153 /* TSMessageStorageTests.m in Sources */, 45046FE01D95A6130015EFF2 /* TSMessagesManagerTest.m in Sources */, 45458B7C1CC342B600A02153 /* MessagePaddingTests.m in Sources */, 45A856AC1D220BFF0056CD4D /* TSAttributesTest.m in Sources */, + 45731A6C1DA87AA1007E22AA /* TSOutgoingMessageTest.m in Sources */, 6323E339D5B8F4CB77EB3ED4 /* SignalRecipientTest.m in Sources */, 6323E1F7730289398452E5C5 /* OWSFingerprintTest.m in Sources */, ); diff --git a/src/Messages/Interactions/TSMessage.h b/src/Messages/Interactions/TSMessage.h index 799b4de05..d308fa19d 100644 --- a/src/Messages/Interactions/TSMessage.h +++ b/src/Messages/Interactions/TSMessage.h @@ -26,6 +26,7 @@ typedef NS_ENUM(NSInteger, TSGroupMetaMessage) { @property (nonatomic) uint64_t expireStartedAt; @property (nonatomic, readonly) uint64_t expiresAt; @property (nonatomic, readonly) BOOL isExpiringMessage; +@property (nonatomic, readonly) BOOL shouldStartExpireTimer; - (instancetype)initWithTimestamp:(uint64_t)timestamp; diff --git a/src/Messages/Interactions/TSMessage.m b/src/Messages/Interactions/TSMessage.m index 65aad7987..2b9084236 100644 --- a/src/Messages/Interactions/TSMessage.m +++ b/src/Messages/Interactions/TSMessage.m @@ -146,6 +146,11 @@ static const NSUInteger OWSMessageSchemaVersion = 3; [self updateExpiresAt]; } +- (BOOL)shouldStartExpireTimer +{ + return self.isExpiringMessage; +} + // TODO a downloaded media doesn't start counting until download is complete. - (void)updateExpiresAt { diff --git a/src/Messages/Interactions/TSOutgoingMessage.m b/src/Messages/Interactions/TSOutgoingMessage.m index c0430e662..a0e14e651 100644 --- a/src/Messages/Interactions/TSOutgoingMessage.m +++ b/src/Messages/Interactions/TSOutgoingMessage.m @@ -52,13 +52,12 @@ NS_ASSUME_NONNULL_BEGIN attachmentIds:(NSMutableArray *)attachmentIds expiresInSeconds:(uint32_t)expiresInSeconds { - uint64_t now = [NSDate ows_millisecondTimeStamp]; return [self initWithTimestamp:timestamp inThread:thread messageBody:body attachmentIds:attachmentIds expiresInSeconds:expiresInSeconds - expireStartedAt:now]; + expireStartedAt:0]; } - (instancetype)initWithTimestamp:(uint64_t)timestamp @@ -95,6 +94,18 @@ NS_ASSUME_NONNULL_BEGIN return self.thread.contactIdentifier; } +- (BOOL)shouldStartExpireTimer +{ + switch (self.messageState) { + case TSOutgoingMessageStateSent: + case TSOutgoingMessageStateDelivered: + return self.isExpiringMessage; + case TSOutgoingMessageStateAttemptingOut: + case TSOutgoingMessageStateUnsent: + return NO; + } +} + - (OWSSignalServiceProtosDataMessageBuilder *)dataMessageBuilder { TSThread *thread = self.thread; diff --git a/src/Messages/OWSDisappearingMessagesFinder.m b/src/Messages/OWSDisappearingMessagesFinder.m index 64c411d91..7c243ccd7 100644 --- a/src/Messages/OWSDisappearingMessagesFinder.m +++ b/src/Messages/OWSDisappearingMessagesFinder.m @@ -4,6 +4,7 @@ #import "OWSDisappearingMessagesFinder.h" #import "NSDate+millisecondTimeStamp.h" #import "TSMessage.h" +#import "TSOutgoingMessage.h" #import "TSStorageManager.h" #import "TSThread.h" #import @@ -14,7 +15,7 @@ NS_ASSUME_NONNULL_BEGIN static NSString *const OWSDisappearingMessageFinderThreadIdColumn = @"thread_id"; static NSString *const OWSDisappearingMessageFinderExpiresAtColumn = @"expires_at"; -static NSString *const OWSDisappearingMessageFinderExpiresAtIndex = @"index_messages_on_expires_at_and_thread_id"; +static NSString *const OWSDisappearingMessageFinderExpiresAtIndex = @"index_messages_on_expires_at_and_thread_id_v2"; @interface OWSDisappearingMessagesFinder () @@ -184,13 +185,17 @@ static NSString *const OWSDisappearingMessageFinderExpiresAtIndex = @"index_mess NSString *collection, NSString *key, id object) { - if ([object isKindOfClass:[TSMessage class]]) { - TSMessage *message = (TSMessage *)object; - if (message.expiresInSeconds > 0) { - dict[OWSDisappearingMessageFinderExpiresAtColumn] = @(message.expiresAt); - dict[OWSDisappearingMessageFinderThreadIdColumn] = message.uniqueThreadId; - } // else don't index non-expiring messages. + if (![object isKindOfClass:[TSMessage class]]) { + return; } + TSMessage *message = (TSMessage *)object; + + if (!message.shouldStartExpireTimer) { + return; + } + + dict[OWSDisappearingMessageFinderExpiresAtColumn] = @(message.expiresAt); + dict[OWSDisappearingMessageFinderThreadIdColumn] = message.uniqueThreadId; }]; return [[YapDatabaseSecondaryIndex alloc] initWithSetup:setup handler:handler]; diff --git a/src/Messages/OWSMessageSender.h b/src/Messages/OWSMessageSender.h new file mode 100644 index 000000000..2d69b87bd --- /dev/null +++ b/src/Messages/OWSMessageSender.h @@ -0,0 +1,24 @@ +// Created by Michael Kirk on 10/7/16. +// Copyright © 2016 Open Whisper Systems. All rights reserved. + +NS_ASSUME_NONNULL_BEGIN + +@class TSOutgoingMessage; +@class TSNetworkManager; +@class TSStorageManager; +@class ContactsUpdater; +@protocol ContactsManagerProtocol; + +@interface OWSMessageSender : NSObject + +- (instancetype)initWithMessage:(TSOutgoingMessage *)message + networkManager:(TSNetworkManager *)networkManager + storageManager:(TSStorageManager *)storageManager + contactsManager:(id)contactsManager + contactsUpdater:(ContactsUpdater *)contactsUpdater; + +- (void)sendWithSuccess:(void (^)())successBlock failure:(void (^)(NSError *error))failureBlock; + +@end + +NS_ASSUME_NONNULL_END diff --git a/src/Messages/OWSMessageSender.m b/src/Messages/OWSMessageSender.m new file mode 100644 index 000000000..7e4094ec6 --- /dev/null +++ b/src/Messages/OWSMessageSender.m @@ -0,0 +1,60 @@ +// Created by Michael Kirk on 10/7/16. +// Copyright © 2016 Open Whisper Systems. All rights reserved. + +#import "OWSMessageSender.h" +#import "OWSError.h" +#import "TSMessagesManager+sendMessages.h" +#import "TSNetworkManager.h" +#import "TSOutgoingMessage.h" +#import "TSThread.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface OWSMessageSender () + +@property (nonatomic, readonly) TSOutgoingMessage *message; +@property (nonatomic, readonly) TSNetworkManager *networkManager; +@property (nonatomic, readonly) TSMessagesManager *messagesManager; + +@end + +@implementation OWSMessageSender + +- (instancetype)initWithMessage:(TSOutgoingMessage *)message + networkManager:(TSNetworkManager *)networkManager + storageManager:(TSStorageManager *)storageManager + contactsManager:(id)contactsManager + contactsUpdater:(ContactsUpdater *)contactsUpdater +{ + self = [super init]; + if (!self) { + return self; + } + + _message = message; + _networkManager = networkManager; + _messagesManager = [[TSMessagesManager alloc] initWithNetworkManager:networkManager + storageManager:storageManager + contactsManager:contactsManager + contactsUpdater:contactsUpdater]; + return self; +} + +- (void)sendWithSuccess:(void (^)())successBlock failure:(void (^)(NSError *error))failureBlock +{ + [self.messagesManager sendMessage:self.message + inThread:self.message.thread + success:^{ + successBlock(); + } + failure:^{ + NSString *localizedError + = NSLocalizedString(@"NOTIFICATION_SEND_FAILED", @"Generic notice when message failed to send."); + NSError *error = OWSErrorWithCodeDescription(OWSErrorCodeFailedToSendOutgoingMessage, localizedError); + failureBlock(error); + }]; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/src/Messages/TSMessagesManager+sendMessages.m b/src/Messages/TSMessagesManager+sendMessages.m index 1895970ea..53e832ea2 100644 --- a/src/Messages/TSMessagesManager+sendMessages.m +++ b/src/Messages/TSMessagesManager+sendMessages.m @@ -137,12 +137,8 @@ dispatch_queue_t sendingQueue() { ? [TSAccountManager localNumber] : contactThread.contactIdentifier; - __block SignalRecipient *recipient; - [self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { - recipient = [SignalRecipient recipientWithTextSecureIdentifier:recipientContactId - withTransaction:transaction]; - }]; - + __block SignalRecipient *recipient = + [SignalRecipient recipientWithTextSecureIdentifier:recipientContactId]; if (!recipient) { [self.contactsUpdater synchronousLookup:contactThread.contactIdentifier success:^(SignalRecipient *recip) { @@ -258,7 +254,7 @@ dispatch_queue_t sendingQueue() { self.logTag, exception); [self processException:exception outgoingMessage:message inThread:thread]; - return; + BLOCK_SAFE_RUN(failureBlock); } } @@ -317,6 +313,7 @@ dispatch_queue_t sendingQueue() { if (!responseData) { DDLogWarn(@"Stale devices but server didn't specify devices in response."); + BLOCK_SAFE_RUN(failureBlock); return; } diff --git a/src/Protocols/ContactsManagerProtocol.h b/src/Protocols/ContactsManagerProtocol.h index 318374d14..68b88a139 100644 --- a/src/Protocols/ContactsManagerProtocol.h +++ b/src/Protocols/ContactsManagerProtocol.h @@ -3,6 +3,7 @@ @class PhoneNumber; @class Contact; +@class UIImage; @protocol ContactsManagerProtocol diff --git a/src/Util/OWSError.h b/src/Util/OWSError.h index ec1d054f2..8f4bd93a7 100644 --- a/src/Util/OWSError.h +++ b/src/Util/OWSError.h @@ -10,8 +10,8 @@ typedef NS_ENUM(NSInteger, OWSErrorCode) { OWSErrorCodeFailedToDecodeJson = 13, OWSErrorCodeFailedToEncodeJson = 14, OWSErrorCodeFailedToDecodeQR = 15, - OWSErrorCodePrivacyVerificationFailure = 20 - + OWSErrorCodePrivacyVerificationFailure = 20, + OWSErrorCodeFailedToSendOutgoingMessage = 30 }; extern NSError *OWSErrorWithCodeDescription(OWSErrorCode code, NSString *description); diff --git a/tests/Messages/Interactions/TSOutgoingMessageTest.m b/tests/Messages/Interactions/TSOutgoingMessageTest.m new file mode 100644 index 000000000..8ef856ea6 --- /dev/null +++ b/tests/Messages/Interactions/TSOutgoingMessageTest.m @@ -0,0 +1,77 @@ +// Created by Michael Kirk on 10/7/16. +// Copyright © 2016 Open Whisper Systems. All rights reserved. + +#import "TSContactThread.h" +#import "TSOutgoingMessage.h" +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface TSOutgoingMessageTest : XCTestCase + +@property (nonatomic) TSContactThread *thread; + +@end + +@implementation TSOutgoingMessageTest + +- (void)setUp +{ + [super setUp]; + self.thread = [[TSContactThread alloc] initWithUniqueId:@"fake-thread-id"]; +} + +- (void)testShouldNotStartExpireTimerWithMessageThatDoesNotExpire +{ + TSOutgoingMessage *message = [[TSOutgoingMessage alloc] initWithTimestamp:100 inThread:self.thread messageBody:nil]; + XCTAssertFalse(message.shouldStartExpireTimer); +} + +- (void)testShouldStartExpireTimerWithSentMessage +{ + TSOutgoingMessage *message = [[TSOutgoingMessage alloc] initWithTimestamp:100 + inThread:self.thread + messageBody:nil + attachmentIds:[NSMutableArray new] + expiresInSeconds:10]; + message.messageState = TSOutgoingMessageStateSent; + XCTAssert(message.shouldStartExpireTimer); +} + +- (void)testShouldStartExpireTimerWithDeliveredMessage +{ + TSOutgoingMessage *message = [[TSOutgoingMessage alloc] initWithTimestamp:100 + inThread:self.thread + messageBody:nil + attachmentIds:[NSMutableArray new] + expiresInSeconds:10]; + message.messageState = TSOutgoingMessageStateDelivered; + XCTAssert(message.shouldStartExpireTimer); +} + +- (void)testShouldNotStartExpireTimerWithUnsentMessage +{ + TSOutgoingMessage *message = [[TSOutgoingMessage alloc] initWithTimestamp:100 + inThread:self.thread + messageBody:nil + attachmentIds:[NSMutableArray new] + expiresInSeconds:10]; + message.messageState = TSOutgoingMessageStateUnsent; + XCTAssertFalse(message.shouldStartExpireTimer); +} + +- (void)testShouldNotStartExpireTimerWithAttemptingOutMessage +{ + TSOutgoingMessage *message = [[TSOutgoingMessage alloc] initWithTimestamp:100 + inThread:self.thread + messageBody:nil + attachmentIds:[NSMutableArray new] + expiresInSeconds:10]; + message.messageState = TSOutgoingMessageStateAttemptingOut; + XCTAssertFalse(message.shouldStartExpireTimer); +} + + +@end + +NS_ASSUME_NONNULL_END diff --git a/tests/Messages/OWSMessageSenderTest.m b/tests/Messages/OWSMessageSenderTest.m new file mode 100644 index 000000000..c7a4dc1f5 --- /dev/null +++ b/tests/Messages/OWSMessageSenderTest.m @@ -0,0 +1,170 @@ +// Created by Michael Kirk on 10/7/16. +// Copyright © 2016 Open Whisper Systems. All rights reserved. + +#import "OWSError.h" +#import "OWSFakeContactsManager.h" +#import "OWSFakeContactsUpdater.h" +#import "OWSMessageSender.h" +#import "TSContactThread.h" +#import "TSNetworkManager.h" +#import "TSOutgoingMessage.h" +#import "TSStorageManager.h" +#import + +NS_ASSUME_NONNULL_BEGIN + +@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 : TSNetworkManager + +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initWithSuccess:(BOOL)shouldSucceed NS_DESIGNATED_INITIALIZER; + +@property (nonatomic, readonly) BOOL shouldSucceed; + +@end + +@implementation OWSMessageSenderFakeNetworkManager + +- (instancetype)initWithSuccess:(BOOL)shouldSucceed +{ + // intentionally skipping super init which explodes without setup. + _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 { + NSLog(@"Ignoring unhandled request: %@", request); + } +} + +@end + +@interface OWSMessageSenderTest : XCTestCase + +@property (nonatomic) TSThread *thread; +@property (nonatomic) TSOutgoingMessage *expiringMessage; +@property (nonatomic) OWSMessageSenderFakeNetworkManager *networkManager; + +@end + +@implementation OWSMessageSenderTest + +- (void)setUp +{ + [super setUp]; + + self.thread = [[TSContactThread alloc] initWithUniqueId:@"fake-thread-id"]; + [self.thread save]; + + self.expiringMessage = [[TSOutgoingMessage alloc] initWithTimestamp:1 + inThread:self.thread + messageBody:@"outgoing message" + attachmentIds:[NSMutableArray new] + expiresInSeconds:30]; + [self.expiringMessage save]; +} + +- (void)testExpiringMessageTimerStartsOnSuccess +{ + TSNetworkManager *networkManager = [[OWSMessageSenderFakeNetworkManager alloc] initWithSuccess:YES]; + OWSMessageSender *messageSender = [[OWSMessageSender alloc] initWithMessage:self.expiringMessage + networkManager:networkManager + storageManager:[TSStorageManager sharedManager] + contactsManager:[OWSFakeContactsManager new] + contactsUpdater:[OWSFakeContactsUpdater new]]; + + // Sanity Check + XCTAssertEqual(0, self.expiringMessage.expiresAt); + + XCTestExpectation *messageStartedExpiration = [self expectationWithDescription:@"messageStartedExpiration"]; + [messageSender sendWithSuccess:^() { + 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)testExpiringMessageTimerDoesNotStartsOnFailure +{ + TSNetworkManager *networkManager = [[OWSMessageSenderFakeNetworkManager alloc] initWithSuccess:NO]; + OWSMessageSender *messageSender = [[OWSMessageSender alloc] initWithMessage:self.expiringMessage + networkManager:networkManager + storageManager:[TSStorageManager sharedManager] + contactsManager:[OWSFakeContactsManager new] + contactsUpdater:[OWSFakeContactsUpdater new]]; + + // Sanity Check + XCTAssertEqual(0, self.expiringMessage.expiresAt); + + XCTestExpectation *messageDidNotStartExpiration = [self expectationWithDescription:@"messageStartedExpiration"]; + [messageSender sendWithSuccess:^() { + 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:^(NSError *error) { + NSLog(@"Wasn't able to verify."); + }]; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/tests/Messages/TSMessagesManagerTest.m b/tests/Messages/TSMessagesManagerTest.m index 5fab8917a..aa77c9ed4 100644 --- a/tests/Messages/TSMessagesManagerTest.m +++ b/tests/Messages/TSMessagesManagerTest.m @@ -3,12 +3,14 @@ #import +#import "ContactsManagerProtocol.h" #import "ContactsUpdater.h" +#import "OWSFakeContactsManager.h" +#import "OWSFakeContactsUpdater.h" #import "OWSSignalServiceProtos.pb.h" #import "TSMessagesManager.h" #import "TSNetworkManager.h" #import "TSStorageManager.h" -#import "ContactsManagerProtocol.h" #import "objc/runtime.h" @interface TSMessagesManagerTest : XCTestCase @@ -93,15 +95,15 @@ - (instancetype)initWithExpectation:(XCTestExpectation *)messageWasSubmitted; -@property XCTestExpectation *messageWasSubmitted; +@property XCTestExpectation *expectation; @end @implementation OWSTSMessagesManagerTestNetworkManager -- (instancetype)initWithExpectation:(XCTestExpectation *)messageWasSubmitted +- (instancetype)initWithExpectation:(XCTestExpectation *)expectation { - _messageWasSubmitted = messageWasSubmitted; + _expectation = expectation; return self; } @@ -114,7 +116,7 @@ NSDictionary *fakeResponse = @{ @"id" : @(1234), @"location" : @"fake-location" }; success(nil, fakeResponse); } else if ([request isKindOfClass:[TSSubmitMessageRequest class]]) { - [self.messageWasSubmitted fulfill]; + [self.expectation fulfill]; } else { NSLog(@"Ignoring unhandled request: %@", request); } @@ -122,56 +124,6 @@ @end -@interface OWSFakeContactsUpdater : ContactsUpdater - -@end - -@implementation OWSFakeContactsUpdater - -- (void)synchronousLookup:(NSString *)identifier - success:(void (^)(SignalRecipient *))success - failure:(void (^)(NSError *error))failure -{ - NSLog(@"Fake contact lookup."); - SignalRecipient *fakeRecipient = - [[SignalRecipient alloc] initWithTextSecureIdentifier:@"fake-recipient-id" relay:nil supportsVoice:YES]; - success(fakeRecipient); -} - -@end - -@interface OWSFakeContactsManager : NSObject - -@end - -/** - * I don't know that this test relys on any of the specific behavior in this implementation. - * We're just trying to avoid setting up/accessing the TSEnv global. ~mjk - */ -@implementation OWSFakeContactsManager - -- (NSString *)nameStringForPhoneIdentifier:(NSString *)phoneNumber -{ - return @"Fake name"; -} - -- (NSArray *)signalContacts -{ - return @[]; -} - -+ (BOOL)name:(NSString *)nameString matchesQuery:(NSString *)queryString -{ - return YES; -} - -- (UIImage *)imageForPhoneIdentifier:(NSString *)phoneNumber -{ - return nil; -} - -@end - @implementation TSMessagesManagerTest - (void)testIncomingSyncContactMessage @@ -202,5 +154,4 @@ }]; } - @end diff --git a/tests/TestSupport/Fakes/OWSFakeContactsManager.h b/tests/TestSupport/Fakes/OWSFakeContactsManager.h new file mode 100644 index 000000000..ac8c8b0a7 --- /dev/null +++ b/tests/TestSupport/Fakes/OWSFakeContactsManager.h @@ -0,0 +1,12 @@ +// Created by Michael Kirk on 10/7/16. +// Copyright © 2016 Open Whisper Systems. All rights reserved. + +#import "ContactsManagerProtocol.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface OWSFakeContactsManager : NSObject + +@end + +NS_ASSUME_NONNULL_END diff --git a/tests/TestSupport/Fakes/OWSFakeContactsManager.m b/tests/TestSupport/Fakes/OWSFakeContactsManager.m new file mode 100644 index 000000000..e3ae874f2 --- /dev/null +++ b/tests/TestSupport/Fakes/OWSFakeContactsManager.m @@ -0,0 +1,34 @@ +// Created by Michael Kirk on 10/7/16. +// Copyright © 2016 Open Whisper Systems. All rights reserved. + +#import "OWSFakeContactsManager.h" + +NS_ASSUME_NONNULL_BEGIN + +@class UIImage; + +@implementation OWSFakeContactsManager + +- (NSString *)nameStringForPhoneIdentifier:(NSString *)phoneNumber +{ + return @"Fake name"; +} + +- (NSArray *)signalContacts +{ + return @[]; +} + ++ (BOOL)name:(NSString *)nameString matchesQuery:(NSString *)queryString +{ + return YES; +} + +- (nullable UIImage *)imageForPhoneIdentifier:(NSString *)phoneNumber +{ + return nil; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/tests/TestSupport/Fakes/OWSFakeContactsUpdater.h b/tests/TestSupport/Fakes/OWSFakeContactsUpdater.h new file mode 100644 index 000000000..9adc1fecf --- /dev/null +++ b/tests/TestSupport/Fakes/OWSFakeContactsUpdater.h @@ -0,0 +1,12 @@ +// Created by Michael Kirk on 10/7/16. +// Copyright © 2016 Open Whisper Systems. All rights reserved. + +#import "ContactsUpdater.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface OWSFakeContactsUpdater : ContactsUpdater + +@end + +NS_ASSUME_NONNULL_END diff --git a/tests/TestSupport/Fakes/OWSFakeContactsUpdater.m b/tests/TestSupport/Fakes/OWSFakeContactsUpdater.m new file mode 100644 index 000000000..2e7d01835 --- /dev/null +++ b/tests/TestSupport/Fakes/OWSFakeContactsUpdater.m @@ -0,0 +1,23 @@ +// Created by Michael Kirk on 10/7/16. +// Copyright © 2016 Open Whisper Systems. All rights reserved. + +#import "OWSFakeContactsUpdater.h" +#import "SignalRecipient.h" + +NS_ASSUME_NONNULL_BEGIN + +@implementation OWSFakeContactsUpdater + +- (void)synchronousLookup:(NSString *)identifier + success:(void (^)(SignalRecipient *))success + failure:(void (^)(NSError *error))failure +{ + NSLog(@"[OWSFakeContactsUpdater] Faking contact lookup."); + SignalRecipient *fakeRecipient = + [[SignalRecipient alloc] initWithTextSecureIdentifier:@"fake-recipient-id" relay:nil supportsVoice:YES]; + success(fakeRecipient); +} + +@end + +NS_ASSUME_NONNULL_END