Skip to main content

MockTool

Struct MockTool 

Source
pub struct MockTool { /* private fields */ }
Expand description

A mock Tool that returns a fixed result or error when called.

Useful for testing tool dispatch, registries, and agent-loop tool execution without implementing a real tool. Configure the behaviour via the builder-style methods:

§Construction

use loopctl::testing::MockTool;

let tool = MockTool::new("echo", "Echoes input")
    .with_result("Echo: hello")
    .with_concurrency_safe(true);

§Example — registering in a tool registry

use loopctl::testing::MockTool;
use loopctl::tool::ToolRegistry;

let tool = MockTool::new("echo", "Echoes input")
    .with_result("Echo: hello");

let mut registry = ToolRegistry::new();
registry.register(tool);

assert!(registry.contains("echo"));

Implementations§

Source§

impl MockTool

Construction and builder methods for MockTool.

The builder pattern lets you configure the mock’s behaviour fluently. All builder methods consume and return Self, so you can chain them directly after MockTool::new.

§Configuration matrix

MethodAffects
MockTool::with_resultText returned on success
MockTool::with_errorSwitches to error path
MockTool::with_concurrency_safeConcurrency flag
MockTool::with_read_onlyRead-only flag
MockTool::with_schemaJSON input schema
MockTool::with_system_promptSystem prompt text
Source

pub fn new(name: &str, description: &str) -> Self

Create a new mock tool with the given name and description.

Returns a tool with sensible defaults that can be registered in a ToolRegistry immediately. Use the builder methods to customise behaviour before registration.

Defaults:

PropertyDefault
result"mock result"
is_errorfalse
is_concurrency_safefalse
is_read_onlytrue
input_schema{"type":"object","properties":{"input":{"type":"string"}}}
system_promptNone
§Example
use loopctl::testing::MockTool;

let tool = MockTool::new("calculator", "Performs arithmetic");
Source

pub fn with_result(self, result: &str) -> Self

Set the text result this tool returns on success.

The value is wrapped in ToolOutput::text when Tool::call is invoked. If MockTool::with_error is also called, this string is used as the error message instead.

§Example
use loopctl::testing::MockTool;

let tool = MockTool::new("echo", "Echoes input")
    .with_result("Echo: hello");
Source

pub fn with_error(self) -> Self

Make this tool return a ToolError::Execution instead of a successful result.

The result value (set via MockTool::with_result) is used as the error message string. Useful for testing agent error-handling and retry logic. Call this after MockTool::with_result to ensure the error message is set correctly.

§Example
use loopctl::testing::MockTool;

let tool = MockTool::new("fail", "Always fails")
    .with_result("something went wrong")
    .with_error();
Source

pub fn with_concurrency_safe(self, safe: bool) -> Self

Set the concurrency-safety flag.

When true, the framework may invoke this tool concurrently with other concurrency-safe tools. Returned by Tool::is_concurrency_safe.

Defaults to false — most test tools don’t need concurrency. Set to true when testing the framework’s parallel tool execution logic.

§Example
use loopctl::testing::MockTool;

let tool = MockTool::new("read", "Reads data")
    .with_concurrency_safe(true);
Source

pub fn with_read_only(self, read_only: bool) -> Self

Set the read-only flag.

When true (the default), the tool is considered side-effect free. Returned by Tool::is_read_only.

Set to false when testing that the framework serialises write operations correctly — e.g. two write tools should not execute concurrently.

§Example
use loopctl::testing::MockTool;

let tool = MockTool::new("write", "Writes data")
    .with_read_only(false);
Source

pub fn with_delay(self, delay: Duration) -> Self

Inject an artificial delay into Tool::call before it resolves.

Defaults to zero (instant resolution). Use this when testing timing-sensitive behaviour such as parallel-dispatch overlap, cancellation-during-execution, or per-event timeouts.

§Example
use std::time::Duration;
use loopctl::testing::MockTool;

let tool = MockTool::new("slow", "A slow tool")
    .with_delay(Duration::from_millis(50));
Source

pub fn with_schema(self, schema: Value) -> Self

Override the input JSON schema.

The default schema is a trivial object with a single input string property. Use this when the code under test validates tool schemas, generates documentation from them, or when the model needs a richer schema to produce correct tool calls.

§Example
use loopctl::testing::MockTool;
use serde_json::json;

let tool = MockTool::new("search", "Searches the web")
    .with_schema(json!({
        "type": "object",
        "properties": {
            "query": { "type": "string" },
            "limit": { "type": "integer" }
        },
        "required": ["query"]
    }));
Source

pub fn with_system_prompt(self, prompt: &str) -> Self

Attach a system prompt that the framework injects when this tool is available.

Returned by Tool::system_prompt. Useful for testing that the agent correctly assembles system prompts from tool metadata.

When the tool is registered, the framework concatenates all tool system prompts into the system message sent to the model.

§Example
use loopctl::testing::MockTool;

let tool = MockTool::new("bash", "Runs shell commands")
    .with_system_prompt("Prefer simple commands over pipelines.");

Trait Implementations§

Source§

impl Tool for MockTool

Trait implementation that returns canned tool metadata and results.

Every method delegates to the fields configured via the builder methods on MockTool. The call implementation ignores its _input and _context parameters entirely, returning either ToolOutput::text or ToolError::Execution depending on whether MockTool::with_error was called.

§Metadata methods

The name, description, and schema methods return the values set at construction time via MockTool::new. The is_concurrency_safe, is_read_only, and system_prompt methods reflect the flags configured through their respective builder methods.

§Execution semantics

The call future resolves immediately — there is no artificial delay. If your test needs to verify timeout or cancellation behaviour, wrap the mock in a layer that adds delays.

Source§

fn name(&self) -> &str

Return the tool name.

Always returns the string passed to MockTool::new. The framework uses this to look up tools in the ToolRegistry and to correlate tool-call requests from the model with the right implementation.

Source§

fn description(&self) -> &str

Return the tool description.

Always returns the string passed to MockTool::new. The description is included in the ToolSchema sent to the model so it can decide which tool to invoke.

Source§

fn schema(&self) -> ToolSchema

Build the ToolSchema for this mock tool.

Combines the name, description, and input_schema fields into the schema struct the framework sends to the model. The schema is also used by the ToolRegistry to describe available tools when calling the API.

Source§

fn call( &self, _input: Value, _context: &ToolContext, ) -> Pin<Box<dyn Future<Output = Result<ToolOutput, ToolError>> + Send + '_>>

Execute the mock tool, returning the canned result or error.

The _input and _context parameters are ignored — the mock always returns the preconfigured value. This means you cannot test input validation through the mock; if you need that, write a real tool implementation.

The future resolves immediately (zero delay), making tests fast and deterministic.

§Example
use loopctl::testing::MockTool;
use loopctl::tool::{Tool, ToolContext};
use serde_json::json;

let tool = MockTool::new("echo", "Echoes").with_result("pong");
let ctx = ToolContext::default();
let result = tool.call(json!({"msg": "ping"}), &ctx).await;
assert_eq!(result.unwrap().text_content(), "pong");
Source§

fn is_concurrency_safe(&self) -> bool

Return whether this tool is safe to run concurrently.

Set via MockTool::with_concurrency_safe. Defaults to false. When true, the framework’s tool executor may invoke this tool in parallel with other concurrency-safe tools, improving throughput for read-only or independent operations.

Source§

fn is_read_only(&self) -> bool

Return whether this tool is read-only (no side effects).

Set via MockTool::with_read_only. Defaults to true because most test tools don’t need to simulate writes.

Source§

fn system_prompt(&self) -> Option<String>

Return the optional system prompt for this tool.

Set via MockTool::with_system_prompt. Defaults to None. When present, the framework appends this prompt to the agent’s system message, giving the model contextual guidance on how to use the tool effectively.

Source§

fn is_safe_for_concurrent_execution(&self, _input: &Value) -> bool

Dynamic concurrency check based on the specific input. Read more
Source§

fn resource_key(&self, _input: &Value) -> Option<String>

A stable key identifying the resource this call touches, for conflict detection during parallel dispatch. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more