node: e2e test for canonical ref update

Adds a test to ensure that canonical reference updates are received on the node
events stream.

Added a helper to `NodeHandle` for perfoming a commit to a reference and signing
the nodes references, to help with this use case.
This commit is contained in:
Fintan Halpenny 2025-08-20 09:49:07 +01:00
parent 0d96af5d07
commit 7d2f0e387c
2 changed files with 106 additions and 0 deletions

View File

@ -352,6 +352,72 @@ impl<G: Signer<Signature> + cyphernet::Ecdh> NodeHandle<G> {
.unwrap()
.id()
}
/// Perform a commit to `refname` by generating a blob of random data to a
/// random path in a new tree.
///
/// If the reference does not exist, a new one will be created with the new
/// commit as its target.
///
/// If the reference already exists, then its target is used as the parent
/// of the new commit, and the reference will be updated.
///
/// The `rad/sigrefs` are then updated to reflect the new change.
pub fn commit_to(&self, rid: RepoId, refname: impl AsRef<git::RefStr>) {
use radicle::test::arbitrary;
let refname = refname.as_ref();
let repo = self.storage.repository(rid).unwrap();
let raw = &repo.backend;
let info = self.storage.info();
let author = git::raw::Signature::now(&info.name(), &info.email()).unwrap();
let tree = {
let mut tb = raw.treebuilder(None).unwrap();
let blob = raw.blob(&arbitrary::vec::<u8>(100)).unwrap();
tb.insert(
arbitrary::alphanumeric(10),
blob,
git::raw::FileMode::Blob.into(),
)
.unwrap();
let oid = tb.write().unwrap();
raw.find_tree(oid).unwrap()
};
let parent = {
let target = raw
.find_reference(refname.as_str())
.ok()
.and_then(|r| r.target());
target.and_then(|oid| raw.find_commit(oid).ok())
};
match parent {
None => repo
.backend
.commit(
Some(refname.as_str()),
&author,
&author,
"New commit",
&tree,
&[],
)
.unwrap(),
Some(parent) => repo
.backend
.commit(
Some(refname.as_str()),
&author,
&author,
"New commit",
&tree,
&[&parent],
)
.unwrap(),
};
repo.sign_refs(&self.signer).unwrap();
}
}
impl Node<MockSigner> {

View File

@ -1504,3 +1504,43 @@ fn test_channel_reader_limit() {
"actual: {reason}"
);
}
#[test]
fn test_fetch_emits_canonical_ref_update() {
let tmp = tempfile::tempdir().unwrap();
let scale = config::scale();
let mut alice = Node::init(tmp.path(), config::relay("alice"));
let bob = Node::init(tmp.path(), config::relay("bob"));
let (repo, _) = fixtures::repository(tmp.path());
fixtures::populate(&repo, scale.max(3));
let rid = alice.project_from("acme", "", &repo);
let mut alice = alice.spawn();
let mut bob = bob.spawn();
let bob_events = bob.handle.events();
bob.handle.seed(rid, Scope::All).unwrap();
alice.connect(&bob);
let result = bob.handle.fetch(rid, alice.id, DEFAULT_TIMEOUT).unwrap();
assert!(result.is_success());
let default_branch: git::Qualified = {
let repo = alice.storage.repository(rid).unwrap();
let proj = repo.project().unwrap();
git::lit::refs_heads(proj.default_branch()).into()
};
alice.commit_to(rid, &default_branch);
bob_events
.wait(
|e| {
matches!(e, Event::CanonicalRefUpdated { refname, .. } if *refname == default_branch)
.then_some(())
},
time::Duration::from_secs(9 * scale as u64),
)
.unwrap();
}