aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/mozart/src/commands/bump.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/mozart/src/commands/bump.rs')
-rw-r--r--crates/mozart/src/commands/bump.rs518
1 files changed, 0 insertions, 518 deletions
diff --git a/crates/mozart/src/commands/bump.rs b/crates/mozart/src/commands/bump.rs
index 2d6eea8..2dbd84a 100644
--- a/crates/mozart/src/commands/bump.rs
+++ b/crates/mozart/src/commands/bump.rs
@@ -355,521 +355,3 @@ fn strip_inline_constraint(arg: &str) -> &str {
.map(|pos| &arg[..pos])
.unwrap_or(arg)
}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use mozart_core::repository::lockfile::{LockFile, LockedPackage};
- use tempfile::tempdir;
-
- fn minimal_lock(packages: Vec<LockedPackage>, packages_dev: Vec<LockedPackage>) -> LockFile {
- LockFile {
- readme: LockFile::default_readme(),
- content_hash: "placeholder".to_string(),
- packages,
- packages_dev: Some(packages_dev),
- aliases: vec![],
- minimum_stability: "stable".to_string(),
- stability_flags: serde_json::json!({}),
- prefer_stable: false,
- prefer_lowest: false,
- platform: serde_json::json!({}),
- platform_dev: serde_json::json!({}),
- plugin_api_version: Some("2.6.0".to_string()),
- }
- }
-
- fn make_locked_package(name: &str, version: &str) -> LockedPackage {
- LockedPackage {
- name: name.to_string(),
- version: version.to_string(),
- version_normalized: Some(format!("{version}.0")),
- source: None,
- dist: None,
- require: indexmap::IndexMap::new(),
- require_dev: indexmap::IndexMap::new(),
- conflict: indexmap::IndexMap::new(),
- provide: indexmap::IndexMap::new(),
- replace: indexmap::IndexMap::new(),
- suggest: None,
- package_type: None,
- autoload: None,
- autoload_dev: None,
- license: None,
- description: None,
- homepage: None,
- keywords: None,
- authors: None,
- support: None,
- funding: None,
- time: None,
- extra_fields: indexmap::IndexMap::new(),
- }
- }
-
- fn write_composer_json(dir: &std::path::Path, content: &str) {
- std::fs::write(dir.join("composer.json"), content).unwrap();
- }
-
- fn write_lock_with_hash(dir: &std::path::Path, mut lock: LockFile, composer_json: &str) {
- let hash = LockFile::compute_content_hash(composer_json).unwrap();
- lock.content_hash = hash;
- lock.write_to_file(&dir.join("composer.lock")).unwrap();
- }
-
- fn make_cli(working_dir: &std::path::Path) -> super::super::Cli {
- super::super::Cli {
- command: Some(super::super::Commands::Bump(BumpArgs {
- packages: vec![],
- dev_only: false,
- no_dev_only: false,
- dry_run: false,
- })),
- version: false,
- verbose: 0,
- profile: false,
- no_plugins: false,
- no_scripts: false,
- working_dir: Some(working_dir.to_str().unwrap().to_string()),
- no_cache: false,
- no_interaction: false,
- quiet: false,
- ansi: false,
- no_ansi: false,
- }
- }
-
- fn quiet_io() -> std::sync::Arc<std::sync::Mutex<Box<dyn IoInterface>>> {
- std::sync::Arc::new(std::sync::Mutex::new(
- Box::new(mozart_core::console::Console::new(
- 0, false, false, false, false,
- )) as Box<dyn IoInterface>,
- ))
- }
-
- #[tokio::test]
- async fn test_basic_bump_modifies_composer_json() {
- let dir = tempdir().unwrap();
- let composer_json = r#"{
- "name": "test/project",
- "type": "project",
- "require": {
- "psr/log": "^1.0"
- }
-}"#;
- write_composer_json(dir.path(), composer_json);
-
- let lock = minimal_lock(vec![make_locked_package("psr/log", "1.1.4")], vec![]);
- write_lock_with_hash(dir.path(), lock, composer_json);
-
- let args = BumpArgs {
- packages: vec![],
- dev_only: false,
- no_dev_only: false,
- dry_run: false,
- };
- let cli = make_cli(dir.path());
- execute(&args, &cli, quiet_io()).await.unwrap();
-
- let updated = std::fs::read_to_string(dir.path().join("composer.json")).unwrap();
- let parsed: serde_json::Value = serde_json::from_str(&updated).unwrap();
- assert_eq!(parsed["require"]["psr/log"], "^1.1.4");
- }
-
- #[tokio::test]
- async fn test_dry_run_does_not_modify_files() {
- let dir = tempdir().unwrap();
- let composer_json = r#"{
- "name": "test/project",
- "type": "project",
- "require": {
- "psr/log": "^1.0"
- }
-}"#;
- write_composer_json(dir.path(), composer_json);
-
- let lock = minimal_lock(vec![make_locked_package("psr/log", "1.1.4")], vec![]);
- write_lock_with_hash(dir.path(), lock, composer_json);
-
- let args = BumpArgs {
- packages: vec![],
- dev_only: false,
- no_dev_only: false,
- dry_run: true,
- };
- let cli = make_cli(dir.path());
- let result = execute(&args, &cli, quiet_io()).await;
-
- // dry-run with changes returns exit code 1 (for CI usage)
- let err = result.unwrap_err();
- let mozart_err = err
- .downcast_ref::<mozart_core::exit_code::MozartError>()
- .expect("should be MozartError");
- assert_eq!(mozart_err.exit_code, mozart_core::exit_code::GENERAL_ERROR);
-
- // composer.json should be unchanged
- let content = std::fs::read_to_string(dir.path().join("composer.json")).unwrap();
- let parsed: serde_json::Value = serde_json::from_str(&content).unwrap();
- assert_eq!(parsed["require"]["psr/log"], "^1.0");
- }
-
- #[tokio::test]
- async fn test_no_changes_when_already_bumped() {
- let dir = tempdir().unwrap();
- let composer_json = r#"{
- "name": "test/project",
- "type": "project",
- "require": {
- "psr/log": "^1.1.4"
- }
-}"#;
- write_composer_json(dir.path(), composer_json);
-
- let lock = minimal_lock(vec![make_locked_package("psr/log", "1.1.4")], vec![]);
- write_lock_with_hash(dir.path(), lock, composer_json);
-
- let args = BumpArgs {
- packages: vec![],
- dev_only: false,
- no_dev_only: false,
- dry_run: false,
- };
- let cli = make_cli(dir.path());
- execute(&args, &cli, quiet_io()).await.unwrap();
-
- // No changes should be made
- let content = std::fs::read_to_string(dir.path().join("composer.json")).unwrap();
- let parsed: serde_json::Value = serde_json::from_str(&content).unwrap();
- assert_eq!(parsed["require"]["psr/log"], "^1.1.4");
- }
-
- #[tokio::test]
- async fn test_dev_only_flag_only_bumps_require_dev() {
- let dir = tempdir().unwrap();
- let composer_json = r#"{
- "name": "test/project",
- "type": "project",
- "require": {
- "psr/log": "^1.0"
- },
- "require-dev": {
- "phpunit/phpunit": "^9.0"
- }
-}"#;
- write_composer_json(dir.path(), composer_json);
-
- let lock = minimal_lock(
- vec![make_locked_package("psr/log", "1.1.4")],
- vec![make_locked_package("phpunit/phpunit", "9.5.0")],
- );
- write_lock_with_hash(dir.path(), lock, composer_json);
-
- let args = BumpArgs {
- packages: vec![],
- dev_only: true,
- no_dev_only: false,
- dry_run: false,
- };
- let cli = make_cli(dir.path());
- execute(&args, &cli, quiet_io()).await.unwrap();
-
- let content = std::fs::read_to_string(dir.path().join("composer.json")).unwrap();
- let parsed: serde_json::Value = serde_json::from_str(&content).unwrap();
- // require should NOT be bumped
- assert_eq!(parsed["require"]["psr/log"], "^1.0");
- // require-dev should be bumped
- assert_eq!(parsed["require-dev"]["phpunit/phpunit"], "^9.5");
- }
-
- #[tokio::test]
- async fn test_no_dev_only_flag_only_bumps_require() {
- let dir = tempdir().unwrap();
- let composer_json = r#"{
- "name": "test/project",
- "type": "project",
- "require": {
- "psr/log": "^1.0"
- },
- "require-dev": {
- "phpunit/phpunit": "^9.0"
- }
-}"#;
- write_composer_json(dir.path(), composer_json);
-
- let lock = minimal_lock(
- vec![make_locked_package("psr/log", "1.1.4")],
- vec![make_locked_package("phpunit/phpunit", "9.5.0")],
- );
- write_lock_with_hash(dir.path(), lock, composer_json);
-
- let args = BumpArgs {
- packages: vec![],
- dev_only: false,
- no_dev_only: true,
- dry_run: false,
- };
- let cli = make_cli(dir.path());
- execute(&args, &cli, quiet_io()).await.unwrap();
-
- let content = std::fs::read_to_string(dir.path().join("composer.json")).unwrap();
- let parsed: serde_json::Value = serde_json::from_str(&content).unwrap();
- // require should be bumped
- assert_eq!(parsed["require"]["psr/log"], "^1.1.4");
- // require-dev should NOT be bumped
- assert_eq!(parsed["require-dev"]["phpunit/phpunit"], "^9.0");
- }
-
- #[tokio::test]
- async fn test_stale_lock_file_produces_exit_code_2() {
- let dir = tempdir().unwrap();
- let composer_json = r#"{
- "name": "test/project",
- "require": {
- "psr/log": "^1.0"
- }
-}"#;
- write_composer_json(dir.path(), composer_json);
-
- // Write lock with a wrong hash (stale)
- let mut lock = minimal_lock(vec![make_locked_package("psr/log", "1.1.4")], vec![]);
- lock.content_hash = "wrong_hash_here".to_string();
- lock.write_to_file(&dir.path().join("composer.lock"))
- .unwrap();
-
- let args = BumpArgs {
- packages: vec![],
- dev_only: false,
- no_dev_only: false,
- dry_run: false,
- };
- let cli = make_cli(dir.path());
- let result = execute(&args, &cli, quiet_io()).await;
-
- // stale lock file should return exit code 2 (ERROR_LOCK_OUTDATED)
- let err = result.unwrap_err();
- let mozart_err = err
- .downcast_ref::<mozart_core::exit_code::MozartError>()
- .expect("should be MozartError");
- assert_eq!(mozart_err.exit_code, ERROR_LOCK_OUTDATED);
- }
-
- #[tokio::test]
- async fn test_lock_file_hash_updated_after_bump() {
- let dir = tempdir().unwrap();
- let composer_json = r#"{
- "name": "test/project",
- "type": "project",
- "require": {
- "psr/log": "^1.0"
- }
-}"#;
- write_composer_json(dir.path(), composer_json);
-
- let lock = minimal_lock(vec![make_locked_package("psr/log", "1.1.4")], vec![]);
- write_lock_with_hash(dir.path(), lock, composer_json);
-
- let args = BumpArgs {
- packages: vec![],
- dev_only: false,
- no_dev_only: false,
- dry_run: false,
- };
- let cli = make_cli(dir.path());
- execute(&args, &cli, quiet_io()).await.unwrap();
-
- // The lock file content-hash should now match the updated composer.json
- let updated_composer = std::fs::read_to_string(dir.path().join("composer.json")).unwrap();
- let updated_lock = LockFile::read_from_file(&dir.path().join("composer.lock")).unwrap();
- assert!(
- updated_lock.is_fresh(&updated_composer),
- "Lock file hash should be updated to match new composer.json"
- );
- }
-
- #[tokio::test]
- async fn test_no_lock_falls_back_to_local_repository() {
- let dir = tempdir().unwrap();
- let composer_json = r#"{
- "name": "test/project",
- "type": "project",
- "require": {
- "psr/log": "^1.0"
- }
-}"#;
- write_composer_json(dir.path(), composer_json);
-
- // No composer.lock — instead populate vendor/composer/installed.json.
- let installed_dir = dir.path().join("vendor/composer");
- std::fs::create_dir_all(&installed_dir).unwrap();
- let installed = serde_json::json!({
- "packages": [
- {
- "name": "psr/log",
- "version": "1.1.4",
- "version_normalized": "1.1.4.0",
- }
- ],
- "dev": false,
- });
- std::fs::write(
- installed_dir.join("installed.json"),
- serde_json::to_string_pretty(&installed).unwrap(),
- )
- .unwrap();
-
- let args = BumpArgs {
- packages: vec![],
- dev_only: false,
- no_dev_only: false,
- dry_run: false,
- };
- let cli = make_cli(dir.path());
- execute(&args, &cli, quiet_io()).await.unwrap();
-
- let content = std::fs::read_to_string(dir.path().join("composer.json")).unwrap();
- let parsed: serde_json::Value = serde_json::from_str(&content).unwrap();
- assert_eq!(parsed["require"]["psr/log"], "^1.1.4");
- }
-
- #[test]
- fn test_strip_inline_constraint_colon() {
- assert_eq!(strip_inline_constraint("vendor/pkg:^2.0"), "vendor/pkg");
- }
-
- #[test]
- fn test_strip_inline_constraint_equals() {
- assert_eq!(strip_inline_constraint("vendor/pkg=2.0.0"), "vendor/pkg");
- }
-
- #[test]
- fn test_strip_inline_constraint_space() {
- assert_eq!(strip_inline_constraint("vendor/pkg ^2.0"), "vendor/pkg");
- }
-
- #[test]
- fn test_strip_inline_constraint_no_suffix() {
- assert_eq!(strip_inline_constraint("vendor/pkg"), "vendor/pkg");
- assert_eq!(strip_inline_constraint("psr/log"), "psr/log");
- }
-
- #[tokio::test]
- async fn test_package_filter_only_bumps_specified_packages() {
- let dir = tempdir().unwrap();
- let composer_json = r#"{
- "name": "test/project",
- "type": "project",
- "require": {
- "psr/log": "^1.0",
- "psr/http-message": "^1.0"
- }
-}"#;
- write_composer_json(dir.path(), composer_json);
-
- let lock = minimal_lock(
- vec![
- make_locked_package("psr/log", "1.1.4"),
- make_locked_package("psr/http-message", "1.2.0"),
- ],
- vec![],
- );
- write_lock_with_hash(dir.path(), lock, composer_json);
-
- let args = BumpArgs {
- packages: vec!["psr/log".to_string()],
- dev_only: false,
- no_dev_only: false,
- dry_run: false,
- };
- let cli = make_cli(dir.path());
- execute(&args, &cli, quiet_io()).await.unwrap();
-
- let content = std::fs::read_to_string(dir.path().join("composer.json")).unwrap();
- let parsed: serde_json::Value = serde_json::from_str(&content).unwrap();
- assert_eq!(parsed["require"]["psr/log"], "^1.1.4");
- // psr/http-message should NOT be bumped
- assert_eq!(parsed["require"]["psr/http-message"], "^1.0");
- }
-
- #[tokio::test]
- async fn test_package_filter_glob_wildcard() {
- let dir = tempdir().unwrap();
- let composer_json = r#"{
- "name": "test/project",
- "type": "project",
- "require": {
- "psr/log": "^1.0",
- "psr/container": "^1.0",
- "monolog/monolog": "^2.0"
- }
-}"#;
- write_composer_json(dir.path(), composer_json);
-
- let lock = minimal_lock(
- vec![
- make_locked_package("psr/log", "1.1.4"),
- make_locked_package("psr/container", "1.1.1"),
- make_locked_package("monolog/monolog", "2.9.0"),
- ],
- vec![],
- );
- write_lock_with_hash(dir.path(), lock, composer_json);
-
- // Filter using a wildcard: only bump psr/* packages
- let args = BumpArgs {
- packages: vec!["psr/*".to_string()],
- dev_only: false,
- no_dev_only: false,
- dry_run: false,
- };
- let cli = make_cli(dir.path());
- execute(&args, &cli, quiet_io()).await.unwrap();
-
- let content = std::fs::read_to_string(dir.path().join("composer.json")).unwrap();
- let parsed: serde_json::Value = serde_json::from_str(&content).unwrap();
- // Both psr/* packages should be bumped
- assert_eq!(parsed["require"]["psr/log"], "^1.1.4");
- assert_eq!(parsed["require"]["psr/container"], "^1.1.1");
- // monolog/monolog should NOT be bumped
- assert_eq!(parsed["require"]["monolog/monolog"], "^2.0");
- }
-
- #[tokio::test]
- async fn test_package_filter_with_inline_constraint() {
- let dir = tempdir().unwrap();
- let composer_json = r#"{
- "name": "test/project",
- "type": "project",
- "require": {
- "psr/log": "^1.0",
- "monolog/monolog": "^2.0"
- }
-}"#;
- write_composer_json(dir.path(), composer_json);
-
- let lock = minimal_lock(
- vec![
- make_locked_package("psr/log", "1.1.4"),
- make_locked_package("monolog/monolog", "2.9.0"),
- ],
- vec![],
- );
- write_lock_with_hash(dir.path(), lock, composer_json);
-
- // Specify filter with an inline constraint suffix (Composer-style: "psr/log:^1.0")
- let args = BumpArgs {
- packages: vec!["psr/log:^1.0".to_string()],
- dev_only: false,
- no_dev_only: false,
- dry_run: false,
- };
- let cli = make_cli(dir.path());
- execute(&args, &cli, quiet_io()).await.unwrap();
-
- let content = std::fs::read_to_string(dir.path().join("composer.json")).unwrap();
- let parsed: serde_json::Value = serde_json::from_str(&content).unwrap();
- // psr/log should be bumped (constraint suffix stripped from filter arg)
- assert_eq!(parsed["require"]["psr/log"], "^1.1.4");
- // monolog/monolog should NOT be bumped
- assert_eq!(parsed["require"]["monolog/monolog"], "^2.0");
- }
-}