From 89dcbff9c57e3ebf1e63c3d57ef0d96e4cc42ef3 Mon Sep 17 00:00:00 2001 From: j178 <10510431+j178@users.noreply.github.com> Date: Wed, 24 Jul 2024 20:11:09 +0800 Subject: [PATCH] Add tests --- crates/uv-configuration/src/vcs_options.rs | 4 +- crates/uv/src/commands/project/init.rs | 38 +++--- crates/uv/tests/init.rs | 137 +++++++++++++++++++++ 3 files changed, 160 insertions(+), 19 deletions(-) diff --git a/crates/uv-configuration/src/vcs_options.rs b/crates/uv-configuration/src/vcs_options.rs index c10bd222143de..035167863a56f 100644 --- a/crates/uv-configuration/src/vcs_options.rs +++ b/crates/uv-configuration/src/vcs_options.rs @@ -1,7 +1,7 @@ use serde::Deserialize; use std::str::FromStr; -/// +/// The version control system to use. #[derive(Clone, Copy, Debug, PartialEq, Default, Deserialize)] #[serde(deny_unknown_fields, rename_all = "kebab-case")] #[cfg_attr(feature = "clap", derive(clap::ValueEnum))] @@ -22,7 +22,7 @@ impl FromStr for VersionControl { match s { "git" => Ok(VersionControl::Git), "none" => Ok(VersionControl::None), - other => Err(format!("unknown vcs specification: `{}`", other)), + other => Err(format!("unknown vcs specification: `{other}`")), } } } diff --git a/crates/uv/src/commands/project/init.rs b/crates/uv/src/commands/project/init.rs index 803b726465206..a1812606950f6 100644 --- a/crates/uv/src/commands/project/init.rs +++ b/crates/uv/src/commands/project/init.rs @@ -349,15 +349,21 @@ async fn init_project( fn init_vcs(path: &Path, vcs: VersionControl) -> Result<()> { match vcs { VersionControl::Git => { - if !path.join(".git").exists() { + if path.join(".git").try_exists()? { + warn_user!( + "Git repository already exists at `{}`", + path.simplified_display() + ); + } else { init_git_repo(path)?; + } - // Create the `.gitignore` if it does not already exist. - let gitignore = path.join(".gitignore"); - if !gitignore.exists() { - fs_err::write( - gitignore, - indoc::indoc! {r" + // Create the `.gitignore` if it does not already exist. + let gitignore = path.join(".gitignore"); + if !gitignore.try_exists()? { + fs_err::write( + gitignore, + indoc::indoc! {r" # Python generated files __pycache__/ *.py[oc] @@ -369,8 +375,7 @@ fn init_vcs(path: &Path, vcs: VersionControl) -> Result<()> { # venv .venv "}, - )?; - } + )?; } } VersionControl::None => {} @@ -379,9 +384,10 @@ fn init_vcs(path: &Path, vcs: VersionControl) -> Result<()> { Ok(()) } -// Check if we are in an existing repo. +/// Check if the path is inside a VCS repository. +/// +/// Currently only supports Git. fn existing_vcs_repo(dir: &Path) -> bool { - // Check git repo only for now. is_inside_git_work_tree(dir) } @@ -402,16 +408,14 @@ fn is_inside_git_work_tree(dir: &Path) -> bool { fn init_git_repo(dir: &Path) -> Result<()> { if !Command::new("git") .arg("init") - .current_dir(&dir) + .current_dir(dir) .stdout(Stdio::null()) .stderr(Stdio::null()) - .status()? + .status() + .context("Failed to run `git init`")? .success() { - anyhow::bail!( - "Run `git init` failed at `{}`", - dir.simplified_display() - ); + anyhow::bail!("`git init` failed at `{}`", dir.simplified_display()); } Ok(()) diff --git a/crates/uv/tests/init.rs b/crates/uv/tests/init.rs index c60fc87f76219..fb511b2ff96d6 100644 --- a/crates/uv/tests/init.rs +++ b/crates/uv/tests/init.rs @@ -4,6 +4,8 @@ use anyhow::Result; use assert_fs::prelude::*; use indoc::indoc; use insta::assert_snapshot; +use predicates::prelude::predicate; +use std::process::Command; use common::{uv_snapshot, TestContext}; @@ -1129,3 +1131,138 @@ fn init_hidden() { error: Not a valid package or extra name: ".foo". Names must start and end with a letter or digit and may only contain -, _, ., and alphanumeric characters. "###); } + +#[test] +fn init_git() -> Result<()> { + let context = TestContext::new("3.12"); + + let child = context.temp_dir.child("foo"); + + uv_snapshot!(context.filters(), context.init().arg(child.as_ref()), @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + warning: `uv init` is experimental and may change without warning + Initialized project `foo` at `[TEMP_DIR]/foo` + "###); + + let gitignore = fs_err::read_to_string(child.join(".gitignore"))?; + insta::with_settings!({ + filters => context.filters(), + }, { + assert_snapshot!( + gitignore, @r###" + # Python generated files + __pycache__/ + *.py[oc] + build/ + dist/ + wheels/ + *.egg-info + + # venv + .venv + "### + ); + }); + + child.child(".git").assert(predicate::path::is_dir()); + + Ok(()) +} + +#[test] +fn init_vcs_none() -> Result<()> { + let context = TestContext::new("3.12"); + + let child = context.temp_dir.child("foo"); + + uv_snapshot!(context.filters(), context.init().arg(child.as_ref()).arg("--vcs").arg("none"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + warning: `uv init` is experimental and may change without warning + Initialized project `foo` at `[TEMP_DIR]/foo` + "###); + + child.child(".gitignore").assert(predicate::path::missing()); + child.child(".git").assert(predicate::path::missing()); + + Ok(()) +} + +/// Run `uv init` from within a Git repository. Do not try to reinitialize one. +#[test] +fn init_inside_git_repo() -> Result<()> { + let context = TestContext::new("3.12"); + + Command::new("git") + .arg("init") + .current_dir(&context.temp_dir) + .status()?; + + let child = context.temp_dir.child("foo"); + + uv_snapshot!(context.filters(), context.init().arg(child.as_ref()).arg("--vcs").arg("git"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + warning: `uv init` is experimental and may change without warning + warning: The project is already in a version control system, `--vcs git` is ignored + Initialized project `foo` at `[TEMP_DIR]/foo` + "###); + + child.child(".gitignore").assert(predicate::path::missing()); + + let child = context.temp_dir.child("bar"); + uv_snapshot!(context.filters(), context.init().arg(child.as_ref()), @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + warning: `uv init` is experimental and may change without warning + Initialized project `bar` at `[TEMP_DIR]/bar` + "###); + + child.child(".gitignore").assert(predicate::path::missing()); + + Ok(()) +} + +#[test] +fn init_git_not_installed() { + let context = TestContext::new("3.12"); + + let child = context.temp_dir.child("foo"); + + // Without explicit `--vcs git`, `uv init` succeeds without initializing a Git repository. + uv_snapshot!(context.filters(), context.init().env_remove("PATH").arg(child.as_ref()), @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + warning: `uv init` is experimental and may change without warning + Initialized project `foo` at `[TEMP_DIR]/foo` + "###); + + // With explicit `--vcs git`, `uv init` will fail. + let child = context.temp_dir.child("bar"); + uv_snapshot!(context.filters(), context.init().env_remove("PATH").arg(child.as_ref()).arg("--vcs").arg("git"), @r###" + success: false + exit_code: 2 + ----- stdout ----- + + ----- stderr ----- + warning: `uv init` is experimental and may change without warning + error: Failed to run `git init` + Caused by: program not found + "###); +}