/ keychain / escrowrequest / tests / SecEscrowRequestTests.m
SecEscrowRequestTests.m
  1  
  2  #import <XCTest/XCTest.h>
  3  #import <OCMock/OCMock.h>
  4  #import <CoreCDP/CDPError.h>
  5  #import <CoreCDP/CDPStateController.h>
  6  #import <CloudServices/CloudServices.h>
  7  
  8  #import "keychain/categories/NSError+UsefulConstructors.h"
  9  #import "keychain/escrowrequest/Framework/SecEscrowRequest.h"
 10  #import "keychain/escrowrequest/EscrowRequestServer.h"
 11  
 12  #import "keychain/escrowrequest/EscrowRequestServerHelpers.h"
 13  #import "keychain/escrowrequest/operations/EscrowRequestInformCloudServicesOperation.h"
 14  #import "keychain/escrowrequest/operations/EscrowRequestPerformEscrowEnrollOperation.h"
 15  #import "keychain/escrowrequest/EscrowRequestServerHelpers.h"
 16  
 17  #import "keychain/escrowrequest/tests/MockSynchronousEscrowServer.h"
 18  
 19  #include "keychain/ckks/CKKS.h"
 20  #include "keychain/ckks/CKKSLockStateTracker.h"
 21  #include "keychain/securityd/SecItemServer.h"
 22  #include "keychain/securityd/spi.h"
 23  #include "utilities/SecFileLocations.h"
 24  
 25  #include "tests/secdmockaks/mockaks.h"
 26  
 27  @interface SecEscrowRequestTests : XCTestCase
 28  @property SecEscrowRequest* escrowRequest;
 29  @property EscrowRequestServer* escrowServer;
 30  @property CKKSLockStateTracker* lockStateTracker;
 31  
 32  @property id escrowRequestServerClassMock;
 33  @property id escrowRequestInformCloudServicesOperationMock;
 34  @property id escrowRequestPerformEscrowEnrollOperationMock;
 35  @end
 36  
 37  @implementation SecEscrowRequestTests
 38  
 39  + (void)setUp {
 40      securityd_init_local_spi();
 41      EscrowRequestServerSetEnabled(true);
 42  }
 43  
 44  - (void)setUp {
 45      NSString* testName = [self.name componentsSeparatedByString:@" "][1];
 46      testName = [testName stringByReplacingOccurrencesOfString:@"]" withString:@""];
 47      secnotice("secescrowtest", "Beginning test %@", testName);
 48  
 49      [SecMockAKS unlockAllClasses];
 50  
 51      // Make a new fake keychain
 52      NSString* tmp_dir = [NSString stringWithFormat: @"/tmp/%@.%X", testName, arc4random()];
 53      [[NSFileManager defaultManager] createDirectoryAtPath:[NSString stringWithFormat: @"%@/Library/Keychains", tmp_dir] withIntermediateDirectories:YES attributes:nil error:NULL];
 54  
 55      SecCKKSDisable();
 56      SecCKKSTestDisableSOS();
 57  
 58      // Mock out the SBD layer
 59      self.escrowRequestServerClassMock = OCMClassMock([EscrowRequestServer class]);
 60      self.escrowRequestInformCloudServicesOperationMock = OCMClassMock([EscrowRequestInformCloudServicesOperation class]);
 61      self.escrowRequestPerformEscrowEnrollOperationMock = OCMClassMock([EscrowRequestPerformEscrowEnrollOperation class]);
 62  
 63      self.lockStateTracker = [[CKKSLockStateTracker alloc] init];
 64      self.escrowServer = [[EscrowRequestServer alloc] initWithLockStateTracker:self.lockStateTracker];
 65  
 66      id mockConnection = OCMPartialMock([[NSXPCConnection alloc] init]);
 67      OCMStub([mockConnection remoteObjectProxyWithErrorHandler:[OCMArg any]]).andCall(self, @selector(escrowServer));
 68      OCMStub([mockConnection synchronousRemoteObjectProxyWithErrorHandler:[OCMArg any]]).andCall(self, @selector(synchronousEscrowServer));
 69      self.escrowRequest = [[SecEscrowRequest alloc] initWithConnection:mockConnection];
 70  
 71      SecSetCustomHomeURLString((__bridge CFStringRef) tmp_dir);
 72      SecKeychainDbReset(NULL);
 73  
 74      // Actually load the database.
 75      kc_with_dbt(true, NULL, ^bool (SecDbConnectionRef dbt) { return false; });
 76  }
 77  
 78  
 79  - (NSData* _Nullable)mockTriggerCloudServicesPasscodeRequest:(NSString*)uuid error:(NSError**)error
 80  {
 81      return [@"certdata" dataUsingEncoding:NSUTF8StringEncoding];
 82  }
 83  
 84  - (NSData* _Nullable)mockTriggerCloudServicesPasscodeRequestAndLockClassA:(NSString*)uuid error:(NSError**)error
 85  {
 86      [SecMockAKS lockClassA];
 87      [self.lockStateTracker recheck];
 88      return [@"certdata" dataUsingEncoding:NSUTF8StringEncoding];
 89  }
 90  
 91  - (NSData* _Nullable)mockFailTriggerCloudServicesPasscodeRequest:(NSString*)uuid error:(NSError**)error
 92  {
 93      if(error) {
 94          *error = [NSError errorWithDomain:@"NSURLErrorDomain"
 95                                       code:-1009
 96                                description:@"The internet connection appears to be offline (mock)"];
 97      }
 98      return nil;
 99  }
100  
101  - (void)mockcdpUploadPrerecord:(SecEscrowPendingRecord*)recordToSend
102                      secretType:(CDPDeviceSecretType)secretType
103                           reply:(void (^)(bool didUpdate, NSError* _Nullable error))reply
104  {
105      dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^ {
106          reply(YES, nil);
107      });
108  }
109  
110  - (void)mockFailcdpUploadPrerecord:(SecEscrowPendingRecord*)recordToSend
111                          secretType:(CDPDeviceSecretType)secretType
112                               reply:(void (^)(bool didUpdate, NSError* _Nullable error))reply
113  {
114      dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^ {
115          reply(NO, [NSError errorWithDomain:@"EscrowServiceErrorDomain" code:-6570 description:@"mock CLUBH error"]);
116      });
117  }
118  
119  - (void)mockcdpFailBadPeerUploadPrerecord:(SecEscrowPendingRecord*)recordToSend
120                             secretType:(CDPDeviceSecretType)secretType
121                                  reply:(void (^)(bool didUpdate, NSError* _Nullable error))reply
122  {
123      dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^ {
124          reply(NO, [NSError errorWithDomain:kSecureBackupErrorDomain code:kSecureBackupInternalError description:@"SOS peer ID mismatch"]);
125      });
126  }
127  
128  - (void)mockcdpFailNoSOSPeerUploadPrerecord:(SecEscrowPendingRecord*)recordToSend
129                                   secretType:(CDPDeviceSecretType)secretType
130                                        reply:(void (^)(bool didUpdate, NSError* _Nullable error))reply
131  {
132      dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^ {
133          reply(NO, [NSError errorWithDomain:kSecureBackupErrorDomain code:kSecureBackupNotInSyncCircleError description:@"SOS peer not present"]);
134      });
135  }
136  
137  - (void)mockcdpFailNoCDPPeerUploadPrerecord:(SecEscrowPendingRecord*)recordToSend
138                                secretType:(CDPDeviceSecretType)secretType
139                                     reply:(void (^)(bool didUpdate, NSError* _Nullable error))reply
140  {
141      dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^ {
142          reply(NO, [NSError errorWithDomain:CDPStateErrorDomain code:CDPStateErrorNoPeerIdFound description:@"SOS peer not present"]);
143      });
144  }
145  
146  
147  - (id<EscrowRequestXPCProtocol>)synchronousEscrowServer
148  {
149      return [[MockSynchronousEscrowServer alloc] initWithServer:self.escrowServer];
150  }
151  
152  - (void)tearDown {
153      [self.escrowServer.controller.stateMachine haltOperation];
154  }
155  
156  - (void)allCloudServicesCallsSucceed {
157      OCMStub([self.escrowRequestInformCloudServicesOperationMock triggerCloudServicesPasscodeRequest:[OCMArg any] error:[OCMArg anyObjectRef]]).andCall(self, @selector(mockTriggerCloudServicesPasscodeRequest:error:));
158  }
159  
160  - (void)testStateMachineEntersNothingToDo
161  {
162      [self.escrowServer.controller.stateMachine startOperation];
163      XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.stateConditions[EscrowRequestStateNothingToDo] wait:10*NSEC_PER_SEC], "State machine enters NothingToDo");
164  }
165  
166  - (void)testTriggerUpdate {
167      [self allCloudServicesCallsSucceed];
168  
169      NSError* error = nil;
170  
171      XCTAssertNil([self.escrowRequest fetchRequestWaitingOnPasscode:&error], @"Should be no pending updates");
172      XCTAssertNil(error, @"Should be no error fetching pending updates");
173  
174      XCTAssertTrue([self.escrowRequest triggerEscrowUpdate:@"test" error:&error], @"Should be able to trigger an update");
175      XCTAssertNil(error, @"Should be no error triggering an escrow update");
176  
177      XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should churn through");
178  
179      NSString* pendingUUID = [self.escrowRequest fetchRequestWaitingOnPasscode:&error];
180      XCTAssertNil(error, @"Should be no error fetching a pending UUID");
181      XCTAssertNotNil(pendingUUID, "Should be a pending request UUID after a trigger");
182  }
183  
184  - (void)testTriggerUpdateWhileLocked {
185      [self allCloudServicesCallsSucceed];
186  
187      [SecMockAKS lockClassA];
188  
189      NSError* error = nil;
190  
191      XCTAssertNil([self.escrowRequest fetchRequestWaitingOnPasscode:&error], @"Should be no pending updates");
192      XCTAssertNil(error, @"Should be no error fetching pending updates");
193  
194      XCTAssertFalse([self.escrowRequest triggerEscrowUpdate:@"test" error:&error], @"Should not be able to trigger an update when locked");
195      XCTAssertNotNil(error, @"Should be an error triggering an escrow update (while locked)");
196      error = nil;
197  
198      XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should churn through");
199  
200      NSString* pendingUUID = [self.escrowRequest fetchRequestWaitingOnPasscode:&error];
201      XCTAssertNil(error, @"Should be no error fetching a pending UUID");
202      XCTAssertNil(pendingUUID, "Should be no pending request UUID after a failed trigger");
203  }
204  
205  - (void)testMultipleTriggers {
206      [self allCloudServicesCallsSucceed];
207  
208      NSError* error = nil;
209  
210      XCTAssertTrue([self.escrowRequest triggerEscrowUpdate:@"test" error:&error], @"Should be able to trigger an update");
211      XCTAssertNil(error, @"Should be no error triggering an escrow update");
212  
213      XCTAssertTrue([self.escrowRequest triggerEscrowUpdate:@"test" error:&error], @"Should be able to trigger an update");
214      XCTAssertNil(error, @"Should be no error triggering an escrow update");
215  
216      XCTAssertTrue([self.escrowRequest triggerEscrowUpdate:@"test" error:&error], @"Should be able to trigger an update");
217      XCTAssertNil(error, @"Should be no error triggering an escrow update");
218  
219      NSDictionary<NSString*, NSString*>* statuses = [self.escrowRequest fetchStatuses:&error];
220      XCTAssertNotNil(statuses, @"Should be able to fetch statuses");
221      XCTAssertNil(error, @"Should be no error fetching statuses");
222  
223      XCTAssertEqual(statuses.count, 1, @"Should be one in-flight status");
224  }
225  
226  - (void)testFetchUpdateWhileLocked {
227      [self allCloudServicesCallsSucceed];
228  
229      NSError* error = nil;
230  
231      XCTAssertNil([self.escrowRequest fetchRequestWaitingOnPasscode:&error], @"Should be no pending updates");
232      XCTAssertNil(error, @"Should be no error fetching pending updates");
233  
234      XCTAssertTrue([self.escrowRequest triggerEscrowUpdate:@"test" error:&error], @"Should be able to trigger an update");
235      XCTAssertNil(error, @"Should be no error triggering an escrow update");
236  
237      XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should churn through");
238      error = nil;
239  
240      [SecMockAKS lockClassA];
241  
242      NSString* pendingUUID = [self.escrowRequest fetchRequestWaitingOnPasscode:&error];
243      XCTAssertNotNil(error, @"Should be an error fetching a pending UUID while the device is locked");
244      XCTAssertNil(pendingUUID, "Should be no pending request UUID while device is locked");
245  }
246  
247  - (void)testReaskCloudServicesWhenLocked {
248      // In this test, CloudServices is able to succeed (and cache a certificate), but we can't write down that the operation succeeded.
249      NSError* error = nil;
250  
251      // No rate limiting for us!
252      self.escrowServer.controller.forceIgnoreCloudServicesRateLimiting = true;
253  
254      // Pause the state machine for a bit...
255      [self.escrowServer.controller.stateMachine startOperation];
256      XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should pause");
257      [self.escrowServer.controller.stateMachine haltOperation];
258  
259      XCTAssertTrue([self.escrowRequest triggerEscrowUpdate:@"test" error:&error], @"Should be able to trigger an update");
260      XCTAssertNil(error, @"Should be no error triggering an escrow update");
261  
262      // First, let's ensure that we properly handle the case where the device is locked when we go to ask CloudServices about things
263  
264      [SecMockAKS lockClassA];
265      [self.lockStateTracker recheck];
266      [self.escrowServer.controller.stateMachine startOperation];
267      XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.stateConditions[EscrowRequestStateWaitForUnlock] wait:10*NSEC_PER_SEC], "State machine enters waitforunlock");
268  
269      // Now, we should call CloudServices, but the device locks between CS success and us writing it down
270      OCMExpect([self.escrowRequestInformCloudServicesOperationMock triggerCloudServicesPasscodeRequest:[OCMArg any] error:[OCMArg anyObjectRef]]).andCall(self, @selector(mockTriggerCloudServicesPasscodeRequestAndLockClassA:error:));
271      [SecMockAKS unlockAllClasses];
272      [self.lockStateTracker recheck];
273  
274      OCMVerifyAllWithDelay(self.escrowRequestInformCloudServicesOperationMock, 10);
275      // and we should be back in wait for unlock:
276      XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.stateConditions[EscrowRequestStateWaitForUnlock] wait:10*NSEC_PER_SEC], "State machine enters waitforunlock");
277  
278      // Then, unlock one last time, and let everything succeed
279      OCMExpect([self.escrowRequestInformCloudServicesOperationMock triggerCloudServicesPasscodeRequest:[OCMArg any] error:[OCMArg anyObjectRef]]).andCall(self, @selector(mockTriggerCloudServicesPasscodeRequest:error:));
280      [SecMockAKS unlockAllClasses];
281      [self.lockStateTracker recheck];
282  
283      OCMVerifyAllWithDelay(self.escrowRequestInformCloudServicesOperationMock, 10);
284  }
285  
286  - (void)testRateLimitSBDTriggerWhenFailing {
287  
288      // Trigger an escrow update, which should call CloudServices and fail
289      OCMExpect([self.escrowRequestInformCloudServicesOperationMock triggerCloudServicesPasscodeRequest:[OCMArg any] error:[OCMArg anyObjectRef]]).andCall(self, @selector(mockFailTriggerCloudServicesPasscodeRequest:error:));
290  
291      NSError* error = nil;
292      XCTAssertTrue([self.escrowRequest triggerEscrowUpdate:@"test" error:&error], @"Should be able to trigger an update");
293      XCTAssertNil(error, @"Should be no error triggering an escrow update");
294  
295      OCMVerifyAll(self.escrowRequestServerClassMock);
296  
297      // And, the state machine should pause
298      XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should rest");
299  
300      // But, there should be no pending requests (as we need to retry CloudServices)
301      NSString* pendingUUID = [self.escrowRequest fetchRequestWaitingOnPasscode:&error];
302      XCTAssertNil(error, @"Should be no error fetching a pending UUID");
303      XCTAssertNil(pendingUUID, "Should be no pending request UUID after a failed trigger");
304  
305  
306      // But, the controller will recover at some point (for now, disable rate limiting)
307      [self allCloudServicesCallsSucceed];
308      self.escrowServer.controller.forceIgnoreCloudServicesRateLimiting = true;
309      [self.escrowServer.controller.stateMachine pokeStateMachine];
310  
311      XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should rest");
312  
313      pendingUUID = [self.escrowRequest fetchRequestWaitingOnPasscode:&error];
314      XCTAssertNil(error, @"Should be no error fetching a pending UUID");
315      XCTAssertNotNil(pendingUUID, "Should be a pending request UUID after the state machine retries certificate caching");
316  }
317  
318  - (void)testRateLimitSBDUploadWhenFailing {
319  
320      // Trigger an escrow update, which should call CloudServices
321      OCMExpect([self.escrowRequestInformCloudServicesOperationMock triggerCloudServicesPasscodeRequest:[OCMArg any] error:[OCMArg anyObjectRef]]).andCall(self, @selector(mockTriggerCloudServicesPasscodeRequest:error:));
322  
323      NSError* error = nil;
324      XCTAssertTrue([self.escrowRequest triggerEscrowUpdate:@"test" error:&error], @"Should be able to trigger an update");
325      XCTAssertNil(error, @"Should be no error triggering an escrow update");
326  
327      OCMVerifyAllWithDelay(self.escrowRequestInformCloudServicesOperationMock, 5);
328  
329      XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should pause within some time");
330  
331      NSString* pendingUUID = [self.escrowRequest fetchRequestWaitingOnPasscode:&error];
332      XCTAssertNil(error, @"Should be no error fetching a pending UUID");
333      XCTAssertNotNil(pendingUUID, "Should be a pending request UUID after a trigger");
334  
335  
336      [[[[self.escrowRequestPerformEscrowEnrollOperationMock expect] andCall:@selector(mockFailcdpUploadPrerecord:secretType:reply:)
337                                                                    onObject:self] ignoringNonObjectArgs]
338       cdpUploadPrerecord:[OCMArg any] secretType:0 reply:[OCMArg any]];
339  
340  
341      NSData* d = [[NSData alloc]initWithBase64EncodedString:@"YXNkZgo=" options:0];
342      XCTAssertTrue([self.escrowRequest cachePrerecord:pendingUUID
343                                   serializedPrerecord:d
344                                                 error:&error], @"Should be able to cache a prerecord");
345      XCTAssertNil(error, @"Should be no error caching a prerecord");
346  
347      // Wait for the upload to fail...
348      OCMVerifyAllWithDelay(self.escrowRequestPerformEscrowEnrollOperationMock, 10);
349  
350      XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should rest");
351  
352      //////////////////////////
353      // Trigger another update, to check coalescing
354      OCMExpect([self.escrowRequestInformCloudServicesOperationMock triggerCloudServicesPasscodeRequest:[OCMArg any] error:[OCMArg anyObjectRef]]).andCall(self, @selector(mockTriggerCloudServicesPasscodeRequest:error:));
355  
356      XCTAssertTrue([self.escrowRequest triggerEscrowUpdate:@"test2" error:&error], @"Should be able to trigger an update");
357      XCTAssertNil(error, @"Should be no error triggering an escrow update");
358      OCMVerifyAllWithDelay(self.escrowRequestInformCloudServicesOperationMock, 5);
359  
360      XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should rest");
361  
362      NSString* pendingUUIDTheSecond = [self.escrowRequest fetchRequestWaitingOnPasscode:&error];
363      XCTAssertNotNil(pendingUUIDTheSecond, @"Should have a request waiting on passcode");
364      XCTAssertEqualObjects(pendingUUID, pendingUUIDTheSecond, @"In-flight request should have been restarted");
365      //////////////
366  
367      [[[[self.escrowRequestPerformEscrowEnrollOperationMock expect] andCall:@selector(mockFailcdpUploadPrerecord:secretType:reply:)
368                                                                    onObject:self] ignoringNonObjectArgs]
369       cdpUploadPrerecord:[OCMArg any] secretType:0 reply:[OCMArg any]];
370      XCTAssertTrue([self.escrowRequest cachePrerecord:pendingUUIDTheSecond
371                                   serializedPrerecord:d
372                                                 error:&error], @"Should be able to cache a prerecord");
373      XCTAssertNil(error, @"Should be no error caching a prerecord");
374  
375      XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should rest");
376  
377      NSDictionary<NSString*, NSString*>* statuses = [self.escrowRequest fetchStatuses:&error];
378      XCTAssertNotNil(statuses, @"Should be able to fetch statuses");
379      XCTAssertNil(error, @"Should be no error fetching statuses");
380  
381      XCTAssertEqual(statuses.count, 1, @"Should be one in-flight status");
382  
383      // But if escrow plays nice again, kick state machine, and make sure we don't touch escrow proxy
384      [[[[self.escrowRequestPerformEscrowEnrollOperationMock stub] andCall:@selector(mockcdpUploadPrerecord:secretType:reply:)
385                                                                  onObject:self] ignoringNonObjectArgs]
386       cdpUploadPrerecord:[OCMArg any] secretType:0 reply:[OCMArg any]];
387  
388  
389      [self.escrowServer.controller.stateMachine startOperation];
390  
391      XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should rest");
392  
393      /* item should still be in flight */
394      statuses = [self.escrowRequest fetchStatuses:&error];
395      XCTAssertNotNil(statuses, @"Should be able to fetch statuses");
396      XCTAssertNil(error, @"Should be no error fetching statuses");
397  
398      XCTAssertEqual(statuses.count, 1, @"Should be one in-flight status");
399  }
400  
401  
402  - (void)testPrerecordCaching {
403      NSError* error = nil;
404  
405      // Trigger an escrow update, which should call CloudServices
406      OCMExpect([self.escrowRequestInformCloudServicesOperationMock triggerCloudServicesPasscodeRequest:[OCMArg any] error:[OCMArg anyObjectRef]]).andCall(self, @selector(mockTriggerCloudServicesPasscodeRequest:error:));
407  
408      XCTAssertTrue([self.escrowRequest triggerEscrowUpdate:@"test" error:&error], @"Should be able to trigger an update");
409      XCTAssertNil(error, @"Should be no error triggering an escrow update");
410  
411      OCMVerifyAll(self.escrowRequestServerClassMock);
412  
413      XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should rest");
414  
415      // Now, there should be a pending request
416  
417      NSString* pendingUUID = [self.escrowRequest fetchRequestWaitingOnPasscode:&error];
418      XCTAssertNil(error, @"Should be no error fetching a pending UUID");
419      XCTAssertNotNil(pendingUUID, "Should be a pending request UUID after a trigger");
420  
421      // Caching a prerecord will attempt an upload via the state machine. But, since we're testing prerecord fetching, we don't want that to happen.
422      // So, let the upload fail.
423      [[[[self.escrowRequestPerformEscrowEnrollOperationMock expect] andCall:@selector(mockFailcdpUploadPrerecord:secretType:reply:)
424                                                                    onObject:self] ignoringNonObjectArgs]
425       cdpUploadPrerecord:[OCMArg any] secretType:0 reply:[OCMArg any]];
426  
427  
428      NSData* d = [[NSData alloc]initWithBase64EncodedString:@"YXNkZgo=" options:0];
429      XCTAssertTrue([self.escrowRequest cachePrerecord:pendingUUID
430                                   serializedPrerecord:d
431                                                 error:&error], @"Should be able to cache a prerecord");
432      XCTAssertNil(error, @"Should be no error caching a prerecord");
433  
434      // Wait for the upload to fail...
435      OCMVerifyAllWithDelay(self.escrowRequestPerformEscrowEnrollOperationMock, 10);
436  
437      NSString* nowPendingUUID = [self.escrowRequest fetchRequestWaitingOnPasscode:&error];
438      XCTAssertNil(error, @"Should be no error fetching a pending UUID");
439      XCTAssertNil(nowPendingUUID, "Should be no pending request UUID after a prerecord cache");
440  
441      NSData* d2 = [self.escrowRequest fetchPrerecord:pendingUUID
442                                                error:&error];
443      XCTAssertNotNil(d2, @"Should be able to retrieve a cached prerecord");
444      XCTAssertNil(error, @"Should be no error fetching a cached prerecord");
445  
446      XCTAssertEqualObjects(d,d2, @"Cached prerecord should be equal to original");
447  }
448  
449  - (void)testPrerecordCachingWhileLocked {
450      NSError* error = nil;
451  
452      // Trigger an escrow update, which should call CloudServices
453      OCMExpect([self.escrowRequestInformCloudServicesOperationMock triggerCloudServicesPasscodeRequest:[OCMArg any] error:[OCMArg anyObjectRef]]).andCall(self, @selector(mockTriggerCloudServicesPasscodeRequest:error:));
454  
455      XCTAssertTrue([self.escrowRequest triggerEscrowUpdate:@"test" error:&error], @"Should be able to trigger an update");
456      XCTAssertNil(error, @"Should be no error triggering an escrow update");
457  
458      OCMVerifyAll(self.escrowRequestServerClassMock);
459  
460      XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should rest");
461  
462      // Now, there should be a pending request
463      NSString* pendingUUID = [self.escrowRequest fetchRequestWaitingOnPasscode:&error];
464      XCTAssertNil(error, @"Should be no error fetching a pending UUID");
465      XCTAssertNotNil(pendingUUID, "Should be a pending request UUID after a trigger");
466  
467      [SecMockAKS lockClassA];
468      // Caching a prerecord will fail, and should not invoke CDP.
469      // So, let the upload fail.
470      [[[self.escrowRequestPerformEscrowEnrollOperationMock reject] ignoringNonObjectArgs]
471       cdpUploadPrerecord:[OCMArg any] secretType:0 reply:[OCMArg any]];
472  
473      NSData* d = [[NSData alloc]initWithBase64EncodedString:@"YXNkZgo=" options:0];
474      XCTAssertFalse([self.escrowRequest cachePrerecord:pendingUUID
475                                    serializedPrerecord:d
476                                                  error:&error], @"Should be not able to cache a prerecord while the device is locked");
477      XCTAssertNotNil(error, @"Should be an error caching a prerecord while the device is locked");
478  
479      // Ensure we don't invoke CDP
480      XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should rest");
481      OCMVerifyAll(self.escrowRequestPerformEscrowEnrollOperationMock);
482  
483      // And now, when we unlock, there's still a pending request
484      error = nil;
485      [SecMockAKS unlockAllClasses];
486      pendingUUID = [self.escrowRequest fetchRequestWaitingOnPasscode:&error];
487      XCTAssertNil(error, @"Should be no error fetching a pending UUID");
488      XCTAssertNotNil(pendingUUID, "Should be a pending request UUID after a trigger");
489  }
490  
491  - (void)testEscrowUploadViaStateMachine {
492      // This test should call CloudServices, which will succeed. It should then tell CoreCDP to upload the record.
493      [self allCloudServicesCallsSucceed];
494      [[[[self.escrowRequestPerformEscrowEnrollOperationMock expect] andCall:@selector(mockcdpUploadPrerecord:secretType:reply:)
495                                                                    onObject:self] ignoringNonObjectArgs]
496       cdpUploadPrerecord:[OCMArg any] secretType:0 reply:[OCMArg any]];
497  
498      [self.escrowServer.controller.stateMachine startOperation];
499      XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.stateConditions[EscrowRequestStateNothingToDo] wait:10*NSEC_PER_SEC], "State machine enters NothingToDo when started");
500  
501      NSError* error = nil;
502      OCMExpect([self.escrowRequestInformCloudServicesOperationMock triggerCloudServicesPasscodeRequest:[OCMArg any] error:[OCMArg anyObjectRef]]).andCall(self, @selector(mockTriggerCloudServicesPasscodeRequest:error:));
503      XCTAssertTrue([self.escrowRequest triggerEscrowUpdate:@"test" error:&error], @"Should be able to trigger an update");
504      XCTAssertNil(error, @"Should be no error triggering an escrow update");
505      OCMVerifyAll(self.escrowRequestServerClassMock);
506  
507  
508      XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should pause within some time");
509  
510      NSString* pendingUUID = [self.escrowRequest fetchRequestWaitingOnPasscode:&error];
511      XCTAssertNil(error, @"Should be no error fetching a pending UUID");
512      XCTAssertNotNil(pendingUUID, "Should be a pending request UUID after a trigger");
513  
514      NSData* d = [[NSData alloc]initWithBase64EncodedString:@"YXNkZgo=" options:0];
515      XCTAssertTrue([self.escrowRequest cachePrerecord:pendingUUID
516                                   serializedPrerecord:d
517                                                 error:&error], @"Should be able to cache a prerecord");
518      XCTAssertNil(error, @"Should be no error caching a prerecord");
519  
520      // Don't call the "do it" RPC, but it should happen anyway
521      OCMVerifyAllWithDelay(self.escrowRequestPerformEscrowEnrollOperationMock, 10);
522  }
523  
524  - (void)testEscrowUploadViaRPCAfterFailure {
525      [[[[self.escrowRequestPerformEscrowEnrollOperationMock expect] andCall:@selector(mockFailcdpUploadPrerecord:secretType:reply:)
526                                                                    onObject:self] ignoringNonObjectArgs]
527          cdpUploadPrerecord:[OCMArg any] secretType:0 reply:[OCMArg any]];
528  
529      NSError* error = nil;
530  
531      OCMExpect([self.escrowRequestInformCloudServicesOperationMock triggerCloudServicesPasscodeRequest:[OCMArg any] error:[OCMArg anyObjectRef]]).andCall(self, @selector(mockTriggerCloudServicesPasscodeRequest:error:));
532  
533      XCTAssertTrue([self.escrowRequest triggerEscrowUpdate:@"test" error:&error], @"Should be able to trigger an update");
534      XCTAssertNil(error, @"Should be no error triggering an escrow update");
535  
536      OCMVerifyAll(self.escrowRequestServerClassMock);
537  
538      XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should pause within some time");
539  
540      NSString* pendingUUID = [self.escrowRequest fetchRequestWaitingOnPasscode:&error];
541      XCTAssertNil(error, @"Should be no error fetching a pending UUID");
542      XCTAssertNotNil(pendingUUID, "Should be a pending request UUID after a trigger");
543  
544      NSData* d = [[NSData alloc]initWithBase64EncodedString:@"YXNkZgo=" options:0];
545      XCTAssertTrue([self.escrowRequest cachePrerecord:pendingUUID
546                                   serializedPrerecord:d
547                                                 error:&error], @"Should be able to cache a prerecord");
548      XCTAssertNil(error, @"Should be no error caching a prerecord");
549  
550  
551      // Now, the state machine should try and fail the upload
552      OCMVerifyAllWithDelay(self.escrowRequestPerformEscrowEnrollOperationMock, 10);
553  
554      // And pause for a while
555      XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should pause within some time");
556  
557      // But if escrow plays nice again, a 'try again' RPC should succeed
558      [[[[self.escrowRequestPerformEscrowEnrollOperationMock stub] andCall:@selector(mockcdpUploadPrerecord:secretType:reply:)
559                                                                    onObject:self] ignoringNonObjectArgs]
560       cdpUploadPrerecord:[OCMArg any] secretType:0 reply:[OCMArg any]];
561  
562      uint64_t records = [self.escrowRequest storePrerecordsInEscrow:&error];
563      XCTAssertNil(error, @"Should be no error storing prerecords in escrow");
564      XCTAssertEqual(records, 1, @"Should have stored one record in escrow");
565  
566      NSDictionary<NSString*, NSString*>* statuses = [self.escrowRequest fetchStatuses:&error];
567      XCTAssertNotNil(statuses, @"Should be able to fetch statuses");
568      XCTAssertNil(error, @"Should be no error fetching statuses");
569  
570      XCTAssertEqual(statuses.count, 1, @"Should be one in-flight status");
571      for(id key in statuses.keyEnumerator) {
572          XCTAssertEqualObjects(statuses[key], @"complete", "Record should be in 'complete' state");
573      }
574  }
575  
576  - (void)testEscrowUploadViaStateMachineAfterFailureDueToLockState {
577      // This test should call CloudServices, which will succeed. It should then tell CDP to upload the record, which will fail due to lock state.
578      [self allCloudServicesCallsSucceed];
579      [[[[self.escrowRequestPerformEscrowEnrollOperationMock expect] andCall:@selector(mockcdpUploadPrerecord:secretType:reply:)
580                                                                    onObject:self] ignoringNonObjectArgs]
581       cdpUploadPrerecord:[OCMArg any] secretType:0 reply:[OCMArg any]];
582  
583      [self.escrowServer.controller.stateMachine startOperation];
584      XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.stateConditions[EscrowRequestStateNothingToDo] wait:10*NSEC_PER_SEC], "State machine enters NothingToDo when started");
585  
586      NSError* error = nil;
587      OCMExpect([self.escrowRequestInformCloudServicesOperationMock triggerCloudServicesPasscodeRequest:[OCMArg any] error:[OCMArg anyObjectRef]]).andCall(self, @selector(mockTriggerCloudServicesPasscodeRequest:error:));
588      XCTAssertTrue([self.escrowRequest triggerEscrowUpdate:@"test" error:&error], @"Should be able to trigger an update");
589      XCTAssertNil(error, @"Should be no error triggering an escrow update");
590      OCMVerifyAll(self.escrowRequestServerClassMock);
591  
592      XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should pause within some time");
593  
594      NSString* pendingUUID = [self.escrowRequest fetchRequestWaitingOnPasscode:&error];
595      XCTAssertNil(error, @"Should be no error fetching a pending UUID");
596      XCTAssertNotNil(pendingUUID, "Should be a pending request UUID after a trigger");
597  
598      // We need to lock directly after the prerecord is cached, but before the CDP attempt is attempted
599      [self.escrowServer.controller.stateMachine haltOperation];
600  
601      NSData* d = [[NSData alloc]initWithBase64EncodedString:@"YXNkZgo=" options:0];
602      XCTAssertTrue([self.escrowRequest cachePrerecord:pendingUUID
603                                   serializedPrerecord:d
604                                                 error:&error], @"Should be able to cache a prerecord");
605      XCTAssertNil(error, @"Should be no error caching a prerecord");
606  
607      [SecMockAKS lockClassA];
608      [self.escrowServer.controller.stateMachine startOperation];
609  
610      // The state machine should notice the unlock, and try again
611      XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.stateConditions[EscrowRequestStateWaitForUnlock] wait:10*NSEC_PER_SEC], "State machine enters waitforunlock");
612  
613      // The upload should be tried after an unlock
614      [SecMockAKS unlockAllClasses];
615      [self.lockStateTracker recheck];
616  
617      OCMVerifyAllWithDelay(self.escrowRequestPerformEscrowEnrollOperationMock, 10);
618  }
619  
620  - (void)testEscrowUploadBadPeerFailure {
621      [[[[self.escrowRequestPerformEscrowEnrollOperationMock expect] andCall:@selector(mockcdpFailBadPeerUploadPrerecord:secretType:reply:)
622                                                                    onObject:self] ignoringNonObjectArgs]
623       cdpUploadPrerecord:[OCMArg any] secretType:0 reply:[OCMArg any]];
624  
625      NSError* error = nil;
626  
627      OCMExpect([self.escrowRequestInformCloudServicesOperationMock triggerCloudServicesPasscodeRequest:[OCMArg any] error:[OCMArg anyObjectRef]]).andCall(self, @selector(mockTriggerCloudServicesPasscodeRequest:error:));
628  
629      XCTAssertTrue([self.escrowRequest triggerEscrowUpdate:@"test" error:&error], @"Should be able to trigger an update");
630      XCTAssertNil(error, @"Should be no error triggering an escrow update");
631  
632      OCMVerifyAll(self.escrowRequestServerClassMock);
633  
634      XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should pause within some time");
635  
636      NSString* pendingUUID = [self.escrowRequest fetchRequestWaitingOnPasscode:&error];
637      XCTAssertNil(error, @"Should be no error fetching a pending UUID");
638      XCTAssertNotNil(pendingUUID, "Should be a pending request UUID after a trigger");
639  
640      NSData* d = [[NSData alloc]initWithBase64EncodedString:@"YXNkZgo=" options:0];
641      XCTAssertTrue([self.escrowRequest cachePrerecord:pendingUUID
642                                   serializedPrerecord:d
643                                                 error:&error], @"Should be able to cache a prerecord");
644      XCTAssertNil(error, @"Should be no error caching a prerecord");
645  
646  
647      // Now, the state machine should try and fail the upload
648      OCMVerifyAllWithDelay(self.escrowRequestPerformEscrowEnrollOperationMock, 10);
649  
650      NSDictionary<NSString*, NSString*>* statuses = [self.escrowRequest fetchStatuses:&error];
651      XCTAssertNotNil(statuses, @"Should be able to fetch statuses");
652      XCTAssertNil(error, @"Should be no error fetching statuses");
653  
654      XCTAssertEqual(statuses.count, 0, @"Should be zero in-flight status");
655  }
656  
657  - (void)testEscrowUploadNoSOSPeerFailure {
658      [[[[self.escrowRequestPerformEscrowEnrollOperationMock expect] andCall:@selector(mockcdpFailNoSOSPeerUploadPrerecord:secretType:reply:)
659                                                                    onObject:self] ignoringNonObjectArgs]
660       cdpUploadPrerecord:[OCMArg any] secretType:0 reply:[OCMArg any]];
661  
662      NSError* error = nil;
663  
664      OCMExpect([self.escrowRequestInformCloudServicesOperationMock triggerCloudServicesPasscodeRequest:[OCMArg any] error:[OCMArg anyObjectRef]]).andCall(self, @selector(mockTriggerCloudServicesPasscodeRequest:error:));
665  
666      XCTAssertTrue([self.escrowRequest triggerEscrowUpdate:@"test" error:&error], @"Should be able to trigger an update");
667      XCTAssertNil(error, @"Should be no error triggering an escrow update");
668  
669      OCMVerifyAll(self.escrowRequestServerClassMock);
670  
671      XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should pause within some time");
672  
673      NSString* pendingUUID = [self.escrowRequest fetchRequestWaitingOnPasscode:&error];
674      XCTAssertNil(error, @"Should be no error fetching a pending UUID");
675      XCTAssertNotNil(pendingUUID, "Should be a pending request UUID after a trigger");
676  
677      NSData* d = [[NSData alloc]initWithBase64EncodedString:@"YXNkZgo=" options:0];
678      XCTAssertTrue([self.escrowRequest cachePrerecord:pendingUUID
679                                   serializedPrerecord:d
680                                                 error:&error], @"Should be able to cache a prerecord");
681      XCTAssertNil(error, @"Should be no error caching a prerecord");
682  
683  
684      // Now, the state machine should try and fail the upload
685      OCMVerifyAllWithDelay(self.escrowRequestPerformEscrowEnrollOperationMock, 10);
686  
687      NSDictionary<NSString*, NSString*>* statuses = [self.escrowRequest fetchStatuses:&error];
688      XCTAssertNotNil(statuses, @"Should be able to fetch statuses");
689      XCTAssertNil(error, @"Should be no error fetching statuses");
690  
691      XCTAssertEqual(statuses.count, 0, @"Should be zero in-flight status");
692  }
693  
694  - (void)testEscrowUploadNoCDPSOSPeerFailure {
695      [[[[self.escrowRequestPerformEscrowEnrollOperationMock expect] andCall:@selector(mockcdpFailNoCDPPeerUploadPrerecord:secretType:reply:)
696                                                                    onObject:self] ignoringNonObjectArgs]
697       cdpUploadPrerecord:[OCMArg any] secretType:0 reply:[OCMArg any]];
698  
699      NSError* error = nil;
700  
701      OCMExpect([self.escrowRequestInformCloudServicesOperationMock triggerCloudServicesPasscodeRequest:[OCMArg any] error:[OCMArg anyObjectRef]]).andCall(self, @selector(mockTriggerCloudServicesPasscodeRequest:error:));
702  
703      XCTAssertTrue([self.escrowRequest triggerEscrowUpdate:@"test" error:&error], @"Should be able to trigger an update");
704      XCTAssertNil(error, @"Should be no error triggering an escrow update");
705  
706      OCMVerifyAll(self.escrowRequestServerClassMock);
707  
708      XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should pause within some time");
709  
710      NSString* pendingUUID = [self.escrowRequest fetchRequestWaitingOnPasscode:&error];
711      XCTAssertNil(error, @"Should be no error fetching a pending UUID");
712      XCTAssertNotNil(pendingUUID, "Should be a pending request UUID after a trigger");
713  
714      NSData* d = [[NSData alloc]initWithBase64EncodedString:@"YXNkZgo=" options:0];
715      XCTAssertTrue([self.escrowRequest cachePrerecord:pendingUUID
716                                   serializedPrerecord:d
717                                                 error:&error], @"Should be able to cache a prerecord");
718      XCTAssertNil(error, @"Should be no error caching a prerecord");
719  
720  
721      // Now, the state machine should try and fail the upload
722      OCMVerifyAllWithDelay(self.escrowRequestPerformEscrowEnrollOperationMock, 10);
723  
724      XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should pause within some time");
725  
726      NSDictionary<NSString*, NSString*>* statuses = [self.escrowRequest fetchStatuses:&error];
727      XCTAssertNotNil(statuses, @"Should be able to fetch statuses");
728      XCTAssertNil(error, @"Should be no error fetching statuses");
729  
730      XCTAssertEqual(statuses.count, 0, @"Should be zero in-flight status");
731  }
732  
733  - (void)testPendingPreRecordsCheck {
734      [self allCloudServicesCallsSucceed];
735  
736      NSError* error = nil;
737  
738      XCTAssertTrue([self.escrowRequest triggerEscrowUpdate:@"test" error:&error], @"Should be able to trigger an update");
739      XCTAssertNil(error, @"Should be no error triggering an escrow update");
740  
741      XCTAssertTrue([self.escrowRequest triggerEscrowUpdate:@"test" error:&error], @"Should be able to trigger an update");
742      XCTAssertNil(error, @"Should be no error triggering an escrow update");
743  
744      XCTAssertTrue([self.escrowRequest triggerEscrowUpdate:@"test" error:&error], @"Should be able to trigger an update");
745      XCTAssertNil(error, @"Should be no error triggering an escrow update");
746  
747      NSDictionary<NSString*, NSString*>* statuses = [self.escrowRequest fetchStatuses:&error];
748      XCTAssertNotNil(statuses, @"Should be able to fetch statuses");
749      XCTAssertNil(error, @"Should be no error fetching statuses");
750  
751      XCTAssertEqual(statuses.count, 1, @"Should be one in-flight status");
752  
753      BOOL result = [self.escrowRequest pendingEscrowUpload:&error];
754      XCTAssertNil(error, @"Should be no error checking for pending uploads");
755      XCTAssertEqual(result, true, @"pendingEscrowUpload should return true");
756  
757  }
758  
759  - (void)testClearedPreRecordsCheck {
760      [self allCloudServicesCallsSucceed];
761  
762      NSError* error = nil;
763  
764      [[[[self.escrowRequestPerformEscrowEnrollOperationMock expect] andCall:@selector(mockcdpFailNoCDPPeerUploadPrerecord:secretType:reply:)
765                                                                    onObject:self] ignoringNonObjectArgs]
766      cdpUploadPrerecord:[OCMArg any] secretType:0 reply:[OCMArg any]];
767  
768      OCMExpect([self.escrowRequestInformCloudServicesOperationMock triggerCloudServicesPasscodeRequest:[OCMArg any] error:[OCMArg anyObjectRef]]).andCall(self, @selector(mockTriggerCloudServicesPasscodeRequest:error:));
769  
770      XCTAssertTrue([self.escrowRequest triggerEscrowUpdate:@"test" error:&error], @"Should be able to trigger an update");
771      XCTAssertNil(error, @"Should be no error triggering an escrow update");
772  
773      OCMVerifyAll(self.escrowRequestServerClassMock);
774  
775      XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should pause within some time");
776  
777      NSString* pendingUUID = [self.escrowRequest fetchRequestWaitingOnPasscode:&error];
778      XCTAssertNil(error, @"Should be no error fetching a pending UUID");
779      XCTAssertNotNil(pendingUUID, "Should be a pending request UUID after a trigger");
780  
781      NSData* d = [[NSData alloc]initWithBase64EncodedString:@"YXNkZgo=" options:0];
782      XCTAssertTrue([self.escrowRequest cachePrerecord:pendingUUID
783                                   serializedPrerecord:d
784                                                 error:&error], @"Should be able to cache a prerecord");
785      XCTAssertNil(error, @"Should be no error caching a prerecord");
786  
787  
788      // Now, the state machine should try and fail the upload
789      OCMVerifyAllWithDelay(self.escrowRequestPerformEscrowEnrollOperationMock, 10);
790  
791      NSDictionary<NSString*, NSString*>* statuses = [self.escrowRequest fetchStatuses:&error];
792      XCTAssertNotNil(statuses, @"Should be able to fetch statuses");
793      XCTAssertNil(error, @"Should be no error fetching statuses");
794  
795      XCTAssertEqual(statuses.count, 0, @"Should be zero in-flight status");
796  
797      BOOL result = [self.escrowRequest pendingEscrowUpload:&error];
798      XCTAssertNil(error, @"Should be no error checking for pending uploads");
799      XCTAssertEqual(result, false, @"pendingEscrowUpload should return true");
800  }
801  
802  - (void)testServerPendingPreRecordsCheck {
803      [self allCloudServicesCallsSucceed];
804  
805      NSError* error = nil;
806  
807      XCTAssertTrue([self.escrowServer triggerEscrowUpdate:@"test" error:&error], @"Should be able to trigger an update");
808      XCTAssertNil(error, @"Should be no error triggering an escrow update");
809  
810      XCTAssertTrue([self.escrowServer triggerEscrowUpdate:@"test" error:&error], @"Should be able to trigger an update");
811      XCTAssertNil(error, @"Should be no error triggering an escrow update");
812  
813      XCTAssertTrue([self.escrowServer triggerEscrowUpdate:@"test" error:&error], @"Should be able to trigger an update");
814      XCTAssertNil(error, @"Should be no error triggering an escrow update");
815  
816      NSDictionary<NSString*, NSString*>* statuses = [self.escrowServer fetchStatuses:&error];
817      XCTAssertNotNil(statuses, @"Should be able to fetch statuses");
818      XCTAssertNil(error, @"Should be no error fetching statuses");
819  
820      XCTAssertEqual(statuses.count, 1, @"Should be one in-flight status");
821  
822      BOOL result = [self.escrowServer pendingEscrowUpload:&error];
823      XCTAssertNil(error, @"Should be no error checking for pending uploads");
824      XCTAssertEqual(result, true, @"pendingEscrowUpload should return true");
825  
826  }
827  
828  - (void)testServerClearedPreRecordsCheck {
829      [self allCloudServicesCallsSucceed];
830  
831      NSError* error = nil;
832  
833      [[[[self.escrowRequestPerformEscrowEnrollOperationMock expect] andCall:@selector(mockcdpFailNoCDPPeerUploadPrerecord:secretType:reply:)
834                                                                    onObject:self] ignoringNonObjectArgs]
835      cdpUploadPrerecord:[OCMArg any] secretType:0 reply:[OCMArg any]];
836  
837      OCMExpect([self.escrowRequestInformCloudServicesOperationMock triggerCloudServicesPasscodeRequest:[OCMArg any] error:[OCMArg anyObjectRef]]).andCall(self, @selector(mockTriggerCloudServicesPasscodeRequest:error:));
838  
839      XCTAssertTrue([self.escrowRequest triggerEscrowUpdate:@"test" error:&error], @"Should be able to trigger an update");
840      XCTAssertNil(error, @"Should be no error triggering an escrow update");
841  
842      OCMVerifyAll(self.escrowRequestServerClassMock);
843  
844      XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should pause within some time");
845  
846      NSString* pendingUUID = [self.escrowRequest fetchRequestWaitingOnPasscode:&error];
847      XCTAssertNil(error, @"Should be no error fetching a pending UUID");
848      XCTAssertNotNil(pendingUUID, "Should be a pending request UUID after a trigger");
849  
850      NSData* d = [[NSData alloc]initWithBase64EncodedString:@"YXNkZgo=" options:0];
851      XCTAssertTrue([self.escrowRequest cachePrerecord:pendingUUID
852                                   serializedPrerecord:d
853                                                 error:&error], @"Should be able to cache a prerecord");
854      XCTAssertNil(error, @"Should be no error caching a prerecord");
855  
856  
857      // Now, the state machine should try and fail the upload
858      OCMVerifyAllWithDelay(self.escrowRequestPerformEscrowEnrollOperationMock, 10);
859  
860      NSDictionary<NSString*, NSString*>* statuses = [self.escrowServer fetchStatuses:&error];
861      XCTAssertNotNil(statuses, @"Should be able to fetch statuses");
862      XCTAssertNil(error, @"Should be no error fetching statuses");
863  
864      XCTAssertEqual(statuses.count, 0, @"Should be zero in-flight status");
865  
866      BOOL result = [self.escrowServer pendingEscrowUpload:&error];
867      XCTAssertNil(error, @"Should be no error checking for pending uploads");
868      XCTAssertEqual(result, false, @"pendingEscrowUpload should return true");
869  }
870  
871  @end