Skip to main content

agent_sdk_core/testing/
telemetry.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 telemetry portion of that
5//! contract.
6//!
7use std::sync::{Arc, Mutex};
8
9use crate::{
10    telemetry_ports::{TelemetrySink, TelemetrySinkAck, TelemetrySinkError, TelemetrySinkSpec},
11    telemetry_records::{TelemetryExportCursor, TelemetryProjection},
12};
13
14#[derive(Clone)]
15/// In-memory scripted telemetry sink fixture for SDK conformance tests.
16/// Use it to script deterministic behavior in memory; any transcript or endpoint mutation is documented on the method that performs it.
17pub struct ScriptedTelemetrySink {
18    spec: TelemetrySinkSpec,
19    fail_next: Arc<Mutex<Option<TelemetrySinkError>>>,
20    exports: Arc<Mutex<Vec<TelemetryProjection>>>,
21}
22
23impl ScriptedTelemetrySink {
24    /// Creates a new testing::telemetry value with explicit
25    /// caller-provided inputs. This constructor is data-only and
26    /// performs no I/O or external side effects.
27    pub fn new(spec: TelemetrySinkSpec) -> Self {
28        Self {
29            spec,
30            fail_next: Arc::new(Mutex::new(None)),
31            exports: Arc::new(Mutex::new(Vec::new())),
32        }
33    }
34
35    /// Returns the sink spec currently held by this value.
36    /// This configures deterministic in-memory test state only.
37    pub fn sink_spec(&self) -> &TelemetrySinkSpec {
38        &self.spec
39    }
40
41    /// Builds the fail next value.
42    /// This is data construction and performs no I/O, journal append, event publication, or
43    /// process work.
44    pub fn fail_next(&self, summary: impl Into<String>) {
45        *self
46            .fail_next
47            .lock()
48            .expect("telemetry scripted sink fail lock") =
49            Some(TelemetrySinkError::unavailable(summary));
50    }
51
52    /// Returns the exports currently held by this value.
53    /// This configures deterministic in-memory test state only.
54    pub fn exports(&self) -> Vec<TelemetryProjection> {
55        self.exports
56            .lock()
57            .expect("telemetry scripted sink exports lock")
58            .clone()
59    }
60}
61
62impl TelemetrySink for ScriptedTelemetrySink {
63    fn spec(&self) -> &TelemetrySinkSpec {
64        &self.spec
65    }
66
67    fn export(
68        &self,
69        projection: &TelemetryProjection,
70        cursor: &TelemetryExportCursor,
71    ) -> Result<TelemetrySinkAck, TelemetrySinkError> {
72        if let Some(error) = self
73            .fail_next
74            .lock()
75            .expect("telemetry scripted sink fail lock")
76            .take()
77        {
78            return Err(error);
79        }
80        self.exports
81            .lock()
82            .expect("telemetry scripted sink exports lock")
83            .push(projection.clone());
84        Ok(TelemetrySinkAck::accepted(cursor.clone().acknowledged(
85            projection.source_record.source_cursor.clone(),
86        )))
87    }
88}