use super::*;
fn coauthor_emails(message: &str) -> Vec<String> {
let block = trailer_block(message.as_bytes());
COAUTHOR
.captures_iter(block)
.filter_map(|caps| {
let email = caps.get(2)?;
Some(String::from_utf8_lossy(email.as_bytes()).into_owned())
})
.collect()
}
#[test]
fn coauthor_in_final_trailer_block_is_counted() {
let msg = "pair work\n\nCo-authored-by: Grace Hopper <grace@example.com>\n";
assert_eq!(coauthor_emails(msg), vec!["grace@example.com".to_string()]);
}
#[test]
fn coauthor_alongside_other_trailers_is_counted() {
let msg = "\
fix the thing
Co-authored-by: Real Dev <real@example.com>
Signed-off-by: Ada <ada@example.com>";
assert_eq!(coauthor_emails(msg), vec!["real@example.com".to_string()]);
}
#[test]
fn coauthor_quoted_in_body_is_not_counted() {
let msg = "\
Revert a bad merge
This reverts the merge that introduced the regression. For the record,
the original commit read:
Add pairing helper
Co-authored-by: Eve Quoted <eve@example.com>
Signed-off-by: Ada Lovelace <ada@example.com>";
assert!(
coauthor_emails(msg).is_empty(),
"a body-quoted co-author must not be honoured"
);
}
#[test]
fn coauthor_outside_final_trailer_block_is_not_counted() {
let msg = "\
Document the convention
Co-authored-by: Ghost <ghost@example.com>
This paragraph explains the footer format we standardized on.
Signed-off-by: Real Dev <real@example.com>";
assert!(
coauthor_emails(msg).is_empty(),
"a column-0 co-author outside the final trailer block is not counted"
);
}
#[test]
fn single_paragraph_trailer_only_message_is_counted() {
let msg = "Co-authored-by: Solo <solo@example.com>";
assert_eq!(coauthor_emails(msg), vec!["solo@example.com".to_string()]);
}
#[test]
fn ordinary_single_author_message_yields_no_coauthor() {
let msg = "just a normal commit\n\nwith a body paragraph that explains why.";
assert!(coauthor_emails(msg).is_empty());
}
#[test]
fn empty_message_yields_no_coauthor() {
assert!(coauthor_emails("").is_empty());
}
mod push_if_human {
use super::*;
use crate::vcs::identity::AuthorId;
fn resolver() -> ParticipantResolver<'static> {
let snapshot: &'static gix::mailmap::Snapshot =
Box::leak(Box::new(gix::mailmap::Snapshot::default()));
ParticipantResolver::new(snapshot, None)
}
#[test]
fn keyless_authors_are_dropped_not_collapsed() {
let resolver = resolver();
let mut out = Vec::new();
resolver.push_if_human(&mut out, b"", b"");
resolver.push_if_human(&mut out, b" ", b"");
assert!(
out.is_empty(),
"keyless authors must not anchor any participant: {out:?}"
);
}
#[test]
fn named_and_name_only_authors_are_kept() {
let resolver = resolver();
let mut out = Vec::new();
resolver.push_if_human(&mut out, b"Ada", b"ada@example.com");
resolver.push_if_human(&mut out, b"", b"");
resolver.push_if_human(&mut out, b"Grace Hopper", b"");
assert_eq!(out.len(), 2, "two real identities survive: {out:?}");
assert!(out.contains(&AuthorId::new(b"Ada", b"ada@example.com")));
assert!(out.contains(&AuthorId::new(b"Grace Hopper", b"")));
}
}