cli/tests: Refactor cob command tests

This commit is contained in:
Adrian Duke 2026-03-03 15:42:45 +00:00 committed by Fintan Halpenny
parent 1f3dc6aea0
commit d39e485c13
2 changed files with 232 additions and 219 deletions

View File

@ -3,7 +3,6 @@ use std::path::Path;
use std::str::FromStr;
use std::{fs, net, thread, time};
use radicle::cob;
use radicle::git;
use radicle::node;
use radicle::node::address::Store as _;
@ -16,7 +15,7 @@ use radicle::node::{Address, Alias, Config, Handle as _, DEFAULT_TIMEOUT};
use radicle::prelude::RepoId;
use radicle::profile;
use radicle::profile::Home;
use radicle::storage::{ReadStorage, RefUpdate, RemoteRepository};
use radicle::storage::{ReadStorage, RemoteRepository};
use radicle::test::fixtures;
use radicle_localtime::LocalTime;
@ -32,6 +31,7 @@ use util::formula::formula;
mod commands {
mod checkout;
mod clone;
mod cob;
}
/// Run a CLI test file.
@ -130,101 +130,6 @@ fn rad_issue_list() {
Environment::alice(["rad-init", "rad-issue", "rad-issue-list"]);
}
#[test]
fn rad_cob_update() {
Environment::alice(["rad-init", "rad-cob-log"]);
}
#[test]
fn rad_cob_update_identity() {
let mut environment = Environment::new();
let profile = environment.profile("alice");
let working = environment.tempdir().join("working");
let home = &profile.home;
let base = Path::new(env!("CARGO_MANIFEST_DIR"));
std::fs::create_dir_all(base).unwrap();
std::fs::create_dir_all(working.clone()).unwrap();
// Setup a test repository.
fixtures::repository(&working);
test("examples/rad-init.md", &working, Some(home), []).unwrap();
test(
"examples/rad-cob-update-identity.md",
&working,
Some(home),
[],
)
.unwrap();
}
#[test]
fn rad_cob_multiset() {
// `rad-cob-multiset` is a `jq` script, which requires `jq` to be installed.
// We test whether `jq` is installed, and have this test succeed if it is not.
// Programmatic skipping of tests is not supported as of 2024-08.
if !program_reports_version("jq") {
return;
}
let mut environment = Environment::new();
let profile = environment.profile("alice");
let home = &profile.home;
let working = environment.tempdir().join("working");
let base = Path::new(env!("CARGO_MANIFEST_DIR"));
std::fs::create_dir_all(base).unwrap();
std::fs::create_dir_all(working.clone()).unwrap();
// Copy over the script that implements the multiset COB.
std::fs::copy(
base.join("examples").join("rad-cob-multiset"),
working.join("rad-cob-multiset"),
)
.unwrap();
// Setup a test repository.
fixtures::repository(&working);
test("examples/rad-init.md", &working, Some(home), []).unwrap();
test("examples/rad-cob-multiset.md", &working, Some(home), []).unwrap();
}
#[test]
fn rad_cob_log() {
Environment::alice(["rad-init", "rad-cob-log"]);
}
#[test]
fn rad_cob_show() {
Environment::alice(["rad-init", "rad-cob-show"]);
}
#[test]
fn rad_cob_migrate() {
let mut environment = Environment::new();
let profile = environment.profile("alice");
let home = &profile.home;
home.cobs_db_mut()
.unwrap()
.raw_query(|conn| conn.execute("PRAGMA user_version = 0"))
.unwrap();
environment.repository(&profile);
environment
.tests(["rad-init", "rad-cob-migrate"], &profile)
.unwrap();
}
#[test]
fn rad_cob_operations() {
Environment::alice(["rad-init", "rad-cob-operations"]);
}
#[test]
#[ignore = "part of many other tests"]
fn rad_init() {
@ -1463,128 +1368,6 @@ fn rad_diff() {
test("examples/rad-diff.md", tmp, None, []).unwrap();
}
#[test]
fn test_cob_replication() {
let mut environment = Environment::new();
let working = tempfile::tempdir().unwrap();
let mut alice = environment.node("alice");
let bob = environment.node("bob");
let rid = alice.project("heartwood", "");
let mut alice = alice.spawn();
let mut bob = bob.spawn();
let events = alice.handle.events();
alice.handle.follow(bob.id, None).unwrap();
alice.connect(&bob);
bob.routes_to(&[(rid, alice.id)]);
bob.fork(rid, working.path()).unwrap();
// Wait for Alice to fetch the clone refs.
events
.wait(
|e| {
matches!(
e,
Event::RefsFetched { updated, .. }
if updated.iter().any(|u| matches!(u, RefUpdate::Created { .. }))
)
.then_some(())
},
time::Duration::from_secs(6),
)
.unwrap();
let bob_repo = bob.storage.repository(rid).unwrap();
let mut bob_issues = radicle::cob::issue::Issues::open(&bob_repo).unwrap();
let mut bob_cache = radicle::cob::cache::InMemory::default();
let issue = bob_issues
.create(
cob::Title::new("Something's fishy").unwrap(),
"I don't know what it is",
&[],
&[],
[],
&mut bob_cache,
&bob.signer,
)
.unwrap();
log::debug!(target: "test", "Issue {} created", issue.id());
// Make sure that Bob's issue refs announcement has a different timestamp than his fork's
// announcement, otherwise Alice will consider it stale.
thread::sleep(time::Duration::from_millis(3));
bob.handle.announce_refs_for(rid, [bob.id]).unwrap();
// Wait for Alice to fetch the issue refs.
events
.iter()
.find(|e| matches!(e, Event::RefsFetched { .. }))
.unwrap();
let alice_repo = alice.storage.repository(rid).unwrap();
let alice_issues = radicle::cob::issue::Issues::open(&alice_repo).unwrap();
let alice_issue = alice_issues.get(issue.id()).unwrap().unwrap();
assert_eq!(alice_issue.title(), "Something's fishy");
}
#[test]
fn test_cob_deletion() {
let mut environment = Environment::new();
let working = tempfile::tempdir().unwrap();
let mut alice = environment.node("alice");
let bob = environment.node("bob");
let rid = alice.project("heartwood", "");
let mut alice = alice.spawn();
let mut bob = bob.spawn();
alice.handle.seed(rid, Scope::All).unwrap();
bob.handle.seed(rid, Scope::All).unwrap();
alice.connect(&bob);
bob.routes_to(&[(rid, alice.id)]);
let alice_repo = alice.storage.repository(rid).unwrap();
let mut alice_issues = radicle::cob::issue::Cache::no_cache(&alice_repo).unwrap();
let issue = alice_issues
.create(
cob::Title::new("Something's fishy").unwrap(),
"I don't know what it is",
&[],
&[],
[],
&alice.signer,
)
.unwrap();
let issue_id = issue.id();
log::debug!(target: "test", "Issue {issue_id} created");
bob.rad("clone", &[rid.to_string().as_str()], working.path())
.unwrap();
let bob_repo = bob.storage.repository(rid).unwrap();
let bob_issues = radicle::cob::issue::Issues::open(&bob_repo).unwrap();
assert!(bob_issues.get(issue_id).unwrap().is_some());
let mut alice_issues = radicle::cob::issue::Cache::no_cache(&alice_repo).unwrap();
alice_issues.remove(issue_id, &alice.signer).unwrap();
log::debug!(target: "test", "Removing issue..");
radicle::assert_matches!(
bob.handle.fetch(rid, alice.id, DEFAULT_TIMEOUT).unwrap(),
radicle::node::FetchResult::Success { .. }
);
let bob_repo = bob.storage.repository(rid).unwrap();
let bob_issues = radicle::cob::issue::Issues::open(&bob_repo).unwrap();
assert!(bob_issues.get(issue_id).unwrap().is_none());
}
#[test]
fn rad_sync() {
let mut environment = Environment::new();

View File

@ -0,0 +1,230 @@
use std::path::Path;
use crate::util::environment::Environment;
use crate::{program_reports_version, test};
use radicle::node::Handle;
use radicle::test::fixtures;
#[test]
fn rad_cob_update() {
Environment::alice(["rad-init", "rad-cob-log"]);
}
#[test]
fn rad_cob_update_identity() {
let mut environment = Environment::new();
let profile = environment.profile("alice");
let working = environment.tempdir().join("working");
let home = &profile.home;
let base = Path::new(env!("CARGO_MANIFEST_DIR"));
std::fs::create_dir_all(base).unwrap();
std::fs::create_dir_all(working.clone()).unwrap();
// Setup a test repository.
fixtures::repository(&working);
test("examples/rad-init.md", &working, Some(home), []).unwrap();
test(
"examples/rad-cob-update-identity.md",
&working,
Some(home),
[],
)
.unwrap();
}
#[test]
fn rad_cob_multiset() {
// `rad-cob-multiset` is a `jq` script, which requires `jq` to be installed.
// We test whether `jq` is installed, and have this test succeed if it is not.
// Programmatic skipping of tests is not supported as of 2024-08.
if !program_reports_version("jq") {
return;
}
let mut environment = Environment::new();
let profile = environment.profile("alice");
let home = &profile.home;
let working = environment.tempdir().join("working");
let base = Path::new(env!("CARGO_MANIFEST_DIR"));
std::fs::create_dir_all(base).unwrap();
std::fs::create_dir_all(working.clone()).unwrap();
// Copy over the script that implements the multiset COB.
std::fs::copy(
base.join("examples").join("rad-cob-multiset"),
working.join("rad-cob-multiset"),
)
.unwrap();
// Setup a test repository.
fixtures::repository(&working);
test("examples/rad-init.md", &working, Some(home), []).unwrap();
test("examples/rad-cob-multiset.md", &working, Some(home), []).unwrap();
}
#[test]
fn rad_cob_log() {
Environment::alice(["rad-init", "rad-cob-log"]);
}
#[test]
fn rad_cob_show() {
Environment::alice(["rad-init", "rad-cob-show"]);
}
#[test]
fn rad_cob_migrate() {
let mut environment = Environment::new();
let profile = environment.profile("alice");
let home = &profile.home;
home.cobs_db_mut()
.unwrap()
.raw_query(|conn| conn.execute("PRAGMA user_version = 0"))
.unwrap();
environment.repository(&profile);
environment
.tests(["rad-init", "rad-cob-migrate"], &profile)
.unwrap();
}
#[test]
fn rad_cob_operations() {
Environment::alice(["rad-init", "rad-cob-operations"]);
}
#[test]
fn test_cob_replication() {
let mut environment = Environment::new();
let working = tempfile::tempdir().unwrap();
let mut alice = environment.node("alice");
let bob = environment.node("bob");
let rid = alice.project("heartwood", "");
let mut alice = alice.spawn();
let mut bob = bob.spawn();
let events = alice.handle.events();
alice.handle.follow(bob.id, None).unwrap();
alice.connect(&bob);
bob.routes_to(&[(rid, alice.id)]);
bob.fork(rid, working.path()).unwrap();
// Wait for Alice to fetch the clone refs.
events
.wait(
|e| {
matches!(
e,
radicle::node::Event::RefsFetched { updated, .. }
if updated.iter().any(|u| matches!(u, radicle::storage::RefUpdate::Created { .. }))
)
.then_some(())
},
std::time::Duration::from_secs(6),
)
.unwrap();
let bob_repo = radicle::storage::ReadStorage::repository(&bob.storage, rid).unwrap();
let mut bob_issues = radicle::cob::issue::Issues::open(&bob_repo).unwrap();
let mut bob_cache = radicle::cob::cache::InMemory::default();
let issue = bob_issues
.create(
radicle::cob::Title::new("Something's fishy").unwrap(),
"I don't know what it is",
&[],
&[],
[],
&mut bob_cache,
&bob.signer,
)
.unwrap();
log::debug!(target: "test", "Issue {} created", issue.id());
// Make sure that Bob's issue refs announcement has a different timestamp than his fork's
// announcement, otherwise Alice will consider it stale.
std::thread::sleep(std::time::Duration::from_millis(3));
bob.handle.announce_refs_for(rid, [bob.id]).unwrap();
// Wait for Alice to fetch the issue refs.
events
.iter()
.find(|e| matches!(e, radicle::node::Event::RefsFetched { .. }))
.unwrap();
let alice_repo = radicle::storage::ReadStorage::repository(&alice.storage, rid).unwrap();
let alice_issues = radicle::cob::issue::Issues::open(&alice_repo).unwrap();
let alice_issue = alice_issues.get(issue.id()).unwrap().unwrap();
assert_eq!(alice_issue.title(), "Something's fishy");
}
#[test]
fn test_cob_deletion() {
let mut environment = Environment::new();
let working = tempfile::tempdir().unwrap();
let mut alice = environment.node("alice");
let bob = environment.node("bob");
let rid = alice.project("heartwood", "");
let mut alice = alice.spawn();
let mut bob = bob.spawn();
alice
.handle
.seed(rid, radicle::node::policy::Scope::All)
.unwrap();
bob.handle
.seed(rid, radicle::node::policy::Scope::All)
.unwrap();
alice.connect(&bob);
bob.routes_to(&[(rid, alice.id)]);
let alice_repo = radicle::storage::ReadStorage::repository(&alice.storage, rid).unwrap();
let mut alice_issues = radicle::cob::issue::Cache::no_cache(&alice_repo).unwrap();
let issue = alice_issues
.create(
radicle::cob::Title::new("Something's fishy").unwrap(),
"I don't know what it is",
&[],
&[],
[],
&alice.signer,
)
.unwrap();
let issue_id = issue.id();
log::debug!(target: "test", "Issue {issue_id} created");
bob.rad("clone", &[rid.to_string().as_str()], working.path())
.unwrap();
let bob_repo = radicle::storage::ReadStorage::repository(&bob.storage, rid).unwrap();
let bob_issues = radicle::cob::issue::Issues::open(&bob_repo).unwrap();
assert!(bob_issues.get(issue_id).unwrap().is_some());
let mut alice_issues = radicle::cob::issue::Cache::no_cache(&alice_repo).unwrap();
alice_issues.remove(issue_id, &alice.signer).unwrap();
log::debug!(target: "test", "Removing issue..");
radicle::assert_matches!(
bob.handle
.fetch(rid, alice.id, radicle::node::DEFAULT_TIMEOUT)
.unwrap(),
radicle::node::FetchResult::Success { .. }
);
let bob_repo = radicle::storage::ReadStorage::repository(&bob.storage, rid).unwrap();
let bob_issues = radicle::cob::issue::Issues::open(&bob_repo).unwrap();
assert!(bob_issues.get(issue_id).unwrap().is_none());
}