#![allow(clippy::unwrap_used, clippy::expect_used)]
use git2::{Repository, Signature};
use indoc::{formatdoc, indoc};
use std::fs;
use std::path::Path;
use tempfile::TempDir;
use verctl::config::Config;
use verctl::versions::{self, Skip};
fn commit_tree(repo: &Repository, message: &str) -> git2::Oid {
let sig = Signature::now("t", "t@example.com").unwrap();
let mut index = repo.index().unwrap();
index
.add_all(["."].iter(), git2::IndexAddOption::DEFAULT, None)
.unwrap();
index.write().unwrap();
let tid = index.write_tree().unwrap();
let tree = repo.find_tree(tid).unwrap();
let parent = repo.head().ok().and_then(|head| head.peel_to_commit().ok());
let parents: Vec<&git2::Commit<'_>> = parent.as_ref().into_iter().collect();
repo.commit(Some("HEAD"), &sig, &sig, message, &tree, &parents)
.unwrap()
}
fn write_crate(root: &Path, version: &str) {
fs::write(
root.join("verctl.toml"),
indoc! {r#"
[[packages]]
name = "demo"
path = "Cargo.toml"
"#},
)
.unwrap();
fs::write(
root.join("Cargo.toml"),
formatdoc! {r#"
[package]
name = "demo"
version = "{version}"
"#},
)
.unwrap();
}
fn repo_with_origin_main(version: &str) -> (TempDir, Repository) {
let dir = TempDir::new().unwrap();
let repo = Repository::init(dir.path()).unwrap();
write_crate(dir.path(), version);
let oid = commit_tree(&repo, "init");
repo.remote(
"origin",
"https://github.com/victor-software-house/verctl.git",
)
.unwrap();
repo.reference("refs/remotes/origin/main", oid, true, "test")
.unwrap();
(dir, repo)
}
fn load(root: &Path) -> Config {
Config::load(&root.join("verctl.toml")).unwrap()
}
fn check(root: &Path, skip: Skip) -> anyhow::Result<versions::VersionReport> {
versions::require_with(root, &load(root), skip, &versions::stock_candidates())
}
#[test]
fn matching_version_is_ok() {
let (dir, _) = repo_with_origin_main("1.0.0");
check(dir.path(), Skip::None).unwrap();
}
#[test]
fn hand_edited_version_fails() {
let (dir, _) = repo_with_origin_main("1.0.0");
write_crate(dir.path(), "1.0.1");
let err = check(dir.path(), Skip::None).unwrap_err();
assert!(
format!("{err:#}").contains("demo 1.0.0 -> 1.0.1"),
"{err:#}"
);
}
#[test]
fn fragment_only_change_still_matches() {
let (dir, _) = repo_with_origin_main("1.0.0");
fs::create_dir_all(dir.path().join(".changeset")).unwrap();
fs::write(
dir.path().join(".changeset/note.md"),
indoc! {"
---
demo: patch
---
A fragment. Version is still 1.0.0.
"},
)
.unwrap();
check(dir.path(), Skip::None).unwrap();
}
#[test]
fn version_packages_branch_is_exempt() {
let (dir, _) = repo_with_origin_main("1.0.0");
write_crate(dir.path(), "1.0.1");
check(dir.path(), Skip::VersionBranch).unwrap();
}
#[test]
fn ci_is_exempt() {
let (dir, _) = repo_with_origin_main("1.0.0");
write_crate(dir.path(), "1.0.1");
check(dir.path(), Skip::Ci).unwrap();
}
#[test]
fn new_package_not_on_default_is_ok() {
let (dir, _) = repo_with_origin_main("1.0.0");
fs::write(
dir.path().join("verctl.toml"),
indoc! {r#"
[[packages]]
name = "demo"
path = "Cargo.toml"
[[packages]]
name = "fresh"
path = "crates/fresh/Cargo.toml"
"#},
)
.unwrap();
fs::create_dir_all(dir.path().join("crates/fresh")).unwrap();
fs::write(
dir.path().join("crates/fresh/Cargo.toml"),
indoc! {r#"
[package]
name = "fresh"
version = "0.0.1"
"#},
)
.unwrap();
check(dir.path(), Skip::None).unwrap();
}
#[test]
fn no_origin_is_ok() {
let dir = TempDir::new().unwrap();
Repository::init(dir.path()).unwrap();
write_crate(dir.path(), "1.0.0");
check(dir.path(), Skip::None).unwrap();
}
#[test]
fn bun_package_json_hand_edit_fails() {
let dir = TempDir::new().unwrap();
let repo = Repository::init(dir.path()).unwrap();
fs::write(
dir.path().join("verctl.toml"),
indoc! {r#"
[[packages]]
name = "@org/pkg"
path = "package.json"
"#},
)
.unwrap();
fs::write(
dir.path().join("package.json"),
indoc! {r#"
{ "name": "@org/pkg", "version": "0.1.0" }
"#},
)
.unwrap();
let oid = commit_tree(&repo, "init");
repo.remote(
"origin",
"https://github.com/victor-software-house/verctl.git",
)
.unwrap();
repo.reference("refs/remotes/origin/main", oid, true, "test")
.unwrap();
fs::write(
dir.path().join("package.json"),
indoc! {r#"
{ "name": "@org/pkg", "version": "0.2.0" }
"#},
)
.unwrap();
let err = check(dir.path(), Skip::None).unwrap_err();
assert!(
format!("{err:#}").contains("@org/pkg 0.1.0 -> 0.2.0"),
"{err:#}"
);
}
#[test]
fn cli_hand_edit_fails_and_prints_table() {
let (dir, _) = repo_with_origin_main("1.0.0");
write_crate(dir.path(), "1.0.1");
let output = std::process::Command::new(env!("CARGO_BIN_EXE_verctl"))
.current_dir(dir.path())
.env_remove("CI")
.env_remove("GITHUB_ACTIONS")
.args(["check", "--versions", "--color", "never"])
.output()
.unwrap();
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(!output.status.success(), "{stdout}{stderr}");
assert!(stdout.contains("demo"), "{stdout}");
assert!(stdout.contains("1.0.0"), "{stdout}");
assert!(stdout.contains("1.0.1"), "{stdout}");
assert!(stderr.contains("fragment"), "{stderr}");
}
#[test]
fn ci_env_does_not_skip() {
let (dir, _) = repo_with_origin_main("1.0.0");
write_crate(dir.path(), "1.0.1");
let output = std::process::Command::new(env!("CARGO_BIN_EXE_verctl"))
.current_dir(dir.path())
.env("CI", "true")
.env("GITHUB_ACTIONS", "true")
.args(["check", "--versions", "--color", "never"])
.output()
.unwrap();
assert!(!output.status.success());
}
#[test]
fn behind_released_main_without_local_edit_is_ok() {
let (dir, repo) = repo_with_origin_main("1.0.0");
let first = repo.head().unwrap().peel_to_commit().unwrap();
repo.branch("feature", &first, false).unwrap();
write_crate(dir.path(), "1.1.0");
let released = commit_tree(&repo, "release");
assert_ne!(released, first.id());
repo.reference("refs/remotes/origin/main", released, true, "test")
.unwrap();
repo.set_head("refs/heads/feature").unwrap();
repo.checkout_head(Some(git2::build::CheckoutBuilder::new().force()))
.unwrap();
assert_eq!(
repo.head().unwrap().peel_to_commit().unwrap().id(),
first.id()
);
check(dir.path(), Skip::None).unwrap();
}
#[test]
fn nested_config_maps_onto_the_git_tree() {
let dir = TempDir::new().unwrap();
let repo = Repository::init(dir.path()).unwrap();
fs::create_dir_all(dir.path().join("crates/demo")).unwrap();
fs::write(
dir.path().join("crates/demo/verctl.toml"),
indoc! {r#"
[[packages]]
name = "demo"
path = "Cargo.toml"
"#},
)
.unwrap();
fs::write(
dir.path().join("crates/demo/Cargo.toml"),
indoc! {r#"
[package]
name = "demo"
version = "1.0.0"
"#},
)
.unwrap();
let oid = commit_tree(&repo, "init");
repo.remote(
"origin",
"https://github.com/victor-software-house/verctl.git",
)
.unwrap();
repo.reference("refs/remotes/origin/main", oid, true, "test")
.unwrap();
fs::write(
dir.path().join("crates/demo/Cargo.toml"),
indoc! {r#"
[package]
name = "demo"
version = "1.0.1"
"#},
)
.unwrap();
let err = versions::require_with(
&dir.path().join("crates/demo"),
&Config::load(&dir.path().join("crates/demo/verctl.toml")).unwrap(),
Skip::None,
&versions::stock_candidates(),
)
.unwrap_err();
assert!(
format!("{err:#}").contains("demo 1.0.0 -> 1.0.1"),
"{err:#}"
);
}
#[test]
fn current_branch_reads_version_packages() {
let (dir, repo) = repo_with_origin_main("1.0.0");
let oid = repo.head().unwrap().peel_to_commit().unwrap().id();
repo.reference("refs/heads/version-packages", oid, true, "test")
.unwrap();
repo.set_head("refs/heads/version-packages").unwrap();
assert_eq!(
verctl::git::current_branch(dir.path()).as_deref(),
Some("version-packages")
);
}