Fix contact/group sync messages. (#32)

Initially we don't have device messages for our remote device. We need to send the empty device list to get the updated list of remote devices.

Introduced lots of dependency injection to make MessagesManager more testable.

// FREEBIE
pull/1/head
Michael Kirk 9 years ago committed by GitHub
parent 1824af5335
commit 2dba7d141a

@ -8,6 +8,7 @@
/* Begin PBXBuildFile section */
308D7DFA789594CEA62740D9 /* libPods-TSKitiOSTestAppTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C0DC1A83C39CBC09FB2405A3 /* libPods-TSKitiOSTestAppTests.a */; };
45046FE01D95A6130015EFF2 /* TSMessagesManagerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 45046FDF1D95A6130015EFF2 /* TSMessagesManagerTest.m */; };
452EE6CF1D4A754C00E934BA /* TSThreadTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 452EE6CE1D4A754C00E934BA /* TSThreadTest.m */; };
452EE6D51D4AC43300E934BA /* OWSOrphanedDataCleanerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 452EE6D41D4AC43300E934BA /* OWSOrphanedDataCleanerTest.m */; };
45458B751CC342B600A02153 /* SignedPreKeyDeletionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 45458B6A1CC342B600A02153 /* SignedPreKeyDeletionTests.m */; };
@ -46,6 +47,7 @@
1A50A62A8930EE2BC9B8AC11 /* Pods-TSKitiOSTestApp.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TSKitiOSTestApp.release.xcconfig"; path = "Pods/Target Support Files/Pods-TSKitiOSTestApp/Pods-TSKitiOSTestApp.release.xcconfig"; sourceTree = "<group>"; };
31DFDA8F9523F5B15EA2376B /* Pods-TSKitiOSTestApp.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TSKitiOSTestApp.debug.xcconfig"; path = "Pods/Target Support Files/Pods-TSKitiOSTestApp/Pods-TSKitiOSTestApp.debug.xcconfig"; sourceTree = "<group>"; };
36DA6C703F99948D553F4E3F /* Pods-TSKitiOSTestAppTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TSKitiOSTestAppTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-TSKitiOSTestAppTests/Pods-TSKitiOSTestAppTests.debug.xcconfig"; sourceTree = "<group>"; };
45046FDF1D95A6130015EFF2 /* TSMessagesManagerTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSMessagesManagerTest.m; path = ../../../tests/Messages/TSMessagesManagerTest.m; sourceTree = "<group>"; };
452EE6CE1D4A754C00E934BA /* TSThreadTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSThreadTest.m; sourceTree = "<group>"; };
452EE6D41D4AC43300E934BA /* OWSOrphanedDataCleanerTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSOrphanedDataCleanerTest.m; sourceTree = "<group>"; };
45458B6A1CC342B600A02153 /* SignedPreKeyDeletionTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SignedPreKeyDeletionTests.m; sourceTree = "<group>"; };
@ -154,6 +156,7 @@
isa = PBXGroup;
children = (
45C6A0981D2F0264007D8AC0 /* Interactions */,
45046FDF1D95A6130015EFF2 /* TSMessagesManagerTest.m */,
);
name = Messages;
sourceTree = "<group>";
@ -476,6 +479,7 @@
459850C11D22C6F2006FFEDB /* PhoneNumberTest.m in Sources */,
45458B7A1CC342B600A02153 /* TSStorageSignedPreKeyStore.m in Sources */,
45458B771CC342B600A02153 /* TSMessageStorageTests.m in Sources */,
45046FE01D95A6130015EFF2 /* TSMessagesManagerTest.m in Sources */,
45458B7C1CC342B600A02153 /* MessagePaddingTests.m in Sources */,
45A856AC1D220BFF0056CD4D /* TSAttributesTest.m in Sources */,
6323E339D5B8F4CB77EB3ED4 /* SignalRecipientTest.m in Sources */,

@ -1,10 +1,5 @@
//
// ContactsManager+updater.h
// Signal
//
// Created by Frederic Jacobs on 21/11/15.
// Copyright © 2015 Open Whisper Systems. All rights reserved.
//
#import "SignalRecipient.h"

@ -17,7 +17,7 @@
static dispatch_once_t onceToken;
static id sharedInstance = nil;
dispatch_once(&onceToken, ^{
sharedInstance = [self.class new];
sharedInstance = [self new];
});
return sharedInstance;
}

@ -51,13 +51,13 @@ dispatch_queue_t attachmentsQueue() {
thread = [TSContactThread getOrCreateThreadWithContactId:envelope.source];
}
OWSAttachmentsProcessor *attachmentsProcessor =
[[OWSAttachmentsProcessor alloc] initWithAttachmentPointersProtos:attachmentPointerProtos
timestamp:envelope.timestamp
relay:envelope.relay
avatarGroupId:avatarGroupId
inThread:thread
messagesManager:[TSMessagesManager sharedManager]];
OWSAttachmentsProcessor *attachmentsProcessor = [[OWSAttachmentsProcessor alloc]
initWithAttachmentPointersProtos:attachmentPointerProtos
timestamp:envelope.timestamp
relay:envelope.relay
avatarGroupId:avatarGroupId
inThread:thread
messagesManager:[TSMessagesManager sharedManager]]; // TODO self?
if (attachmentsProcessor.hasSupportedAttachments) {
TSIncomingMessage *possiblyCreatedMessage =
@ -106,7 +106,7 @@ dispatch_queue_t attachmentsQueue() {
success:(successSendingCompletionBlock)successCompletionBlock
failure:(failedSendingCompletionBlock)failedCompletionBlock {
TSRequest *allocateAttachment = [[TSAllocAttachmentRequest alloc] init];
[[TSNetworkManager sharedManager] makeRequest:allocateAttachment
[self.networkManager makeRequest:allocateAttachment
success:^(NSURLSessionDataTask *task, id responseObject) {
dispatch_async(attachmentsQueue(), ^{
if ([responseObject isKindOfClass:[NSDictionary class]]) {
@ -187,7 +187,7 @@ dispatch_queue_t attachmentsQueue() {
TSAttachmentRequest *attachmentRequest =
[[TSAttachmentRequest alloc] initWithId:[attachment identifier] relay:attachment.relay];
[[TSNetworkManager sharedManager] makeRequest:attachmentRequest
[self.networkManager makeRequest:attachmentRequest
success:^(NSURLSessionDataTask *task, id responseObject) {
if ([responseObject isKindOfClass:[NSDictionary class]]) {
dispatch_async(attachmentsQueue(), ^{

@ -26,7 +26,10 @@
#define InvalidDeviceException @"InvalidDeviceException"
@interface TSMessagesManager ()
dispatch_queue_t sendingQueue(void);
@property TSNetworkManager *networkManager;
@end
typedef void (^messagesQueue)(NSArray *messages);
@ -56,7 +59,7 @@ dispatch_queue_t sendingQueue() {
if (!recipient) {
[[self contactUpdater] synchronousLookup:recipientId
[self.contactsUpdater synchronousLookup:recipientId
success:^(SignalRecipient *newRecipient) {
[recipients addObject:newRecipient];
}
@ -78,10 +81,6 @@ dispatch_queue_t sendingQueue() {
return;
}
- (ContactsUpdater *)contactUpdater {
return [ContactsUpdater sharedUpdater];
}
- (void)resendMessage:(TSOutgoingMessage *)message
toRecipient:(SignalRecipient *)recipient
inThread:(TSThread *)thread
@ -142,7 +141,7 @@ dispatch_queue_t sendingQueue() {
}];
if (!recipient) {
[[ContactsUpdater sharedUpdater] synchronousLookup:contactThread.contactIdentifier
[self.contactsUpdater synchronousLookup:contactThread.contactIdentifier
success:^(SignalRecipient *recip) {
recipient = recip;
}
@ -172,7 +171,7 @@ dispatch_queue_t sendingQueue() {
// Special situation: if we are sending to ourselves in a single thread, we treat this as an incoming
// message
[self handleMessageSent:message];
[[TSMessagesManager sharedManager] handleSendToMyself:message];
[[TSMessagesManager sharedManager] handleSendToMyself:message]; // TODO self?
}
}
});
@ -261,26 +260,12 @@ dispatch_queue_t sendingQueue() {
}
}
if (deviceMessages.count == 0) {
DDLogWarn(@"%@ Failed to build any device messages. Not sending.", self.tag);
// Retrying incase we fixed our stale devices last time 'round.
dispatch_async(sendingQueue(), ^{
[self sendMessage:message
toRecipient:recipient
inThread:thread
withAttemps:remainingAttempts
success:successBlock
failure:failureBlock];
});
return;
}
TSSubmitMessageRequest *request = [[TSSubmitMessageRequest alloc] initWithRecipient:recipient.uniqueId
messages:deviceMessages
relay:recipient.relay
timeStamp:message.timestamp];
[[TSNetworkManager sharedManager] makeRequest:request
[self.networkManager makeRequest:request
success:^(NSURLSessionDataTask *task, id responseObject) {
dispatch_async(sendingQueue(), ^{
[recipient save];
@ -456,7 +441,7 @@ dispatch_queue_t sendingQueue() {
__block dispatch_semaphore_t sema = dispatch_semaphore_create(0);
__block PreKeyBundle *bundle;
[[TSNetworkManager sharedManager]
[self.networkManager
makeRequest:[[TSRecipientPrekeyRequest alloc] initWithRecipient:identifier
deviceId:[deviceNumber stringValue]]
success:^(NSURLSessionDataTask *task, id responseObject) {

@ -7,14 +7,22 @@
@class TSCall;
@class YapDatabaseConnection;
@class TSNetworkManager;
@class OWSSignalServiceProtosEnvelope;
@class OWSSignalServiceProtosDataMessage;
@class ContactsUpdater;
@interface TSMessagesManager : NSObject
- (instancetype)initWithNetworkManager:(TSNetworkManager *)networkManager
dbConnection:(YapDatabaseConnection *)dbConnection
contactsUpdater:(ContactsUpdater *)contactsUpdater NS_DESIGNATED_INITIALIZER;
+ (instancetype)sharedManager;
@property (readonly) YapDatabaseConnection *dbConnection;
@property (readonly) TSNetworkManager *networkManager;
@property (readonly) ContactsUpdater *contactsUpdater;
- (void)handleReceivedEnvelope:(OWSSignalServiceProtosEnvelope *)envelope;

@ -3,6 +3,7 @@
#import "TSMessagesManager.h"
#import "ContactsManagerProtocol.h"
#import "ContactsUpdater.h"
#import "MimeTypeUtil.h"
#import "NSData+messagePadding.h"
#import "OWSIncomingSentMessageTranscript.h"
@ -19,6 +20,7 @@
#import "TSInfoMessage.h"
#import "TSInvalidIdentityKeyReceivingErrorMessage.h"
#import "TSMessagesManager+attachments.h"
#import "TSNetworkManager.h"
#import "TSStorageHeaders.h"
#import "TextSecureKitEnv.h"
#import <AxolotlKit/AxolotlExceptions.h>
@ -35,13 +37,27 @@
return sharedMyManager;
}
- (instancetype)init {
- (instancetype)init
{
return [self initWithNetworkManager:[TSNetworkManager sharedManager]
dbConnection:[TSStorageManager sharedManager].newDatabaseConnection
contactsUpdater:[ContactsUpdater sharedUpdater]];
}
- (instancetype)initWithNetworkManager:(TSNetworkManager *)networkManager
dbConnection:(YapDatabaseConnection *)dbConnection
contactsUpdater:(ContactsUpdater *)contactsUpdater
{
self = [super init];
if (self) {
_dbConnection = [TSStorageManager sharedManager].newDatabaseConnection;
if (!self) {
return self;
}
_networkManager = networkManager;
_dbConnection = dbConnection;
_contactsUpdater = contactsUpdater;
return self;
}

@ -0,0 +1,171 @@
// Created by Michael Kirk on 9/23/16.
// Copyright © 2016 Open Whisper Systems. All rights reserved.
#import <XCTest/XCTest.h>
#import "ContactsUpdater.h"
#import "OWSSignalServiceProtos.pb.h"
#import "TSMessagesManager.h"
#import "TSNetworkManager.h"
#import "TSStorageManager.h"
#import "objc/runtime.h"
@interface TSMessagesManagerTest : XCTestCase
@end
@interface TSMessagesManager (Testing)
// private method we are testing
- (void)handleIncomingEnvelope:(OWSSignalServiceProtosEnvelope *)messageEnvelope
withSyncMessage:(OWSSignalServiceProtosSyncMessage *)syncMessage;
// private method we are stubbing via swizzle.
- (BOOL)uploadDataWithProgress:(NSData *)cipherText location:(NSString *)location attachmentID:(NSString *)attachmentID;
@end
@implementation TSMessagesManager (Testing)
+ (void)swapOriginalSelector:(SEL)originalSelector replacement:(SEL)replacementSelector
{
Class class = [self class];
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method replacementMethod = class_getInstanceMethod(class, replacementSelector);
// When swizzling a class method, use the following:
// Class class = object_getClass((id)self);
// ...
// Method originalMethod = class_getClassMethod(class, originalSelector);
// Method swizzledMethod = class_getClassMethod(class, swizzledSelector);
BOOL didAddMethod = class_addMethod(class,
originalSelector,
method_getImplementation(replacementMethod),
method_getTypeEncoding(replacementMethod));
if (didAddMethod) {
class_replaceMethod(class,
replacementSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, replacementMethod);
}
}
+ (void)load
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self swapOriginalSelector:@selector(uploadDataWithProgress:location:attachmentID:)
replacement:@selector(stubbedUploadDataWithProgress:location:attachmentID:)];
[self swapOriginalSelector:@selector(deviceMessages:forRecipient:inThread:)
replacement:@selector(stubbedDeviceMessages:forRecipient:inThread:)];
});
}
#pragma mark - Method Swizzling
- (BOOL)stubbedUploadDataWithProgress:(NSData *)cipherText
location:(NSString *)location
attachmentID:(NSString *)attachmentID
{
NSLog(@"Faking successful upload.");
return YES;
}
- (NSArray<NSDictionary *> *)stubbedDeviceMessages:(TSOutgoingMessage *)message
forRecipient:(SignalRecipient *)recipient
inThread:(TSThread *)thread
{
// Upon originally provisioning, we won't have a device to send to.
NSLog(@"Stubbed device message to return empty list.");
return @[];
}
@end
@interface OWSTSMessagesManagerTestNetworkManager : TSNetworkManager
- (instancetype)initWithExpectation:(XCTestExpectation *)messageWasSubmitted;
@property XCTestExpectation *messageWasSubmitted;
@end
@implementation OWSTSMessagesManagerTestNetworkManager
- (instancetype)initWithExpectation:(XCTestExpectation *)messageWasSubmitted
{
_messageWasSubmitted = messageWasSubmitted;
return self;
}
- (void)makeRequest:(TSRequest *)request
success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
{
if ([request isKindOfClass:[TSAllocAttachmentRequest class]]) {
NSDictionary *fakeResponse = @{ @"id" : @(1234), @"location" : @"fake-location" };
success(nil, fakeResponse);
} else if ([request isKindOfClass:[TSSubmitMessageRequest class]]) {
[self.messageWasSubmitted fulfill];
} else {
NSLog(@"Ignoring unhandled request: %@", request);
}
}
@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
@implementation TSMessagesManagerTest
- (void)testIncomingSyncContactMessage
{
OWSFakeContactsUpdater *fakeContactsUpdater = [OWSFakeContactsUpdater new];
XCTestExpectation *messageWasSubmitted = [self expectationWithDescription:@"message was submitted"];
OWSTSMessagesManagerTestNetworkManager *fakeNetworkManager =
[[OWSTSMessagesManagerTestNetworkManager alloc] initWithExpectation:messageWasSubmitted];
TSMessagesManager *messagesManager =
[[TSMessagesManager alloc] initWithNetworkManager:fakeNetworkManager
dbConnection:[TSStorageManager sharedManager].newDatabaseConnection
contactsUpdater:fakeContactsUpdater];
OWSSignalServiceProtosEnvelopeBuilder *envelopeBuilder = [OWSSignalServiceProtosEnvelopeBuilder new];
OWSSignalServiceProtosSyncMessageBuilder *messageBuilder = [OWSSignalServiceProtosSyncMessageBuilder new];
OWSSignalServiceProtosSyncMessageRequestBuilder *requestBuilder =
[OWSSignalServiceProtosSyncMessageRequestBuilder new];
[requestBuilder setType:OWSSignalServiceProtosSyncMessageRequestTypeGroups];
[messageBuilder setRequest:[requestBuilder build]];
[messagesManager handleIncomingEnvelope:[envelopeBuilder build] withSyncMessage:[messageBuilder build]];
[self waitForExpectationsWithTimeout:5
handler:^(NSError *error) {
NSLog(@"No message submitted.");
}];
}
@end
Loading…
Cancel
Save