@@ -4608,6 +4608,190 @@ mod tests {
46084608 ) ;
46094609 }
46104610
4611+ #[ cfg( feature = "server" ) ]
4612+ #[ test]
4613+ fn test_family_single_in_clause_with_cursor_v0_lowering_frozen_at_protocol_v13 ( ) {
4614+ // Freeze test for the pre-v14 lowering: protocol versions <= 13 are
4615+ // on chain, so their (defective) cursor lowering must replay
4616+ // byte-for-byte. The expected values below are the v0 outputs the
4617+ // sibling-branch fix corrects at v14 — see
4618+ // test_family_single_in_clause_with_cursor_keeps_sibling_branches_intact
4619+ // for the correct shapes. Never edit these expectations.
4620+ let platform_version = PlatformVersion :: latest ( ) ;
4621+ let protocol_v13 = PlatformVersion :: get ( 13 ) . expect ( "protocol version 13 exists" ) ;
4622+ assert_eq ! (
4623+ protocol_v13
4624+ . drive
4625+ . methods
4626+ . document
4627+ . query
4628+ . non_primary_key_path_query,
4629+ 0 ,
4630+ "protocol v13 must keep the v0 non-primary-key path query lowering"
4631+ ) ;
4632+
4633+ let people: Vec < Person > = [
4634+ ( 1u8 , "Adam" , "Kline" ) ,
4635+ ( 2 , "Adam" , "Moore" ) ,
4636+ ( 3 , "Adam" , "Sinclair" ) ,
4637+ ( 4 , "Ben" , "Barber" ) ,
4638+ ( 5 , "Ben" , "Nichols" ) ,
4639+ ( 6 , "Cara" , "Abbott" ) ,
4640+ ( 7 , "Cara" , "Zane" ) ,
4641+ ]
4642+ . into_iter ( )
4643+ . map ( |( seed, first_name, last_name) | Person {
4644+ id : vec ! [ seed; 32 ] ,
4645+ owner_id : vec ! [ seed. wrapping_add( 100 ) ; 32 ] ,
4646+ first_name : first_name. to_string ( ) ,
4647+ middle_name : format ! ( "{}middle" , first_name) ,
4648+ last_name : last_name. to_string ( ) ,
4649+ age : seed + 20 ,
4650+ } )
4651+ . collect ( ) ;
4652+
4653+ let ( drive, contract) = setup_family_tests_with_people ( & people, platform_version) ;
4654+ let person_document_type = contract
4655+ . document_type_for_name ( "person" )
4656+ . expect ( "contract should have a person document type" ) ;
4657+
4658+ let root_hash = drive
4659+ . grove
4660+ . root_hash ( None , & platform_version. drive . grove_version )
4661+ . unwrap ( )
4662+ . expect ( "there is always a root hash" ) ;
4663+
4664+ let assert_v13_query_returns =
4665+ |query_value : & serde_json:: Value , expected : Vec < ( & str , & str ) > | {
4666+ let where_cbor = cbor_serializer:: serializable_value_to_cbor ( query_value, None )
4667+ . expect ( "expected to serialize to cbor" ) ;
4668+ let query = DriveDocumentQuery :: from_cbor (
4669+ where_cbor. as_slice ( ) ,
4670+ & contract,
4671+ person_document_type,
4672+ & drive. config ,
4673+ )
4674+ . expect ( "query should be built" ) ;
4675+ let ( results, _, _) = query
4676+ . execute_raw_results_no_proof ( & drive, None , None , protocol_v13)
4677+ . expect ( "query should be executed" ) ;
4678+
4679+ let names: Vec < ( String , String ) > = results
4680+ . iter ( )
4681+ . map ( |result| {
4682+ let document = Document :: from_bytes (
4683+ result. as_slice ( ) ,
4684+ person_document_type,
4685+ protocol_v13,
4686+ )
4687+ . expect ( "we should be able to deserialize the document" ) ;
4688+ let first_name = document
4689+ . get ( "firstName" )
4690+ . expect ( "we should be able to get the first name" )
4691+ . as_text ( )
4692+ . expect ( "the first name should be a string" )
4693+ . to_string ( ) ;
4694+ let last_name = document
4695+ . get ( "lastName" )
4696+ . expect ( "we should be able to get the last name" )
4697+ . as_text ( )
4698+ . expect ( "the last name should be a string" )
4699+ . to_string ( ) ;
4700+ ( first_name, last_name)
4701+ } )
4702+ . collect ( ) ;
4703+ let expected: Vec < ( String , String ) > = expected
4704+ . into_iter ( )
4705+ . map ( |( first, last) | ( first. to_string ( ) , last. to_string ( ) ) )
4706+ . collect ( ) ;
4707+
4708+ assert_eq ! ( names, expected) ;
4709+
4710+ let ( proof_root_hash, proof_results, _) = query
4711+ . execute_with_proof_only_get_elements ( & drive, None , None , protocol_v13)
4712+ . expect ( "we should be able to a proof" ) ;
4713+ assert_eq ! ( root_hash, proof_root_hash) ;
4714+ assert_eq ! ( results, proof_results) ;
4715+ } ;
4716+
4717+ let encoded_id = |seed : u8 | bs58:: encode ( vec ! [ seed; 32 ] ) . into_string ( ) ;
4718+
4719+ // v0 bakes the cursor's lastName ("Moore") into every branch's
4720+ // default subquery, dropping (Ben, Barber) and (Cara, Abbott).
4721+ assert_v13_query_returns (
4722+ & json ! ( {
4723+ "where" : [
4724+ [ "firstName" , "in" , [ "Adam" , "Ben" , "Cara" ] ] ,
4725+ ] ,
4726+ "startAfter" : encoded_id( 2 ) , // Adam Moore
4727+ "limit" : 100 ,
4728+ "orderBy" : [
4729+ [ "firstName" , "asc" ] ,
4730+ [ "lastName" , "asc" ]
4731+ ]
4732+ } ) ,
4733+ vec ! [ ( "Adam" , "Sinclair" ) , ( "Ben" , "Nichols" ) , ( "Cara" , "Zane" ) ] ,
4734+ ) ;
4735+
4736+ // Cursor in a middle branch: v0 wrongly includes (Adam, Sinclair),
4737+ // which is ordered before the cursor, and drops (Cara, Abbott).
4738+ assert_v13_query_returns (
4739+ & json ! ( {
4740+ "where" : [
4741+ [ "firstName" , "in" , [ "Adam" , "Ben" , "Cara" ] ] ,
4742+ ] ,
4743+ "startAfter" : encoded_id( 5 ) , // Ben Nichols
4744+ "limit" : 100 ,
4745+ "orderBy" : [
4746+ [ "firstName" , "asc" ] ,
4747+ [ "lastName" , "asc" ]
4748+ ]
4749+ } ) ,
4750+ vec ! [ ( "Adam" , "Sinclair" ) , ( "Cara" , "Zane" ) ] ,
4751+ ) ;
4752+
4753+ // Descending cursor: v0 keeps only earlier-branch values below the
4754+ // cursor's lastName and drops the entire Adam branch.
4755+ assert_v13_query_returns (
4756+ & json ! ( {
4757+ "where" : [
4758+ [ "firstName" , "in" , [ "Adam" , "Ben" , "Cara" ] ] ,
4759+ ] ,
4760+ "startAfter" : encoded_id( 4 ) , // Ben Barber
4761+ "limit" : 100 ,
4762+ "orderBy" : [
4763+ [ "firstName" , "desc" ] ,
4764+ [ "lastName" , "desc" ]
4765+ ]
4766+ } ) ,
4767+ vec ! [ ( "Cara" , "Abbott" ) ] ,
4768+ ) ;
4769+
4770+ // Cursorless descending page: v0 iterates branches descending but
4771+ // each branch's lastNames ascending (index direction).
4772+ assert_v13_query_returns (
4773+ & json ! ( {
4774+ "where" : [
4775+ [ "firstName" , "in" , [ "Adam" , "Ben" , "Cara" ] ] ,
4776+ ] ,
4777+ "limit" : 100 ,
4778+ "orderBy" : [
4779+ [ "firstName" , "desc" ] ,
4780+ [ "lastName" , "desc" ]
4781+ ]
4782+ } ) ,
4783+ vec ! [
4784+ ( "Cara" , "Abbott" ) ,
4785+ ( "Cara" , "Zane" ) ,
4786+ ( "Ben" , "Barber" ) ,
4787+ ( "Ben" , "Nichols" ) ,
4788+ ( "Adam" , "Kline" ) ,
4789+ ( "Adam" , "Moore" ) ,
4790+ ( "Adam" , "Sinclair" ) ,
4791+ ] ,
4792+ ) ;
4793+ }
4794+
46114795 #[ cfg( feature = "server" ) ]
46124796 #[ test]
46134797 fn test_family_sql_query ( ) {
0 commit comments