use std::collections::HashMap;
use crate::runtime::values::Value;
use crate::runtime::Runtime;
#[derive(Debug, Clone)]
pub struct MockFunction {
pub name: String,
pub namespace: Option<String>,
pub return_value: Option<Value>,
pub side_effects: Vec<MockSideEffect>,
pub call_count: usize,
pub expected_calls: Option<usize>,
}
impl MockFunction {
pub fn new(name: &str) -> Self {
Self {
name: name.to_string(),
namespace: None,
return_value: None,
side_effects: Vec::new(),
call_count: 0,
expected_calls: None,
}
}
pub fn with_namespace(mut self, namespace: &str) -> Self {
self.namespace = Some(namespace.to_string());
self
}
pub fn returns(mut self, value: Value) -> Self {
self.return_value = Some(value);
self
}
pub fn with_side_effect(mut self, side_effect: MockSideEffect) -> Self {
self.side_effects.push(side_effect);
self
}
pub fn expects_calls(mut self, count: usize) -> Self {
self.expected_calls = Some(count);
self
}
pub fn validates_arguments<F>(self, _validator: F) -> Self
where
F: Fn(&[Value]) -> Result<(), String> + 'static
{
self
}
pub fn call(&mut self, args: &[Value]) -> Result<Value, String> {
self.call_count += 1;
for side_effect in &self.side_effects {
side_effect.execute(args)?;
}
Ok(self.return_value.clone().unwrap_or(Value::Null))
}
pub fn verify(&self) -> Result<(), String> {
if let Some(expected) = self.expected_calls {
if self.call_count != expected {
return Err(format!(
"Mock function '{}' was called {} times, expected {} times",
self.name, self.call_count, expected
));
}
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub enum MockSideEffect {
SetVariable(String, Value),
CallFunction(String, Vec<Value>),
LogMessage(String),
ThrowError(String),
Delay(std::time::Duration),
}
impl MockSideEffect {
pub fn execute(&self, _args: &[Value]) -> Result<(), String> {
match self {
MockSideEffect::SetVariable(_, _) => {
Ok(())
}
MockSideEffect::CallFunction(_, _) => {
Ok(())
}
MockSideEffect::LogMessage(message) => {
println!("[MOCK] {}", message);
Ok(())
}
MockSideEffect::ThrowError(error) => {
Err(error.clone())
}
MockSideEffect::Delay(duration) => {
std::thread::sleep(*duration);
Ok(())
}
}
}
}
#[derive(Debug, Clone)]
pub struct MockRegistry {
pub mocks: HashMap<String, MockFunction>,
pub enabled: bool,
}
impl MockRegistry {
pub fn new() -> Self {
Self {
mocks: HashMap::new(),
enabled: true,
}
}
pub fn register(&mut self, mock: MockFunction) {
let key = self.get_mock_key(&mock.name, mock.namespace.as_deref());
self.mocks.insert(key, mock);
}
pub fn get_mock(&mut self, name: &str, namespace: Option<&str>) -> Option<&mut MockFunction> {
let key = self.get_mock_key(name, namespace);
self.mocks.get_mut(&key)
}
pub fn call_mock(&mut self, name: &str, namespace: Option<&str>, args: &[Value]) -> Result<Value, String> {
if !self.enabled {
return Err("Mock registry is disabled".to_string());
}
if let Some(mock) = self.get_mock(name, namespace) {
mock.call(args)
} else {
Err(format!("No mock found for '{}{}'",
namespace.map(|ns| format!("{}::", ns)).unwrap_or_default(),
name))
}
}
pub fn verify_all(&self) -> Result<(), String> {
for (key, mock) in &self.mocks {
mock.verify().map_err(|e| format!("Mock '{}': {}", key, e))?;
}
Ok(())
}
pub fn clear(&mut self) {
self.mocks.clear();
}
pub fn enable(&mut self) {
self.enabled = true;
}
pub fn disable(&mut self) {
self.enabled = false;
}
fn get_mock_key(&self, name: &str, namespace: Option<&str>) -> String {
match namespace {
Some(ns) => format!("{}::{}", ns, name),
None => name.to_string(),
}
}
}
pub struct MockBuilder {
name: String,
namespace: Option<String>,
return_value: Option<Value>,
side_effects: Vec<MockSideEffect>,
expected_calls: Option<usize>,
arguments_validator: Option<Box<dyn Fn(&[Value]) -> Result<(), String>>>,
}
impl MockBuilder {
pub fn new(name: &str) -> Self {
Self {
name: name.to_string(),
namespace: None,
return_value: None,
side_effects: Vec::new(),
expected_calls: None,
arguments_validator: None,
}
}
pub fn in_namespace(mut self, namespace: &str) -> Self {
self.namespace = Some(namespace.to_string());
self
}
pub fn returns(mut self, value: Value) -> Self {
self.return_value = Some(value);
self
}
pub fn logs(mut self, message: &str) -> Self {
self.side_effects.push(MockSideEffect::LogMessage(message.to_string()));
self
}
pub fn throws(mut self, error: &str) -> Self {
self.side_effects.push(MockSideEffect::ThrowError(error.to_string()));
self
}
pub fn delays(mut self, duration: std::time::Duration) -> Self {
self.side_effects.push(MockSideEffect::Delay(duration));
self
}
pub fn expects_calls(mut self, count: usize) -> Self {
self.expected_calls = Some(count);
self
}
pub fn validates_args<F>(mut self, validator: F) -> Self
where
F: Fn(&[Value]) -> Result<(), String> + 'static
{
self.arguments_validator = Some(Box::new(validator));
self
}
pub fn build(self) -> MockFunction {
MockFunction {
name: self.name,
namespace: self.namespace,
return_value: self.return_value,
side_effects: self.side_effects,
call_count: 0,
expected_calls: self.expected_calls,
}
}
}
pub struct MockRuntime {
pub runtime: Runtime,
pub mock_registry: MockRegistry,
}
impl MockRuntime {
pub fn new() -> Self {
Self {
runtime: Runtime::new(),
mock_registry: MockRegistry::new(),
}
}
pub fn with_mock(mut self, mock: MockFunction) -> Self {
self.mock_registry.register(mock);
self
}
pub fn execute_with_mocks(&mut self, _source_code: &str) -> Result<Value, String> {
Ok(Value::String("Mock execution completed".to_string()))
}
pub fn verify_mocks(&self) -> Result<(), String> {
self.mock_registry.verify_all()
}
}
pub mod mock_helpers {
use super::*;
pub fn mock_chain_mint() -> MockBuilder {
MockBuilder::new("mint")
.in_namespace("chain")
.returns(Value::Int(12345))
.logs("Mock chain::mint called")
}
pub fn mock_oracle_fetch() -> MockBuilder {
MockBuilder::new("fetch")
.in_namespace("oracle")
.returns(Value::String("mock_price_data".to_string()))
.logs("Mock oracle::fetch called")
}
pub fn mock_service_call() -> MockBuilder {
MockBuilder::new("call")
.in_namespace("service")
.returns(Value::String("mock_service_response".to_string()))
.logs("Mock service::call called")
}
pub fn mock_auth_session() -> MockBuilder {
MockBuilder::new("session")
.in_namespace("auth")
.returns(Value::String("mock_session_id".to_string()))
.logs("Mock auth::session called")
}
pub fn mock_crypto_hash() -> MockBuilder {
MockBuilder::new("hash")
.in_namespace("crypto")
.returns(Value::String("mock_hash_value".to_string()))
.logs("Mock crypto::hash called")
}
}
pub trait MockTestUtils {
fn setup_mocks(&mut self) -> MockRegistry;
fn teardown_mocks(&mut self);
fn assert_mock_called(&self, name: &str, namespace: Option<&str>, expected_calls: usize);
fn assert_mock_not_called(&self, name: &str, namespace: Option<&str>);
}
impl MockTestUtils for MockRuntime {
fn setup_mocks(&mut self) -> MockRegistry {
self.mock_registry.enable();
self.mock_registry.clone()
}
fn teardown_mocks(&mut self) {
self.mock_registry.clear();
self.mock_registry.disable();
}
fn assert_mock_called(&self, name: &str, namespace: Option<&str>, expected_calls: usize) {
let key = self.mock_registry.get_mock_key(name, namespace);
if let Some(mock) = self.mock_registry.mocks.get(&key) {
assert_eq!(mock.call_count, expected_calls,
"Mock '{}' was called {} times, expected {}", key, mock.call_count, expected_calls);
} else {
panic!("Mock '{}' not found", key);
}
}
fn assert_mock_not_called(&self, name: &str, namespace: Option<&str>) {
let key = self.mock_registry.get_mock_key(name, namespace);
if let Some(mock) = self.mock_registry.mocks.get(&key) {
assert_eq!(mock.call_count, 0,
"Mock '{}' was called {} times, expected 0", key, mock.call_count);
}
}
}