Skip to main content

traitclaw_test_utils/
runtime.rs

1//! Runtime construction helpers for strategy tests.
2//!
3//! [`make_runtime`] constructs a fully-configured [`AgentRuntime`]
4//! with sensible defaults, reducing test setup to a single function call.
5//!
6//! # Example
7//!
8//! ```rust
9//! use traitclaw_test_utils::provider::MockProvider;
10//! use traitclaw_test_utils::runtime::make_runtime;
11//!
12//! let rt = make_runtime(MockProvider::text("ok"), vec![]);
13//! // rt is ready for strategy.execute(&rt, "input", "session-1")
14//! ```
15
16use std::sync::Arc;
17
18use async_trait::async_trait;
19
20use traitclaw_core::config::AgentConfig;
21use traitclaw_core::traits::context_manager::ContextManager;
22use traitclaw_core::traits::output_transformer::OutputTransformer;
23use traitclaw_core::traits::provider::Provider;
24use traitclaw_core::traits::strategy::AgentRuntime;
25use traitclaw_core::traits::tool::ErasedTool;
26use traitclaw_core::traits::tool_registry::SimpleRegistry;
27use traitclaw_core::traits::tracker::Tracker;
28use traitclaw_core::types::agent_state::AgentState;
29use traitclaw_core::types::completion::CompletionResponse;
30use traitclaw_core::types::message::Message;
31
32use crate::memory::MockMemory;
33
34// ── Noop Implementations (pub(crate) only) ──────────────────────────────
35
36pub(crate) struct NoopTracker;
37
38impl Tracker for NoopTracker {
39    fn on_iteration(&self, _state: &mut AgentState) {}
40    fn on_tool_call(&self, _name: &str, _args: &serde_json::Value, _state: &mut AgentState) {}
41    fn on_llm_response(&self, _response: &CompletionResponse, _state: &mut AgentState) {}
42    fn recommended_concurrency(&self, _state: &AgentState) -> usize {
43        usize::MAX
44    }
45}
46
47pub(crate) struct NoopContextManager;
48
49#[async_trait]
50impl ContextManager for NoopContextManager {
51    async fn prepare(
52        &self,
53        _messages: &mut Vec<Message>,
54        _context_window: usize,
55        _state: &mut AgentState,
56    ) {
57    }
58}
59
60pub(crate) struct NoopOutputTransformer;
61
62#[async_trait]
63impl OutputTransformer for NoopOutputTransformer {
64    async fn transform(&self, output: String, _tool_name: &str, _state: &AgentState) -> String {
65        output
66    }
67}
68
69// ── Public API ───────────────────────────────────────────────────────────
70
71/// Create a minimal [`AgentRuntime`] with the given provider and tools.
72///
73/// All other components use default no-op implementations:
74/// - Memory: [`MockMemory`]
75/// - Tracker: no-op
76/// - Context/Output: pass-through
77/// - Guards/Hints/Hooks: empty
78/// - Tool execution: sequential
79/// - Config: [`AgentConfig::default()`]
80///
81/// # Example
82///
83/// ```rust
84/// use std::sync::Arc;
85/// use traitclaw_test_utils::provider::MockProvider;
86/// use traitclaw_test_utils::runtime::make_runtime;
87///
88/// let rt = make_runtime(MockProvider::text("hello"), vec![]);
89/// assert_eq!(rt.config.max_iterations, 20);
90/// ```
91pub fn make_runtime(
92    provider: impl Provider + 'static,
93    tools: Vec<Arc<dyn ErasedTool>>,
94) -> AgentRuntime {
95    make_runtime_with_config(provider, tools, AgentConfig::default())
96}
97
98/// Create a minimal [`AgentRuntime`] with custom [`AgentConfig`].
99///
100/// Same as [`make_runtime`], but accepts a custom config for tests
101/// that need non-default settings (e.g., `max_turns`, `model`).
102///
103/// # Example
104///
105/// ```rust
106/// use std::sync::Arc;
107/// use traitclaw_core::config::AgentConfig;
108/// use traitclaw_test_utils::provider::MockProvider;
109/// use traitclaw_test_utils::runtime::make_runtime_with_config;
110///
111/// let mut config = AgentConfig::default();
112/// config.max_iterations = 5;
113/// let rt = make_runtime_with_config(MockProvider::text("ok"), vec![], config);
114/// assert_eq!(rt.config.max_iterations, 5);
115/// ```
116pub fn make_runtime_with_config(
117    provider: impl Provider + 'static,
118    tools: Vec<Arc<dyn ErasedTool>>,
119    config: AgentConfig,
120) -> AgentRuntime {
121    AgentRuntime {
122        provider: Arc::new(provider),
123        tools: tools.clone(),
124        memory: Arc::new(MockMemory::new()),
125        guards: vec![],
126        hints: vec![],
127        tracker: Arc::new(NoopTracker),
128        context_manager: Arc::new(NoopContextManager),
129        execution_strategy: Arc::new(traitclaw_core::SequentialStrategy),
130        output_transformer: Arc::new(NoopOutputTransformer),
131        tool_registry: Arc::new(SimpleRegistry::new(tools)),
132        config,
133        hooks: vec![],
134    }
135}
136
137#[cfg(test)]
138mod tests {
139    use super::*;
140    use crate::provider::MockProvider;
141
142    #[test]
143    fn test_make_runtime_returns_valid_runtime() {
144        let rt = make_runtime(MockProvider::text("ok"), vec![]);
145        assert!(rt.guards.is_empty());
146        assert!(rt.hints.is_empty());
147        assert!(rt.hooks.is_empty());
148        assert!(rt.tools.is_empty());
149    }
150
151    #[test]
152    fn test_make_runtime_uses_default_config() {
153        let rt = make_runtime(MockProvider::text("ok"), vec![]);
154        assert_eq!(rt.config.max_iterations, 20);
155    }
156
157    #[test]
158    fn test_make_runtime_with_config_applies_custom_config() {
159        let mut config = AgentConfig::default();
160        config.max_iterations = 3;
161        let rt = make_runtime_with_config(MockProvider::text("ok"), vec![], config);
162        assert_eq!(rt.config.max_iterations, 3);
163    }
164
165    #[test]
166    fn test_make_runtime_with_tools() {
167        use crate::tools::EchoTool;
168
169        let echo: Arc<dyn ErasedTool> = Arc::new(EchoTool);
170        let rt = make_runtime(MockProvider::text("ok"), vec![echo]);
171        assert_eq!(rt.tools.len(), 1);
172    }
173}