monochromatic-git-policy-cli 0.5.11

Repository-owned Git policy wrapper: forwards Git commands and checks commits against configured policies.
//! Controls for the tree and commit parsers; the comparisons with real Git are in `repo_read_query_tests.rs`.

use super::*;

/// A tree entry in the stored layout: mode, space, name, NUL, raw name.
fn stored(mode: &str, name: &str, id: u8) -> Vec<u8> {
    let mut bytes: Vec<u8> = format!("{mode} {name}").into_bytes();
    bytes.push(0);
    bytes.extend_from_slice(&[id; 20]);
    return bytes;
}

/// The subject of a message that holds a NUL byte, which Git's string handling would cut.
fn parse_subject_with_nul() -> Option<Vec<u8>> {
    return subject_of(b"left\0right\n");
}

/// Entries come back in stored order with their modes, and malformed ones are refused.
#[test]
fn trees_parse_and_malformed_ones_are_refused() {
    let mut bytes = stored("100644", "a", 1);
    bytes.extend(stored("40000", "dir", 2));
    let entries = parse_tree(bytes.as_slice(), 20).expect("plain tree");
    assert_eq!(entries.len(), 2);
    assert!(is_tree_mode(entries[1].mode));
    assert!(!is_tree_mode(entries[0].mode));
    assert!(parse_tree(&bytes[..bytes.len() - 1], 20).is_none(), "a short name");
    assert!(parse_tree(stored("0100644", "a", 1).as_slice(), 20).is_none(), "a leading zero");
    assert!(parse_tree(stored("100644", "a/b", 1).as_slice(), 20).is_none(), "a slash in a name");
    assert!(parse_tree(stored("10x644", "a", 1).as_slice(), 20).is_none(), "a non-octal mode");
    assert!(parse_tree(stored("100664", "a", 1).as_slice(), 20).is_none(), "a mode Git canonicalizes to 100644");
    assert!(parse_subject_with_nul().is_none(), "a message with a NUL");
}

/// The tree, parents and message of a commit are read; an `encoding` header is declined.
#[test]
fn commits_parse_and_encodings_are_declined() {
    let tree = "1".repeat(40);
    let parent = "2".repeat(40);
    let text = format!("tree {tree}\nparent {parent}\nauthor A <a@e> 1 +0000\ncommitter A <a@e> 1 +0000\n\nsubject\n\nbody\n");
    let fields = parse_commit(text.as_bytes(), ObjectFormat::Sha1).expect("commit");
    assert_eq!(fields.parents.len(), 1);
    assert_eq!(fields.message, b"subject\n\nbody\n".to_vec());
    let encoded = format!("tree {tree}\nauthor A <a@e> 1 +0000\ncommitter A <a@e> 1 +0000\nencoding latin1\n\nsubject\n");
    assert!(parse_commit(encoded.as_bytes(), ObjectFormat::Sha1).is_none());
    assert!(parse_commit(b"no header", ObjectFormat::Sha1).is_none());
}

/// The subject is the first paragraph on one line; a carriage return is declined.
#[test]
fn subjects_join_the_first_paragraph() {
    assert_eq!(subject_of(b"one\ntwo\n\nbody\n"), Some(b"one two".to_vec()));
    assert_eq!(subject_of(b"\n\nlead\n"), Some(b"lead".to_vec()));
    assert_eq!(subject_of(b""), Some(Vec::new()));
    assert!(subject_of(b"a\r\nb\n").is_none());
}