From 3413a999d6de7271185c45411e0fd54b0ac8f1c9 Mon Sep 17 00:00:00 2001 From: Kaitlin Mahar Date: Tue, 21 May 2019 17:34:21 -0400 Subject: [PATCH 1/3] Fix TTL index test and move tests to new file --- .../MongoSwift/MongoCollection+Indexes.swift | 6 +- .../MongoCollection+IndexTests.swift | 238 ++++++++++++++++++ .../MongoCollectionTests.swift | 122 --------- 3 files changed, 241 insertions(+), 125 deletions(-) create mode 100644 Tests/MongoSwiftTests/MongoCollection+IndexTests.swift diff --git a/Sources/MongoSwift/MongoCollection+Indexes.swift b/Sources/MongoSwift/MongoCollection+Indexes.swift index 6dd2f55ea..cc0775045 100644 --- a/Sources/MongoSwift/MongoCollection+Indexes.swift +++ b/Sources/MongoSwift/MongoCollection+Indexes.swift @@ -32,7 +32,7 @@ public struct IndexModel: Encodable { } /// Options to use when creating an index for a collection. -public struct IndexOptions: Encodable { +public struct IndexOptions: Codable { /// Optionally tells the server to build the index in the background and not block other tasks. public let background: Bool? @@ -48,7 +48,7 @@ public struct IndexOptions: Encodable { * * - Example: For an index of name: 1, age: -1, the generated name would be "name_1_age_-1". */ - public let name: String? + public var name: String? = nil /// Optionally tells the index to only reference documents with the specified field in the index. public let sparse: Bool? @@ -61,7 +61,7 @@ public struct IndexOptions: Encodable { public let unique: Bool? /// Optionally specifies the index version number, either 0 or 1. - public let indexVersion: Int32? + public var indexVersion: Int32? /// Optionally specifies the default language for text indexes. Is 'english' if none is provided. public let defaultLanguage: String? diff --git a/Tests/MongoSwiftTests/MongoCollection+IndexTests.swift b/Tests/MongoSwiftTests/MongoCollection+IndexTests.swift new file mode 100644 index 000000000..488c13fbd --- /dev/null +++ b/Tests/MongoSwiftTests/MongoCollection+IndexTests.swift @@ -0,0 +1,238 @@ +@testable import MongoSwift +import Nimble +import XCTest + +final class MongoCollection_IndexTests: MongoSwiftTestCase { + var collName: String = "" + var coll: MongoCollection! + let doc1: Document = ["_id": 1, "cat": "dog"] + let doc2: Document = ["_id": 2, "cat": "cat"] + + /// Set up the entire suite - run once before all tests + override class func setUp() { + super.setUp() + do { + _client = try MongoClient() + } catch { + print("Setup failed: \(error)") + } + } + + /// Set up a single test - run before each testX function + override func setUp() { + super.setUp() + self.continueAfterFailure = false + self.collName = self.getCollectionName() + + do { + guard let client = _client else { + XCTFail("Invalid client") + return + } + coll = try client.db(type(of: self).testDatabase).createCollection(self.collName) + try coll.insertMany([doc1, doc2]) + } catch { + XCTFail("Setup failed: \(error)") + } + } + + /// Teardown a single test - run after each testX function + override func tearDown() { + super.tearDown() + do { + if coll != nil { try coll.drop() } + } catch { + XCTFail("Dropping test collection \(type(of: self).testDatabase).\(self.collName) failed: \(error)") + } + } + + /// Teardown the entire suite - run after all tests complete + override class func tearDown() { + super.tearDown() + do { + guard let client = _client else { + print("Invalid client") + return + } + try client.db(self.testDatabase).drop() + } catch { + print("Dropping test database \(self.testDatabase) failed: \(error)") + } + } + + + func testCreateIndexFromModel() throws { + let model = IndexModel(keys: ["cat": 1]) + expect(try self.coll.createIndex(model)).to(equal("cat_1")) + let indexes = try coll.listIndexes() + expect(indexes.next()?["name"]).to(bsonEqual("_id_")) + expect(indexes.next()?["name"]).to(bsonEqual("cat_1")) + expect(indexes.next()).to(beNil()) + } + + func testIndexOptions() throws { + let options = IndexOptions( + background: true, + name: "testOptions", + sparse: false, + storageEngine: ["wiredTiger": ["configString": "access_pattern_hint=random"] as Document], + unique: true, + indexVersion: 2, + defaultLanguage: "english", + languageOverride: "cat", + textIndexVersion: 2, + weights: ["cat": 0.5, "_id": 0.5], + sphereIndexVersion: 2, + bits: 32, + max: 30, + min: 0, + bucketSize: 10, + collation: ["locale": "fr"] + ) + + let model = IndexModel(keys: ["cat": 1, "_id": -1], options: options) + expect(try self.coll.createIndex(model)).to(equal("testOptions")) + + let ttlOptions = IndexOptions(expireAfterSeconds: 100, name: "ttl") + let ttlModel = IndexModel(keys: ["cat": 1], options: ttlOptions) + expect(try self.coll.createIndex(ttlModel)).to(equal("ttl")) + + var indexes: [IndexOptions] = try self.coll.listIndexes().map { indexDoc in + var decoded = try BSONDecoder().decode(IndexOptions.self, from: indexDoc) + // name is not one of the CodingKeys for IndexOptions so manually pull + // it out of the doc and set it on the options. + decoded.name = indexDoc.name as? String + return decoded + } + + indexes.sort { $0.name! < $1.name! } + expect(indexes).to(haveCount(3)) + + // _id index + expect(indexes[0]).to(equal(IndexOptions(name: "_id_", indexVersion: 2))) + + // testOptions index + var expectedTestOptions = options + expectedTestOptions.name = "testOptions" + expect(indexes[1]).to(equal(expectedTestOptions)) + + // ttl index + var expectedTtlOptions = ttlOptions + expectedTtlOptions.indexVersion = 2 + expect(indexes[2]).to(equal(expectedTtlOptions)) + } + + func testCreateIndexesFromModels() throws { + let model1 = IndexModel(keys: ["cat": 1]) + let model2 = IndexModel(keys: ["cat": -1]) + expect( try self.coll.createIndexes([model1, model2]) ).to(equal(["cat_1", "cat_-1"])) + let indexes = try coll.listIndexes() + expect(indexes.next()?["name"]).to(bsonEqual("_id_")) + expect(indexes.next()?["name"]).to(bsonEqual("cat_1")) + expect(indexes.next()?["name"]).to(bsonEqual("cat_-1")) + expect(indexes.next()).to(beNil()) + } + + func testCreateIndexFromKeys() throws { + expect(try self.coll.createIndex(["cat": 1])).to(equal("cat_1")) + + let indexOptions = IndexOptions(name: "blah", unique: true) + let model = IndexModel(keys: ["cat": -1], options: indexOptions) + expect(try self.coll.createIndex(model)).to(equal("blah")) + + let indexes = try coll.listIndexes() + expect(indexes.next()?["name"]).to(bsonEqual("_id_")) + expect(indexes.next()?["name"]).to(bsonEqual("cat_1")) + + let thirdIndex = indexes.next() + expect(thirdIndex?["name"]).to(bsonEqual("blah")) + expect(thirdIndex?["unique"]).to(bsonEqual(true)) + + expect(indexes.next()).to(beNil()) + } + + func testDropIndexByName() throws { + let model = IndexModel(keys: ["cat": 1]) + expect(try self.coll.createIndex(model)).to(equal("cat_1")) + expect(try self.coll.dropIndex("cat_1")).toNot(throwError()) + + // now there should only be _id_ left + let indexes = try coll.listIndexes() + expect(indexes.next()?["name"]).to(bsonEqual("_id_")) + expect(indexes.next()).to(beNil()) + } + + func testDropIndexByModel() throws { + let model = IndexModel(keys: ["cat": 1]) + expect(try self.coll.createIndex(model)).to(equal("cat_1")) + + let res = try self.coll.dropIndex(model) + expect((res["ok"] as? BSONNumber)?.doubleValue).to(bsonEqual(1.0)) + + // now there should only be _id_ left + let indexes = try coll.listIndexes() + expect(indexes).toNot(beNil()) + expect(indexes.next()?["name"]).to(bsonEqual("_id_")) + expect(indexes.next()).to(beNil()) + } + + func testDropIndexByKeys() throws { + let model = IndexModel(keys: ["cat": 1]) + expect(try self.coll.createIndex(model)).to(equal("cat_1")) + + let res = try self.coll.dropIndex(["cat": 1]) + expect((res["ok"] as? BSONNumber)?.doubleValue).to(bsonEqual(1.0)) + + // now there should only be _id_ left + let indexes = try coll.listIndexes() + expect(indexes).toNot(beNil()) + expect(indexes.next()?["name"]).to(bsonEqual("_id_")) + expect(indexes.next()).to(beNil()) + } + + func testDropAllIndexes() throws { + let model = IndexModel(keys: ["cat": 1]) + expect(try self.coll.createIndex(model)).to(equal("cat_1")) + + let res = try self.coll.dropIndexes() + expect((res["ok"] as? BSONNumber)?.doubleValue).to(bsonEqual(1.0)) + + // now there should only be _id_ left + let indexes = try coll.listIndexes() + expect(indexes.next()?["name"]).to(bsonEqual("_id_")) + expect(indexes.next()).to(beNil()) + } + + func testListIndexes() throws { + let indexes = try coll.listIndexes() + // New collection, so expect just the _id_ index to exist. + expect(indexes.next()?["name"]).to(bsonEqual("_id_")) + expect(indexes.next()).to(beNil()) + } +} + +extension IndexOptions: Equatable { + public static func == (lhs: IndexOptions, rhs: IndexOptions) -> Bool { + return lhs.background == rhs.background && + lhs.expireAfterSeconds == rhs.expireAfterSeconds && + lhs.name == rhs.name && + lhs.sparse == rhs.sparse && + lhs.storageEngine == rhs.storageEngine && + lhs.unique == rhs.unique && + lhs.indexVersion == rhs.indexVersion && + lhs.defaultLanguage == rhs.defaultLanguage && + lhs.languageOverride == rhs.languageOverride && + lhs.textIndexVersion == rhs.textIndexVersion && + lhs.weights == rhs.weights && + lhs.sphereIndexVersion == rhs.sphereIndexVersion && + lhs.bits == rhs.bits && + lhs.max == rhs.max && + lhs.min == rhs.min && + lhs.bucketSize == rhs.bucketSize && + lhs.partialFilterExpression == rhs.partialFilterExpression && + lhs.collation?["locale"] as? String == rhs.collation?["locale"] as? String + // ^ server adds a bunch of extra fields and a version number + // to collations. rather than deal with those, just verify the + // locale matches. + } +} \ No newline at end of file diff --git a/Tests/MongoSwiftTests/MongoCollectionTests.swift b/Tests/MongoSwiftTests/MongoCollectionTests.swift index 089bb23be..01c09413f 100644 --- a/Tests/MongoSwiftTests/MongoCollectionTests.swift +++ b/Tests/MongoSwiftTests/MongoCollectionTests.swift @@ -260,128 +260,6 @@ final class MongoCollectionTests: MongoSwiftTestCase { // swiftlint:enable trailing_closure } - func testCreateIndexFromModel() throws { - let model = IndexModel(keys: ["cat": 1]) - expect(try self.coll.createIndex(model)).to(equal("cat_1")) - let indexes = try coll.listIndexes() - expect(indexes.next()?["name"]).to(bsonEqual("_id_")) - expect(indexes.next()?["name"]).to(bsonEqual("cat_1")) - expect(indexes.next()).to(beNil()) - } - - func testIndexOptions() throws { - let options = IndexOptions( - background: true, - expireAfterSeconds: 100, - name: "testOptions", - sparse: false, - storageEngine: ["wiredTiger": ["configString": "access_pattern_hint=random"] as Document], - unique: true, - indexVersion: 2, - defaultLanguage: "english", - languageOverride: "cat", - textIndexVersion: 2, - weights: ["cat": 0.5, "_id": 0.5], - sphereIndexVersion: 2, - bits: 32, - max: 30, - min: 0, - bucketSize: 10, - collation: ["locale": "fr"] - ) - - let model = IndexModel(keys: ["cat": 1, "_id": -1], options: options) - expect(try self.coll.createIndex(model)).to(equal("testOptions")) - } - - func testCreateIndexesFromModels() throws { - let model1 = IndexModel(keys: ["cat": 1]) - let model2 = IndexModel(keys: ["cat": -1]) - expect( try self.coll.createIndexes([model1, model2]) ).to(equal(["cat_1", "cat_-1"])) - let indexes = try coll.listIndexes() - expect(indexes.next()?["name"]).to(bsonEqual("_id_")) - expect(indexes.next()?["name"]).to(bsonEqual("cat_1")) - expect(indexes.next()?["name"]).to(bsonEqual("cat_-1")) - expect(indexes.next()).to(beNil()) - } - - func testCreateIndexFromKeys() throws { - expect(try self.coll.createIndex(["cat": 1])).to(equal("cat_1")) - - let indexOptions = IndexOptions(name: "blah", unique: true) - let model = IndexModel(keys: ["cat": -1], options: indexOptions) - expect(try self.coll.createIndex(model)).to(equal("blah")) - - let indexes = try coll.listIndexes() - expect(indexes.next()?["name"]).to(bsonEqual("_id_")) - expect(indexes.next()?["name"]).to(bsonEqual("cat_1")) - - let thirdIndex = indexes.next() - expect(thirdIndex?["name"]).to(bsonEqual("blah")) - expect(thirdIndex?["unique"]).to(bsonEqual(true)) - - expect(indexes.next()).to(beNil()) - } - - func testDropIndexByName() throws { - let model = IndexModel(keys: ["cat": 1]) - expect(try self.coll.createIndex(model)).to(equal("cat_1")) - expect(try self.coll.dropIndex("cat_1")).toNot(throwError()) - - // now there should only be _id_ left - let indexes = try coll.listIndexes() - expect(indexes.next()?["name"]).to(bsonEqual("_id_")) - expect(indexes.next()).to(beNil()) - } - - func testDropIndexByModel() throws { - let model = IndexModel(keys: ["cat": 1]) - expect(try self.coll.createIndex(model)).to(equal("cat_1")) - - let res = try self.coll.dropIndex(model) - expect((res["ok"] as? BSONNumber)?.doubleValue).to(bsonEqual(1.0)) - - // now there should only be _id_ left - let indexes = try coll.listIndexes() - expect(indexes).toNot(beNil()) - expect(indexes.next()?["name"]).to(bsonEqual("_id_")) - expect(indexes.next()).to(beNil()) - } - - func testDropIndexByKeys() throws { - let model = IndexModel(keys: ["cat": 1]) - expect(try self.coll.createIndex(model)).to(equal("cat_1")) - - let res = try self.coll.dropIndex(["cat": 1]) - expect((res["ok"] as? BSONNumber)?.doubleValue).to(bsonEqual(1.0)) - - // now there should only be _id_ left - let indexes = try coll.listIndexes() - expect(indexes).toNot(beNil()) - expect(indexes.next()?["name"]).to(bsonEqual("_id_")) - expect(indexes.next()).to(beNil()) - } - - func testDropAllIndexes() throws { - let model = IndexModel(keys: ["cat": 1]) - expect(try self.coll.createIndex(model)).to(equal("cat_1")) - - let res = try self.coll.dropIndexes() - expect((res["ok"] as? BSONNumber)?.doubleValue).to(bsonEqual(1.0)) - - // now there should only be _id_ left - let indexes = try coll.listIndexes() - expect(indexes.next()?["name"]).to(bsonEqual("_id_")) - expect(indexes.next()).to(beNil()) - } - - func testListIndexes() throws { - let indexes = try coll.listIndexes() - // New collection, so expect just the _id_ index to exist. - expect(indexes.next()?["name"]).to(bsonEqual("_id_")) - expect(indexes.next()).to(beNil()) - } - func testGetName() { expect(self.coll.name).to(equal(self.collName)) } From ae22682259adf75cb52bebff6ae74cd22ad2ffe3 Mon Sep 17 00:00:00 2001 From: Kaitlin Mahar Date: Tue, 21 May 2019 17:35:09 -0400 Subject: [PATCH 2/3] lint --- Sources/MongoSwift/MongoCollection+Indexes.swift | 2 +- Tests/MongoSwiftTests/MongoCollection+IndexTests.swift | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Sources/MongoSwift/MongoCollection+Indexes.swift b/Sources/MongoSwift/MongoCollection+Indexes.swift index cc0775045..c991a49a9 100644 --- a/Sources/MongoSwift/MongoCollection+Indexes.swift +++ b/Sources/MongoSwift/MongoCollection+Indexes.swift @@ -48,7 +48,7 @@ public struct IndexOptions: Codable { * * - Example: For an index of name: 1, age: -1, the generated name would be "name_1_age_-1". */ - public var name: String? = nil + public var name: String? /// Optionally tells the index to only reference documents with the specified field in the index. public let sparse: Bool? diff --git a/Tests/MongoSwiftTests/MongoCollection+IndexTests.swift b/Tests/MongoSwiftTests/MongoCollection+IndexTests.swift index 488c13fbd..18926c0db 100644 --- a/Tests/MongoSwiftTests/MongoCollection+IndexTests.swift +++ b/Tests/MongoSwiftTests/MongoCollection+IndexTests.swift @@ -60,7 +60,6 @@ final class MongoCollection_IndexTests: MongoSwiftTestCase { } } - func testCreateIndexFromModel() throws { let model = IndexModel(keys: ["cat": 1]) expect(try self.coll.createIndex(model)).to(equal("cat_1")) @@ -235,4 +234,4 @@ extension IndexOptions: Equatable { // to collations. rather than deal with those, just verify the // locale matches. } -} \ No newline at end of file +} From 4f0dcf9bf4d49b19095d893f07f2b174c982fc58 Mon Sep 17 00:00:00 2001 From: Kaitlin Mahar Date: Tue, 21 May 2019 18:04:26 -0400 Subject: [PATCH 3/3] update linuxmain --- Tests/LinuxMain.swift | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/Tests/LinuxMain.swift b/Tests/LinuxMain.swift index 05bb80407..7890102cd 100644 --- a/Tests/LinuxMain.swift +++ b/Tests/LinuxMain.swift @@ -148,15 +148,6 @@ extension MongoCollectionTests { ("testUpdateMany", testUpdateMany), ("testUpdateManyWithUnacknowledgedWriteConcern", testUpdateManyWithUnacknowledgedWriteConcern), ("testDistinct", testDistinct), - ("testCreateIndexFromModel", testCreateIndexFromModel), - ("testIndexOptions", testIndexOptions), - ("testCreateIndexesFromModels", testCreateIndexesFromModels), - ("testCreateIndexFromKeys", testCreateIndexFromKeys), - ("testDropIndexByName", testDropIndexByName), - ("testDropIndexByModel", testDropIndexByModel), - ("testDropIndexByKeys", testDropIndexByKeys), - ("testDropAllIndexes", testDropAllIndexes), - ("testListIndexes", testListIndexes), ("testGetName", testGetName), ("testCursorIteration", testCursorIteration), ("testCodableCollection", testCodableCollection), @@ -181,6 +172,20 @@ extension MongoCollection_BulkWriteTests { ] } +extension MongoCollection_IndexTests { + static var allTests = [ + ("testCreateIndexFromModel", testCreateIndexFromModel), + ("testIndexOptions", testIndexOptions), + ("testCreateIndexesFromModels", testCreateIndexesFromModels), + ("testCreateIndexFromKeys", testCreateIndexFromKeys), + ("testDropIndexByName", testDropIndexByName), + ("testDropIndexByModel", testDropIndexByModel), + ("testDropIndexByKeys", testDropIndexByKeys), + ("testDropAllIndexes", testDropAllIndexes), + ("testListIndexes", testListIndexes), + ] +} + extension MongoDatabaseTests { static var allTests = [ ("testMongoDatabase", testMongoDatabase), @@ -231,6 +236,7 @@ XCTMain([ testCase(MongoClientTests.allTests), testCase(MongoCollectionTests.allTests), testCase(MongoCollection_BulkWriteTests.allTests), + testCase(MongoCollection_IndexTests.allTests), testCase(MongoDatabaseTests.allTests), testCase(ReadPreferenceTests.allTests), testCase(ReadWriteConcernTests.allTests),