OTCloudStoreTests.m [plain text]
/*
* Copyright (c) 2017 Apple Inc. All Rights Reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this
* file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
#if OCTAGON
#import <Foundation/Foundation.h>
#import <XCTest/XCTest.h>
#import <OCMock/OCMock.h>
#import "OTTestsBase.h"
static NSString* OTCKRecordBottledPeerType = @"OTBottledPeer";
static NSString* OTCKRecordEscrowRecordID = @"escrowRecordID";
@interface OTCloudStoreUnitTests : OTTestsBase
@property (nonatomic, strong) OTBottledPeerRecord* fakeBottledPeerRecord;
@end
@implementation OTCloudStoreUnitTests
- (void)setUp {
[super setUp];
self.continueAfterFailure = NO;
self.fakeBottledPeerRecord = [[OTBottledPeerRecord alloc] init];
self.fakeBottledPeerRecord.bottle = [@"bottled peer data" dataUsingEncoding:NSUTF8StringEncoding];
self.fakeBottledPeerRecord.signatureUsingEscrowKey = [@"bottled peer escrow sig" dataUsingEncoding:NSUTF8StringEncoding];
self.fakeBottledPeerRecord.signatureUsingPeerKey = [@"bottled peer peer sig" dataUsingEncoding:NSUTF8StringEncoding];
self.fakeBottledPeerRecord.peerID = @"peer id";
self.fakeBottledPeerRecord.spID = @"sos peer id";
self.fakeBottledPeerRecord.escrowRecordID = @"escrowRecordID";
self.fakeBottledPeerRecord.escrowedSigningSPKI = [@"escrowedSigningSPKI" dataUsingEncoding:kCFStringEncodingUTF8];
self.fakeBottledPeerRecord.peerSigningSPKI = [@"peerSigningSPKI" dataUsingEncoding:kCFStringEncodingUTF8];
}
- (void)tearDown {
self.zones = nil;
self.operationQueue = nil;
[super tearDown];
}
- (void)testWriteSameBottledPeerTwiceToFakeRecord {
NSError* error = nil;
NSMutableDictionary* recordDictionary = [NSMutableDictionary dictionaryWithObjectsAndKeys:[[NSNumber alloc] initWithInt:1], OTCKRecordBottledPeerType, nil];
[self expectAddedCKModifyRecords:recordDictionary holdFetch:YES];
[self startCKKSSubsystem];
XCTAssertTrue([self.cloudStore uploadBottledPeerRecord:self.fakeBottledPeerRecord escrowRecordID:self.fakeBottledPeerRecord.escrowRecordID error:&error], @"should create bottled peer record");
XCTAssertNil(error, "error should be nil");
[self waitForCKModifications];
OCMVerifyAllWithDelay(self.mockDatabase, 8);
[self releaseCloudKitFetchHold];
[self expectAddedCKModifyRecords:recordDictionary holdFetch:YES];
XCTAssertTrue([self.cloudStore uploadBottledPeerRecord:self.fakeBottledPeerRecord escrowRecordID:self.fakeBottledPeerRecord.escrowRecordID error:&error], @"should create bottled peer record");
XCTAssertNil(error, "error should be nil");
[self waitForCKModifications];
OCMVerifyAllWithDelay(self.mockDatabase, 8);
[self releaseCloudKitFetchHold];
}
- (void)testWriteBottledPeerToFakeRecord {
NSError* error = nil;
NSMutableDictionary* recordDictionary = [NSMutableDictionary dictionary];
recordDictionary[OTCKRecordBottledPeerType] = [[NSNumber alloc] initWithInt:1];
[self expectAddedCKModifyRecords:recordDictionary holdFetch:YES];
[self startCKKSSubsystem];
XCTAssertTrue([self.cloudStore uploadBottledPeerRecord:self.fakeBottledPeerRecord escrowRecordID:self.fakeBottledPeerRecord.escrowRecordID error:&error], @"should create bottled peer record");
XCTAssertNil(error, "error should be nil");
[self waitForCKModifications];
OCMVerifyAllWithDelay(self.mockDatabase, 8);
[self releaseCloudKitFetchHold];
}
- (void)testWriteMultipleBottledPeersToSAMEFakeRecord {
NSError* error = nil;
NSMutableDictionary* recordDictionary = [NSMutableDictionary dictionary];
recordDictionary[OTCKRecordBottledPeerType] = [[NSNumber alloc] initWithInt:1];
[self startCKKSSubsystem];
for(int i = 0; i < 10; i++){
[self expectAddedCKModifyRecords:recordDictionary holdFetch:NO];
XCTAssertTrue([self.cloudStore uploadBottledPeerRecord:self.fakeBottledPeerRecord escrowRecordID:self.fakeBottledPeerRecord.escrowRecordID error:&error], @"should create bottled peer record");
[self waitForCKModifications];
XCTAssertNil(error, "error should be nil");
OCMVerifyAllWithDelay(self.mockDatabase, 8);
[self releaseCloudKitFetchHold];
}
}
- (void)testWriteBottledPeersToDifferentFakeRecord {
NSError* error = nil;
NSMutableDictionary* recordDictionary = [NSMutableDictionary dictionary];
recordDictionary[OTCKRecordBottledPeerType] = [[NSNumber alloc] initWithInt:1];
[self startCKKSSubsystem];
for(int i = 0; i < 10; i++){
[self expectAddedCKModifyRecords:recordDictionary holdFetch:YES];
NSString *escrowID = [NSString stringWithFormat:@"bp-sospeer self.fakeBottledPeerRecord.escrowRecordID = escrowID;
XCTAssertTrue([self.cloudStore uploadBottledPeerRecord:self.fakeBottledPeerRecord escrowRecordID:escrowID error:&error], @"should create bottled peer record");
[self waitForCKModifications];
XCTAssertNil(error, "error should be nil");
OCMVerifyAllWithDelay(self.mockDatabase, 8);
[self releaseCloudKitFetchHold];
}
XCTAssertTrue( [[self.cloudStore retrieveListOfEligibleEscrowRecordIDs:&error] count] == 10, @"should have 1 record");
}
- (void)testReadBottledPeerRecordFromCloudKit {
NSError *error = nil;
[self startCKKSSubsystem];
CKRecord* newRecord = [[CKRecord alloc]initWithRecordType:OTCKRecordBottledPeerType];
newRecord[OTCKRecordEscrowRecordID] = @"escrowRecordID";
[self.otFakeZone addToZone:newRecord];
[self.cloudStore notifyZoneChange:nil];
[self waitForCKModifications];
OCMVerifyAllWithDelay(self.mockDatabase, 8);
XCTAssertTrue( [[self.cloudStore retrieveListOfEligibleEscrowRecordIDs:&error] count] > 0, @"should have 1 record");
}
-(void) testOTCloudStoreDownloadBP{
NSError* error = nil;
[self startCKKSSubsystem];
CKRecord* newRecord = [[CKRecord alloc]initWithRecordType:OTCKRecordBottledPeerType];
newRecord[OTCKRecordEscrowRecordID] = @"escrowRecordID";
[self.otFakeZone addToZone:newRecord];
XCTAssertTrue([self.cloudStore downloadBottledPeerRecord:&error] == YES, @"downloading records should succeed: XCTAssertNil(error, @"error should be nil");
[self waitForCKModifications];
OCMVerifyAllWithDelay(self.mockDatabase, 8);
XCTAssertNil(error, "error should be nil");
XCTAssertEqual([[self.cloudStore retrieveListOfEligibleEscrowRecordIDs:&error] count], (unsigned long)1, @"should have 1 record");
XCTAssertNil(error, "error should be nil");
}
-(void) testOTCloudStoreDownloadMultipleBP{
NSError* error = nil;
[self startCKKSSubsystem];
for(int i = 0; i < 10; i++){
CKRecord* newRecord = [[CKRecord alloc]initWithRecordType:OTCKRecordBottledPeerType zoneID:self.otZoneID];
newRecord[OTCKRecordEscrowRecordID] = [NSString stringWithFormat:@"escrowRecordID [self.otFakeZone addToZone:newRecord];
}
[self waitForCKModifications];
XCTAssertTrue([self.cloudStore downloadBottledPeerRecord:&error] == YES, @"downloading records should succeed: XCTAssertNil(error, @"error should be nil");
[self waitForCKModifications];
OCMVerifyAllWithDelay(self.mockDatabase, 8);
XCTAssertNil(error, "error should be nil");
XCTAssertEqual( [[self.cloudStore retrieveListOfEligibleEscrowRecordIDs:&error] count], (unsigned long)10, @"should have 1 record");
}
-(void) testOTCloudStoreUploadMultipleToSameRecord{
NSError* error = nil;
[self startCKKSSubsystem];
CKRecord* newRecord = [[CKRecord alloc]initWithRecordType:OTCKRecordBottledPeerType zoneID:self.otZoneID];
newRecord[OTCKRecordEscrowRecordID] = @"escrowRecordID";
for(int i = 0; i < 10; i++){
[self.otFakeZone addToZone:newRecord];
}
[self waitForCKModifications];
XCTAssertTrue([self.cloudStore downloadBottledPeerRecord:&error] == YES, @"downloading records should succeed: XCTAssertNil(error, @"error should be nil");
[self waitForCKModifications];
OCMVerifyAllWithDelay(self.mockDatabase, 8);
XCTAssertNil(error, "error should be nil");
XCTAssertEqual([[self.cloudStore retrieveListOfEligibleEscrowRecordIDs:&error] count], (unsigned long)1, @"should have 1 record");
}
-(void) testRemoveRecordIDs{
[self startCKKSSubsystem];
NSError *error = nil;
CKRecord* newRecord = [[CKRecord alloc]initWithRecordType:OTCKRecordBottledPeerType zoneID:self.otZoneID];
newRecord[OTCKRecordEscrowRecordID] = @"escrowRecordID";
[self expectCKFetch];
[self.otFakeZone addToZone:newRecord];
[self waitForCKModifications];
[self.cloudStore notifyZoneChange:nil];
[self waitForCKModifications];
XCTAssertTrue( [[self.cloudStore retrieveListOfEligibleEscrowRecordIDs:&error] count] == 1, @"should have 1 record");
[self expectCKFetch];
XCTAssertTrue([self.cloudStore downloadBottledPeerRecord:&error] == YES, @"downloading records should succeed: XCTAssertNil(error, @"error should be nil");
[self waitForCKModifications];
}
-(void) testFetchTimeout
{
[self startCKKSSubsystem];
NSError* error = nil;
CKRecord* newRecord = [[CKRecord alloc]initWithRecordType:OTCKRecordBottledPeerType zoneID:self.otZoneID];
newRecord[OTCKRecordEscrowRecordID] = @"escrowRecordID";
[self holdCloudKitFetches];
[self.cloudStore downloadBottledPeerRecord:&error];
XCTAssertNotNil(error, "error should not be nil");
XCTAssertTrue([(NSString*)error.userInfo[@"NSLocalizedDescription"] isEqualToString:@"Operation(CKKSResultOperation(cloudkit-fetch-and-process-changes)) timed out waiting to start for [<CKKSResultOperation(fetch-and-process-updates-watcher): ready>]"], "expecting timed out error");
}
-(void) testModifyRecordsTimeout
{
NSError* error = nil;
NSMutableDictionary* recordDictionary = [NSMutableDictionary dictionaryWithObjectsAndKeys:[[NSNumber alloc] initWithInt:1], OTCKRecordBottledPeerType, nil];
[self expectAddedCKModifyRecords:recordDictionary holdFetch:NO];
[self startCKKSSubsystem];
[self createAndSaveFakeKeyHierarchy: self.keychainZoneID]; // Make life easy for this test.
[self startCKKSSubsystem];
XCTAssertEqual(0, [self.keychainView.keyHierarchyConditions[SecCKKSZoneKeyStateReady] wait:4*NSEC_PER_SEC], @"Key state should have arrived at ready");
[self holdCloudKitModifications];
[self.cloudStore uploadBottledPeerRecord:self.fakeBottledPeerRecord
escrowRecordID:self.fakeBottledPeerRecord.escrowRecordID error:&error];
XCTAssertNotNil(error, "error should not be nil");
XCTAssertTrue([(NSString*)error.userInfo[@"NSLocalizedDescription"] isEqualToString:@"Operation(CKKSResultOperation(cloudkit-modify-changes)) timed out waiting to start for [<CKKSResultOperation(modify-records-watcher): ready>]"], "expecting timed out error");
[self expectAddedCKModifyRecords:recordDictionary holdFetch:NO];
[self releaseCloudKitModificationHold];
[self waitForCKModifications];
}
@end
#endif /* OCTAGON */