From 45e6afd0101f109637a071d0c1bd0c6e97cb9e48 Mon Sep 17 00:00:00 2001 From: Fintan Halpenny Date: Mon, 9 Mar 2026 16:20:19 +0000 Subject: [PATCH] cli/tests: Refactor utility command tests Place utility command tests in their own module. These commands relate to inspection of state, such as: `rad self`, `rad config`, `rad inspect`, etc. --- crates/radicle-cli/tests/commands.rs | 206 +++-------------- crates/radicle-cli/tests/commands/utility.rs | 226 +++++++++++++++++++ new.txt | 111 +++++++++ old.txt | 112 +++++++++ 4 files changed, 484 insertions(+), 171 deletions(-) create mode 100644 crates/radicle-cli/tests/commands/utility.rs create mode 100644 new.txt create mode 100644 old.txt diff --git a/crates/radicle-cli/tests/commands.rs b/crates/radicle-cli/tests/commands.rs index 5aca6317..b977dc77 100644 --- a/crates/radicle-cli/tests/commands.rs +++ b/crates/radicle-cli/tests/commands.rs @@ -1,15 +1,10 @@ use core::panic; -use std::fs; use std::path::Path; use std::str::FromStr; -use radicle::node::config::seeds::RADICLE_NODE_BOOTSTRAP_IRIS; -use radicle::node::policy::Scope; -use radicle::node::{Alias, Config, Handle as _, DEFAULT_TIMEOUT}; +use radicle::node::{Alias, Handle as _}; use radicle::prelude::RepoId; -use radicle::profile; use radicle::profile::Home; -use radicle::test::fixtures; #[allow(unused_imports)] use radicle_node::test::logger; @@ -33,6 +28,7 @@ mod commands { mod policy; mod remote; mod sync; + mod utility; } /// Run a CLI test file. @@ -93,94 +89,20 @@ fn program_reports_version(program: &str) -> bool { } #[test] -fn rad_help() { - Environment::alice(["rad-help"]); -} - -#[test] -fn rad_auth() { - test("examples/rad-auth.md", Path::new("."), None, []).unwrap(); -} - -#[test] -fn rad_key_mismatch() { +fn rad_remote() { let mut environment = Environment::new(); - let alice = environment.profile("alice"); + let alice = environment.relay("alice"); + let bob = environment.relay("bob"); + let eve = environment.relay("eve"); + let home = alice.home.clone(); + let rid = RepoId::from_str("z42hL2jL4XNk6K8oHQaSWfMgCL7ji").unwrap(); + // Setup a test repository. environment.repository(&alice); - environment.test("rad-init", &alice).unwrap(); - - // Replace the public key with one that does not match the secret key anymore. - fs::write(alice.home.path().join("keys").join("radicle.pub"), "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIE6Ul/D+P0I/Hl1JVOWGS8Z589us9FqKQXWv8OMOpKCh snakeoil\n").unwrap(); - - environment.test("rad-key-mismatch", &alice).unwrap(); -} - -#[test] -fn rad_auth_errors() { - test("examples/rad-auth-errors.md", Path::new("."), None, []).unwrap(); -} - -#[test] -fn rad_inspect() { - let mut environment = Environment::new(); - let profile = environment.profile("alice"); - - environment.repository(&profile); - - environment - .tests(["rad-init", "rad-inspect"], &profile) - .unwrap(); - - // NOTE: The next test runs without $RAD_HOME set. - test( - "examples/rad-inspect-noauth.md", - environment.work(&profile), - None, - [], - ) - .unwrap(); -} - -#[test] -fn rad_config() { - let mut environment = Environment::new(); - let alias = Alias::new("alice"); - let profile = environment.profile_with(profile::Config { - preferred_seeds: vec![RADICLE_NODE_BOOTSTRAP_IRIS.clone().first().unwrap().clone()], - ..profile::Config::new(alias) - }); - let working = tempfile::tempdir().unwrap(); - - test( - "examples/rad-config.md", - working.path(), - Some(&profile.home), - [], - ) - .unwrap(); -} - -#[test] -fn rad_warn_old_nodes() { - Environment::alice(["rad-warn-old-nodes"]); -} - -#[test] -fn rad_clean() { - let mut environment = Environment::new(); - let alice = environment.node("alice"); - let bob = environment.node("bob"); - let eve = environment.node("eve"); - let working = environment.tempdir().join("working"); - - // Setup a test project. - let acme = RepoId::from_str("z42hL2jL4XNk6K8oHQaSWfMgCL7ji").unwrap(); - fixtures::repository(working.join("acme")); test( "examples/rad-init.md", - working.join("acme"), - Some(&alice.home), + environment.work(&alice), + Some(&home), [], ) .unwrap(); @@ -188,90 +110,32 @@ fn rad_clean() { let mut alice = alice.spawn(); let mut bob = bob.spawn(); let mut eve = eve.spawn(); - alice.handle.seed(acme, Scope::All).unwrap(); - eve.handle.seed(acme, Scope::Followed).unwrap(); - - bob.connect(&alice).converge([&alice]); - eve.connect(&alice).converge([&alice]); - - eve.handle.fetch(acme, alice.id, DEFAULT_TIMEOUT).unwrap(); - - bob.fork(acme, bob.home.path()).unwrap(); - bob.announce(acme, 1, bob.home.path()).unwrap(); - bob.has_remote_of(&acme, &alice.id); - alice.has_remote_of(&acme, &bob.id); - eve.has_remote_of(&acme, &alice.id); - - formula(&environment.tempdir(), "examples/rad-clean.md") - .unwrap() - .home( - "alice", - working.join("acme"), - [("RAD_HOME", alice.home.path().display())], - ) - .home( - "bob", - working.join("bob"), - [("RAD_HOME", bob.home.path().display())], - ) - .home( - "eve", - working.join("eve"), - [("RAD_HOME", eve.home.path().display())], - ) - .run() + alice + .handle + .follow(bob.id, Some(Alias::new("bob"))) .unwrap(); -} - -#[test] -fn rad_self() { - let mut environment = Environment::new(); - let alice = environment.node_with(Config { - external_addresses: vec!["seed.alice.acme:8776".parse().unwrap()], - ..Config::test(Alias::new("alice")) - }); - let working = environment.tempdir().join("working"); - - test("examples/rad-self.md", working, Some(&alice.home), []).unwrap(); -} - -#[cfg(unix)] -#[test] -fn rad_diff() { - if std::env::consts::OS == "macos" { - // macOS's `sed` requires an argument for `-i`, which we don't provide - // in the example. Providing it makes the test fail on Linux. - // Since this command is deprecated anyway, we just skip macOS. - return; - } - - let tmp = tempfile::tempdir().unwrap(); - - fixtures::repository(&tmp); - - test("examples/rad-diff.md", tmp, None, []).unwrap(); -} - -#[test] -fn framework_home() { - let mut environment = Environment::new(); - let alice = environment.node("alice"); - let bob = environment.node("bob"); - - formula(&environment.tempdir(), "examples/framework/home.md") - .unwrap() - .home( - "alice", - alice.home.path(), - [("RAD_HOME", alice.home.path().display())], - ) - .home( - "bob", - bob.home.path(), - [("RAD_HOME", bob.home.path().display())], - ) - .run() + alice + .handle + .follow(eve.id, Some(Alias::new("eve"))) .unwrap(); + + bob.connect(&alice); + bob.routes_to(&[(rid, alice.id)]); + bob.fork(rid, bob.home.path()).unwrap(); + alice.has_remote_of(&rid, &bob.id); + + eve.connect(&alice); + eve.routes_to(&[(rid, alice.id)]); + eve.fork(rid, eve.home.path()).unwrap(); + alice.has_remote_of(&rid, &eve.id); + + test( + "examples/rad-remote.md", + environment.work(&alice), + Some(&home), + [], + ) + .unwrap(); } #[test] diff --git a/crates/radicle-cli/tests/commands/utility.rs b/crates/radicle-cli/tests/commands/utility.rs new file mode 100644 index 00000000..66040c3f --- /dev/null +++ b/crates/radicle-cli/tests/commands/utility.rs @@ -0,0 +1,226 @@ +use std::str::FromStr as _; + +use radicle::node::policy::Scope; +use radicle::node::DEFAULT_TIMEOUT; +use radicle::node::{Alias, Handle as _}; +use radicle::prelude::RepoId; +use radicle::profile; + +use crate::test; +use crate::util::environment::Environment; +use crate::util::formula::formula; + +#[test] +fn rad_inspect() { + let mut environment = Environment::new(); + let profile = environment.profile("alice"); + + environment.repository(&profile); + + environment + .tests(["rad-init", "rad-inspect"], &profile) + .unwrap(); + + // NOTE: The next test runs without $RAD_HOME set. + test( + "examples/rad-inspect-noauth.md", + environment.work(&profile), + None, + [], + ) + .unwrap(); +} + +#[test] +fn rad_config() { + let mut environment = Environment::new(); + let alias = Alias::new("alice"); + let profile = environment.profile_with(profile::Config { + preferred_seeds: vec![radicle::node::config::seeds::RADICLE_NODE_BOOTSTRAP_IRIS + .clone() + .first() + .unwrap() + .clone()], + ..profile::Config::new(alias) + }); + let working = tempfile::tempdir().unwrap(); + + test( + "examples/rad-config.md", + working.path(), + Some(&profile.home), + [], + ) + .unwrap(); +} + +#[test] +fn rad_warn_old_nodes() { + Environment::alice(["rad-warn-old-nodes"]); +} + +#[test] +fn rad_clean() { + let mut environment = Environment::new(); + let alice = environment.node("alice"); + let bob = environment.node("bob"); + let eve = environment.node("eve"); + let working = environment.tempdir().join("working"); + + // Setup a test project. + let acme = RepoId::from_str("z42hL2jL4XNk6K8oHQaSWfMgCL7ji").unwrap(); + radicle::test::fixtures::repository(working.join("acme")); + test( + "examples/rad-init.md", + working.join("acme"), + Some(&alice.home), + [], + ) + .unwrap(); + + let mut alice = alice.spawn(); + let mut bob = bob.spawn(); + let mut eve = eve.spawn(); + alice.handle.seed(acme, Scope::All).unwrap(); + eve.handle.seed(acme, Scope::Followed).unwrap(); + + bob.connect(&alice).converge([&alice]); + eve.connect(&alice).converge([&alice]); + + eve.handle.fetch(acme, alice.id, DEFAULT_TIMEOUT).unwrap(); + + bob.fork(acme, bob.home.path()).unwrap(); + bob.announce(acme, 1, bob.home.path()).unwrap(); + bob.has_remote_of(&acme, &alice.id); + alice.has_remote_of(&acme, &bob.id); + eve.has_remote_of(&acme, &alice.id); + + formula(&environment.tempdir(), "examples/rad-clean.md") + .unwrap() + .home( + "alice", + working.join("acme"), + [("RAD_HOME", alice.home.path().display())], + ) + .home( + "bob", + working.join("bob"), + [("RAD_HOME", bob.home.path().display())], + ) + .home( + "eve", + working.join("eve"), + [("RAD_HOME", eve.home.path().display())], + ) + .run() + .unwrap(); +} + +#[test] +fn rad_self() { + let mut environment = Environment::new(); + let alice = environment.node_with(radicle::node::Config { + external_addresses: vec!["seed.alice.acme:8776".parse().unwrap()], + ..radicle::node::Config::test(Alias::new("alice")) + }); + let working = environment.tempdir().join("working"); + + test("examples/rad-self.md", working, Some(&alice.home), []).unwrap(); +} + +#[test] +fn rad_help() { + Environment::alice(["rad-help"]); +} + +#[test] +fn rad_auth() { + test("examples/rad-auth.md", std::path::Path::new("."), None, []).unwrap(); +} + +#[test] +fn rad_key_mismatch() { + let mut environment = Environment::new(); + let alice = environment.profile("alice"); + environment.repository(&alice); + + environment.test("rad-init", &alice).unwrap(); + + // Replace the public key with one that does not match the secret key anymore. + std::fs::write(alice.home.path().join("keys").join("radicle.pub"), "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIE6Ul/D+P0I/Hl1JVOWGS8Z589us9FqKQXWv8OMOpKCh snakeoil\n").unwrap(); + + environment.test("rad-key-mismatch", &alice).unwrap(); +} + +#[test] +fn rad_auth_errors() { + test( + "examples/rad-auth-errors.md", + std::path::Path::new("."), + None, + [], + ) + .unwrap(); +} + +#[cfg(unix)] +#[test] +fn rad_diff() { + use radicle::test::fixtures; + + if std::env::consts::OS == "macos" { + // macOS's `sed` requires an argument for `-i`, which we don't provide + // in the example. Providing it makes the test fail on Linux. + // Since this command is deprecated anyway, we just skip macOS. + return; + } + + let tmp = tempfile::tempdir().unwrap(); + + fixtures::repository(&tmp); + + test("examples/rad-diff.md", tmp, None, []).unwrap(); +} + +#[test] +fn rad_fork() { + let mut environment = Environment::new(); + let alice = environment.node("alice"); + let bob = environment.node("bob"); + + let mut alice = alice.spawn(); + let bob = bob.spawn(); + + alice.connect(&bob); + environment.repository(&alice); + + // Alice initializes a repo after her node has started, and after bob has connected to it. + environment.test("rad-init-sync", &alice).unwrap(); + + // Wait for bob to get any updates to the routing table. + bob.converge([&alice]); + + environment.tests(["rad-fetch", "rad-fork"], &bob).unwrap(); +} + +#[test] +fn framework_home() { + let mut environment = Environment::new(); + let alice = environment.node("alice"); + let bob = environment.node("bob"); + + formula(&environment.tempdir(), "examples/framework/home.md") + .unwrap() + .home( + "alice", + alice.home.path(), + [("RAD_HOME", alice.home.path().display())], + ) + .home( + "bob", + bob.home.path(), + [("RAD_HOME", bob.home.path().display())], + ) + .run() + .unwrap(); +} diff --git a/new.txt b/new.txt new file mode 100644 index 00000000..0bd5342e --- /dev/null +++ b/new.txt @@ -0,0 +1,111 @@ +framework_home +git_push_amend +git_push_and_fetch +git_push_canonical_annotated_tags +git_push_canonical_lightweight_tags +git_push_converge +git_push_diverge +git_push_force_with_lease +git_push_rollback +git_tag +rad_auth +rad_auth_errors +rad_block +rad_checkout +rad_clean +rad_clone +rad_clone_all +rad_clone_bare +rad_clone_connect +rad_clone_directory +rad_clone_partial_fail +rad_clone_unknown +rad_cob_log +rad_cob_migrate +rad_cob_multiset +rad_cob_operations +rad_cob_show +rad_cob_update +rad_cob_update_identity +rad_config +rad_diff +rad_fetch +rad_help +rad_id +rad_id_collaboration +rad_id_conflict +rad_id_multi_delegate +rad_id_private +rad_id_threshold +rad_id_threshold_soft_fork +rad_id_unauthorized_delegate +rad_id_unknown_field +rad_id_update_delete_field +rad_inbox +rad_init +rad_init_bare +rad_init_detached_head +rad_init_existing +rad_init_existing_bare +rad_init_no_git +rad_init_no_seed +rad_init_private +rad_init_private_clone +rad_init_private_clone_seed +rad_init_private_no_seed +rad_init_private_seed +rad_init_sync_and_clone +rad_init_sync_not_connected +rad_init_sync_preferred +rad_init_sync_timeout +rad_init_with_existing_remote +rad_inspect +rad_issue +rad_issue_list +rad_jj_bare +rad_jj_colocated_patch +rad_key_mismatch +rad_merge_after_update +rad_merge_no_ff +rad_merge_via_push +rad_node +rad_node_connect +rad_node_connect_without_address +rad_patch +rad_patch_ahead_behind +rad_patch_change_base +rad_patch_checkout +rad_patch_checkout_force +rad_patch_checkout_revision +rad_patch_delete +rad_patch_detached_head +rad_patch_diff +rad_patch_draft +rad_patch_edit +rad_patch_fetch_1 +rad_patch_fetch_2 +rad_patch_merge_draft +rad_patch_open_explore +rad_patch_pull_update +rad_patch_revert_merge +rad_patch_update +rad_patch_via_push +rad_publish +rad_push_and_pull_patches +rad_remote +rad_review_by_hunk +rad_seed_and_follow +rad_seed_many +rad_seed_policy_allow_no_scope +rad_self +rad_sync +rad_sync_without_node +rad_unseed +rad_unseed_many +rad_warn_old_nodes +rad_watch +rad_workflow +test_clone_without_seeds +test_cob_deletion +test_cob_replication +test_replication_via_seed diff --git a/old.txt b/old.txt new file mode 100644 index 00000000..65e2d5f8 --- /dev/null +++ b/old.txt @@ -0,0 +1,112 @@ +framework_home +git_push_amend +git_push_and_fetch +git_push_canonical_annotated_tags +git_push_canonical_lightweight_tags +git_push_converge +git_push_diverge +git_push_force_with_lease +git_push_rollback +git_tag +rad_auth +rad_auth_errors +rad_block +rad_checkout +rad_clean +rad_clone +rad_clone_all +rad_clone_bare +rad_clone_connect +rad_clone_directory +rad_clone_partial_fail +rad_clone_unknown +rad_cob_log +rad_cob_migrate +rad_cob_multiset +rad_cob_operations +rad_cob_show +rad_cob_update +rad_cob_update_identity +rad_config +rad_diff +rad_fetch +rad_fork +rad_help +rad_id +rad_id_collaboration +rad_id_conflict +rad_id_multi_delegate +rad_id_private +rad_id_threshold +rad_id_threshold_soft_fork +rad_id_unauthorized_delegate +rad_id_unknown_field +rad_id_update_delete_field +rad_inbox +rad_init +rad_init_bare +rad_init_detached_head +rad_init_existing +rad_init_existing_bare +rad_init_no_git +rad_init_no_seed +rad_init_private +rad_init_private_clone +rad_init_private_clone_seed +rad_init_private_no_seed +rad_init_private_seed +rad_init_sync_and_clone +rad_init_sync_not_connected +rad_init_sync_preferred +rad_init_sync_timeout +rad_init_with_existing_remote +rad_inspect +rad_issue +rad_issue_list +rad_jj_bare +rad_jj_colocated_patch +rad_key_mismatch +rad_merge_after_update +rad_merge_no_ff +rad_merge_via_push +rad_node +rad_node_connect +rad_node_connect_without_address +rad_patch +rad_patch_ahead_behind +rad_patch_change_base +rad_patch_checkout +rad_patch_checkout_force +rad_patch_checkout_revision +rad_patch_delete +rad_patch_detached_head +rad_patch_diff +rad_patch_draft +rad_patch_edit +rad_patch_fetch_1 +rad_patch_fetch_2 +rad_patch_merge_draft +rad_patch_open_explore +rad_patch_pull_update +rad_patch_revert_merge +rad_patch_update +rad_patch_via_push +rad_publish +rad_push_and_pull_patches +rad_remote +rad_review_by_hunk +rad_seed_and_follow +rad_seed_many +rad_seed_policy_allow_no_scope +rad_self +rad_sync +rad_sync_without_node +rad_unseed +rad_unseed_many +rad_warn_old_nodes +rad_watch +rad_workflow +test_clone_without_seeds +test_cob_deletion +test_cob_replication +test_replication_via_seed