use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use headless_chrome::Browser;
use super::BrowserFactory;
use crate::error::{BrowserPoolError, Result};
pub struct MockBrowserFactory {
should_fail: bool,
error_message: String,
creation_count: Arc<AtomicUsize>,
fail_after: Option<usize>,
}
impl MockBrowserFactory {
pub fn new() -> Self {
Self {
should_fail: false,
error_message: String::new(),
creation_count: Arc::new(AtomicUsize::new(0)),
fail_after: None,
}
}
pub fn always_fails<S: Into<String>>(message: S) -> Self {
Self {
should_fail: true,
error_message: message.into(),
creation_count: Arc::new(AtomicUsize::new(0)),
fail_after: None,
}
}
pub fn fail_after_n<S: Into<String>>(n: usize, message: S) -> Self {
Self {
should_fail: false,
error_message: message.into(),
creation_count: Arc::new(AtomicUsize::new(0)),
fail_after: Some(n),
}
}
pub fn creation_count(&self) -> usize {
self.creation_count.load(Ordering::SeqCst)
}
pub fn reset_count(&self) {
self.creation_count.store(0, Ordering::SeqCst);
}
pub fn counter(&self) -> Arc<AtomicUsize> {
Arc::clone(&self.creation_count)
}
}
impl Default for MockBrowserFactory {
fn default() -> Self {
Self::new()
}
}
impl BrowserFactory for MockBrowserFactory {
fn create(&self) -> Result<Browser> {
let count = self.creation_count.fetch_add(1, Ordering::SeqCst);
if self.should_fail {
log::debug!("MockBrowserFactory: Returning configured failure");
return Err(BrowserPoolError::BrowserCreation(
self.error_message.clone(),
));
}
if let Some(fail_after) = self.fail_after {
if count >= fail_after {
log::debug!("MockBrowserFactory: Failing after {} creations", fail_after);
return Err(BrowserPoolError::BrowserCreation(
self.error_message.clone(),
));
}
}
log::debug!(
"MockBrowserFactory: Attempting real browser creation #{}",
count + 1
);
use super::chrome::create_chrome_options;
let options = create_chrome_options(None)
.map_err(|e| BrowserPoolError::Configuration(e.to_string()))?;
Browser::new(options).map_err(|e| {
log::error!("MockBrowserFactory: Real browser creation failed: {}", e);
BrowserPoolError::BrowserCreation(e.to_string())
})
}
}
impl std::fmt::Debug for MockBrowserFactory {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("MockBrowserFactory")
.field("should_fail", &self.should_fail)
.field("error_message", &self.error_message)
.field(
"creation_count",
&self.creation_count.load(Ordering::SeqCst),
)
.field("fail_after", &self.fail_after)
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_mock_factory_creation() {
let _factory = MockBrowserFactory::new();
let _factory = MockBrowserFactory::always_fails("test");
let _factory = MockBrowserFactory::fail_after_n(3, "exhausted");
}
#[test]
fn test_mock_factory_always_fails() {
let factory = MockBrowserFactory::always_fails("Test error");
let result = factory.create();
assert!(result.is_err());
match result {
Err(BrowserPoolError::BrowserCreation(msg)) => {
assert_eq!(msg, "Test error");
}
_ => panic!("Expected BrowserCreation error"),
}
}
#[test]
fn test_mock_factory_creation_count() {
let factory = MockBrowserFactory::always_fails("Test");
assert_eq!(factory.creation_count(), 0);
let _ = factory.create();
assert_eq!(factory.creation_count(), 1);
let _ = factory.create();
assert_eq!(factory.creation_count(), 2);
}
#[test]
fn test_mock_factory_reset_count() {
let factory = MockBrowserFactory::always_fails("Test");
let _ = factory.create();
let _ = factory.create();
assert_eq!(factory.creation_count(), 2);
factory.reset_count();
assert_eq!(factory.creation_count(), 0);
}
#[test]
fn test_mock_factory_counter() {
let factory = MockBrowserFactory::always_fails("Test");
let counter = factory.counter();
assert_eq!(counter.load(Ordering::SeqCst), 0);
let _ = factory.create();
assert_eq!(counter.load(Ordering::SeqCst), 1);
}
#[test]
fn test_mock_factory_fail_after_n() {
let factory = MockBrowserFactory::fail_after_n(2, "Exhausted");
let _ = factory.create();
let _ = factory.create();
assert_eq!(factory.creation_count(), 2);
let result = factory.create();
assert!(result.is_err());
if let Err(BrowserPoolError::BrowserCreation(msg)) = result {
assert_eq!(msg, "Exhausted");
}
}
#[test]
fn test_mock_factory_default() {
let factory: MockBrowserFactory = Default::default();
assert_eq!(factory.creation_count(), 0);
assert!(!factory.should_fail);
}
#[test]
fn test_mock_factory_debug() {
let factory = MockBrowserFactory::always_fails("Test");
let debug_str = format!("{:?}", factory);
assert!(debug_str.contains("MockBrowserFactory"));
assert!(debug_str.contains("should_fail"));
assert!(debug_str.contains("true"));
}
}