chore(release): prepare public source release
This commit is contained in:
@@ -0,0 +1,288 @@
|
||||
//! Integration tests for the `gitshape` command.
|
||||
|
||||
use std::fs;
|
||||
use std::process::Command as ProcessCommand;
|
||||
|
||||
use assert_cmd::Command;
|
||||
use predicates::prelude::*;
|
||||
use tempfile::tempdir;
|
||||
|
||||
fn cargo_command() -> Command {
|
||||
Command::cargo_bin("gitshape").expect("binary")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn status_subcommand_summarizes_dirty_repository_as_json() {
|
||||
let temp = tempdir().expect("tempdir");
|
||||
run_git(temp.path(), ["-c", "init.defaultBranch=main", "init"]);
|
||||
run_git(temp.path(), ["config", "user.name", "Codex"]);
|
||||
run_git(temp.path(), ["config", "user.email", "codex@example.com"]);
|
||||
fs::write(temp.path().join("tracked.txt"), "first\n").expect("tracked");
|
||||
run_git(temp.path(), ["add", "tracked.txt"]);
|
||||
run_git(temp.path(), ["commit", "-m", "init"]);
|
||||
|
||||
fs::write(temp.path().join("tracked.txt"), "second\n").expect("modify");
|
||||
fs::write(temp.path().join("new.txt"), "new\n").expect("untracked");
|
||||
|
||||
let mut command = cargo_command();
|
||||
command
|
||||
.arg("--json")
|
||||
.arg("status")
|
||||
.arg("--repo")
|
||||
.arg(temp.path())
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(predicate::str::contains("\"modified\":1"))
|
||||
.stdout(predicate::str::contains("\"untracked\":1"))
|
||||
.stdout(predicate::str::contains("\"clean\":false"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn diff_subcommand_reports_staged_change_summary() {
|
||||
let temp = tempdir().expect("tempdir");
|
||||
run_git(temp.path(), ["init"]);
|
||||
run_git(temp.path(), ["config", "user.name", "Codex"]);
|
||||
run_git(temp.path(), ["config", "user.email", "codex@example.com"]);
|
||||
fs::write(
|
||||
temp.path().join("src.rs"),
|
||||
"fn build() {\n println!(\"old\");\n}\n",
|
||||
)
|
||||
.expect("src");
|
||||
run_git(temp.path(), ["add", "src.rs"]);
|
||||
run_git(temp.path(), ["commit", "-m", "init"]);
|
||||
|
||||
fs::write(
|
||||
temp.path().join("src.rs"),
|
||||
"fn build() {\n println!(\"new\");\n}\n",
|
||||
)
|
||||
.expect("modify");
|
||||
run_git(temp.path(), ["add", "src.rs"]);
|
||||
|
||||
let mut command = cargo_command();
|
||||
command
|
||||
.arg("--json")
|
||||
.arg("diff")
|
||||
.arg("--repo")
|
||||
.arg(temp.path())
|
||||
.arg("--staged")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(predicate::str::contains("\"mode\":\"staged\""))
|
||||
.stdout(predicate::str::contains("\"files_changed\":1"))
|
||||
.stdout(predicate::str::contains("\"status\":\"modified\""));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn status_subcommand_handles_unborn_repository() {
|
||||
let temp = tempdir().expect("tempdir");
|
||||
run_git(temp.path(), ["-c", "init.defaultBranch=main", "init"]);
|
||||
|
||||
let mut command = cargo_command();
|
||||
command
|
||||
.arg("--json")
|
||||
.arg("status")
|
||||
.arg("--repo")
|
||||
.arg(temp.path())
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(predicate::str::contains("\"branch\":\"main\""))
|
||||
.stdout(predicate::str::contains("\"head\":\"-\""))
|
||||
.stdout(predicate::str::contains("\"clean\":true"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn diff_subcommand_accepts_explicit_revisions() {
|
||||
let temp = tempdir().expect("tempdir");
|
||||
run_git(temp.path(), ["init"]);
|
||||
run_git(temp.path(), ["config", "user.name", "Codex"]);
|
||||
run_git(temp.path(), ["config", "user.email", "codex@example.com"]);
|
||||
fs::write(temp.path().join("demo.txt"), "one\n").expect("demo");
|
||||
run_git(temp.path(), ["add", "demo.txt"]);
|
||||
run_git(temp.path(), ["commit", "-m", "first"]);
|
||||
let first = run_git_capture(temp.path(), ["rev-parse", "--short", "HEAD"]);
|
||||
|
||||
fs::write(temp.path().join("demo.txt"), "one\ntwo\n").expect("update");
|
||||
run_git(temp.path(), ["add", "demo.txt"]);
|
||||
run_git(temp.path(), ["commit", "-m", "second"]);
|
||||
let second = run_git_capture(temp.path(), ["rev-parse", "--short", "HEAD"]);
|
||||
|
||||
let mut command = cargo_command();
|
||||
command
|
||||
.arg("--json")
|
||||
.arg("diff")
|
||||
.arg("--repo")
|
||||
.arg(temp.path())
|
||||
.arg(first)
|
||||
.arg(second)
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(predicate::str::contains("\"mode\":\"revisions\""))
|
||||
.stdout(predicate::str::contains("\"revisions\":["))
|
||||
.stdout(predicate::str::contains("\"files_changed\":1"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn diff_subcommand_returns_success_for_empty_revision_diff() {
|
||||
let temp = tempdir().expect("tempdir");
|
||||
run_git(temp.path(), ["init"]);
|
||||
run_git(temp.path(), ["config", "user.name", "Codex"]);
|
||||
run_git(temp.path(), ["config", "user.email", "codex@example.com"]);
|
||||
fs::write(temp.path().join("demo.txt"), "one\n").expect("demo");
|
||||
run_git(temp.path(), ["add", "demo.txt"]);
|
||||
run_git(temp.path(), ["commit", "-m", "first"]);
|
||||
|
||||
let mut command = cargo_command();
|
||||
command
|
||||
.arg("diff")
|
||||
.arg("--repo")
|
||||
.arg(temp.path())
|
||||
.arg("HEAD")
|
||||
.arg("HEAD")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(predicate::str::contains("mode=revisions"))
|
||||
.stdout(predicate::str::contains("files=0"))
|
||||
.stdout(predicate::str::contains("line_additions=0"))
|
||||
.stdout(predicate::str::contains("line_deletions=0"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn help_includes_new_subcommands_and_flags() {
|
||||
let mut command = cargo_command();
|
||||
command
|
||||
.arg("--help")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(predicate::str::contains("gitshape [OPTIONS] status"))
|
||||
.stdout(predicate::str::contains("gitshape [OPTIONS] diff"))
|
||||
.stdout(predicate::str::contains("--repo"))
|
||||
.stdout(predicate::str::contains("--staged"))
|
||||
.stdout(predicate::str::contains("--ignored"))
|
||||
.stdout(predicate::str::contains("gitshape"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bare_path_defaults_to_status_mode() {
|
||||
let temp = tempdir().expect("tempdir");
|
||||
run_git(temp.path(), ["-c", "init.defaultBranch=main", "init"]);
|
||||
|
||||
let mut command = cargo_command();
|
||||
command
|
||||
.arg("--json")
|
||||
.arg(temp.path())
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(predicate::str::contains("\"repository_present\":true"))
|
||||
.stdout(predicate::str::contains("\"branch\":\"main\""));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn status_reports_missing_repository_without_failing() {
|
||||
let temp = tempdir().expect("tempdir");
|
||||
|
||||
let mut command = cargo_command();
|
||||
command
|
||||
.arg("--json")
|
||||
.arg("status")
|
||||
.arg("--repo")
|
||||
.arg(temp.path())
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(predicate::str::contains("\"repository_present\":false"))
|
||||
.stdout(predicate::str::contains(
|
||||
"\"error\":\"fatal: not a git repository",
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn diff_subcommand_rejects_staged_with_explicit_revisions() {
|
||||
let mut command = cargo_command();
|
||||
command
|
||||
.arg("diff")
|
||||
.arg("--staged")
|
||||
.arg("HEAD")
|
||||
.assert()
|
||||
.failure()
|
||||
.stderr(predicate::str::contains(
|
||||
"diff --staged does not accept explicit revisions",
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn status_subcommand_rejects_repo_and_positional_path_together() {
|
||||
let temp = tempdir().expect("tempdir");
|
||||
|
||||
let mut command = cargo_command();
|
||||
command
|
||||
.arg("status")
|
||||
.arg("--repo")
|
||||
.arg(temp.path())
|
||||
.arg(temp.path())
|
||||
.assert()
|
||||
.failure()
|
||||
.stderr(predicate::str::contains(
|
||||
"status accepts either --repo <PATH> or positional [PATH], not both",
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn diff_subcommand_text_output_renders_rename_details() {
|
||||
let temp = tempdir().expect("tempdir");
|
||||
run_git(temp.path(), ["init"]);
|
||||
run_git(temp.path(), ["config", "user.name", "Codex"]);
|
||||
run_git(temp.path(), ["config", "user.email", "codex@example.com"]);
|
||||
fs::write(
|
||||
temp.path().join("old_name.rs"),
|
||||
"fn rename_me() {\n println!(\"old\");\n}\n",
|
||||
)
|
||||
.expect("old file");
|
||||
run_git(temp.path(), ["add", "old_name.rs"]);
|
||||
run_git(temp.path(), ["commit", "-m", "before rename"]);
|
||||
let first = run_git_capture(temp.path(), ["rev-parse", "--short", "HEAD"]);
|
||||
|
||||
run_git(temp.path(), ["mv", "old_name.rs", "new_name.rs"]);
|
||||
fs::write(
|
||||
temp.path().join("new_name.rs"),
|
||||
"fn rename_me() {\n println!(\"old\");\n}\nfn helper() {}\n",
|
||||
)
|
||||
.expect("new file");
|
||||
run_git(temp.path(), ["add", "new_name.rs"]);
|
||||
run_git(temp.path(), ["commit", "-m", "after rename"]);
|
||||
let second = run_git_capture(temp.path(), ["rev-parse", "--short", "HEAD"]);
|
||||
|
||||
let mut command = cargo_command();
|
||||
command
|
||||
.arg("diff")
|
||||
.arg("--repo")
|
||||
.arg(temp.path())
|
||||
.arg(first)
|
||||
.arg(second)
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(predicate::str::contains("mode=revisions"))
|
||||
.stdout(predicate::str::contains("status=renamed"))
|
||||
.stdout(predicate::str::contains("path=new_name.rs"))
|
||||
.stdout(predicate::str::contains("previous=old_name.rs"));
|
||||
}
|
||||
|
||||
fn run_git<const N: usize>(cwd: &std::path::Path, args: [&str; N]) {
|
||||
let status = ProcessCommand::new("git")
|
||||
.current_dir(cwd)
|
||||
.args(args)
|
||||
.status()
|
||||
.expect("git command");
|
||||
assert!(status.success(), "git command failed");
|
||||
}
|
||||
|
||||
fn run_git_capture<const N: usize>(cwd: &std::path::Path, args: [&str; N]) -> String {
|
||||
let output = ProcessCommand::new("git")
|
||||
.current_dir(cwd)
|
||||
.args(args)
|
||||
.output()
|
||||
.expect("git command output");
|
||||
assert!(output.status.success(), "git command failed");
|
||||
String::from_utf8(output.stdout)
|
||||
.expect("utf8")
|
||||
.trim()
|
||||
.to_string()
|
||||
}
|
||||
Reference in New Issue
Block a user