aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe/tests/common/io_stub.rs
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-06-25 14:57:23 +0900
committernsfisis <nsfisis@gmail.com>2026-06-26 00:20:05 +0900
commit8dd3d67884a204ca40e3364206868fea77a312be (patch)
treeea0b603220112dc5d6a512796cda616250b50213 /crates/shirabe/tests/common/io_stub.rs
parent230319ae3db47ed9c35dc7faa733a84f9ce87ff9 (diff)
downloadphp-shirabe-8dd3d67884a204ca40e3364206868fea77a312be.tar.gz
php-shirabe-8dd3d67884a204ca40e3364206868fea77a312be.tar.zst
php-shirabe-8dd3d67884a204ca40e3364206868fea77a312be.zip
feat(test): add HttpDownloaderMock, IOStub, and Config stub helpers
IOStub and ConfigStubBuilder provide getMockBuilder-style configurable stubs. Wired into util/repository/downloader/command test targets. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'crates/shirabe/tests/common/io_stub.rs')
-rw-r--r--crates/shirabe/tests/common/io_stub.rs211
1 files changed, 211 insertions, 0 deletions
diff --git a/crates/shirabe/tests/common/io_stub.rs b/crates/shirabe/tests/common/io_stub.rs
new file mode 100644
index 0000000..9318d45
--- /dev/null
+++ b/crates/shirabe/tests/common/io_stub.rs
@@ -0,0 +1,211 @@
+//! Configurable IOInterface stub, equivalent to PHPUnit's
+//! `getMockBuilder(IOInterface::class)->getMock()` where individual methods are
+//! configured via `->method('x')->willReturn(y)`.
+//!
+//! Each configurable method is backed by an `Option<ReturnValue>` field plus a
+//! builder-style setter. Unset methods fall back to NullIO-equivalent defaults.
+#![allow(dead_code)]
+
+use shirabe::config::Config;
+use shirabe::io::{BaseIO, IOInterface, IOInterfaceImmutable, IOInterfaceMutable};
+use shirabe_php_shim::PhpMixed;
+
+#[derive(Debug, Default)]
+pub struct IOStub {
+ authentications: indexmap::IndexMap<String, indexmap::IndexMap<String, Option<String>>>,
+
+ is_interactive: Option<bool>,
+ is_decorated: Option<bool>,
+ is_verbose: Option<bool>,
+ is_very_verbose: Option<bool>,
+ is_debug: Option<bool>,
+
+ // `hasAuthentication` is keyed: PHPUnit tests usually configure a single bool
+ // willReturn that applies to any argument, so a single Option<bool> matches.
+ has_authentication: Option<bool>,
+ // `getAuthentication` willReturn is a `['username' => ..., 'password' => ...]` array.
+ get_authentication: Option<indexmap::IndexMap<String, Option<String>>>,
+
+ ask: Option<PhpMixed>,
+ ask_confirmation: Option<bool>,
+ ask_and_hide_answer: Option<Option<String>>,
+}
+
+impl IOStub {
+ pub fn new() -> Self {
+ Self::default()
+ }
+
+ pub fn with_is_interactive(mut self, value: bool) -> Self {
+ self.is_interactive = Some(value);
+ self
+ }
+ pub fn with_is_decorated(mut self, value: bool) -> Self {
+ self.is_decorated = Some(value);
+ self
+ }
+ pub fn with_is_verbose(mut self, value: bool) -> Self {
+ self.is_verbose = Some(value);
+ self
+ }
+ pub fn with_is_very_verbose(mut self, value: bool) -> Self {
+ self.is_very_verbose = Some(value);
+ self
+ }
+ pub fn with_is_debug(mut self, value: bool) -> Self {
+ self.is_debug = Some(value);
+ self
+ }
+ pub fn with_has_authentication(mut self, value: bool) -> Self {
+ self.has_authentication = Some(value);
+ self
+ }
+ pub fn with_get_authentication(
+ mut self,
+ value: indexmap::IndexMap<String, Option<String>>,
+ ) -> Self {
+ self.get_authentication = Some(value);
+ self
+ }
+ pub fn with_ask(mut self, value: PhpMixed) -> Self {
+ self.ask = Some(value);
+ self
+ }
+ pub fn with_ask_confirmation(mut self, value: bool) -> Self {
+ self.ask_confirmation = Some(value);
+ self
+ }
+ pub fn with_ask_and_hide_answer(mut self, value: Option<String>) -> Self {
+ self.ask_and_hide_answer = Some(value);
+ self
+ }
+}
+
+impl IOInterfaceImmutable for IOStub {
+ fn is_interactive(&self) -> bool {
+ self.is_interactive.unwrap_or(false)
+ }
+ fn is_verbose(&self) -> bool {
+ self.is_verbose.unwrap_or(false)
+ }
+ fn is_very_verbose(&self) -> bool {
+ self.is_very_verbose.unwrap_or(false)
+ }
+ fn is_debug(&self) -> bool {
+ self.is_debug.unwrap_or(false)
+ }
+ fn is_decorated(&self) -> bool {
+ self.is_decorated.unwrap_or(false)
+ }
+
+ fn write3(&self, _message: &str, _newline: bool, _verbosity: i64) {}
+ fn write_error3(&self, _message: &str, _newline: bool, _verbosity: i64) {}
+ fn write_raw3(&self, _message: &str, _newline: bool, _verbosity: i64) {}
+ fn write_error_raw3(&self, _message: &str, _newline: bool, _verbosity: i64) {}
+ fn overwrite4(&self, _message: &str, _newline: bool, _size: Option<i64>, _verbosity: i64) {}
+ fn overwrite_error4(
+ &self,
+ _message: &str,
+ _newline: bool,
+ _size: Option<i64>,
+ _verbosity: i64,
+ ) {
+ }
+
+ fn ask(&self, _question: String, default: PhpMixed) -> PhpMixed {
+ self.ask.clone().unwrap_or(default)
+ }
+ fn ask_confirmation(&self, _question: String, default: bool) -> bool {
+ self.ask_confirmation.unwrap_or(default)
+ }
+ fn ask_and_validate(
+ &self,
+ _question: String,
+ _validator: Box<dyn Fn(PhpMixed) -> anyhow::Result<PhpMixed>>,
+ _attempts: Option<i64>,
+ default: PhpMixed,
+ ) -> anyhow::Result<PhpMixed> {
+ Ok(default)
+ }
+ fn ask_and_hide_answer(&self, _question: String) -> Option<String> {
+ self.ask_and_hide_answer.clone().unwrap_or(None)
+ }
+ fn select(
+ &self,
+ _question: String,
+ _choices: Vec<String>,
+ default: PhpMixed,
+ _attempts: PhpMixed,
+ _error_message: String,
+ _multiselect: bool,
+ ) -> PhpMixed {
+ default
+ }
+
+ fn get_authentications(
+ &self,
+ ) -> indexmap::IndexMap<String, indexmap::IndexMap<String, Option<String>>> {
+ <Self as BaseIO>::get_authentications(self)
+ }
+ fn has_authentication(&self, repository_name: &str) -> bool {
+ match self.has_authentication {
+ Some(value) => value,
+ None => <Self as BaseIO>::has_authentication(self, repository_name),
+ }
+ }
+ fn get_authentication(
+ &self,
+ repository_name: &str,
+ ) -> indexmap::IndexMap<String, Option<String>> {
+ match &self.get_authentication {
+ Some(value) => value.clone(),
+ None => <Self as BaseIO>::get_authentication(self, repository_name),
+ }
+ }
+
+ fn error(&self, message: &str, context: &[(&str, &str)]) {
+ <Self as BaseIO>::error(self, message, context);
+ }
+ fn warning(&self, message: &str, context: &[(&str, &str)]) {
+ <Self as BaseIO>::warning(self, message, context);
+ }
+ fn debug(&self, message: &str, context: &[(&str, &str)]) {
+ <Self as BaseIO>::debug(self, message, context);
+ }
+}
+
+impl IOInterfaceMutable for IOStub {
+ fn set_authentication(
+ &mut self,
+ repository_name: String,
+ username: String,
+ password: Option<String>,
+ ) {
+ <Self as BaseIO>::set_authentication(self, repository_name, username, password)
+ }
+ fn load_configuration(&mut self, config: &mut Config) -> anyhow::Result<()> {
+ <Self as BaseIO>::load_configuration(self, config)
+ }
+}
+
+impl IOInterface for IOStub {
+ fn as_any(&self) -> &dyn std::any::Any {
+ self
+ }
+ fn as_base_io_mut(&mut self) -> Option<&mut dyn BaseIO> {
+ Some(self)
+ }
+}
+
+impl BaseIO for IOStub {
+ fn authentications(
+ &self,
+ ) -> &indexmap::IndexMap<String, indexmap::IndexMap<String, Option<String>>> {
+ &self.authentications
+ }
+ fn authentications_mut(
+ &mut self,
+ ) -> &mut indexmap::IndexMap<String, indexmap::IndexMap<String, Option<String>>> {
+ &mut self.authentications
+ }
+}