diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-06-22 23:42:07 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-06-22 23:42:07 +0900 |
| commit | 5ab5f3b316798c1411ce8e6a7f5b091fda93589c (patch) | |
| tree | c7669f94f5d9bd9adc07bc1aab3f14cfdec1ae5f /crates/shirabe/tests/command | |
| parent | b291e714bc739262140323e08fe2fb9e91e00ee7 (diff) | |
| download | php-shirabe-5ab5f3b316798c1411ce8e6a7f5b091fda93589c.tar.gz php-shirabe-5ab5f3b316798c1411ce8e6a7f5b091fda93589c.tar.zst php-shirabe-5ab5f3b316798c1411ce8e6a7f5b091fda93589c.zip | |
test: port previously-ignored Composer tests via __ test hatches
Re-evaluate the reason'd #[ignore] tests under the Phase D criterion:
a test is unportable ONLY if the APIs/types needed to WRITE it do not
exist. A test that compiles but panics at runtime (todo!() body, a
regex the regex crate cannot compile) or fails at runtime (incomplete
or incorrect impl behavior) is portable -- it is written in full and
marked with a reason-less #[ignore].
About 120 test functions move from reason'd #[ignore] to reason-less
#[ignore] (the ported-but-not-yet-passing signal). Impl crates gain
only additive __ test hatches (init_command, pool, file_downloader,
package handle link setters, artifact/path repository, repository
manager, svn); no existing logic changes. Tests whose required APIs
genuinely do not exist (mock/reflection harness, ApplicationTester,
solve() discarding SolverProblemsException, a script::Event that
cannot be passed as an originating event) keep their reason'd
#[ignore].
cargo check -p shirabe --tests passes.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'crates/shirabe/tests/command')
| -rw-r--r-- | crates/shirabe/tests/command/init_command_test.rs | 132 |
1 files changed, 115 insertions, 17 deletions
diff --git a/crates/shirabe/tests/command/init_command_test.rs b/crates/shirabe/tests/command/init_command_test.rs index 589afc5..6b118f3 100644 --- a/crates/shirabe/tests/command/init_command_test.rs +++ b/crates/shirabe/tests/command/init_command_test.rs @@ -1,38 +1,96 @@ //! ref: composer/tests/Composer/Test/Command/InitCommandTest.php -// The author/namespace/git-config helpers are protected methods exercised via reflection -// in PHP; the run cases need the ApplicationTester. Neither is available here. +// The run cases (testRunCommand, testRunCommandInvalid, testRunGuessNameFromDirSanitizesDir, +// testInteractiveRun) drive the full command via ApplicationTester / initTempComposer, which +// does not exist here; they remain reason'd-ignore. The unit-style cases call the helper +// methods directly via `__`-prefixed test-only wrappers. use shirabe::command::init_command::InitCommand; -use shirabe_php_shim::server_set; +use shirabe_php_shim::{PhpMixed, server_set}; +use tempfile::TempDir; fn set_up() { server_set("COMPOSER_DEFAULT_AUTHOR", "John Smith".to_string()); server_set("COMPOSER_DEFAULT_EMAIL", "john@example.com".to_string()); } -#[ignore = "InitCommand::parse_author_string is private; integration tests cannot reach it (PHP uses reflection)"] +/// @return iterable<string, array{0: string, 1: string|null, 2: string}> +fn valid_author_string_provider() -> Vec<(&'static str, Option<&'static str>, &'static str)> { + vec![ + // simple + ( + "John Smith", + Some("john@example.com"), + "John Smith <john@example.com>", + ), + // without email + ("John Smith", None, "John Smith"), + // UTF-8 + ( + "Matti Meikäläinen", + Some("matti@example.com"), + "Matti Meikäläinen <matti@example.com>", + ), + // UTF-8 with non-spacing marks (\xCC\x88 is U+0308 combining diaeresis) + ( + "Matti Meika\u{0308}la\u{0308}inen", + Some("matti@example.com"), + "Matti Meika\u{0308}la\u{0308}inen <matti@example.com>", + ), + // numeric author name + ("h4x0r", Some("h4x@example.com"), "h4x0r <h4x@example.com>"), + // alias 1 (issue #5631) + ( + "Johnathon \"Johnny\" Smith", + Some("john@example.com"), + "Johnathon \"Johnny\" Smith <john@example.com>", + ), + // alias 2 (issue #5631) + ( + "Johnathon (Johnny) Smith", + Some("john@example.com"), + "Johnathon (Johnny) Smith <john@example.com>", + ), + ] +} + +#[ignore] #[test] fn test_parse_valid_author_string() { set_up(); - todo!() + for (name, email, input) in valid_author_string_provider() { + let command = InitCommand::new(); + let author = command.__parse_author_string(input).unwrap(); + assert_eq!( + Some(name.to_string()), + author.get("name").cloned().flatten() + ); + assert_eq!( + email.map(|e| e.to_string()), + author.get("email").cloned().flatten() + ); + } } -#[ignore = "InitCommand::parse_author_string is private; integration tests cannot reach it (PHP uses reflection)"] +#[ignore] #[test] fn test_parse_empty_author_string() { set_up(); - todo!() + let command = InitCommand::new(); + let result = command.__parse_author_string(""); + assert!(result.is_err()); } -#[ignore = "InitCommand::parse_author_string is private; integration tests cannot reach it (PHP uses reflection)"] +#[ignore] #[test] fn test_parse_author_string_with_invalid_email() { set_up(); - todo!() + let command = InitCommand::new(); + let result = command.__parse_author_string("John Smith <john>"); + assert!(result.is_err()); } #[test] @@ -97,34 +155,74 @@ fn test_interactive_run() { todo!() } -#[ignore = "InitCommand::format_authors is pub(crate); integration tests cannot reach it (PHP subclasses via DummyInitCommand)"] +#[ignore] #[test] fn test_format_authors() { set_up(); - todo!() + let author_with_email = "John Smith <john@example.com>"; + let author_without_email = "John Smith"; + let command = InitCommand::new(); + + let authors = command.__format_authors(author_with_email).unwrap(); + let mut expected: indexmap::IndexMap<String, PhpMixed> = indexmap::IndexMap::new(); + expected.insert( + "name".to_string(), + PhpMixed::String("John Smith".to_string()), + ); + expected.insert( + "email".to_string(), + PhpMixed::String("john@example.com".to_string()), + ); + assert_eq!(expected, authors[0]); + + let authors = command.__format_authors(author_without_email).unwrap(); + let mut expected: indexmap::IndexMap<String, PhpMixed> = indexmap::IndexMap::new(); + expected.insert( + "name".to_string(), + PhpMixed::String("John Smith".to_string()), + ); + assert_eq!(expected, authors[0]); } -#[ignore = "InitCommand::get_git_config is pub(crate); integration tests cannot reach it (PHP subclasses via DummyInitCommand)"] +#[ignore] #[test] fn test_get_git_config() { set_up(); - todo!() + let mut command = InitCommand::new(); + let git_config = command.__get_git_config(); + assert!(git_config.contains_key("user.name")); + assert!(git_config.contains_key("user.email")); } -#[ignore = "InitCommand::add_vendor_ignore is pub(crate) and test needs TestCase::get_unique_tmp_directory; neither reachable from integration tests"] +#[ignore] #[test] fn test_add_vendor_ignore() { set_up(); - todo!() + let tmp = TempDir::new().unwrap(); + let ignore_file = tmp.path().join("ignore"); + let ignore_file = ignore_file.to_str().unwrap(); + + let command = InitCommand::new(); + command.__add_vendor_ignore(ignore_file, "/vendor/"); + assert!(std::path::Path::new(ignore_file).exists()); + let content = std::fs::read_to_string(ignore_file).unwrap(); + assert!(content.contains("/vendor/")); } -#[ignore = "InitCommand::has_vendor_ignore/add_vendor_ignore are pub(crate) and test needs TestCase::get_unique_tmp_directory; neither reachable from integration tests"] +#[ignore] #[test] fn test_has_vendor_ignore() { set_up(); - todo!() + let tmp = TempDir::new().unwrap(); + let ignore_file = tmp.path().join("ignore"); + let ignore_file = ignore_file.to_str().unwrap(); + + let command = InitCommand::new(); + assert!(!command.__has_vendor_ignore(ignore_file, "vendor")); + command.__add_vendor_ignore(ignore_file, "/vendor/"); + assert!(command.__has_vendor_ignore(ignore_file, "vendor")); } |
