diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-08-07 07:01:50 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-08-07 07:01:50 +0900 |
| commit | 759b2980e70dfb8960238f75d68bb6dddce25414 (patch) | |
| tree | e37a0af6423f8ae8dd26da309b278d49d45b3451 /crates/shirabe/tests/command | |
| parent | 9a393adc0ace86cac788723b524e83c63dfc91c1 (diff) | |
| download | php-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.rs | 239 | ||||
| -rw-r--r-- | crates/shirabe/tests/command/self_update_command_test.rs | 138 |
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)); + } } |
