Radish alpha
r
rad:z6cFWeWpnZNHh9rUW8phgA3b5yGt
Git libraries for Radicle
Radicle
Git
radicle-git radicle-git-ext t src commit.rs
use std::{io, str::FromStr as _, string::ToString as _};

use proptest::proptest;
use radicle_git_ext::{
    author::{self, Author},
    commit::{headers::Headers, trailers::OwnedTrailer, Commit},
};
use test_helpers::tempdir::{self, WithTmpDir};

use crate::gen;

const NO_TRAILER: &str = "\
tree 50d6ef440728217febf9e35716d8b0296608d7f8
parent 0ad95dbdfe9fdf81938ca419cf740469173e2022
parent a4ec9e07e1b2e6f37f7119651ae3bb63b79988b6
author Fintan Halpenny <fintan.halpenny@gmail.com> 1669292989 +0000
committer Fintan Halpenny <fintan.halpenny@gmail.com> 1669292989 +0000

Merge remote-tracking branch 'origin/surf/organise-tests'

* origin/surf/organise-tests:
  radicle-surf: organise tests
";

const SINGLE_TRAILER: &str = "\
tree 50d6ef440728217febf9e35716d8b0296608d7f8
parent 0ad95dbdfe9fdf81938ca419cf740469173e2022
parent a4ec9e07e1b2e6f37f7119651ae3bb63b79988b6
author Fintan Halpenny <fintan.halpenny@gmail.com> 1669292989 +0000
committer Fintan Halpenny <fintan.halpenny@gmail.com> 1669292989 +0000

Merge remote-tracking branch 'origin/surf/organise-tests'

* origin/surf/organise-tests:
  radicle-surf: organise tests

Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com>
";

const UNSIGNED: &str = "\
tree c66cc435f83ed0fba90ed4500e9b4b96e9bd001b
parent af06ad645133f580a87895353508053c5de60716
author Alexis Sellier <alexis@radicle.xyz> 1664467633 +0200
committer Alexis Sellier <alexis@radicle.xyz> 1664786099 -0200

Add SSH functionality with new `radicle-ssh`

We borrow code from `thrussh`, refactored to be runtime-less.

X-Signed-Off-By: Alex Sellier
X-Co-Authored-By: Fintan Halpenny
";

const SSH_SIGNATURE: &str = "\
-----BEGIN SSH SIGNATURE-----
U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgvjrQogRxxLjzzWns8+mKJAGzEX
4fm2ALoN7pyvD2ttQAAAADZ2l0AAAAAAAAAAZzaGE1MTIAAABTAAAAC3NzaC1lZDI1NTE5
AAAAQIQvhIewOgGfnXLgR5Qe1ZEr2vjekYXTdOfNWICi6ZiosgfZnIqV0enCPC4arVqQg+
GPp0HqxaB911OnSAr6bwU=
-----END SSH SIGNATURE-----";

const PGP_SIGNATURE: &str = "\
-----BEGIN PGP SIGNATURE-----
iQIzBAABCAAdFiEEHe7BWIo9taTY6TIiJVL7b2QGbLcFAmNcDhsACgkQJVL7b2QG
bLcc9Q//RgKf5N4enta9AuszGJZvdFhMPfIDUdw+WAZA6Z8zDPb/aAXZrPP/KIOM
zmX08FTqjP9B9YeWrEcFuAtxsRNqbDKrfpko9Y6bTsdrAJg3WIypBb9F8YDKJ6BO
CORJJqWOsLW129jW+mJDhcE0YTvPlcMiMI2qjVXKhU6Ag11W8IRZyTb9tvEaDjBR
YUnkPvgubv61K9BeUKexE2MakPBldaQtl0MF1Dk7/zo5btLd+KP0SOUKEhuMEu5b
LATHHdiYjt/2Xz7q8EcrFxXUaipxZe89dfTdi2ooJQw3ZDqjDHsGTHpDeBuzuSaJ
9fKVRwFz/78onfHPhmU4wfUhh+Fcl90p5/T+4dt2K6cr+7rq078e+aJYxkX2d0MG
PG0xGP0RN4g+X92K1kGuzoe4870xAnRTNh5nUB+X9snO8tVqQZTb0M2yI+sTsKrv
w/f+uiqL6e9DgIxlO5dgiNHCVoCs1QJ900jUGisrlzS4+n6GzMsG6s3c01X4yY9G
Ou/kGkMsn7tqejqC9RufygcchCFZqYwaHQwPkiYhfYGMarMpoCFvll0h8tSparpS
nnpAQXVdu8m3v1YdPUuTg5ksxSOe9HCIlVXGFhxy3iqCVRn+51FRnUI63rMTOm9/
LBqzvji02lDUPGqPgXfcCS0ty8FM2flBIXnwb8TDzCaPYhf53+U=
=6dw2
-----END PGP SIGNATURE-----";

const SIGNED: &str = "\
tree c66cc435f83ed0fba90ed4500e9b4b96e9bd001b
parent af06ad645133f580a87895353508053c5de60716
author Alexis Sellier <alexis@radicle.xyz> 1664467633 +0200
committer Alexis Sellier <alexis@radicle.xyz> 1664786099 -0200
other e6fe3c97619deb8ab4198620f9a7eb79d98363dd
gpgsig -----BEGIN SSH SIGNATURE-----
 U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgvjrQogRxxLjzzWns8+mKJAGzEX
 4fm2ALoN7pyvD2ttQAAAADZ2l0AAAAAAAAAAZzaGE1MTIAAABTAAAAC3NzaC1lZDI1NTE5
 AAAAQIQvhIewOgGfnXLgR5Qe1ZEr2vjekYXTdOfNWICi6ZiosgfZnIqV0enCPC4arVqQg+
 GPp0HqxaB911OnSAr6bwU=
 -----END SSH SIGNATURE-----
gpgsig -----BEGIN PGP SIGNATURE-----
 iQIzBAABCAAdFiEEHe7BWIo9taTY6TIiJVL7b2QGbLcFAmNcDhsACgkQJVL7b2QG
 bLcc9Q//RgKf5N4enta9AuszGJZvdFhMPfIDUdw+WAZA6Z8zDPb/aAXZrPP/KIOM
 zmX08FTqjP9B9YeWrEcFuAtxsRNqbDKrfpko9Y6bTsdrAJg3WIypBb9F8YDKJ6BO
 CORJJqWOsLW129jW+mJDhcE0YTvPlcMiMI2qjVXKhU6Ag11W8IRZyTb9tvEaDjBR
 YUnkPvgubv61K9BeUKexE2MakPBldaQtl0MF1Dk7/zo5btLd+KP0SOUKEhuMEu5b
 LATHHdiYjt/2Xz7q8EcrFxXUaipxZe89dfTdi2ooJQw3ZDqjDHsGTHpDeBuzuSaJ
 9fKVRwFz/78onfHPhmU4wfUhh+Fcl90p5/T+4dt2K6cr+7rq078e+aJYxkX2d0MG
 PG0xGP0RN4g+X92K1kGuzoe4870xAnRTNh5nUB+X9snO8tVqQZTb0M2yI+sTsKrv
 w/f+uiqL6e9DgIxlO5dgiNHCVoCs1QJ900jUGisrlzS4+n6GzMsG6s3c01X4yY9G
 Ou/kGkMsn7tqejqC9RufygcchCFZqYwaHQwPkiYhfYGMarMpoCFvll0h8tSparpS
 nnpAQXVdu8m3v1YdPUuTg5ksxSOe9HCIlVXGFhxy3iqCVRn+51FRnUI63rMTOm9/
 LBqzvji02lDUPGqPgXfcCS0ty8FM2flBIXnwb8TDzCaPYhf53+U=
 =6dw2
 -----END PGP SIGNATURE-----

Add SSH functionality with new `radicle-ssh`

We borrow code from `thrussh`, refactored to be runtime-less.

X-Signed-Off-By: Alex Sellier
X-Co-Authored-By: Fintan Halpenny
";

#[test]
fn test_push_header() {
    let mut commit = Commit::from_str(UNSIGNED).unwrap();

    commit.push_header("other", "e6fe3c97619deb8ab4198620f9a7eb79d98363dd");
    commit.push_header("gpgsig", SSH_SIGNATURE);
    commit.push_header("gpgsig", PGP_SIGNATURE);

    assert_eq!(commit.to_string(), SIGNED);
}

#[test]
fn test_get_header() {
    let commit = Commit::from_str(SIGNED).unwrap();

    assert_eq!(
        commit
            .signatures()
            .map(|sig| sig.to_string())
            .collect::<Vec<_>>(),
        vec![SSH_SIGNATURE.to_owned(), PGP_SIGNATURE.to_owned()]
    );
    assert_eq!(
        commit.values("other").collect::<Vec<_>>(),
        vec![String::from("e6fe3c97619deb8ab4198620f9a7eb79d98363dd")],
    );
    assert!(commit.values("unknown").next().is_none());
}

#[test]
fn test_conversion() {
    assert_eq!(
        Commit::from_str(NO_TRAILER).unwrap().to_string(),
        NO_TRAILER
    );
    assert_eq!(
        Commit::from_str(SINGLE_TRAILER).unwrap().to_string(),
        SINGLE_TRAILER
    );
    assert_eq!(Commit::from_str(SIGNED).unwrap().to_string(), SIGNED);
    assert_eq!(Commit::from_str(UNSIGNED).unwrap().to_string(), UNSIGNED);
}

proptest! {
    #[test]
    fn valid_commits(commits in proptest::collection::vec(gen::commit::commit(), 5..20)) {
        let repo = tempdir::WithTmpDir::new(|path| {
            git2::Repository::init(path).map_err(io::Error::other)
        }).unwrap();
        let commits = gen::commit::write_commits(&repo, commits).unwrap();
        repo.reference("refs/heads/master", *commits.last().unwrap(), true, "").unwrap();

        let mut walk = repo.revwalk().unwrap();
        walk.push_head().unwrap();

        for oid in walk.take(20) {
            let oid = oid.unwrap();
            let commit = Commit::read(&repo, oid);
            assert!(commit.is_ok(), "Oid: {oid}, Error: {commit:?}");
        }
    }
}

#[test]
fn write_valid_commit() {
    let repo =
        WithTmpDir::new(|path| git2::Repository::init(path).map_err(io::Error::other)).unwrap();

    let author = Author {
        name: "Terry".to_owned(),
        email: "terry.pratchett@proton.mail".to_owned(),
        time: author::Time::new(0, 0),
    };
    let blob = repo.blob(b"The Colour of Magic").unwrap();
    let mut tb = repo.treebuilder(None).unwrap();
    tb.insert("The Colour of Magic", blob, git2::FileMode::Blob.into())
        .unwrap();
    let tree = tb.write().unwrap();
    let commit = repo
        .commit(
            None,
            &git2::Signature::try_from(&author).unwrap(),
            &git2::Signature::try_from(&author).unwrap(),
            "New beginnings",
            &repo.find_tree(tree).unwrap(),
            &[],
        )
        .unwrap();

    let headers = Headers::new();
    let message = "Write Discworld".to_owned();

    let invalid = Commit::new(
        tree,
        vec![blob],
        author.clone(),
        author.clone(),
        headers.clone(),
        message.clone(),
        None::<OwnedTrailer>,
    )
    .write(&repo);
    assert!(invalid.is_err());

    let invalid = Commit::new(
        blob,
        vec![commit],
        author.clone(),
        author.clone(),
        headers.clone(),
        message.clone(),
        None::<OwnedTrailer>,
    )
    .write(&repo);
    assert!(invalid.is_err());

    let valid = Commit::new(
        tree,
        vec![commit],
        author.clone(),
        author,
        headers,
        message,
        None::<OwnedTrailer>,
    )
    .write(&repo);
    assert!(valid.is_ok())
}

#[test]
fn author_roundtrip() {
    let author = "author Fintan Halpenny <fintan.halpenny@gmail.com> 1669292989 +0000";
    assert_eq!(
        author.parse::<Author>().unwrap().to_string(),
        author.to_string()
    );

    let author = "author Alexis Sellier <alexis@radicle.xyz> 1664467633 +0200";
    assert_eq!(
        author.parse::<Author>().unwrap().to_string(),
        author.to_string()
    );

    let author = "author Alexis Sellier <alexis@radicle.xyz> 1664467633 -0200";
    assert_eq!(
        author.parse::<Author>().unwrap().to_string(),
        author.to_string()
    );
}