use anyhow::Result; use gitbutler_branch_actions::BranchListingFilter; #[test] fn one_vbranch_on_integration() -> Result<()> { init_env(); let list = list_branches(&project_ctx("one-vbranch-on-integration")?, None)?; assert_eq!(list.len(), 1); assert_equal( &list[0], ExpectedBranchListing { identity: "virtual".into(), virtual_branch_given_name: Some("virtual"), virtual_branch_in_workspace: true, has_local: false, ..Default::default() }, "It's a bare virtual branch with no commit", ); Ok(()) } #[test] fn one_vbranch_on_integration_one_commit() -> Result<()> { init_env(); let ctx = project_ctx("one-vbranch-on-integration-one-commit")?; let list = list_branches(&ctx, None)?; assert_eq!(list.len(), 1); assert_equal( &list[0], ExpectedBranchListing { identity: "virtual".into(), virtual_branch_given_name: Some("virtual"), virtual_branch_in_workspace: true, has_local: false, ..Default::default() }, "It's a bare virtual branch with a single commit", ); Ok(()) } #[test] fn two_vbranches_on_integration_one_commit() -> Result<()> { init_env(); let ctx = project_ctx("two-vbranches-on-integration-one-applied")?; let list = list_branches( &ctx, Some(BranchListingFilter { local: Some(true), applied: Some(true), }), )?; assert_eq!(list.len(), 1, "only one of these is applied"); assert_equal( &list[0], ExpectedBranchListing { identity: "other".into(), virtual_branch_given_name: Some("other"), virtual_branch_in_workspace: true, has_local: false, ..Default::default() }, "It's a bare virtual branch without any branches with the same identity", ); let list = list_branches( &ctx, Some(BranchListingFilter { local: Some(true), applied: Some(false), }), )?; assert_eq!(list.len(), 1, "only one of these is *not* applied"); assert_equal( &list[0], ExpectedBranchListing { identity: "virtual".into(), virtual_branch_given_name: Some("virtual"), virtual_branch_in_workspace: false, has_local: true, ..Default::default() }, "It's a bare virtual branch without any branches with the same identity", ); Ok(()) } #[test] fn one_feature_branch_and_one_vbranch_on_integration_one_commit() -> Result<()> { init_env(); let ctx = project_ctx("a-vbranch-named-like-target-branch-short-name")?; let list = list_branches(&ctx, None)?; assert_eq!( list.len(), 1, "it finds our single virtual branch despite it having the same 'identity' as the target branch: 'main'" ); assert_equal( &list[0], ExpectedBranchListing { identity: "main".into(), remotes: vec!["origin"], virtual_branch_given_name: Some("main"), virtual_branch_in_workspace: true, has_local: true, }, "virtual branches can have the name of the target, even though it's probably not going to work when pushing. \ The remotes of the local `refs/heads/main` are shown." ); Ok(()) } #[test] fn one_branch_on_integration_multiple_remotes() -> Result<()> { init_env(); let ctx = project_ctx("one-vbranch-on-integration-two-remotes")?; let list = list_branches(&ctx, None)?; assert_eq!(list.len(), 1, "a single virtual branch"); assert_equal( &list[0], ExpectedBranchListing { identity: "main".into(), remotes: vec!["origin", "other-remote"], virtual_branch_given_name: Some("main"), virtual_branch_in_workspace: true, has_local: true, }, "multiple remotes are detected", ); Ok(()) } mod util { use anyhow::Result; use gitbutler_branch::BranchIdentity; use gitbutler_branch_actions::{BranchListing, BranchListingFilter}; use gitbutler_command_context::CommandContext; /// A flattened and simplified mirror of `BranchListing` for comparing the actual and expected data. #[derive(Debug, PartialEq)] pub struct ExpectedBranchListing<'a> { pub identity: BranchIdentity, pub remotes: Vec<&'a str>, pub virtual_branch_given_name: Option<&'a str>, pub virtual_branch_in_workspace: bool, pub has_local: bool, } impl Default for ExpectedBranchListing<'static> { fn default() -> Self { ExpectedBranchListing { identity: "invalid-identity-should-always-be-specified".into(), remotes: vec![], virtual_branch_given_name: None, virtual_branch_in_workspace: false, has_local: false, } } } pub fn assert_equal( BranchListing { name, remotes, virtual_branch, updated_at: _, head: _, // NOTE: can't have stable commits while `gitbutler-change-id` is not stable/is a UUID. last_commiter: _, has_local, }: &BranchListing, expected: ExpectedBranchListing, msg: &str, ) { assert_eq!(*name, expected.identity, "identity: {msg}"); assert_eq!( *remotes, expected .remotes .into_iter() .map(|name| gix::remote::Name::Symbol(name.into())) .collect::>(), "remotes: {msg}" ); assert_eq!( virtual_branch.as_ref().map(|b| b.given_name.as_str()), expected.virtual_branch_given_name, "virtual-branch-name: {msg}" ); assert_eq!( virtual_branch.as_ref().map_or(false, |b| b.in_workspace), expected.virtual_branch_in_workspace, "virtual-branch-in-workspace: {msg}" ); assert_eq!(*has_local, expected.has_local, "{msg}"); } /// This function affects all tests, but those who care should just call it, assuming /// they all care for the same default value. /// If not, they should be placed in their own integration test or run with `#[serial_test:serial]`. /// For `list_branches` it's needed as it compares the current author with commit authors to determine ownership. pub fn init_env() { for (name, value) in [ ("GIT_AUTHOR_DATE", "2000-01-01 00:00:00 +0000"), ("GIT_AUTHOR_EMAIL", "author@example.com"), ("GIT_AUTHOR_NAME", "author"), ("GIT_COMMITTER_DATE", "2000-01-02 00:00:00 +0000"), ("GIT_COMMITTER_EMAIL", "committer@example.com"), ("GIT_COMMITTER_NAME", "committer"), ] { std::env::set_var(name, value); } } pub fn project_ctx(name: &str) -> Result { gitbutler_testsupport::read_only::fixture("for-listing.sh", name) } pub fn list_branches( ctx: &CommandContext, filter: Option, ) -> Result> { let mut branches = gitbutler_branch_actions::list_branches(ctx, filter, None)?; branches.sort_by(|a, b| a.name.cmp(&b.name)); Ok(branches) } } pub use util::{assert_equal, init_env, list_branches, project_ctx, ExpectedBranchListing};