use std::collections::{BTreeMap, VecDeque};
use std::fmt;
use std::sync::{Arc, Mutex};
use std::time::Duration;
use async_trait::async_trait;
use harn_clock::{Clock, PausedClock, RealClock};
use time::OffsetDateTime;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct HarnessKind(Option<harn_builtin_meta::CapabilityId>);
#[allow(non_upper_case_globals)]
impl HarnessKind {
pub const Root: Self = Self(None);
pub const Stdio: Self = Self(Some(harn_builtin_meta::CapabilityId::Stdio));
pub const Term: Self = Self(Some(harn_builtin_meta::CapabilityId::Term));
pub const Clock: Self = Self(Some(harn_builtin_meta::CapabilityId::Clock));
pub const Fs: Self = Self(Some(harn_builtin_meta::CapabilityId::Fs));
pub const Env: Self = Self(Some(harn_builtin_meta::CapabilityId::Env));
pub const Random: Self = Self(Some(harn_builtin_meta::CapabilityId::Random));
pub const Net: Self = Self(Some(harn_builtin_meta::CapabilityId::Net));
pub const Process: Self = Self(Some(harn_builtin_meta::CapabilityId::Process));
pub const Channels: Self = Self(Some(harn_builtin_meta::CapabilityId::Channels));
pub const System: Self = Self(Some(harn_builtin_meta::CapabilityId::System));
pub const Secrets: Self = Self(Some(harn_builtin_meta::CapabilityId::Secrets));
pub const Llm: Self = Self(Some(harn_builtin_meta::CapabilityId::Llm));
pub const Agent: Self = Self(Some(harn_builtin_meta::CapabilityId::Agent));
pub const Tenant: Self = Self(Some(harn_builtin_meta::CapabilityId::Tenant));
pub const Auth: Self = Self(Some(harn_builtin_meta::CapabilityId::Auth));
pub const Obs: Self = Self(Some(harn_builtin_meta::CapabilityId::Observability));
pub const Verdict: Self = Self(Some(harn_builtin_meta::CapabilityId::Verdict));
pub const Tools: Self = Self(Some(harn_builtin_meta::CapabilityId::Tools));
pub const Ast: Self = Self(Some(harn_builtin_meta::CapabilityId::Ast));
pub const CodeIndex: Self = Self(Some(harn_builtin_meta::CapabilityId::CodeIndex));
pub const Computer: Self = Self(Some(harn_builtin_meta::CapabilityId::Computer));
pub const Embed: Self = Self(Some(harn_builtin_meta::CapabilityId::Embed));
pub const Memory: Self = Self(Some(harn_builtin_meta::CapabilityId::Memory));
pub const Sqlite: Self = Self(Some(harn_builtin_meta::CapabilityId::Sqlite));
pub const Postgres: Self = Self(Some(harn_builtin_meta::CapabilityId::Postgres));
pub const FsWatch: Self = Self(Some(harn_builtin_meta::CapabilityId::FsWatch));
pub const HostLease: Self = Self(Some(harn_builtin_meta::CapabilityId::HostLease));
pub const Scanner: Self = Self(Some(harn_builtin_meta::CapabilityId::Scanner));
pub const SecretStore: Self = Self(Some(harn_builtin_meta::CapabilityId::SecretStore));
pub const TerminalSession: Self = Self(Some(harn_builtin_meta::CapabilityId::TerminalSession));
pub const Rules: Self = Self(Some(harn_builtin_meta::CapabilityId::Rules));
pub const Lint: Self = Self(Some(harn_builtin_meta::CapabilityId::Lint));
pub const Runtime: Self = Self(Some(harn_builtin_meta::CapabilityId::Runtime));
pub const Interaction: Self = Self(Some(harn_builtin_meta::CapabilityId::Interaction));
pub const Project: Self = Self(Some(harn_builtin_meta::CapabilityId::Project));
pub const Testing: Self = Self(Some(harn_builtin_meta::CapabilityId::Testing));
pub const fn capability_id(self) -> Option<harn_builtin_meta::CapabilityId> {
self.0
}
pub const fn type_name(self) -> &'static str {
match self.0 {
None => "Harness",
Some(capability) => capability.type_name(),
}
}
pub const fn field_name(self) -> Option<&'static str> {
match self.0 {
None => None,
Some(capability) => Some(capability.field_name()),
}
}
pub fn from_field_name(name: &str) -> Option<Self> {
harn_builtin_meta::CapabilityId::from_field_name(name)
.map(|capability| Self(Some(capability)))
}
pub const SUB_HANDLES: &'static [HarnessKind] = &[
HarnessKind::Stdio,
HarnessKind::Term,
HarnessKind::Clock,
HarnessKind::Fs,
HarnessKind::Env,
HarnessKind::Random,
HarnessKind::Net,
HarnessKind::Process,
HarnessKind::Channels,
HarnessKind::System,
HarnessKind::Secrets,
HarnessKind::Llm,
HarnessKind::Agent,
HarnessKind::Tenant,
HarnessKind::Auth,
HarnessKind::Obs,
HarnessKind::Verdict,
HarnessKind::Tools,
HarnessKind::Ast,
HarnessKind::CodeIndex,
HarnessKind::Computer,
HarnessKind::Embed,
HarnessKind::Memory,
HarnessKind::Sqlite,
HarnessKind::Postgres,
HarnessKind::FsWatch,
HarnessKind::HostLease,
HarnessKind::Scanner,
HarnessKind::SecretStore,
HarnessKind::TerminalSession,
HarnessKind::Rules,
HarnessKind::Lint,
HarnessKind::Runtime,
HarnessKind::Interaction,
HarnessKind::Project,
HarnessKind::Testing,
];
pub const ALL: &'static [HarnessKind] = &[
HarnessKind::Root,
HarnessKind::Stdio,
HarnessKind::Term,
HarnessKind::Clock,
HarnessKind::Fs,
HarnessKind::Env,
HarnessKind::Random,
HarnessKind::Net,
HarnessKind::Process,
HarnessKind::Channels,
HarnessKind::System,
HarnessKind::Secrets,
HarnessKind::Llm,
HarnessKind::Agent,
HarnessKind::Tenant,
HarnessKind::Auth,
HarnessKind::Obs,
HarnessKind::Verdict,
HarnessKind::Tools,
HarnessKind::Ast,
HarnessKind::CodeIndex,
HarnessKind::Computer,
HarnessKind::Embed,
HarnessKind::Memory,
HarnessKind::Sqlite,
HarnessKind::Postgres,
HarnessKind::FsWatch,
HarnessKind::HostLease,
HarnessKind::Scanner,
HarnessKind::SecretStore,
HarnessKind::TerminalSession,
HarnessKind::Rules,
HarnessKind::Lint,
HarnessKind::Runtime,
HarnessKind::Interaction,
HarnessKind::Project,
HarnessKind::Testing,
];
}
#[derive(Debug)]
struct HarnessClockRouter {
base: Arc<dyn Clock>,
override_clock: Mutex<Option<Arc<PausedClock>>>,
}
impl HarnessClockRouter {
fn new(base: Arc<dyn Clock>) -> Self {
Self {
base,
override_clock: Mutex::new(None),
}
}
fn active(&self) -> Arc<dyn Clock> {
self.override_clock
.lock()
.expect("harness clock override poisoned")
.as_ref()
.map(|clock| Arc::clone(clock) as Arc<dyn Clock>)
.unwrap_or_else(|| Arc::clone(&self.base))
}
fn set_unix_ms(&self, unix_ms: i64) -> Result<(), crate::VmError> {
let nanos = i128::from(unix_ms).checked_mul(1_000_000).ok_or_else(|| {
crate::VmError::TypeError("HarnessTesting.clock_set timestamp overflow".to_string())
})?;
let wall = OffsetDateTime::from_unix_timestamp_nanos(nanos).map_err(|error| {
crate::VmError::TypeError(format!(
"HarnessTesting.clock_set timestamp is out of range: {error}"
))
})?;
*self
.override_clock
.lock()
.expect("harness clock override poisoned") = Some(PausedClock::new(wall));
Ok(())
}
fn advance_ms(&self, milliseconds: i64) -> Result<i64, crate::VmError> {
let milliseconds = u64::try_from(milliseconds).map_err(|_| {
crate::VmError::TypeError(
"HarnessTesting.clock_advance expects non-negative milliseconds".to_string(),
)
})?;
let clock = self
.override_clock
.lock()
.expect("harness clock override poisoned")
.clone()
.ok_or_else(|| {
crate::VmError::Runtime(
"HarnessTesting.clock_advance requires clock_set first".to_string(),
)
})?;
clock.advance(Duration::from_millis(milliseconds));
Ok(harn_clock::now_wall_ms(clock.as_ref()))
}
fn clear_override(&self) {
*self
.override_clock
.lock()
.expect("harness clock override poisoned") = None;
}
async fn wait_for_advance(&self, duration: Duration) {
self.active().sleep(duration).await;
}
}
#[async_trait]
impl Clock for HarnessClockRouter {
fn now_utc(&self) -> OffsetDateTime {
self.active().now_utc()
}
fn monotonic_ms(&self) -> i64 {
self.active().monotonic_ms()
}
async fn sleep(&self, duration: Duration) {
let clock = self.active();
if self
.override_clock
.lock()
.expect("harness clock override poisoned")
.is_some()
{
if let Some(paused) = self
.override_clock
.lock()
.expect("harness clock override poisoned")
.clone()
{
paused.advance(duration);
return;
}
}
clock.sleep(duration).await;
}
async fn sleep_until_utc(&self, deadline: OffsetDateTime) {
let clock = self.active();
if let Some(paused) = self
.override_clock
.lock()
.expect("harness clock override poisoned")
.clone()
{
let now = paused.now_utc();
if deadline > now {
paused.advance_time(deadline - now);
}
return;
}
clock.sleep_until_utc(deadline).await;
}
}
pub struct HarnessInner {
clock: Arc<dyn Clock>,
clock_control: Arc<HarnessClockRouter>,
mode: HarnessMode,
net_policy: Option<crate::harness_net::NetPolicy>,
secret_provider: Option<Arc<dyn crate::secrets::SecretProvider>>,
quarantined: Mutex<bool>,
fixtures: Arc<CapabilityFixtureState>,
}
impl fmt::Debug for HarnessInner {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("HarnessInner")
.field("clock", &"<dyn Clock>")
.field("mode", &self.mode)
.field("net_policy", &self.net_policy)
.field(
"secret_provider",
&self
.secret_provider
.as_ref()
.map(|provider| provider.namespace().to_string()),
)
.field("quarantined", &self.is_quarantined())
.field("fixtures", &self.fixtures)
.finish()
}
}
impl HarnessInner {
pub fn clock(&self) -> &Arc<dyn Clock> {
&self.clock
}
pub(crate) fn set_test_clock(&self, unix_ms: i64) -> Result<(), crate::VmError> {
self.clock_control.set_unix_ms(unix_ms)
}
pub(crate) fn advance_test_clock(&self, milliseconds: i64) -> Result<i64, crate::VmError> {
self.clock_control.advance_ms(milliseconds)
}
pub(crate) fn clear_test_clock(&self) {
self.clock_control.clear_override();
}
pub(crate) async fn wait_for_clock_advance(&self, duration: Duration) {
self.clock_control.wait_for_advance(duration).await;
}
pub(crate) fn mode(&self) -> &HarnessMode {
&self.mode
}
pub fn net_policy(&self) -> Option<&crate::harness_net::NetPolicy> {
self.net_policy.as_ref()
}
pub fn secret_provider(&self) -> Option<&Arc<dyn crate::secrets::SecretProvider>> {
self.secret_provider.as_ref()
}
pub(crate) fn mark_quarantined(&self) {
if let Ok(mut guard) = self.quarantined.lock() {
*guard = true;
}
}
pub fn is_quarantined(&self) -> bool {
self.quarantined.lock().map(|guard| *guard).unwrap_or(false)
}
pub(crate) fn fixtures(&self) -> &CapabilityFixtureState {
&self.fixtures
}
pub(crate) fn fixtures_arc(&self) -> Arc<CapabilityFixtureState> {
Arc::clone(&self.fixtures)
}
}
#[derive(Debug, Default)]
pub(crate) struct CapabilityFixtureState {
inner: Mutex<CapabilityFixtureScopes>,
}
#[derive(Debug, Default)]
struct CapabilityFixtureScopes {
current: CapabilityFixtureInner,
stack: Vec<CapabilityFixtureInner>,
}
#[derive(Debug, Default, Clone)]
struct CapabilityFixtureInner {
enabled: bool,
responses: BTreeMap<(String, String), VecDeque<CapabilityFixtureResponse>>,
calls: Vec<CapabilityFixtureCall>,
}
#[derive(Debug, Clone)]
struct CapabilityFixtureResponse {
when: Option<crate::value::DictMap>,
repeat: bool,
result: Result<crate::VmValue, String>,
}
#[derive(Debug, Clone)]
pub(crate) struct CapabilityFixtureCall {
pub(crate) capability: String,
pub(crate) member: String,
pub(crate) args: Vec<crate::VmValue>,
pub(crate) host_operation: bool,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) struct CapabilityDriverFixtureContract {
pub(crate) capability: harn_builtin_meta::CapabilityId,
pub(crate) method: &'static str,
}
pub(crate) const CAPABILITY_DRIVER_FIXTURES: &[CapabilityDriverFixtureContract] = &[
CapabilityDriverFixtureContract {
capability: harn_builtin_meta::CapabilityId::Interaction,
method: "question_response",
},
CapabilityDriverFixtureContract {
capability: harn_builtin_meta::CapabilityId::Interaction,
method: "approval_response",
},
CapabilityDriverFixtureContract {
capability: harn_builtin_meta::CapabilityId::Interaction,
method: "dual_control_response",
},
CapabilityDriverFixtureContract {
capability: harn_builtin_meta::CapabilityId::Interaction,
method: "escalation_response",
},
CapabilityDriverFixtureContract {
capability: harn_builtin_meta::CapabilityId::Embed,
method: "text_response",
},
];
pub(crate) fn is_capability_driver_fixture(
capability: harn_builtin_meta::CapabilityId,
method: &str,
) -> bool {
CAPABILITY_DRIVER_FIXTURES
.iter()
.any(|contract| contract.capability == capability && contract.method == method)
}
impl CapabilityFixtureState {
pub(crate) fn clear(&self) {
let mut scopes = self.inner.lock().expect("capability fixtures poisoned");
scopes.current = CapabilityFixtureInner {
enabled: true,
..CapabilityFixtureInner::default()
};
}
pub(crate) fn push_scope(&self) {
let mut scopes = self.inner.lock().expect("capability fixtures poisoned");
let previous = std::mem::replace(
&mut scopes.current,
CapabilityFixtureInner {
enabled: true,
..CapabilityFixtureInner::default()
},
);
scopes.stack.push(previous);
}
pub(crate) fn pop_scope(&self) -> Result<(), crate::VmError> {
let mut scopes = self.inner.lock().expect("capability fixtures poisoned");
let Some(previous) = scopes.stack.pop() else {
return Err(crate::VmError::Runtime(
"HarnessTesting.pop_scope called without a matching push_scope".to_string(),
));
};
scopes.current = previous;
Ok(())
}
pub(crate) fn respond(
&self,
capability: &str,
member: &str,
response: Result<crate::VmValue, String>,
when: Option<crate::value::DictMap>,
repeat: bool,
) {
let mut scopes = self.inner.lock().expect("capability fixtures poisoned");
scopes.current.enabled = true;
scopes
.current
.responses
.entry((capability.to_string(), member.to_string()))
.or_default()
.push_back(CapabilityFixtureResponse {
when,
repeat,
result: response,
});
}
pub(crate) fn dispatch(
&self,
capability: harn_builtin_meta::CapabilityId,
method: &str,
args: &[crate::VmValue],
) -> Option<Result<crate::VmValue, crate::VmError>> {
self.dispatch_target(capability.field_name(), method, args, false)
}
pub(crate) fn dispatch_host(
&self,
capability: &str,
operation: &str,
params: &crate::value::DictMap,
) -> Option<Result<crate::VmValue, crate::VmError>> {
self.dispatch_target(
capability,
operation,
&[crate::VmValue::dict(params.clone())],
true,
)
}
fn dispatch_target(
&self,
capability: &str,
member: &str,
args: &[crate::VmValue],
host_operation: bool,
) -> Option<Result<crate::VmValue, crate::VmError>> {
let mut scopes = self.inner.lock().expect("capability fixtures poisoned");
if !scopes.current.enabled {
return None;
}
let key = (capability.to_string(), member.to_string());
if !scopes.current.responses.contains_key(&key) {
return None;
}
scopes.current.calls.push(CapabilityFixtureCall {
capability: capability.to_string(),
member: member.to_string(),
args: args.to_vec(),
host_operation,
});
let queue = scopes
.current
.responses
.get_mut(&key)
.expect("fixture key checked above");
let selector_match = |fixture: &CapabilityFixtureResponse| {
let Some(selector) = fixture.when.as_ref() else {
return false;
};
let Some(actual) = args.first().and_then(crate::VmValue::as_dict) else {
return false;
};
selector.iter().all(|(key, expected)| {
actual
.get(key)
.is_some_and(|value| crate::value::values_equal(value, expected))
})
};
let matched = queue
.iter()
.position(selector_match)
.or_else(|| queue.iter().position(|fixture| fixture.when.is_none()));
match matched {
Some(index) => {
let fixture = if queue[index].repeat {
Some(queue[index].clone())
} else {
queue.remove(index)
};
fixture.map(|fixture| {
fixture.result.map_err(|message| {
crate::VmError::Thrown(crate::VmValue::String(arcstr::ArcStr::from(
message,
)))
})
})
}
None => Some(Err(crate::VmError::Runtime(format!(
"no fixture for {capability}.{member} matched arguments {}",
crate::VmValue::List(std::sync::Arc::new(args.to_vec())).display()
)))),
}
}
pub(crate) fn calls(&self) -> Vec<CapabilityFixtureCall> {
self.inner
.lock()
.expect("capability fixtures poisoned")
.current
.calls
.clone()
}
}
#[derive(Debug)]
pub(crate) enum HarnessMode {
Real,
Null(NullHarnessState),
Mock(Arc<MockHarnessState>),
}
#[derive(Debug, Default)]
pub(crate) struct NullHarnessState {
deny_events: Mutex<Vec<DenyEvent>>,
}
impl NullHarnessState {
pub(crate) fn record_deny(
&self,
sub_handle: HarnessKind,
method: &str,
args: &[crate::VmValue],
) {
self.deny_events
.lock()
.expect("deny events poisoned")
.push(DenyEvent::new(
sub_handle,
method,
args.iter().map(crate::VmValue::display).collect(),
));
}
pub(crate) fn deny_events(&self) -> Vec<DenyEvent> {
self.deny_events
.lock()
.expect("deny events poisoned")
.clone()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DenyEvent {
pub sub_handle: HarnessKind,
pub method: String,
pub args: Vec<String>,
}
impl DenyEvent {
fn new(sub_handle: HarnessKind, method: &str, args: Vec<String>) -> Self {
Self {
sub_handle,
method: method.to_string(),
args,
}
}
}
#[derive(Debug)]
pub(crate) struct MockHarnessState {
calls: Mutex<Vec<HarnessCall>>,
clock: Arc<PausedClock>,
env: BTreeMap<String, String>,
fs_reads: BTreeMap<String, Vec<u8>>,
net_gets: BTreeMap<String, String>,
random_u64: Mutex<VecDeque<u64>>,
capability_responses:
Mutex<BTreeMap<(harn_builtin_meta::CapabilityId, String), VecDeque<crate::VmValue>>>,
stdin_lines: Mutex<VecDeque<String>>,
stdio: Mutex<String>,
stderr: Mutex<String>,
}
impl MockHarnessState {
pub(crate) fn record_call(
&self,
sub_handle: HarnessKind,
method: &str,
args: &[crate::VmValue],
) {
self.calls
.lock()
.expect("calls poisoned")
.push(HarnessCall::new(
sub_handle,
method,
args.iter().map(crate::VmValue::display).collect(),
));
}
pub(crate) fn calls(&self) -> Vec<HarnessCall> {
self.calls.lock().expect("calls poisoned").clone()
}
pub(crate) fn env_get(&self, key: &str) -> Option<&str> {
self.env.get(key).map(String::as_str)
}
pub(crate) fn fs_read(&self, path: &str) -> Option<&[u8]> {
self.fs_reads.get(path).map(Vec::as_slice)
}
pub(crate) fn net_get(&self, url: &str) -> Option<&str> {
self.net_gets.get(url).map(String::as_str)
}
pub(crate) fn next_random_u64(&self) -> Option<u64> {
let mut values = self.random_u64.lock().expect("random values poisoned");
values.pop_front()
}
pub(crate) fn capability_response(
&self,
capability: harn_builtin_meta::CapabilityId,
method: &str,
) -> Option<crate::VmValue> {
self.capability_responses
.lock()
.expect("capability responses poisoned")
.get_mut(&(capability, method.to_string()))
.and_then(VecDeque::pop_front)
}
pub(crate) fn advance_clock(&self, duration: std::time::Duration) {
self.clock.advance(duration);
}
pub(crate) fn push_stdio(&self, text: &str) {
self.stdio
.lock()
.expect("stdio buffer poisoned")
.push_str(text);
}
pub(crate) fn stdio(&self) -> String {
self.stdio.lock().expect("stdio buffer poisoned").clone()
}
pub(crate) fn push_stderr(&self, text: &str) {
self.stderr
.lock()
.expect("stderr buffer poisoned")
.push_str(text);
}
pub(crate) fn stderr(&self) -> String {
self.stderr.lock().expect("stderr buffer poisoned").clone()
}
pub(crate) fn pop_stdin_line(&self) -> Option<String> {
self.stdin_lines
.lock()
.expect("stdin queue poisoned")
.pop_front()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct HarnessCall {
pub sub_handle: HarnessKind,
pub method: String,
pub args: Vec<String>,
}
impl HarnessCall {
fn new(sub_handle: HarnessKind, method: &str, args: Vec<String>) -> Self {
Self {
sub_handle,
method: method.to_string(),
args,
}
}
}
#[derive(Debug)]
pub struct MockHarnessBuilder {
clock: Arc<PausedClock>,
env: BTreeMap<String, String>,
fs_reads: BTreeMap<String, Vec<u8>>,
net_gets: BTreeMap<String, String>,
random_u64: Vec<u64>,
capability_responses: BTreeMap<(harn_builtin_meta::CapabilityId, String), Vec<crate::VmValue>>,
stdin_lines: Vec<String>,
}
impl MockHarnessBuilder {
fn new() -> Self {
Self {
clock: paused_clock_at_unix_ms(0),
env: BTreeMap::new(),
fs_reads: BTreeMap::new(),
net_gets: BTreeMap::new(),
random_u64: Vec::new(),
capability_responses: BTreeMap::new(),
stdin_lines: Vec::new(),
}
}
pub fn clock_at_unix_ms(mut self, unix_ms: i64) -> Self {
self.clock = paused_clock_at_unix_ms(unix_ms);
self
}
pub fn clock_at(mut self, origin: OffsetDateTime) -> Self {
self.clock = PausedClock::new(origin);
self
}
pub fn env(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
self.env.insert(key.into(), value.into());
self
}
pub fn fs_read(mut self, path: impl Into<String>, data: impl Into<Vec<u8>>) -> Self {
self.fs_reads.insert(path.into(), data.into());
self
}
pub fn net_get(mut self, url: impl Into<String>, body: impl Into<String>) -> Self {
self.net_gets.insert(url.into(), body.into());
self
}
pub fn random_u64(mut self, value: u64) -> Self {
self.random_u64.push(value);
self
}
pub fn capability_response(
mut self,
capability: harn_builtin_meta::CapabilityId,
method: impl Into<String>,
value: crate::VmValue,
) -> Self {
self.capability_responses
.entry((capability, method.into()))
.or_default()
.push(value);
self
}
pub fn stdin_line(mut self, line: impl Into<String>) -> Self {
self.stdin_lines.push(line.into());
self
}
pub fn build(self) -> Harness {
let clock = self.clock;
Harness::with_mode(
clock.clone() as Arc<dyn Clock>,
HarnessMode::Mock(Arc::new(MockHarnessState {
calls: Mutex::new(Vec::new()),
clock,
env: self.env,
fs_reads: self.fs_reads,
net_gets: self.net_gets,
random_u64: Mutex::new(self.random_u64.into()),
capability_responses: Mutex::new(
self.capability_responses
.into_iter()
.map(|(key, values)| (key, values.into()))
.collect(),
),
stdin_lines: Mutex::new(self.stdin_lines.into()),
stdio: Mutex::new(String::new()),
stderr: Mutex::new(String::new()),
})),
)
}
}
#[derive(Debug, Clone)]
pub struct Harness {
inner: Arc<HarnessInner>,
}
impl Harness {
pub fn real() -> Self {
Self::with_mode(Arc::new(RealClock::new()), HarnessMode::Real)
}
pub fn null() -> Self {
Self::with_mode(
paused_clock_at_unix_ms(0) as Arc<dyn Clock>,
HarnessMode::Null(NullHarnessState::default()),
)
}
pub fn mock() -> MockHarnessBuilder {
MockHarnessBuilder::new()
}
pub fn with_clock(clock: Arc<dyn Clock>) -> Self {
Self::with_mode(clock, HarnessMode::Real)
}
pub fn from_inner(inner: Arc<HarnessInner>) -> Self {
Self { inner }
}
fn with_mode(clock: Arc<dyn Clock>, mode: HarnessMode) -> Self {
let clock_control = Arc::new(HarnessClockRouter::new(clock));
let clock: Arc<dyn Clock> = clock_control.clone();
let inner = Arc::new(HarnessInner {
clock,
clock_control,
mode,
net_policy: None,
secret_provider: None,
quarantined: Mutex::new(false),
fixtures: Arc::new(CapabilityFixtureState::default()),
});
Self { inner }
}
pub fn with_net_policy(&self, policy: crate::harness_net::NetPolicy) -> Self {
let clock = Arc::clone(&self.inner.clock);
let clock_control = Arc::clone(&self.inner.clock_control);
let mode = self.clone_mode_for_child();
#[allow(clippy::arc_with_non_send_sync)]
let inner = Arc::new(HarnessInner {
clock,
clock_control,
mode,
net_policy: Some(policy),
secret_provider: self.inner.secret_provider.clone(),
quarantined: Mutex::new(self.is_quarantined()),
fixtures: Arc::clone(&self.inner.fixtures),
});
Self { inner }
}
pub fn with_secret_provider(&self, provider: Arc<dyn crate::secrets::SecretProvider>) -> Self {
let clock = Arc::clone(&self.inner.clock);
let clock_control = Arc::clone(&self.inner.clock_control);
let mode = self.clone_mode_for_child();
#[allow(clippy::arc_with_non_send_sync)]
let inner = Arc::new(HarnessInner {
clock,
clock_control,
mode,
net_policy: self.inner.net_policy.clone(),
secret_provider: Some(provider),
quarantined: Mutex::new(self.is_quarantined()),
fixtures: Arc::clone(&self.inner.fixtures),
});
Self { inner }
}
fn clone_mode_for_child(&self) -> HarnessMode {
match &self.inner.mode {
HarnessMode::Real => HarnessMode::Real,
HarnessMode::Null(_) => HarnessMode::Null(NullHarnessState::default()),
HarnessMode::Mock(state) => HarnessMode::Mock(Arc::clone(state)),
}
}
pub fn is_quarantined(&self) -> bool {
self.inner.is_quarantined()
}
pub fn deny_events(&self) -> Vec<DenyEvent> {
match self.inner.mode() {
HarnessMode::Null(state) => state.deny_events(),
HarnessMode::Real | HarnessMode::Mock(_) => Vec::new(),
}
}
pub fn calls(&self) -> Vec<HarnessCall> {
match self.inner.mode() {
HarnessMode::Mock(state) => state.calls(),
HarnessMode::Real | HarnessMode::Null(_) => Vec::new(),
}
}
pub fn captured_stdio(&self) -> String {
match self.inner.mode() {
HarnessMode::Mock(state) => state.stdio(),
HarnessMode::Real | HarnessMode::Null(_) => String::new(),
}
}
pub fn captured_stderr(&self) -> String {
match self.inner.mode() {
HarnessMode::Mock(state) => state.stderr(),
HarnessMode::Real | HarnessMode::Null(_) => String::new(),
}
}
pub fn test() -> (Self, Arc<PausedClock>) {
Self::with_paused_clock(OffsetDateTime::UNIX_EPOCH)
}
pub fn with_paused_clock(origin: OffsetDateTime) -> (Self, Arc<PausedClock>) {
let paused = PausedClock::new(origin);
let as_dyn: Arc<dyn Clock> = paused.clone();
(Self::with_clock(as_dyn), paused)
}
pub fn stdio(&self) -> HarnessStdio {
HarnessStdio {
inner: Arc::clone(&self.inner),
}
}
pub fn term(&self) -> HarnessTerm {
HarnessTerm {
inner: Arc::clone(&self.inner),
}
}
pub fn clock(&self) -> HarnessClock {
HarnessClock {
inner: Arc::clone(&self.inner),
}
}
pub fn fs(&self) -> HarnessFs {
HarnessFs {
inner: Arc::clone(&self.inner),
}
}
pub fn env(&self) -> HarnessEnv {
HarnessEnv {
inner: Arc::clone(&self.inner),
}
}
pub fn random(&self) -> HarnessRandom {
HarnessRandom {
inner: Arc::clone(&self.inner),
}
}
pub fn net(&self) -> HarnessNet {
HarnessNet {
inner: Arc::clone(&self.inner),
}
}
pub fn process(&self) -> HarnessProcess {
HarnessProcess {
inner: Arc::clone(&self.inner),
}
}
pub fn channels(&self) -> HarnessChannels {
HarnessChannels {
inner: Arc::clone(&self.inner),
}
}
pub fn system(&self) -> HarnessSystem {
HarnessSystem {
inner: Arc::clone(&self.inner),
}
}
pub fn secrets(&self) -> HarnessSecrets {
HarnessSecrets {
inner: Arc::clone(&self.inner),
}
}
pub fn llm(&self) -> HarnessLlm {
HarnessLlm {
inner: Arc::clone(&self.inner),
}
}
pub fn tenant(&self) -> HarnessTenant {
HarnessTenant {
inner: Arc::clone(&self.inner),
}
}
pub fn auth(&self) -> HarnessAuth {
HarnessAuth {
inner: Arc::clone(&self.inner),
}
}
pub fn obs(&self) -> HarnessObs {
HarnessObs {
inner: Arc::clone(&self.inner),
}
}
pub fn testing(&self) -> HarnessTesting {
HarnessTesting {
inner: Arc::clone(&self.inner),
}
}
pub fn memory(&self) -> HarnessMemory {
HarnessMemory {
inner: Arc::clone(&self.inner),
}
}
pub fn sqlite(&self) -> HarnessSqlite {
HarnessSqlite {
inner: Arc::clone(&self.inner),
}
}
pub fn postgres(&self) -> HarnessPostgres {
HarnessPostgres {
inner: Arc::clone(&self.inner),
}
}
pub fn agent(&self) -> HarnessAgent {
HarnessAgent {
inner: Arc::clone(&self.inner),
}
}
pub fn into_vm_value(self) -> crate::value::VmValue {
crate::value::VmValue::harness(VmHarness {
inner: self.inner,
kind: HarnessKind::Root,
})
}
}
fn paused_clock_at_unix_ms(unix_ms: i64) -> Arc<PausedClock> {
let nanos = (unix_ms as i128).saturating_mul(1_000_000);
let origin =
OffsetDateTime::from_unix_timestamp_nanos(nanos).unwrap_or(OffsetDateTime::UNIX_EPOCH);
PausedClock::new(origin)
}
pub(crate) fn vm_string(value: impl Into<String>) -> crate::VmValue {
crate::VmValue::String(arcstr::ArcStr::from(value.into()))
}
impl Default for Harness {
fn default() -> Self {
Self::real()
}
}
#[derive(Debug, Clone)]
pub struct HarnessStdio {
inner: Arc<HarnessInner>,
}
#[derive(Debug, Clone)]
pub struct HarnessTerm {
inner: Arc<HarnessInner>,
}
#[derive(Debug, Clone)]
pub struct HarnessClock {
inner: Arc<HarnessInner>,
}
impl HarnessClock {
pub fn clock(&self) -> &Arc<dyn Clock> {
self.inner.clock()
}
}
include!("harness/value.rs");
include!("harness/tests.rs");
#[derive(Debug, Clone)]
pub struct HarnessFs {
inner: Arc<HarnessInner>,
}
#[derive(Debug, Clone)]
pub struct HarnessEnv {
inner: Arc<HarnessInner>,
}
#[derive(Debug, Clone)]
pub struct HarnessRandom {
inner: Arc<HarnessInner>,
}
#[derive(Debug, Clone)]
pub struct HarnessNet {
inner: Arc<HarnessInner>,
}
#[derive(Debug, Clone)]
pub struct HarnessProcess {
inner: Arc<HarnessInner>,
}
#[derive(Debug, Clone)]
pub struct HarnessChannels {
inner: Arc<HarnessInner>,
}
#[derive(Debug, Clone)]
pub struct HarnessSystem {
inner: Arc<HarnessInner>,
}
#[derive(Debug, Clone)]
pub struct HarnessSecrets {
inner: Arc<HarnessInner>,
}
#[derive(Debug, Clone)]
pub struct HarnessLlm {
inner: Arc<HarnessInner>,
}
#[derive(Debug, Clone)]
pub struct HarnessTenant {
inner: Arc<HarnessInner>,
}
#[derive(Debug, Clone)]
pub struct HarnessAuth {
inner: Arc<HarnessInner>,
}
#[derive(Debug, Clone)]
pub struct HarnessObs {
inner: Arc<HarnessInner>,
}
#[derive(Debug, Clone)]
pub struct HarnessTesting {
inner: Arc<HarnessInner>,
}
#[derive(Debug, Clone)]
pub struct HarnessMemory {
inner: Arc<HarnessInner>,
}
#[derive(Debug, Clone)]
pub struct HarnessSqlite {
inner: Arc<HarnessInner>,
}
#[derive(Debug, Clone)]
pub struct HarnessPostgres {
inner: Arc<HarnessInner>,
}
#[derive(Debug, Clone)]
pub struct HarnessAgent {
inner: Arc<HarnessInner>,
}
macro_rules! sub_handle_inner {
($($ty:ty),* $(,)?) => {
$(
impl $ty {
#[allow(dead_code)]
pub(crate) fn inner(&self) -> &Arc<HarnessInner> {
&self.inner
}
}
)*
};
}
sub_handle_inner!(
HarnessStdio,
HarnessTerm,
HarnessFs,
HarnessEnv,
HarnessRandom,
HarnessNet,
HarnessProcess,
HarnessChannels,
HarnessSystem,
HarnessSecrets,
HarnessLlm,
HarnessTenant,
HarnessAuth,
HarnessObs,
HarnessMemory,
HarnessSqlite,
HarnessPostgres,
HarnessAgent,
HarnessTesting,
);
impl HarnessClock {
#[allow(dead_code)]
pub(crate) fn inner(&self) -> &Arc<HarnessInner> {
&self.inner
}
}