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>
8 #import "keychain/categories/NSError+UsefulConstructors.h"
9 #import "keychain/escrowrequest/Framework/SecEscrowRequest.h"
10 #import "keychain/escrowrequest/EscrowRequestServer.h"
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"
17 #import "keychain/escrowrequest/tests/MockSynchronousEscrowServer.h"
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"
25 #include "tests/secdmockaks/mockaks.h"
27 @interface SecEscrowRequestTests : XCTestCase
28 @property SecEscrowRequest* escrowRequest;
29 @property EscrowRequestServer* escrowServer;
30 @property CKKSLockStateTracker* lockStateTracker;
32 @property id escrowRequestServerClassMock;
33 @property id escrowRequestInformCloudServicesOperationMock;
34 @property id escrowRequestPerformEscrowEnrollOperationMock;
37 @implementation SecEscrowRequestTests
40 securityd_init_local_spi();
41 EscrowRequestServerSetEnabled(true);
45 NSString* testName = [self.name componentsSeparatedByString:@" "][1];
46 testName = [testName stringByReplacingOccurrencesOfString:@"]" withString:@""];
47 secnotice("secescrowtest", "Beginning test %@", testName);
49 [SecMockAKS unlockAllClasses];
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];
56 SecCKKSTestDisableSOS();
58 // Mock out the SBD layer
59 self.escrowRequestServerClassMock = OCMClassMock([EscrowRequestServer class]);
60 self.escrowRequestInformCloudServicesOperationMock = OCMClassMock([EscrowRequestInformCloudServicesOperation class]);
61 self.escrowRequestPerformEscrowEnrollOperationMock = OCMClassMock([EscrowRequestPerformEscrowEnrollOperation class]);
63 self.lockStateTracker = [[CKKSLockStateTracker alloc] init];
64 self.escrowServer = [[EscrowRequestServer alloc] initWithLockStateTracker:self.lockStateTracker];
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];
71 SetCustomHomeURLString((__bridge CFStringRef) tmp_dir);
72 SecKeychainDbReset(NULL);
74 // Actually load the database.
75 kc_with_dbt(true, NULL, ^bool (SecDbConnectionRef dbt) { return false; });
79 - (NSData* _Nullable)mockTriggerCloudServicesPasscodeRequest:(NSString*)uuid error:(NSError**)error
81 return [@"certdata" dataUsingEncoding:NSUTF8StringEncoding];
84 - (NSData* _Nullable)mockTriggerCloudServicesPasscodeRequestAndLockClassA:(NSString*)uuid error:(NSError**)error
86 [SecMockAKS lockClassA];
87 [self.lockStateTracker recheck];
88 return [@"certdata" dataUsingEncoding:NSUTF8StringEncoding];
91 - (NSData* _Nullable)mockFailTriggerCloudServicesPasscodeRequest:(NSString*)uuid error:(NSError**)error
94 *error = [NSError errorWithDomain:@"NSURLErrorDomain"
96 description:@"The internet connection appears to be offline (mock)"];
101 - (void)mockcdpUploadPrerecord:(SecEscrowPendingRecord*)recordToSend
102 secretType:(CDPDeviceSecretType)secretType
103 reply:(void (^)(bool didUpdate, NSError* _Nullable error))reply
105 dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^ {
110 - (void)mockFailcdpUploadPrerecord:(SecEscrowPendingRecord*)recordToSend
111 secretType:(CDPDeviceSecretType)secretType
112 reply:(void (^)(bool didUpdate, NSError* _Nullable error))reply
114 dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^ {
115 reply(NO, [NSError errorWithDomain:@"EscrowServiceErrorDomain" code:-6570 description:@"mock CLUBH error"]);
119 - (void)mockcdpFailBadPeerUploadPrerecord:(SecEscrowPendingRecord*)recordToSend
120 secretType:(CDPDeviceSecretType)secretType
121 reply:(void (^)(bool didUpdate, NSError* _Nullable error))reply
123 dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^ {
124 reply(NO, [NSError errorWithDomain:kSecureBackupErrorDomain code:kSecureBackupInternalError description:@"SOS peer ID mismatch"]);
128 - (void)mockcdpFailNoSOSPeerUploadPrerecord:(SecEscrowPendingRecord*)recordToSend
129 secretType:(CDPDeviceSecretType)secretType
130 reply:(void (^)(bool didUpdate, NSError* _Nullable error))reply
132 dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^ {
133 reply(NO, [NSError errorWithDomain:kSecureBackupErrorDomain code:kSecureBackupNotInSyncCircleError description:@"SOS peer not present"]);
137 - (void)mockcdpFailNoCDPPeerUploadPrerecord:(SecEscrowPendingRecord*)recordToSend
138 secretType:(CDPDeviceSecretType)secretType
139 reply:(void (^)(bool didUpdate, NSError* _Nullable error))reply
141 dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^ {
142 reply(NO, [NSError errorWithDomain:CDPStateErrorDomain code:CDPStateErrorNoPeerIdFound description:@"SOS peer not present"]);
147 - (id<EscrowRequestXPCProtocol>)synchronousEscrowServer
149 return [[MockSynchronousEscrowServer alloc] initWithServer:self.escrowServer];
153 [self.escrowServer.controller.stateMachine haltOperation];
156 - (void)allCloudServicesCallsSucceed {
157 OCMStub([self.escrowRequestInformCloudServicesOperationMock triggerCloudServicesPasscodeRequest:[OCMArg any] error:[OCMArg anyObjectRef]]).andCall(self, @selector(mockTriggerCloudServicesPasscodeRequest:error:));
160 - (void)testStateMachineEntersNothingToDo
162 [self.escrowServer.controller.stateMachine startOperation];
163 XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.stateConditions[EscrowRequestStateNothingToDo] wait:10*NSEC_PER_SEC], "State machine enters NothingToDo");
166 - (void)testTriggerUpdate {
167 [self allCloudServicesCallsSucceed];
169 NSError* error = nil;
171 XCTAssertNil([self.escrowRequest fetchRequestWaitingOnPasscode:&error], @"Should be no pending updates");
172 XCTAssertNil(error, @"Should be no error fetching pending updates");
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");
177 XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should churn through");
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");
184 - (void)testTriggerUpdateWhileLocked {
185 [self allCloudServicesCallsSucceed];
187 [SecMockAKS lockClassA];
189 NSError* error = nil;
191 XCTAssertNil([self.escrowRequest fetchRequestWaitingOnPasscode:&error], @"Should be no pending updates");
192 XCTAssertNil(error, @"Should be no error fetching pending updates");
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)");
198 XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should churn through");
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");
205 - (void)testMultipleTriggers {
206 [self allCloudServicesCallsSucceed];
208 NSError* error = nil;
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");
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");
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");
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");
223 XCTAssertEqual(statuses.count, 1, @"Should be one in-flight status");
226 - (void)testFetchUpdateWhileLocked {
227 [self allCloudServicesCallsSucceed];
229 NSError* error = nil;
231 XCTAssertNil([self.escrowRequest fetchRequestWaitingOnPasscode:&error], @"Should be no pending updates");
232 XCTAssertNil(error, @"Should be no error fetching pending updates");
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");
237 XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should churn through");
240 [SecMockAKS lockClassA];
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");
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;
251 // No rate limiting for us!
252 self.escrowServer.controller.forceIgnoreCloudServicesRateLimiting = true;
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];
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");
262 // First, let's ensure that we properly handle the case where the device is locked when we go to ask CloudServices about things
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");
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];
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");
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];
283 OCMVerifyAllWithDelay(self.escrowRequestInformCloudServicesOperationMock, 10);
286 - (void)testRateLimitSBDTriggerWhenFailing {
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:));
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");
295 OCMVerifyAll(self.escrowRequestServerClassMock);
297 // And, the state machine should pause
298 XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should rest");
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");
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];
311 XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should rest");
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");
318 - (void)testRateLimitSBDUploadWhenFailing {
320 // Trigger an escrow update, which should call CloudServices
321 OCMExpect([self.escrowRequestInformCloudServicesOperationMock triggerCloudServicesPasscodeRequest:[OCMArg any] error:[OCMArg anyObjectRef]]).andCall(self, @selector(mockTriggerCloudServicesPasscodeRequest:error:));
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");
327 OCMVerifyAllWithDelay(self.escrowRequestInformCloudServicesOperationMock, 5);
329 XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should pause within some time");
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");
336 [[[[self.escrowRequestPerformEscrowEnrollOperationMock expect] andCall:@selector(mockFailcdpUploadPrerecord:secretType:reply:)
337 onObject:self] ignoringNonObjectArgs]
338 cdpUploadPrerecord:[OCMArg any] secretType:0 reply:[OCMArg any]];
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");
347 // Wait for the upload to fail...
348 OCMVerifyAllWithDelay(self.escrowRequestPerformEscrowEnrollOperationMock, 10);
350 XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should rest");
352 //////////////////////////
353 // Trigger another update, to check coalescing
354 OCMExpect([self.escrowRequestInformCloudServicesOperationMock triggerCloudServicesPasscodeRequest:[OCMArg any] error:[OCMArg anyObjectRef]]).andCall(self, @selector(mockTriggerCloudServicesPasscodeRequest:error:));
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);
360 XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should rest");
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");
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");
375 XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should rest");
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");
381 XCTAssertEqual(statuses.count, 1, @"Should be one in-flight status");
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]];
389 [self.escrowServer.controller.stateMachine startOperation];
391 XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should rest");
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");
398 XCTAssertEqual(statuses.count, 1, @"Should be one in-flight status");
402 - (void)testPrerecordCaching {
403 NSError* error = nil;
405 // Trigger an escrow update, which should call CloudServices
406 OCMExpect([self.escrowRequestInformCloudServicesOperationMock triggerCloudServicesPasscodeRequest:[OCMArg any] error:[OCMArg anyObjectRef]]).andCall(self, @selector(mockTriggerCloudServicesPasscodeRequest:error:));
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");
411 OCMVerifyAll(self.escrowRequestServerClassMock);
413 XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should rest");
415 // Now, there should be a pending request
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");
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]];
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");
434 // Wait for the upload to fail...
435 OCMVerifyAllWithDelay(self.escrowRequestPerformEscrowEnrollOperationMock, 10);
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");
441 NSData* d2 = [self.escrowRequest fetchPrerecord:pendingUUID
443 XCTAssertNotNil(d2, @"Should be able to retrieve a cached prerecord");
444 XCTAssertNil(error, @"Should be no error fetching a cached prerecord");
446 XCTAssertEqualObjects(d,d2, @"Cached prerecord should be equal to original");
449 - (void)testPrerecordCachingWhileLocked {
450 NSError* error = nil;
452 // Trigger an escrow update, which should call CloudServices
453 OCMExpect([self.escrowRequestInformCloudServicesOperationMock triggerCloudServicesPasscodeRequest:[OCMArg any] error:[OCMArg anyObjectRef]]).andCall(self, @selector(mockTriggerCloudServicesPasscodeRequest:error:));
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");
458 OCMVerifyAll(self.escrowRequestServerClassMock);
460 XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should rest");
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");
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]];
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");
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);
483 // And now, when we unlock, there's still a pending request
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");
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]];
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");
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);
508 XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should pause within some time");
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");
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");
520 // Don't call the "do it" RPC, but it should happen anyway
521 OCMVerifyAllWithDelay(self.escrowRequestPerformEscrowEnrollOperationMock, 10);
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]];
529 NSError* error = nil;
531 OCMExpect([self.escrowRequestInformCloudServicesOperationMock triggerCloudServicesPasscodeRequest:[OCMArg any] error:[OCMArg anyObjectRef]]).andCall(self, @selector(mockTriggerCloudServicesPasscodeRequest:error:));
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");
536 OCMVerifyAll(self.escrowRequestServerClassMock);
538 XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should pause within some time");
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");
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");
551 // Now, the state machine should try and fail the upload
552 OCMVerifyAllWithDelay(self.escrowRequestPerformEscrowEnrollOperationMock, 10);
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");
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]];
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");
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");
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");
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]];
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");
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);
592 XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should pause within some time");
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");
598 // We need to lock directly after the prerecord is cached, but before the CDP attempt is attempted
599 [self.escrowServer.controller.stateMachine haltOperation];
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");
607 [SecMockAKS lockClassA];
608 [self.escrowServer.controller.stateMachine startOperation];
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");
613 // The upload should be tried after an unlock
614 [SecMockAKS unlockAllClasses];
615 [self.lockStateTracker recheck];
617 OCMVerifyAllWithDelay(self.escrowRequestPerformEscrowEnrollOperationMock, 10);
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]];
625 NSError* error = nil;
627 OCMExpect([self.escrowRequestInformCloudServicesOperationMock triggerCloudServicesPasscodeRequest:[OCMArg any] error:[OCMArg anyObjectRef]]).andCall(self, @selector(mockTriggerCloudServicesPasscodeRequest:error:));
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");
632 OCMVerifyAll(self.escrowRequestServerClassMock);
634 XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should pause within some time");
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");
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");
647 // Now, the state machine should try and fail the upload
648 OCMVerifyAllWithDelay(self.escrowRequestPerformEscrowEnrollOperationMock, 10);
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");
654 XCTAssertEqual(statuses.count, 0, @"Should be zero in-flight status");
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]];
662 NSError* error = nil;
664 OCMExpect([self.escrowRequestInformCloudServicesOperationMock triggerCloudServicesPasscodeRequest:[OCMArg any] error:[OCMArg anyObjectRef]]).andCall(self, @selector(mockTriggerCloudServicesPasscodeRequest:error:));
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");
669 OCMVerifyAll(self.escrowRequestServerClassMock);
671 XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should pause within some time");
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");
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");
684 // Now, the state machine should try and fail the upload
685 OCMVerifyAllWithDelay(self.escrowRequestPerformEscrowEnrollOperationMock, 10);
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");
691 XCTAssertEqual(statuses.count, 0, @"Should be zero in-flight status");
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]];
699 NSError* error = nil;
701 OCMExpect([self.escrowRequestInformCloudServicesOperationMock triggerCloudServicesPasscodeRequest:[OCMArg any] error:[OCMArg anyObjectRef]]).andCall(self, @selector(mockTriggerCloudServicesPasscodeRequest:error:));
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");
706 OCMVerifyAll(self.escrowRequestServerClassMock);
708 XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should pause within some time");
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");
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");
721 // Now, the state machine should try and fail the upload
722 OCMVerifyAllWithDelay(self.escrowRequestPerformEscrowEnrollOperationMock, 10);
724 XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should pause within some time");
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");
730 XCTAssertEqual(statuses.count, 0, @"Should be zero in-flight status");
733 - (void)testPendingPreRecordsCheck {
734 [self allCloudServicesCallsSucceed];
736 NSError* error = nil;
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");
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");
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");
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");
751 XCTAssertEqual(statuses.count, 1, @"Should be one in-flight status");
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");
759 - (void)testClearedPreRecordsCheck {
760 [self allCloudServicesCallsSucceed];
762 NSError* error = nil;
764 [[[[self.escrowRequestPerformEscrowEnrollOperationMock expect] andCall:@selector(mockcdpFailNoCDPPeerUploadPrerecord:secretType:reply:)
765 onObject:self] ignoringNonObjectArgs]
766 cdpUploadPrerecord:[OCMArg any] secretType:0 reply:[OCMArg any]];
768 OCMExpect([self.escrowRequestInformCloudServicesOperationMock triggerCloudServicesPasscodeRequest:[OCMArg any] error:[OCMArg anyObjectRef]]).andCall(self, @selector(mockTriggerCloudServicesPasscodeRequest:error:));
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");
773 OCMVerifyAll(self.escrowRequestServerClassMock);
775 XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should pause within some time");
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");
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");
788 // Now, the state machine should try and fail the upload
789 OCMVerifyAllWithDelay(self.escrowRequestPerformEscrowEnrollOperationMock, 10);
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");
795 XCTAssertEqual(statuses.count, 0, @"Should be zero in-flight status");
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");
802 - (void)testServerPendingPreRecordsCheck {
803 [self allCloudServicesCallsSucceed];
805 NSError* error = nil;
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");
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");
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");
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");
820 XCTAssertEqual(statuses.count, 1, @"Should be one in-flight status");
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");
828 - (void)testServerClearedPreRecordsCheck {
829 [self allCloudServicesCallsSucceed];
831 NSError* error = nil;
833 [[[[self.escrowRequestPerformEscrowEnrollOperationMock expect] andCall:@selector(mockcdpFailNoCDPPeerUploadPrerecord:secretType:reply:)
834 onObject:self] ignoringNonObjectArgs]
835 cdpUploadPrerecord:[OCMArg any] secretType:0 reply:[OCMArg any]];
837 OCMExpect([self.escrowRequestInformCloudServicesOperationMock triggerCloudServicesPasscodeRequest:[OCMArg any] error:[OCMArg anyObjectRef]]).andCall(self, @selector(mockTriggerCloudServicesPasscodeRequest:error:));
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");
842 OCMVerifyAll(self.escrowRequestServerClassMock);
844 XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should pause within some time");
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");
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");
857 // Now, the state machine should try and fail the upload
858 OCMVerifyAllWithDelay(self.escrowRequestPerformEscrowEnrollOperationMock, 10);
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");
864 XCTAssertEqual(statuses.count, 0, @"Should be zero in-flight status");
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");