]> git.saurik.com Git - apple/security.git/blob - keychain/ckks/tests/CKKSSQLTests.m
Security-58286.31.2.tar.gz
[apple/security.git] / keychain / ckks / tests / CKKSSQLTests.m
1 /*
2 * Copyright (c) 2016 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 #if OCTAGON
25
26 #import <XCTest/XCTest.h>
27 #import "CloudKitMockXCTest.h"
28
29 #import "keychain/ckks/CKKS.h"
30 #import "keychain/ckks/CKKSKey.h"
31 #import "keychain/ckks/CKKSOutgoingQueueEntry.h"
32 #import "keychain/ckks/CKKSZoneStateEntry.h"
33 #import "keychain/ckks/CKKSDeviceStateEntry.h"
34 #import "keychain/ckks/CKKSRateLimiter.h"
35
36 #include <securityd/SecItemServer.h>
37
38 @interface CloudKitKeychainSQLTests : CloudKitMockXCTest
39 @end
40
41 @implementation CloudKitKeychainSQLTests
42
43 + (void)setUp {
44 [super setUp];
45 }
46
47 - (void)setUp {
48 SecCKKSDisable();
49 [super setUp];
50 }
51
52 - (void)tearDown {
53 [super tearDown];
54 SecCKKSResetSyncing();
55 }
56
57 - (void)addTestZoneEntries {
58 CKKSOutgoingQueueEntry* one = [[CKKSOutgoingQueueEntry alloc] initWithCKKSItem:
59 [[CKKSItem alloc] initWithUUID:[[NSUUID UUID] UUIDString]
60 parentKeyUUID:[[NSUUID UUID] UUIDString]
61 zoneID:self.testZoneID
62 encItem:[@"nonsense" dataUsingEncoding:NSUTF8StringEncoding]
63 wrappedkey:[[CKKSWrappedAESSIVKey alloc]initWithBase64: @"KFfL58XtugiYNoD859EjG0StfrYd6eakm0CQrgX7iO+DEo4kio3WbEeA1kctCU0GaeTGsRFpbdy4oo6jXhVu7cZqB0svhUPGq55aGnszUjI="]
64 generationCount:0
65 encver:0]
66 action:SecCKKSActionAdd
67 state:SecCKKSStateError
68 waitUntil:nil
69 accessGroup:@"nope"];
70
71
72 CKKSOutgoingQueueEntry* two = [[CKKSOutgoingQueueEntry alloc] initWithCKKSItem:
73 [[CKKSItem alloc] initWithUUID:[[NSUUID UUID] UUIDString]
74 parentKeyUUID:[[NSUUID UUID] UUIDString]
75 zoneID:self.testZoneID
76 encItem:[@"nonsense" dataUsingEncoding:NSUTF8StringEncoding]
77 wrappedkey:[[CKKSWrappedAESSIVKey alloc]initWithBase64: @"KFfL58XtugiYNoD859EjG0StfrYd6eakm0CQrgX7iO+DEo4kio3WbEeA1kctCU0GaeTGsRFpbdy4oo6jXhVu7cZqB0svhUPGq55aGnszUjI="]
78 generationCount:0
79 encver:0]
80 action:SecCKKSActionAdd
81 state:SecCKKSStateNew
82 waitUntil:nil
83 accessGroup:@"nope"];
84
85 CKKSOutgoingQueueEntry* three = [[CKKSOutgoingQueueEntry alloc] initWithCKKSItem:
86 [[CKKSItem alloc] initWithUUID:[[NSUUID UUID] UUIDString]
87 parentKeyUUID:[[NSUUID UUID] UUIDString]
88 zoneID:self.testZoneID
89 encItem:[@"nonsense" dataUsingEncoding:NSUTF8StringEncoding]
90 wrappedkey:[[CKKSWrappedAESSIVKey alloc]initWithBase64: @"KFfL58XtugiYNoD859EjG0StfrYd6eakm0CQrgX7iO+DEo4kio3WbEeA1kctCU0GaeTGsRFpbdy4oo6jXhVu7cZqB0svhUPGq55aGnszUjI="]
91 generationCount:0
92 encver:0]
93 action:SecCKKSActionModify
94 state:SecCKKSStateError
95 waitUntil:nil
96 accessGroup:@"nope"];
97
98 NSError* error = nil;
99 [one saveToDatabase:&error];
100 [two saveToDatabase: &error];
101 [three saveToDatabase: &error];
102 XCTAssertNil(error, "no error saving ZoneStateEntries to database");
103 }
104
105 - (void)testCKKSOutgoingQueueEntry {
106 NSString* testUUID = @"157A3171-0677-451B-9EAE-0DDC4D4315B0";
107 NSUUID* testKeyUUID = [[NSUUID alloc] init];
108
109 NSError * nserror;
110 __block CFErrorRef error = NULL;
111
112 CKKSOutgoingQueueEntry* shouldFail = [CKKSOutgoingQueueEntry fromDatabase:testUUID state:SecCKKSStateInFlight zoneID:self.testZoneID error: &nserror];
113 XCTAssertNil(shouldFail, "Can't find a nonexisting object");
114 XCTAssertNotNil(nserror, "NSError exists when things break");
115
116 __weak __typeof(self) weakSelf = self;
117 kc_with_dbt(true, &error, ^bool (SecDbConnectionRef dbconn) {
118 __strong __typeof(weakSelf) strongSelf = weakSelf;
119 XCTAssertNotNil(strongSelf, "called while self still exists");
120
121 NSString * sql = @"insert INTO outgoingqueue (UUID, parentKeyUUID, ckzone, action, state, accessgroup, gencount, encitem, wrappedkey, encver) VALUES (?,?,?,?,?,?,?,?,?,?);";
122 SecDbPrepare(dbconn, (__bridge CFStringRef) sql, &error, ^void (sqlite3_stmt *stmt) {
123 SecDbBindText(stmt, 1, [testUUID UTF8String], strlen([testUUID UTF8String]), NULL, &error);
124 SecDbBindText(stmt, 2, [[testKeyUUID UUIDString] UTF8String], strlen([[testKeyUUID UUIDString] UTF8String]), NULL, &error);
125 SecDbBindObject(stmt, 3, (__bridge CFStringRef) weakSelf.testZoneID.zoneName, &error);
126 SecDbBindText(stmt, 4, "newitem", strlen("newitem"), NULL, &error);
127 SecDbBindText(stmt, 5, "unprocessed", strlen("unprocessed"), NULL, &error);
128 SecDbBindText(stmt, 6, "com.apple.access", strlen("com.apple.access"), NULL, &error);
129 SecDbBindText(stmt, 7, "0", strlen("0"), NULL, &error);
130 SecDbBindText(stmt, 8, "bm9uc2Vuc2UK", strlen("bm9uc2Vuc2UK"), NULL, &error);
131 SecDbBindObject(stmt, 9, CFSTR("KFfL58XtugiYNoD859EjG0StfrYd6eakm0CQrgX7iO+DEo4kio3WbEeA1kctCU0GaeTGsRFpbdy4oo6jXhVu7cZqB0svhUPGq55aGnszUjI="), &error);
132 SecDbBindText(stmt, 10, "0", strlen("0"), NULL, &error);
133
134 SecDbStep(dbconn, stmt, &error, ^(bool *stop) {
135 // don't do anything, I guess?
136 });
137
138 XCTAssertNil((__bridge NSError*)error, @"no error occurred while adding row to database");
139
140 CFReleaseNull(error);
141 });
142 XCTAssertNil((__bridge NSError*)error, @"no error occurred preparing sql");
143
144 CFReleaseNull(error);
145 return true;
146 });
147
148 // Create another oqe with different values
149 CKKSItem* baseitem = [[CKKSItem alloc] initWithUUID: [[NSUUID UUID] UUIDString]
150 parentKeyUUID:[[NSUUID UUID] UUIDString]
151 zoneID:self.testZoneID
152 encItem:[@"nonsense" dataUsingEncoding:NSUTF8StringEncoding]
153 wrappedkey:[[CKKSWrappedAESSIVKey alloc]initWithBase64: @"KFfL58XtugiYNoD859EjG0StfrYd6eakm0CQrgX7iO+DEo4kio3WbEeA1kctCU0GaeTGsRFpbdy4oo6jXhVu7cZqB0svhUPGq55aGnszUjI="]
154 generationCount:0
155 encver:0];
156 CKKSOutgoingQueueEntry* other = [[CKKSOutgoingQueueEntry alloc] initWithCKKSItem:baseitem
157 action:SecCKKSActionAdd
158 state:SecCKKSStateError
159 waitUntil:[NSDate date]
160 accessGroup:@"nope"];
161 [other saveToDatabase:&nserror];
162 XCTAssertNil(nserror, "no error occurred saving to database");
163
164 CKKSOutgoingQueueEntry * oqe = [CKKSOutgoingQueueEntry fromDatabase:testUUID state:@"unprocessed" zoneID:self.testZoneID error: &nserror];
165 XCTAssertNil(nserror, "no error occurred creating from database");
166
167 XCTAssertNotNil(oqe, "load outgoing queue entry from database");
168 XCTAssertEqualObjects(oqe.state, @"unprocessed", "state matches what was in the DB");
169
170 oqe.item.parentKeyUUID = @"not a parent key either";
171 oqe.action = @"null";
172 oqe.state = @"savedtocloud";
173 oqe.accessgroup = @"com.evil.access";
174 oqe.item.generationCount = (NSInteger) 1;
175 oqe.item.base64encitem = @"bW9yZW5vbnNlbnNlCg==";
176 oqe.item.encver = 1;
177
178 XCTAssertTrue([oqe saveToDatabase: &nserror], "saving to database");
179
180 CKKSOutgoingQueueEntry * oqe2 = [CKKSOutgoingQueueEntry fromDatabase:testUUID state:@"savedtocloud" zoneID:self.testZoneID error: &nserror];
181 XCTAssertNil(nserror, "no error occurred");
182
183 XCTAssertEqualObjects(oqe2.item.parentKeyUUID, @"not a parent key either", @"parent key uuid persisted through db save and load");
184 XCTAssertEqualObjects(oqe2.item.zoneID , self.testZoneID , @"zone id persisted through db save and load");
185 XCTAssertEqualObjects(oqe2.action , @"null" , @"action persisted through db save and load");
186 XCTAssertEqualObjects(oqe2.state , @"savedtocloud" , @"state persisted through db save and load");
187 XCTAssertEqual( oqe2.waitUntil , nil , @"no date when none given");
188 XCTAssertEqualObjects(oqe2.accessgroup , @"com.evil.access" , @"accessgroup persisted through db save and load");
189 XCTAssertEqual( oqe2.item.generationCount, (NSUInteger) 1 , @"generationCount persisted through db save and load");
190 XCTAssertEqualObjects(oqe2.item.base64encitem, @"bW9yZW5vbnNlbnNlCg==" , @"encitem persisted through db save and load");
191 XCTAssertEqual( oqe2.item.encver, 1 , @"encver persisted through db save and load");
192 XCTAssertEqualObjects([oqe2.item.wrappedkey base64WrappedKey], @"KFfL58XtugiYNoD859EjG0StfrYd6eakm0CQrgX7iO+DEo4kio3WbEeA1kctCU0GaeTGsRFpbdy4oo6jXhVu7cZqB0svhUPGq55aGnszUjI=",
193 @"wrapped key persisted through db save and load");
194
195 // Test 'all' methods
196 NSArray<CKKSOutgoingQueueEntry*>* oqes = [CKKSOutgoingQueueEntry all:&nserror];
197 XCTAssertNil(nserror, "no error occurred");
198 XCTAssertNotNil(oqes, "receive oqes from database");
199 XCTAssert([oqes count] == 2, "received 2 oqes from all");
200
201 NSArray<CKKSOutgoingQueueEntry*>* oqeswhere = [CKKSOutgoingQueueEntry allWhere: @{@"state": @"savedtocloud"} error:&nserror];
202 XCTAssertNil(nserror, "no error occurred");
203 XCTAssertNotNil(oqeswhere, "receive oqes from database");
204 XCTAssert([oqeswhere count] == 1, "received 1 oqe from allWhere");
205
206 // Test row deletion
207 nserror = nil;
208 [oqe2 deleteFromDatabase:&nserror];
209 XCTAssertNil(nserror, "No NSError exists when deleting existing item");
210 oqe2 = [CKKSOutgoingQueueEntry fromDatabase:testUUID state:@"savedtocloud" zoneID:self.testZoneID error: &nserror];
211 XCTAssertNil(oqe2, "Can't find a nonexisting object");
212 XCTAssertNotNil(nserror, "NSError exists when things break");
213
214 // Test loading other
215 nserror = nil;
216 CKKSOutgoingQueueEntry* other2 = [CKKSOutgoingQueueEntry fromDatabase: other.item.uuid state:SecCKKSStateError zoneID:self.testZoneID error:&nserror];
217 XCTAssertNil(nserror, "No error loading other2 from database");
218 XCTAssertNotNil(other2, "Able to re-load other.");
219 XCTAssertEqualObjects(other, other2, "loaded object is equal to object");
220 }
221
222 -(void)testCKKSZoneStateEntrySQL {
223 CKKSZoneStateEntry* zse = [[CKKSZoneStateEntry alloc] initWithCKZone:@"sqltest"
224 zoneCreated:true
225 zoneSubscribed:true
226 changeToken:[@"nonsense" dataUsingEncoding:NSUTF8StringEncoding]
227 lastFetch:[NSDate date]
228 lastFixup:CKKSCurrentFixupNumber
229 encodedRateLimiter:nil];
230 zse.rateLimiter = [[CKKSRateLimiter alloc] init];
231
232 CKKSZoneStateEntry* zseClone = [[CKKSZoneStateEntry alloc] initWithCKZone:@"sqltest"
233 zoneCreated:true
234 zoneSubscribed:true
235 changeToken:[@"nonsense" dataUsingEncoding:NSUTF8StringEncoding]
236 lastFetch:zse.lastFetchTime
237 lastFixup:CKKSCurrentFixupNumber
238 encodedRateLimiter:zse.encodedRateLimiter];
239
240 CKKSZoneStateEntry* zseDifferent = [[CKKSZoneStateEntry alloc] initWithCKZone:@"sqltest"
241 zoneCreated:true
242 zoneSubscribed:true
243 changeToken:[@"allnonsense" dataUsingEncoding:NSUTF8StringEncoding]
244 lastFetch:zse.lastFetchTime
245 lastFixup:CKKSCurrentFixupNumber
246 encodedRateLimiter:zse.encodedRateLimiter];
247 XCTAssertEqualObjects(zse, zseClone, "CKKSZoneStateEntry isEqual of equal objects seems sane");
248 XCTAssertNotEqualObjects(zse, zseDifferent, "CKKSZoneStateEntry isEqual of nonequal objects seems sane");
249
250 NSError* error = nil;
251 CKKSZoneStateEntry* loaded = [CKKSZoneStateEntry tryFromDatabase: @"sqltest" error:&error];
252 XCTAssertNil(error, "No error trying to load nonexistent record");
253 XCTAssertNil(loaded, "No record saved in database");
254
255 [zse saveToDatabase: &error];
256 XCTAssertNil(error, "no error saving CKKSZoneStateEntry to database");
257
258 loaded = [CKKSZoneStateEntry tryFromDatabase: @"sqltest" error:&error];
259 XCTAssertNil(error, "No error trying to load saved record");
260 XCTAssertNotNil(loaded, "CKKSZoneStateEntry came back out of database");
261
262 XCTAssertEqualObjects(zse.ckzone, loaded.ckzone, "ckzone persisted through db save and load");
263 XCTAssertEqual (zse.ckzonecreated, loaded.ckzonecreated, "ckzonecreated persisted through db save and load");
264 XCTAssertEqual (zse.ckzonesubscribed, loaded.ckzonesubscribed, "ckzonesubscribed persisted through db save and load");
265 XCTAssertEqualObjects(zse.encodedChangeToken, loaded.encodedChangeToken, "encodedChangeToken persisted through db save and load");
266
267 XCTAssert([[NSCalendar currentCalendar] isDate:zse.lastFetchTime equalToDate: loaded.lastFetchTime toUnitGranularity:NSCalendarUnitSecond],
268 "lastFetchTime persisted through db save and load");
269 }
270
271 -(void)testRoundtripCKKSDeviceStateEntry {
272 // Very simple test: can these objects roundtrip through the db?
273 NSString* testUUID = @"157A3171-0677-451B-9EAE-0DDC4D4315B0";
274 CKKSDeviceStateEntry* cdse = [[CKKSDeviceStateEntry alloc] initForDevice:testUUID
275 circlePeerID:@"asdf"
276 circleStatus:kSOSCCInCircle
277 keyState:SecCKKSZoneKeyStateReady
278 currentTLKUUID:@"tlk"
279 currentClassAUUID:@"classA"
280 currentClassCUUID:@"classC"
281 zoneID:self.testZoneID
282 encodedCKRecord:nil];
283 XCTAssertNotNil(cdse, "Constructor works");
284 NSError* saveError = nil;
285 [cdse saveToDatabase:&saveError];
286 XCTAssertNil(saveError, "No error saving cdse to database");
287
288 NSError* loadError = nil;
289 CKKSDeviceStateEntry* loadedCDSE = [CKKSDeviceStateEntry fromDatabase:testUUID zoneID:self.testZoneID error:&loadError];
290 XCTAssertNil(loadError, "No error loading CDSE");
291 XCTAssertNotNil(loadedCDSE, "Received a CDSE back");
292
293 XCTAssertEqualObjects(cdse, loadedCDSE, "Roundtripping CKKSDeviceStateEntry ends up with equivalent objects");
294 }
295
296 // disabled, as CKKS syncing is disabled in this class.
297 // To re-enable, need to add flags CKKS syncing to perform queue actions but not automatically start queue processing operations
298 -(void)disabledtestItemAddCreatesCKKSOutgoingQueueEntry {
299 CFMutableDictionaryRef attrs;
300 CFDataRef data;
301
302 NSError* error;
303
304 NSArray* oqes = [CKKSOutgoingQueueEntry all: &error];
305 XCTAssertEqual([oqes count], 0ul, @"Nothing in outgoing queue");
306 XCTAssertNil(error, @"No error loading queue");
307
308 attrs = CFDictionaryCreateMutable( NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
309 CFDictionarySetValue( attrs, kSecClass, kSecClassGenericPassword );
310 CFDictionarySetValue( attrs, kSecAttrAccessible, kSecAttrAccessibleAlways );
311 CFDictionarySetValue( attrs, kSecAttrLabel, CFSTR( "TestLabel" ) );
312 CFDictionarySetValue( attrs, kSecAttrDescription, CFSTR( "TestDescription" ) );
313 CFDictionarySetValue( attrs, kSecAttrAccount, CFSTR( "TestAccount" ) );
314 CFDictionarySetValue( attrs, kSecAttrService, CFSTR( "TestService" ) );
315 CFDictionarySetValue( attrs, kSecAttrAccessGroup, CFSTR("com.apple.lakitu"));
316 data = CFDataCreate( NULL, (const uint8_t *) "important data", strlen("important data"));
317 CFDictionarySetValue( attrs, kSecValueData, data );
318 CFRelease( data );
319
320 XCTAssertEqual(SecItemAdd(attrs, NULL), errSecSuccess, @"Adding item works flawlessly");
321
322 oqes = [CKKSOutgoingQueueEntry all: &error];
323 XCTAssertEqual([oqes count], 1ul, @"Single entry in outgoing queue after adding item");
324 XCTAssertNil(error, @"No error loading queue");
325
326 CFDictionarySetValue( attrs, kSecAttrLabel, CFSTR( "TestLabel2" ) );
327 CFDictionarySetValue( attrs, kSecAttrAccount, CFSTR( "TestAccount2" ) );
328 CFDictionarySetValue( attrs, kSecAttrService, CFSTR( "TestService2" ) );
329 XCTAssertEqual(SecItemAdd(attrs, NULL), errSecSuccess);
330 CFRelease( attrs );
331
332 oqes = [CKKSOutgoingQueueEntry all: &error];
333 XCTAssertEqual([oqes count], 2ul, @"Two entries in outgoing queue after adding item");
334 XCTAssertNil(error, @"No error loading queue");
335 }
336
337 - (void)testCKKSKey {
338 CKKSKey* key = nil;
339 NSString* testUUID = @"157A3171-0677-451B-9EAE-0DDC4D4315B0";
340 NSString* testParentUUID = @"f5e7f20f-0885-48f9-b75d-9f0cfd2171b6";
341
342 NSData* testCKRecord = [@"nonsense" dataUsingEncoding:NSUTF8StringEncoding];
343
344 CKKSWrappedAESSIVKey* wrappedkey = [[CKKSWrappedAESSIVKey alloc] initWithBase64:@"KFfL58XtugiYNoD859EjG0StfrYd6eakm0CQrgX7iO+DEo4kio3WbEeA1kctCU0GaeTGsRFpbdy4oo6jXhVu7cZqB0svhUPGq55aGnszUjI="];
345
346 NSError* error = nil;
347
348 key = [CKKSKey fromDatabase:testUUID zoneID:self.testZoneID error:&error];
349 XCTAssertNil(key, "key does not exist yet");
350 XCTAssertNotNil(error, "error exists when things go wrong");
351 error = nil;
352
353 key = [[CKKSKey alloc] initWithWrappedAESKey: wrappedkey
354 uuid: testUUID
355 parentKeyUUID:testParentUUID
356 keyclass:SecCKKSKeyClassA
357 state: SecCKKSProcessedStateLocal
358 zoneID:self.testZoneID
359 encodedCKRecord:testCKRecord
360 currentkey:true];
361 XCTAssertNotNil(key, "could create key");
362
363 [key saveToDatabase: &error];
364 XCTAssertNil(error, "could save key to database");
365 error = nil;
366
367 CKKSKey* key2 = [CKKSKey fromDatabase:testUUID zoneID:self.testZoneID error:&error];
368 XCTAssertNil(error, "no error exists when loading key");
369 XCTAssertNotNil(key2, "key was fetched properly");
370
371 XCTAssertEqualObjects(key.uuid, key2.uuid, "key uuids match");
372 XCTAssertEqualObjects(key.parentKeyUUID, key2.parentKeyUUID, "parent key uuids match");
373 XCTAssertEqualObjects(key.state, key2.state, "key states match");
374 XCTAssertEqualObjects(key.encodedCKRecord, key2.encodedCKRecord, "encodedCKRecord match");
375 XCTAssertEqualObjects(key.wrappedkey, key2.wrappedkey, "wrapped keys match");
376 XCTAssertEqual(key.currentkey, key2.currentkey, "currentkey match");
377 }
378
379 - (void)testWhere {
380 NSError* error = nil;
381
382 NSData* testCKRecord = [@"nonsense" dataUsingEncoding:NSUTF8StringEncoding];
383
384 CKKSKey* tlk = [[CKKSKey alloc] initSelfWrappedWithAESKey: [[CKKSAESSIVKey alloc] initWithBase64: @"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="]
385 uuid:@"8b2aeb7f-4af3-43e9-b6e6-70d5c728ebf7"
386 keyclass:SecCKKSKeyClassTLK
387 state: SecCKKSProcessedStateLocal
388 zoneID:self.testZoneID
389 encodedCKRecord: testCKRecord
390 currentkey: true];
391 XCTAssertTrue([tlk saveToDatabase: &error], "TLK saved to database");
392 XCTAssertNil(error, "no error saving TLK to database");
393
394 CKKSKey* wrappedKey = [[CKKSKey alloc] initWrappedBy: tlk
395 AESKey:[CKKSAESSIVKey randomKey]
396 uuid:@"157A3171-0677-451B-9EAE-0DDC4D4315B0"
397 keyclass:SecCKKSKeyClassC
398 state: SecCKKSProcessedStateLocal
399 zoneID:self.testZoneID
400 encodedCKRecord:testCKRecord
401 currentkey:true];
402 XCTAssertTrue([wrappedKey saveToDatabase: &error], "key saved to database");
403 XCTAssertNil(error, "no error saving key to database");
404
405 NSArray<CKKSKey*>* tlks = [CKKSKey allWhere: @{@"UUID": @"8b2aeb7f-4af3-43e9-b6e6-70d5c728ebf7"} error: &error];
406 XCTAssertNotNil(tlks, "Returned some array from allWhere");
407 XCTAssertNil(error, "no error back from allWhere");
408 XCTAssertEqual([tlks count], 1ul, "Received one row (and expected one row)");
409
410 NSArray<CKKSKey*>* selfWrapped = [CKKSKey allWhere: @{@"parentKeyUUID": [CKKSSQLWhereObject op:@"=" string:@"uuid"]} error: &error];
411 XCTAssertNotNil(selfWrapped, "Returned some array from allWhere");
412 XCTAssertNil(error, "no error back from allWhere");
413 XCTAssertEqual([selfWrapped count], 1ul, "Received one row (and expected one row)");
414
415 // Try using CKKSSQLWhereObject alongside normal binds
416 NSArray<CKKSKey*>* selfWrapped2 = [CKKSKey allWhere: @{@"parentKeyUUID": [CKKSSQLWhereObject op:@"=" string:@"uuid"],
417 @"uuid": @"8b2aeb7f-4af3-43e9-b6e6-70d5c728ebf7"}
418 error: &error];
419 XCTAssertNotNil(selfWrapped2, "Returned some array from allWhere");
420 XCTAssertNil(error, "no error back from allWhere");
421 XCTAssertEqual([selfWrapped2 count], 1ul, "Received one row (and expected one row)");
422 }
423
424 - (void)testGroupBy {
425 [self addTestZoneEntries];
426 NSError* error = nil;
427
428 __block NSMutableDictionary<NSString*, NSString*>* results = [[NSMutableDictionary alloc] init];
429 NSDictionary* expectedResults = nil;
430
431 [CKKSSQLDatabaseObject queryDatabaseTable: [CKKSOutgoingQueueEntry sqlTable]
432 where: nil
433 columns: @[@"action", @"count(rowid)"]
434 groupBy: @[@"action"]
435 orderBy:nil
436 limit: -1
437 processRow: ^(NSDictionary* row) {
438 results[row[@"action"]] = row[@"count(rowid)"];
439 }
440 error: &error];
441
442 XCTAssertNil(error, "no error doing group by query");
443 expectedResults = @{
444 SecCKKSActionAdd: @"2",
445 SecCKKSActionModify: @"1"
446 };
447 XCTAssertEqualObjects(results, expectedResults, "Recieved correct group by result");
448
449 // Now test with a where clause:
450 results = [[NSMutableDictionary alloc] init];
451 [CKKSSQLDatabaseObject queryDatabaseTable: [CKKSOutgoingQueueEntry sqlTable]
452 where: @{@"state": SecCKKSStateError}
453 columns: @[@"action", @"count(rowid)"]
454 groupBy: @[@"action"]
455 orderBy:nil
456 limit: -1
457 processRow: ^(NSDictionary* row) {
458 results[row[@"action"]] = row[@"count(rowid)"];
459 }
460 error: &error];
461
462 XCTAssertNil(error, "no error doing where+group by query");
463 expectedResults = @{
464 SecCKKSActionAdd: @"1",
465 SecCKKSActionModify: @"1"
466 };
467 XCTAssertEqualObjects(results, expectedResults, "Recieved correct where+group by result");
468 }
469
470 - (void)testOrderBy {
471 [self addTestZoneEntries];
472 NSError* error = nil;
473
474 __block NSMutableArray* rows = [[NSMutableArray alloc] init];
475
476 [CKKSSQLDatabaseObject queryDatabaseTable: [CKKSOutgoingQueueEntry sqlTable]
477 where: nil
478 columns: @[@"action", @"uuid"]
479 groupBy:nil
480 orderBy:@[@"uuid"]
481 limit:-1
482 processRow: ^(NSDictionary* row) {
483 [rows addObject:row];
484 }
485 error: &error];
486
487 XCTAssertNil(error, "no error doing order by query");
488 XCTAssertEqual(rows.count, 3u, "got three items");
489
490 XCTAssertEqual([rows[0][@"uuid"] compare: rows[1][@"uuid"]], NSOrderedAscending, "first order is fine");
491 XCTAssertEqual([rows[1][@"uuid"] compare: rows[2][@"uuid"]], NSOrderedAscending, "second order is fine");
492
493 // Check that order-by + limit works to page
494 __block NSString* lastUUID = nil;
495 __block NSString* uuid = nil;
496 uint64_t count = 0;
497
498 while(count == 0 || uuid != nil) {
499 uuid = nil;
500 [CKKSSQLDatabaseObject queryDatabaseTable: [CKKSOutgoingQueueEntry sqlTable]
501 where: lastUUID ? @{@"UUID": [CKKSSQLWhereObject op:@">" stringValue:lastUUID]} : nil
502 columns: @[@"action", @"UUID"]
503 groupBy:nil
504 orderBy:@[@"uuid"]
505 limit:1
506 processRow: ^(NSDictionary* row) {
507 XCTAssertNil(uuid, "Only one row returned");
508 uuid = row[@"UUID"];
509 }
510 error: &error];
511 XCTAssertNil(error, "No error doing SQL");
512 if(uuid && lastUUID) {
513 XCTAssertEqual([lastUUID compare:uuid], NSOrderedAscending, "uuids returning in right order");
514 }
515 lastUUID = uuid;
516 count += 1;
517 }
518 XCTAssertEqual(count, 4u, "Received 3 objects (and 1 nil)");
519 }
520
521 - (void)testLimit {
522 [self addTestZoneEntries];
523 NSError* error = nil;
524
525 __block NSMutableDictionary<NSString*, NSString*>* results = [[NSMutableDictionary alloc] init];
526
527 [CKKSSQLDatabaseObject queryDatabaseTable: [CKKSOutgoingQueueEntry sqlTable]
528 where: nil
529 columns: @[@"uuid", @"action"]
530 groupBy: nil
531 orderBy:nil
532 limit: -1
533 processRow: ^(NSDictionary* row) {
534 results[row[@"uuid"]] = row[@"action"];
535 }
536 error: &error];
537
538 XCTAssertNil(error, "no error doing vanilla query");
539 XCTAssertEqual(results.count, 3u, "Received three elements in normal query");
540 results = [[NSMutableDictionary alloc] init];
541
542 [CKKSSQLDatabaseObject queryDatabaseTable: [CKKSOutgoingQueueEntry sqlTable]
543 where: nil
544 columns: @[@"uuid", @"action"]
545 groupBy: nil
546 orderBy:nil
547 limit: 1
548 processRow: ^(NSDictionary* row) {
549 results[row[@"uuid"]] = row[@"action"];
550 }
551 error: &error];
552
553 XCTAssertNil(error, "no error doing limit query");
554 XCTAssertEqual(results.count, 1u, "Received one element in limited query");
555 results = [[NSMutableDictionary alloc] init];
556
557 // Now test with a where clause:
558 results = [[NSMutableDictionary alloc] init];
559 [CKKSSQLDatabaseObject queryDatabaseTable: [CKKSOutgoingQueueEntry sqlTable]
560 where: @{@"state": SecCKKSStateError}
561 columns: @[@"uuid", @"action"]
562 groupBy: nil
563 orderBy:nil
564 limit: 3
565 processRow: ^(NSDictionary* row) {
566 results[row[@"uuid"]] = row[@"action"];
567 }
568 error: &error];
569
570 XCTAssertNil(error, "no error doing limit+where query");
571 XCTAssertEqual(results.count, 2u, "Received two elements in where+limited query");
572 results = [[NSMutableDictionary alloc] init];
573
574 results = [[NSMutableDictionary alloc] init];
575 [CKKSSQLDatabaseObject queryDatabaseTable: [CKKSOutgoingQueueEntry sqlTable]
576 where: @{@"state": SecCKKSStateError}
577 columns: @[@"uuid", @"action"]
578 groupBy: nil
579 orderBy:nil
580 limit: 1
581 processRow: ^(NSDictionary* row) {
582 results[row[@"uuid"]] = row[@"action"];
583 }
584 error: &error];
585
586 XCTAssertNil(error, "no error doing limit+where query");
587 XCTAssertEqual(results.count, 1u, "Received one element in where+limited query");
588 results = [[NSMutableDictionary alloc] init];
589 }
590
591 @end
592
593 #endif