aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe/tests/repository
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-06-25 15:12:06 +0900
committernsfisis <nsfisis@gmail.com>2026-06-26 00:20:05 +0900
commit8117e5450693d19726da877bce8aacf5c7aa53af (patch)
tree4b06e6f6924599f3402d29cdcdeb0f8a88354d89 /crates/shirabe/tests/repository
parent8dd3d67884a204ca40e3364206868fea77a312be (diff)
downloadphp-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')
-rw-r--r--crates/shirabe/tests/repository/main.rs2
-rw-r--r--crates/shirabe/tests/repository/vcs/github_driver_test.rs803
-rw-r--r--crates/shirabe/tests/repository/vcs/gitlab_driver_test.rs780
3 files changed, 1520 insertions, 65 deletions
diff --git a/crates/shirabe/tests/repository/main.rs b/crates/shirabe/tests/repository/main.rs
index 15a42a3..f5ecbc6 100644
--- a/crates/shirabe/tests/repository/main.rs
+++ b/crates/shirabe/tests/repository/main.rs
@@ -4,6 +4,8 @@ mod config_stub;
mod http_downloader_mock;
#[path = "../common/io_stub.rs"]
mod io_stub;
+#[path = "../common/process_executor_mock.rs"]
+mod process_executor_mock;
#[path = "../common/test_case.rs"]
mod test_case;
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()
+ );
}
diff --git a/crates/shirabe/tests/repository/vcs/gitlab_driver_test.rs b/crates/shirabe/tests/repository/vcs/gitlab_driver_test.rs
index 0333a2b..e21835d 100644
--- a/crates/shirabe/tests/repository/vcs/gitlab_driver_test.rs
+++ b/crates/shirabe/tests/repository/vcs/gitlab_driver_test.rs
@@ -8,8 +8,15 @@ use shirabe::config::Config;
use shirabe::io::IOInterface;
use shirabe::io::null_io::NullIO;
use shirabe::repository::vcs::GitLabDriver;
+use shirabe::util::http_downloader::{HttpDownloader, HttpDownloaderMockHandler};
+use shirabe::util::process_executor::{MockHandler, ProcessExecutor};
use shirabe_php_shim::{PhpMixed, extension_loaded};
+use crate::http_downloader_mock::{
+ HttpDownloaderMockGuard, expect, expect_full, get_http_downloader_mock,
+};
+use crate::process_executor_mock::{ProcessExecutorMockGuard, get_process_executor_mock};
+
// Mirrors GitLabDriverTest::setUp's `gitlab-domains` configuration.
fn make_config() -> Config {
let mut config = Config::new(true, None);
@@ -30,74 +37,596 @@ fn make_config() -> Config {
config
}
+// Common test fixtures: the IO mock (a bare NullIO, matching PHP's unconfigured
+// getMock), the config and a no-op process executor mock. The PHP test passes a
+// bare `getMock()` ProcessExecutor; none of these tests invoke git, so an empty
+// strict expectation set both matches PHP and catches unexpected process calls.
+struct Fixtures {
+ io: Rc<RefCell<dyn IOInterface>>,
+ config: Rc<RefCell<Config>>,
+ process: Rc<RefCell<ProcessExecutor>>,
+ _process_guard: ProcessExecutorMockGuard,
+}
+
+fn fixtures() -> Fixtures {
+ let io: Rc<RefCell<dyn IOInterface>> = Rc::new(RefCell::new(NullIO::new()));
+ let config = Rc::new(RefCell::new(make_config()));
+ let (process, _process_guard) = get_process_executor_mock(vec![], true, MockHandler::default());
+ Fixtures {
+ io,
+ config,
+ process,
+ _process_guard,
+ }
+}
+
+// Mirrors $this->getHttpDownloaderMock() with a single project-data expectation.
+fn http_mock_with_body(
+ url: &str,
+ body: &str,
+) -> (Rc<RefCell<HttpDownloader>>, HttpDownloaderMockGuard) {
+ get_http_downloader_mock(
+ vec![expect_full(url, None, 200, body, vec![String::new()])],
+ true,
+ HttpDownloaderMockHandler::default(),
+ )
+}
+
+fn provide_initialize_urls() -> Vec<(&'static str, &'static str)> {
+ vec![
+ (
+ "https://gitlab.com/mygroup/myproject",
+ "https://gitlab.com/api/v4/projects/mygroup%2Fmyproject",
+ ),
+ (
+ "http://gitlab.com/mygroup/myproject",
+ "http://gitlab.com/api/v4/projects/mygroup%2Fmyproject",
+ ),
+ (
+ "git@gitlab.com:mygroup/myproject",
+ "https://gitlab.com/api/v4/projects/mygroup%2Fmyproject",
+ ),
+ ]
+}
+
+// Mirrors testInitialize: returns the initialized driver together with the http
+// mock guard (kept alive by the caller so __assert_complete runs at scope end).
+fn do_test_initialize(
+ fixtures: &Fixtures,
+ url: &str,
+ api_url: &str,
+) -> (GitLabDriver, HttpDownloaderMockGuard) {
+ // @link http://doc.gitlab.com/ce/api/projects.html#get-single-project
+ let project_data = r#"{
+ "id": 17,
+ "default_branch": "mymaster",
+ "visibility": "private",
+ "issues_enabled": true,
+ "archived": false,
+ "http_url_to_repo": "https://gitlab.com/mygroup/myproject.git",
+ "ssh_url_to_repo": "git@gitlab.com:mygroup/myproject.git",
+ "last_activity_at": "2014-12-01T09:17:51.000+01:00",
+ "name": "My Project",
+ "name_with_namespace": "My Group / My Project",
+ "path": "myproject",
+ "path_with_namespace": "mygroup/myproject",
+ "web_url": "https://gitlab.com/mygroup/myproject"
+}"#;
+
+ let (http_downloader, guard) = http_mock_with_body(api_url, project_data);
+
+ let mut repo_config: IndexMap<String, PhpMixed> = IndexMap::new();
+ repo_config.insert("url".to_string(), PhpMixed::String(url.to_string()));
+ let mut driver = GitLabDriver::new(
+ repo_config,
+ fixtures.io.clone(),
+ fixtures.config.clone(),
+ http_downloader,
+ fixtures.process.clone(),
+ );
+ driver.initialize().unwrap();
+
+ assert_eq!(
+ api_url,
+ driver.get_api_url(),
+ "API URL is derived from the repository URL"
+ );
+ assert_eq!(
+ "mymaster",
+ driver.get_root_identifier().unwrap(),
+ "Root identifier is the default branch in GitLab"
+ );
+ assert_eq!(
+ "git@gitlab.com:mygroup/myproject.git",
+ driver.get_repository_url(),
+ "The repository URL is the SSH one by default"
+ );
+ assert_eq!("https://gitlab.com/mygroup/myproject", driver.get_url());
+
+ (driver, guard)
+}
+
+// Mirrors testInitializePublicProject.
+fn do_test_initialize_public_project(
+ fixtures: &Fixtures,
+ url: &str,
+ api_url: &str,
+) -> (GitLabDriver, HttpDownloaderMockGuard) {
+ // @link http://doc.gitlab.com/ce/api/projects.html#get-single-project
+ let project_data = r#"{
+ "id": 17,
+ "default_branch": "mymaster",
+ "visibility": "public",
+ "http_url_to_repo": "https://gitlab.com/mygroup/myproject.git",
+ "ssh_url_to_repo": "git@gitlab.com:mygroup/myproject.git",
+ "last_activity_at": "2014-12-01T09:17:51.000+01:00",
+ "name": "My Project",
+ "name_with_namespace": "My Group / My Project",
+ "path": "myproject",
+ "path_with_namespace": "mygroup/myproject",
+ "web_url": "https://gitlab.com/mygroup/myproject"
+}"#;
+
+ let (http_downloader, guard) = http_mock_with_body(api_url, project_data);
+
+ let mut repo_config: IndexMap<String, PhpMixed> = IndexMap::new();
+ repo_config.insert("url".to_string(), PhpMixed::String(url.to_string()));
+ let mut driver = GitLabDriver::new(
+ repo_config,
+ fixtures.io.clone(),
+ fixtures.config.clone(),
+ http_downloader,
+ fixtures.process.clone(),
+ );
+ driver.initialize().unwrap();
+
+ assert_eq!(
+ api_url,
+ driver.get_api_url(),
+ "API URL is derived from the repository URL"
+ );
+ assert_eq!(
+ "mymaster",
+ driver.get_root_identifier().unwrap(),
+ "Root identifier is the default branch in GitLab"
+ );
+ assert_eq!(
+ "https://gitlab.com/mygroup/myproject.git",
+ driver.get_repository_url(),
+ "The repository URL is the SSH one by default"
+ );
+ assert_eq!("https://gitlab.com/mygroup/myproject", driver.get_url());
+
+ (driver, guard)
+}
+
#[test]
-#[ignore = "HttpDownloaderMock (getHttpDownloaderMock) and the IOInterface MockObject are not ported"]
fn test_initialize() {
- todo!()
+ let fixtures = fixtures();
+ for (url, api_url) in provide_initialize_urls() {
+ let (_driver, _guard) = do_test_initialize(&fixtures, url, api_url);
+ }
}
#[test]
-#[ignore = "HttpDownloaderMock (getHttpDownloaderMock) and the IOInterface MockObject are not ported"]
fn test_initialize_public_project() {
- todo!()
+ let fixtures = fixtures();
+ for (url, api_url) in provide_initialize_urls() {
+ let (_driver, _guard) = do_test_initialize_public_project(&fixtures, url, api_url);
+ }
}
#[test]
-#[ignore = "HttpDownloaderMock (getHttpDownloaderMock) and the IOInterface MockObject are not ported"]
fn test_initialize_public_project_as_anonymous() {
- todo!()
+ let fixtures = fixtures();
+ for (url, api_url) in provide_initialize_urls() {
+ // @link http://doc.gitlab.com/ce/api/projects.html#get-single-project
+ let project_data = r#"{
+ "id": 17,
+ "default_branch": "mymaster",
+ "http_url_to_repo": "https://gitlab.com/mygroup/myproject.git",
+ "ssh_url_to_repo": "git@gitlab.com:mygroup/myproject.git",
+ "last_activity_at": "2014-12-01T09:17:51.000+01:00",
+ "name": "My Project",
+ "name_with_namespace": "My Group / My Project",
+ "path": "myproject",
+ "path_with_namespace": "mygroup/myproject",
+ "web_url": "https://gitlab.com/mygroup/myproject"
+}"#;
+
+ let (http_downloader, _guard) = http_mock_with_body(api_url, project_data);
+
+ let mut repo_config: IndexMap<String, PhpMixed> = IndexMap::new();
+ repo_config.insert("url".to_string(), PhpMixed::String(url.to_string()));
+ let mut driver = GitLabDriver::new(
+ repo_config,
+ fixtures.io.clone(),
+ fixtures.config.clone(),
+ http_downloader,
+ fixtures.process.clone(),
+ );
+ driver.initialize().unwrap();
+
+ assert_eq!(
+ api_url,
+ driver.get_api_url(),
+ "API URL is derived from the repository URL"
+ );
+ assert_eq!(
+ "mymaster",
+ driver.get_root_identifier().unwrap(),
+ "Root identifier is the default branch in GitLab"
+ );
+ assert_eq!(
+ "https://gitlab.com/mygroup/myproject.git",
+ driver.get_repository_url(),
+ "The repository URL is the SSH one by default"
+ );
+ assert_eq!("https://gitlab.com/mygroup/myproject", driver.get_url());
+ }
}
+/// Also support repositories over HTTP (TLS) and has a port number.
#[test]
-#[ignore = "HttpDownloaderMock (getHttpDownloaderMock) and the IOInterface MockObject are not ported"]
fn test_initialize_with_port_number() {
- todo!()
+ let fixtures = fixtures();
+ let domain = "gitlab.mycompany.com";
+ let port = "5443";
+ let namespace = "mygroup/myproject";
+ let url = format!("https://{}:{}/{}", domain, port, namespace);
+ // urlencode($namespace) replaces '/' with '%2F'.
+ let api_url = format!(
+ "https://{}:{}/api/v4/projects/{}",
+ domain, port, "mygroup%2Fmyproject"
+ );
+
+ // An incomplete single project API response payload.
+ // @link http://doc.gitlab.com/ce/api/projects.html#get-single-project
+ let project_data = format!(
+ r#"{{
+ "default_branch": "1.0.x",
+ "http_url_to_repo": "https://{0}:{1}/{2}.git",
+ "path": "myproject",
+ "path_with_namespace": "{2}",
+ "web_url": "https://{0}:{1}/{2}"
+}}"#,
+ domain, port, namespace
+ );
+
+ let (http_downloader, _guard) = http_mock_with_body(&api_url, &project_data);
+
+ let mut repo_config: IndexMap<String, PhpMixed> = IndexMap::new();
+ repo_config.insert("url".to_string(), PhpMixed::String(url.clone()));
+ let mut driver = GitLabDriver::new(
+ repo_config,
+ fixtures.io.clone(),
+ fixtures.config.clone(),
+ http_downloader,
+ fixtures.process.clone(),
+ );
+ driver.initialize().unwrap();
+
+ assert_eq!(
+ api_url,
+ driver.get_api_url(),
+ "API URL is derived from the repository URL"
+ );
+ assert_eq!(
+ "1.0.x",
+ driver.get_root_identifier().unwrap(),
+ "Root identifier is the default branch in GitLab"
+ );
+ assert_eq!(
+ format!("{}.git", url),
+ driver.get_repository_url(),
+ "The repository URL is the SSH one by default"
+ );
+ assert_eq!(url, driver.get_url());
}
#[test]
-#[ignore = "depends on testInitialize, which needs HttpDownloaderMock + setAttribute (\\ReflectionProperty) not ported"]
fn test_invalid_support_data() {
- todo!()
+ let fixtures = fixtures();
+ let repo_url = "https://gitlab.com/mygroup/myproject";
+ let (mut driver, _init_guard) = do_test_initialize(
+ &fixtures,
+ repo_url,
+ "https://gitlab.com/api/v4/projects/mygroup%2Fmyproject",
+ );
+
+ let mut branches: IndexMap<String, String> = IndexMap::new();
+ branches.insert("main".to_string(), "SOMESHA".to_string());
+ driver.__set_branches(Some(branches));
+ driver.__set_tags(Some(IndexMap::new()));
+
+ let (http_downloader, _guard) = get_http_downloader_mock(
+ vec![expect_full(
+ "https://gitlab.com/api/v4/projects/mygroup%2Fmyproject/repository/files/composer%2Ejson/raw?ref=SOMESHA",
+ None,
+ 200,
+ format!(r#"{{"support": "{}" }}"#, repo_url),
+ vec![String::new()],
+ )],
+ true,
+ HttpDownloaderMockHandler::default(),
+ );
+ driver.set_http_downloader(http_downloader);
+
+ let data = driver.get_composer_information("main").unwrap().unwrap();
+
+ let source = data
+ .get("support")
+ .and_then(|v| v.as_array())
+ .and_then(|m| m.get("source"))
+ .and_then(|v| v.as_string())
+ .unwrap();
+ assert_eq!("https://gitlab.com/mygroup/myproject/-/tree/main", source);
}
#[test]
-#[ignore = "depends on testInitialize, which needs HttpDownloaderMock + the IOInterface MockObject not ported"]
fn test_get_dist() {
- todo!()
+ let fixtures = fixtures();
+ let (driver, _guard) = do_test_initialize(
+ &fixtures,
+ "https://gitlab.com/mygroup/myproject",
+ "https://gitlab.com/api/v4/projects/mygroup%2Fmyproject",
+ );
+
+ let reference = "c3ebdbf9cceddb82cd2089aaef8c7b992e536363";
+ let mut expected: IndexMap<String, PhpMixed> = IndexMap::new();
+ expected.insert("type".to_string(), PhpMixed::String("zip".to_string()));
+ expected.insert(
+ "url".to_string(),
+ PhpMixed::String(format!(
+ "https://gitlab.com/api/v4/projects/mygroup%2Fmyproject/repository/archive.zip?sha={}",
+ reference
+ )),
+ );
+ expected.insert(
+ "reference".to_string(),
+ PhpMixed::String(reference.to_string()),
+ );
+ expected.insert("shasum".to_string(), PhpMixed::String(String::new()));
+
+ assert_eq!(Some(expected), driver.get_dist(reference));
}
#[test]
-#[ignore = "depends on testInitialize, which needs HttpDownloaderMock + the IOInterface MockObject not ported"]
fn test_get_source() {
- todo!()
+ let fixtures = fixtures();
+ let (driver, _guard) = do_test_initialize(
+ &fixtures,
+ "https://gitlab.com/mygroup/myproject",
+ "https://gitlab.com/api/v4/projects/mygroup%2Fmyproject",
+ );
+
+ let reference = "c3ebdbf9cceddb82cd2089aaef8c7b992e536363";
+ let mut expected: IndexMap<String, PhpMixed> = IndexMap::new();
+ expected.insert("type".to_string(), PhpMixed::String("git".to_string()));
+ expected.insert(
+ "url".to_string(),
+ PhpMixed::String("git@gitlab.com:mygroup/myproject.git".to_string()),
+ );
+ expected.insert(
+ "reference".to_string(),
+ PhpMixed::String(reference.to_string()),
+ );
+
+ assert_eq!(expected, driver.get_source(reference));
}
#[test]
-#[ignore = "depends on testInitializePublicProject, which needs HttpDownloaderMock + the IOInterface MockObject not ported"]
fn test_get_source_given_public_project() {
- todo!()
+ let fixtures = fixtures();
+ let (driver, _guard) = do_test_initialize_public_project(
+ &fixtures,
+ "https://gitlab.com/mygroup/myproject",
+ "https://gitlab.com/api/v4/projects/mygroup%2Fmyproject",
+ );
+
+ let reference = "c3ebdbf9cceddb82cd2089aaef8c7b992e536363";
+ let mut expected: IndexMap<String, PhpMixed> = IndexMap::new();
+ expected.insert("type".to_string(), PhpMixed::String("git".to_string()));
+ expected.insert(
+ "url".to_string(),
+ PhpMixed::String("https://gitlab.com/mygroup/myproject.git".to_string()),
+ );
+ expected.insert(
+ "reference".to_string(),
+ PhpMixed::String(reference.to_string()),
+ );
+
+ assert_eq!(expected, driver.get_source(reference));
}
#[test]
-#[ignore = "depends on testInitialize, which needs HttpDownloaderMock + the IOInterface MockObject not ported"]
fn test_get_tags() {
- todo!()
+ let fixtures = fixtures();
+ let (mut driver, _init_guard) = do_test_initialize(
+ &fixtures,
+ "https://gitlab.com/mygroup/myproject",
+ "https://gitlab.com/api/v4/projects/mygroup%2Fmyproject",
+ );
+
+ let api_url =
+ "https://gitlab.com/api/v4/projects/mygroup%2Fmyproject/repository/tags?per_page=100";
+
+ // @link http://doc.gitlab.com/ce/api/repositories.html#list-project-repository-tags
+ let tag_data = r#"[
+ {
+ "name": "v1.0.0",
+ "commit": {
+ "id": "092ed2c762bbae331e3f51d4a17f67310bf99a81",
+ "committed_date": "2012-05-28T04:42:42-07:00"
+ }
+ },
+ {
+ "name": "v2.0.0",
+ "commit": {
+ "id": "8e8f60b3ec86d63733db3bd6371117a758027ec6",
+ "committed_date": "2014-07-06T12:59:11.000+02:00"
+ }
+ }
+]"#;
+
+ let (http_downloader, _guard) = http_mock_with_body(api_url, tag_data);
+ driver.set_http_downloader(http_downloader);
+
+ let mut expected: IndexMap<String, String> = IndexMap::new();
+ expected.insert(
+ "v1.0.0".to_string(),
+ "092ed2c762bbae331e3f51d4a17f67310bf99a81".to_string(),
+ );
+ expected.insert(
+ "v2.0.0".to_string(),
+ "8e8f60b3ec86d63733db3bd6371117a758027ec6".to_string(),
+ );
+
+ assert_eq!(expected, driver.get_tags().unwrap());
+ assert_eq!(expected, driver.get_tags().unwrap(), "Tags are cached");
}
#[test]
-#[ignore = "depends on testInitialize, which needs HttpDownloaderMock + the IOInterface MockObject not ported"]
+#[ignore = "Response::find_header_value passes a `(?i)`-prefixed (non-delimited) pattern that compile_php_pattern (Preg) mis-translates to an invalid regex (`(?s)?i)^link:...`), so get_next_page panics when parsing the Link header. Pre-existing Preg/Response porting bug, unrelated to GitLabDriver."]
fn test_get_paginated_refs() {
- todo!()
+ let fixtures = fixtures();
+ let (mut driver, _init_guard) = do_test_initialize(
+ &fixtures,
+ "https://gitlab.com/mygroup/myproject",
+ "https://gitlab.com/api/v4/projects/mygroup%2Fmyproject",
+ );
+
+ // @link http://doc.gitlab.com/ce/api/repositories.html#list-project-repository-branches
+ let mut branch_data: Vec<PhpMixed> = vec![
+ branch_entry(
+ "mymaster",
+ "97eda36b5c1dd953a3792865c222d4e85e5f302e",
+ "2013-01-03T21:04:07.000+01:00",
+ ),
+ branch_entry(
+ "staging",
+ "502cffe49f136443f2059803f2e7192d1ac066cd",
+ "2013-03-09T16:35:23.000+01:00",
+ ),
+ ];
+ for _ in 0..98 {
+ branch_data.push(branch_entry(
+ "stagingdupe",
+ "502cffe49f136443f2059803f2e7192d1ac066cd",
+ "2013-03-09T16:35:23.000+01:00",
+ ));
+ }
+ let branch_data = shirabe::json::JsonFile::encode(&PhpMixed::List(branch_data));
+
+ let (http_downloader, _guard) = get_http_downloader_mock(
+ vec![
+ expect_full(
+ "https://gitlab.com/api/v4/projects/mygroup%2Fmyproject/repository/branches?per_page=100",
+ None,
+ 200,
+ branch_data.clone(),
+ vec!["Link: <http://gitlab.com/api/v4/projects/mygroup%2Fmyproject/repository/tags?id=mygroup%2Fmyproject&page=2&per_page=20>; rel=\"next\", <http://gitlab.com/api/v4/projects/mygroup%2Fmyproject/repository/tags?id=mygroup%2Fmyproject&page=1&per_page=20>; rel=\"first\", <http://gitlab.com/api/v4/projects/mygroup%2Fmyproject/repository/tags?id=mygroup%2Fmyproject&page=3&per_page=20>; rel=\"last\"".to_string()],
+ ),
+ expect_full(
+ "http://gitlab.com/api/v4/projects/mygroup%2Fmyproject/repository/tags?id=mygroup%2Fmyproject&page=2&per_page=20",
+ None,
+ 200,
+ branch_data.clone(),
+ vec!["Link: <http://gitlab.com/api/v4/projects/mygroup%2Fmyproject/repository/tags?id=mygroup%2Fmyproject&page=2&per_page=20>; rel=\"prev\", <http://gitlab.com/api/v4/projects/mygroup%2Fmyproject/repository/tags?id=mygroup%2Fmyproject&page=1&per_page=20>; rel=\"first\", <http://gitlab.com/api/v4/projects/mygroup%2Fmyproject/repository/tags?id=mygroup%2Fmyproject&page=3&per_page=20>; rel=\"last\"".to_string()],
+ ),
+ ],
+ true,
+ HttpDownloaderMockHandler::default(),
+ );
+ driver.set_http_downloader(http_downloader);
+
+ let mut expected: IndexMap<String, String> = IndexMap::new();
+ expected.insert(
+ "mymaster".to_string(),
+ "97eda36b5c1dd953a3792865c222d4e85e5f302e".to_string(),
+ );
+ expected.insert(
+ "staging".to_string(),
+ "502cffe49f136443f2059803f2e7192d1ac066cd".to_string(),
+ );
+ expected.insert(
+ "stagingdupe".to_string(),
+ "502cffe49f136443f2059803f2e7192d1ac066cd".to_string(),
+ );
+
+ assert_eq!(expected, driver.get_branches().unwrap());
+ assert_eq!(
+ expected,
+ driver.get_branches().unwrap(),
+ "Branches are cached"
+ );
+}
+
+fn branch_entry(name: &str, id: &str, committed_date: &str) -> PhpMixed {
+ let mut commit: IndexMap<String, PhpMixed> = IndexMap::new();
+ commit.insert("id".to_string(), PhpMixed::String(id.to_string()));
+ commit.insert(
+ "committed_date".to_string(),
+ PhpMixed::String(committed_date.to_string()),
+ );
+ let mut entry: IndexMap<String, PhpMixed> = IndexMap::new();
+ entry.insert("name".to_string(), PhpMixed::String(name.to_string()));
+ entry.insert("commit".to_string(), PhpMixed::Array(commit));
+ PhpMixed::Array(entry)
}
#[test]
-#[ignore = "depends on testInitialize, which needs HttpDownloaderMock + the IOInterface MockObject not ported"]
fn test_get_branches() {
- todo!()
+ let fixtures = fixtures();
+ let (mut driver, _init_guard) = do_test_initialize(
+ &fixtures,
+ "https://gitlab.com/mygroup/myproject",
+ "https://gitlab.com/api/v4/projects/mygroup%2Fmyproject",
+ );
+
+ let api_url =
+ "https://gitlab.com/api/v4/projects/mygroup%2Fmyproject/repository/branches?per_page=100";
+
+ // @link http://doc.gitlab.com/ce/api/repositories.html#list-project-repository-branches
+ let branch_data = r#"[
+ {
+ "name": "mymaster",
+ "commit": {
+ "id": "97eda36b5c1dd953a3792865c222d4e85e5f302e",
+ "committed_date": "2013-01-03T21:04:07.000+01:00"
+ }
+ },
+ {
+ "name": "staging",
+ "commit": {
+ "id": "502cffe49f136443f2059803f2e7192d1ac066cd",
+ "committed_date": "2013-03-09T16:35:23.000+01:00"
+ }
+ }
+]"#;
+
+ let (http_downloader, _guard) = http_mock_with_body(api_url, branch_data);
+ driver.set_http_downloader(http_downloader);
+
+ let mut expected: IndexMap<String, String> = IndexMap::new();
+ expected.insert(
+ "mymaster".to_string(),
+ "97eda36b5c1dd953a3792865c222d4e85e5f302e".to_string(),
+ );
+ expected.insert(
+ "staging".to_string(),
+ "502cffe49f136443f2059803f2e7192d1ac066cd".to_string(),
+ );
+
+ assert_eq!(expected, driver.get_branches().unwrap());
+ assert_eq!(
+ expected,
+ driver.get_branches().unwrap(),
+ "Branches are cached"
+ );
}
#[test]
-#[ignore]
fn test_supports() {
for (url, expected) in data_for_test_supports() {
let io: Rc<RefCell<dyn IOInterface>> = Rc::new(RefCell::new(NullIO::new()));
@@ -147,31 +676,216 @@ fn data_for_test_supports() -> Vec<(&'static str, bool)> {
}
#[test]
-#[ignore = "HttpDownloaderMock (getHttpDownloaderMock) and the IOInterface MockObject are not ported"]
fn test_gitlab_sub_directory() {
- todo!()
+ let fixtures = fixtures();
+ let url = "https://mycompany.com/gitlab/mygroup/my-pro.ject";
+ let api_url = "https://mycompany.com/gitlab/api/v4/projects/mygroup%2Fmy-pro%2Eject";
+
+ let project_data = r#"{
+ "id": 17,
+ "default_branch": "mymaster",
+ "visibility": "private",
+ "http_url_to_repo": "https://gitlab.com/gitlab/mygroup/my-pro.ject",
+ "ssh_url_to_repo": "git@gitlab.com:mygroup/my-pro.ject.git",
+ "last_activity_at": "2014-12-01T09:17:51.000+01:00",
+ "name": "My Project",
+ "name_with_namespace": "My Group / My Project",
+ "path": "myproject",
+ "path_with_namespace": "mygroup/my-pro.ject",
+ "web_url": "https://gitlab.com/gitlab/mygroup/my-pro.ject"
+}"#;
+
+ let (http_downloader, _guard) = http_mock_with_body(api_url, project_data);
+
+ let mut repo_config: IndexMap<String, PhpMixed> = IndexMap::new();
+ repo_config.insert("url".to_string(), PhpMixed::String(url.to_string()));
+ let mut driver = GitLabDriver::new(
+ repo_config,
+ fixtures.io.clone(),
+ fixtures.config.clone(),
+ http_downloader,
+ fixtures.process.clone(),
+ );
+ driver.initialize().unwrap();
+
+ assert_eq!(
+ api_url,
+ driver.get_api_url(),
+ "API URL is derived from the repository URL"
+ );
}
#[test]
-#[ignore = "HttpDownloaderMock (getHttpDownloaderMock) and the IOInterface MockObject are not ported"]
fn test_gitlab_sub_group() {
- todo!()
+ let fixtures = fixtures();
+ let url = "https://gitlab.com/mygroup/mysubgroup/myproject";
+ let api_url = "https://gitlab.com/api/v4/projects/mygroup%2Fmysubgroup%2Fmyproject";
+
+ let project_data = r#"{
+ "id": 17,
+ "default_branch": "mymaster",
+ "visibility": "private",
+ "http_url_to_repo": "https://gitlab.com/mygroup/mysubgroup/my-pro.ject",
+ "ssh_url_to_repo": "git@gitlab.com:mygroup/mysubgroup/my-pro.ject.git",
+ "last_activity_at": "2014-12-01T09:17:51.000+01:00",
+ "name": "My Project",
+ "name_with_namespace": "My Group / My Project",
+ "path": "myproject",
+ "path_with_namespace": "mygroup/mysubgroup/my-pro.ject",
+ "web_url": "https://gitlab.com/mygroup/mysubgroup/my-pro.ject"
+}"#;
+
+ let (http_downloader, _guard) = http_mock_with_body(api_url, project_data);
+
+ let mut repo_config: IndexMap<String, PhpMixed> = IndexMap::new();
+ repo_config.insert("url".to_string(), PhpMixed::String(url.to_string()));
+ let mut driver = GitLabDriver::new(
+ repo_config,
+ fixtures.io.clone(),
+ fixtures.config.clone(),
+ http_downloader,
+ fixtures.process.clone(),
+ );
+ driver.initialize().unwrap();
+
+ assert_eq!(
+ api_url,
+ driver.get_api_url(),
+ "API URL is derived from the repository URL"
+ );
}
#[test]
-#[ignore = "HttpDownloaderMock (getHttpDownloaderMock) and the IOInterface MockObject are not ported"]
fn test_gitlab_sub_directory_sub_group() {
- todo!()
+ let fixtures = fixtures();
+ let url = "https://mycompany.com/gitlab/mygroup/mysubgroup/myproject";
+ let api_url = "https://mycompany.com/gitlab/api/v4/projects/mygroup%2Fmysubgroup%2Fmyproject";
+
+ let project_data = r#"{
+ "id": 17,
+ "default_branch": "mymaster",
+ "visibility": "private",
+ "http_url_to_repo": "https://mycompany.com/gitlab/mygroup/mysubgroup/my-pro.ject",
+ "ssh_url_to_repo": "git@mycompany.com:mygroup/mysubgroup/my-pro.ject.git",
+ "last_activity_at": "2014-12-01T09:17:51.000+01:00",
+ "name": "My Project",
+ "name_with_namespace": "My Group / My Project",
+ "path": "myproject",
+ "path_with_namespace": "mygroup/mysubgroup/my-pro.ject",
+ "web_url": "https://mycompany.com/gitlab/mygroup/mysubgroup/my-pro.ject"
+}"#;
+
+ let (http_downloader, _guard) = http_mock_with_body(api_url, project_data);
+
+ let mut repo_config: IndexMap<String, PhpMixed> = IndexMap::new();
+ repo_config.insert("url".to_string(), PhpMixed::String(url.to_string()));
+ let mut driver = GitLabDriver::new(
+ repo_config,
+ fixtures.io.clone(),
+ fixtures.config.clone(),
+ http_downloader,
+ fixtures.process.clone(),
+ );
+ driver.initialize().unwrap();
+
+ assert_eq!(
+ api_url,
+ driver.get_api_url(),
+ "API URL is derived from the repository URL"
+ );
}
#[test]
-#[ignore = "HttpDownloaderMock (getHttpDownloaderMock) and the IOInterface MockObject are not ported"]
fn test_forwards_options() {
- todo!()
+ let fixtures = fixtures();
+ let mut ssl: IndexMap<String, PhpMixed> = IndexMap::new();
+ ssl.insert("verify_peer".to_string(), PhpMixed::Bool(false));
+ let mut options: IndexMap<String, PhpMixed> = IndexMap::new();
+ options.insert("ssl".to_string(), PhpMixed::Array(ssl));
+
+ let project_data = r#"{
+ "id": 17,
+ "default_branch": "mymaster",
+ "visibility": "private",
+ "http_url_to_repo": "https://gitlab.mycompany.local/mygroup/myproject",
+ "ssh_url_to_repo": "git@gitlab.mycompany.local:mygroup/myproject.git",
+ "last_activity_at": "2014-12-01T09:17:51.000+01:00",
+ "name": "My Project",
+ "name_with_namespace": "My Group / My Project",
+ "path": "myproject",
+ "path_with_namespace": "mygroup/myproject",
+ "web_url": "https://gitlab.mycompany.local/mygroup/myproject"
+}"#;
+
+ let (http_downloader, _guard) = http_mock_with_body(
+ "https://gitlab.mycompany.local/api/v4/projects/mygroup%2Fmyproject",
+ project_data,
+ );
+
+ let mut repo_config: IndexMap<String, PhpMixed> = IndexMap::new();
+ repo_config.insert(
+ "url".to_string(),
+ PhpMixed::String("https://gitlab.mycompany.local/mygroup/myproject".to_string()),
+ );
+ repo_config.insert("options".to_string(), PhpMixed::Array(options));
+ let mut driver = GitLabDriver::new(
+ repo_config,
+ fixtures.io.clone(),
+ fixtures.config.clone(),
+ http_downloader,
+ fixtures.process.clone(),
+ );
+ driver.initialize().unwrap();
}
#[test]
-#[ignore = "HttpDownloaderMock (getHttpDownloaderMock) and the IOInterface MockObject are not ported"]
fn test_protocol_override_repository_url_generation() {
- todo!()
+ let fixtures = fixtures();
+ // @link http://doc.gitlab.com/ce/api/projects.html#get-single-project
+ let project_data = r#"{
+ "id": 17,
+ "default_branch": "mymaster",
+ "visibility": "private",
+ "http_url_to_repo": "https://gitlab.com/mygroup/myproject.git",
+ "ssh_url_to_repo": "git@gitlab.com:mygroup/myproject.git",
+ "last_activity_at": "2014-12-01T09:17:51.000+01:00",
+ "name": "My Project",
+ "name_with_namespace": "My Group / My Project",
+ "path": "myproject",
+ "path_with_namespace": "mygroup/myproject",
+ "web_url": "https://gitlab.com/mygroup/myproject"
+}"#;
+
+ let api_url = "https://gitlab.com/api/v4/projects/mygroup%2Fmyproject";
+ let url = "git@gitlab.com:mygroup/myproject";
+
+ let (http_downloader, _guard) = http_mock_with_body(api_url, project_data);
+
+ // clone $this->config and merge gitlab-protocol => http.
+ let mut config = make_config();
+ let mut top: IndexMap<String, PhpMixed> = IndexMap::new();
+ let mut config_section: IndexMap<String, PhpMixed> = IndexMap::new();
+ config_section.insert(
+ "gitlab-protocol".to_string(),
+ PhpMixed::String("http".to_string()),
+ );
+ top.insert("config".to_string(), PhpMixed::Array(config_section));
+ config.merge(&top, Config::SOURCE_UNKNOWN);
+ let config = Rc::new(RefCell::new(config));
+
+ let mut repo_config: IndexMap<String, PhpMixed> = IndexMap::new();
+ repo_config.insert("url".to_string(), PhpMixed::String(url.to_string()));
+ let mut driver = GitLabDriver::new(
+ repo_config,
+ fixtures.io.clone(),
+ config,
+ http_downloader,
+ fixtures.process.clone(),
+ );
+ driver.initialize().unwrap();
+ assert_eq!(
+ "https://gitlab.com/mygroup/myproject.git",
+ driver.get_repository_url(),
+ "Repository URL matches config request for http not git"
+ );
}