aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe/tests/command
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-08-07 07:01:50 +0900
committernsfisis <nsfisis@gmail.com>2026-08-07 07:01:50 +0900
commit759b2980e70dfb8960238f75d68bb6dddce25414 (patch)
treee37a0af6423f8ae8dd26da309b278d49d45b3451 /crates/shirabe/tests/command
parent9a393adc0ace86cac788723b524e83c63dfc91c1 (diff)
downloadphp-shirabe-759b2980e70dfb8960238f75d68bb6dddce25414.tar.gz
php-shirabe-759b2980e70dfb8960238f75d68bb6dddce25414.tar.zst
php-shirabe-759b2980e70dfb8960238f75d68bb6dddce25414.zip
test: port the tests left as todo!() stubs
Replace the todo!() bodies with real ports. Four autoload-generator tests now run for real; the rest stay #[ignore]d, but each ignore reason now names the concrete missing symbol instead of a vague subsystem. Production additions the ports need: the deprecated AuthHelper::addAuthenticationHeader wrapper, EventDispatcher::__set_dispatch_script_override as the seam for PHPUnit onlyMethods(['dispatchScript']), and a define() stub in the shim. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'crates/shirabe/tests/command')
-rw-r--r--crates/shirabe/tests/command/run_script_command_test.rs239
-rw-r--r--crates/shirabe/tests/command/self_update_command_test.rs138
2 files changed, 341 insertions, 36 deletions
diff --git a/crates/shirabe/tests/command/run_script_command_test.rs b/crates/shirabe/tests/command/run_script_command_test.rs
index 10e171ba..311091a0 100644
--- a/crates/shirabe/tests/command/run_script_command_test.rs
+++ b/crates/shirabe/tests/command/run_script_command_test.rs
@@ -121,20 +121,239 @@ fn test_can_define_aliases() {
drop(tear_down);
}
+/// ref: RunScriptCommandTest::testExecutionOfSimpleSymfonyCommand
#[test]
-#[ignore = "the test invokes the script name as a top-level composer command, which requires Application::do_run to import the user's PHP Command class (MyCommand.php) as a live application command (todo!() in application.rs: the worker-side console application exists, but the import arm is not wired to it), and the command's output would go to the worker's inherited stdio, which the in-process application tester cannot capture"]
+#[serial]
+#[ignore = "invoking the script name as a top-level composer command needs Application::do_run to import the user's PHP Command class as a live application command, which is a todo!() in application.rs, and the worker writes to inherited stdio the in-process application tester cannot capture"]
fn test_execution_of_simple_symfony_command() {
- // TODO(phase-d): the test invokes the script name as a top-level composer command, which
- // requires Application::do_run to import the user's PHP Command class (MyCommand.php) as a
- // live application command (todo!() in application.rs: the worker-side console application exists, but the import arm is not wired to it), and the worker writes to inherited stdio the tester cannot capture.
- todo!()
+ let description = "Sample description for test command";
+ let tear_down = init_temp_composer(
+ Some(&serde_json::json!({
+ "scripts": {
+ "test-direct": "Test\\MyCommand",
+ "test-ref": ["@test-direct --inneropt innerarg"],
+ },
+ "scripts-descriptions": {
+ "test-direct": description,
+ },
+ "autoload": {
+ "psr-4": {
+ "Test\\": "",
+ },
+ },
+ })),
+ None,
+ None,
+ true,
+ );
+
+ std::fs::write(
+ "MyCommand.php",
+ r#"<?php
+
+namespace Test;
+
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Command\Command;
+
+class MyCommand extends Command
+{
+ protected function configure(): void
+ {
+ $this->setDefinition([
+ new InputArgument('req-arg', InputArgument::REQUIRED, 'Required arg.'),
+ new InputArgument('opt-arg', InputArgument::OPTIONAL, 'Optional arg.'),
+ new InputOption('inneropt', null, InputOption::VALUE_NONE, 'Option.'),
+ new InputOption('outeropt', null, InputOption::VALUE_OPTIONAL, 'Optional option.'),
+ ]);
+ }
+
+ public function execute(InputInterface $input, OutputInterface $output): int
+ {
+ $output->writeln($input->getArgument('req-arg'));
+ $output->writeln((string) $input->getArgument('opt-arg'));
+ $output->writeln('inneropt: '.($input->getOption('inneropt') ? 'set' : 'unset'));
+ $output->writeln('outeropt: '.($input->getOption('outeropt') ? 'set' : 'unset'));
+
+ return 2;
+ }
+}
+"#,
+ )
+ .unwrap();
+
+ let mut app_tester = get_application_tester();
+ app_tester
+ .run(
+ vec![
+ (PhpMixed::from("command"), PhpMixed::from("test-direct")),
+ (PhpMixed::from("--outeropt"), PhpMixed::from(true)),
+ (PhpMixed::from("req-arg"), PhpMixed::from("lala")),
+ ],
+ RunOptions::default(),
+ )
+ .unwrap();
+
+ assert_eq!(
+ "lala\n\ninneropt: unset\nouteropt: set\n",
+ app_tester.get_display()
+ );
+ assert_eq!(2, app_tester.get_status_code());
+
+ let mut app_tester = get_application_tester();
+ app_tester
+ .run(
+ vec![
+ (PhpMixed::from("command"), PhpMixed::from("test-ref")),
+ (PhpMixed::from("--outeropt"), PhpMixed::from(true)),
+ (PhpMixed::from("req-arg"), PhpMixed::from("lala")),
+ ],
+ RunOptions::default(),
+ )
+ .unwrap();
+
+ assert_eq!(
+ "innerarg\nlala\ninneropt: set\nouteropt: set\n",
+ app_tester.get_display()
+ );
+ assert_eq!(2, app_tester.get_status_code());
+
+ // check if the description from composer.json is correctly shown
+ let mut app_tester = get_application_tester();
+ let status_code = app_tester
+ .run(
+ vec![
+ (PhpMixed::from("command"), PhpMixed::from("run-script")),
+ (PhpMixed::from("--list"), PhpMixed::from(true)),
+ ],
+ RunOptions::default(),
+ )
+ .unwrap();
+ assert_eq!(0, status_code, "assertCommandIsSuccessful");
+ let output = app_tester.get_display();
+ assert!(
+ output.contains(description),
+ "The contents of scripts-description for the test script should be printed"
+ );
+
+ drop(tear_down);
}
+/// ref: RunScriptCommandTest::testExecutionOfSymfonyCommandWithConfiguration
#[test]
-#[ignore = "the test invokes the script name as a top-level composer command, which requires Application::do_run to import the user's PHP Command class (MyCommandWithDefinitions.php) as a live application command (todo!() in application.rs: the worker-side console application exists, but the import arm is not wired to it), and the command's output would go to the worker's inherited stdio, which the in-process application tester cannot capture"]
+#[serial]
+#[ignore = "invoking the script name as a top-level composer command needs Application::do_run to import the user's PHP Command class as a live application command, which is a todo!() in application.rs, and the worker writes to inherited stdio the in-process application tester cannot capture"]
fn test_execution_of_symfony_command_with_configuration() {
- // TODO(phase-d): the test invokes the script name as a top-level composer command, which
- // requires Application::do_run to import the user's PHP Command class (MyCommandWithDefinitions.php)
- // as a live application command (todo!() in application.rs: the worker-side console application exists, but the import arm is not wired to it), and the worker writes to inherited stdio the tester cannot capture.
- todo!()
+ let cmd_name = "custom-cmd-123";
+ let cmd_alias = format!("{}-alias", cmd_name);
+ let cmd_desc = "This is a Symfony command with custom configuration";
+ let wrong_desc = "this should be ignored";
+
+ let tear_down = init_temp_composer(
+ Some(&serde_json::json!({
+ "scripts": {
+ cmd_name: "Test\\MyCommandWithDefinitions",
+ },
+ "scripts-descriptions": {
+ cmd_name: wrong_desc,
+ },
+ "autoload": {
+ "psr-4": {
+ "Test\\": "",
+ },
+ },
+ })),
+ None,
+ None,
+ true,
+ );
+
+ std::fs::write(
+ "MyCommandWithDefinitions.php",
+ r#"<?php
+
+namespace Test;
+
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Command\Command;
+
+class MyCommandWithDefinitions extends Command
+{
+ protected function configure(): void
+ {
+ $this
+ ->setDescription('__CMD_DESC__')
+ ->setAliases(['__CMD_ALIAS__'])
+ ->setDefinition([new InputArgument('req-arg', InputArgument::REQUIRED, 'Required arg.')]);
+ }
+
+ public function execute(InputInterface $input, OutputInterface $output): int
+ {
+ $output->writeln($input->getArgument('req-arg'));
+ return Command::SUCCESS;
+ }
+}
+"#
+ .replace("__CMD_DESC__", cmd_desc)
+ .replace("__CMD_ALIAS__", &cmd_alias),
+ )
+ .unwrap();
+
+ // makes sure the command executes with the name defined inside its `configure()`...
+ let mut app_tester = get_application_tester();
+ app_tester
+ .run(
+ vec![
+ (PhpMixed::from("command"), PhpMixed::from(cmd_name)),
+ (PhpMixed::from("req-arg"), PhpMixed::from("lala")),
+ ],
+ RunOptions::default(),
+ )
+ .unwrap();
+ assert_eq!("lala\n", app_tester.get_display());
+
+ // ...with the alias defined there as well...
+ let mut app_tester = get_application_tester();
+ app_tester
+ .run(
+ vec![
+ (
+ PhpMixed::from("command"),
+ PhpMixed::from(cmd_alias.as_str()),
+ ),
+ (PhpMixed::from("req-arg"), PhpMixed::from("lala")),
+ ],
+ RunOptions::default(),
+ )
+ .unwrap();
+ assert_eq!("lala\n", app_tester.get_display());
+
+ // ...and also uses its own description, instead of the one in composer.scripts-descriptions
+ let mut app_tester = get_application_tester();
+ let status_code = app_tester
+ .run(
+ vec![
+ (PhpMixed::from("command"), PhpMixed::from("run-script")),
+ (PhpMixed::from("--list"), PhpMixed::from(true)),
+ ],
+ RunOptions::default(),
+ )
+ .unwrap();
+ assert_eq!(0, status_code, "assertCommandIsSuccessful");
+ let output = app_tester.get_display();
+ assert!(
+ output.contains(cmd_desc),
+ "The custom description for the test script should be printed"
+ );
+ assert!(
+ !output.contains(wrong_desc),
+ "The dummy description shouldn't show"
+ );
+
+ drop(tear_down);
}
diff --git a/crates/shirabe/tests/command/self_update_command_test.rs b/crates/shirabe/tests/command/self_update_command_test.rs
index 3b73adbb..6dc64104 100644
--- a/crates/shirabe/tests/command/self_update_command_test.rs
+++ b/crates/shirabe/tests/command/self_update_command_test.rs
@@ -1,40 +1,102 @@
//! ref: composer/tests/Composer/Test/Command/SelfUpdateCommandTest.php
use crate::test_case::{RunOptions, get_application_tester, init_temp_composer};
+use indexmap::IndexMap;
use serial_test::serial;
-use shirabe_php_shim::PhpMixed;
+use shirabe_external_packages::symfony::process::Process;
+use shirabe_php_shim::{PHP_BINARY, PhpMixed};
-/// ref: SelfUpdateCommandTest::setUp (portable part: initTempComposer; the composer-test.phar copy
-/// is omitted because the phar fixture and Symfony Process are not ported).
+/// ref: SelfUpdateCommandTest::setUp. The `composer-test.phar` copy PHP also performs here lives in
+/// `set_up_with_phar` instead, so the one test that never touches the phar is not blocked by the
+/// missing fixture.
fn set_up() -> crate::test_case::TearDown {
init_temp_composer(None, None, None, true)
}
+/// ref: SelfUpdateCommandTest::setUp, including the `composer-test.phar` copy. Returns the tear-down
+/// guard and `$this->phar`.
+fn set_up_with_phar() -> (crate::test_case::TearDown, String) {
+ let tear_down = set_up();
+ let phar = tear_down.working_dir().join("composer.phar");
+ std::fs::copy(
+ std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
+ .join("../../composer/tests/composer-test.phar"),
+ &phar,
+ )
+ .unwrap();
+
+ (tear_down, phar.display().to_string())
+}
+
+/// ref: SelfUpdateCommandTest::channelOptions
+fn channel_options() -> Vec<(&'static str, &'static str)> {
+ vec![
+ ("--stable", "stable channel"),
+ ("--preview", "preview channel"),
+ ("--snapshot", "snapshot channel"),
+ ]
+}
+
#[test]
#[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"]
+#[ignore = "composer-test.phar is built by AllFunctionalTest::testBuildPhar via bin/compile, which has no equivalent here, so the fixture set_up_with_phar copies can never exist"]
fn test_successful_update() {
- let _tear_down = set_up();
+ let (_tear_down, phar) = set_up_with_phar();
+
+ if shirabe::composer::VERSION != concat!("@package_version", "@") {
+ eprintln!(
+ "skipping: On releases this test can fail to upgrade as we are already on latest version"
+ );
+ return;
+ }
+
+ let mut app_tester = Process::new(
+ vec![PHP_BINARY.to_string(), phar, "self-update".to_string()],
+ None,
+ None,
+ PhpMixed::Null,
+ None,
+ )
+ .unwrap();
+ let status = app_tester.run(None, IndexMap::new()).unwrap();
+ assert_eq!(0, status, "{}", app_tester.get_error_output().unwrap());
- // TODO(phase-d): 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.
- todo!()
+ assert!(
+ app_tester
+ .get_output()
+ .unwrap()
+ .contains("Upgrading to version")
+ );
}
#[test]
#[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"]
+#[ignore = "composer-test.phar is built by AllFunctionalTest::testBuildPhar via bin/compile, which has no equivalent here, so the fixture set_up_with_phar copies can never exist"]
fn test_update_to_specific_version() {
- let _tear_down = set_up();
+ let (_tear_down, phar) = set_up_with_phar();
+
+ let mut app_tester = Process::new(
+ vec![
+ PHP_BINARY.to_string(),
+ phar,
+ "self-update".to_string(),
+ "2.4.0".to_string(),
+ ],
+ None,
+ None,
+ PhpMixed::Null,
+ None,
+ )
+ .unwrap();
+ let status = app_tester.run(None, IndexMap::new()).unwrap();
+ assert_eq!(0, status, "{}", app_tester.get_error_output().unwrap());
- // TODO(phase-d): 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.
- todo!()
+ assert!(
+ app_tester
+ .get_output()
+ .unwrap()
+ .contains("Upgrading to version 2.4.0")
+ );
}
#[test]
@@ -63,14 +125,38 @@ fn test_update_with_invalid_option_throws_exception() {
#[test]
#[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"]
+#[ignore = "composer-test.phar is built by AllFunctionalTest::testBuildPhar via bin/compile, which has no equivalent here, so the fixture set_up_with_phar copies can never exist"]
fn test_update_to_different_channel() {
- let _tear_down = set_up();
+ for (option, expected_output) in channel_options() {
+ let (_tear_down, phar) = set_up_with_phar();
+
+ if shirabe::composer::VERSION != concat!("@package_version", "@")
+ && ["--stable", "--preview"].contains(&option)
+ {
+ eprintln!(
+ "skipping: On releases this test can fail to upgrade as we are already on latest version"
+ );
+ continue;
+ }
+
+ let mut app_tester = Process::new(
+ vec![
+ PHP_BINARY.to_string(),
+ phar,
+ "self-update".to_string(),
+ option.to_string(),
+ ],
+ None,
+ None,
+ PhpMixed::Null,
+ None,
+ )
+ .unwrap();
+ let status = app_tester.run(None, IndexMap::new()).unwrap();
+ assert_eq!(0, status, "{}", app_tester.get_error_output().unwrap());
- // TODO(phase-d): 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.
- todo!()
+ let output = app_tester.get_output().unwrap();
+ assert!(output.contains("Upgrading to version"));
+ assert!(output.contains(expected_output));
+ }
}