codebase 0.11.0

Manage your codebase like a boss!
Documentation
use std::collections::BTreeMap;
use std::path::{Path, PathBuf};
use std::{env, fs};

use codebase::codebase::Codebase;
use codebase::project::{Project, ProjectOpts};

fn random_dir() -> PathBuf {
    let tmp_dir = tempfile::Builder::new()
        .prefix("codebase")
        .tempdir()
        .unwrap();
    tmp_dir.into_path().join("Codebase")
}

#[test]
fn init_codebase() {
    let dir = random_dir();
    let codebase = Codebase::init(
        dir.as_path(),
        &Some("https://github.com/creekorful/test.git"),
        false,
        dir.parent().unwrap(),
    );

    assert!(codebase.is_ok());
    assert!(dir.join(".codebase").join("README.md").exists());
}

#[test]
fn init_codebase_already_exist() {
    let dir = random_dir();
    fs::create_dir_all(dir.join(".codebase")).unwrap();

    let codebase = Codebase::init(
        dir.as_path(),
        &Some("https://github.com/creekorful/test.git"),
        false,
        dir.parent().unwrap(),
    );
    assert!(codebase.is_err()); // Should fail because .codebase directory already exist
}

#[test]
fn init_codebase_invalid_remote() {
    let dir = random_dir();

    let codebase = Codebase::init(dir.as_path(), &Some(""), false, dir.parent().unwrap());
    assert!(codebase.is_err()); // Should fail because remote is invalid
}

#[test]
fn init_codebase_import() {
    let dir = random_dir();

    // Clone some projects
    git2::Repository::clone(
        "https://github.com/codebase-rs/dot-codebase-test.git",
        dir.join("Codebase/dot-codebase-test"),
    )
    .unwrap();
    git2::Repository::clone(
        "https://github.com/codebase-rs/hooks.git",
        dir.join("Codebase/hooks"),
    )
    .unwrap();
    let ftpsync = git2::Repository::clone(
        "https://github.com/creekorful/ftpsync.git",
        dir.join("Personal/Python/ftpsync"),
    )
    .unwrap();

    // set configuration
    ftpsync
        .config()
        .unwrap()
        .set_str("user.name", "Janitor")
        .unwrap();

    let codebase = Codebase::init(&dir, &None, true, &dir.parent().unwrap());
    assert!(codebase.is_ok());

    let codebase = codebase.unwrap();
    let manifest = codebase.read_manifest().unwrap();
    assert_eq!(manifest.projects.len(), 3);

    let project = manifest.find_project("Codebase/dot-codebase-test");
    assert!(project.is_some());

    let project = manifest.find_project("Codebase/hooks");
    assert!(project.is_some());

    let project = manifest.find_project("Personal/Python/ftpsync");
    assert!(project.is_some());

    // make sure git config is applied
    let project = project.unwrap();
    assert_eq!(project.configuration.get("user.name").unwrap(), "Janitor");
}

#[test]
fn test_clone_codebase() {
    let dir = random_dir();

    let callback = |_: &str, _: &Project| ();
    let codebase = Codebase::clone(
        dir.as_path(),
        "https://github.com/codebase-rs/dot-codebase-test.git",
        &callback,
        dir.parent().unwrap(),
    );
    assert!(codebase.is_ok());

    // make sure everything is properly configured
    let repo = git2::Repository::open(
        dir.join("Contributing")
            .join("Debian")
            .join("dh-make-golang"),
    );
    assert!(repo.is_ok());

    let repo = repo.unwrap();

    // make sure config is applied
    let config = repo.config().unwrap().snapshot().unwrap();
    assert_eq!(config.get_str("user.email"), Ok("alois@micard.lu"));

    // make sure hooks are applied
    assert!(dir
        .join("Contributing")
        .join("Debian")
        .join("dh-make-golang")
        .join(".git")
        .join("hooks")
        .join("pre-commit")
        .exists())
}

#[test]
fn test_add_project() {
    let dir = random_dir();

    let codebase = Codebase::init(dir.as_path(), &None, false, dir.parent().unwrap()).unwrap();

    let mut config: BTreeMap<String, String> = BTreeMap::new();
    config.insert("user.email".to_string(), "alois@micard.lu".to_string());
    config.insert("user.name".to_string(), "Janitor".to_string());

    let project = codebase.add_project::<PathBuf>(
        &None,
        "https://github.com/codebase-rs/dot-codebase-test.git",
        &ProjectOpts::new(None, config, Some("rust/continuous-integration")),
    );
    assert!(project.is_ok());

    let project = project.unwrap();
    assert_eq!(
        project.remote,
        "https://github.com/codebase-rs/dot-codebase-test.git"
    );
    assert_eq!(project.branch, "master");
    assert_eq!(
        project.configuration.get("user.email").unwrap(),
        "alois@micard.lu"
    );
    assert_eq!(project.configuration.get("user.name").unwrap(), "Janitor");

    let repo = git2::Repository::open(dir.join("dot-codebase-test")).unwrap();

    // make sure config is applied
    let config = repo.config().unwrap().snapshot().unwrap();
    assert_eq!(config.get_str("user.email"), Ok("alois@micard.lu"));
    assert_eq!(config.get_str("user.name"), Ok("Janitor"));

    // make sure hooks are applied
    assert!(dir
        .join("dot-codebase-test")
        .join(".git")
        .join("hooks")
        .join("pre-commit")
        .exists())
}

#[test]
fn test_add_project_subfolder() {
    let dir = random_dir();

    assert!(Codebase::init(dir.as_path(), &None, false, dir.parent().unwrap()).is_ok());

    fs::create_dir_all(dir.join("A/B")).expect("unable to create directory");
    env::set_current_dir(dir.join("A/B")).expect("unable to cd into director");

    let cwd = env::current_dir().unwrap();
    let codebase = Codebase::open(cwd.as_path(), dir.parent().unwrap());

    assert!(codebase.is_ok());

    let codebase = codebase.unwrap();

    let project = codebase.add_project(
        &Some(PathBuf::from("C")),
        "https://github.com/codebase-rs/dot-codebase-test.git",
        &ProjectOpts::new(None, BTreeMap::new(), Some("rust/continuous-integration")),
    );
    assert!(project.is_ok());

    let manifest = codebase.read_manifest().expect("unable to read manifest");
    assert!(manifest.find_project("A/B/C/dot-codebase-test").is_some());
}

#[test]
fn test_create_project() {
    let dir = random_dir();

    let codebase = Codebase::init(dir.as_path(), &None, false, dir.parent().unwrap()).unwrap();

    let mut config: BTreeMap<String, String> = BTreeMap::new();
    config.insert("user.email".to_string(), "alois@micard.lu".to_string());
    config.insert("user.name".to_string(), "Janitor".to_string());

    let project = codebase.create_project(
        &Path::new("test").join("a"),
        "https://github.com/codebase-rs/test.git",
        &ProjectOpts::new(None, config, Some("rust/continuous-integration")),
        &None,
        &BTreeMap::new(),
    );
    assert!(project.is_ok());

    let (_, project) = project.unwrap();
    assert_eq!(project.remote, "https://github.com/codebase-rs/test.git");
    assert_eq!(project.branch, "master");
    assert_eq!(
        project.configuration.get("user.email").unwrap(),
        "alois@micard.lu"
    );
    assert_eq!(project.configuration.get("user.name").unwrap(), "Janitor");

    let repo = git2::Repository::open(dir.join("test").join("a")).unwrap();

    // make sure config is applied
    let config = repo.config().unwrap().snapshot().unwrap();
    assert_eq!(config.get_str("user.email"), Ok("alois@micard.lu"));
    assert_eq!(config.get_str("user.name"), Ok("Janitor"));

    // make sure hooks are applied
    assert!(dir
        .join("test")
        .join("a")
        .join(".git")
        .join("hooks")
        .join("pre-commit")
        .exists())
}

#[test]
fn test_add_empty_repo() {
    let dir = random_dir();

    let codebase = Codebase::init(dir.as_path(), &None, false, dir.parent().unwrap()).unwrap();

    let project = codebase.add_project::<PathBuf>(
        &None,
        "https://github.com/codebase-rs/empty-test.git",
        &ProjectOpts::new(None, BTreeMap::new(), None),
    );
    assert!(project.is_ok());
}

#[test]
fn test_local_path() {
    let dir = random_dir();
    let codebase = Codebase::init(dir.as_path(), &None, false, dir.parent().unwrap())
        .expect("unable to initialize codebase");
    assert_eq!(codebase.local_path(), PathBuf::new());

    let local_path = PathBuf::from("A").join("B").join("C");
    fs::create_dir_all(dir.join(&local_path)).expect("unable to create directories");
    env::set_current_dir(dir.join(&local_path)).expect("unable to change current directory");

    let cwd = env::current_dir().unwrap();
    let codebase = Codebase::open(cwd, dir.parent().unwrap()).expect("unable to open codebase");

    assert_eq!(codebase.local_path(), local_path);
}