diff --git a/CouchbaseLite.xcodeproj/project.pbxproj b/CouchbaseLite.xcodeproj/project.pbxproj index fdb21db10..d85c63d38 100644 --- a/CouchbaseLite.xcodeproj/project.pbxproj +++ b/CouchbaseLite.xcodeproj/project.pbxproj @@ -1784,10 +1784,10 @@ AEC806BC2C89EA68001C9723 /* CBLArrayIndexConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = AEC806B62C89EA68001C9723 /* CBLArrayIndexConfiguration.m */; }; AEC806BD2C89EA68001C9723 /* CBLArrayIndexConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = AEC806B62C89EA68001C9723 /* CBLArrayIndexConfiguration.m */; }; AEC806BE2C89EA68001C9723 /* CBLArrayIndexConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = AEC806B52C89EA68001C9723 /* CBLArrayIndexConfiguration.h */; settings = {ATTRIBUTES = (Private, ); }; }; - AECA89782CAC765A00C7B6BE /* CBLIndexable+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = AECA89652CAC714800C7B6BE /* CBLIndexable+Internal.h */; }; - AECA89792CAC765B00C7B6BE /* CBLIndexable+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = AECA89652CAC714800C7B6BE /* CBLIndexable+Internal.h */; }; - AECA897A2CAC765C00C7B6BE /* CBLIndexable+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = AECA89652CAC714800C7B6BE /* CBLIndexable+Internal.h */; }; - AECA897B2CAC765D00C7B6BE /* CBLIndexable+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = AECA89652CAC714800C7B6BE /* CBLIndexable+Internal.h */; }; + AECA897D2CADADAA00C7B6BE /* UnnestArrayTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = AECA897C2CADADAA00C7B6BE /* UnnestArrayTest.swift */; }; + AECA897E2CADADAA00C7B6BE /* UnnestArrayTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = AECA897C2CADADAA00C7B6BE /* UnnestArrayTest.swift */; }; + AECA897F2CADADAA00C7B6BE /* UnnestArrayTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = AECA897C2CADADAA00C7B6BE /* UnnestArrayTest.swift */; }; + AECA89802CADADAA00C7B6BE /* UnnestArrayTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = AECA897C2CADADAA00C7B6BE /* UnnestArrayTest.swift */; }; AECD5A162C0E21D900B1247E /* CBLIndexUpdater+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = AECD5A0F2C0E21D900B1247E /* CBLIndexUpdater+Internal.h */; }; AECD5A172C0E21D900B1247E /* CBLIndexUpdater+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = AECD5A0F2C0E21D900B1247E /* CBLIndexUpdater+Internal.h */; }; /* End PBXBuildFile section */ @@ -2811,7 +2811,7 @@ AE83D0832C0637ED0055D2CF /* CBLIndexUpdater.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = CBLIndexUpdater.mm; sourceTree = ""; }; AEC806B52C89EA68001C9723 /* CBLArrayIndexConfiguration.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CBLArrayIndexConfiguration.h; sourceTree = ""; }; AEC806B62C89EA68001C9723 /* CBLArrayIndexConfiguration.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CBLArrayIndexConfiguration.m; sourceTree = ""; }; - AECA89652CAC714800C7B6BE /* CBLIndexable+Internal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CBLIndexable+Internal.h"; sourceTree = ""; }; + AECA897C2CADADAA00C7B6BE /* UnnestArrayTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnnestArrayTest.swift; sourceTree = ""; }; AECD59F92C0E137200B1247E /* CBLQueryIndex+Internal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CBLQueryIndex+Internal.h"; sourceTree = ""; }; AECD5A0F2C0E21D900B1247E /* CBLIndexUpdater+Internal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CBLIndexUpdater+Internal.h"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -3097,6 +3097,7 @@ 1A13DD3328B8809300BC1084 /* URLEndpointListenerTest+Collection.swift */, AE5803A32B99C67D001A1BE3 /* VectorSearchTest.swift */, 406F8DFC2C27F097000223FC /* VectorSearchTest+Lazy.swift */, + AECA897C2CADADAA00C7B6BE /* UnnestArrayTest.swift */, 1AA91DC522B0356000BF0BDE /* CustomLogger.swift */, 93249D81246B99FD000A8A6E /* iOS */, 939B79241E679017009A70EF /* Info.plist */, @@ -3606,7 +3607,6 @@ 27CDE75E207407280082D458 /* CBLDocumentChangeNotifier.h */, 27CDE75F207407280082D458 /* CBLDocumentChangeNotifier.mm */, 1A1612A8283DE8A200AA4987 /* CBLScope+Internal.h */, - AECA89652CAC714800C7B6BE /* CBLIndexable+Internal.h */, ); name = Database; sourceTree = ""; @@ -4382,7 +4382,6 @@ 93EB264521DF1AE40006FB88 /* CBLDocumentFlags.h in Headers */, 9383A5961F1EEFCD0083053D /* CBLQueryResult+Internal.h in Headers */, 934A27931F30E5CA003946A7 /* CBLBinaryExpression.h in Headers */, - AECA89792CAC765B00C7B6BE /* CBLIndexable+Internal.h in Headers */, 27D7219A1F8E97F400AA4458 /* CBLFleece.hh in Headers */, 9374A8A7201FC53600BA0D9E /* CBLReplicator+Backgrounding.h in Headers */, 932565A521ED13290092F4E0 /* CBLLogFileConfiguration.h in Headers */, @@ -4693,7 +4692,6 @@ 40FC1BD72B928A4F00394276 /* CBLCoreMLPredictiveModel.h in Headers */, 40FC1B502B92873000394276 /* CBLEncryptionKey.h in Headers */, 9343EFE4207D611600F19A89 /* CBLDatabase+Internal.h in Headers */, - AECA897A2CAC765C00C7B6BE /* CBLIndexable+Internal.h in Headers */, 40FC1BE02B928A4F00394276 /* CBLPredictiveIndex.h in Headers */, 9343EFE5207D611600F19A89 /* CBLDictionaryFragment.h in Headers */, 40FC1C1E2B928B5000394276 /* CBLVectorEncoding+Internal.h in Headers */, @@ -4898,7 +4896,6 @@ 9343F0FB207D61AB00F19A89 /* CBLQueryResultSet.h in Headers */, 9343F0FC207D61AB00F19A89 /* CBLDocumentFragment.h in Headers */, 9343F0FD207D61AB00F19A89 /* CBLQueryBuilder.h in Headers */, - AECA897B2CAC765D00C7B6BE /* CBLIndexable+Internal.h in Headers */, 40FC1BEE2B928A4F00394276 /* CBLQueryFunction+Vector.h in Headers */, 9343F0FF207D61AB00F19A89 /* CBLMutableArrayFragment.h in Headers */, 1AA2EE0328A682A800DEB47E /* CBLCollectionConfiguration+Swift.h in Headers */, @@ -4987,7 +4984,6 @@ 1AAFB66F284A260A00878453 /* CBLCollectionChangeObservable.h in Headers */, 9384D8401FC405D200FE89D8 /* CBLQueryFullTextFunction.h in Headers */, 1A3470E9266F69220042C6BA /* CBLIndexConfiguration+Internal.h in Headers */, - AECA89782CAC765A00C7B6BE /* CBLIndexable+Internal.h in Headers */, 933208141E77415E000D9993 /* CBLQueryExpression.h in Headers */, 935A58B621AFA34D009A29CB /* CBLDocumentReplication.h in Headers */, 1AEF0585283380D500D5DDEA /* CBLScope.h in Headers */, @@ -6228,6 +6224,7 @@ 1A8F85092893F318008C4333 /* QueryTest+Collection.swift in Sources */, 93E17F151ED4ED4000671CA1 /* NotificationTest.swift in Sources */, 27BE3B4D1E4E51C80012B74A /* DatabaseTest.swift in Sources */, + AECA897D2CADADAA00C7B6BE /* UnnestArrayTest.swift in Sources */, 1A6084F828758AE00037C66F /* CollectionTest.swift in Sources */, 934C2CDE1F4FC0D20010316F /* CBLTestHelper.m in Sources */, 9388CC5521C25CC7005CA66D /* LogTest.swift in Sources */, @@ -6271,6 +6268,7 @@ 93BB1CB4246BB2ED004FFA00 /* ReplicatorTest.swift in Sources */, 1A6084FA28758AE20037C66F /* CollectionTest.swift in Sources */, 93BB1CB1246BB2E4004FFA00 /* PredictiveQueryTest.swift in Sources */, + AECA89802CADADAA00C7B6BE /* UnnestArrayTest.swift in Sources */, 93BB1CBA246BB2F4004FFA00 /* CustomLogger.swift in Sources */, 1A13DD4428B882BA00BC1084 /* URLEndpointListenerTest+Collection.swift in Sources */, 93BB1CA1246BB2CA004FFA00 /* DateTimeQueryFunctionTest.swift in Sources */, @@ -6818,6 +6816,7 @@ 93EB25A421CDCC160006FB88 /* PredictiveQueryTest.swift in Sources */, 1A6084FB28758AE90037C66F /* CollectionTest.swift in Sources */, 9343F18E207D636300F19A89 /* DocumentTest.swift in Sources */, + AECA897F2CADADAA00C7B6BE /* UnnestArrayTest.swift in Sources */, 1A13DD4328B882BA00BC1084 /* URLEndpointListenerTest+Collection.swift in Sources */, 9343F18F207D636300F19A89 /* ArrayTest.swift in Sources */, 1A690CD7242150DF0084D017 /* ReplicatorTest+PendingDocIds.swift in Sources */, @@ -7071,6 +7070,7 @@ 1A8F85082893F317008C4333 /* QueryTest+Collection.swift in Sources */, 93BB1CAE246BB2DC004FFA00 /* QueryTest.swift in Sources */, 93BB1CAD246BB2DC004FFA00 /* NotificationTest.swift in Sources */, + AECA897E2CADADAA00C7B6BE /* UnnestArrayTest.swift in Sources */, 1A6084F728758ADF0037C66F /* CollectionTest.swift in Sources */, 93BB1CA6246BB2D3004FFA00 /* LogTest.swift in Sources */, 93BB1CB7246BB2F3004FFA00 /* CustomLogger.swift in Sources */, diff --git a/Objective-C/CBLCollection.mm b/Objective-C/CBLCollection.mm index d8a1af991..8313fd7e7 100644 --- a/Objective-C/CBLCollection.mm +++ b/Objective-C/CBLCollection.mm @@ -976,7 +976,7 @@ - (void) freeC4Observer { _docChangeNotifiers = nil; } -- (nullable NSArray*) _indexesInfo: (NSError**)error { +- (nullable NSArray*) indexesInfo: (NSError**)error { CBL_LOCK(_mutex) { if (![self checkIsValid: error]) return nil; diff --git a/Objective-C/CBLQueryIndex.mm b/Objective-C/CBLQueryIndex.mm index 41554015a..3d14bd265 100644 --- a/Objective-C/CBLQueryIndex.mm +++ b/Objective-C/CBLQueryIndex.mm @@ -82,16 +82,4 @@ - (id) mutex { return _mutex; } -- (NSString*) _unnestPathForIndex { - CBL_LOCK(_mutex) { - C4IndexOptions* opts = {}; - c4index_getOptions(_c4index, opts); - if (!opts->unnestPath) { - return [NSString stringWithUTF8String: opts->unnestPath]; - } else { - return nil; - } - } -} - @end diff --git a/Objective-C/Internal/CBLCollection+Internal.h b/Objective-C/Internal/CBLCollection+Internal.h index 2ff4ece1c..c2038829e 100644 --- a/Objective-C/Internal/CBLCollection+Internal.h +++ b/Objective-C/Internal/CBLCollection+Internal.h @@ -21,7 +21,6 @@ #import "CBLCollection.h" #import "CBLChangeListenerToken.h" #import "CBLDatabase.h" -#import "CBLIndexable+Internal.h" #import "c4.h" #define CBLCollectionErrorNotOpen [NSError errorWithDomain: CBLErrorDomain \ @@ -35,7 +34,7 @@ NS_ASSUME_NONNULL_BEGIN -@interface CBLCollection () +@interface CBLCollection () /** dispatch queue */ @property (readonly, nonatomic) dispatch_queue_t dispatchQueue; @@ -64,6 +63,8 @@ NS_ASSUME_NONNULL_BEGIN - (BOOL) checkIsValid: (NSError**)error; +- (nullable NSArray*) indexesInfo: (NSError**)error; + @end @interface CBLCollectionChange () diff --git a/Objective-C/Internal/CBLCollection+Swift.h b/Objective-C/Internal/CBLCollection+Swift.h index a92e4c1a4..4e9edc917 100644 --- a/Objective-C/Internal/CBLCollection+Swift.h +++ b/Objective-C/Internal/CBLCollection+Swift.h @@ -28,6 +28,8 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, readonly) BOOL isValid; +- (nullable NSArray*) indexesInfo: (NSError**)error; + @end NS_ASSUME_NONNULL_END diff --git a/Objective-C/Internal/CBLIndexable+Internal.h b/Objective-C/Internal/CBLIndexable+Internal.h deleted file mode 100644 index 9db66452d..000000000 --- a/Objective-C/Internal/CBLIndexable+Internal.h +++ /dev/null @@ -1,31 +0,0 @@ -// -// CBLIndexable+Internal.h -// CouchbaseLite -// -// Copyright (c) 2024 Couchbase, Inc All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import "CouchbaseLite/CBLIndexable.h" - -NS_ASSUME_NONNULL_BEGIN - -@protocol CBLIndexableInternal - -/** Return all index information about all indexes. */ -- (nullable NSArray*) _indexesInfo: (NSError**)error; - -@end - -NS_ASSUME_NONNULL_END diff --git a/Objective-C/Internal/CBLQueryIndex+Internal.h b/Objective-C/Internal/CBLQueryIndex+Internal.h index aed7724e9..bd062f706 100644 --- a/Objective-C/Internal/CBLQueryIndex+Internal.h +++ b/Objective-C/Internal/CBLQueryIndex+Internal.h @@ -35,7 +35,6 @@ NS_ASSUME_NONNULL_BEGIN name: (NSString*) name collection: (CBLCollection*) collection; -- (NSString*) _unnestPathForIndex; @end NS_ASSUME_NONNULL_END diff --git a/Objective-C/Tests/UnnestArrayIndexTest.m b/Objective-C/Tests/UnnestArrayIndexTest.m index 232e12498..4446f83ba 100644 --- a/Objective-C/Tests/UnnestArrayIndexTest.m +++ b/Objective-C/Tests/UnnestArrayIndexTest.m @@ -1,15 +1,24 @@ // // UnnestArrayIndexTest.m -// CBL_ObjC +// CouchbaseLite // -// Created by Vlad Velicu on 01/10/2024. -// Copyright © 2024 Couchbase. All rights reserved. +// Copyright (c) 2024 Couchbase, Inc. All rights reserved. +// +// Licensed under the Couchbase License Agreement (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// https://info.couchbase.com/rs/302-GJY-034/images/2017-10-30_License_Agreement.pdf +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. // #import "CBLTestCase.h" #import "CBLArrayIndexConfiguration.h" #import "CBLCollection+Internal.h" -#import "CBLQueryIndex+Internal.h" @interface UnnestArrayIndexTest : CBLTestCase @@ -18,6 +27,7 @@ @interface UnnestArrayIndexTest : CBLTestCase @implementation UnnestArrayIndexTest /** + Test Spec v1.0.1: https://github.com/couchbaselabs/couchbase-lite-api/blob/master/spec/tests/T0004-Unnest-Array-Index.md */ @@ -27,8 +37,8 @@ @implementation UnnestArrayIndexTest Test that creating an ArrayIndexConfiguration with invalid expressions which are an empty expressions or contain null. Steps 1. Create a ArrayIndexConfiguration object. - path: "contacts" - expressions: [] + - path: "contacts" + - expressions: [] 2. Check that an invalid arument exception is thrown. */ @@ -45,8 +55,8 @@ - (void) testArrayIndexConfigInvalidExpressions { Steps 1. Load profiles.json into the collection named "_default.profiles". 2. Create a ArrayIndexConfiguration object. - path: "contacts" - expressions: null + - path: "contacts" + - expressions: null 3. Create an array index named "contacts" in the profiles collection. 4. Get index names from the profiles collection and check that the index named "contacts" exists. 5. Get info of the index named "contacts" using an internal API and check that the index has path and expressions as configured. @@ -59,11 +69,9 @@ - (void) testCreateArrayIndexWithPath { CBLArrayIndexConfiguration* config = [[CBLArrayIndexConfiguration alloc] initWithPath: @"contacts" expressions: nil]; [profiles createIndexWithName: @"contacts" config: config error: &err]; - NSArray* indexes = [profiles _indexesInfo: nil]; + NSArray* indexes = [profiles indexesInfo: nil]; AssertEqual(indexes.count, 1u); AssertEqualObjects(indexes[0][@"expr"], @""); - - //AssertEqual([[profiles indexWithName: @"contacts" error: &err] _unnestPathForIndex], @"contacts"); } /** @@ -73,8 +81,8 @@ - (void) testCreateArrayIndexWithPath { Steps 1. Load profiles.json into the collection named "_default.profiles". 2. Create a ArrayIndexConfiguration object. - path: "contacts" - expressions: ["address.city", "address.state"] + - path: "contacts" + - expressions: ["address.city", "address.state"] 3. Create an array index named "contacts" in the profiles collection. 4. Get index names from the profiles collection and check that the index named "contacts" exists. 5. Get info of the index named "contacts" using an internal API and check that the index has path and expressions as configured. @@ -87,11 +95,9 @@ - (void) testCreateArrayIndexWithPathAndExpressions { CBLArrayIndexConfiguration* config = [[CBLArrayIndexConfiguration alloc] initWithPath: @"contacts" expressions: @[@"address.city", @"address.state"]]; [profiles createIndexWithName: @"contacts" config: config error: &err]; - NSArray* indexes = [profiles _indexesInfo: nil]; + NSArray* indexes = [profiles indexesInfo: nil]; AssertEqual(indexes.count, 1u); AssertEqualObjects(indexes[0][@"expr"], @"address.city,address.state"); - - // AssertEqual([[profiles indexWithName: @"contacts" error: &err] _unnestPathForIndex], @"contacts"); } @end diff --git a/Swift/Collection.swift b/Swift/Collection.swift index 18791674c..50d61b2f0 100644 --- a/Swift/Collection.swift +++ b/Swift/Collection.swift @@ -352,6 +352,10 @@ public final class Collection : CollectionChangeObservable, Indexable, Equatable // MARK: Internal + func indexesInfo() throws -> [[String: Any]]? { + return try impl.indexesInfo() as? [[String: Any]] + } + init(_ impl: CBLCollection, db: Database) { self.impl = impl self.database = db diff --git a/Swift/IndexConfiguration.swift b/Swift/IndexConfiguration.swift index d77f5b847..4577b6b44 100644 --- a/Swift/IndexConfiguration.swift +++ b/Swift/IndexConfiguration.swift @@ -88,7 +88,12 @@ public struct ArrayIndexConfiguration: IndexConfiguration, IndexConfigConvertabl /// represents an expression defining the values within the array to be indexed. /// If the array specified by the path contains scalar values, this parameter can be null. /// - Returns The ArrayIndexConfiguration object. - public init(path: String, _ expressions: [String]?) { + public init(path: String, _ expressions: [String]? = nil) { + if let expressions = expressions, expressions.isEmpty { + NSException(name: .invalidArgumentException, + reason: "Empty expressions is not allowed, use nil instead", + userInfo: nil).raise() + } self.path = path self.expressions = expressions } diff --git a/Swift/Tests/ArrayTest.swift b/Swift/Tests/ArrayTest.swift index d408e01ba..f29427c61 100644 --- a/Swift/Tests/ArrayTest.swift +++ b/Swift/Tests/ArrayTest.swift @@ -235,7 +235,7 @@ class ArrayTest: CBLTestCase { func testUnsavedMutableArrayToJSON() throws { let json = "[{\"unsaved\":\"mutableDoc\"}]" var mArray = try MutableArrayObject(json: json) - expectExcepion(exception: .internalInconsistencyException) { + expectException(exception: .internalInconsistencyException) { let _ = mArray.toJSON() } @@ -244,7 +244,7 @@ class ArrayTest: CBLTestCase { try saveDocument(mDoc) mArray = mDoc.array(forKey: "array")! - expectExcepion(exception: .internalInconsistencyException) { + expectException(exception: .internalInconsistencyException) { let _ = mArray.toJSON() } } @@ -278,7 +278,7 @@ class ArrayTest: CBLTestCase { var blob = mDoc.blob(forKey: "origin") // before save it should throw the exception - expectExcepion(exception: .internalInconsistencyException) { + expectException(exception: .internalInconsistencyException) { print("\(blob!.content?.count ?? 0)") } try self.db.saveDocument(mDoc) diff --git a/Swift/Tests/CBLTestCase.swift b/Swift/Tests/CBLTestCase.swift index f1bfb5696..5aa06eace 100644 --- a/Swift/Tests/CBLTestCase.swift +++ b/Swift/Tests/CBLTestCase.swift @@ -274,7 +274,7 @@ class CBLTestCase: XCTestCase { } } - func expectExcepion(exception: NSExceptionName, block: @escaping () -> Void) { + func expectException(exception: NSExceptionName, block: @escaping () -> Void) { var exceptionThrown = false do { try CBLTestHelper.catchException { diff --git a/Swift/Tests/DictionaryTest.swift b/Swift/Tests/DictionaryTest.swift index 9a2c2ab23..f16d9d82d 100644 --- a/Swift/Tests/DictionaryTest.swift +++ b/Swift/Tests/DictionaryTest.swift @@ -268,7 +268,7 @@ class DictionaryTest: CBLTestCase { mDoc = doc!.toMutable() mDict = dict!.toMutable() mDict.setValue("newValueAppended", forKey: "newKeyAppended") - expectExcepion(exception: .internalInconsistencyException) { + expectException(exception: .internalInconsistencyException) { let _ = mDict.toJSON() } mDoc.setValue(mDict, forKey: "dict") @@ -282,7 +282,7 @@ class DictionaryTest: CBLTestCase { func testUnsavedMutableDictionaryToJSON() throws { let mDict = try MutableDictionaryObject(json: "{\"unsaved\":\"dict\"}") - expectExcepion(exception: .internalInconsistencyException) { + expectException(exception: .internalInconsistencyException) { let _ = mDict.toJSON() } } diff --git a/Swift/Tests/DocumentTest.swift b/Swift/Tests/DocumentTest.swift index cc361e99c..d0a757a88 100644 --- a/Swift/Tests/DocumentTest.swift +++ b/Swift/Tests/DocumentTest.swift @@ -1653,7 +1653,7 @@ class DocumentTest: CBLTestCase { func testUnsavedMutableDocumentToJSON() throws { let mDoc = try MutableDocument(id: "doc", json: "{\"unsaved\":\"doc\"}") - expectExcepion(exception: .internalInconsistencyException) { + expectException(exception: .internalInconsistencyException) { let _ = mDoc.toJSON() } } @@ -1732,7 +1732,7 @@ class DocumentTest: CBLTestCase { func testUnsavedBlob() throws { let content = kTestBlob.data(using: .utf8)! let blob = Blob(contentType: "text/plain", data: content) - expectExcepion(exception: .internalInconsistencyException) { + expectException(exception: .internalInconsistencyException) { let _ = blob.toJSON() } } @@ -1751,7 +1751,7 @@ class DocumentTest: CBLTestCase { try self.db.saveBlob(blob: blob) var b: Blob? - expectExcepion(exception: .invalidArgumentException) { + expectException(exception: .invalidArgumentException) { b = try! self.db.getBlob(properties: [Blob.typeProperty:"bl0b", Blob.blobDigestProperty: blob.digest! as String, Blob.blobContentType: "text/plain", @@ -1759,7 +1759,7 @@ class DocumentTest: CBLTestCase { } XCTAssertNil(b) - expectExcepion(exception: .invalidArgumentException) { + expectException(exception: .invalidArgumentException) { b = try! self.db.getBlob(properties: ["type":Blob.blobType, Blob.blobDigestProperty: blob.digest! as String, Blob.blobContentType: "text/plain", @@ -1767,7 +1767,7 @@ class DocumentTest: CBLTestCase { } XCTAssertNil(b) - expectExcepion(exception: .invalidArgumentException) { + expectException(exception: .invalidArgumentException) { b = try! self.db.getBlob(properties: [Blob.typeProperty:Blob.blobType, Blob.blobDigestProperty: blob.digest! as String, Blob.blobContentType: 1234, @@ -1775,7 +1775,7 @@ class DocumentTest: CBLTestCase { } XCTAssertNil(b) - expectExcepion(exception: .invalidArgumentException) { + expectException(exception: .invalidArgumentException) { b = try! self.db.getBlob(properties: [Blob.typeProperty:Blob.blobType, Blob.blobDigestProperty: blob.digest! as String, Blob.blobContentType: "text/plain", @@ -1783,7 +1783,7 @@ class DocumentTest: CBLTestCase { } XCTAssertNil(b) - expectExcepion(exception: .invalidArgumentException) { + expectException(exception: .invalidArgumentException) { b = try! self.db.getBlob(properties: [Blob.typeProperty:Blob.blobType, Blob.blobDigestProperty: 12, Blob.blobContentType: "text/plain", diff --git a/Swift/Tests/ReplicatorTest+Collection.swift b/Swift/Tests/ReplicatorTest+Collection.swift index 93ee82470..ad8bc9d8a 100644 --- a/Swift/Tests/ReplicatorTest+Collection.swift +++ b/Swift/Tests/ReplicatorTest+Collection.swift @@ -360,7 +360,7 @@ class ReplicatorTest_Collection: ReplicatorTest { let target = URLEndpoint(url: url) var config = ReplicatorConfiguration(target: target) - expectExcepion(exception: .invalidArgumentException) { + expectException(exception: .invalidArgumentException) { config.addCollections([col1a, col1b]) } @@ -371,7 +371,7 @@ class ReplicatorTest_Collection: ReplicatorTest { XCTAssertEqual(config.collections.count, 1) XCTAssert(config.collections.contains(where: { $0.name == "colA" && $0.scope.name == "scopeA" })) - expectExcepion(exception: .invalidArgumentException) { + expectException(exception: .invalidArgumentException) { config.addCollection(col1b) } } @@ -389,7 +389,7 @@ class ReplicatorTest_Collection: ReplicatorTest { let target = URLEndpoint(url: url) var config = ReplicatorConfiguration(target: target) - expectExcepion(exception: .invalidArgumentException) { + expectException(exception: .invalidArgumentException) { config.addCollections([col1a, col1b]) } @@ -400,7 +400,7 @@ class ReplicatorTest_Collection: ReplicatorTest { XCTAssertEqual(config.collections.count, 1) XCTAssert(config.collections.contains(where: { $0.name == "colA" && $0.scope.name == "scopeA" })) - expectExcepion(exception: .invalidArgumentException) { + expectException(exception: .invalidArgumentException) { config.addCollection(col1b) } } diff --git a/Swift/Tests/UnnestArrayTest.swift b/Swift/Tests/UnnestArrayTest.swift new file mode 100644 index 000000000..fb39d6499 --- /dev/null +++ b/Swift/Tests/UnnestArrayTest.swift @@ -0,0 +1,81 @@ +// +// UnnestArrayTest.swift +// CouchbaseLite +// +// Copyright (c) 2024 Couchbase, Inc. All rights reserved. +// +// Licensed under the Couchbase License Agreement (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// https://info.couchbase.com/rs/302-GJY-034/images/2017-10-30_License_Agreement.pdf +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import XCTest +@testable import CouchbaseLiteSwift + +/// Test Spec v1.0.1: +/// https://github.com/couchbaselabs/couchbase-lite-api/blob/master/spec/tests/T0004-Unnest-Array-Index.md + +class UnnestArrayTest: CBLTestCase { + /// 1. TestArrayIndexConfigInvalidExpressions + /// Description + /// Test that creating an ArrayIndexConfiguration with invalid expressions which are an empty expressions or contain null. + /// Steps + /// 1. Create a ArrayIndexConfiguration object. + /// - path: "contacts" + /// - expressions: [] + /// 2. Check that an invalid arument exception is thrown. + func testArrayIndexConfigInvalidExpressions() throws { + expectException(exception: .invalidArgumentException) { + _ = ArrayIndexConfiguration(path: "contacts", []) + } + } + + /// 2. TestCreateArrayIndexWithPath + /// Description + /// Test that creating an ArrayIndexConfiguration with invalid expressions which are an empty expressions or contain null. + /// Steps + /// 1. Load profiles.json into the collection named "_default.profiles". + /// 2. Create a ArrayIndexConfiguration object. + /// - path: "contacts" + /// - expressions: null + /// 3. Create an array index named "contacts" in the profiles collection. + /// 4. Get index names from the profiles collection and check that the index named "contacts" exists. + /// 5. Get info of the index named "contacts" using an internal API and check that the index has path and expressions as configured. + func testCreateArrayIndexWithPath() throws { + let profiles = try db.createCollection(name: "profiles") + try loadJSONResource("profiles_100", collection: profiles) + let config = ArrayIndexConfiguration(path: "contacts") + try profiles.createIndex(withName: "contacts", config: config) + let indexes = try profiles.indexesInfo() + XCTAssertEqual(indexes!.count, 1) + XCTAssertEqual(indexes![0]["expr"] as! String, "") + } + + /// 3. TestCreateArrayIndexWithPathAndExpressions + /// Description + /// Test that creating an array index with path and expressions works as expected. + /// Steps + /// 1. Load profiles.json into the collection named "_default.profiles". + /// 2. Create a ArrayIndexConfiguration object. + /// - path: "contacts" + /// - expressions: ["address.city", "address.state"] + /// 3. Create an array index named "contacts" in the profiles collection. + /// 4. Get index names from the profiles collection and check that the index named "contacts" exists. + /// 5. Get info of the index named "contacts" using an internal API and check that the index has path and expressions as configured. + func testCreateArrayIndexWithPathAndExpressions() throws { + let profiles = try db.createCollection(name: "profiles") + try loadJSONResource("profiles_100", collection: profiles) + let config = ArrayIndexConfiguration(path: "contacts", ["address.city", "address.state"]) + try profiles.createIndex(withName: "contacts", config: config) + let indexes = try profiles.indexesInfo() + XCTAssertEqual(indexes!.count, 1) + XCTAssertEqual(indexes![0]["expr"] as! String, "address.city,address.state") + } +} diff --git a/Swift/Tests/VectorSearchTest+Lazy.swift b/Swift/Tests/VectorSearchTest+Lazy.swift index 260e9230d..5fe62799c 100644 --- a/Swift/Tests/VectorSearchTest+Lazy.swift +++ b/Swift/Tests/VectorSearchTest+Lazy.swift @@ -195,7 +195,7 @@ class VectorSearchTest_Lazy : VectorSearchTest { let index = try wordsIndex() - expectExcepion(exception: .invalidArgumentException) { + expectException(exception: .invalidArgumentException) { _ = try? index.beginUpdate(limit: 0) } } @@ -774,55 +774,55 @@ class VectorSearchTest_Lazy : VectorSearchTest { let updater = try index.beginUpdate(limit: 10)! XCTAssertEqual(updater.count, 1) - expectExcepion(exception: .rangeException) { + expectException(exception: .rangeException) { _ = updater.string(at: 1) } - expectExcepion(exception: .rangeException) { + expectException(exception: .rangeException) { _ = updater.int(at: 1) } - expectExcepion(exception: .rangeException) { + expectException(exception: .rangeException) { _ = updater.int64(at: 1) } - expectExcepion(exception: .rangeException) { + expectException(exception: .rangeException) { _ = updater.float(at: 1) } - expectExcepion(exception: .rangeException) { + expectException(exception: .rangeException) { _ = updater.double(at: 1) } - expectExcepion(exception: .rangeException) { + expectException(exception: .rangeException) { _ = updater.boolean(at: 1) } - expectExcepion(exception: .rangeException) { + expectException(exception: .rangeException) { _ = updater.date(at: 1) } - expectExcepion(exception: .rangeException) { + expectException(exception: .rangeException) { _ = updater.blob(at: 1) } - expectExcepion(exception: .rangeException) { + expectException(exception: .rangeException) { _ = updater.dictionary(at: 1) } - expectExcepion(exception: .rangeException) { + expectException(exception: .rangeException) { _ = updater.array(at: 1) } - expectExcepion(exception: .rangeException) { + expectException(exception: .rangeException) { _ = updater.value(at: 1) } - expectExcepion(exception: .rangeException) { + expectException(exception: .rangeException) { try! updater.setVector([1.0, 2.0, 3.0], at: 1) } - expectExcepion(exception: .rangeException) { + expectException(exception: .rangeException) { updater.skipVector(at: 1) } } diff --git a/Swift/Tests/VectorSearchTest.swift b/Swift/Tests/VectorSearchTest.swift index 57d84bdf2..9bb1e87db 100644 --- a/Swift/Tests/VectorSearchTest.swift +++ b/Swift/Tests/VectorSearchTest.swift @@ -274,11 +274,11 @@ class VectorSearchTest_Main: VectorSearchTest { let config2 = VectorIndexConfiguration(expression: "vector", dimensions: 4096, centroids: 8) try wordsCollection.createIndex(withName: "words_index_2", config: config2) - expectExcepion(exception: .invalidArgumentException) { + expectException(exception: .invalidArgumentException) { _ = VectorIndexConfiguration(expression: "vector", dimensions: 1, centroids: 8) } - expectExcepion(exception: .invalidArgumentException) { + expectException(exception: .invalidArgumentException) { _ = VectorIndexConfiguration(expression: "vector", dimensions: 4097, centroids: 8) } } @@ -305,11 +305,11 @@ class VectorSearchTest_Main: VectorSearchTest { let config2 = VectorIndexConfiguration(expression: "vector", dimensions: 300, centroids: 64000) try wordsCollection.createIndex(withName: "words_index_2", config: config2) - expectExcepion(exception: .invalidArgumentException) { + expectException(exception: .invalidArgumentException) { _ = VectorIndexConfiguration(expression: "vector", dimensions: 300, centroids: 0) } - expectExcepion(exception: .invalidArgumentException) { + expectException(exception: .invalidArgumentException) { _ = VectorIndexConfiguration(expression: "vector", dimensions: 300, centroids: 64001) } } @@ -806,7 +806,7 @@ class VectorSearchTest_Main: VectorSearchTest { for numberOfSubq in [0, 7] { try deleteWordsIndex() config.encoding = .productQuantizer(subquantizers: UInt32(numberOfSubq), bits: 8) - expectExcepion(exception: .invalidArgumentException) { + expectException(exception: .invalidArgumentException) { try? self.createWordsIndex(config: config) } } @@ -874,7 +874,7 @@ class VectorSearchTest_Main: VectorSearchTest { try deleteWordsIndex() config.minTrainingSize = 10 config.maxTrainingSize = 9 - expectExcepion(exception: .invalidArgumentException) { + expectException(exception: .invalidArgumentException) { try? self.createWordsIndex(config: config) } }