use super::*;
fn project_info(dir: &std::path::Path, name: &str, path: &str) -> ProjectInfo {
ProjectInfo::for_test(name, AbsolutePath::new(dir.join(path)).unwrap())
}
#[tokio::test]
async fn write_version_file_not_found() {
let dir = temp_dir();
let adapter = recording_adapter(CargoConfig::default(), dir.path(), 0);
let info = project_info(dir.path(), "my-crate", "");
let version: semver::Version = "1.0.0".parse().unwrap();
let result = adapter.write_version(&info, &version, false).await;
assert!(result.is_err());
}
#[tokio::test]
async fn write_version_invalid_toml() {
let dir = temp_dir();
write_cargo_toml(dir.path(), "not valid toml [[[");
let adapter = recording_adapter(CargoConfig::default(), dir.path(), 0);
let info = project_info(dir.path(), "my-crate", "");
let version: semver::Version = "1.0.0".parse().unwrap();
let result = adapter.write_version(&info, &version, false).await;
assert!(result.is_err());
}
#[tokio::test]
async fn write_version_missing_package_section() {
let dir = temp_dir();
write_cargo_toml(dir.path(), "[dependencies]\n");
let adapter = recording_adapter(CargoConfig::default(), dir.path(), 0);
let info = project_info(dir.path(), "my-crate", "");
let version: semver::Version = "1.0.0".parse().unwrap();
let result = adapter.write_version(&info, &version, false).await;
assert!(result.is_err());
assert!(
result
.unwrap_err()
.to_string()
.contains("No [package] table")
);
}
#[tokio::test]
async fn write_version_updates_cargo_toml() {
let dir = temp_dir();
write_cargo_toml(
dir.path(),
r#"
[package]
name = "my-crate"
version = "1.0.0"
edition = "2024"
"#,
);
let adapter = recording_adapter(CargoConfig::default(), dir.path(), 0);
let info = project_info(dir.path(), "my-crate", "");
let new_version: semver::Version = "2.0.0".parse().unwrap();
let paths = adapter
.write_version(&info, &new_version, false)
.await
.unwrap();
let contents = std::fs::read_to_string(dir.path().join("Cargo.toml")).unwrap();
assert!(contents.contains("version = \"2.0.0\""));
assert!(contents.contains("edition = \"2024\""));
assert_eq!(paths.len(), 1);
assert_eq!(paths[0], dir.path().join("Cargo.toml"));
}
#[tokio::test]
async fn write_version_dry_run_does_not_write_file() {
let dir = temp_dir();
write_cargo_toml(
dir.path(),
r#"
[package]
name = "my-crate"
version = "1.0.0"
edition = "2024"
"#,
);
let adapter = recording_adapter(CargoConfig::default(), dir.path(), 0);
let info = project_info(dir.path(), "my-crate", "");
let new_version: semver::Version = "2.0.0".parse().unwrap();
let paths = adapter
.write_version(&info, &new_version, true)
.await
.unwrap();
let contents = std::fs::read_to_string(dir.path().join("Cargo.toml")).unwrap();
assert!(
contents.contains("version = \"1.0.0\""),
"dry-run should not modify the file, got: {contents}"
);
assert!(
!contents.contains("version = \"2.0.0\""),
"dry-run should not write new version, got: {contents}"
);
assert_eq!(paths.len(), 1);
assert_eq!(paths[0], dir.path().join("Cargo.toml"));
}
#[tokio::test]
async fn write_version_roundtrip() {
let dir = temp_dir();
write_cargo_toml(
dir.path(),
r#"
[package]
name = "my-crate"
version = "0.1.0"
"#,
);
let adapter = recording_adapter(CargoConfig::default(), dir.path(), 0);
let info = project_info(dir.path(), "my-crate", "");
let new_v: semver::Version = "0.2.0".parse().unwrap();
adapter.write_version(&info, &new_v, false).await.unwrap();
let projects = enumerate(dir.path()).await.unwrap();
assert_eq!(projects.len(), 1);
assert_eq!(projects[0].version.to_string(), "0.2.0");
}
#[tokio::test]
async fn write_version_workspace_inherited_updates_root() {
let dir = temp_dir();
write_cargo_toml(
dir.path(),
r#"
[workspace]
members = ["crates/*"]
[workspace.package]
version = "1.0.0"
"#,
);
let member = dir.path().join("crates/member");
std::fs::create_dir_all(&member).unwrap();
write_cargo_toml(
&member,
r#"
[package]
name = "member"
version.workspace = true
"#,
);
let adapter = recording_adapter(CargoConfig::default(), dir.path(), 0);
let info = project_info(dir.path(), "member", "crates/member");
let new_v: semver::Version = "2.0.0".parse().unwrap();
let paths = adapter.write_version(&info, &new_v, false).await.unwrap();
let member_toml = std::fs::read_to_string(member.join("Cargo.toml")).unwrap();
assert!(
member_toml.contains("version.workspace = true"),
"Member Cargo.toml should preserve workspace inheritance, got:\n{member_toml}"
);
let root_toml = std::fs::read_to_string(dir.path().join("Cargo.toml")).unwrap();
assert!(
root_toml.contains("\"2.0.0\""),
"Workspace root should have updated version, got:\n{root_toml}"
);
let projects = enumerate(dir.path()).await.unwrap();
assert_eq!(projects.len(), 1);
assert_eq!(projects[0].version.to_string(), "2.0.0");
assert_eq!(paths.len(), 1, "Expected exactly one modified path");
assert_eq!(
paths[0],
dir.path().join("Cargo.toml"),
"Expected workspace root Cargo.toml, got: {:?}",
paths[0]
);
}
#[tokio::test]
async fn write_version_workspace_inherited_dry_run_does_not_write() {
let dir = temp_dir();
write_cargo_toml(
dir.path(),
r#"
[workspace]
members = ["crates/*"]
[workspace.package]
version = "1.0.0"
"#,
);
let member = dir.path().join("crates/member");
std::fs::create_dir_all(&member).unwrap();
write_cargo_toml(
&member,
r#"
[package]
name = "member"
version.workspace = true
"#,
);
let adapter = recording_adapter(CargoConfig::default(), dir.path(), 0);
let info = project_info(dir.path(), "member", "crates/member");
let new_v: semver::Version = "2.0.0".parse().unwrap();
let paths = adapter.write_version(&info, &new_v, true).await.unwrap();
let root_toml = std::fs::read_to_string(dir.path().join("Cargo.toml")).unwrap();
assert!(
root_toml.contains("\"1.0.0\""),
"Dry-run should not update workspace root, got:\n{root_toml}"
);
assert!(
!root_toml.contains("\"2.0.0\""),
"Dry-run should not write new version, got:\n{root_toml}"
);
assert_eq!(paths.len(), 1, "Expected exactly one modified path");
assert_eq!(
paths[0],
dir.path().join("Cargo.toml"),
"Expected workspace root Cargo.toml, got: {:?}",
paths[0]
);
}