11import { describe , expect , it , jest } from '@jest/globals' ;
22import { getFirestore } from '../lib' ;
33import { getApp } from '@react-native-firebase/app' ;
4+ import { FieldPath } from '../lib/FieldPath' ;
5+ import { Timestamp } from '../lib/FirestoreTimestamp' ;
46import {
57 arrayFilter ,
68 arrayFirst ,
@@ -27,9 +29,19 @@ import {
2729 countAll ,
2830 average ,
2931 subcollection ,
32+ array ,
33+ map ,
3034} from '../lib/pipelines' ;
3135import '../lib/pipelines' ;
32- import { ConstantExpression , FunctionExpression } from '../lib/pipelines/expressions' ;
36+ import {
37+ avg ,
38+ ConstantExpression ,
39+ eq ,
40+ FunctionExpression ,
41+ gt ,
42+ gte ,
43+ lt ,
44+ } from '../lib/pipelines/expressions' ;
3345
3446describe ( 'Firestore pipelines runtime' , function ( ) {
3547 it ( 'installs pipeline() and serializes source builders' , function ( ) {
@@ -646,6 +658,151 @@ describe('Firestore pipelines runtime', function () {
646658 } ) ;
647659 } ) ;
648660
661+ it ( 'serializes comparison alias exports gt, eq, gte, and lt' , function ( ) {
662+ expect ( gt ( field ( 'rating' ) , constant ( 4 ) ) ) . toMatchObject ( {
663+ exprType : 'Function' ,
664+ name : 'greaterThan' ,
665+ } ) ;
666+ expect ( eq ( field ( 'sku' ) , constant ( 'SKU001' ) ) ) . toMatchObject ( {
667+ exprType : 'Function' ,
668+ name : 'equal' ,
669+ } ) ;
670+ expect ( gte ( field ( 'stock' ) , constant ( 50 ) ) ) . toMatchObject ( {
671+ exprType : 'Function' ,
672+ name : 'greaterThanOrEqual' ,
673+ } ) ;
674+ expect ( lt ( field ( 'price' ) , constant ( 100 ) ) ) . toMatchObject ( {
675+ exprType : 'Function' ,
676+ name : 'lessThan' ,
677+ } ) ;
678+ } ) ;
679+
680+ it ( 'serializes avg aggregate alias to average' , function ( ) {
681+ expect ( avg ( field ( 'score' ) ) ) . toMatchObject ( {
682+ exprType : 'AggregateFunction' ,
683+ kind : 'average' ,
684+ } ) ;
685+ } ) ;
686+
687+ it ( 'normalizes array and map helpers that embed runtime expression nodes' , function ( ) {
688+ const arrayExpr : any = array ( [ field ( 'score' ) , constant ( 'tail' ) ] ) ;
689+ expect ( arrayExpr ) . toMatchObject ( {
690+ exprType : 'Function' ,
691+ name : 'array' ,
692+ args : [
693+ { exprType : 'Field' , path : 'score' } ,
694+ { exprType : 'Constant' , value : 'tail' } ,
695+ ] ,
696+ } ) ;
697+
698+ const mapExpr : any = map ( { label : field ( 'name' ) , version : constant ( 1 ) } ) ;
699+ expect ( mapExpr ) . toMatchObject ( {
700+ exprType : 'Function' ,
701+ name : 'map' ,
702+ } ) ;
703+ expect ( mapExpr . args [ 0 ] . exprType ) . toBe ( 'Constant' ) ;
704+ expect ( mapExpr . args [ 0 ] . value . label . exprType ) . toBe ( 'Field' ) ;
705+ expect ( mapExpr . args [ 0 ] . value . version . value ) . toBe ( 1 ) ;
706+ } ) ;
707+
708+ it ( 'preserves non-plain constant values without walking prototype objects' , function ( ) {
709+ class Marker {
710+ readonly tag = 'marker' ;
711+ }
712+ const marker = new Marker ( ) ;
713+ const expr : any = constant ( marker ) ;
714+ expect ( expr ) . toMatchObject ( {
715+ exprType : 'Constant' ,
716+ value : marker ,
717+ } ) ;
718+ } ) ;
719+
720+ it ( 'normalizes pipeline execute results across timestamp and field path shapes' , async function ( ) {
721+ const db : any = getFirestore ( ) ;
722+ const existingExecutionTime = new Timestamp ( 1735689600 , 123000000 ) ;
723+ const nativeExecute = jest . fn ( async ( ) => ( {
724+ executionTime : existingExecutionTime ,
725+ results : [
726+ {
727+ path : 'books/alpha' ,
728+ id : 'alpha' ,
729+ data : { title : 'Alpha' , nested : { score : 9 } } ,
730+ createTime : 1700000000123 ,
731+ updateTime : [ 1700000001 , 456000000 ] ,
732+ } ,
733+ {
734+ path : 'books/beta' ,
735+ data : { plain : true } ,
736+ createTime : { seconds : 2 , nanoseconds : 3 } ,
737+ updateTime : { seconds : 4 , nanoseconds : 5 } ,
738+ } ,
739+ ] ,
740+ } ) ) ;
741+
742+ const originalNativeModule = db . _nativeModule ;
743+ db . _nativeModule = { pipelineExecute : nativeExecute } ;
744+
745+ try {
746+ const snapshot = await execute ( db . pipeline ( ) . documents ( [ 'books/alpha' , 'books/beta' ] ) ) ;
747+
748+ expect ( snapshot . executionTime ) . toBe ( existingExecutionTime ) ;
749+ expect ( snapshot . results [ 0 ] ?. createTime ?. toMillis ( ) ) . toBe ( 1700000000123 ) ;
750+ expect ( snapshot . results [ 0 ] ?. updateTime ?. seconds ) . toBe ( 1700000001 ) ;
751+ expect ( snapshot . results [ 0 ] ?. updateTime ?. nanoseconds ) . toBe ( 456000000 ) ;
752+ expect ( snapshot . results [ 0 ] ?. get ( 'nested.score' ) ) . toBe ( 9 ) ;
753+ expect ( snapshot . results [ 0 ] ?. get ( new FieldPath ( 'nested' , 'score' ) ) ) . toBe ( 9 ) ;
754+ expect ( snapshot . results [ 0 ] ?. get ( field ( 'nested.score' ) ) ) . toBe ( 9 ) ;
755+ expect ( snapshot . results [ 1 ] ?. data ( ) ) . toEqual ( { plain : true } ) ;
756+ expect ( snapshot . results [ 1 ] ?. createTime ?. seconds ) . toBe ( 2 ) ;
757+ expect ( snapshot . results [ 1 ] ?. updateTime ?. nanoseconds ) . toBe ( 5 ) ;
758+ } finally {
759+ db . _nativeModule = originalNativeModule ;
760+ }
761+ } ) ;
762+
763+ it ( 'serializes FieldPath and Timestamp arguments in pipeline stages' , function ( ) {
764+ const db : any = getFirestore ( ) ;
765+ const timestamp = new Timestamp ( 12 , 34 ) ;
766+ const fieldPath = new FieldPath ( 'meta' , 'createdAt' ) ;
767+
768+ const serialized = db
769+ . pipeline ( )
770+ . collection ( 'books' )
771+ . where ( greaterThan ( field ( 'rating' ) , timestamp as any ) )
772+ . sort ( Ordering . of ( fieldPath as any ) . descending ( ) )
773+ . distinct ( { groups : [ fieldPath ] } as any )
774+ . select (
775+ field ( 'title' )
776+ . add ( timestamp as any )
777+ . as ( 'createdAt' ) ,
778+ )
779+ . serialize ( ) ;
780+
781+ expect ( serialized . stages [ 0 ] ?. options ?. condition ?. args ?. [ 1 ] ) . toMatchObject ( {
782+ exprType : 'Constant' ,
783+ value : {
784+ seconds : 12 ,
785+ nanoseconds : 34 ,
786+ } ,
787+ } ) ;
788+ expect ( serialized . stages [ 1 ] ?. options ?. orderings ?. [ 0 ] ?. expr ) . toMatchObject ( {
789+ exprType : 'Constant' ,
790+ value : {
791+ segments : [ 'meta' , 'createdAt' ] ,
792+ } ,
793+ } ) ;
794+ expect ( serialized . stages [ 2 ] ?. options ?. groups ?. [ 0 ] ) . toEqual ( {
795+ segments : [ 'meta' , 'createdAt' ] ,
796+ } ) ;
797+ expect ( serialized . stages [ 3 ] ?. options ?. selections ?. [ 0 ] ?. expr ?. args ?. [ 1 ] ) . toMatchObject ( {
798+ exprType : 'Constant' ,
799+ value : {
800+ seconds : 12 ,
801+ nanoseconds : 34 ,
802+ } ,
803+ } ) ;
804+ } ) ;
805+
649806 it ( 'supports method-style expression chaining and ordering helper serialization' , function ( ) {
650807 const db : any = getFirestore ( ) ;
651808
0 commit comments