summary refs log blame commit diff stats
path: root/src/git/tests.rs
blob: ef6c763e3b13f36b08ce26b413d600f696616743 (plain) (tree)




































































































































































































































































































































































































                                                                              
// This file is part of GAnarchy - decentralized development hub
// Copyright (C) 2021  Soni L.
// 
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
// 
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

//! Unit tests for the git module.

use super::*;

use std::panic::AssertUnwindSafe;

use assert_fs::assert::PathAssert;
use assert_fs::fixture::{TempDir, PathChild};
use predicates;
use testserver::serve;

const MAGIC_BRANCH: &'static str = "gan\
          0000000000000000000000000000000000000000000000000000000000000000";

const MAGIC_COMMIT: &'static str = "9d7224353c34ad675ee5e43fb3115aaaf98832e9";

// Oh yes.
static REPO: &'static str =
r#####"!<arch>
branches.a/     0           0     0     644     8         `
!<arch>
config.t/       0           0     0     644     66        `
[core]
	repositoryformatversion = 0
	filemode = true
	bare = true
description.t/  0           0     0     644     73        `
Unnamed repository; edit this file 'description' to name the repository.

HEAD.t/         0           0     0     644     24        `
ref: refs/heads/default
hooks.a/        0           0     0     644     8         `
!<arch>
info.a/         0           0     0     644     428       `
!<arch>
exclude.t/      0           0     0     644     240       `
# git ls-files --others --exclude-from=.git/info/exclude
# Lines that start with '#' are comments.
# For a project mostly in C, the following would be a good set of
# exclude patterns (uncomment them if you want to use them):
# *.[oa]
# *~
refs.t/         0           0     0     644     60        `
9d7224353c34ad675ee5e43fb3115aaaf98832e9	refs/heads/default
objects.a/      0           0     0     644     916       `
!<arch>
4b.a/           0           0     0     644     192       `
!<arch>
//                                              42        `
825dc642cb6eb9a060e54bf8d69288fbee4904.b/
/0              0           0     0     644     21        `
eAErKUpNVTBgAAAKLAIB

9d.a/           0           0     0     644     398       `
!<arch>
//                                              42        `
7224353c34ad675ee5e43fb3115aaaf98832e9.b/
/0              0           0     0     644     227       `
eAGdjUsKwjAURR1nFW8DlpikIQURHRSngs7EQT6vNtIkJUZod2/BrkC4o3M5HJtC8AUYp5uSEUEY
xWpnpWDWSDSNppJiLUynnGyYUp1BFA0VRH9KnzJcU/TtxGCP0WEOKeIckjs+g/ZDZVM4wE4yXnMm
dw1sKaeULHRJFvxLJvdLTi+05QHtpMM4IKyAkFvv37BMR8D1O5+izrafYfxZFfkCj/1MqQ==

info.a/         0           0     0     644     70        `
!<arch>
packs.t/        0           0     0     644     1         `


pack.a/         0           0     0     644     8         `
!<arch>
refs.a/         0           0     0     644     246       `
!<arch>
heads.a/        0           0     0     644     110       `
!<arch>
default.t/      0           0     0     644     41        `
9d7224353c34ad675ee5e43fb3115aaaf98832e9

tags.a/         0           0     0     644     8         `
!<arch>
"#####;

/// Tests the low-level (raw) commands.
mod low_level {
    use super::*;

    /// Tests that cmd_init works.
    #[test]
    fn test_cmd_git_init() {
        let dir = TempDir::new().unwrap();
        let repo = dir.child("ganarchy-cache.git");
        let git = Git::at_path(Initializer, repo.path()).unwrap();
        git.cmd_init(|args| {
            // templates actually override the -b flag, but at least
            // this tests that passing args to git init actually works.
            // also note that the -b flag requires a fairly recent version
            // of git but ah well. .-.
            args.arg("-b").arg("hello");
        }).unwrap();
        // git init *always* creates HEAD, even for bare repos!
        // we don't actually care about what HEAD contains tho.
        repo.child("HEAD").assert(predicates::path::exists());
    }

    /// Tests that cmd_clone works.
    #[test]
    fn test_cmd_git_clone() {
        let dir = TempDir::new().unwrap();
        let repo = dir.child("ganarchy-cache.git");
        let git = Git::at_path(Initializer, repo.path()).unwrap();
        git.cmd_init(|_| {}).unwrap();
        // check that a HEAD exists
        repo.child("HEAD").assert(predicates::path::exists());
        let clone = dir.child("ganarchy-fetch-0.git");
        // have to create a Git manually for this one. ah well. :(
        let git_clone = Git {
            path: clone.path().into(),
            pending_branches: None,
            sha256: false,
        };
        git_clone.cmd_clone_from(repo.path(), |args| {
            // also test if git clone args work.
            args.arg("--shared");
        }).unwrap();
        // git clone should carry over the HEAD
        let pred = predicates::path::eq_file(repo.child("HEAD").path());
        clone.child("HEAD").assert(pred);
    }

    /// Tests that cmd works and that we can get output from it.
    #[test]
    fn test_cmd() {
        let dir = TempDir::new().unwrap();
        let repo = dir.child("ganarchy-cache.git");
        let git = Git::at_path(Initializer, repo.path()).unwrap();
        // we do need to init this because git attempts to cd to it.
        git.cmd_init(|_| {}).unwrap();
        let output = git.cmd(|args| {
            args.arg("check-ref-format");
            args.arg("--normalize");
            args.arg("//refs/heads/headache");
        }).unwrap();
        assert_eq!(output.stdout, b"refs/heads/headache\n");
    }
}

/// Tests private operations.
mod privates {
    use super::*;

    /// Tests that fork works.
    #[test]
    fn test_fork() {
        let dir = TempDir::new().unwrap();
        let repo = dir.child("ganarchy-cache.git");
        let mut git = Git::at_path(Initializer, repo.path()).unwrap();
        git.ensure_exists().unwrap();
        let clone = dir.child("ganarchy-fetch-0.git");
        // have to create a Git manually for this one. ah well. :(
        let mut git_clone = Git {
            path: clone.path().into(),
            pending_branches: Some(Default::default()),
            sha256: false,
        };
        git.fork(&mut git_clone).unwrap();
    }

    /// Tests that delete works.
    #[test]
    fn test_delete() {
        let dir = TempDir::new().unwrap();
        let repo = dir.child("ganarchy-cache.git");
        let mut git = Git::at_path(Initializer, repo.path()).unwrap();
        git.ensure_exists().unwrap();
        let clone = dir.child("ganarchy-fetch-0.git");
        // have to create a Git manually for this one. ah well. :(
        let mut git_clone = Git {
            path: clone.path().into(),
            pending_branches: Some(Default::default()),
            sha256: false,
        };
        git.fork(&mut git_clone).unwrap();
        git_clone.delete().unwrap();
    }

    // TODO rm_branch, replace, fetch_work, etc!
    // (or we can rely on with_work_repos as an integration test)
}

/// Tests public operations.
mod publics {
    use super::*;

    /// Tests that ensure_exists works.
    #[test]
    fn test_ensure_exists() {
        let dir = TempDir::new().unwrap();
        let repo = dir.child("ganarchy-cache.git");
        let mut git = Git::at_path(Initializer, repo.path()).unwrap();
        git.ensure_exists().unwrap();
    }

    /// Tests that with_work_repos works.
    #[test]
    fn test_with_work_repos() {
        let dir = TempDir::new().unwrap();
        let repo = dir.child("ganarchy-cache.git");
        let mut git = Git::at_path(Initializer, repo.path()).unwrap();
        git.ensure_exists().unwrap();
        let server = serve(REPO);
        let addr = format!("http://{}:{}/", testserver::IP, server.port());
        git.with_work_repos(1, |repos| {
            let repo = &mut repos[0];
            repo.fetch_source(&addr, MAGIC_BRANCH, "HEAD")
        }).unwrap();
        git.with_work_repos(1, |repos| {
            let repo = &mut repos[0];
            // This is basically check_history, but we skip cache repo check.
            repo.cmd(|args| {
                args.arg("merge-base");
                args.arg("--is-ancestor");
                args.arg(MAGIC_COMMIT);
                args.arg(format!("refs/heads/{}", MAGIC_BRANCH));
            })
        }).unwrap();
    }

    /// Tests that fetch_source works.
    #[test]
    fn test_fetch_source() {
        let dir = TempDir::new().unwrap();
        let repo = dir.child("ganarchy-cache.git");
        let mut git = Git::at_path(Initializer, repo.path()).unwrap();
        git.ensure_exists().unwrap();
        let server = serve(REPO);
        let addr = format!("http://{}:{}/", testserver::IP, server.port());
        git.with_work_repos(1, |repos| {
            let repo = &mut repos[0];
            repo.fetch_source(&addr, MAGIC_BRANCH, "HEAD")
        }).unwrap();
    }

    /// Tests that with_work_repos works properly on failure.
    #[test]
    fn test_with_work_repos_failure() {
        let dir = TempDir::new().unwrap();
        let repo = dir.child("ganarchy-cache.git");
        let mut git = Git::at_path(Initializer, repo.path()).unwrap();
        git.ensure_exists().unwrap();
        let server = serve(REPO);
        let addr = format!("http://{}:{}/", testserver::IP, server.port());
        let addr2 = format!("{}{}", &addr, "nonexistent");
        let res = git.with_work_repos(1, |repos| {
            let repo = &mut repos[0];
            repo.fetch_source(&addr, MAGIC_BRANCH, "HEAD").unwrap();
            repo.fetch_source(&addr2, MAGIC_BRANCH, "HEAD")
        });
        assert!(res.is_err());
        // it should not merge the successful one.
        assert!(git.check_history(MAGIC_BRANCH, MAGIC_COMMIT).is_err());
    }

    /// Tests that panicking through with_work_repos leaves the disk "dirty".
    /// Also tests that a future call to with_work_repos fails gracefully.
    #[test]
    fn test_with_work_repos_panic() {
        let dir = TempDir::new().unwrap();
        let repo = dir.child("ganarchy-cache.git");
        let mut git = Git::at_path(Initializer, repo.path()).unwrap();
        git.ensure_exists().unwrap();
        let server = serve(REPO);
        let addr = format!("http://{}:{}/", testserver::IP, server.port());
        let res = std::panic::catch_unwind(AssertUnwindSafe(|| {
            // we DO NOT want to call unwrap() on ANY of these.
            git.with_work_repos::<_, ()>(1, |repos| {
                let repo = &mut repos[0];
                repo.fetch_source(&addr, MAGIC_BRANCH, "HEAD")?;
                panic!()
            })
        }));
        // check that it panicked.
        assert!(res.is_err());
        // now check that future calls return an error, without calling the
        // closure.
        let res: Result<(), _> = git.with_work_repos(1, |_| panic!());
        assert!(res.is_err());
    }

    /// Tests that check_branch is correct.
    #[test]
    fn test_check_branch() {
        let dir = TempDir::new().unwrap();
        let repo = dir.child("ganarchy-cache.git");
        let mut git = Git::at_path(Initializer, repo.path()).unwrap();
        git.ensure_exists().unwrap();
        git.check_branch("default").unwrap();
        assert!(git.check_branch("HEAD").is_err());
    }

    /// Tests that check_history is correct.
    #[test]
    fn test_check_history() {
        let dir = TempDir::new().unwrap();
        let repo = dir.child("ganarchy-cache.git");
        let mut git = Git::at_path(Initializer, repo.path()).unwrap();
        git.ensure_exists().unwrap();
        let server = serve(REPO);
        let addr = format!("http://{}:{}/", testserver::IP, server.port());
        git.with_work_repos(1, |repos| {
            let repo = &mut repos[0];
            repo.fetch_source(&addr, MAGIC_BRANCH, "HEAD")
        }).unwrap();
        git.check_history(MAGIC_BRANCH, MAGIC_COMMIT).unwrap();
    }

    /// Tests that get_counts is correct. (non-exhaustive)
    #[test]
    fn test_get_counts() {
        let dir = TempDir::new().unwrap();
        let repo = dir.child("ganarchy-cache.git");
        let mut git = Git::at_path(Initializer, repo.path()).unwrap();
        git.ensure_exists().unwrap();
        let server = serve(REPO);
        let addr = format!("http://{}:{}/", testserver::IP, server.port());
        let counts = git.with_work_repos(1, |repos| {
            let repo = &mut repos[0];
            repo.fetch_source(&addr, MAGIC_BRANCH, "HEAD")?;
            repo.get_counts(MAGIC_COMMIT, MAGIC_COMMIT)
        }).unwrap();
        // Unfortunately we can only check MAGIC_COMMIT...MAGIC_COMMIT,
        // so this is always gonna be (0, 0).
        assert_eq!(counts, (0, 0));
    }

    /// Tests that get_hash is correct.
    #[test]
    fn test_get_hash() {
        let dir = TempDir::new().unwrap();
        let repo = dir.child("ganarchy-cache.git");
        let mut git = Git::at_path(Initializer, repo.path()).unwrap();
        git.ensure_exists().unwrap();
        let server = serve(REPO);
        let addr = format!("http://{}:{}/", testserver::IP, server.port());
        let hash = git.with_work_repos(1, |repos| {
            let repo = &mut repos[0];
            repo.fetch_source(&addr, MAGIC_BRANCH, "HEAD")?;
            repo.get_hash(MAGIC_BRANCH)
        }).unwrap();
        let hashtoo = git.get_hash(MAGIC_BRANCH).unwrap();
        assert_eq!(hash, MAGIC_COMMIT);
        assert_eq!(hash, hashtoo);
    }

    /// Tests that get_message is correct.
    #[test]
    fn test_get_message() {
        let dir = TempDir::new().unwrap();
        let repo = dir.child("ganarchy-cache.git");
        let mut git = Git::at_path(Initializer, repo.path()).unwrap();
        git.ensure_exists().unwrap();
        let server = serve(REPO);
        let addr = format!("http://{}:{}/", testserver::IP, server.port());
        let msg = git.with_work_repos(1, |repos| {
            let repo = &mut repos[0];
            repo.fetch_source(&addr, MAGIC_BRANCH, "HEAD")?;
            repo.get_message(MAGIC_COMMIT)
        }).unwrap();
        let msgtoo = git.get_message(MAGIC_COMMIT).unwrap();
        let expect = "[Project] Example Project\n\
                      \n\
                      This is an example GAnarchy project.\n";
        assert_eq!(msg, expect);
        assert_eq!(msg, msgtoo);
    }
}