Skip to content

Commit 0f7d8ec

Browse files
committed
add more tests
1 parent 51086da commit 0f7d8ec

File tree

2 files changed

+256
-7
lines changed

2 files changed

+256
-7
lines changed

Firestore/Swift/Source/SwiftAPI/Pipeline/Expr/FunctionExpr/BooleanExpr.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ public class BooleanExpr: FunctionExpr, @unchecked Sendable {
1717
super.init(functionName, agrs)
1818
}
1919

20+
public func countIf() -> AggregateFunction {
21+
return AggregateFunction("count_if", [self])
22+
}
23+
2024
public static func && (lhs: BooleanExpr,
2125
rhs: @autoclosure () throws -> BooleanExpr) rethrows -> BooleanExpr {
2226
try BooleanExpr("and", [lhs, rhs()])

Firestore/Swift/Tests/Integration/PipelineTests.swift

Lines changed: 252 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,10 @@ private func areEqual(_ value1: Sendable?, _ value2: Sendable?) -> Bool {
151151
return v1 == v2
152152
case let (v1 as Int, v2 as Int):
153153
return v1 == v2
154+
case let (v1 as Double, v2 as Double):
155+
return v1 == v2
156+
case let (v1 as Float, v2 as Float):
157+
return v1 == v2
154158
case let (v1 as String, v2 as String):
155159
return v1 == v2
156160
case let (v1 as Bool, v2 as Bool):
@@ -169,10 +173,11 @@ private func areDictionariesEqual(_ dict1: [String: Sendable?],
169173
guard dict1.count == dict2.count else { return false }
170174

171175
for (key, value1) in dict1 {
172-
print("key1: \(key)")
173-
print("value1: \(String(describing: value1))")
174-
print("value2: \(String(describing: dict2[key]))")
175176
guard let value2 = dict2[key], areEqual(value1, value2) else {
177+
print("The Dictionary value is not equal.")
178+
print("key1: \(key)")
179+
print("value1: \(String(describing: value1))")
180+
print("value2: \(String(describing: dict2[key]))")
176181
return false
177182
}
178183
}
@@ -184,11 +189,11 @@ private func areArraysEqual(_ array1: [Sendable?], _ array2: [Sendable?]) -> Boo
184189
guard array1.count == array2.count else { return false }
185190

186191
for (index, value1) in array1.enumerated() {
187-
print("value1: \(String(describing: value1))")
188-
189192
let value2 = array2[index]
190-
print("value2: \(String(describing: value2))")
191193
if !areEqual(value1, value2) {
194+
print("The Array value is not equal.")
195+
print("value1: \(String(describing: value1))")
196+
print("value2: \(String(describing: value2))")
192197
return false
193198
}
194199
}
@@ -715,6 +720,246 @@ class PipelineIntegrationTests: FSTIntegrationTestCase {
715720
)
716721
let snapshot = try await pipeline.execute()
717722

718-
expectResults(result: snapshot.results[0], expected: expectedResultsMap)
723+
XCTAssertEqual(snapshot.results.count, 1)
724+
expectResults(result: snapshot.results.first!, expected: expectedResultsMap)
725+
}
726+
727+
func testConvertsArraysAndPlainObjectsToFunctionValues() async throws {
728+
let collRef = collectionRef(withDocuments: bookDocs) // Uses existing bookDocs
729+
let db = collRef.firestore
730+
731+
// Expected data for "The Lord of the Rings"
732+
let expectedTitle = "The Lord of the Rings"
733+
let expectedAuthor = "J.R.R. Tolkien"
734+
let expectedGenre = "Fantasy"
735+
let expectedPublished = 1954
736+
let expectedRating = 4.7
737+
let expectedTags = ["adventure", "magic", "epic"]
738+
let expectedAwards: [String: Sendable] = ["hugo": false, "nebula": false]
739+
740+
let metadataArrayElements: [Sendable] = [
741+
1,
742+
2,
743+
expectedGenre,
744+
expectedRating * 10,
745+
[expectedTitle],
746+
["published": expectedPublished],
747+
]
748+
749+
let metadataMapElements: [String: Sendable] = [
750+
"genre": expectedGenre,
751+
"rating": expectedRating * 10,
752+
"nestedArray": [expectedTitle],
753+
"nestedMap": ["published": expectedPublished],
754+
]
755+
756+
let pipeline = db.pipeline()
757+
.collection(collRef.path)
758+
.sort(Field("rating").descending())
759+
.limit(1) // This should pick "The Lord of the Rings" (rating 4.7)
760+
.select(
761+
Field("title"),
762+
Field("author"),
763+
Field("genre"),
764+
Field("rating"),
765+
Field("published"),
766+
Field("tags"),
767+
Field("awards")
768+
)
769+
.addFields(
770+
ArrayExpression([
771+
1,
772+
2,
773+
Field("genre"),
774+
Field("rating").multiply(10),
775+
ArrayExpression([Field("title")]),
776+
MapExpression(["published": Field("published")]),
777+
]).as("metadataArray"),
778+
MapExpression([
779+
"genre": Field("genre"),
780+
"rating": Field("rating").multiply(10),
781+
"nestedArray": ArrayExpression([Field("title")]),
782+
"nestedMap": MapExpression(["published": Field("published")]),
783+
]).as("metadata")
784+
)
785+
.where(
786+
Field("metadataArray").eq(metadataArrayElements) &&
787+
Field("metadata").eq(metadataMapElements)
788+
)
789+
790+
let snapshot = try await pipeline.execute()
791+
792+
XCTAssertEqual(snapshot.results.count, 1, "Should retrieve one document")
793+
794+
if let resultDoc = snapshot.results.first {
795+
let expectedFullDoc: [String: Sendable?] = [
796+
"title": expectedTitle,
797+
"author": expectedAuthor,
798+
"genre": expectedGenre,
799+
"published": expectedPublished,
800+
"rating": expectedRating,
801+
"tags": expectedTags,
802+
"awards": expectedAwards,
803+
"metadataArray": metadataArrayElements,
804+
"metadata": metadataMapElements,
805+
]
806+
XCTAssertTrue(
807+
areDictionariesEqual(resultDoc.data, expectedFullDoc as [String: Sendable]),
808+
"Document data does not match expected."
809+
)
810+
} else {
811+
XCTFail("No document retrieved")
812+
}
813+
}
814+
815+
func testSupportsAggregate() async throws {
816+
let collRef = collectionRef(withDocuments: bookDocs)
817+
let db = collRef.firestore
818+
819+
var pipeline = db.pipeline()
820+
.collection(collRef.path)
821+
.aggregate(CountAll().as("count"))
822+
var snapshot = try await pipeline.execute()
823+
824+
XCTAssertEqual(snapshot.results.count, 1, "Count all should return a single aggregate document")
825+
if let result = snapshot.results.first {
826+
expectResults(result: result, expected: ["count": bookDocs.count])
827+
} else {
828+
XCTFail("No result for count all aggregation")
829+
}
830+
831+
pipeline = db.pipeline()
832+
.collection(collRef.path)
833+
.where(Field("genre").eq("Science Fiction"))
834+
.aggregate(
835+
CountAll().as("count"),
836+
Field("rating").avg().as("avgRating"),
837+
Field("rating").maximum().as("maxRating")
838+
)
839+
snapshot = try await pipeline.execute()
840+
841+
XCTAssertEqual(snapshot.results.count, 1, "Filtered aggregate should return a single document")
842+
if let result = snapshot.results.first {
843+
let expectedAggValues: [String: Sendable] = [
844+
"count": 2,
845+
"avgRating": 4.4,
846+
"maxRating": 4.6,
847+
]
848+
expectResults(result: result, expected: expectedAggValues)
849+
} else {
850+
XCTFail("No result for filtered aggregation")
851+
}
852+
}
853+
854+
func testRejectsGroupsWithoutAccumulators() async throws {
855+
let collRef = collectionRef(withDocuments: bookDocs)
856+
let db = collRef.firestore
857+
858+
let dummyDocRef = collRef.document("dummyDocForRejectTest")
859+
try await dummyDocRef.setData(["field": "value"])
860+
861+
do {
862+
_ = try await db.pipeline()
863+
.collection(collRef.path)
864+
.where(Field("published").lt(1900))
865+
.aggregate([], groups: ["genre"])
866+
.execute()
867+
868+
XCTFail(
869+
"The pipeline should have thrown an error for groups without accumulators, but it did not."
870+
)
871+
872+
} catch {
873+
XCTAssert(true, "Successfully caught expected error for groups without accumulators.")
874+
}
875+
}
876+
877+
func testReturnsGroupAndAccumulateResults() async throws {
878+
let collRef = collectionRef(withDocuments: bookDocs)
879+
let db = collRef.firestore
880+
881+
let pipeline = db.pipeline()
882+
.collection(collRef.path)
883+
.where(Field("published").lt(1984))
884+
.aggregate(
885+
[Field("rating").avg().as("avgRating")],
886+
groups: ["genre"]
887+
)
888+
.where(Field("avgRating").gt(4.3))
889+
.sort(Field("avgRating").descending())
890+
891+
let snapshot = try await pipeline.execute()
892+
893+
XCTAssertEqual(
894+
snapshot.results.count,
895+
3,
896+
"Should return 3 documents after grouping and filtering."
897+
)
898+
899+
let expectedResultsArray: [[String: Sendable]] = [
900+
["avgRating": 4.7, "genre": "Fantasy"],
901+
["avgRating": 4.5, "genre": "Romance"],
902+
["avgRating": 4.4, "genre": "Science Fiction"],
903+
]
904+
905+
for i in 0 ..< expectedResultsArray.count {
906+
guard i < snapshot.results.count else {
907+
XCTFail("Mismatch in expected results count and actual results count.")
908+
return
909+
}
910+
expectResults(result: snapshot.results[i], expected: expectedResultsArray[i])
911+
}
912+
}
913+
914+
func testReturnsMinMaxCountAndCountAllAccumulations() async throws {
915+
let collRef = collectionRef(withDocuments: bookDocs)
916+
let db = collRef.firestore
917+
918+
let pipeline = db.pipeline()
919+
.collection(collRef.path)
920+
.aggregate(
921+
Field("cost").count().as("booksWithCost"),
922+
CountAll().as("count"),
923+
Field("rating").maximum().as("maxRating"),
924+
Field("published").minimum().as("minPublished")
925+
)
926+
927+
let snapshot = try await pipeline.execute()
928+
929+
XCTAssertEqual(snapshot.results.count, 1, "Aggregate should return a single document")
930+
931+
let expectedValues: [String: Sendable] = [
932+
"booksWithCost": 1,
933+
"count": bookDocs.count,
934+
"maxRating": 4.7,
935+
"minPublished": 1813,
936+
]
937+
938+
if let result = snapshot.results.first {
939+
expectResults(result: result, expected: expectedValues)
940+
} else {
941+
XCTFail("No result for min/max/count/countAll aggregation")
942+
}
943+
}
944+
945+
func testReturnsCountIfAccumulation() async throws {
946+
let collRef = collectionRef(withDocuments: bookDocs)
947+
let db = collRef.firestore
948+
949+
let expectedCount = 3
950+
let expectedResults: [String: Sendable] = ["count": expectedCount]
951+
let condition = Field("rating").gt(4.3)
952+
953+
var pipeline = db.pipeline()
954+
.collection(collRef.path)
955+
.aggregate(condition.countIf().as("count"))
956+
var snapshot = try await pipeline.execute()
957+
958+
XCTAssertEqual(snapshot.results.count, 1, "countIf aggregate should return a single document")
959+
if let result = snapshot.results.first {
960+
expectResults(result: result, expected: expectedResults)
961+
} else {
962+
XCTFail("No result for countIf aggregation")
963+
}
719964
}
720965
}

0 commit comments

Comments
 (0)