From a670b6e668761ea5893b7d343b4515b888712f14 Mon Sep 17 00:00:00 2001 From: Fintan Halpenny Date: Wed, 20 Aug 2025 08:03:31 +0100 Subject: [PATCH] radicle: fix stream tests The stream tests did not take into account that the tips of previous COB entries need to be inserted so that a Git history can be formed. This was surfaced by the added regression test. --- crates/radicle/src/cob/stream.rs | 118 +++++++++++++++++++++++++------ 1 file changed, 95 insertions(+), 23 deletions(-) diff --git a/crates/radicle/src/cob/stream.rs b/crates/radicle/src/cob/stream.rs index 07721892..2e0a4345 100644 --- a/crates/radicle/src/cob/stream.rs +++ b/crates/radicle/src/cob/stream.rs @@ -190,35 +190,49 @@ mod tests { let n = gen::(1).clamp(1, 10); let mut entries = Vec::with_capacity(n.into()); + let mut parent = None; for _ in 0..n { - // Number of actions in this bop + // Number of actions in this op let m = gen::(1).clamp(1, 3); - let contents = NonEmpty::collect((0..m).map(|_| { - json::to_vec(&json!({ - "test": arbitrary::alphanumeric(1), - })) - .unwrap() - })) - .unwrap(); - let entry = repo - .store( - None, - vec![], - signer, - cob::change::Template { - type_name: typename(), - tips: vec![], - message: "Test Op Stream".to_string(), - embeds: vec![], - contents, - }, - ) - .unwrap(); + let contents = create_contents((0..m).map(|_| arbitrary::alphanumeric(1))); + let entry = create_entry(repo, signer, contents, parent); + parent = Some(entry.id); entries.push(entry); } entries } + fn create_contents(values: impl Iterator) -> NonEmpty> { + NonEmpty::collect(values.map(|value| { + json::to_vec(&json!({ + "test": value, + })) + .unwrap() + })) + .unwrap() + } + + fn create_entry( + repo: &git2::Repository, + signer: &MockSigner, + contents: NonEmpty>, + parent: Option, + ) -> cob::Entry { + repo.store( + None, + parent.into_iter().collect(), + signer, + cob::change::Template { + type_name: typename(), + tips: vec![], + message: "Test Op Stream".to_string(), + embeds: vec![], + contents, + }, + ) + .unwrap() + } + /// all === from(root) fn prop_all_from(stream: &S) where @@ -309,7 +323,7 @@ mod tests { .cloned() .collect::>(), from_until_s, - "from: {from_s:?}\nuntil: {until_s:?}" + "\nfrom_until: {from_until_s:?}\nfrom: {from_s:?}\nuntil: {until_s:?}" ) } @@ -379,4 +393,62 @@ mod tests { let stream = Stream::::new(&repo, history, typename()); prop_from_until(&stream, from, until) } + + #[test] + fn test_regression_from_until() { + let tmp = tempfile::tempdir().unwrap(); + let (repo, _) = test::fixtures::repository(tmp.path()); + let signer = MockSigner::default(); + // Set up 3 entries that make up the COB history + let op1 = create_entry( + &repo, + &signer, + create_contents(std::iter::once("hello".to_string())), + None, + ); + let op2 = create_entry( + &repo, + &signer, + create_contents(std::iter::once("radicle".to_string())), + Some(op1.id), + ); + let op3 = create_entry( + &repo, + &signer, + create_contents(std::iter::once("world".to_string())), + Some(op2.id), + ); + + // The history spans from the 1st op to the last + let history = CobRange { + root: op1.id, + until: op3.id.into(), + }; + let stream = Stream::::new(&repo, history, typename()); + eprintln!("Op 1: {}", op1.id); + eprintln!("Op 2: {}", op2.id); + eprintln!("Op 3: {}", op3.id); + + // "since" the root operation should include all operations + assert_eq!( + stream + .since(op1.id) + .unwrap() + .map(|op| op.unwrap().id) + .collect::>(), + [op1.id, op2.id, op3.id] + .into_iter() + .collect::>() + ); + // "until" the second operation should only include up to the second operation + assert_eq!( + stream + .until(op2.id) + .unwrap() + .map(|op| op.unwrap().id) + .collect::>(), + [op1.id, op2.id].into_iter().collect::>() + ); + prop_from_until(&stream, op1.id, op2.id); + } }