@@ -151,6 +151,10 @@ private func areEqual(_ value1: Sendable?, _ value2: Sendable?) -> Bool {
151
151
return v1 == v2
152
152
case let ( v1 as Int , v2 as Int ) :
153
153
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
154
158
case let ( v1 as String , v2 as String ) :
155
159
return v1 == v2
156
160
case let ( v1 as Bool , v2 as Bool ) :
@@ -169,10 +173,11 @@ private func areDictionariesEqual(_ dict1: [String: Sendable?],
169
173
guard dict1. count == dict2. count else { return false }
170
174
171
175
for (key, value1) in dict1 {
172
- print ( " key1: \( key) " )
173
- print ( " value1: \( String ( describing: value1) ) " )
174
- print ( " value2: \( String ( describing: dict2 [ key] ) ) " )
175
176
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] ) ) " )
176
181
return false
177
182
}
178
183
}
@@ -184,11 +189,11 @@ private func areArraysEqual(_ array1: [Sendable?], _ array2: [Sendable?]) -> Boo
184
189
guard array1. count == array2. count else { return false }
185
190
186
191
for (index, value1) in array1. enumerated ( ) {
187
- print ( " value1: \( String ( describing: value1) ) " )
188
-
189
192
let value2 = array2 [ index]
190
- print ( " value2: \( String ( describing: value2) ) " )
191
193
if !areEqual( value1, value2) {
194
+ print ( " The Array value is not equal. " )
195
+ print ( " value1: \( String ( describing: value1) ) " )
196
+ print ( " value2: \( String ( describing: value2) ) " )
192
197
return false
193
198
}
194
199
}
@@ -715,6 +720,246 @@ class PipelineIntegrationTests: FSTIntegrationTestCase {
715
720
)
716
721
let snapshot = try await pipeline. execute ( )
717
722
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
+ }
719
964
}
720
965
}
0 commit comments