aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe/tests
diff options
context:
space:
mode:
Diffstat (limited to 'crates/shirabe/tests')
-rw-r--r--crates/shirabe/tests/command/audit_command_test.rs114
-rw-r--r--crates/shirabe/tests/command/check_platform_reqs_command_test.rs172
-rw-r--r--crates/shirabe/tests/command/global_command_test.rs369
-rw-r--r--crates/shirabe/tests/command/remove_command_test.rs957
-rw-r--r--crates/shirabe/tests/command/self_update_command_test.rs54
-rw-r--r--crates/shirabe/tests/command/show_command_test.rs1490
-rw-r--r--crates/shirabe/tests/command/validate_command_test.rs30
7 files changed, 3032 insertions, 154 deletions
diff --git a/crates/shirabe/tests/command/audit_command_test.rs b/crates/shirabe/tests/command/audit_command_test.rs
index 7d585d9..bccc7c2 100644
--- a/crates/shirabe/tests/command/audit_command_test.rs
+++ b/crates/shirabe/tests/command/audit_command_test.rs
@@ -1,25 +1,123 @@
//! ref: composer/tests/Composer/Test/Command/AuditCommandTest.php
+use crate::test_case::{
+ RunOptions, create_composer_lock, create_installed_json, get_application_tester, get_package,
+ init_temp_composer,
+};
+use serial_test::serial;
+use shirabe::package::handle::PackageInterfaceHandle;
+use shirabe_php_shim::PhpMixed;
+
#[test]
-#[ignore = "missing TestCase::init_temp_composer and get_application_tester (ApplicationTester) infrastructure"]
+#[serial]
+#[ignore = "todo!() at crates/shirabe/src/command/audit_command.rs:285 (AuditCommand::get_packages non-locked branch: installed-repo conversion unported)"]
fn test_successful_response_code_when_no_packages_are_required() {
- todo!()
+ let tear_down = init_temp_composer(None, None, None, true);
+
+ let mut app_tester = get_application_tester();
+ app_tester
+ .run(
+ vec![(PhpMixed::from("command"), PhpMixed::from("audit"))],
+ RunOptions::default(),
+ )
+ .unwrap();
+
+ assert_eq!(0, app_tester.get_status_code());
+ assert_eq!(
+ "No packages - skipping audit.",
+ app_tester.get_display().trim()
+ );
+
+ drop(tear_down);
}
#[test]
-#[ignore = "missing TestCase::init_temp_composer, create_installed_json, and get_application_tester (ApplicationTester) infrastructure"]
+#[serial]
fn test_error_auditing_lock_file_when_it_is_missing() {
- todo!()
+ let tear_down = init_temp_composer(None, None, None, true);
+ let packages: Vec<PackageInterfaceHandle> = vec![get_package("dummy/pkg", "1.0.0")];
+ create_installed_json(&packages, &[], true);
+
+ let mut app_tester = get_application_tester();
+ let err = app_tester
+ .run(
+ vec![
+ (PhpMixed::from("command"), PhpMixed::from("audit")),
+ (PhpMixed::from("--locked"), PhpMixed::from(true)),
+ ],
+ RunOptions::default(),
+ )
+ .expect_err("expected UnexpectedValueException");
+ assert!(
+ err.to_string().contains(
+ "Valid composer.json and composer.lock files are required to run this command with --locked"
+ ),
+ "unexpected error: {err}"
+ );
+
+ drop(tear_down);
}
#[test]
-#[ignore = "missing TestCase::init_temp_composer, create_installed_json, create_composer_lock, and get_application_tester (ApplicationTester) infrastructure"]
+#[serial]
+#[ignore = "'RefCell already mutably borrowed' panic at crates/shirabe/src/io/io_interface.rs:215 (write_error3) in the locked-audit advisory-fetch path; not in target file"]
fn test_audit_package_with_no_security_vulnerabilities() {
- todo!()
+ let tear_down = init_temp_composer(None, None, None, true);
+ let packages: Vec<PackageInterfaceHandle> = vec![get_package("dummy/pkg", "1.0.0")];
+ create_installed_json(&packages, &[], true);
+ create_composer_lock(&packages, &[]);
+
+ let mut app_tester = get_application_tester();
+ app_tester
+ .run(
+ vec![
+ (PhpMixed::from("command"), PhpMixed::from("audit")),
+ (PhpMixed::from("--locked"), PhpMixed::from(true)),
+ ],
+ RunOptions::default(),
+ )
+ .unwrap();
+
+ assert!(
+ app_tester
+ .get_display()
+ .trim()
+ .contains("No security vulnerability advisories found."),
+ "unexpected display: {}",
+ app_tester.get_display()
+ );
+
+ drop(tear_down);
}
#[test]
-#[ignore = "missing TestCase::init_temp_composer, create_installed_json, create_composer_lock, and get_application_tester (ApplicationTester) infrastructure"]
+#[serial]
+#[ignore = "todo!() at crates/shirabe/src/command/audit_command.rs:285 (AuditCommand::get_packages non-locked branch: installed-repo conversion unported)"]
fn test_audit_package_with_no_dev_option_passed() {
- todo!()
+ let tear_down = init_temp_composer(None, None, None, true);
+ let dev_package: Vec<PackageInterfaceHandle> = vec![get_package("dummy/pkg", "1.0.0")];
+ create_installed_json(&[], &dev_package, true);
+ create_composer_lock(&[], &dev_package);
+
+ let mut app_tester = get_application_tester();
+ app_tester
+ .run(
+ vec![
+ (PhpMixed::from("command"), PhpMixed::from("audit")),
+ (PhpMixed::from("--no-dev"), PhpMixed::from(true)),
+ ],
+ RunOptions::default(),
+ )
+ .unwrap();
+
+ assert!(
+ app_tester
+ .get_display()
+ .trim()
+ .contains("No packages - skipping audit."),
+ "unexpected display: {}",
+ app_tester.get_display()
+ );
+
+ drop(tear_down);
}
diff --git a/crates/shirabe/tests/command/check_platform_reqs_command_test.rs b/crates/shirabe/tests/command/check_platform_reqs_command_test.rs
index 47f89b9..1e19259 100644
--- a/crates/shirabe/tests/command/check_platform_reqs_command_test.rs
+++ b/crates/shirabe/tests/command/check_platform_reqs_command_test.rs
@@ -1,25 +1,173 @@
//! ref: composer/tests/Composer/Test/Command/CheckPlatformReqsCommandTest.php
+use crate::test_case::{
+ RunOptions, create_composer_lock, create_installed_json, get_application_tester,
+ get_complete_package, init_temp_composer,
+};
+use serial_test::serial;
+use shirabe::package::handle::PackageInterfaceHandle;
+use shirabe_php_shim::PhpMixed;
+
+/// ref: CheckPlatformReqsCommandTest::testPlatformReqsAreSatisfied (data provider rolled into one body).
+fn run_platform_reqs_are_satisfied_case(
+ composer_json: serde_json::Value,
+ command: &[(&str, PhpMixed)],
+ expected: &str,
+ lock: bool,
+) {
+ let tear_down = init_temp_composer(Some(&composer_json), None, None, false);
+
+ let packages: Vec<PackageInterfaceHandle> =
+ vec![get_complete_package("ext-foobar", "2.3.4").into()];
+ let dev_packages: Vec<PackageInterfaceHandle> =
+ vec![get_complete_package("ext-barbaz", "2.3.4.5").into()];
+
+ create_installed_json(&packages, &dev_packages, true);
+
+ if lock {
+ create_composer_lock(&packages, &dev_packages);
+ }
+
+ let mut input: Vec<(PhpMixed, PhpMixed)> = vec![(
+ PhpMixed::from("command"),
+ PhpMixed::from("check-platform-reqs"),
+ )];
+ for (k, v) in command {
+ input.push((PhpMixed::from(*k), v.clone()));
+ }
+
+ let mut app_tester = get_application_tester();
+ let status_code = app_tester.run(input, RunOptions::default()).unwrap();
+
+ // assertCommandIsSuccessful
+ assert_eq!(0, status_code, "{}", app_tester.get_display());
+ assert_eq!(expected.trim(), app_tester.get_display().trim());
+
+ drop(tear_down);
+}
+
#[test]
-#[ignore = "needs create_installed_json / create_composer_lock helpers (InstalledFilesystemRepository \
- / Locker::set_lock_data), and require_composer -> Factory reaches ProcessExecutor (git) \
- -> shirabe-php-shim stream_set_blocking (stream.rs todo!(), requires fcntl(2))"]
+#[serial]
+#[ignore = "src porting bug: check_platform_reqs_command.rs:288-294 wraps the existing \
+ InstalledRepository inside a SECOND InstalledRepository (instead of PHP line 89 \
+ `$installedRepo->addRepository(new PlatformRepository(...))`), tripping the \
+ add_repository assertion at installed_repository.rs:367 (\"An InstalledRepository can \
+ not contain a repository of type installed repo ...\"). Cannot fix from test files."]
fn test_platform_reqs_are_satisfied() {
- todo!()
+ // 'Disables checking of require-dev packages requirements.'
+ run_platform_reqs_are_satisfied_case(
+ serde_json::json!({
+ "require": { "ext-foobar": "^2.0" },
+ "require-dev": { "ext-barbaz": "~4.0" },
+ }),
+ &[("--no-dev", PhpMixed::from(true))],
+ "Checking non-dev platform requirements for packages in the vendor dir
+ext-foobar 2.3.4 success",
+ true,
+ );
+
+ // 'Checks requirements only from the lock file, not from installed packages.'
+ run_platform_reqs_are_satisfied_case(
+ serde_json::json!({
+ "require": { "ext-foobar": "^2.3" },
+ "require-dev": { "ext-barbaz": "~2.0" },
+ }),
+ &[("--lock", PhpMixed::from(true))],
+ "Checking platform requirements using the lock file\next-barbaz 2.3.4.5 success \next-foobar 2.3.4 success",
+ true,
+ );
}
#[test]
-#[ignore = "check-platform-reqs calls require_composer -> Factory, reaching ProcessExecutor (git) \
- -> shirabe-php-shim stream_set_blocking (stream.rs todo!(), requires fcntl(2)) before \
- the missing-lockfile LogicException can be thrown"]
+#[serial]
fn test_exception_thrown_if_no_lockfile_found() {
- todo!()
+ let tear_down = init_temp_composer(Some(&serde_json::json!({})), None, None, false);
+
+ let mut app_tester = get_application_tester();
+ let err = app_tester
+ .run(
+ vec![(
+ PhpMixed::from("command"),
+ PhpMixed::from("check-platform-reqs"),
+ )],
+ RunOptions::default(),
+ )
+ .expect_err("expected LogicException for missing lockfile");
+ assert!(
+ err.to_string()
+ .contains("No lockfile found. Unable to read locked packages"),
+ "got: {}",
+ err
+ );
+
+ drop(tear_down);
}
#[test]
-#[ignore = "needs create_installed_json / create_composer_lock helpers (InstalledFilesystemRepository \
- / Locker::set_lock_data), and require_composer -> Factory reaches ProcessExecutor (git) \
- -> shirabe-php-shim stream_set_blocking (stream.rs todo!(), requires fcntl(2))"]
+#[serial]
+#[ignore = "src porting bug: check_platform_reqs_command.rs:288-294 wraps the existing \
+ InstalledRepository inside a SECOND InstalledRepository (instead of PHP line 89 \
+ `$installedRepo->addRepository(new PlatformRepository(...))`), tripping the \
+ add_repository assertion at installed_repository.rs:367 (\"An InstalledRepository can \
+ not contain a repository of type installed repo ...\"). Cannot fix from test files."]
fn test_failed_platform_requirement() {
- todo!()
+ let tear_down = init_temp_composer(
+ Some(&serde_json::json!({
+ "require": { "ext-foobar": "^0.3" },
+ "require-dev": { "ext-barbaz": "^2.3" },
+ })),
+ None,
+ None,
+ false,
+ );
+
+ let packages: Vec<PackageInterfaceHandle> =
+ vec![get_complete_package("ext-foobar", "2.3.4").into()];
+ let dev_packages: Vec<PackageInterfaceHandle> =
+ vec![get_complete_package("ext-barbaz", "2.3.4.5").into()];
+
+ create_installed_json(&packages, &dev_packages, true);
+
+ create_composer_lock(&packages, &dev_packages);
+
+ let mut app_tester = get_application_tester();
+ app_tester
+ .run(
+ vec![
+ (
+ PhpMixed::from("command"),
+ PhpMixed::from("check-platform-reqs"),
+ ),
+ (PhpMixed::from("--format"), PhpMixed::from("json")),
+ ],
+ RunOptions::default(),
+ )
+ .unwrap();
+
+ let expected = r#"Checking platform requirements for packages in the vendor dir
+[
+ {
+ "name": "ext-barbaz",
+ "version": "2.3.4.5",
+ "status": "success",
+ "failed_requirement": null,
+ "provider": null
+ },
+ {
+ "name": "ext-foobar",
+ "version": "2.3.4",
+ "status": "failed",
+ "failed_requirement": {
+ "source": "__root__",
+ "type": "requires",
+ "target": "ext-foobar",
+ "constraint": "^0.3"
+ },
+ "provider": null
+ }
+]"#;
+
+ assert_eq!(expected.trim(), app_tester.get_display().trim());
+
+ drop(tear_down);
}
diff --git a/crates/shirabe/tests/command/global_command_test.rs b/crates/shirabe/tests/command/global_command_test.rs
index 0b62150..6eec017 100644
--- a/crates/shirabe/tests/command/global_command_test.rs
+++ b/crates/shirabe/tests/command/global_command_test.rs
@@ -1,80 +1,385 @@
//! ref: composer/tests/Composer/Test/Command/GlobalCommandTest.php
+use crate::test_case::{
+ RunOptions, create_composer_lock, create_installed_json, get_application_tester,
+ get_complete_package, get_package, init_temp_composer,
+};
+use serial_test::serial;
use shirabe::util::platform::Platform;
+use shirabe_php_shim::PhpMixed;
+use std::path::PathBuf;
+use tempfile::TempDir;
-fn tear_down() {
- Platform::clear_env("COMPOSER_HOME");
- Platform::clear_env("COMPOSER");
-}
-
-struct TearDown;
+/// ref: GlobalCommandTest::tearDown (the COMPOSER part; COMPOSER_HOME is restored by the
+/// `init_temp_composer` guard).
+struct EnvGuard;
-impl Drop for TearDown {
+impl Drop for EnvGuard {
fn drop(&mut self) {
- tear_down();
+ Platform::clear_env("COMPOSER");
}
}
+/// ref: TestCase::getUniqueTmpDirectory followed by `chdir($dir)`.
+///
+/// Returns the temp dir guard (kept alive by the caller so it is removed at end of scope).
+fn chdir_unique_tmp_directory() -> TempDir {
+ let test_dir = TempDir::new().unwrap();
+ std::env::set_current_dir(test_dir.path()).unwrap();
+ test_dir
+}
+
#[test]
-#[ignore = "requires ApplicationTester and TestCase::initTempComposer/getUniqueTmpDirectory harness (not implemented)"]
+#[serial]
+#[ignore = "GlobalCommand::run proxies to Application::run via todo!() at crates/shirabe/src/command/global_command.rs:190 (pending shared Application handle)"]
fn test_global() {
- let _tear_down = TearDown;
+ let _env_guard = EnvGuard;
+
+ let script = r#"@php -r "echo 'COMPOSER SCRIPT OUTPUT: '.getenv('COMPOSER') . PHP_EOL;""#;
+ let fake_composer = "TMP_COMPOSER.JSON";
+ let tear_down = init_temp_composer(
+ Some(&serde_json::json!({
+ "scripts": {
+ "test-script": script,
+ },
+ })),
+ None,
+ None,
+ true,
+ );
+ let composer_home: PathBuf = tear_down.working_dir();
+ let composer_home_str = composer_home.to_string_lossy().to_string();
- todo!()
+ Platform::put_env("COMPOSER_HOME", &composer_home_str);
+ Platform::put_env("COMPOSER", fake_composer);
+
+ let _test_dir = chdir_unique_tmp_directory();
+
+ let mut app_tester = get_application_tester();
+ let _ = app_tester.run(
+ vec![
+ (PhpMixed::from("command"), PhpMixed::from("global")),
+ (
+ PhpMixed::from("command-name"),
+ PhpMixed::from("test-script"),
+ ),
+ (PhpMixed::from("--no-interaction"), PhpMixed::from(true)),
+ ],
+ RunOptions::default(),
+ );
+
+ let display = app_tester.get_display();
+
+ assert_eq!(
+ format!(
+ "Changed current directory to {}\nCOMPOSER SCRIPT OUTPUT: \n",
+ composer_home_str
+ ),
+ display
+ );
+
+ drop(tear_down);
}
#[test]
-#[ignore = "requires ApplicationTester and TestCase::getUniqueTmpDirectory harness (not implemented)"]
+#[serial]
fn test_cannot_create_home() {
- let _tear_down = TearDown;
+ let _env_guard = EnvGuard;
+
+ let dir = TempDir::new().unwrap();
+ let filename = dir.path().join("file");
+ std::fs::write(&filename, "").unwrap();
+ let filename_str = filename.to_string_lossy().to_string();
- todo!()
+ Platform::put_env("COMPOSER_HOME", &filename_str);
+
+ let mut app_tester = get_application_tester();
+ let err = app_tester
+ .run(
+ vec![
+ (PhpMixed::from("command"), PhpMixed::from("global")),
+ (
+ PhpMixed::from("command-name"),
+ PhpMixed::from("test-script"),
+ ),
+ (PhpMixed::from("--no-interaction"), PhpMixed::from(true)),
+ ],
+ RunOptions::default(),
+ )
+ .expect_err("expected RuntimeException for a non-directory COMPOSER_HOME");
+
+ assert!(
+ err.to_string()
+ .contains(&format!("{} exists and is not a directory.", filename_str)),
+ "got: {}",
+ err
+ );
+
+ Platform::clear_env("COMPOSER_HOME");
}
#[test]
-#[ignore = "requires ApplicationTester and TestCase::initTempComposer/createInstalledJson harness (not implemented)"]
+#[serial]
+#[ignore = "GlobalCommand::run proxies to Application::run via todo!() at crates/shirabe/src/command/global_command.rs:190 (pending shared Application handle)"]
fn test_global_show() {
- let _tear_down = TearDown;
+ let _env_guard = EnvGuard;
+
+ let tear_down = init_temp_composer(
+ Some(&serde_json::json!({
+ "repositories": {
+ "packages": {
+ "type": "package",
+ "package": [
+ {"name": "vendor/global-tool", "version": "1.0.0"},
+ ],
+ },
+ },
+ "require": {
+ "vendor/global-tool": "1.0.0",
+ },
+ })),
+ None,
+ None,
+ true,
+ );
+ let composer_home_str = tear_down.working_dir().to_string_lossy().to_string();
+
+ let pkg = get_complete_package("vendor/global-tool", "1.0.0");
+ pkg.set_description("A globally installed tool".to_string());
+ create_installed_json(&[pkg.into()], &[], true);
- todo!()
+ Platform::put_env("COMPOSER_HOME", &composer_home_str);
+
+ let _test_dir = chdir_unique_tmp_directory();
+
+ let mut app_tester = get_application_tester();
+ app_tester.set_inputs(vec!["".to_string()]);
+ let _ = app_tester.run(
+ vec![
+ (PhpMixed::from("command"), PhpMixed::from("global")),
+ (PhpMixed::from("command-name"), PhpMixed::from("show")),
+ ],
+ RunOptions::default(),
+ );
+
+ let output = app_tester.get_display();
+ assert!(output.contains("vendor/global-tool"), "got: {}", output);
+ assert!(output.contains("1.0.0"), "got: {}", output);
+
+ drop(tear_down);
}
#[test]
-#[ignore = "requires ApplicationTester and TestCase::initTempComposer/createInstalledJson harness (not implemented)"]
+#[serial]
+#[ignore = "GlobalCommand::run proxies to Application::run via todo!() at crates/shirabe/src/command/global_command.rs:190 (pending shared Application handle)"]
fn test_global_show_without_packages() {
- let _tear_down = TearDown;
+ let _env_guard = EnvGuard;
+
+ let tear_down = init_temp_composer(None, None, None, true);
+ let composer_home_str = tear_down.working_dir().to_string_lossy().to_string();
+
+ create_installed_json(&[], &[], true);
- todo!()
+ Platform::put_env("COMPOSER_HOME", &composer_home_str);
+
+ let _test_dir = chdir_unique_tmp_directory();
+
+ let mut app_tester = get_application_tester();
+ app_tester.set_inputs(vec!["".to_string()]);
+ let status_code = app_tester
+ .run(
+ vec![
+ (PhpMixed::from("command"), PhpMixed::from("global")),
+ (PhpMixed::from("command-name"), PhpMixed::from("show")),
+ ],
+ RunOptions::default(),
+ )
+ .unwrap();
+
+ assert_eq!(0, status_code);
+
+ drop(tear_down);
}
#[test]
-#[ignore = "requires ApplicationTester and TestCase::initTempComposer harness (not implemented)"]
+#[serial]
+#[ignore = "GlobalCommand::run proxies to Application::run via todo!() at crates/shirabe/src/command/global_command.rs:190 (pending shared Application handle)"]
fn test_global_require() {
- let _tear_down = TearDown;
+ let _env_guard = EnvGuard;
+
+ let tear_down = init_temp_composer(
+ Some(&serde_json::json!({
+ "repositories": {
+ "packages": {
+ "type": "package",
+ "package": [
+ {
+ "name": "vendor/required-pkg",
+ "version": "2.0.0",
+ "dist": {"type": "file", "url": file!()},
+ },
+ ],
+ },
+ },
+ })),
+ None,
+ None,
+ true,
+ );
+ let composer_home_str = tear_down.working_dir().to_string_lossy().to_string();
+
+ Platform::put_env("COMPOSER_HOME", &composer_home_str);
- todo!()
+ let _test_dir = chdir_unique_tmp_directory();
+
+ let mut app_tester = get_application_tester();
+ app_tester.set_inputs(vec!["".to_string()]);
+ let status_code = app_tester
+ .run(
+ vec![
+ (PhpMixed::from("command"), PhpMixed::from("global")),
+ (PhpMixed::from("command-name"), PhpMixed::from("require")),
+ (
+ PhpMixed::from("packages"),
+ PhpMixed::List(vec![PhpMixed::from("vendor/required-pkg:2.0.0")]),
+ ),
+ ],
+ RunOptions::default(),
+ )
+ .unwrap();
+
+ assert_eq!(0, status_code);
+ assert!(
+ app_tester
+ .get_display()
+ .contains("Installing vendor/required-pkg"),
+ "got: {}",
+ app_tester.get_display()
+ );
+
+ drop(tear_down);
}
#[test]
-#[ignore = "requires ApplicationTester and TestCase::initTempComposer/createInstalledJson/createComposerLock harness (not implemented)"]
+#[serial]
+#[ignore = "GlobalCommand::run proxies to Application::run via todo!() at crates/shirabe/src/command/global_command.rs:190 (pending shared Application handle)"]
fn test_global_update() {
- let _tear_down = TearDown;
+ let _env_guard = EnvGuard;
+
+ let tear_down = init_temp_composer(
+ Some(&serde_json::json!({
+ "repositories": {
+ "packages": {
+ "type": "package",
+ "package": [
+ {"name": "vendor/pkg", "version": "1.0.0"},
+ ],
+ },
+ },
+ "require": {
+ "vendor/pkg": "1.0.0",
+ },
+ })),
+ None,
+ None,
+ true,
+ );
+ let composer_home_str = tear_down.working_dir().to_string_lossy().to_string();
+
+ let pkg = get_package("vendor/pkg", "1.0.0");
+ create_installed_json(&[pkg.clone()], &[], true);
+ create_composer_lock(&[pkg], &[]);
+
+ Platform::put_env("COMPOSER_HOME", &composer_home_str);
- todo!()
+ let _test_dir = chdir_unique_tmp_directory();
+
+ let mut app_tester = get_application_tester();
+ app_tester.set_inputs(vec!["".to_string()]);
+ let status_code = app_tester
+ .run(
+ vec![
+ (PhpMixed::from("command"), PhpMixed::from("global")),
+ (PhpMixed::from("command-name"), PhpMixed::from("update")),
+ ],
+ RunOptions::default(),
+ )
+ .unwrap();
+
+ assert_eq!(0, status_code);
+
+ drop(tear_down);
}
#[test]
-#[ignore = "requires ApplicationTester and TestCase::initTempComposer harness (not implemented)"]
+#[serial]
+#[ignore = "GlobalCommand::run proxies to Application::run via todo!() at crates/shirabe/src/command/global_command.rs:190 (pending shared Application handle)"]
fn test_global_changes_directory() {
- let _tear_down = TearDown;
+ let _env_guard = EnvGuard;
+
+ let tear_down = init_temp_composer(
+ Some(&serde_json::json!({
+ "name": "test/global",
+ })),
+ None,
+ None,
+ true,
+ );
+ let composer_home_str = tear_down.working_dir().to_string_lossy().to_string();
+
+ Platform::put_env("COMPOSER_HOME", &composer_home_str);
+
+ let _test_dir = chdir_unique_tmp_directory();
- todo!()
+ let mut app_tester = get_application_tester();
+ app_tester.set_inputs(vec!["".to_string()]);
+ let _ = app_tester.run(
+ vec![
+ (PhpMixed::from("command"), PhpMixed::from("global")),
+ (PhpMixed::from("command-name"), PhpMixed::from("config")),
+ (PhpMixed::from("setting-key"), PhpMixed::from("name")),
+ ],
+ RunOptions::default(),
+ );
+
+ let output = app_tester.get_display();
+ assert!(
+ output.contains(&format!(
+ "Changed current directory to {}",
+ composer_home_str
+ )),
+ "got: {}",
+ output
+ );
+
+ drop(tear_down);
}
#[test]
-#[ignore = "requires ApplicationTester and TestCase::initTempComposer harness (not implemented)"]
+#[serial]
fn test_global_missing_command_name() {
- let _tear_down = TearDown;
+ let _env_guard = EnvGuard;
+
+ let tear_down = init_temp_composer(None, None, None, true);
+ let composer_home_str = tear_down.working_dir().to_string_lossy().to_string();
+
+ Platform::put_env("COMPOSER_HOME", &composer_home_str);
+
+ let mut app_tester = get_application_tester();
+ app_tester.set_inputs(vec!["".to_string()]);
+ let err = app_tester
+ .run(
+ vec![(PhpMixed::from("command"), PhpMixed::from("global"))],
+ RunOptions::default(),
+ )
+ .expect_err("expected a RuntimeException for the missing command-name argument");
+
+ assert!(
+ err.to_string()
+ .contains("Not enough arguments (missing: \"command-name\")"),
+ "got: {}",
+ err
+ );
- todo!()
+ drop(tear_down);
}
diff --git a/crates/shirabe/tests/command/remove_command_test.rs b/crates/shirabe/tests/command/remove_command_test.rs
index 2ea8c88..3fc125b 100644
--- a/crates/shirabe/tests/command/remove_command_test.rs
+++ b/crates/shirabe/tests/command/remove_command_test.rs
@@ -1,97 +1,990 @@
//! ref: composer/tests/Composer/Test/Command/RemoveCommandTest.php
-#[ignore = "missing TestCase::get_application_tester (ApplicationTester) infrastructure"]
+use crate::test_case::{
+ RunOptions, create_composer_lock, create_installed_json, get_application_tester, get_package,
+ init_temp_composer,
+};
+use indexmap::IndexMap;
+use serial_test::serial;
+use shirabe::json::JsonFile;
+use shirabe::package::Link;
+use shirabe::package::handle::PackageInterfaceHandle;
+use shirabe_php_shim::PhpMixed;
+use shirabe_semver::constraint::{AnyConstraint, MatchAllConstraint};
+
+// Symfony\Component\Console\Command\Command exit codes.
+const SUCCESS: i32 = 0;
+const FAILURE: i32 = 1;
+const INVALID: i32 = 2;
+
+fn input(pairs: Vec<(&str, PhpMixed)>) -> Vec<(PhpMixed, PhpMixed)> {
+ pairs
+ .into_iter()
+ .map(|(k, v)| (PhpMixed::from(k), v))
+ .collect()
+}
+
+/// Read a JSON file in the CWD and decode it to a `serde_json::Value`.
+fn read_json_file(path: &str) -> serde_json::Value {
+ let mut json = JsonFile::new(path.to_string(), None, None).unwrap();
+ let read = json.read().unwrap();
+ serde_json::from_str(&JsonFile::encode(&read)).unwrap()
+}
+
+/// Mirrors PHPUnit's assertEmpty for a decoded JSON document (empty object/array/null).
+fn is_empty_json(value: &serde_json::Value) -> bool {
+ match value {
+ serde_json::Value::Null => true,
+ serde_json::Value::Object(map) => map.is_empty(),
+ serde_json::Value::Array(list) => list.is_empty(),
+ _ => false,
+ }
+}
+
#[test]
+#[serial]
fn test_exception_running_with_no_remove_packages() {
- todo!()
+ let mut app_tester = get_application_tester();
+ let err = app_tester
+ .run(
+ input(vec![("command", PhpMixed::from("remove"))]),
+ RunOptions::default(),
+ )
+ .expect_err("expected InvalidArgumentException for missing packages argument");
+ assert!(
+ err.to_string()
+ .contains(r#"Not enough arguments (missing: "packages")."#),
+ "got: {}",
+ err
+ );
}
-#[ignore = "missing TestCase::init_temp_composer and get_application_tester (ApplicationTester) infrastructure"]
#[test]
+#[serial]
fn test_exception_when_running_unused_without_lock_file() {
- todo!()
+ let tear_down = init_temp_composer(None, None, None, true);
+
+ let mut app_tester = get_application_tester();
+ let err = app_tester
+ .run(
+ input(vec![
+ ("command", PhpMixed::from("remove")),
+ ("--unused", PhpMixed::from(true)),
+ ]),
+ RunOptions::default(),
+ )
+ .expect_err("expected UnexpectedValueException when --unused without lock file");
+ assert!(
+ err.to_string()
+ .contains("A valid composer.lock file is required to run this command with --unused"),
+ "got: {}",
+ err
+ );
+
+ drop(tear_down);
}
-#[ignore = "missing TestCase::init_temp_composer, create_installed_json, and get_application_tester (ApplicationTester) infrastructure"]
+#[ignore = "remove runs post-remove Installer; panics at crates/shirabe/src/composer.rs:500:36 'RefCell already mutably borrowed' (ComposerHandle::borrow during active borrow_mut) in unported install/update path"]
#[test]
+#[serial]
fn test_warning_when_removing_non_existent_package() {
- todo!()
+ let tear_down = init_temp_composer(None, None, None, true);
+ create_installed_json(&[], &[], true);
+
+ let mut app_tester = get_application_tester();
+ let status_code = app_tester
+ .run(
+ input(vec![
+ ("command", PhpMixed::from("remove")),
+ (
+ "packages",
+ PhpMixed::List(vec![PhpMixed::from("vendor1/package1")]),
+ ),
+ ]),
+ RunOptions::default(),
+ )
+ .unwrap();
+ assert_eq!(SUCCESS, status_code);
+ assert!(
+ app_tester.get_display().trim().starts_with(
+ "<warning>vendor1/package1 is not required in your composer.json and has not been removed</warning>"
+ ),
+ "got: {}",
+ app_tester.get_display()
+ );
+
+ drop(tear_down);
}
-#[ignore = "missing TestCase::init_temp_composer and get_application_tester (ApplicationTester) infrastructure"]
#[test]
+#[serial]
fn test_warning_when_removing_package_from_wrong_type() {
- todo!()
+ let tear_down = init_temp_composer(
+ Some(&serde_json::json!({
+ "require": { "root/req": "1.*" },
+ })),
+ None,
+ None,
+ true,
+ );
+
+ let mut app_tester = get_application_tester();
+ let status_code = app_tester
+ .run(
+ input(vec![
+ ("command", PhpMixed::from("remove")),
+ ("packages", PhpMixed::List(vec![PhpMixed::from("root/req")])),
+ ("--dev", PhpMixed::from(true)),
+ ("--no-update", PhpMixed::from(true)),
+ ("--no-interaction", PhpMixed::from(true)),
+ ]),
+ RunOptions::default(),
+ )
+ .unwrap();
+ assert_eq!(SUCCESS, status_code);
+ assert_eq!(
+ "<warning>root/req could not be found in require-dev but it is present in require</warning>
+./composer.json has been updated",
+ app_tester.get_display().trim()
+ );
+ assert_eq!(
+ serde_json::json!({ "require": { "root/req": "1.*" } }),
+ read_json_file("./composer.json")
+ );
+
+ drop(tear_down);
}
-#[ignore = "missing TestCase::init_temp_composer and get_application_tester (ApplicationTester) infrastructure"]
#[test]
+#[serial]
fn test_warning_when_removing_package_with_deprecated_dependencies_flag() {
- todo!()
+ let tear_down = init_temp_composer(
+ Some(&serde_json::json!({
+ "require": { "root/req": "1.*" },
+ })),
+ None,
+ None,
+ true,
+ );
+
+ let mut app_tester = get_application_tester();
+ let status_code = app_tester
+ .run(
+ input(vec![
+ ("command", PhpMixed::from("remove")),
+ ("packages", PhpMixed::List(vec![PhpMixed::from("root/req")])),
+ ("--update-with-dependencies", PhpMixed::from(true)),
+ ("--no-update", PhpMixed::from(true)),
+ ("--no-interaction", PhpMixed::from(true)),
+ ]),
+ RunOptions::default(),
+ )
+ .unwrap();
+ assert_eq!(SUCCESS, status_code);
+ assert_eq!(
+ "<warning>You are using the deprecated option \"update-with-dependencies\". This is now default behaviour. The --no-update-with-dependencies option can be used to remove a package without its dependencies.</warning>
+./composer.json has been updated",
+ app_tester.get_display().trim()
+ );
+ assert!(
+ is_empty_json(&read_json_file("./composer.json")),
+ "expected composer.json to be empty, got: {}",
+ read_json_file("./composer.json")
+ );
+
+ drop(tear_down);
}
-#[ignore = "missing TestCase::init_temp_composer, create_installed_json, create_composer_lock, and get_application_tester (ApplicationTester) infrastructure"]
#[test]
+#[serial]
fn test_message_output_when_no_unused_packages_to_remove() {
- todo!()
+ let tear_down = init_temp_composer(
+ Some(&serde_json::json!({
+ "repositories": {
+ "packages": {
+ "type": "package",
+ "package": [
+ {"name": "root/req", "version": "1.0.0", "require": {"nested/req": "^1"}},
+ {"name": "nested/req", "version": "1.1.0"},
+ ],
+ },
+ },
+ "require": { "root/req": "1.*" },
+ })),
+ None,
+ None,
+ true,
+ );
+
+ let required_package = get_package("root/req", "1.0.0");
+ let mut requires = IndexMap::new();
+ requires.insert(
+ "nested/req".to_string(),
+ Link::new(
+ "root/req".to_string(),
+ "nested/req".to_string(),
+ AnyConstraint::MatchAll(MatchAllConstraint::new(None)),
+ Some(Link::TYPE_REQUIRE.to_string()),
+ "^1".to_string(),
+ ),
+ );
+ required_package.__set_requires(requires);
+ let nested_package = get_package("nested/req", "1.1.0");
+
+ let packages: Vec<PackageInterfaceHandle> =
+ vec![required_package.clone(), nested_package.clone()];
+ create_installed_json(&packages, &[], true);
+ create_composer_lock(&packages, &[]);
+
+ let mut app_tester = get_application_tester();
+ let status_code = app_tester
+ .run(
+ input(vec![
+ ("command", PhpMixed::from("remove")),
+ ("--unused", PhpMixed::from(true)),
+ ("--no-audit", PhpMixed::from(true)),
+ ("--no-interaction", PhpMixed::from(true)),
+ ]),
+ RunOptions::default(),
+ )
+ .unwrap();
+ assert_eq!(SUCCESS, status_code);
+ assert_eq!(
+ "No unused packages to remove",
+ app_tester.get_display().trim()
+ );
+
+ drop(tear_down);
}
-#[ignore = "missing TestCase::init_temp_composer, create_installed_json, create_composer_lock, and get_application_tester (ApplicationTester) infrastructure"]
+#[ignore = "remove runs post-remove Installer; panics at crates/shirabe/src/composer.rs:500:36 'RefCell already mutably borrowed' (ComposerHandle::borrow during active borrow_mut) in unported install/update path"]
#[test]
+#[serial]
fn test_remove_unused_package() {
- todo!()
+ let tear_down = init_temp_composer(
+ Some(&serde_json::json!({
+ "repositories": {
+ "packages": {
+ "type": "package",
+ "package": [
+ {"name": "root/req", "version": "1.0.0"},
+ {"name": "not/req", "version": "1.0.0"},
+ ],
+ },
+ },
+ "require": { "root/req": "1.*" },
+ })),
+ None,
+ None,
+ true,
+ );
+
+ let required_package = get_package("root/req", "1.0.0");
+ let extraneous_package = get_package("not/req", "1.0.0");
+
+ create_installed_json(&[required_package.clone()], &[], true);
+ create_composer_lock(&[required_package.clone(), extraneous_package.clone()], &[]);
+
+ let mut app_tester = get_application_tester();
+ let status_code = app_tester
+ .run(
+ input(vec![
+ ("command", PhpMixed::from("remove")),
+ ("--unused", PhpMixed::from(true)),
+ ("--no-audit", PhpMixed::from(true)),
+ ("--no-interaction", PhpMixed::from(true)),
+ ]),
+ RunOptions::default(),
+ )
+ .unwrap();
+ assert_eq!(SUCCESS, status_code);
+ assert!(
+ app_tester.get_display().starts_with(
+ "<warning>not/req is not required in your composer.json and has not been removed</warning>"
+ ),
+ "got: {}",
+ app_tester.get_display()
+ );
+ assert!(
+ app_tester
+ .get_display()
+ .contains("Running composer update not/req"),
+ "got: {}",
+ app_tester.get_display()
+ );
+ assert!(
+ app_tester
+ .get_display()
+ .contains("- Removing not/req (1.0.0)"),
+ "got: {}",
+ app_tester.get_display()
+ );
+
+ drop(tear_down);
}
-#[ignore = "missing TestCase::init_temp_composer, create_installed_json, create_composer_lock, and get_application_tester (ApplicationTester) infrastructure"]
+#[ignore = "remove runs post-remove Installer; panics at crates/shirabe/src/composer.rs:500:36 'RefCell already mutably borrowed' (ComposerHandle::borrow during active borrow_mut) in unported install/update path"]
#[test]
+#[serial]
fn test_remove_package_by_name() {
- todo!()
+ let tear_down = init_temp_composer(
+ Some(&serde_json::json!({
+ "repositories": {
+ "packages": {
+ "type": "package",
+ "package": [
+ {"name": "root/req", "version": "1.0.0", "type": "metapackage"},
+ {"name": "root/another", "version": "1.0.0", "type": "metapackage"},
+ ],
+ },
+ },
+ "require": { "root/req": "1.*", "root/another": "1.*" },
+ })),
+ None,
+ None,
+ true,
+ );
+ let root_req_package = get_package("root/req", "1.0.0");
+ let root_another_package = get_package("root/another", "1.0.0");
+ // Set as a metapackage so that we can do the whole post-remove update & install process without
+ // Composer trying to download them (DownloadManager::getDownloaderForPackage).
+ root_req_package.__set_type("metapackage".to_string());
+ root_another_package.__set_type("metapackage".to_string());
+
+ let packages: Vec<PackageInterfaceHandle> =
+ vec![root_req_package.clone(), root_another_package.clone()];
+ create_installed_json(&packages, &[], true);
+ create_composer_lock(&packages, &[]);
+
+ let mut app_tester = get_application_tester();
+ let status_code = app_tester
+ .run(
+ input(vec![
+ ("command", PhpMixed::from("remove")),
+ ("packages", PhpMixed::List(vec![PhpMixed::from("root/req")])),
+ ("--no-audit", PhpMixed::from(true)),
+ ("--no-interaction", PhpMixed::from(true)),
+ ]),
+ RunOptions::default(),
+ )
+ .unwrap();
+ assert_eq!(SUCCESS, status_code);
+ let display = app_tester.get_display();
+ let trimmed = display.trim();
+ assert!(
+ trimmed.starts_with("./composer.json has been updated"),
+ "got: {}",
+ display
+ );
+ assert!(
+ trimmed.contains("Running composer update root/req"),
+ "got: {}",
+ display
+ );
+ assert!(
+ trimmed.contains("Lock file operations: 0 installs, 0 updates, 1 removal"),
+ "got: {}",
+ display
+ );
+ assert!(
+ trimmed.contains("- Removing root/req (1.0.0)"),
+ "got: {}",
+ display
+ );
+ assert!(
+ trimmed.contains("Package operations: 0 installs, 0 updates, 1 removal"),
+ "got: {}",
+ display
+ );
+ assert_eq!(
+ serde_json::json!({ "root/another": "1.*" }),
+ read_json_file("./composer.json")["require"]
+ );
+ assert_eq!(
+ serde_json::json!([{"name": "root/another", "version": "1.0.0", "type": "metapackage"}]),
+ read_json_file("./composer.lock")["packages"]
+ );
+
+ drop(tear_down);
}
-#[ignore = "missing TestCase::init_temp_composer, create_installed_json, create_composer_lock, and get_application_tester (ApplicationTester) infrastructure"]
+#[ignore = "remove runs post-remove Installer; panics at crates/shirabe/src/composer.rs:500:36 'RefCell already mutably borrowed' (ComposerHandle::borrow during active borrow_mut) in unported install/update path"]
#[test]
+#[serial]
fn test_remove_package_by_name_with_dry_run() {
- todo!()
+ let tear_down = init_temp_composer(
+ Some(&serde_json::json!({
+ "repositories": {
+ "packages": {
+ "type": "package",
+ "package": [
+ {"name": "root/req", "version": "1.0.0", "type": "metapackage"},
+ {"name": "root/another", "version": "1.0.0", "type": "metapackage"},
+ ],
+ },
+ },
+ "require": { "root/req": "1.*", "root/another": "1.*" },
+ })),
+ None,
+ None,
+ true,
+ );
+ let root_req_package = get_package("root/req", "1.0.0");
+ let root_another_package = get_package("root/another", "1.0.0");
+ root_req_package.__set_type("metapackage".to_string());
+ root_another_package.__set_type("metapackage".to_string());
+
+ let packages: Vec<PackageInterfaceHandle> =
+ vec![root_req_package.clone(), root_another_package.clone()];
+ create_installed_json(&packages, &[], true);
+ create_composer_lock(&packages, &[]);
+
+ let mut app_tester = get_application_tester();
+ let status_code = app_tester
+ .run(
+ input(vec![
+ ("command", PhpMixed::from("remove")),
+ ("packages", PhpMixed::List(vec![PhpMixed::from("root/req")])),
+ ("--dry-run", PhpMixed::from(true)),
+ ("--no-audit", PhpMixed::from(true)),
+ ("--no-interaction", PhpMixed::from(true)),
+ ]),
+ RunOptions::default(),
+ )
+ .unwrap();
+ assert_eq!(SUCCESS, status_code);
+ let display = app_tester.get_display();
+ let trimmed = display.trim();
+ assert!(
+ trimmed.contains("./composer.json has been updated"),
+ "got: {}",
+ display
+ );
+ assert!(
+ trimmed.contains("Running composer update root/req"),
+ "got: {}",
+ display
+ );
+ assert!(
+ trimmed.contains("Lock file operations: 0 installs, 0 updates, 1 removal"),
+ "got: {}",
+ display
+ );
+ assert!(
+ trimmed.contains("- Removing root/req (1.0.0)"),
+ "got: {}",
+ display
+ );
+ assert!(
+ trimmed.contains("Package operations: 0 installs, 0 updates, 1 removal"),
+ "got: {}",
+ display
+ );
+ assert_eq!(
+ serde_json::json!({ "root/req": "1.*", "root/another": "1.*" }),
+ read_json_file("./composer.json")["require"]
+ );
+ assert_eq!(
+ serde_json::json!([
+ {"name": "root/another", "version": "1.0.0", "type": "metapackage"},
+ {"name": "root/req", "version": "1.0.0", "type": "metapackage"},
+ ]),
+ read_json_file("./composer.lock")["packages"]
+ );
+
+ drop(tear_down);
}
-#[ignore = "missing TestCase::init_temp_composer, create_installed_json, create_composer_lock, and get_application_tester (ApplicationTester) infrastructure"]
+#[ignore = "remove runs post-remove Installer; panics at crates/shirabe/src/composer.rs:500:36 'RefCell already mutably borrowed' (ComposerHandle::borrow during active borrow_mut) in unported install/update path"]
#[test]
+#[serial]
fn test_remove_allowed_plugin_package_with_no_other_allowed_plugins() {
- todo!()
+ let tear_down = init_temp_composer(
+ Some(&serde_json::json!({
+ "repositories": {
+ "packages": {
+ "type": "package",
+ "package": [
+ {"name": "root/req", "version": "1.0.0", "type": "metapackage"},
+ {"name": "root/another", "version": "1.0.0", "type": "metapackage"},
+ ],
+ },
+ },
+ "require": { "root/req": "1.*", "root/another": "1.*" },
+ "config": { "allow-plugins": { "root/req": true } },
+ })),
+ None,
+ None,
+ true,
+ );
+ let root_req_package = get_package("root/req", "1.0.0");
+ let root_another_package = get_package("root/another", "1.0.0");
+ root_req_package.__set_type("metapackage".to_string());
+ root_another_package.__set_type("metapackage".to_string());
+
+ let packages: Vec<PackageInterfaceHandle> =
+ vec![root_req_package.clone(), root_another_package.clone()];
+ create_installed_json(&packages, &[], true);
+ create_composer_lock(&packages, &[]);
+
+ let mut app_tester = get_application_tester();
+ let status_code = app_tester
+ .run(
+ input(vec![
+ ("command", PhpMixed::from("remove")),
+ ("packages", PhpMixed::List(vec![PhpMixed::from("root/req")])),
+ ("--no-audit", PhpMixed::from(true)),
+ ("--no-interaction", PhpMixed::from(true)),
+ ]),
+ RunOptions::default(),
+ )
+ .unwrap();
+ assert_eq!(SUCCESS, status_code);
+ assert_eq!(
+ serde_json::json!({ "root/another": "1.*" }),
+ read_json_file("./composer.json")["require"]
+ );
+ assert!(
+ is_empty_json(&read_json_file("./composer.json")["config"]),
+ "expected config to be empty, got: {}",
+ read_json_file("./composer.json")["config"]
+ );
+
+ drop(tear_down);
}
-#[ignore = "missing TestCase::init_temp_composer and get_application_tester (ApplicationTester) infrastructure"]
+#[ignore = "remove runs post-remove Installer; panics at crates/shirabe/src/composer.rs:500:36 'RefCell already mutably borrowed' (ComposerHandle::borrow during active borrow_mut) in unported install/update path"]
#[test]
+#[serial]
fn test_remove_allowed_plugin_package_with_other_allowed_plugins() {
- todo!()
+ let tear_down = init_temp_composer(
+ Some(&serde_json::json!({
+ "repositories": {
+ "packages": {
+ "type": "package",
+ "package": [
+ {"name": "root/req", "version": "1.0.0", "type": "metapackage"},
+ {"name": "root/another", "version": "1.0.0", "type": "metapackage"},
+ ],
+ },
+ },
+ "require": { "root/req": "1.*", "root/another": "1.*" },
+ "config": { "allow-plugins": { "root/another": true, "root/req": true } },
+ })),
+ None,
+ None,
+ true,
+ );
+ let root_req_package = get_package("root/req", "1.0.0");
+ let root_another_package = get_package("root/another", "1.0.0");
+ root_req_package.__set_type("metapackage".to_string());
+ root_another_package.__set_type("metapackage".to_string());
+
+ let mut app_tester = get_application_tester();
+ let status_code = app_tester
+ .run(
+ input(vec![
+ ("command", PhpMixed::from("remove")),
+ ("packages", PhpMixed::List(vec![PhpMixed::from("root/req")])),
+ ("--no-audit", PhpMixed::from(true)),
+ ("--no-interaction", PhpMixed::from(true)),
+ ]),
+ RunOptions::default(),
+ )
+ .unwrap();
+ assert_eq!(SUCCESS, status_code);
+ assert_eq!(
+ serde_json::json!({ "root/another": "1.*" }),
+ read_json_file("./composer.json")["require"]
+ );
+ assert_eq!(
+ serde_json::json!({ "allow-plugins": { "root/another": true } }),
+ read_json_file("./composer.json")["config"]
+ );
+
+ drop(tear_down);
}
-#[ignore = "missing TestCase::init_temp_composer, create_installed_json, create_composer_lock, and get_application_tester (ApplicationTester) infrastructure"]
+#[ignore = "remove runs post-remove Installer; panics at crates/shirabe/src/composer.rs:500:36 'RefCell already mutably borrowed' (ComposerHandle::borrow during active borrow_mut) in unported install/update path"]
#[test]
+#[serial]
fn test_remove_packages_by_vendor() {
- todo!()
+ let tear_down = init_temp_composer(
+ Some(&serde_json::json!({
+ "repositories": {
+ "packages": {
+ "type": "package",
+ "package": [
+ {"name": "root/req", "version": "1.0.0"},
+ {"name": "root/another", "version": "1.0.0"},
+ {"name": "another/req", "version": "1.0.0"},
+ ],
+ },
+ },
+ "require": { "root/req": "1.*", "root/another": "1.*", "another/req": "1.*" },
+ })),
+ None,
+ None,
+ true,
+ );
+ let root_req_package = get_package("root/req", "1.0.0");
+ let root_another_package = get_package("root/another", "1.0.0");
+ let another_req_package = get_package("another/req", "1.0.0");
+
+ let packages: Vec<PackageInterfaceHandle> = vec![
+ root_req_package.clone(),
+ root_another_package.clone(),
+ another_req_package.clone(),
+ ];
+ create_installed_json(&packages, &[], true);
+ create_composer_lock(&packages, &[]);
+
+ let mut app_tester = get_application_tester();
+ let status_code = app_tester
+ .run(
+ input(vec![
+ ("command", PhpMixed::from("remove")),
+ ("packages", PhpMixed::List(vec![PhpMixed::from("root/*")])),
+ ("--no-install", PhpMixed::from(true)),
+ ("--no-audit", PhpMixed::from(true)),
+ ("--no-interaction", PhpMixed::from(true)),
+ ]),
+ RunOptions::default(),
+ )
+ .unwrap();
+ assert_eq!(SUCCESS, status_code);
+ let display = app_tester.get_display();
+ assert!(
+ display
+ .trim()
+ .starts_with("./composer.json has been updated"),
+ "got: {}",
+ display
+ );
+ assert!(
+ display.contains("Running composer update root/*"),
+ "got: {}",
+ display
+ );
+ assert!(
+ display.contains("- Removing root/another (1.0.0)"),
+ "got: {}",
+ display
+ );
+ assert!(
+ display.contains("- Removing root/req (1.0.0)"),
+ "got: {}",
+ display
+ );
+ assert!(display.contains("Writing lock file"), "got: {}", display);
+ assert_eq!(
+ serde_json::json!({ "another/req": "1.*" }),
+ read_json_file("./composer.json")["require"]
+ );
+ assert_eq!(
+ serde_json::json!([{"name": "another/req", "version": "1.0.0", "type": "library"}]),
+ read_json_file("./composer.lock")["packages"]
+ );
+
+ drop(tear_down);
}
-#[ignore = "missing TestCase::init_temp_composer, create_installed_json, create_composer_lock, and get_application_tester (ApplicationTester) infrastructure"]
+#[ignore = "remove runs post-remove Installer; panics at crates/shirabe/src/composer.rs:500:36 'RefCell already mutably borrowed' (ComposerHandle::borrow during active borrow_mut) in unported install/update path"]
#[test]
+#[serial]
fn test_remove_packages_by_vendor_with_dry_run() {
- todo!()
+ let tear_down = init_temp_composer(
+ Some(&serde_json::json!({
+ "repositories": {
+ "packages": {
+ "type": "package",
+ "package": [
+ {"name": "root/req", "version": "1.0.0"},
+ {"name": "root/another", "version": "1.0.0"},
+ {"name": "another/req", "version": "1.0.0"},
+ ],
+ },
+ },
+ "require": { "root/req": "1.*", "root/another": "1.*", "another/req": "1.*" },
+ })),
+ None,
+ None,
+ true,
+ );
+ let root_req_package = get_package("root/req", "1.0.0");
+ let root_another_package = get_package("root/another", "1.0.0");
+ let another_req_package = get_package("another/req", "1.0.0");
+
+ let packages: Vec<PackageInterfaceHandle> = vec![
+ root_req_package.clone(),
+ root_another_package.clone(),
+ another_req_package.clone(),
+ ];
+ create_installed_json(&packages, &[], true);
+ create_composer_lock(&packages, &[]);
+
+ let mut app_tester = get_application_tester();
+ app_tester
+ .run(
+ input(vec![
+ ("command", PhpMixed::from("remove")),
+ ("packages", PhpMixed::List(vec![PhpMixed::from("root/*")])),
+ ("--dry-run", PhpMixed::from(true)),
+ ("--no-install", PhpMixed::from(true)),
+ ("--no-audit", PhpMixed::from(true)),
+ ("--no-interaction", PhpMixed::from(true)),
+ ]),
+ RunOptions::default(),
+ )
+ .unwrap();
+ assert_eq!(SUCCESS, app_tester.get_status_code());
+ assert_eq!(
+ "./composer.json has been updated
+Running composer update root/*
+Loading composer repositories with package information
+Updating dependencies
+Lock file operations: 0 installs, 0 updates, 2 removals
+ - Removing root/another (1.0.0)
+ - Removing root/req (1.0.0)",
+ app_tester.get_display().trim()
+ );
+ assert!(
+ !app_tester.get_display().contains("Writing lock file"),
+ "got: {}",
+ app_tester.get_display()
+ );
+ assert_eq!(
+ serde_json::json!({ "root/req": "1.*", "root/another": "1.*", "another/req": "1.*" }),
+ read_json_file("./composer.json")["require"]
+ );
+ assert_eq!(
+ serde_json::json!([
+ {"name": "another/req", "version": "1.0.0", "type": "library"},
+ {"name": "root/another", "version": "1.0.0", "type": "library"},
+ {"name": "root/req", "version": "1.0.0", "type": "library"},
+ ]),
+ read_json_file("./composer.lock")["packages"]
+ );
+
+ drop(tear_down);
}
-#[ignore = "missing TestCase::init_temp_composer and get_application_tester (ApplicationTester) infrastructure"]
#[test]
+#[serial]
fn test_warning_when_removing_packages_by_vendor_from_wrong_type() {
- todo!()
+ let tear_down = init_temp_composer(
+ Some(&serde_json::json!({
+ "require": { "root/req": "1.*", "root/another": "1.*", "another/req": "1.*" },
+ })),
+ None,
+ None,
+ true,
+ );
+
+ let mut app_tester = get_application_tester();
+ let status_code = app_tester
+ .run(
+ input(vec![
+ ("command", PhpMixed::from("remove")),
+ ("packages", PhpMixed::List(vec![PhpMixed::from("root/*")])),
+ ("--dev", PhpMixed::from(true)),
+ ("--no-interaction", PhpMixed::from(true)),
+ ("--no-update", PhpMixed::from(true)),
+ ]),
+ RunOptions::default(),
+ )
+ .unwrap();
+ assert_eq!(SUCCESS, status_code);
+ assert_eq!(
+ "<warning>root/req could not be found in require-dev but it is present in require</warning>
+<warning>root/another could not be found in require-dev but it is present in require</warning>
+./composer.json has been updated",
+ app_tester.get_display().trim()
+ );
+ assert_eq!(
+ serde_json::json!({
+ "require": { "root/req": "1.*", "root/another": "1.*", "another/req": "1.*" }
+ }),
+ read_json_file("./composer.json")
+ );
+
+ drop(tear_down);
}
-#[ignore = "missing TestCase::init_temp_composer, create_installed_json, create_composer_lock, and get_application_tester (ApplicationTester) infrastructure"]
+#[ignore = "remove runs post-remove Installer; panics at crates/shirabe/src/composer.rs:500:36 'RefCell already mutably borrowed' (ComposerHandle::borrow during active borrow_mut) in unported install/update path"]
#[test]
+#[serial]
fn test_package_still_present_error_when_no_install_flag_used() {
- todo!()
+ let tear_down = init_temp_composer(
+ Some(&serde_json::json!({
+ "require": { "root/req": "1.*" },
+ })),
+ None,
+ None,
+ true,
+ );
+ let root_req_package = get_package("root/req", "1.0.0");
+
+ create_installed_json(&[root_req_package.clone()], &[], true);
+ create_composer_lock(&[root_req_package.clone()], &[]);
+
+ let mut app_tester = get_application_tester();
+ let status_code = app_tester
+ .run(
+ input(vec![
+ ("command", PhpMixed::from("remove")),
+ ("packages", PhpMixed::List(vec![PhpMixed::from("root/req")])),
+ ("--no-install", PhpMixed::from(true)),
+ ("--no-audit", PhpMixed::from(true)),
+ ("--no-interaction", PhpMixed::from(true)),
+ ]),
+ RunOptions::default(),
+ )
+ .unwrap();
+ assert_eq!(INVALID, status_code);
+ let display = app_tester.get_display();
+ assert!(
+ display.contains("./composer.json has been updated"),
+ "got: {}",
+ display
+ );
+ assert!(
+ display.contains("Lock file operations: 0 installs, 0 updates, 1 removal"),
+ "got: {}",
+ display
+ );
+ assert!(
+ display.contains("- Removing root/req (1.0.0)"),
+ "got: {}",
+ display
+ );
+ assert!(display.contains("Writing lock file"), "got: {}", display);
+ assert!(
+ display.contains(
+ "Removal failed, root/req is still present, it may be required by another package. See `composer why root/req`"
+ ),
+ "got: {}",
+ display
+ );
+ assert!(
+ is_empty_json(&read_json_file("./composer.json")),
+ "expected composer.json to be empty, got: {}",
+ read_json_file("./composer.json")
+ );
+ assert!(
+ is_empty_json(&read_json_file("./composer.lock")["packages"]),
+ "expected composer.lock packages to be empty, got: {}",
+ read_json_file("./composer.lock")["packages"]
+ );
+ assert_eq!(
+ serde_json::json!([{
+ "name": "root/req",
+ "version": "1.0.0",
+ "version_normalized": "1.0.0.0",
+ "type": "library",
+ "install-path": "../root/req"
+ }]),
+ read_json_file("./vendor/composer/installed.json")["packages"]
+ );
+
+ drop(tear_down);
+}
+
+/// ref: RemoveCommandTest::testUpdateInheritedDependenciesFlagIsPassedToPostRemoveInstaller
+/// (data provider provideInheritedDependenciesUpdateFlag).
+fn run_update_inherited_dependencies_flag_case(
+ install_flag_name: &str,
+ expected_composer_update_command: &str,
+) {
+ let tear_down = init_temp_composer(
+ Some(&serde_json::json!({
+ "repositories": {
+ "packages": {
+ "type": "package",
+ "package": [
+ {"name": "root/req", "version": "1.0.0", "type": "metapackage"},
+ ],
+ },
+ },
+ "require": { "root/req": "1.*" },
+ })),
+ None,
+ None,
+ true,
+ );
+ let root_req_package = get_package("root/req", "1.0.0");
+ root_req_package.__set_type("metapackage".to_string());
+
+ create_installed_json(&[root_req_package.clone()], &[], true);
+ create_composer_lock(&[root_req_package.clone()], &[]);
+
+ let mut app_tester = get_application_tester();
+ let status_code = app_tester
+ .run(
+ input(vec![
+ ("command", PhpMixed::from("remove")),
+ ("packages", PhpMixed::List(vec![PhpMixed::from("root/req")])),
+ (install_flag_name, PhpMixed::from(true)),
+ ("--no-audit", PhpMixed::from(true)),
+ ("--no-interaction", PhpMixed::from(true)),
+ ]),
+ RunOptions::default(),
+ )
+ .unwrap();
+ assert_eq!(SUCCESS, status_code);
+ let display = app_tester.get_display();
+ assert!(
+ display.contains("./composer.json has been updated"),
+ "got: {}",
+ display
+ );
+ assert!(
+ display.contains(expected_composer_update_command),
+ "got: {}",
+ display
+ );
+ assert!(
+ display.contains("Package operations: 0 installs, 0 updates, 1 removal"),
+ "got: {}",
+ display
+ );
+ assert!(
+ display.contains("- Removing root/req (1.0.0)"),
+ "got: {}",
+ display
+ );
+ assert!(display.contains("Writing lock file"), "got: {}", display);
+ assert!(
+ display.contains("Lock file operations: 0 installs, 0 updates, 1 removal"),
+ "got: {}",
+ display
+ );
+ assert!(
+ is_empty_json(&read_json_file("./composer.lock")["packages"]),
+ "expected composer.lock packages to be empty, got: {}",
+ read_json_file("./composer.lock")["packages"]
+ );
+
+ drop(tear_down);
}
-#[ignore = "missing TestCase::init_temp_composer, create_installed_json, create_composer_lock, and get_application_tester (ApplicationTester) infrastructure"]
+#[ignore = "remove runs post-remove Installer; panics at crates/shirabe/src/composer.rs:500:36 'RefCell already mutably borrowed' (ComposerHandle::borrow during active borrow_mut) in unported install/update path"]
#[test]
+#[serial]
fn test_update_inherited_dependencies_flag_is_passed_to_post_remove_installer() {
- todo!()
+ // 'update with all dependencies'
+ run_update_inherited_dependencies_flag_case(
+ "--update-with-all-dependencies",
+ "Running composer update root/req --with-all-dependencies",
+ );
+ // 'with all dependencies'
+ run_update_inherited_dependencies_flag_case(
+ "--with-all-dependencies",
+ "Running composer update root/req --with-all-dependencies",
+ );
+ // 'no update with dependencies'
+ run_update_inherited_dependencies_flag_case(
+ "--no-update-with-dependencies",
+ "Running composer update root/req --with-dependencies",
+ );
}
diff --git a/crates/shirabe/tests/command/self_update_command_test.rs b/crates/shirabe/tests/command/self_update_command_test.rs
index 8e51580..094eb5e 100644
--- a/crates/shirabe/tests/command/self_update_command_test.rs
+++ b/crates/shirabe/tests/command/self_update_command_test.rs
@@ -1,39 +1,67 @@
//! ref: composer/tests/Composer/Test/Command/SelfUpdateCommandTest.php
-/// Returns the path to the copied composer.phar used by the test bodies.
-fn set_up() -> String {
- // Depends on initTempComposer and the composer-test.phar fixture, neither ported yet.
- todo!()
+use crate::test_case::{RunOptions, get_application_tester, init_temp_composer};
+use serial_test::serial;
+use shirabe_php_shim::PhpMixed;
+
+/// ref: SelfUpdateCommandTest::setUp (portable part: initTempComposer; the composer-test.phar copy
+/// is omitted because the phar fixture and Symfony Process are not ported).
+fn set_up() -> crate::test_case::TearDown {
+ init_temp_composer(None, None, None, true)
}
#[test]
-#[ignore = "depends on initTempComposer, composer-test.phar fixture, and Symfony Process to spawn the phar; none ported"]
+#[serial]
+#[ignore = "spawns `new Process([PHP_BINARY, $this->phar, 'self-update'])` running composer-test.phar \
+ over HTTP; requires Symfony Process and the composer-test.phar fixture, neither ported"]
fn test_successful_update() {
- let _phar = set_up();
+ let _tear_down = set_up();
todo!()
}
#[test]
-#[ignore = "depends on initTempComposer, composer-test.phar fixture, and Symfony Process to spawn the phar; none ported"]
+#[serial]
+#[ignore = "spawns `new Process([PHP_BINARY, $this->phar, 'self-update', '2.4.0'])` running \
+ composer-test.phar over HTTP; requires Symfony Process and the composer-test.phar \
+ fixture, neither ported"]
fn test_update_to_specific_version() {
- let _phar = set_up();
+ let _tear_down = set_up();
todo!()
}
#[test]
-#[ignore = "depends on getApplicationTester (ApplicationTester) which is not ported"]
+#[serial]
fn test_update_with_invalid_option_throws_exception() {
- let _phar = set_up();
+ let _tear_down = set_up();
- todo!()
+ let mut app_tester = get_application_tester();
+ let err = app_tester
+ .run(
+ vec![
+ (PhpMixed::from("command"), PhpMixed::from("self-update")),
+ (PhpMixed::from("invalid-option"), PhpMixed::from(true)),
+ ],
+ RunOptions::default(),
+ )
+ .expect_err("expected InvalidArgumentException for the unknown argument");
+
+ assert!(
+ err.to_string()
+ .contains("The \"invalid-option\" argument does not exist."),
+ "expected error about unknown argument, got: {:?}",
+ err,
+ );
}
#[test]
-#[ignore = "depends on initTempComposer, composer-test.phar fixture, and Symfony Process to spawn the phar; none ported"]
+#[serial]
+#[ignore = "spawns `new Process([PHP_BINARY, $this->phar, 'self-update', $option])` running \
+ composer-test.phar over HTTP (data provider: --stable/--preview/--snapshot); requires \
+ Symfony Process and the composer-test.phar fixture, neither ported"]
fn test_update_to_different_channel() {
- let _phar = set_up();
+ let _tear_down = set_up();
todo!()
}
diff --git a/crates/shirabe/tests/command/show_command_test.rs b/crates/shirabe/tests/command/show_command_test.rs
index 56e8106..1ba3882 100644
--- a/crates/shirabe/tests/command/show_command_test.rs
+++ b/crates/shirabe/tests/command/show_command_test.rs
@@ -1,139 +1,1531 @@
//! ref: composer/tests/Composer/Test/Command/ShowCommandTest.php
-#[ignore = "missing TestCase::init_temp_composer, create_installed_json, create_composer_lock, and get_application_tester (ApplicationTester) infrastructure"]
+use crate::test_case::{
+ RunOptions, get_application_tester, get_complete_package, get_package, get_version_constraint,
+ init_temp_composer,
+};
+use serial_test::serial;
+use shirabe::package::Link;
+use shirabe::package::handle::PackageInterfaceHandle;
+use shirabe::repository::PlatformRepository;
+use shirabe_php_shim::PhpMixed;
+
+/// Build a `Vec<(PhpMixed, PhpMixed)>` command input from `(key, value)` pairs.
+fn input(pairs: Vec<(&str, PhpMixed)>) -> Vec<(PhpMixed, PhpMixed)> {
+ pairs
+ .into_iter()
+ .map(|(k, v)| (PhpMixed::from(k), v))
+ .collect()
+}
+
+/// ref: ShowCommandTest::testShow composer.json fixture.
+fn show_composer_json(requires: serde_json::Value) -> serde_json::Value {
+ serde_json::json!({
+ "name": "root/pkg",
+ "version": "1.2.3",
+ "repositories": {
+ "packages": {
+ "type": "package",
+ "package": [
+ {"name": "vendor/package", "description": "generic description", "version": "v1.0.0"},
+
+ {"name": "outdated/major", "description": "outdated/major v1.0.0 description", "version": "v1.0.0"},
+ {"name": "outdated/major", "description": "outdated/major v1.0.1 description", "version": "v1.0.1"},
+ {"name": "outdated/major", "description": "outdated/major v1.1.0 description", "version": "v1.1.0"},
+ {"name": "outdated/major", "description": "outdated/major v1.1.1 description", "version": "v1.1.1"},
+ {"name": "outdated/major", "description": "outdated/major v2.0.0 description", "version": "v2.0.0"},
+
+ {"name": "outdated/minor", "description": "outdated/minor v1.0.0 description", "version": "1.0.0"},
+ {"name": "outdated/minor", "description": "outdated/minor v1.0.1 description", "version": "1.0.1"},
+ {"name": "outdated/minor", "description": "outdated/minor v1.1.0 description", "version": "1.1.0"},
+ {"name": "outdated/minor", "description": "outdated/minor v1.1.1 description", "version": "1.1.1"},
+
+ {"name": "outdated/patch", "description": "outdated/patch v1.0.0 description", "version": "1.0.0"},
+ {"name": "outdated/patch", "description": "outdated/patch v1.0.1 description", "version": "1.0.1"},
+ ],
+ },
+ },
+ "require": requires,
+ })
+}
+
+/// ref: ShowCommandTest::testShow (one data-provider case).
+///
+/// NOTE: PHP sets release dates on the installed packages via `setReleaseDate`, but no such setter
+/// is exposed on `PackageInterfaceHandle`, so it is omitted here. Only the "sorting by age" case
+/// depends on release dates; every other case produces identical output without them.
+fn run_show_case(command: Vec<(PhpMixed, PhpMixed)>, expected: &str, requires: serde_json::Value) {
+ let _tear_down = init_temp_composer(Some(&show_composer_json(requires)), None, None, true);
+
+ let pkg = get_complete_package("vendor/package", "v1.0.0");
+ pkg.set_description("description of installed package".to_string());
+ let major = get_complete_package("outdated/major", "v1.0.0");
+ let minor = get_complete_package("outdated/minor", "1.0.0");
+ let patch = get_complete_package("outdated/patch", "1.0.0");
+
+ let packages: Vec<PackageInterfaceHandle> =
+ vec![pkg.into(), major.into(), minor.into(), patch.into()];
+ create_installed_json(&packages, &[], true);
+
+ let locked = get_complete_package("vendor/locked", "3.0.0");
+ locked.set_description("description of locked package".to_string());
+ create_composer_lock(&[locked.into()], &[]);
+
+ let mut app_tester = get_application_tester();
+ app_tester.run(command, RunOptions::default()).unwrap();
+ assert_eq!(expected.trim(), app_tester.get_display().trim());
+}
+
+use crate::test_case::{create_composer_lock, create_installed_json};
+
+#[ignore = "blocked: ShowCommand::configure() at src/command/show_command.rs:98-100 registers an empty set_definition() (TODO placeholder); no package argument or options are defined, so every show invocation fails with an option/argument-does-not-exist InvalidArgumentException (the outdated command proxies into show and fails identically). Unported src path; cannot fix from test files."]
+#[test]
+#[serial]
+fn test_show_default_shows_installed_with_version_and_description() {
+ run_show_case(
+ input(vec![("command", PhpMixed::from("show"))]),
+ "outdated/major 1.0.0
+outdated/minor 1.0.0
+outdated/patch 1.0.0
+vendor/package 1.0.0 description of installed package",
+ serde_json::json!({}),
+ );
+}
+
+#[ignore = "blocked: ShowCommand::configure() at src/command/show_command.rs:98-100 registers an empty set_definition() (TODO placeholder); no package argument or options are defined, so every show invocation fails with an option/argument-does-not-exist InvalidArgumentException (the outdated command proxies into show and fails identically). Unported src path; cannot fix from test files."]
+#[test]
+#[serial]
+fn test_show_with_installed_and_self() {
+ run_show_case(
+ input(vec![
+ ("command", PhpMixed::from("show")),
+ ("--installed", PhpMixed::from(true)),
+ ("--self", PhpMixed::from(true)),
+ ]),
+ "outdated/major 1.0.0
+outdated/minor 1.0.0
+outdated/patch 1.0.0
+root/pkg 1.2.3
+vendor/package 1.0.0 description of installed package",
+ serde_json::json!({}),
+ );
+}
+
+#[ignore = "blocked: ShowCommand::configure() at src/command/show_command.rs:98-100 registers an empty set_definition() (TODO placeholder); no package argument or options are defined, so every show invocation fails with an option/argument-does-not-exist InvalidArgumentException (the outdated command proxies into show and fails identically). Unported src path; cannot fix from test files."]
+#[test]
+#[serial]
+fn test_show_with_locked_and_self() {
+ run_show_case(
+ input(vec![
+ ("command", PhpMixed::from("show")),
+ ("--locked", PhpMixed::from(true)),
+ ("--self", PhpMixed::from(true)),
+ ]),
+ "root/pkg 1.2.3
+vendor/locked 3.0.0 description of locked package",
+ serde_json::json!({}),
+ );
+}
+
+#[ignore = "blocked: ShowCommand::configure() at src/command/show_command.rs:98-100 registers an empty set_definition() (TODO placeholder); no package argument or options are defined, so every show invocation fails with an option/argument-does-not-exist InvalidArgumentException (the outdated command proxies into show and fails identically). Unported src path; cannot fix from test files."]
+#[test]
+#[serial]
+fn test_show_with_available() {
+ run_show_case(
+ input(vec![
+ ("command", PhpMixed::from("show")),
+ ("-a", PhpMixed::from(true)),
+ ]),
+ "outdated/major outdated/major v2.0.0 description
+outdated/minor outdated/minor v1.1.1 description
+outdated/patch outdated/patch v1.0.1 description
+vendor/package generic description",
+ serde_json::json!({}),
+ );
+}
+
+#[ignore = "blocked: ShowCommand::configure() at src/command/show_command.rs:98-100 registers an empty set_definition() (TODO placeholder); no package argument or options are defined, so every show invocation fails with an option/argument-does-not-exist InvalidArgumentException (the outdated command proxies into show and fails identically). Unported src path; cannot fix from test files."]
+#[test]
+#[serial]
+fn test_show_with_direct_shows_nothing_if_no_deps() {
+ run_show_case(
+ input(vec![
+ ("command", PhpMixed::from("show")),
+ ("--direct", PhpMixed::from(true)),
+ ]),
+ "",
+ serde_json::json!({}),
+ );
+}
+
+#[ignore = "blocked: ShowCommand::configure() at src/command/show_command.rs:98-100 registers an empty set_definition() (TODO placeholder); no package argument or options are defined, so every show invocation fails with an option/argument-does-not-exist InvalidArgumentException (the outdated command proxies into show and fails identically). Unported src path; cannot fix from test files."]
+#[test]
+#[serial]
+fn test_show_with_direct_shows_only_root_deps() {
+ run_show_case(
+ input(vec![
+ ("command", PhpMixed::from("show")),
+ ("--direct", PhpMixed::from(true)),
+ ]),
+ "outdated/major 1.0.0",
+ serde_json::json!({"outdated/major": "*"}),
+ );
+}
+
+#[ignore = "blocked: ShowCommand::configure() at src/command/show_command.rs:98-100 registers an empty set_definition() (TODO placeholder); no package argument or options are defined, so every show invocation fails with an option/argument-does-not-exist InvalidArgumentException (the outdated command proxies into show and fails identically). Unported src path; cannot fix from test files."]
+#[test]
+#[serial]
+fn test_show_outdated_deps() {
+ run_show_case(
+ input(vec![("command", PhpMixed::from("outdated"))]),
+ "Legend:
+! patch or minor release available - update recommended
+~ major release available - update possible
+
+Direct dependencies required in composer.json:
+Everything up to date
+
+Transitive dependencies not required in composer.json:
+outdated/major 1.0.0 ~ 2.0.0
+outdated/minor 1.0.0 <highlight>! 1.1.1</highlight>
+outdated/patch 1.0.0 <highlight>! 1.0.1</highlight>",
+ serde_json::json!({}),
+ );
+}
+
+#[ignore = "blocked: ShowCommand::configure() at src/command/show_command.rs:98-100 registers an empty set_definition(), so outdated->show fails with an option-does-not-exist error. Additionally, PackageInterfaceHandle exposes no set_release_date setter, so the 'sorting by age' output ('2 years old', '2 weeks old', 'from today') cannot be reproduced even once the definition is ported."]
+#[test]
+#[serial]
+fn test_show_outdated_deps_sorting_by_age() {
+ run_show_case(
+ input(vec![
+ ("command", PhpMixed::from("outdated")),
+ ("--sort-by-age", PhpMixed::from(true)),
+ ]),
+ "Legend:
+! patch or minor release available - update recommended
+~ major release available - update possible
+
+Direct dependencies required in composer.json:
+Everything up to date
+
+Transitive dependencies not required in composer.json:
+outdated/minor 1.0.0 <highlight>! 1.1.1</highlight> 2 years old
+outdated/patch 1.0.0 <highlight>! 1.0.1</highlight> 2 weeks old
+outdated/major 1.0.0 ~ 2.0.0 from today",
+ serde_json::json!({}),
+ );
+}
+
+#[ignore = "blocked: ShowCommand::configure() at src/command/show_command.rs:98-100 registers an empty set_definition() (TODO placeholder); no package argument or options are defined, so every show invocation fails with an option/argument-does-not-exist InvalidArgumentException (the outdated command proxies into show and fails identically). Unported src path; cannot fix from test files."]
#[test]
-fn test_show() {
- todo!()
+#[serial]
+fn test_show_outdated_deps_with_direct_only_show_direct_deps_with_updated() {
+ run_show_case(
+ input(vec![
+ ("command", PhpMixed::from("outdated")),
+ ("--direct", PhpMixed::from(true)),
+ ]),
+ "Legend:
+! patch or minor release available - update recommended
+~ major release available - update possible
+outdated/major 1.0.0 ~ 2.0.0",
+ serde_json::json!({
+ "vendor/package": "*",
+ "outdated/major": "*",
+ }),
+ );
+}
+
+#[ignore = "blocked: ShowCommand::configure() at src/command/show_command.rs:98-100 registers an empty set_definition() (TODO placeholder); no package argument or options are defined, so every show invocation fails with an option/argument-does-not-exist InvalidArgumentException (the outdated command proxies into show and fails identically). Unported src path; cannot fix from test files."]
+#[test]
+#[serial]
+fn test_show_outdated_deps_with_direct_show_msg_if_all_up_to_date() {
+ run_show_case(
+ input(vec![
+ ("command", PhpMixed::from("outdated")),
+ ("--direct", PhpMixed::from(true)),
+ ]),
+ "All your direct dependencies are up to date",
+ serde_json::json!({"vendor/package": "*"}),
+ );
+}
+
+#[ignore = "blocked: ShowCommand::configure() at src/command/show_command.rs:98-100 registers an empty set_definition() (TODO placeholder); no package argument or options are defined, so every show invocation fails with an option/argument-does-not-exist InvalidArgumentException (the outdated command proxies into show and fails identically). Unported src path; cannot fix from test files."]
+#[test]
+#[serial]
+fn test_show_outdated_deps_with_major_only() {
+ run_show_case(
+ input(vec![
+ ("command", PhpMixed::from("outdated")),
+ ("--major-only", PhpMixed::from(true)),
+ ]),
+ "Legend:
+! patch or minor release available - update recommended
+~ major release available - update possible
+
+Direct dependencies required in composer.json:
+Everything up to date
+
+Transitive dependencies not required in composer.json:
+outdated/major 1.0.0 ~ 2.0.0",
+ serde_json::json!({}),
+ );
+}
+
+#[ignore = "blocked: ShowCommand::configure() at src/command/show_command.rs:98-100 registers an empty set_definition() (TODO placeholder); no package argument or options are defined, so every show invocation fails with an option/argument-does-not-exist InvalidArgumentException (the outdated command proxies into show and fails identically). Unported src path; cannot fix from test files."]
+#[test]
+#[serial]
+fn test_show_outdated_deps_with_minor_only() {
+ run_show_case(
+ input(vec![
+ ("command", PhpMixed::from("outdated")),
+ ("--minor-only", PhpMixed::from(true)),
+ ]),
+ "Legend:
+! patch or minor release available - update recommended
+~ major release available - update possible
+
+Direct dependencies required in composer.json:
+outdated/minor 1.0.0 <highlight>! 1.1.1</highlight>
+
+Transitive dependencies not required in composer.json:
+outdated/major 1.0.0 <highlight>! 1.1.1</highlight>
+outdated/patch 1.0.0 <highlight>! 1.0.1</highlight>",
+ serde_json::json!({"outdated/minor": "*"}),
+ );
}
-#[ignore = "missing TestCase::init_temp_composer, create_installed_json, and get_application_tester (ApplicationTester) infrastructure"]
+#[ignore = "blocked: ShowCommand::configure() at src/command/show_command.rs:98-100 registers an empty set_definition() (TODO placeholder); no package argument or options are defined, so every show invocation fails with an option/argument-does-not-exist InvalidArgumentException (the outdated command proxies into show and fails identically). Unported src path; cannot fix from test files."]
#[test]
+#[serial]
+fn test_show_outdated_deps_with_patch_only() {
+ run_show_case(
+ input(vec![
+ ("command", PhpMixed::from("outdated")),
+ ("--patch-only", PhpMixed::from(true)),
+ ]),
+ "Legend:
+! patch or minor release available - update recommended
+~ major release available - update possible
+
+Direct dependencies required in composer.json:
+Everything up to date
+
+Transitive dependencies not required in composer.json:
+outdated/major 1.0.0 <highlight>! 1.0.1</highlight>
+outdated/minor 1.0.0 <highlight>! 1.0.1</highlight>
+outdated/patch 1.0.0 <highlight>! 1.0.1</highlight>",
+ serde_json::json!({}),
+ );
+}
+
+#[ignore = "blocked: ShowCommand::configure() at src/command/show_command.rs:98-100 registers an empty set_definition() (TODO placeholder); no package argument or options are defined, so every show invocation fails with an option/argument-does-not-exist InvalidArgumentException (the outdated command proxies into show and fails identically). Unported src path; cannot fix from test files."]
+#[test]
+#[serial]
fn test_outdated_filters_according_to_platform_reqs_and_warns() {
- todo!()
+ let _tear_down = init_temp_composer(
+ Some(&serde_json::json!({
+ "repositories": {
+ "packages": {
+ "type": "package",
+ "package": [
+ {"name": "vendor/package", "description": "generic description", "version": "1.0.0"},
+ {"name": "vendor/package", "description": "generic description", "version": "1.1.0", "require": {"ext-missing": "3"}},
+ {"name": "vendor/package", "description": "generic description", "version": "1.2.0", "require": {"ext-missing": "3"}},
+ {"name": "vendor/package", "description": "generic description", "version": "1.3.0", "require": {"ext-missing": "3"}},
+ ],
+ },
+ },
+ })),
+ None,
+ None,
+ true,
+ );
+
+ create_installed_json(&[get_package("vendor/package", "1.1.0")], &[], true);
+
+ let mut app_tester = get_application_tester();
+ app_tester
+ .run(
+ input(vec![("command", PhpMixed::from("outdated"))]),
+ RunOptions::default(),
+ )
+ .unwrap();
+ assert_eq!(
+ "<warning>Cannot use vendor/package 1.1.0 as it requires ext-missing 3 which is missing from your platform.
+Legend:
+! patch or minor release available - update recommended
+~ major release available - update possible
+
+Direct dependencies required in composer.json:
+Everything up to date
+
+Transitive dependencies not required in composer.json:
+vendor/package 1.1.0 ~ 1.0.0",
+ app_tester.get_display().trim()
+ );
+
+ let mut app_tester = get_application_tester();
+ app_tester
+ .run(
+ input(vec![
+ ("command", PhpMixed::from("outdated")),
+ ("--verbose", PhpMixed::from(true)),
+ ]),
+ RunOptions::default(),
+ )
+ .unwrap();
+ assert_eq!(
+ "<warning>Cannot use vendor/package's latest version 1.3.0 as it requires ext-missing 3 which is missing from your platform.
+<warning>Cannot use vendor/package 1.2.0 as it requires ext-missing 3 which is missing from your platform.
+<warning>Cannot use vendor/package 1.1.0 as it requires ext-missing 3 which is missing from your platform.
+Legend:
+! patch or minor release available - update recommended
+~ major release available - update possible
+
+Direct dependencies required in composer.json:
+Everything up to date
+
+Transitive dependencies not required in composer.json:
+vendor/package 1.1.0 ~ 1.0.0",
+ app_tester.get_display().trim()
+ );
}
-#[ignore = "missing TestCase::init_temp_composer, create_installed_json, and get_application_tester (ApplicationTester) infrastructure"]
+#[ignore = "blocked: ShowCommand::configure() at src/command/show_command.rs:98-100 registers an empty set_definition() (TODO placeholder); no package argument or options are defined, so every show invocation fails with an option/argument-does-not-exist InvalidArgumentException (the outdated command proxies into show and fails identically). Unported src path; cannot fix from test files."]
#[test]
+#[serial]
fn test_outdated_filters_according_to_platform_reqs_without_warning_for_higher_versions() {
- todo!()
+ let _tear_down = init_temp_composer(
+ Some(&serde_json::json!({
+ "repositories": {
+ "packages": {
+ "type": "package",
+ "package": [
+ {"name": "vendor/package", "description": "generic description", "version": "1.0.0"},
+ {"name": "vendor/package", "description": "generic description", "version": "1.1.0"},
+ {"name": "vendor/package", "description": "generic description", "version": "1.2.0"},
+ {"name": "vendor/package", "description": "generic description", "version": "1.3.0", "require": {"php": "^99"}},
+ ],
+ },
+ },
+ })),
+ None,
+ None,
+ true,
+ );
+
+ create_installed_json(&[get_package("vendor/package", "1.1.0")], &[], true);
+
+ let mut app_tester = get_application_tester();
+ app_tester
+ .run(
+ input(vec![("command", PhpMixed::from("outdated"))]),
+ RunOptions::default(),
+ )
+ .unwrap();
+ assert_eq!(
+ "Legend:
+! patch or minor release available - update recommended
+~ major release available - update possible
+
+Direct dependencies required in composer.json:
+Everything up to date
+
+Transitive dependencies not required in composer.json:
+vendor/package 1.1.0 <highlight>! 1.2.0</highlight>",
+ app_tester.get_display().trim()
+ );
}
-#[ignore = "missing TestCase::init_temp_composer, create_installed_json, configure_links, and get_application_tester (ApplicationTester) infrastructure"]
+#[ignore = "blocked: ShowCommand::configure() at src/command/show_command.rs:98-100 registers an empty set_definition() (TODO placeholder); no package argument or options are defined, so every show invocation fails with an option/argument-does-not-exist InvalidArgumentException (the outdated command proxies into show and fails identically). Unported src path; cannot fix from test files."]
#[test]
+#[serial]
fn test_show_direct_with_name_does_not_show_transient_dependencies() {
- todo!()
+ // NOTE: PHP also wires `direct/dependent` -> `vendor/package` via configureLinks, which has no
+ // public handle API. It is omitted: vendor/package is installed but not a direct root dependency
+ // either way, so the same InvalidArgumentException is thrown.
+ let _tear_down = init_temp_composer(
+ Some(&serde_json::json!({
+ "repositories": [],
+ "require": {
+ "direct/dependent": "*",
+ },
+ })),
+ None,
+ None,
+ false,
+ );
+
+ create_installed_json(
+ &[
+ get_package("direct/dependent", "1.0.0"),
+ get_package("vendor/package", "1.0.0"),
+ ],
+ &[],
+ true,
+ );
+
+ let mut app_tester = get_application_tester();
+ let err = app_tester
+ .run(
+ input(vec![
+ ("command", PhpMixed::from("show")),
+ ("--direct", PhpMixed::from(true)),
+ ("package", PhpMixed::from("vendor/package")),
+ ]),
+ RunOptions::default(),
+ )
+ .expect_err("expected an InvalidArgumentException");
+ assert!(
+ err.to_string().contains(
+ "Package \"vendor/package\" is installed but not a direct dependent of the root package."
+ ),
+ "unexpected error: {}",
+ err
+ );
}
-#[ignore = "missing TestCase::init_temp_composer, create_installed_json, and get_application_tester (ApplicationTester) infrastructure"]
+#[ignore = "blocked: ShowCommand::configure() at src/command/show_command.rs:98-100 registers an empty set_definition() (TODO placeholder); no package argument or options are defined, so every show invocation fails with an option/argument-does-not-exist InvalidArgumentException (the outdated command proxies into show and fails identically). Unported src path; cannot fix from test files."]
#[test]
+#[serial]
fn test_show_direct_with_name_only_shows_direct_dependents() {
- todo!()
+ let _tear_down = init_temp_composer(
+ Some(&serde_json::json!({
+ "repositories": [],
+ "require": {
+ "direct/dependent": "*",
+ },
+ "require-dev": {
+ "direct/dependent2": "*",
+ },
+ })),
+ None,
+ None,
+ false,
+ );
+
+ create_installed_json(
+ &[
+ get_package("direct/dependent", "1.0.0"),
+ get_package("direct/dependent2", "1.0.0"),
+ ],
+ &[],
+ true,
+ );
+
+ let mut app_tester = get_application_tester();
+ let status_code = app_tester
+ .run(
+ input(vec![
+ ("command", PhpMixed::from("show")),
+ ("--direct", PhpMixed::from(true)),
+ ("package", PhpMixed::from("direct/dependent")),
+ ]),
+ RunOptions::default(),
+ )
+ .unwrap();
+ assert_eq!(0, status_code);
+ assert!(
+ app_tester
+ .get_display()
+ .contains("name : direct/dependent\n")
+ );
+
+ let mut app_tester = get_application_tester();
+ let status_code = app_tester
+ .run(
+ input(vec![
+ ("command", PhpMixed::from("show")),
+ ("--direct", PhpMixed::from(true)),
+ ("package", PhpMixed::from("direct/dependent2")),
+ ]),
+ RunOptions::default(),
+ )
+ .unwrap();
+ assert_eq!(0, status_code);
+ assert!(
+ app_tester
+ .get_display()
+ .contains("name : direct/dependent2\n")
+ );
+}
+
+/// Assert every leading-word of each output line is a platform package, replicating
+/// `Regex::matchAll('{^(\w+)}m', $output)`.
+fn assert_all_platform_packages(output: &str) {
+ let re = regex::Regex::new(r"(?m)^(\w+)").unwrap();
+ for caps in re.captures_iter(output) {
+ let m = caps.get(1).unwrap().as_str();
+ assert!(
+ PlatformRepository::is_platform_package(m),
+ "{} is not a platform package",
+ m
+ );
+ }
}
-#[ignore = "missing TestCase::init_temp_composer, create_installed_json, and get_application_tester (ApplicationTester) infrastructure"]
+#[ignore = "blocked: ShowCommand::configure() at src/command/show_command.rs:98-100 registers an empty set_definition() (TODO placeholder); no package argument or options are defined, so every show invocation fails with an option/argument-does-not-exist InvalidArgumentException (the outdated command proxies into show and fails identically). Unported src path; cannot fix from test files."]
#[test]
+#[serial]
fn test_show_platform_only_shows_platform_packages() {
- todo!()
+ let _tear_down = init_temp_composer(
+ Some(&serde_json::json!({
+ "repositories": {
+ "packages": {
+ "type": "package",
+ "package": [
+ {"name": "vendor/package", "description": "generic description", "version": "1.0.0"},
+ ],
+ },
+ },
+ })),
+ None,
+ None,
+ true,
+ );
+
+ create_installed_json(&[get_package("vendor/package", "1.0.0")], &[], true);
+
+ let mut app_tester = get_application_tester();
+ app_tester
+ .run(
+ input(vec![
+ ("command", PhpMixed::from("show")),
+ ("-p", PhpMixed::from(true)),
+ ]),
+ RunOptions::default(),
+ )
+ .unwrap();
+ let output = app_tester.get_display();
+ assert_all_platform_packages(output.trim());
}
-#[ignore = "missing TestCase::init_temp_composer and get_application_tester (ApplicationTester) infrastructure"]
+#[ignore = "blocked: ShowCommand::configure() at src/command/show_command.rs:98-100 registers an empty set_definition() (TODO placeholder); no package argument or options are defined, so every show invocation fails with an option/argument-does-not-exist InvalidArgumentException (the outdated command proxies into show and fails identically). Unported src path; cannot fix from test files."]
#[test]
+#[serial]
fn test_show_platform_works_without_composer_json() {
- todo!()
+ let _tear_down = init_temp_composer(Some(&serde_json::json!({})), None, None, true);
+ let _ = std::fs::remove_file("./composer.json");
+ let _ = std::fs::remove_file("./auth.json");
+
+ // listing packages
+ let mut app_tester = get_application_tester();
+ app_tester
+ .run(
+ input(vec![
+ ("command", PhpMixed::from("show")),
+ ("-p", PhpMixed::from(true)),
+ ]),
+ RunOptions::default(),
+ )
+ .unwrap();
+ let output = app_tester.get_display();
+ assert_all_platform_packages(output.trim());
+
+ // getting a single package
+ let status_code = app_tester
+ .run(
+ input(vec![
+ ("command", PhpMixed::from("show")),
+ ("-p", PhpMixed::from(true)),
+ ("package", PhpMixed::from("php")),
+ ]),
+ RunOptions::default(),
+ )
+ .unwrap();
+ assert_eq!(0, status_code);
+
+ let status_code = app_tester
+ .run(
+ input(vec![
+ ("command", PhpMixed::from("show")),
+ ("-p", PhpMixed::from(true)),
+ ("-f", PhpMixed::from("json")),
+ ("package", PhpMixed::from("php")),
+ ]),
+ RunOptions::default(),
+ )
+ .unwrap();
+ assert_eq!(0, status_code);
}
-#[ignore = "missing TestCase::init_temp_composer, create_installed_json, and get_application_tester (ApplicationTester) infrastructure"]
+#[ignore = "blocked: ShowCommand::configure() at src/command/show_command.rs:98-100 registers an empty set_definition() (TODO placeholder); no package argument or options are defined, so every show invocation fails with an option/argument-does-not-exist InvalidArgumentException (the outdated command proxies into show and fails identically). Unported src path; cannot fix from test files."]
#[test]
+#[serial]
fn test_outdated_with_zero_major() {
- todo!()
+ let _tear_down = init_temp_composer(
+ Some(&serde_json::json!({
+ "repositories": {
+ "packages": {
+ "type": "package",
+ "package": [
+ {"name": "zerozero/major", "description": "generic description", "version": "0.0.1"},
+ {"name": "zerozero/major", "description": "generic description", "version": "0.0.2"},
+ {"name": "zero/major", "description": "generic description", "version": "0.1.0"},
+ {"name": "zero/major", "description": "generic description", "version": "0.2.0"},
+ {"name": "zero/minor", "description": "generic description", "version": "0.1.0"},
+ {"name": "zero/minor", "description": "generic description", "version": "0.1.2"},
+ {"name": "zero/patch", "description": "generic description", "version": "0.1.2"},
+ {"name": "zero/patch", "description": "generic description", "version": "0.1.2.1"},
+ ],
+ },
+ },
+ "require": {
+ "zerozero/major": "^0.0.1",
+ "zero/major": "^0.1",
+ "zero/minor": "^0.1",
+ "zero/patch": "^0.1",
+ },
+ })),
+ None,
+ None,
+ true,
+ );
+
+ create_installed_json(
+ &[
+ get_package("zerozero/major", "0.0.1"),
+ get_package("zero/major", "0.1.0"),
+ get_package("zero/minor", "0.1.0"),
+ get_package("zero/patch", "0.1.2"),
+ ],
+ &[],
+ true,
+ );
+
+ let mut app_tester = get_application_tester();
+ app_tester
+ .run(
+ input(vec![
+ ("command", PhpMixed::from("outdated")),
+ ("--direct", PhpMixed::from(true)),
+ ("--patch-only", PhpMixed::from(true)),
+ ]),
+ RunOptions::default(),
+ )
+ .unwrap();
+ assert_eq!(
+ "Legend:
+! patch or minor release available - update recommended
+~ major release available - update possible
+zero/patch 0.1.2 <highlight>! 0.1.2.1</highlight>",
+ app_tester.get_display().trim()
+ );
+
+ let mut app_tester = get_application_tester();
+ app_tester
+ .run(
+ input(vec![
+ ("command", PhpMixed::from("outdated")),
+ ("--direct", PhpMixed::from(true)),
+ ("--minor-only", PhpMixed::from(true)),
+ ]),
+ RunOptions::default(),
+ )
+ .unwrap();
+ assert_eq!(
+ "Legend:
+! patch or minor release available - update recommended
+~ major release available - update possible
+zero/minor 0.1.0 <highlight>! 0.1.2 </highlight>
+zero/patch 0.1.2 <highlight>! 0.1.2.1</highlight>",
+ app_tester.get_display().trim()
+ );
+
+ let mut app_tester = get_application_tester();
+ app_tester
+ .run(
+ input(vec![
+ ("command", PhpMixed::from("outdated")),
+ ("--direct", PhpMixed::from(true)),
+ ("--major-only", PhpMixed::from(true)),
+ ]),
+ RunOptions::default(),
+ )
+ .unwrap();
+ assert_eq!(
+ "Legend:
+! patch or minor release available - update recommended
+~ major release available - update possible
+zero/major 0.1.0 ~ 0.2.0
+zerozero/major 0.0.1 ~ 0.0.2",
+ app_tester.get_display().trim()
+ );
}
-#[ignore = "missing TestCase::init_temp_composer, create_installed_json, create_composer_lock, and get_application_tester (ApplicationTester) infrastructure"]
+#[ignore = "blocked: ShowCommand::configure() at src/command/show_command.rs:98-100 registers an empty set_definition() (TODO placeholder); no package argument or options are defined, so every show invocation fails with an option/argument-does-not-exist InvalidArgumentException (the outdated command proxies into show and fails identically). Unported src path; cannot fix from test files."]
#[test]
+#[serial]
fn test_show_all_shows_all_sections() {
- todo!()
+ let _tear_down = init_temp_composer(
+ Some(&serde_json::json!({
+ "repositories": {
+ "packages": {
+ "type": "package",
+ "package": [
+ {"name": "vendor/available", "description": "generic description", "version": "1.0.0"},
+ ],
+ },
+ },
+ })),
+ None,
+ None,
+ true,
+ );
+
+ let pkg = get_complete_package("vendor/installed", "2.0.0");
+ pkg.set_description("description of installed package".to_string());
+ create_installed_json(&[pkg.into()], &[], true);
+
+ let locked = get_complete_package("vendor/locked", "3.0.0");
+ locked.set_description("description of locked package".to_string());
+ create_composer_lock(&[locked.into()], &[]);
+
+ let mut app_tester = get_application_tester();
+ app_tester
+ .run(
+ input(vec![
+ ("command", PhpMixed::from("show")),
+ ("--all", PhpMixed::from(true)),
+ ]),
+ RunOptions::default(),
+ )
+ .unwrap();
+ let output = app_tester.get_display();
+ let output = output.trim();
+ let re = regex::Regex::new(r"platform:(\n .*)+").unwrap();
+ let output = re.replace(output, "platform: wiped");
+
+ assert_eq!(
+ "platform: wiped
+
+locked:
+ vendor/locked 3.0.0 description of locked package
+
+available:
+ vendor/available generic description
+
+installed:
+ vendor/installed 2.0.0 description of installed package",
+ output
+ );
}
-#[ignore = "missing TestCase::init_temp_composer and get_application_tester (ApplicationTester) infrastructure"]
+#[ignore = "blocked: ShowCommand::configure() at src/command/show_command.rs:98-100 registers an empty set_definition() (TODO placeholder); no package argument or options are defined, so every show invocation fails with an option/argument-does-not-exist InvalidArgumentException (the outdated command proxies into show and fails identically). Unported src path; cannot fix from test files."]
#[test]
+#[serial]
fn test_locked_requires_valid_lock_file() {
- todo!()
+ let _tear_down = init_temp_composer(None, None, None, true);
+
+ let mut app_tester = get_application_tester();
+ let err = app_tester
+ .run(
+ input(vec![
+ ("command", PhpMixed::from("show")),
+ ("--locked", PhpMixed::from(true)),
+ ]),
+ RunOptions::default(),
+ )
+ .expect_err("expected an exception");
+ assert!(
+ err.to_string().contains(
+ "A valid composer.json and composer.lock files is required to run this command with --locked"
+ ),
+ "unexpected error: {}",
+ err
+ );
}
-#[ignore = "missing TestCase::init_temp_composer, create_composer_lock, and get_application_tester (ApplicationTester) infrastructure"]
+#[ignore = "blocked: ShowCommand::configure() at src/command/show_command.rs:98-100 registers an empty set_definition() (TODO placeholder); no package argument or options are defined, so every show invocation fails with an option/argument-does-not-exist InvalidArgumentException (the outdated command proxies into show and fails identically). Unported src path; cannot fix from test files."]
#[test]
+#[serial]
fn test_locked_shows_all_locked() {
- todo!()
+ let _tear_down = init_temp_composer(None, None, None, true);
+
+ let pkg = get_complete_package("vendor/locked", "3.0.0");
+ pkg.set_description("description of locked package".to_string());
+ create_composer_lock(&[pkg.clone().into()], &[]);
+
+ let mut app_tester = get_application_tester();
+ app_tester
+ .run(
+ input(vec![
+ ("command", PhpMixed::from("show")),
+ ("--locked", PhpMixed::from(true)),
+ ]),
+ RunOptions::default(),
+ )
+ .unwrap();
+ assert_eq!(
+ "vendor/locked 3.0.0 description of locked package",
+ app_tester.get_display().trim()
+ );
+
+ let pkg2 = get_complete_package("vendor/locked2", "2.0.0");
+ pkg2.set_description("description of locked2 package".to_string());
+ create_composer_lock(&[pkg.into(), pkg2.into()], &[]);
+
+ let mut app_tester = get_application_tester();
+ app_tester
+ .run(
+ input(vec![
+ ("command", PhpMixed::from("show")),
+ ("--locked", PhpMixed::from(true)),
+ ]),
+ RunOptions::default(),
+ )
+ .unwrap();
+ assert_eq!(
+ "vendor/locked 3.0.0 description of locked package
+vendor/locked2 2.0.0 description of locked2 package",
+ app_tester.get_display().trim()
+ );
}
-#[ignore = "missing TestCase::get_application_tester (ApplicationTester) infrastructure"]
+#[ignore = "blocked: ShowCommand::configure() at src/command/show_command.rs:98-100 registers an empty set_definition() (TODO placeholder); no package argument or options are defined, so every show invocation fails with an option/argument-does-not-exist InvalidArgumentException (the outdated command proxies into show and fails identically). Unported src path; cannot fix from test files."]
#[test]
+#[serial]
fn test_invalid_option_combinations() {
- todo!()
+ let combos: Vec<Vec<(&str, PhpMixed)>> = vec![
+ vec![
+ ("--direct", PhpMixed::from(true)),
+ ("--all", PhpMixed::from(true)),
+ ],
+ vec![
+ ("--direct", PhpMixed::from(true)),
+ ("--available", PhpMixed::from(true)),
+ ],
+ vec![
+ ("--direct", PhpMixed::from(true)),
+ ("--platform", PhpMixed::from(true)),
+ ],
+ vec![
+ ("--tree", PhpMixed::from(true)),
+ ("--all", PhpMixed::from(true)),
+ ],
+ vec![
+ ("--tree", PhpMixed::from(true)),
+ ("--available", PhpMixed::from(true)),
+ ],
+ vec![
+ ("--tree", PhpMixed::from(true)),
+ ("--latest", PhpMixed::from(true)),
+ ],
+ vec![
+ ("--tree", PhpMixed::from(true)),
+ ("--path", PhpMixed::from(true)),
+ ],
+ vec![
+ ("--patch-only", PhpMixed::from(true)),
+ ("--minor-only", PhpMixed::from(true)),
+ ],
+ vec![
+ ("--patch-only", PhpMixed::from(true)),
+ ("--major-only", PhpMixed::from(true)),
+ ],
+ vec![
+ ("--minor-only", PhpMixed::from(true)),
+ ("--major-only", PhpMixed::from(true)),
+ ],
+ vec![
+ ("--minor-only", PhpMixed::from(true)),
+ ("--major-only", PhpMixed::from(true)),
+ ("--patch-only", PhpMixed::from(true)),
+ ],
+ vec![("--format", PhpMixed::from("test"))],
+ ];
+
+ for combo in combos {
+ let mut pairs = vec![("command", PhpMixed::from("show"))];
+ pairs.extend(combo.clone());
+ let mut app_tester = get_application_tester();
+ let status_code = app_tester.run(input(pairs), RunOptions::default()).unwrap();
+ assert_eq!(1, status_code, "combo {:?}", combo);
+ }
}
-#[ignore = "missing TestCase::init_temp_composer and get_application_tester (ApplicationTester) infrastructure"]
+#[ignore = "blocked: ShowCommand::configure() at src/command/show_command.rs:98-100 registers an empty set_definition() (TODO placeholder); no package argument or options are defined, so every show invocation fails with an option/argument-does-not-exist InvalidArgumentException (the outdated command proxies into show and fails identically). Unported src path; cannot fix from test files."]
#[test]
+#[serial]
fn test_ignored_option_combinations() {
- todo!()
+ let _tear_down = init_temp_composer(None, None, None, true);
+
+ let mut app_tester = get_application_tester();
+ app_tester
+ .run(
+ input(vec![
+ ("command", PhpMixed::from("show")),
+ ("--installed", PhpMixed::from(true)),
+ ]),
+ RunOptions::default(),
+ )
+ .unwrap();
+ assert!(
+ app_tester
+ .get_display()
+ .contains("You are using the deprecated option \"installed\".")
+ );
+
+ let mut app_tester = get_application_tester();
+ app_tester
+ .run(
+ input(vec![
+ ("command", PhpMixed::from("show")),
+ (
+ "--ignore",
+ PhpMixed::List(vec![PhpMixed::from("vendor/package")]),
+ ),
+ ]),
+ RunOptions::default(),
+ )
+ .unwrap();
+ assert!(
+ app_tester
+ .get_display()
+ .contains("You are using the option \"ignore\"")
+ );
}
-#[ignore = "missing TestCase::init_temp_composer and get_application_tester (ApplicationTester) infrastructure"]
+#[ignore = "blocked: ShowCommand::configure() at src/command/show_command.rs:98-100 registers an empty set_definition() (TODO placeholder); no package argument or options are defined, so every show invocation fails with an option/argument-does-not-exist InvalidArgumentException (the outdated command proxies into show and fails identically). Unported src path; cannot fix from test files."]
#[test]
+#[serial]
fn test_self_and_name_only() {
- todo!()
+ let _tear_down = init_temp_composer(
+ Some(&serde_json::json!({"name": "vendor/package", "version": "1.2.3"})),
+ None,
+ None,
+ true,
+ );
+
+ let mut app_tester = get_application_tester();
+ app_tester
+ .run(
+ input(vec![
+ ("command", PhpMixed::from("show")),
+ ("--self", PhpMixed::from(true)),
+ ("--name-only", PhpMixed::from(true)),
+ ]),
+ RunOptions::default(),
+ )
+ .unwrap();
+ assert_eq!("vendor/package", app_tester.get_display().trim());
}
-#[ignore = "missing TestCase::init_temp_composer and get_application_tester (ApplicationTester) infrastructure"]
+#[ignore = "blocked: ShowCommand::configure() at src/command/show_command.rs:98-100 registers an empty set_definition() (TODO placeholder); no package argument or options are defined, so every show invocation fails with an option/argument-does-not-exist InvalidArgumentException (the outdated command proxies into show and fails identically). Unported src path; cannot fix from test files."]
#[test]
+#[serial]
fn test_self_and_package_combination() {
- todo!()
+ let _tear_down = init_temp_composer(
+ Some(&serde_json::json!({"name": "vendor/package"})),
+ None,
+ None,
+ true,
+ );
+
+ let mut app_tester = get_application_tester();
+ let result = app_tester.run(
+ input(vec![
+ ("command", PhpMixed::from("show")),
+ ("--self", PhpMixed::from(true)),
+ ("package", PhpMixed::from("vendor/package")),
+ ]),
+ RunOptions::default(),
+ );
+ assert!(
+ result.is_err(),
+ "expected an InvalidArgumentException, got {:?}",
+ result
+ );
}
-#[ignore = "missing TestCase::init_temp_composer and get_application_tester (ApplicationTester) infrastructure"]
+#[ignore = "blocked: ShowCommand::configure() at src/command/show_command.rs:98-100 registers an empty set_definition() (TODO placeholder); no package argument or options are defined, so every show invocation fails with an option/argument-does-not-exist InvalidArgumentException (the outdated command proxies into show and fails identically). Unported src path; cannot fix from test files."]
#[test]
+#[serial]
fn test_self() {
- todo!()
+ let today = chrono::Local::now().format("%Y-%m-%d").to_string();
+ let _tear_down = init_temp_composer(
+ Some(&serde_json::json!({
+ "name": "vendor/package",
+ "version": "1.2.3",
+ "time": today,
+ })),
+ None,
+ None,
+ true,
+ );
+
+ let mut app_tester = get_application_tester();
+ app_tester
+ .run(
+ input(vec![
+ ("command", PhpMixed::from("show")),
+ ("--self", PhpMixed::from(true)),
+ ]),
+ RunOptions::default(),
+ )
+ .unwrap();
+
+ let expected: Vec<(&str, String)> = vec![
+ ("name", "vendor/package".to_string()),
+ ("descrip.", "".to_string()),
+ ("keywords", "".to_string()),
+ ("versions", "* 1.2.3".to_string()),
+ ("released", format!("{}, today", today)),
+ ("type", "library".to_string()),
+ ("homepage", "".to_string()),
+ ("source", "[] ".to_string()),
+ ("dist", "[] ".to_string()),
+ ("path", "".to_string()),
+ ("names", "vendor/package".to_string()),
+ ];
+ let expected_string = expected
+ .iter()
+ .map(|(k, v)| format!("{:<8} : {}", k, v))
+ .collect::<Vec<_>>()
+ .join("\n")
+ + "\n";
+
+ assert_eq!(expected_string, app_tester.get_display());
}
-#[ignore = "missing TestCase::init_temp_composer and get_application_tester (ApplicationTester) infrastructure"]
+#[ignore = "blocked: ShowCommand::configure() at src/command/show_command.rs:98-100 registers an empty set_definition() (TODO placeholder); no package argument or options are defined, so every show invocation fails with an option/argument-does-not-exist InvalidArgumentException (the outdated command proxies into show and fails identically). Unported src path; cannot fix from test files."]
#[test]
+#[serial]
fn test_not_installed_error() {
- todo!()
+ let _tear_down = init_temp_composer(
+ Some(&serde_json::json!({
+ "require": {"vendor/package": "1.0.0"},
+ "require-dev": {"vendor/package-dev": "1.0.0"},
+ })),
+ None,
+ None,
+ true,
+ );
+
+ let mut app_tester = get_application_tester();
+ app_tester
+ .run(
+ input(vec![("command", PhpMixed::from("show"))]),
+ RunOptions::default(),
+ )
+ .unwrap();
+ assert!(
+ app_tester
+ .get_display()
+ .trim()
+ .contains("No dependencies installed. Try running composer install or update."),
+ "Should show error message when no dependencies are installed"
+ );
}
-#[ignore = "missing TestCase::init_temp_composer, create_installed_json, and get_application_tester (ApplicationTester) infrastructure"]
+#[ignore = "blocked: ShowCommand::configure() at src/command/show_command.rs:98-100 registers an empty set_definition() (TODO placeholder); no package argument or options are defined, so every show invocation fails with an option/argument-does-not-exist InvalidArgumentException (the outdated command proxies into show and fails identically). Unported src path; cannot fix from test files."]
#[test]
+#[serial]
fn test_no_dev_option() {
- todo!()
+ let _tear_down = init_temp_composer(
+ Some(&serde_json::json!({
+ "require": {"vendor/package": "1.0.0"},
+ "require-dev": {"vendor/package-dev": "1.0.0"},
+ })),
+ None,
+ None,
+ true,
+ );
+ create_installed_json(
+ &[
+ get_package("vendor/package", "1.0.0"),
+ get_package("vendor/package-dev", "1.0.0"),
+ ],
+ &[],
+ true,
+ );
+
+ let mut app_tester = get_application_tester();
+ app_tester
+ .run(
+ input(vec![
+ ("command", PhpMixed::from("show")),
+ ("--no-dev", PhpMixed::from(true)),
+ ]),
+ RunOptions::default(),
+ )
+ .unwrap();
+ assert_eq!("vendor/package 1.0.0", app_tester.get_display().trim());
}
-#[ignore = "missing TestCase::init_temp_composer, create_installed_json, and get_application_tester (ApplicationTester) infrastructure"]
+#[ignore = "blocked: ShowCommand::configure() at src/command/show_command.rs:98-100 registers an empty set_definition() (TODO placeholder); no package argument or options are defined, so every show invocation fails with an option/argument-does-not-exist InvalidArgumentException (the outdated command proxies into show and fails identically). Unported src path; cannot fix from test files."]
#[test]
+#[serial]
fn test_package_filter() {
- todo!()
+ let _tear_down = init_temp_composer(
+ Some(&serde_json::json!({
+ "require": {
+ "vendor/package": "1.0.0",
+ "vendor/other-package": "1.0.0",
+ "company/package": "1.0.0",
+ "company/other-package": "1.0.0",
+ },
+ })),
+ None,
+ None,
+ true,
+ );
+ create_installed_json(
+ &[
+ get_package("vendor/package", "1.0.0"),
+ get_package("vendor/other-package", "1.0.0"),
+ get_package("company/package", "1.0.0"),
+ get_package("company/other-package", "1.0.0"),
+ ],
+ &[],
+ true,
+ );
+
+ let mut app_tester = get_application_tester();
+ app_tester
+ .run(
+ input(vec![
+ ("command", PhpMixed::from("show")),
+ ("package", PhpMixed::from("vendor/package")),
+ ]),
+ RunOptions::default(),
+ )
+ .unwrap();
+ let output = app_tester.get_display();
+ let output = output.trim();
+ assert!(output.contains("vendor/package"));
+ assert!(!output.contains("vendor/other-package"));
+ assert!(!output.contains("company/package"));
+ assert!(!output.contains("company/other-package"));
+
+ let mut app_tester = get_application_tester();
+ app_tester
+ .run(
+ input(vec![
+ ("command", PhpMixed::from("show")),
+ ("package", PhpMixed::from("company/*")),
+ ("--name-only", PhpMixed::from(true)),
+ ]),
+ RunOptions::default(),
+ )
+ .unwrap();
+ let output = app_tester.get_display();
+ let output = output.trim();
+ assert!(!output.contains("vendor/package"));
+ assert!(!output.contains("vendor/other-package"));
+ assert!(output.contains("company/package"));
+ assert!(output.contains("company/other-package"));
+}
+
+/// ref: ShowCommandTest::testNotExistingPackage (one data-provider case).
+fn run_not_existing_package_case(package: &str, options: Vec<(&str, PhpMixed)>, expected: &str) {
+ let _tear_down = init_temp_composer(
+ Some(&serde_json::json!({
+ "require": {"vendor/package": "1.0.0"},
+ })),
+ None,
+ None,
+ true,
+ );
+ let pkg = get_package("vendor/package", "1.0.0");
+ create_installed_json(&[pkg.clone()], &[], true);
+ create_composer_lock(&[pkg], &[]);
+
+ let mut pairs = vec![
+ ("command", PhpMixed::from("show")),
+ ("package", PhpMixed::from(package)),
+ ];
+ pairs.extend(options);
+
+ let mut app_tester = get_application_tester();
+ let err = app_tester
+ .run(input(pairs), RunOptions::default())
+ .expect_err("expected an exception");
+ assert!(
+ err.to_string().contains(expected),
+ "expected message starting with {:?}, got: {}",
+ expected,
+ err
+ );
}
-#[ignore = "missing TestCase::init_temp_composer, create_installed_json, create_composer_lock, and get_application_tester (ApplicationTester) infrastructure"]
+#[ignore = "blocked: ShowCommand::configure() at src/command/show_command.rs:98-100 registers an empty set_definition() (TODO placeholder); no package argument or options are defined, so every show invocation fails with an option/argument-does-not-exist InvalidArgumentException (the outdated command proxies into show and fails identically). Unported src path; cannot fix from test files."]
#[test]
-fn test_not_existing_package() {
- todo!()
+#[serial]
+fn test_not_existing_package_with_no_options() {
+ run_not_existing_package_case(
+ "not/existing",
+ vec![],
+ "Package \"not/existing\" not found, try using --available (-a) to show all available packages.",
+ );
}
-#[ignore = "missing TestCase::init_temp_composer, create_installed_json, and get_application_tester (ApplicationTester) infrastructure"]
+#[ignore = "blocked: ShowCommand::configure() at src/command/show_command.rs:98-100 registers an empty set_definition() (TODO placeholder); no package argument or options are defined, so every show invocation fails with an option/argument-does-not-exist InvalidArgumentException (the outdated command proxies into show and fails identically). Unported src path; cannot fix from test files."]
#[test]
+#[serial]
+fn test_not_existing_package_with_all_option() {
+ run_not_existing_package_case(
+ "not/existing",
+ vec![("--all", PhpMixed::from(true))],
+ "Package \"not/existing\" not found.",
+ );
+}
+
+#[ignore = "blocked: ShowCommand::configure() at src/command/show_command.rs:98-100 registers an empty set_definition() (TODO placeholder); no package argument or options are defined, so every show invocation fails with an option/argument-does-not-exist InvalidArgumentException (the outdated command proxies into show and fails identically). Unported src path; cannot fix from test files."]
+#[test]
+#[serial]
+fn test_not_existing_package_with_locked_option() {
+ run_not_existing_package_case(
+ "not/existing",
+ vec![("--locked", PhpMixed::from(true))],
+ "Package \"not/existing\" not found in lock file, try using --available (-a) to show all available packages.",
+ );
+}
+
+#[ignore = "blocked: ShowCommand::configure() at src/command/show_command.rs:98-100 registers an empty set_definition() (TODO placeholder); no package argument or options are defined, so every show invocation fails with an option/argument-does-not-exist InvalidArgumentException (the outdated command proxies into show and fails identically). Unported src path; cannot fix from test files."]
+#[test]
+#[serial]
+fn test_not_existing_platform_with_platform_option() {
+ run_not_existing_package_case(
+ "ext-nonexisting",
+ vec![("--platform", PhpMixed::from(true))],
+ "Package \"ext-nonexisting\" not found, try using --available (-a) to show all available packages.",
+ );
+}
+
+#[ignore = "blocked: ShowCommand::configure() at src/command/show_command.rs:98-100 registers an empty set_definition() (TODO placeholder); no package argument or options are defined, so every show invocation fails with an option/argument-does-not-exist InvalidArgumentException (the outdated command proxies into show and fails identically). Unported src path; cannot fix from test files."]
+#[test]
+#[serial]
+fn test_not_existing_platform_without_platform_option() {
+ run_not_existing_package_case(
+ "ext-nonexisting",
+ vec![],
+ "Package \"ext-nonexisting\" not found, try using --platform (-p) to show platform packages, try using --available (-a) to show all available packages.",
+ );
+}
+
+#[ignore = "blocked: ShowCommand::configure() at src/command/show_command.rs:98-100 registers an empty set_definition() (TODO placeholder); no package argument or options are defined, so every show invocation fails with an option/argument-does-not-exist InvalidArgumentException (the outdated command proxies into show and fails identically). Unported src path; cannot fix from test files."]
+#[test]
+#[serial]
fn test_not_existing_package_with_working_dir() {
- todo!()
+ let tear_down = init_temp_composer(
+ Some(&serde_json::json!({
+ "require": {"vendor/package": "1.0.0"},
+ })),
+ None,
+ None,
+ true,
+ );
+ let dir = tear_down.working_dir();
+ create_installed_json(&[get_package("vendor/package", "1.0.0")], &[], true);
+
+ let expected = format!(
+ "Package \"not/existing\" not found in {}/composer.json, try using --available (-a) to show all available packages.",
+ dir.display()
+ );
+
+ let mut app_tester = get_application_tester();
+ let err = app_tester
+ .run(
+ input(vec![
+ ("command", PhpMixed::from("show")),
+ ("package", PhpMixed::from("not/existing")),
+ (
+ "--working-dir",
+ PhpMixed::from(dir.display().to_string().as_str()),
+ ),
+ ]),
+ RunOptions::default(),
+ )
+ .expect_err("expected an exception");
+ assert!(
+ err.to_string().contains(&expected),
+ "expected message starting with {:?}, got: {}",
+ expected,
+ err
+ );
+
+ drop(tear_down);
+}
+
+/// ref: ShowCommandTest::testSpecificPackageAndTree (one data-provider case).
+fn run_specific_package_and_tree_case(
+ packages: Vec<PackageInterfaceHandle>,
+ options: Vec<(&str, PhpMixed)>,
+ expected: &str,
+) {
+ let _tear_down = init_temp_composer(
+ Some(&serde_json::json!({
+ "require": {"vendor/package": "1.0.0"},
+ })),
+ None,
+ None,
+ true,
+ );
+
+ create_installed_json(&packages, &[], true);
+
+ let mut pairs = vec![
+ ("command", PhpMixed::from("show")),
+ ("package", PhpMixed::from("vendor/package")),
+ ("--tree", PhpMixed::from(true)),
+ ];
+ pairs.extend(options);
+
+ let mut app_tester = get_application_tester();
+ app_tester.run(input(pairs), RunOptions::default()).unwrap();
+ assert_eq!(expected, app_tester.get_display().trim());
+}
+
+#[ignore = "blocked: ShowCommand::configure() at src/command/show_command.rs:98-100 registers an empty set_definition() (TODO placeholder); no package argument or options are defined, so every show invocation fails with an option/argument-does-not-exist InvalidArgumentException (the outdated command proxies into show and fails identically). Unported src path; cannot fix from test files."]
+#[test]
+#[serial]
+fn test_specific_package_and_tree_just_package() {
+ let pkg = get_package("vendor/package", "1.0.0");
+ run_specific_package_and_tree_case(vec![pkg], vec![], "vendor/package 1.0.0");
}
-#[ignore = "missing TestCase::init_temp_composer, create_installed_json, and get_application_tester (ApplicationTester) infrastructure"]
+#[ignore = "blocked: ShowCommand::configure() at src/command/show_command.rs:98-100 registers an empty set_definition() (TODO placeholder); no package argument or options are defined, so every show invocation fails with an option/argument-does-not-exist InvalidArgumentException (the outdated command proxies into show and fails identically). Unported src path; cannot fix from test files."]
#[test]
-fn test_specific_package_and_tree() {
- todo!()
+#[serial]
+fn test_specific_package_and_tree_with_one_package_requirement() {
+ let pkg = get_complete_package("vendor/package", "1.0.0");
+ let mut requires = indexmap::IndexMap::new();
+ requires.insert(
+ "vendor/required-package".to_string(),
+ Link::new(
+ "vendor/package".to_string(),
+ "vendor/required-package".to_string(),
+ get_version_constraint("=", "1.0.0"),
+ Some(Link::TYPE_REQUIRE.to_string()),
+ "1.0.0".to_string(),
+ ),
+ );
+ pkg.__set_requires(requires);
+ run_specific_package_and_tree_case(
+ vec![pkg.into()],
+ vec![],
+ "vendor/package 1.0.0
+`--vendor/required-package 1.0.0",
+ );
}
-#[ignore = "missing TestCase::init_temp_composer, create_installed_json, and get_application_tester (ApplicationTester) infrastructure"]
+#[ignore = "blocked: ShowCommand::configure() at src/command/show_command.rs:98-100 registers an empty set_definition() (TODO placeholder); no package argument or options are defined, so every show invocation fails with an option/argument-does-not-exist InvalidArgumentException (the outdated command proxies into show and fails identically). Unported src path; cannot fix from test files."]
#[test]
+#[serial]
+fn test_specific_package_and_tree_with_platform_requirement() {
+ let pkg = get_complete_package("vendor/package", "1.0.0");
+ let mut requires = indexmap::IndexMap::new();
+ requires.insert(
+ "php".to_string(),
+ Link::new(
+ "vendor/package".to_string(),
+ "php".to_string(),
+ get_version_constraint("=", "8.2.0"),
+ Some(Link::TYPE_REQUIRE.to_string()),
+ "8.2.0".to_string(),
+ ),
+ );
+ pkg.__set_requires(requires);
+ run_specific_package_and_tree_case(
+ vec![pkg.into()],
+ vec![],
+ "vendor/package 1.0.0
+`--php 8.2.0",
+ );
+}
+
+#[ignore = "blocked: ShowCommand::configure() at src/command/show_command.rs:98-100 registers an empty set_definition() (TODO placeholder); no package argument or options are defined, so every show invocation fails with an option/argument-does-not-exist InvalidArgumentException (the outdated command proxies into show and fails identically). Unported src path; cannot fix from test files."]
+#[test]
+#[serial]
+fn test_specific_package_and_tree_with_json_format() {
+ let pkg = get_package("vendor/package", "1.0.0");
+ run_specific_package_and_tree_case(
+ vec![pkg],
+ vec![("--format", PhpMixed::from("json"))],
+ "{
+ \"installed\": [
+ {
+ \"name\": \"vendor/package\",
+ \"version\": \"1.0.0\",
+ \"description\": null
+ }
+ ]
+}",
+ );
+}
+
+#[ignore = "blocked: ShowCommand::configure() at src/command/show_command.rs:98-100 registers an empty set_definition() (TODO placeholder); no package argument or options are defined, so every show invocation fails with an option/argument-does-not-exist InvalidArgumentException (the outdated command proxies into show and fails identically). Unported src path; cannot fix from test files."]
+#[test]
+#[serial]
fn test_name_only_prints_no_trailing_whitespace() {
- todo!()
+ let _tear_down = init_temp_composer(
+ Some(&serde_json::json!({
+ "repositories": {
+ "packages": {
+ "type": "package",
+ "package": [
+ // CAUTION: package names matter - output is sorted, and we want shorter before longer ones
+ {"name": "vendor/apackage", "description": "generic description", "version": "1.0.0"},
+ {"name": "vendor/apackage", "description": "generic description", "version": "1.1.0"},
+ {"name": "vendor/longpackagename", "description": "generic description", "version": "1.0.0"},
+ {"name": "vendor/longpackagename", "description": "generic description", "version": "1.1.0"},
+ {"name": "vendor/somepackage", "description": "generic description", "version": "1.0.0"},
+ ],
+ },
+ },
+ })),
+ None,
+ None,
+ true,
+ );
+
+ create_installed_json(
+ &[
+ get_package("vendor/apackage", "1.0.0"),
+ get_package("vendor/longpackagename", "1.0.0"),
+ get_package("vendor/somepackage", "1.0.0"),
+ ],
+ &[],
+ true,
+ );
+
+ let mut app_tester = get_application_tester();
+ app_tester
+ .run(
+ input(vec![
+ ("command", PhpMixed::from("show")),
+ ("-N", PhpMixed::from(true)),
+ ]),
+ RunOptions::default(),
+ )
+ .unwrap();
+ assert_eq!(
+ "vendor/apackage
+vendor/longpackagename
+vendor/somepackage",
+ app_tester.get_display().trim()
+ );
+
+ let mut app_tester = get_application_tester();
+ app_tester
+ .run(
+ input(vec![
+ ("command", PhpMixed::from("show")),
+ ("--outdated", PhpMixed::from(true)),
+ ("-N", PhpMixed::from(true)),
+ ]),
+ RunOptions::default(),
+ )
+ .unwrap();
+ assert_eq!(
+ "Legend:
+! patch or minor release available - update recommended
+~ major release available - update possible
+vendor/apackage
+vendor/longpackagename",
+ app_tester.get_display().trim()
+ );
}
diff --git a/crates/shirabe/tests/command/validate_command_test.rs b/crates/shirabe/tests/command/validate_command_test.rs
index 31af4be..5167fc0 100644
--- a/crates/shirabe/tests/command/validate_command_test.rs
+++ b/crates/shirabe/tests/command/validate_command_test.rs
@@ -1,6 +1,8 @@
//! ref: composer/tests/Composer/Test/Command/ValidateCommandTest.php
-use crate::test_case::{RunOptions, get_application_tester, init_temp_composer};
+use crate::test_case::{
+ RunOptions, create_composer_lock, get_application_tester, init_temp_composer,
+};
use serial_test::serial;
use shirabe::util::platform::Platform;
use shirabe_php_shim::PhpMixed;
@@ -86,9 +88,9 @@ fn provide_validate_tests() -> Vec<ValidateCase> {
#[test]
#[serial]
-#[ignore = "validate creates a Composer instance (create_composer_instance -> Factory) which \
- reaches ProcessExecutor (git) -> shirabe-php-shim stream_set_blocking (stream.rs \
- todo!(), requires fcntl(2))"]
+#[ignore = "validate creates a Composer instance (Factory::create_composer) which panics in \
+ PartialComposerHandle::borrow_partial at crates/shirabe/src/composer.rs:446 \
+ ('RefCell already mutably borrowed'); source-level borrow conflict, not a test issue"]
fn test_validate() {
for case in provide_validate_tests() {
let _tear_down = init_temp_composer(Some(&case.composer_json), None, None, true);
@@ -128,11 +130,23 @@ fn test_validate_on_file_issues() {
#[test]
#[serial]
-#[ignore = "validate with a lock file creates a Composer instance and queries the Locker, reaching \
- ProcessExecutor (git) -> shirabe-php-shim stream_set_blocking (stream.rs todo!(), \
- requires fcntl(2))"]
+#[ignore = "validate with a lock file creates a Composer instance (Factory::create_composer) which \
+ panics in PartialComposerHandle::borrow_partial at crates/shirabe/src/composer.rs:446 \
+ ('RefCell already mutably borrowed'); source-level borrow conflict, not a test issue"]
fn test_with_composer_lock() {
- todo!()
+ let tear_down = init_temp_composer(Some(&minimal_valid_configuration()), None, None, true);
+ create_composer_lock(&[], &[]);
+
+ let mut app_tester = get_application_tester();
+ app_tester
+ .run(validate_input(vec![]), RunOptions::default())
+ .unwrap();
+
+ let expected = "<warning>Composer could not detect the root package (test/suite) version, defaulting to '1.0.0'. See https://getcomposer.org/root-version</warning>\n<warning>Composer could not detect the root package (test/suite) version, defaulting to '1.0.0'. See https://getcomposer.org/root-version</warning>\n./composer.json is valid but your composer.lock has some errors\n# Lock file errors\n- Required package \"root/req\" is not present in the lock file.\nThis usually happens when composer files are incorrectly merged or the composer.json file is manually edited.\nRead more about correctly resolving merge conflicts https://getcomposer.org/doc/articles/resolving-merge-conflicts.md\nand prefer using the \"require\" command over editing the composer.json file directly https://getcomposer.org/doc/03-cli.md#require-r";
+
+ assert_eq!(expected.trim(), app_tester.get_display().trim());
+
+ drop(tear_down);
}
#[test]