Skip to content

Commit cb1146e

Browse files
test(drive): freeze v0 cursor lowering outputs at protocol v13
Protocol versions <= 13 are on chain, so the pre-v14 (defective) cursor lowering must replay byte-for-byte. Pin its outputs for the same scenarios the v14 fix corrects, on both the execution and proof paths, and assert protocol v13 selects method version 0. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 5ab7e69 commit cb1146e

1 file changed

Lines changed: 184 additions & 0 deletions

File tree

packages/rs-drive/tests/query_tests.rs

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)