Skip to main content

agent_sdk_core/testing/
extension.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 extension portion of that
5//! contract.
6//!
7use std::sync::{Arc, Mutex};
8
9use crate::{
10    domain::AgentError,
11    extension_ports::{
12        ExtensionActionExecutionOutput, ExtensionActionExecutionRequest, ExtensionActionExecutor,
13    },
14    package_extension::ExtensionBridgeRef,
15};
16
17#[derive(Clone)]
18/// In-memory scripted extension action executor fixture for SDK conformance tests.
19/// Use it to script deterministic behavior in memory; any transcript or endpoint mutation is documented on the method that performs it.
20pub struct ScriptedExtensionActionExecutor {
21    bridge_ref: ExtensionBridgeRef,
22    output: ExtensionActionExecutionOutput,
23    calls: Arc<Mutex<Vec<ExtensionActionExecutionRequest>>>,
24}
25
26impl ScriptedExtensionActionExecutor {
27    /// Creates a new testing::extension value with explicit
28    /// caller-provided inputs. This constructor is data-only and
29    /// performs no I/O or external side effects.
30    ///
31    /// # Panics
32    ///
33    /// Panics if constructor invariants fail, such as invalid identifier
34    /// text or constructor-specific bounds. Use a fallible constructor such as
35    /// `try_new` when one is available for untrusted input.
36    pub fn new(bridge_ref: ExtensionBridgeRef, output: ExtensionActionExecutionOutput) -> Self {
37        Self {
38            bridge_ref,
39            output,
40            calls: Arc::new(Mutex::new(Vec::new())),
41        }
42    }
43
44    /// Operates on in-memory or journal-derived testing::extension state for
45    /// diagnostics and repair evidence. It does not create a second run loop
46    /// or product workflow owner.
47    pub fn calls(&self) -> Vec<ExtensionActionExecutionRequest> {
48        self.calls
49            .lock()
50            .expect("extension action executor calls lock")
51            .clone()
52    }
53
54    /// Returns the call count currently held by this value.
55    /// This reads deterministic in-memory test state and performs no external I/O.
56    pub fn call_count(&self) -> usize {
57        self.calls
58            .lock()
59            .expect("extension action executor calls lock")
60            .len()
61    }
62}
63
64impl ExtensionActionExecutor for ScriptedExtensionActionExecutor {
65    fn bridge_ref(&self) -> &ExtensionBridgeRef {
66        &self.bridge_ref
67    }
68
69    fn execute(
70        &self,
71        request: &ExtensionActionExecutionRequest,
72    ) -> Result<ExtensionActionExecutionOutput, AgentError> {
73        self.calls
74            .lock()
75            .expect("extension action executor calls lock")
76            .push(request.clone());
77        Ok(self.output.clone())
78    }
79}