@@ -4670,6 +4670,91 @@ describe('Parse.Query testing', () => {
46704670 . catch ( done . fail ) ;
46714671 } ) ;
46724672
4673+ it ( 'includeAll handles circular pointer references' , async ( ) => {
4674+ // Create two objects that reference each other
4675+ const objA = new TestObject ( ) ;
4676+ const objB = new TestObject ( ) ;
4677+
4678+ objA . set ( 'name' , 'Object A' ) ;
4679+ objB . set ( 'name' , 'Object B' ) ;
4680+
4681+ // Save them first
4682+ await Parse . Object . saveAll ( [ objA , objB ] ) ;
4683+
4684+ // Create circular references: A -> B -> A
4685+ objA . set ( 'ref' , objB ) ;
4686+ objB . set ( 'ref' , objA ) ;
4687+
4688+ await Parse . Object . saveAll ( [ objA , objB ] ) ;
4689+
4690+ // Query with includeAll
4691+ const query = new Parse . Query ( 'TestObject' ) ;
4692+ query . equalTo ( 'objectId' , objA . id ) ;
4693+ query . includeAll ( ) ;
4694+
4695+ const results = await query . find ( ) ;
4696+
4697+ // Verify the object is returned
4698+ expect ( results . length ) . toBe ( 1 ) ;
4699+ const resultA = results [ 0 ] ;
4700+ expect ( resultA . get ( 'name' ) ) . toBe ( 'Object A' ) ;
4701+
4702+ // Verify the immediate reference is included (1 level deep)
4703+ const refB = resultA . get ( 'ref' ) ;
4704+ expect ( refB ) . toBeDefined ( ) ;
4705+ expect ( refB . get ( 'name' ) ) . toBe ( 'Object B' ) ;
4706+
4707+ // Verify that includeAll only includes 1 level deep
4708+ // B's pointer back to A should exist as an object but without full data
4709+ const refBackToA = refB . get ( 'ref' ) ;
4710+ expect ( refBackToA ) . toBeDefined ( ) ;
4711+ expect ( refBackToA . id ) . toBe ( objA . id ) ;
4712+
4713+ // The circular reference exists but is NOT fully populated
4714+ // (name field is undefined because it's not included at this depth)
4715+ expect ( refBackToA . get ( 'name' ) ) . toBeUndefined ( ) ;
4716+
4717+ // Verify using toJSON that it's stored as a pointer
4718+ const refBackToAJSON = refB . toJSON ( ) . ref ;
4719+ expect ( refBackToAJSON ) . toBeDefined ( ) ;
4720+ expect ( refBackToAJSON . __type ) . toBe ( 'Pointer' ) ;
4721+ expect ( refBackToAJSON . className ) . toBe ( 'TestObject' ) ;
4722+ expect ( refBackToAJSON . objectId ) . toBe ( objA . id ) ;
4723+ } ) ;
4724+
4725+ it ( 'includeAll handles self-referencing pointer' , async ( ) => {
4726+ // Create an object that points to itself
4727+ const selfRef = new TestObject ( ) ;
4728+ selfRef . set ( 'name' , 'Self-Referencing' ) ;
4729+
4730+ await selfRef . save ( ) ;
4731+
4732+ // Make it point to itself
4733+ selfRef . set ( 'ref' , selfRef ) ;
4734+ await selfRef . save ( ) ;
4735+
4736+ // Query with includeAll
4737+ const query = new Parse . Query ( 'TestObject' ) ;
4738+ query . equalTo ( 'objectId' , selfRef . id ) ;
4739+ query . includeAll ( ) ;
4740+
4741+ const results = await query . find ( ) ;
4742+
4743+ // Verify the object is returned
4744+ expect ( results . length ) . toBe ( 1 ) ;
4745+ const result = results [ 0 ] ;
4746+ expect ( result . get ( 'name' ) ) . toBe ( 'Self-Referencing' ) ;
4747+
4748+ // Verify the self-reference is included (since it's at the first level)
4749+ const ref = result . get ( 'ref' ) ;
4750+ expect ( ref ) . toBeDefined ( ) ;
4751+ expect ( ref . id ) . toBe ( selfRef . id ) ;
4752+
4753+ // The self-reference should be fully populated at the first level
4754+ // because includeAll includes all pointer fields at the immediate level
4755+ expect ( ref . get ( 'name' ) ) . toBe ( 'Self-Referencing' ) ;
4756+ } ) ;
4757+
46734758 it ( 'select nested keys 2 level without include (issue #3185)' , function ( done ) {
46744759 const Foobar = new Parse . Object ( 'Foobar' ) ;
46754760 const BarBaz = new Parse . Object ( 'Barbaz' ) ;
0 commit comments