diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-06-25 15:12:06 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-06-26 00:20:05 +0900 |
| commit | 8117e5450693d19726da877bce8aacf5c7aa53af (patch) | |
| tree | 4b06e6f6924599f3402d29cdcdeb0f8a88354d89 /crates/shirabe/tests/repository/vcs/github_driver_test.rs | |
| parent | 8dd3d67884a204ca40e3364206868fea77a312be (diff) | |
| download | php-shirabe-8117e5450693d19726da877bce8aacf5c7aa53af.tar.gz php-shirabe-8117e5450693d19726da877bce8aacf5c7aa53af.tar.zst php-shirabe-8117e5450693d19726da877bce8aacf5c7aa53af.zip | |
test: port 44 vcs/downloader/version tests using mock infra
Port git, version_guesser, gitlab_driver, github_driver, and git_downloader
tests using the ProcessExecutor/HttpDownloader mocks and IO/Config stubs.
Fix production regex-porting bugs surfaced by the now-reachable paths:
Url::sanitize and Response::find_header_value had non-delimited PCRE patterns;
implement array_search_mixed non-strict branch and a datetime format mapping.
Add HttpDownloader::__new_mock so mocked downloaders skip curl construction.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'crates/shirabe/tests/repository/vcs/github_driver_test.rs')
| -rw-r--r-- | crates/shirabe/tests/repository/vcs/github_driver_test.rs | 803 |
1 files changed, 771 insertions, 32 deletions
diff --git a/crates/shirabe/tests/repository/vcs/github_driver_test.rs b/crates/shirabe/tests/repository/vcs/github_driver_test.rs index f5958a4..0469edf 100644 --- a/crates/shirabe/tests/repository/vcs/github_driver_test.rs +++ b/crates/shirabe/tests/repository/vcs/github_driver_test.rs @@ -5,13 +5,22 @@ use std::rc::Rc; use indexmap::IndexMap; use shirabe::config::Config; +use shirabe::config::ConfigSourceInterface; use shirabe::io::IOInterface; use shirabe::io::null_io::NullIO; use shirabe::repository::vcs::GitHubDriver; use shirabe::util::filesystem::Filesystem; +use shirabe::util::http_downloader::HttpDownloaderMockHandler; +use shirabe::util::process_executor::{MockHandler, ProcessExecutor}; use shirabe_php_shim::PhpMixed; use tempfile::TempDir; +use crate::http_downloader_mock::{HttpDownloaderMockGuard, expect_full, get_http_downloader_mock}; +use crate::io_stub::IOStub; +use crate::process_executor_mock::{ + ProcessExecutorMockGuard, cmd, cmd_full, get_process_executor_mock, +}; + struct SetUp { home: TempDir, config: Config, @@ -53,6 +62,82 @@ impl Drop for TearDown { } } +// No-op ConfigSourceInterface, equivalent to PHPUnit's +// `getMockBuilder(ConfigSourceInterface::class)->getMock()` whose methods all do +// nothing. Used by testPrivateRepository to satisfy the OAuth token-storing path. +#[derive(Debug)] +struct NullConfigSource; + +impl ConfigSourceInterface for NullConfigSource { + fn add_repository( + &mut self, + _name: &str, + _config: PhpMixed, + _append: bool, + ) -> anyhow::Result<()> { + Ok(()) + } + fn insert_repository( + &mut self, + _name: &str, + _config: PhpMixed, + _reference_name: &str, + _offset: i64, + ) -> anyhow::Result<()> { + Ok(()) + } + fn set_repository_url(&mut self, _name: &str, _url: &str) -> anyhow::Result<()> { + Ok(()) + } + fn remove_repository(&mut self, _name: &str) -> anyhow::Result<()> { + Ok(()) + } + fn add_config_setting(&mut self, _name: &str, _value: PhpMixed) -> anyhow::Result<()> { + Ok(()) + } + fn remove_config_setting(&mut self, _name: &str) -> anyhow::Result<()> { + Ok(()) + } + fn add_property(&mut self, _name: &str, _value: PhpMixed) -> anyhow::Result<()> { + Ok(()) + } + fn remove_property(&mut self, _name: &str) -> anyhow::Result<()> { + Ok(()) + } + fn add_link(&mut self, r#type: &str, name: &str, value: &str) -> anyhow::Result<()> { + let _ = (r#type, name, value); + Ok(()) + } + fn remove_link(&mut self, r#type: &str, name: &str) -> anyhow::Result<()> { + let _ = (r#type, name); + Ok(()) + } + fn get_name(&self) -> String { + String::new() + } +} + +// Mirrors PHP's $httpDownloader->expects([...], true): an `['url' => .., 'body' => ..]` +// entry (status defaults to 200). +fn http_body( + url: &str, + body: impl Into<String>, +) -> shirabe::util::http_downloader::HttpDownloaderMockExpectation { + expect_full(url, None, 200, body, vec![String::new()]) +} + +// Mirrors PHP's `['url' => .., 'status' => 404]` entry (no body). +fn http_status( + url: &str, + status: i64, +) -> shirabe::util::http_downloader::HttpDownloaderMockExpectation { + expect_full(url, None, status, String::new(), vec![String::new()]) +} + +fn b64(s: &str) -> String { + shirabe_php_shim::base64_encode(s) +} + fn supports_provider() -> Vec<(bool, &'static str)> { vec![ (false, "https://github.com/acme"), @@ -80,77 +165,731 @@ fn test_supports() { } } -// The remaining cases construct a GitHubDriver and mock the HttpDownloader/IO to return -// GitHub API responses; mocking is not available, and a real HttpDownloader reaches -// curl_multi_init (todo!()). #[test] -#[ignore = "HttpDownloaderMock (getHttpDownloaderMock), ProcessExecutorMock, the IOInterface MockObject (askAndHideAnswer/setAuthentication), and setAttribute reflection are not ported"] fn test_private_repository() { - let SetUp { home, config: _ } = set_up(); + let SetUp { home, config } = set_up(); let _tear_down = TearDown::new(home.path().to_path_buf()); - todo!() + + let repo_url = "http://github.com/composer/packagist"; + let repo_api_url = "https://api.github.com/repos/composer/packagist"; + let repo_ssh_url = "git@github.com:composer/packagist.git"; + let identifier = "v0.0.0"; + let sha = "SOMESHA"; + + let config = Rc::new(RefCell::new(config)); + + let io: Rc<RefCell<dyn IOInterface>> = Rc::new(RefCell::new( + IOStub::new() + .with_is_interactive(true) + .with_ask_and_hide_answer(Some("sometoken".to_string())), + )); + + let (http_downloader, _http_guard): (_, HttpDownloaderMockGuard) = get_http_downloader_mock( + vec![ + http_status(repo_api_url, 404), + http_body("https://api.github.com/", "{}"), + http_body( + repo_api_url, + r#"{"master_branch": "test_master", "private": true, "owner": {"login": "composer"}, "name": "packagist"}"#, + ), + ], + true, + HttpDownloaderMockHandler::default(), + ); + + let (process, _process_guard): (_, ProcessExecutorMockGuard) = get_process_executor_mock( + vec![], + false, + MockHandler { + r#return: 1, + stdout: String::new(), + stderr: String::new(), + }, + ); + + config + .borrow_mut() + .set_config_source(Box::new(NullConfigSource)); + config + .borrow_mut() + .set_auth_config_source(Box::new(NullConfigSource)); + + let mut repo_config: IndexMap<String, PhpMixed> = IndexMap::new(); + repo_config.insert("url".to_string(), PhpMixed::String(repo_url.to_string())); + + let mut git_hub_driver = GitHubDriver::new(repo_config, io, config, http_downloader, process); + git_hub_driver.initialize().unwrap(); + let mut tags = IndexMap::new(); + tags.insert(identifier.to_string(), sha.to_string()); + git_hub_driver.__set_tags(Some(tags)); + + assert_eq!("test_master", git_hub_driver.get_root_identifier().unwrap()); + + let dist = git_hub_driver.get_dist(sha).unwrap(); + assert_eq!(Some("zip"), dist.get("type").and_then(|v| v.as_string())); + assert_eq!( + Some("https://api.github.com/repos/composer/packagist/zipball/SOMESHA"), + dist.get("url").and_then(|v| v.as_string()) + ); + assert_eq!( + Some("SOMESHA"), + dist.get("reference").and_then(|v| v.as_string()) + ); + + let source = git_hub_driver.get_source(sha); + assert_eq!(Some("git"), source.get("type").and_then(|v| v.as_string())); + assert_eq!( + Some(repo_ssh_url), + source.get("url").and_then(|v| v.as_string()) + ); + assert_eq!( + Some("SOMESHA"), + source.get("reference").and_then(|v| v.as_string()) + ); } #[test] -#[ignore = "HttpDownloaderMock (getHttpDownloaderMock), ProcessExecutorMock, the IOInterface MockObject, and setAttribute reflection are not ported"] fn test_public_repository() { - let SetUp { home, config: _ } = set_up(); + let SetUp { home, config } = set_up(); let _tear_down = TearDown::new(home.path().to_path_buf()); - todo!() + + let repo_url = "http://github.com/composer/packagist"; + let repo_api_url = "https://api.github.com/repos/composer/packagist"; + let identifier = "v0.0.0"; + let sha = "SOMESHA"; + + let config = Rc::new(RefCell::new(config)); + let io: Rc<RefCell<dyn IOInterface>> = + Rc::new(RefCell::new(IOStub::new().with_is_interactive(true))); + + let (http_downloader, _http_guard) = get_http_downloader_mock( + vec![http_body( + repo_api_url, + r#"{"master_branch": "test_master", "owner": {"login": "composer"}, "name": "packagist"}"#, + )], + true, + HttpDownloaderMockHandler::default(), + ); + + let process = Rc::new(RefCell::new(ProcessExecutor::new(None))); + + let mut repo_config: IndexMap<String, PhpMixed> = IndexMap::new(); + repo_config.insert("url".to_string(), PhpMixed::String(repo_url.to_string())); + let repo_url = "https://github.com/composer/packagist.git"; + + let mut git_hub_driver = GitHubDriver::new(repo_config, io, config, http_downloader, process); + git_hub_driver.initialize().unwrap(); + let mut tags = IndexMap::new(); + tags.insert(identifier.to_string(), sha.to_string()); + git_hub_driver.__set_tags(Some(tags)); + + assert_eq!("test_master", git_hub_driver.get_root_identifier().unwrap()); + + let dist = git_hub_driver.get_dist(sha).unwrap(); + assert_eq!(Some("zip"), dist.get("type").and_then(|v| v.as_string())); + assert_eq!( + Some("https://api.github.com/repos/composer/packagist/zipball/SOMESHA"), + dist.get("url").and_then(|v| v.as_string()) + ); + assert_eq!(Some(sha), dist.get("reference").and_then(|v| v.as_string())); + + let source = git_hub_driver.get_source(sha); + assert_eq!(Some("git"), source.get("type").and_then(|v| v.as_string())); + assert_eq!( + Some(repo_url), + source.get("url").and_then(|v| v.as_string()) + ); + assert_eq!( + Some(sha), + source.get("reference").and_then(|v| v.as_string()) + ); } #[test] -#[ignore = "HttpDownloaderMock (getHttpDownloaderMock), ProcessExecutorMock, the IOInterface MockObject, and setAttribute reflection are not ported"] +#[ignore = "blocked by shim: getComposerInformation -> get_change_date -> shirabe_php_shim::date_create is todo!()"] fn test_public_repository2() { - let SetUp { home, config: _ } = set_up(); + let SetUp { home, config } = set_up(); let _tear_down = TearDown::new(home.path().to_path_buf()); - todo!() + + let repo_url = "http://github.com/composer/packagist"; + let repo_api_url = "https://api.github.com/repos/composer/packagist"; + let identifier = "feature/3.2-foo"; + let sha = "SOMESHA"; + + let config = Rc::new(RefCell::new(config)); + let io: Rc<RefCell<dyn IOInterface>> = + Rc::new(RefCell::new(IOStub::new().with_is_interactive(true))); + + let (http_downloader, _http_guard) = get_http_downloader_mock( + vec![ + http_body( + repo_api_url, + r#"{"master_branch": "test_master", "owner": {"login": "composer"}, "name": "packagist"}"#, + ), + http_body( + "https://api.github.com/repos/composer/packagist/contents/composer.json?ref=feature%2F3.2-foo", + format!( + r#"{{"encoding":"base64","content":"{}"}}"#, + b64(&format!(r#"{{"support": {{"source": "{}" }}}}"#, repo_url)) + ), + ), + http_body( + "https://api.github.com/repos/composer/packagist/commits/feature%2F3.2-foo", + r#"{"commit": {"committer":{ "date": "2012-09-10"}}}"#, + ), + http_body( + "https://api.github.com/repos/composer/packagist/contents/.github/FUNDING.yml", + format!( + r#"{{"encoding": "base64", "content": "{}"}}"#, + b64("custom: https://example.com") + ), + ), + ], + true, + HttpDownloaderMockHandler::default(), + ); + + let process = Rc::new(RefCell::new(ProcessExecutor::new(None))); + + let mut repo_config: IndexMap<String, PhpMixed> = IndexMap::new(); + repo_config.insert("url".to_string(), PhpMixed::String(repo_url.to_string())); + let repo_url = "https://github.com/composer/packagist.git"; + + let mut git_hub_driver = GitHubDriver::new(repo_config, io, config, http_downloader, process); + git_hub_driver.initialize().unwrap(); + let mut tags = IndexMap::new(); + tags.insert(identifier.to_string(), sha.to_string()); + git_hub_driver.__set_tags(Some(tags)); + + assert_eq!("test_master", git_hub_driver.get_root_identifier().unwrap()); + + let dist = git_hub_driver.get_dist(sha).unwrap(); + assert_eq!(Some("zip"), dist.get("type").and_then(|v| v.as_string())); + assert_eq!( + Some("https://api.github.com/repos/composer/packagist/zipball/SOMESHA"), + dist.get("url").and_then(|v| v.as_string()) + ); + assert_eq!(Some(sha), dist.get("reference").and_then(|v| v.as_string())); + + let source = git_hub_driver.get_source(sha); + assert_eq!(Some("git"), source.get("type").and_then(|v| v.as_string())); + assert_eq!( + Some(repo_url), + source.get("url").and_then(|v| v.as_string()) + ); + assert_eq!( + Some(sha), + source.get("reference").and_then(|v| v.as_string()) + ); + + let data = git_hub_driver + .get_composer_information(identifier) + .unwrap() + .unwrap(); + assert!(!data.contains_key("abandoned")); } #[test] -#[ignore = "HttpDownloaderMock (getHttpDownloaderMock), ProcessExecutorMock, the IOInterface MockObject, and setAttribute reflection are not ported"] +#[ignore = "blocked by shim: getComposerInformation -> get_change_date -> shirabe_php_shim::date_create is todo!()"] fn test_invalid_support_data() { - let SetUp { home, config: _ } = set_up(); + let SetUp { home, config } = set_up(); let _tear_down = TearDown::new(home.path().to_path_buf()); - todo!() + + let repo_url = "http://github.com/composer/packagist"; + let repo_api_url = "https://api.github.com/repos/composer/packagist"; + let identifier = "feature/3.2-foo"; + let sha = "SOMESHA"; + + let config = Rc::new(RefCell::new(config)); + let io: Rc<RefCell<dyn IOInterface>> = + Rc::new(RefCell::new(IOStub::new().with_is_interactive(true))); + + let (http_downloader, _http_guard) = get_http_downloader_mock( + vec![ + http_body( + repo_api_url, + r#"{"master_branch": "test_master", "owner": {"login": "composer"}, "name": "packagist"}"#, + ), + http_body( + "https://api.github.com/repos/composer/packagist/contents/composer.json?ref=feature%2F3.2-foo", + format!( + r#"{{"encoding":"base64","content":"{}"}}"#, + b64(&format!(r#"{{"support": "{}" }}"#, repo_url)) + ), + ), + http_body( + "https://api.github.com/repos/composer/packagist/commits/feature%2F3.2-foo", + r#"{"commit": {"committer":{ "date": "2012-09-10"}}}"#, + ), + http_body( + "https://api.github.com/repos/composer/packagist/contents/.github/FUNDING.yml", + format!( + r#"{{"encoding": "base64", "content": "{}"}}"#, + b64("custom: https://example.com") + ), + ), + ], + true, + HttpDownloaderMockHandler::default(), + ); + + let process = Rc::new(RefCell::new(ProcessExecutor::new(None))); + + let mut repo_config: IndexMap<String, PhpMixed> = IndexMap::new(); + repo_config.insert("url".to_string(), PhpMixed::String(repo_url.to_string())); + + let mut git_hub_driver = GitHubDriver::new(repo_config, io, config, http_downloader, process); + git_hub_driver.initialize().unwrap(); + let mut tags = IndexMap::new(); + tags.insert(identifier.to_string(), sha.to_string()); + git_hub_driver.__set_tags(Some(tags)); + let mut branches = IndexMap::new(); + branches.insert("test_master".to_string(), sha.to_string()); + git_hub_driver.__set_branches(Some(branches)); + + let data = git_hub_driver + .get_composer_information(identifier) + .unwrap() + .unwrap(); + + let support = data.get("support").and_then(|v| v.as_array()).unwrap(); + assert_eq!( + Some("https://github.com/composer/packagist/tree/feature/3.2-foo"), + support.get("source").and_then(|v| v.as_string()) + ); +} + +fn funding_url_provider() -> Vec<(&'static str, Option<Vec<(&'static str, &'static str)>>)> { + let all_named_platforms = "community_bridge: project-name +github: [userA, userB] +issuehunt: userName +ko_fi: userName +liberapay: userName +open_collective: userName +patreon: userName +tidelift: Platform/Package +polar: userName +buy_me_a_coffee: userName +thanks_dev: u/gh/userName +otechie: userName"; + + vec![ + ( + all_named_platforms, + Some(vec![ + ( + "community_bridge", + "https://funding.communitybridge.org/projects/project-name", + ), + ("github", "https://github.com/userA"), + ("github", "https://github.com/userB"), + ("issuehunt", "https://issuehunt.io/r/userName"), + ("ko_fi", "https://ko-fi.com/userName"), + ("liberapay", "https://liberapay.com/userName"), + ("open_collective", "https://opencollective.com/userName"), + ("patreon", "https://www.patreon.com/userName"), + ( + "tidelift", + "https://tidelift.com/funding/github/Platform/Package", + ), + ("polar", "https://polar.sh/userName"), + ("buy_me_a_coffee", "https://www.buymeacoffee.com/userName"), + ("thanks_dev", "https://thanks.dev/u/gh/userName"), + ("otechie", "https://otechie.com/userName"), + ]), + ), + ( + "custom: example.com", + Some(vec![("custom", "https://example.com")]), + ), + ( + "custom: [example.com]", + Some(vec![("custom", "https://example.com")]), + ), + ( + "custom: \"https://example.com\"", + Some(vec![("custom", "https://example.com")]), + ), + ( + "custom: [\"https://example.com\"]", + Some(vec![("custom", "https://example.com")]), + ), + ( + "custom: [\"https://example.com\", example.org]", + Some(vec![ + ("custom", "https://example.com"), + ("custom", "https://example.org"), + ]), + ), + ( + "custom: [example.net/funding, \"https://example.com\", example.org]", + Some(vec![ + ("custom", "https://example.com"), + ("custom", "https://example.org"), + ]), + ), + ] } #[test] -#[ignore = "HttpDownloaderMock (getHttpDownloaderMock), ProcessExecutorMock, the IOInterface MockObject, setAttribute reflection, and the fundingUrlProvider data provider are not ported"] +#[ignore = "blocked by shim: getComposerInformation -> get_change_date -> shirabe_php_shim::date_create is todo!()"] fn test_funding_format() { - let SetUp { home, config: _ } = set_up(); - let _tear_down = TearDown::new(home.path().to_path_buf()); - todo!() + for (funding, expected) in funding_url_provider() { + let SetUp { home, config } = set_up(); + let _tear_down = TearDown::new(home.path().to_path_buf()); + + let repo_url = "http://github.com/composer/packagist"; + let repo_api_url = "https://api.github.com/repos/composer/packagist"; + let identifier = "feature/3.2-foo"; + let sha = "SOMESHA"; + + let config = Rc::new(RefCell::new(config)); + let io: Rc<RefCell<dyn IOInterface>> = + Rc::new(RefCell::new(IOStub::new().with_is_interactive(true))); + + let (http_downloader, _http_guard) = get_http_downloader_mock( + vec![ + http_body( + repo_api_url, + r#"{"master_branch": "test_master", "owner": {"login": "composer"}, "name": "packagist"}"#, + ), + http_body( + "https://api.github.com/repos/composer/packagist/contents/composer.json?ref=feature%2F3.2-foo", + format!( + r#"{{"encoding":"base64","content":"{}"}}"#, + b64(&format!(r#"{{"support": {{"source": "{}" }}}}"#, repo_url)) + ), + ), + http_body( + "https://api.github.com/repos/composer/packagist/commits/feature%2F3.2-foo", + r#"{"commit": {"committer":{ "date": "2012-09-10"}}}"#, + ), + http_body( + "https://api.github.com/repos/composer/packagist/contents/.github/FUNDING.yml", + format!(r#"{{"encoding": "base64", "content": "{}"}}"#, b64(funding)), + ), + ], + true, + HttpDownloaderMockHandler::default(), + ); + + let process = Rc::new(RefCell::new(ProcessExecutor::new(None))); + + let mut repo_config: IndexMap<String, PhpMixed> = IndexMap::new(); + repo_config.insert("url".to_string(), PhpMixed::String(repo_url.to_string())); + + let mut git_hub_driver = + GitHubDriver::new(repo_config, io, config, http_downloader, process); + git_hub_driver.initialize().unwrap(); + let mut tags = IndexMap::new(); + tags.insert(identifier.to_string(), sha.to_string()); + git_hub_driver.__set_tags(Some(tags)); + let mut branches = IndexMap::new(); + branches.insert("test_master".to_string(), sha.to_string()); + git_hub_driver.__set_branches(Some(branches)); + + let data = git_hub_driver + .get_composer_information(identifier) + .unwrap() + .unwrap(); + + match expected { + None => assert!(!data.contains_key("funding")), + Some(expected) => { + let funding_list = match data.get("funding") { + Some(PhpMixed::List(l)) => l.clone(), + Some(PhpMixed::Array(m)) => m.values().cloned().collect(), + other => panic!("unexpected funding value: {:?}", other), + }; + let actual: Vec<(String, String)> = funding_list + .iter() + .map(|entry| { + let m = entry.as_array().unwrap(); + ( + m.get("type") + .and_then(|v| v.as_string()) + .unwrap_or("") + .to_string(), + m.get("url") + .and_then(|v| v.as_string()) + .unwrap_or("") + .to_string(), + ) + }) + .collect(); + let expected_vec: Vec<(String, String)> = expected + .iter() + .map(|(t, u)| (t.to_string(), u.to_string())) + .collect(); + assert_eq!(expected_vec, actual); + } + } + } } #[test] -#[ignore = "HttpDownloaderMock (getHttpDownloaderMock), ProcessExecutorMock, the IOInterface MockObject, and setAttribute reflection are not ported"] +#[ignore = "blocked by shim: getComposerInformation -> get_change_date -> shirabe_php_shim::date_create is todo!()"] fn test_public_repository_archived() { - let SetUp { home, config: _ } = set_up(); + let SetUp { home, config } = set_up(); let _tear_down = TearDown::new(home.path().to_path_buf()); - todo!() + + let repo_url = "http://github.com/composer/packagist"; + let repo_api_url = "https://api.github.com/repos/composer/packagist"; + let identifier = "v0.0.0"; + let sha = "SOMESHA"; + let composer_json_url = format!( + "https://api.github.com/repos/composer/packagist/contents/composer.json?ref={}", + sha + ); + + let config = Rc::new(RefCell::new(config)); + let io: Rc<RefCell<dyn IOInterface>> = + Rc::new(RefCell::new(IOStub::new().with_is_interactive(true))); + + let (http_downloader, _http_guard) = get_http_downloader_mock( + vec![ + http_body( + repo_api_url, + r#"{"master_branch": "test_master", "owner": {"login": "composer"}, "name": "packagist", "archived": true}"#, + ), + http_body( + &composer_json_url, + format!( + r#"{{"encoding": "base64", "content": "{}"}}"#, + b64(r#"{"name": "composer/packagist"}"#) + ), + ), + http_body( + &format!( + "https://api.github.com/repos/composer/packagist/commits/{}", + sha + ), + r#"{"commit": {"committer":{ "date": "2012-09-10"}}}"#, + ), + http_body( + "https://api.github.com/repos/composer/packagist/contents/.github/FUNDING.yml", + format!( + r#"{{"encoding": "base64", "content": "{}"}}"#, + b64("custom: https://example.com") + ), + ), + ], + true, + HttpDownloaderMockHandler::default(), + ); + + let process = Rc::new(RefCell::new(ProcessExecutor::new(None))); + + let mut repo_config: IndexMap<String, PhpMixed> = IndexMap::new(); + repo_config.insert("url".to_string(), PhpMixed::String(repo_url.to_string())); + + let mut git_hub_driver = GitHubDriver::new(repo_config, io, config, http_downloader, process); + git_hub_driver.initialize().unwrap(); + let mut tags = IndexMap::new(); + tags.insert(identifier.to_string(), sha.to_string()); + git_hub_driver.__set_tags(Some(tags)); + + let data = git_hub_driver + .get_composer_information(sha) + .unwrap() + .unwrap(); + + assert_eq!(Some(true), data.get("abandoned").and_then(|v| v.as_bool())); } #[test] -#[ignore = "HttpDownloaderMock (getHttpDownloaderMock), ProcessExecutorMock, and the IOInterface MockObject are not ported"] +#[ignore = "GitDriver clone-fallback path runs an unexpected `git --version` (Git::get_version) not in the PHP mock expectation list; needs the version static seeded and the Rust sync_mirror command sequence to match"] fn test_private_repository_no_interaction() { - let SetUp { home, config: _ } = set_up(); + let SetUp { home, config } = set_up(); let _tear_down = TearDown::new(home.path().to_path_buf()); - todo!() + + let repo_url = "http://github.com/composer/packagist"; + let repo_api_url = "https://api.github.com/repos/composer/packagist"; + let repo_ssh_url = "git@github.com:composer/packagist.git"; + let identifier = "v0.0.0"; + let sha = "SOMESHA"; + + let config = Rc::new(RefCell::new(config)); + let io: Rc<RefCell<dyn IOInterface>> = + Rc::new(RefCell::new(IOStub::new().with_is_interactive(false))); + + let (http_downloader, _http_guard) = get_http_downloader_mock( + vec![http_status(repo_api_url, 404)], + true, + HttpDownloaderMockHandler::default(), + ); + + // clean local clone if present + let cache_dir = std::env::temp_dir().join("composer-test"); + let mut fs = Filesystem::new(None); + let _ = fs.remove_directory(&cache_dir); + let cache_vcs_dir = std::env::temp_dir() + .join("composer-test/cache") + .to_string_lossy() + .into_owned(); + { + let mut top: IndexMap<String, PhpMixed> = IndexMap::new(); + let mut config_section: IndexMap<String, PhpMixed> = IndexMap::new(); + config_section.insert( + "cache-vcs-dir".to_string(), + PhpMixed::String(cache_vcs_dir.clone()), + ); + top.insert("config".to_string(), PhpMixed::Array(config_section)); + config.borrow_mut().merge(&top, Config::SOURCE_UNKNOWN); + } + let resolved_cache_vcs_dir = config + .borrow_mut() + .get("cache-vcs-dir") + .as_string() + .unwrap_or("") + .to_string(); + + let (process, _process_guard): (_, ProcessExecutorMockGuard) = get_process_executor_mock( + vec![ + cmd_full(["git", "config", "github.accesstoken"], 1, "", ""), + cmd([ + "git", + "clone", + "--mirror", + "--", + repo_ssh_url, + &format!( + "{}/git-github.com-composer-packagist.git/", + resolved_cache_vcs_dir + ), + ]), + cmd(["git", "remote", "-v"]), + cmd(["git", "remote", "set-url", "origin", "--", repo_ssh_url]), + cmd_full( + ["git", "show-ref", "--tags", "--dereference"], + 0, + format!("{} refs/tags/{}", sha, identifier), + "", + ), + cmd_full( + ["git", "branch", "--no-color", "--no-abbrev", "-v"], + 0, + " test_master edf93f1fccaebd8764383dc12016d0a1a9672d89 Fix test & behavior", + "", + ), + cmd_full(["git", "branch", "--no-color"], 0, "* test_master", ""), + ], + true, + MockHandler::default(), + ); + + let mut repo_config: IndexMap<String, PhpMixed> = IndexMap::new(); + repo_config.insert("url".to_string(), PhpMixed::String(repo_url.to_string())); + + let mut git_hub_driver = GitHubDriver::new(repo_config, io, config, http_downloader, process); + git_hub_driver.initialize().unwrap(); + + assert_eq!("test_master", git_hub_driver.get_root_identifier().unwrap()); + + let dist = git_hub_driver.get_dist(sha).unwrap(); + assert_eq!(Some("zip"), dist.get("type").and_then(|v| v.as_string())); + assert_eq!( + Some("https://api.github.com/repos/composer/packagist/zipball/SOMESHA"), + dist.get("url").and_then(|v| v.as_string()) + ); + assert_eq!(Some(sha), dist.get("reference").and_then(|v| v.as_string())); + + let source = git_hub_driver.get_source(identifier); + assert_eq!(Some("git"), source.get("type").and_then(|v| v.as_string())); + assert_eq!( + Some(repo_ssh_url), + source.get("url").and_then(|v| v.as_string()) + ); + assert_eq!( + Some(identifier), + source.get("reference").and_then(|v| v.as_string()) + ); + + let source = git_hub_driver.get_source(sha); + assert_eq!(Some("git"), source.get("type").and_then(|v| v.as_string())); + assert_eq!( + Some(repo_ssh_url), + source.get("url").and_then(|v| v.as_string()) + ); + assert_eq!( + Some(sha), + source.get("reference").and_then(|v| v.as_string()) + ); +} + +fn invalid_url_provider() -> Vec<&'static str> { + vec![ + "https://github.com/acme", + "https://github.com/acme/repository/releases", + "https://github.com/acme/repository/pulls", + ] } #[test] -#[ignore = "IOInterface MockObject (getMockBuilder) and HttpDownloader MockObject (getMockBuilder) plus the invalidUrlProvider data provider are not ported"] fn test_initialize_invalid_repo_url() { - let SetUp { home, config: _ } = set_up(); - let _tear_down = TearDown::new(home.path().to_path_buf()); - todo!() + for url in invalid_url_provider() { + let SetUp { home, config } = set_up(); + let _tear_down = TearDown::new(home.path().to_path_buf()); + + let config = Rc::new(RefCell::new(config)); + let io: Rc<RefCell<dyn IOInterface>> = Rc::new(RefCell::new(IOStub::new())); + + let (http_downloader, _http_guard) = + get_http_downloader_mock(vec![], true, HttpDownloaderMockHandler::default()); + let process = Rc::new(RefCell::new(ProcessExecutor::new(None))); + + let mut repo_config: IndexMap<String, PhpMixed> = IndexMap::new(); + repo_config.insert("url".to_string(), PhpMixed::String(url.to_string())); + + let mut git_hub_driver = + GitHubDriver::new(repo_config, io, config, http_downloader, process); + let result = git_hub_driver.initialize(); + assert!( + result.is_err(), + "expected InvalidArgumentException for url {}", + url + ); + } } #[test] -#[ignore = "HttpDownloaderMock (getHttpDownloaderMock), ProcessExecutorMock, and the IOInterface MockObject are not ported"] fn test_get_empty_file_content() { - let SetUp { home, config: _ } = set_up(); + let SetUp { home, config } = set_up(); let _tear_down = TearDown::new(home.path().to_path_buf()); - todo!() + + let repo_url = "http://github.com/composer/packagist"; + + let config = Rc::new(RefCell::new(config)); + let io: Rc<RefCell<dyn IOInterface>> = + Rc::new(RefCell::new(IOStub::new().with_is_interactive(true))); + + let (http_downloader, _http_guard) = get_http_downloader_mock( + vec![ + http_body( + "https://api.github.com/repos/composer/packagist", + r#"{"master_branch": "test_master", "owner": {"login": "composer"}, "name": "packagist", "archived": true}"#, + ), + http_body( + "https://api.github.com/repos/composer/packagist/contents/composer.json?ref=main", + r#"{"encoding":"base64","content":""}"#, + ), + ], + true, + HttpDownloaderMockHandler::default(), + ); + + let process = Rc::new(RefCell::new(ProcessExecutor::new(None))); + + let mut repo_config: IndexMap<String, PhpMixed> = IndexMap::new(); + repo_config.insert("url".to_string(), PhpMixed::String(repo_url.to_string())); + + let mut git_hub_driver = GitHubDriver::new(repo_config, io, config, http_downloader, process); + git_hub_driver.initialize().unwrap(); + + assert_eq!( + Some(String::new()), + git_hub_driver + .get_file_content("composer.json", "main") + .unwrap() + ); } |
