Skip to main content

agent_sdk_core/testing/
tool.rs

1//! Deterministic test-kit helpers for SDK consumers. Use these fakes and harnesses to
2//! exercise public contracts without live providers, real stores, product UI, network
3//! telemetry, or wall-clock-dependent infrastructure. They mutate only their
4//! in-memory state unless noted. This file contains the tool portion of that
5//! contract.
6//!
7use std::sync::{Arc, Mutex};
8
9use crate::{
10    capability::ExecutorRef,
11    domain::AgentError,
12    tool_ports::{ToolExecutionOutput, ToolExecutionRequest, ToolExecutor},
13};
14
15#[derive(Clone)]
16/// In-memory scripted tool executor fixture for SDK conformance tests.
17/// Use it to script deterministic behavior in memory; any transcript or endpoint mutation is documented on the method that performs it.
18pub struct ScriptedToolExecutor {
19    executor_ref: ExecutorRef,
20    output: ToolExecutionOutput,
21    calls: Arc<Mutex<Vec<ToolExecutionRequest>>>,
22}
23
24impl ScriptedToolExecutor {
25    /// Creates a new testing::tool value with explicit caller-provided
26    /// inputs. This constructor is data-only and performs no I/O or
27    /// external side effects.
28    ///
29    /// # Panics
30    ///
31    /// Panics if constructor invariants fail, such as invalid identifier
32    /// text or constructor-specific bounds. Use a fallible constructor such as
33    /// `try_new` when one is available for untrusted input.
34    pub fn new(executor_ref: ExecutorRef, output: ToolExecutionOutput) -> Self {
35        Self {
36            executor_ref,
37            output,
38            calls: Arc::new(Mutex::new(Vec::new())),
39        }
40    }
41
42    /// Operates on in-memory or journal-derived testing::tool state for
43    /// diagnostics and repair evidence. It does not create a second run loop
44    /// or product workflow owner.
45    pub fn calls(&self) -> Vec<ToolExecutionRequest> {
46        self.calls.lock().expect("tool executor calls lock").clone()
47    }
48
49    /// Returns the call count currently held by this value.
50    /// This reads deterministic in-memory test state and performs no external I/O.
51    pub fn call_count(&self) -> usize {
52        self.calls.lock().expect("tool executor calls lock").len()
53    }
54}
55
56impl ToolExecutor for ScriptedToolExecutor {
57    fn executor_ref(&self) -> &ExecutorRef {
58        &self.executor_ref
59    }
60
61    fn execute(&self, request: &ToolExecutionRequest) -> Result<ToolExecutionOutput, AgentError> {
62        self.calls
63            .lock()
64            .expect("tool executor calls lock")
65            .push(request.clone());
66        Ok(self.output.clone())
67    }
68}