GULKeychainStorageTests.m 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. /*
  2. * Copyright 2019 Google
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #import <XCTest/XCTest.h>
  17. #import <OCMock/OCMock.h>
  18. #import "FBLPromise+Testing.h"
  19. #import "GoogleUtilities/Example/Tests/Utils/GULTestKeychain.h"
  20. #import "GoogleUtilities/Environment/Private/GULKeychainStorage.h"
  21. @interface GULKeychainStorage (Tests)
  22. - (instancetype)initWithService:(NSString *)service cache:(NSCache *)cache;
  23. - (void)resetInMemoryCache;
  24. @end
  25. @interface GULKeychainStorageTests : XCTestCase
  26. @property(nonatomic, strong) GULKeychainStorage *storage;
  27. @property(nonatomic, strong) NSCache *cache;
  28. @property(nonatomic, strong) id mockCache;
  29. #if TARGET_OS_OSX
  30. @property(nonatomic) GULTestKeychain *privateKeychain;
  31. #endif // TARGET_OSX
  32. @end
  33. @implementation GULKeychainStorageTests
  34. - (void)setUp {
  35. self.cache = [[NSCache alloc] init];
  36. self.mockCache = OCMPartialMock(self.cache);
  37. self.storage = [[GULKeychainStorage alloc] initWithService:@"com.tests.GULKeychainStorageTests"
  38. cache:self.mockCache];
  39. #if TARGET_OS_OSX
  40. self.privateKeychain = [[GULTestKeychain alloc] init];
  41. self.storage.keychainRef = self.privateKeychain.testKeychainRef;
  42. #endif // TARGET_OSX
  43. }
  44. - (void)tearDown {
  45. self.storage = nil;
  46. self.mockCache = nil;
  47. self.cache = nil;
  48. #if TARGET_OS_OSX
  49. self.privateKeychain = nil;
  50. #endif // TARGET_OSX
  51. }
  52. - (void)testSetGetObjectForKey {
  53. // 1. Write and read object initially.
  54. [self assertSuccessWriteObject:@[ @1, @2 ] forKey:@"test-key1"];
  55. [self assertSuccessReadObject:@[ @1, @2 ]
  56. forKey:@"test-key1"
  57. class:[NSArray class]
  58. existsInCache:YES];
  59. // // 2. Override existing object.
  60. [self assertSuccessWriteObject:@{@"key" : @"value"} forKey:@"test-key1"];
  61. [self assertSuccessReadObject:@{@"key" : @"value"}
  62. forKey:@"test-key1"
  63. class:[NSDictionary class]
  64. existsInCache:YES];
  65. // 3. Read existing object which is not present in in-memory cache.
  66. [self.cache removeAllObjects];
  67. [self assertSuccessReadObject:@{@"key" : @"value"}
  68. forKey:@"test-key1"
  69. class:[NSDictionary class]
  70. existsInCache:NO];
  71. // 4. Write and read an object for another key.
  72. [self assertSuccessWriteObject:@{@"key" : @"value"} forKey:@"test-key2"];
  73. [self assertSuccessReadObject:@{@"key" : @"value"}
  74. forKey:@"test-key2"
  75. class:[NSDictionary class]
  76. existsInCache:YES];
  77. }
  78. - (void)testGetNonExistingObject {
  79. [self assertNonExistingObjectForKey:[NSUUID UUID].UUIDString class:[NSArray class]];
  80. }
  81. - (void)testGetExistingObjectClassMismatch {
  82. NSString *key = [NSUUID UUID].UUIDString;
  83. // Write.
  84. [self assertSuccessWriteObject:@[ @8 ] forKey:key];
  85. // Read.
  86. // Skip in-memory cache because the error is relevant only for Keychain.
  87. OCMExpect([self.mockCache objectForKey:key]).andReturn(nil);
  88. FBLPromise<id<NSSecureCoding>> *getPromise = [self.storage getObjectForKey:key
  89. objectClass:[NSString class]
  90. accessGroup:nil];
  91. XCTAssert(FBLWaitForPromisesWithTimeout(1));
  92. XCTAssertNil(getPromise.value);
  93. XCTAssertNotNil(getPromise.error);
  94. // TODO: Test for particular error.
  95. OCMVerifyAll(self.mockCache);
  96. }
  97. - (void)testRemoveExistingObject {
  98. NSString *key = @"testRemoveExistingObject";
  99. // Store the object.
  100. [self assertSuccessWriteObject:@[ @5 ] forKey:(NSString *)key];
  101. // Remove object.
  102. [self assertRemoveObjectForKey:key];
  103. // Check if object is still stored.
  104. [self assertNonExistingObjectForKey:key class:[NSArray class]];
  105. }
  106. - (void)testRemoveNonExistingObject {
  107. NSString *key = [NSUUID UUID].UUIDString;
  108. [self assertRemoveObjectForKey:key];
  109. [self assertNonExistingObjectForKey:key class:[NSArray class]];
  110. }
  111. #pragma mark - Common
  112. - (void)assertSuccessWriteObject:(id<NSSecureCoding>)object forKey:(NSString *)key {
  113. OCMExpect([self.mockCache setObject:object forKey:key]).andForwardToRealObject();
  114. FBLPromise<NSNull *> *setPromise = [self.storage setObject:object forKey:key accessGroup:nil];
  115. XCTAssert(FBLWaitForPromisesWithTimeout(1));
  116. XCTAssertNil(setPromise.error, @"%@", self.name);
  117. OCMVerifyAll(self.mockCache);
  118. // Check in-memory cache.
  119. XCTAssertEqualObjects([self.cache objectForKey:key], object);
  120. }
  121. - (void)assertSuccessReadObject:(id<NSSecureCoding>)object
  122. forKey:(NSString *)key
  123. class:(Class)class
  124. existsInCache:(BOOL)existisInCache {
  125. OCMExpect([self.mockCache objectForKey:key]).andForwardToRealObject();
  126. if (!existisInCache) {
  127. OCMExpect([self.mockCache setObject:object forKey:key]).andForwardToRealObject();
  128. }
  129. FBLPromise<id<NSSecureCoding>> *getPromise =
  130. [self.storage getObjectForKey:key objectClass:class accessGroup:nil];
  131. XCTAssert(FBLWaitForPromisesWithTimeout(1), @"%@", self.name);
  132. XCTAssertEqualObjects(getPromise.value, object, @"%@", self.name);
  133. XCTAssertNil(getPromise.error, @"%@", self.name);
  134. OCMVerifyAll(self.mockCache);
  135. // Check in-memory cache.
  136. XCTAssertEqualObjects([self.cache objectForKey:key], object, @"%@", self.name);
  137. }
  138. - (void)assertNonExistingObjectForKey:(NSString *)key class:(Class)class {
  139. OCMExpect([self.mockCache objectForKey:key]).andForwardToRealObject();
  140. FBLPromise<id<NSSecureCoding>> *promise =
  141. [self.storage getObjectForKey:key objectClass:class accessGroup:nil];
  142. XCTAssert(FBLWaitForPromisesWithTimeout(1));
  143. XCTAssertNil(promise.error, @"%@", self.name);
  144. XCTAssertNil(promise.value, @"%@", self.name);
  145. OCMVerifyAll(self.mockCache);
  146. }
  147. - (void)assertRemoveObjectForKey:(NSString *)key {
  148. OCMExpect([self.mockCache removeObjectForKey:key]).andForwardToRealObject();
  149. FBLPromise<NSNull *> *removePromise = [self.storage removeObjectForKey:key accessGroup:nil];
  150. XCTAssert(FBLWaitForPromisesWithTimeout(1));
  151. XCTAssertNil(removePromise.error);
  152. OCMVerifyAll(self.mockCache);
  153. }
  154. @end