@@ -237,10 +237,24 @@ func expectResults(_ snapshot: PipelineSnapshot,
237
237
}
238
238
239
239
func expectResults( result: PipelineResult ,
240
- expected: [ String : Sendable ] ,
240
+ expected: [ String : Sendable ? ] ,
241
241
file: StaticString = #file,
242
242
line: UInt = #line) {
243
- XCTAssertTrue ( areDictionariesEqual ( result. data, expected) )
243
+ XCTAssertTrue ( areDictionariesEqual ( result. data, expected) ,
244
+ " Document data mismatch. Expected \( expected) , got \( result. data) " )
245
+ }
246
+
247
+ func expectSnapshots( snapshot: PipelineSnapshot ,
248
+ expected: [ [ String : Sendable ? ] ] ,
249
+ file: StaticString = #file,
250
+ line: UInt = #line) {
251
+ for i in 0 ..< expected. count {
252
+ guard i < snapshot. results. count else {
253
+ XCTFail ( " Mismatch in expected results count and actual results count. " )
254
+ return
255
+ }
256
+ expectResults ( result: snapshot. results [ i] , expected: expected [ i] )
257
+ }
244
258
}
245
259
246
260
@available ( iOS 13 , tvOS 13 , macOS 10 . 15 , macCatalyst 13 , watchOS 7 , * )
@@ -902,13 +916,7 @@ class PipelineIntegrationTests: FSTIntegrationTestCase {
902
916
[ " avgRating " : 4.4 , " genre " : " Science Fiction " ] ,
903
917
]
904
918
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
- }
919
+ expectSnapshots ( snapshot: snapshot, expected: expectedResultsArray)
912
920
}
913
921
914
922
func testReturnsMinMaxCountAndCountAllAccumulations( ) async throws {
@@ -962,4 +970,220 @@ class PipelineIntegrationTests: FSTIntegrationTestCase {
962
970
XCTFail ( " No result for countIf aggregation " )
963
971
}
964
972
}
973
+
974
+ func testDistinctStage( ) async throws {
975
+ let collRef = collectionRef ( withDocuments: bookDocs)
976
+ let db = collRef. firestore
977
+
978
+ let pipeline = db. pipeline ( )
979
+ . collection ( collRef. path)
980
+ . distinct ( Field ( " genre " ) , Field ( " author " ) )
981
+ . sort ( Field ( " genre " ) . ascending ( ) , Field ( " author " ) . ascending ( ) )
982
+
983
+ let snapshot = try await pipeline. execute ( )
984
+
985
+ let expectedResults : [ [ String : Sendable ] ] = [
986
+ [ " genre " : " Dystopian " , " author " : " George Orwell " ] ,
987
+ [ " genre " : " Dystopian " , " author " : " Margaret Atwood " ] ,
988
+ [ " genre " : " Fantasy " , " author " : " J.R.R. Tolkien " ] ,
989
+ [ " genre " : " Magical Realism " , " author " : " Gabriel García Márquez " ] ,
990
+ [ " genre " : " Modernist " , " author " : " F. Scott Fitzgerald " ] ,
991
+ [ " genre " : " Psychological Thriller " , " author " : " Fyodor Dostoevsky " ] ,
992
+ [ " genre " : " Romance " , " author " : " Jane Austen " ] ,
993
+ [ " genre " : " Science Fiction " , " author " : " Douglas Adams " ] ,
994
+ [ " genre " : " Science Fiction " , " author " : " Frank Herbert " ] ,
995
+ [ " genre " : " Southern Gothic " , " author " : " Harper Lee " ] ,
996
+ ]
997
+
998
+ XCTAssertEqual ( snapshot. results. count, expectedResults. count, " Snapshot results count mismatch " )
999
+
1000
+ expectSnapshots ( snapshot: snapshot, expected: expectedResults)
1001
+ }
1002
+
1003
+ func testSelectStage( ) async throws {
1004
+ let collRef = collectionRef ( withDocuments: bookDocs)
1005
+ let db = collRef. firestore
1006
+
1007
+ let pipeline = db. pipeline ( )
1008
+ . collection ( collRef. path)
1009
+ . select ( Field ( " title " ) , Field ( " author " ) )
1010
+ . sort ( Field ( " author " ) . ascending ( ) )
1011
+
1012
+ let snapshot = try await pipeline. execute ( )
1013
+
1014
+ let expectedResults : [ [ String : Sendable ] ] = [
1015
+ [ " title " : " The Hitchhiker's Guide to the Galaxy " , " author " : " Douglas Adams " ] ,
1016
+ [ " title " : " The Great Gatsby " , " author " : " F. Scott Fitzgerald " ] ,
1017
+ [ " title " : " Dune " , " author " : " Frank Herbert " ] ,
1018
+ [ " title " : " Crime and Punishment " , " author " : " Fyodor Dostoevsky " ] ,
1019
+ [ " title " : " One Hundred Years of Solitude " , " author " : " Gabriel García Márquez " ] ,
1020
+ [ " title " : " 1984 " , " author " : " George Orwell " ] ,
1021
+ [ " title " : " To Kill a Mockingbird " , " author " : " Harper Lee " ] ,
1022
+ [ " title " : " The Lord of the Rings " , " author " : " J.R.R. Tolkien " ] ,
1023
+ [ " title " : " Pride and Prejudice " , " author " : " Jane Austen " ] ,
1024
+ [ " title " : " The Handmaid's Tale " , " author " : " Margaret Atwood " ] ,
1025
+ ]
1026
+
1027
+ XCTAssertEqual (
1028
+ snapshot. results. count,
1029
+ expectedResults. count,
1030
+ " Snapshot results count mismatch for select stage. "
1031
+ )
1032
+
1033
+ expectSnapshots ( snapshot: snapshot, expected: expectedResults)
1034
+ }
1035
+
1036
+ func testAddFieldStage( ) async throws {
1037
+ let collRef = collectionRef ( withDocuments: bookDocs)
1038
+ let db = collRef. firestore
1039
+
1040
+ let pipeline = db. pipeline ( )
1041
+ . collection ( collRef. path)
1042
+ . select ( Field ( " title " ) , Field ( " author " ) )
1043
+ . addFields ( Constant ( " bar " ) . as ( " foo " ) )
1044
+ . sort ( Field ( " author " ) . ascending ( ) )
1045
+
1046
+ let snapshot = try await pipeline. execute ( )
1047
+
1048
+ let expectedResults : [ [ String : Sendable ] ] = [
1049
+ [ " title " : " The Hitchhiker's Guide to the Galaxy " , " author " : " Douglas Adams " , " foo " : " bar " ] ,
1050
+ [ " title " : " The Great Gatsby " , " author " : " F. Scott Fitzgerald " , " foo " : " bar " ] ,
1051
+ [ " title " : " Dune " , " author " : " Frank Herbert " , " foo " : " bar " ] ,
1052
+ [ " title " : " Crime and Punishment " , " author " : " Fyodor Dostoevsky " , " foo " : " bar " ] ,
1053
+ [ " title " : " One Hundred Years of Solitude " , " author " : " Gabriel García Márquez " , " foo " : " bar " ] ,
1054
+ [ " title " : " 1984 " , " author " : " George Orwell " , " foo " : " bar " ] ,
1055
+ [ " title " : " To Kill a Mockingbird " , " author " : " Harper Lee " , " foo " : " bar " ] ,
1056
+ [ " title " : " The Lord of the Rings " , " author " : " J.R.R. Tolkien " , " foo " : " bar " ] ,
1057
+ [ " title " : " Pride and Prejudice " , " author " : " Jane Austen " , " foo " : " bar " ] ,
1058
+ [ " title " : " The Handmaid's Tale " , " author " : " Margaret Atwood " , " foo " : " bar " ] ,
1059
+ ]
1060
+
1061
+ XCTAssertEqual (
1062
+ snapshot. results. count,
1063
+ expectedResults. count,
1064
+ " Snapshot results count mismatch for addField stage. "
1065
+ )
1066
+
1067
+ expectSnapshots ( snapshot: snapshot, expected: expectedResults)
1068
+ }
1069
+
1070
+ func testRemoveFieldsStage( ) async throws {
1071
+ let collRef = collectionRef ( withDocuments: bookDocs)
1072
+ let db = collRef. firestore
1073
+
1074
+ let pipeline = db. pipeline ( )
1075
+ . collection ( collRef. path)
1076
+ . select ( Field ( " title " ) , Field ( " author " ) )
1077
+ . sort ( Field ( " author " ) . ascending ( ) ) // Sort before removing the 'author' field
1078
+ . removeFields ( Field ( " author " ) )
1079
+
1080
+ let snapshot = try await pipeline. execute ( )
1081
+
1082
+ // Expected results are sorted by author, but only contain the title
1083
+ let expectedResults : [ [ String : Sendable ] ] = [
1084
+ [ " title " : " The Hitchhiker's Guide to the Galaxy " ] , // Douglas Adams
1085
+ [ " title " : " The Great Gatsby " ] , // F. Scott Fitzgerald
1086
+ [ " title " : " Dune " ] , // Frank Herbert
1087
+ [ " title " : " Crime and Punishment " ] , // Fyodor Dostoevsky
1088
+ [ " title " : " One Hundred Years of Solitude " ] , // Gabriel García Márquez
1089
+ [ " title " : " 1984 " ] , // George Orwell
1090
+ [ " title " : " To Kill a Mockingbird " ] , // Harper Lee
1091
+ [ " title " : " The Lord of the Rings " ] , // J.R.R. Tolkien
1092
+ [ " title " : " Pride and Prejudice " ] , // Jane Austen
1093
+ [ " title " : " The Handmaid's Tale " ] , // Margaret Atwood
1094
+ ]
1095
+
1096
+ XCTAssertEqual (
1097
+ snapshot. results. count,
1098
+ expectedResults. count,
1099
+ " Snapshot results count mismatch for removeFields stage. "
1100
+ )
1101
+
1102
+ expectSnapshots ( snapshot: snapshot, expected: expectedResults)
1103
+ }
1104
+
1105
+ func testWhereStageWithAndConditions( ) async throws {
1106
+ let collRef = collectionRef ( withDocuments: bookDocs)
1107
+ let db = collRef. firestore
1108
+
1109
+ // Test Case 1: Two AND conditions
1110
+ var pipeline = db. pipeline ( )
1111
+ . collection ( collRef. path)
1112
+ . where ( Field ( " rating " ) . gt ( 4.5 )
1113
+ && Field ( " genre " ) . eqAny ( [ " Science Fiction " , " Romance " , " Fantasy " ] ) )
1114
+ var snapshot = try await pipeline. execute ( )
1115
+ var expectedIDs = [ " book10 " , " book4 " ] // Dune (SF, 4.6), LOTR (Fantasy, 4.7)
1116
+ expectResults ( snapshot, expectedIDs: expectedIDs)
1117
+
1118
+ // Test Case 2: Three AND conditions
1119
+ pipeline = db. pipeline ( )
1120
+ . collection ( collRef. path)
1121
+ . where (
1122
+ Field ( " rating " ) . gt ( 4.5 )
1123
+ && Field ( " genre " ) . eqAny ( [ " Science Fiction " , " Romance " , " Fantasy " ] )
1124
+ && Field ( " published " ) . lt ( 1965 )
1125
+ )
1126
+ snapshot = try await pipeline. execute ( )
1127
+ expectedIDs = [ " book4 " ] // LOTR (Fantasy, 4.7, published 1954)
1128
+ expectResults ( snapshot, expectedIDs: expectedIDs)
1129
+ }
1130
+
1131
+ func testWhereStageWithOrAndXorConditions( ) async throws {
1132
+ let collRef = collectionRef ( withDocuments: bookDocs)
1133
+ let db = collRef. firestore
1134
+
1135
+ // Test Case 1: OR conditions
1136
+ var pipeline = db. pipeline ( )
1137
+ . collection ( collRef. path)
1138
+ . where (
1139
+ Field ( " genre " ) . eq ( " Romance " )
1140
+ || Field ( " genre " ) . eq ( " Dystopian " )
1141
+ || Field ( " genre " ) . eq ( " Fantasy " )
1142
+ )
1143
+ . select ( Field ( " title " ) )
1144
+ . sort ( Field ( " title " ) . ascending ( ) )
1145
+
1146
+ var snapshot = try await pipeline. execute ( )
1147
+ var expectedResults : [ [ String : Sendable ] ] = [
1148
+ [ " title " : " 1984 " ] , // Dystopian
1149
+ [ " title " : " Pride and Prejudice " ] , // Romance
1150
+ [ " title " : " The Handmaid's Tale " ] , // Dystopian
1151
+ [ " title " : " The Lord of the Rings " ] , // Fantasy
1152
+ ]
1153
+
1154
+ XCTAssertEqual (
1155
+ snapshot. results. count,
1156
+ expectedResults. count,
1157
+ " Snapshot results count mismatch for OR conditions. "
1158
+ )
1159
+ expectSnapshots ( snapshot: snapshot, expected: expectedResults)
1160
+
1161
+ // Test Case 2: XOR conditions
1162
+ // XOR is true if an odd number of its arguments are true.
1163
+ pipeline = db. pipeline ( )
1164
+ . collection ( collRef. path)
1165
+ . where (
1166
+ Field ( " genre " ) . eq ( " Romance " ) // Book2 (T), Book5 (F), Book4 (F), Book8 (F)
1167
+ ^ Field ( " genre " ) . eq ( " Dystopian " ) // Book2 (F), Book5 (T), Book4 (F), Book8 (T)
1168
+ ^ Field ( " genre " ) . eq ( " Fantasy " ) // Book2 (F), Book5 (F), Book4 (T), Book8 (F)
1169
+ ^ Field ( " published " ) . eq ( 1949 ) // Book2 (F), Book5 (F), Book4 (F), Book8 (T)
1170
+ )
1171
+ . select ( Field ( " title " ) )
1172
+ . sort ( Field ( " title " ) . ascending ( ) )
1173
+
1174
+ snapshot = try await pipeline. execute ( )
1175
+
1176
+ expectedResults = [
1177
+ [ " title " : " Pride and Prejudice " ] ,
1178
+ [ " title " : " The Handmaid's Tale " ] ,
1179
+ [ " title " : " The Lord of the Rings " ] ,
1180
+ ]
1181
+
1182
+ XCTAssertEqual (
1183
+ snapshot. results. count,
1184
+ expectedResults. count,
1185
+ " Snapshot results count mismatch for XOR conditions. "
1186
+ )
1187
+ expectSnapshots ( snapshot: snapshot, expected: expectedResults)
1188
+ }
965
1189
}
0 commit comments