1use std::collections::{BTreeMap, VecDeque};
22use std::fmt;
23use std::sync::{Arc, Mutex};
24use std::time::Duration;
25
26use async_trait::async_trait;
27use harn_clock::{Clock, PausedClock, RealClock};
28use time::OffsetDateTime;
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
35pub struct HarnessKind(Option<harn_builtin_meta::CapabilityId>);
36
37#[allow(non_upper_case_globals)]
38impl HarnessKind {
39 pub const Root: Self = Self(None);
40 pub const Stdio: Self = Self(Some(harn_builtin_meta::CapabilityId::Stdio));
41 pub const Term: Self = Self(Some(harn_builtin_meta::CapabilityId::Term));
42 pub const Clock: Self = Self(Some(harn_builtin_meta::CapabilityId::Clock));
43 pub const Fs: Self = Self(Some(harn_builtin_meta::CapabilityId::Fs));
44 pub const Env: Self = Self(Some(harn_builtin_meta::CapabilityId::Env));
45 pub const Random: Self = Self(Some(harn_builtin_meta::CapabilityId::Random));
46 pub const Net: Self = Self(Some(harn_builtin_meta::CapabilityId::Net));
47 pub const Process: Self = Self(Some(harn_builtin_meta::CapabilityId::Process));
48 pub const Channels: Self = Self(Some(harn_builtin_meta::CapabilityId::Channels));
49 pub const System: Self = Self(Some(harn_builtin_meta::CapabilityId::System));
50 pub const Secrets: Self = Self(Some(harn_builtin_meta::CapabilityId::Secrets));
51 pub const Llm: Self = Self(Some(harn_builtin_meta::CapabilityId::Llm));
52 pub const Agent: Self = Self(Some(harn_builtin_meta::CapabilityId::Agent));
53 pub const Tenant: Self = Self(Some(harn_builtin_meta::CapabilityId::Tenant));
54 pub const Auth: Self = Self(Some(harn_builtin_meta::CapabilityId::Auth));
55 pub const Obs: Self = Self(Some(harn_builtin_meta::CapabilityId::Observability));
56 pub const Verdict: Self = Self(Some(harn_builtin_meta::CapabilityId::Verdict));
57 pub const Tools: Self = Self(Some(harn_builtin_meta::CapabilityId::Tools));
58 pub const Ast: Self = Self(Some(harn_builtin_meta::CapabilityId::Ast));
59 pub const CodeIndex: Self = Self(Some(harn_builtin_meta::CapabilityId::CodeIndex));
60 pub const Computer: Self = Self(Some(harn_builtin_meta::CapabilityId::Computer));
61 pub const Embed: Self = Self(Some(harn_builtin_meta::CapabilityId::Embed));
62 pub const Memory: Self = Self(Some(harn_builtin_meta::CapabilityId::Memory));
63 pub const Sqlite: Self = Self(Some(harn_builtin_meta::CapabilityId::Sqlite));
64 pub const Postgres: Self = Self(Some(harn_builtin_meta::CapabilityId::Postgres));
65 pub const FsWatch: Self = Self(Some(harn_builtin_meta::CapabilityId::FsWatch));
66 pub const HostLease: Self = Self(Some(harn_builtin_meta::CapabilityId::HostLease));
67 pub const Scanner: Self = Self(Some(harn_builtin_meta::CapabilityId::Scanner));
68 pub const SecretStore: Self = Self(Some(harn_builtin_meta::CapabilityId::SecretStore));
69 pub const TerminalSession: Self = Self(Some(harn_builtin_meta::CapabilityId::TerminalSession));
70 pub const Rules: Self = Self(Some(harn_builtin_meta::CapabilityId::Rules));
71 pub const Lint: Self = Self(Some(harn_builtin_meta::CapabilityId::Lint));
72 pub const Runtime: Self = Self(Some(harn_builtin_meta::CapabilityId::Runtime));
73 pub const Interaction: Self = Self(Some(harn_builtin_meta::CapabilityId::Interaction));
74 pub const Project: Self = Self(Some(harn_builtin_meta::CapabilityId::Project));
75 pub const Dashboard: Self = Self(Some(harn_builtin_meta::CapabilityId::Dashboard));
76 pub const Workspace: Self = Self(Some(harn_builtin_meta::CapabilityId::Workspace));
77 pub const MergeCaptain: Self = Self(Some(harn_builtin_meta::CapabilityId::MergeCaptain));
78 pub const Session: Self = Self(Some(harn_builtin_meta::CapabilityId::Session));
79 pub const Permission: Self = Self(Some(harn_builtin_meta::CapabilityId::Permission));
80 pub const Text: Self = Self(Some(harn_builtin_meta::CapabilityId::Text));
81 pub const Lsp: Self = Self(Some(harn_builtin_meta::CapabilityId::Lsp));
82 pub const Credentials: Self = Self(Some(harn_builtin_meta::CapabilityId::Credentials));
83 pub const PrMonitor: Self = Self(Some(harn_builtin_meta::CapabilityId::PrMonitor));
84 pub const Workflow: Self = Self(Some(harn_builtin_meta::CapabilityId::Workflow));
85 pub const Testing: Self = Self(Some(harn_builtin_meta::CapabilityId::Testing));
86
87 pub const fn capability_id(self) -> Option<harn_builtin_meta::CapabilityId> {
88 self.0
89 }
90
91 pub const fn type_name(self) -> &'static str {
95 match self.0 {
96 None => "Harness",
97 Some(capability) => capability.type_name(),
98 }
99 }
100
101 pub const fn field_name(self) -> Option<&'static str> {
104 match self.0 {
105 None => None,
106 Some(capability) => Some(capability.field_name()),
107 }
108 }
109
110 pub fn from_field_name(name: &str) -> Option<Self> {
112 harn_builtin_meta::CapabilityId::from_field_name(name)
113 .map(|capability| Self(Some(capability)))
114 }
115
116 pub const SUB_HANDLES: &'static [HarnessKind] = &[
118 HarnessKind::Stdio,
119 HarnessKind::Term,
120 HarnessKind::Clock,
121 HarnessKind::Fs,
122 HarnessKind::Env,
123 HarnessKind::Random,
124 HarnessKind::Net,
125 HarnessKind::Process,
126 HarnessKind::Channels,
127 HarnessKind::System,
128 HarnessKind::Secrets,
129 HarnessKind::Llm,
130 HarnessKind::Agent,
131 HarnessKind::Tenant,
132 HarnessKind::Auth,
133 HarnessKind::Obs,
134 HarnessKind::Verdict,
135 HarnessKind::Tools,
136 HarnessKind::Ast,
137 HarnessKind::CodeIndex,
138 HarnessKind::Computer,
139 HarnessKind::Embed,
140 HarnessKind::Memory,
141 HarnessKind::Sqlite,
142 HarnessKind::Postgres,
143 HarnessKind::FsWatch,
144 HarnessKind::HostLease,
145 HarnessKind::Scanner,
146 HarnessKind::SecretStore,
147 HarnessKind::TerminalSession,
148 HarnessKind::Rules,
149 HarnessKind::Lint,
150 HarnessKind::Runtime,
151 HarnessKind::Interaction,
152 HarnessKind::Project,
153 HarnessKind::Dashboard,
154 HarnessKind::Workspace,
155 HarnessKind::MergeCaptain,
156 HarnessKind::Session,
157 HarnessKind::Permission,
158 HarnessKind::Text,
159 HarnessKind::Lsp,
160 HarnessKind::Credentials,
161 HarnessKind::PrMonitor,
162 HarnessKind::Workflow,
163 HarnessKind::Testing,
164 ];
165
166 pub const ALL: &'static [HarnessKind] = &[
168 HarnessKind::Root,
169 HarnessKind::Stdio,
170 HarnessKind::Term,
171 HarnessKind::Clock,
172 HarnessKind::Fs,
173 HarnessKind::Env,
174 HarnessKind::Random,
175 HarnessKind::Net,
176 HarnessKind::Process,
177 HarnessKind::Channels,
178 HarnessKind::System,
179 HarnessKind::Secrets,
180 HarnessKind::Llm,
181 HarnessKind::Agent,
182 HarnessKind::Tenant,
183 HarnessKind::Auth,
184 HarnessKind::Obs,
185 HarnessKind::Verdict,
186 HarnessKind::Tools,
187 HarnessKind::Ast,
188 HarnessKind::CodeIndex,
189 HarnessKind::Computer,
190 HarnessKind::Embed,
191 HarnessKind::Memory,
192 HarnessKind::Sqlite,
193 HarnessKind::Postgres,
194 HarnessKind::FsWatch,
195 HarnessKind::HostLease,
196 HarnessKind::Scanner,
197 HarnessKind::SecretStore,
198 HarnessKind::TerminalSession,
199 HarnessKind::Rules,
200 HarnessKind::Lint,
201 HarnessKind::Runtime,
202 HarnessKind::Interaction,
203 HarnessKind::Project,
204 HarnessKind::Dashboard,
205 HarnessKind::Workspace,
206 HarnessKind::MergeCaptain,
207 HarnessKind::Session,
208 HarnessKind::Permission,
209 HarnessKind::Text,
210 HarnessKind::Lsp,
211 HarnessKind::Credentials,
212 HarnessKind::PrMonitor,
213 HarnessKind::Workflow,
214 HarnessKind::Testing,
215 ];
216}
217
218#[derive(Debug)]
223struct HarnessClockRouter {
224 base: Arc<dyn Clock>,
225 override_clock: Mutex<Option<Arc<PausedClock>>>,
226}
227
228impl HarnessClockRouter {
229 fn new(base: Arc<dyn Clock>) -> Self {
230 Self {
231 base,
232 override_clock: Mutex::new(None),
233 }
234 }
235
236 fn active(&self) -> Arc<dyn Clock> {
237 self.override_clock
238 .lock()
239 .expect("harness clock override poisoned")
240 .as_ref()
241 .map(|clock| Arc::clone(clock) as Arc<dyn Clock>)
242 .unwrap_or_else(|| Arc::clone(&self.base))
243 }
244
245 fn set_unix_ms(&self, unix_ms: i64) -> Result<(), crate::VmError> {
246 let nanos = i128::from(unix_ms).checked_mul(1_000_000).ok_or_else(|| {
247 crate::VmError::TypeError("HarnessTesting.clock_set timestamp overflow".to_string())
248 })?;
249 let wall = OffsetDateTime::from_unix_timestamp_nanos(nanos).map_err(|error| {
250 crate::VmError::TypeError(format!(
251 "HarnessTesting.clock_set timestamp is out of range: {error}"
252 ))
253 })?;
254 *self
255 .override_clock
256 .lock()
257 .expect("harness clock override poisoned") = Some(PausedClock::new(wall));
258 Ok(())
259 }
260
261 fn advance_ms(&self, milliseconds: i64) -> Result<i64, crate::VmError> {
262 let milliseconds = u64::try_from(milliseconds).map_err(|_| {
263 crate::VmError::TypeError(
264 "HarnessTesting.clock_advance expects non-negative milliseconds".to_string(),
265 )
266 })?;
267 let clock = self
268 .override_clock
269 .lock()
270 .expect("harness clock override poisoned")
271 .clone()
272 .ok_or_else(|| {
273 crate::VmError::Runtime(
274 "HarnessTesting.clock_advance requires clock_set first".to_string(),
275 )
276 })?;
277 clock.advance(Duration::from_millis(milliseconds));
278 Ok(harn_clock::now_wall_ms(clock.as_ref()))
279 }
280
281 fn clear_override(&self) {
282 *self
283 .override_clock
284 .lock()
285 .expect("harness clock override poisoned") = None;
286 }
287
288 async fn wait_for_advance(&self, duration: Duration) {
289 self.active().sleep(duration).await;
290 }
291}
292
293#[async_trait]
294impl Clock for HarnessClockRouter {
295 fn now_utc(&self) -> OffsetDateTime {
296 self.active().now_utc()
297 }
298
299 fn monotonic_ms(&self) -> i64 {
300 self.active().monotonic_ms()
301 }
302
303 async fn sleep(&self, duration: Duration) {
304 let clock = self.active();
305 if self
306 .override_clock
307 .lock()
308 .expect("harness clock override poisoned")
309 .is_some()
310 {
311 if let Some(paused) = self
312 .override_clock
313 .lock()
314 .expect("harness clock override poisoned")
315 .clone()
316 {
317 paused.advance(duration);
318 return;
319 }
320 }
321 clock.sleep(duration).await;
322 }
323
324 async fn sleep_until_utc(&self, deadline: OffsetDateTime) {
325 let clock = self.active();
326 if let Some(paused) = self
327 .override_clock
328 .lock()
329 .expect("harness clock override poisoned")
330 .clone()
331 {
332 let now = paused.now_utc();
333 if deadline > now {
334 paused.advance_time(deadline - now);
335 }
336 return;
337 }
338 clock.sleep_until_utc(deadline).await;
339 }
340}
341
342pub struct HarnessInner {
348 clock: Arc<dyn Clock>,
349 clock_control: Arc<HarnessClockRouter>,
350 mode: HarnessMode,
351 net_policy: Option<crate::harness_net::NetPolicy>,
356 secret_provider: Option<Arc<dyn crate::secrets::SecretProvider>>,
359 quarantined: Mutex<bool>,
366 fixtures: Arc<CapabilityFixtureState>,
367}
368
369impl fmt::Debug for HarnessInner {
370 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
371 f.debug_struct("HarnessInner")
372 .field("clock", &"<dyn Clock>")
373 .field("mode", &self.mode)
374 .field("net_policy", &self.net_policy)
375 .field(
376 "secret_provider",
377 &self
378 .secret_provider
379 .as_ref()
380 .map(|provider| provider.namespace().to_string()),
381 )
382 .field("quarantined", &self.is_quarantined())
383 .field("fixtures", &self.fixtures)
384 .finish()
385 }
386}
387
388impl HarnessInner {
389 pub fn clock(&self) -> &Arc<dyn Clock> {
390 &self.clock
391 }
392
393 pub(crate) fn set_test_clock(&self, unix_ms: i64) -> Result<(), crate::VmError> {
394 self.clock_control.set_unix_ms(unix_ms)
395 }
396
397 pub(crate) fn advance_test_clock(&self, milliseconds: i64) -> Result<i64, crate::VmError> {
398 self.clock_control.advance_ms(milliseconds)
399 }
400
401 pub(crate) fn clear_test_clock(&self) {
402 self.clock_control.clear_override();
403 }
404
405 pub(crate) async fn wait_for_clock_advance(&self, duration: Duration) {
406 self.clock_control.wait_for_advance(duration).await;
407 }
408
409 pub(crate) fn mode(&self) -> &HarnessMode {
410 &self.mode
411 }
412
413 pub fn net_policy(&self) -> Option<&crate::harness_net::NetPolicy> {
414 self.net_policy.as_ref()
415 }
416
417 pub fn secret_provider(&self) -> Option<&Arc<dyn crate::secrets::SecretProvider>> {
418 self.secret_provider.as_ref()
419 }
420
421 pub(crate) fn mark_quarantined(&self) {
422 if let Ok(mut guard) = self.quarantined.lock() {
423 *guard = true;
424 }
425 }
426
427 pub fn is_quarantined(&self) -> bool {
428 self.quarantined.lock().map(|guard| *guard).unwrap_or(false)
429 }
430
431 pub(crate) fn fixtures(&self) -> &CapabilityFixtureState {
432 &self.fixtures
433 }
434
435 pub(crate) fn fixtures_arc(&self) -> Arc<CapabilityFixtureState> {
436 Arc::clone(&self.fixtures)
437 }
438}
439
440#[derive(Debug)]
441pub(crate) enum HarnessMode {
442 Real,
443 Null(NullHarnessState),
444 Mock(Arc<MockHarnessState>),
445}
446
447#[derive(Debug, Default)]
448pub(crate) struct NullHarnessState {
449 deny_events: Mutex<Vec<DenyEvent>>,
450}
451
452impl NullHarnessState {
453 pub(crate) fn record_deny(
454 &self,
455 sub_handle: HarnessKind,
456 method: &str,
457 args: &[crate::VmValue],
458 ) {
459 self.deny_events
460 .lock()
461 .expect("deny events poisoned")
462 .push(DenyEvent::new(
463 sub_handle,
464 method,
465 args.iter().map(crate::VmValue::display).collect(),
466 ));
467 }
468
469 pub(crate) fn deny_events(&self) -> Vec<DenyEvent> {
470 self.deny_events
471 .lock()
472 .expect("deny events poisoned")
473 .clone()
474 }
475}
476
477#[derive(Debug, Clone, PartialEq, Eq)]
478pub struct DenyEvent {
479 pub sub_handle: HarnessKind,
480 pub method: String,
481 pub args: Vec<String>,
482}
483
484impl DenyEvent {
485 fn new(sub_handle: HarnessKind, method: &str, args: Vec<String>) -> Self {
486 Self {
487 sub_handle,
488 method: method.to_string(),
489 args,
490 }
491 }
492}
493
494#[derive(Debug)]
495pub(crate) struct MockHarnessState {
496 calls: Mutex<Vec<HarnessCall>>,
497 clock: Arc<PausedClock>,
498 env: BTreeMap<String, String>,
499 fs_reads: BTreeMap<String, Vec<u8>>,
500 net_gets: BTreeMap<String, String>,
501 random_u64: Mutex<VecDeque<u64>>,
502 capability_responses:
503 Mutex<BTreeMap<(harn_builtin_meta::CapabilityId, String), VecDeque<crate::VmValue>>>,
504 stdin_lines: Mutex<VecDeque<String>>,
505 stdio: Mutex<String>,
506 stderr: Mutex<String>,
507}
508
509impl MockHarnessState {
510 pub(crate) fn record_call(
511 &self,
512 sub_handle: HarnessKind,
513 method: &str,
514 args: &[crate::VmValue],
515 ) {
516 self.calls
517 .lock()
518 .expect("calls poisoned")
519 .push(HarnessCall::new(
520 sub_handle,
521 method,
522 args.iter().map(crate::VmValue::display).collect(),
523 ));
524 }
525
526 pub(crate) fn calls(&self) -> Vec<HarnessCall> {
527 self.calls.lock().expect("calls poisoned").clone()
528 }
529
530 pub(crate) fn env_get(&self, key: &str) -> Option<&str> {
531 self.env.get(key).map(String::as_str)
532 }
533
534 pub(crate) fn fs_read(&self, path: &str) -> Option<&[u8]> {
535 self.fs_reads.get(path).map(Vec::as_slice)
536 }
537
538 pub(crate) fn net_get(&self, url: &str) -> Option<&str> {
539 self.net_gets.get(url).map(String::as_str)
540 }
541
542 pub(crate) fn next_random_u64(&self) -> Option<u64> {
543 let mut values = self.random_u64.lock().expect("random values poisoned");
544 values.pop_front()
545 }
546
547 pub(crate) fn capability_response(
548 &self,
549 capability: harn_builtin_meta::CapabilityId,
550 method: &str,
551 ) -> Option<crate::VmValue> {
552 self.capability_responses
553 .lock()
554 .expect("capability responses poisoned")
555 .get_mut(&(capability, method.to_string()))
556 .and_then(VecDeque::pop_front)
557 }
558
559 pub(crate) fn has_capability_response(
565 &self,
566 capability: harn_builtin_meta::CapabilityId,
567 method: &str,
568 ) -> bool {
569 self.capability_responses
570 .lock()
571 .expect("capability responses poisoned")
572 .get(&(capability, method.to_string()))
573 .is_some_and(|queued| !queued.is_empty())
574 }
575
576 pub(crate) fn advance_clock(&self, duration: std::time::Duration) {
577 self.clock.advance(duration);
578 }
579
580 pub(crate) fn push_stdio(&self, text: &str) {
581 self.stdio
582 .lock()
583 .expect("stdio buffer poisoned")
584 .push_str(text);
585 }
586
587 pub(crate) fn stdio(&self) -> String {
588 self.stdio.lock().expect("stdio buffer poisoned").clone()
589 }
590
591 pub(crate) fn push_stderr(&self, text: &str) {
592 self.stderr
593 .lock()
594 .expect("stderr buffer poisoned")
595 .push_str(text);
596 }
597
598 pub(crate) fn stderr(&self) -> String {
599 self.stderr.lock().expect("stderr buffer poisoned").clone()
600 }
601
602 pub(crate) fn pop_stdin_line(&self) -> Option<String> {
603 self.stdin_lines
604 .lock()
605 .expect("stdin queue poisoned")
606 .pop_front()
607 }
608}
609
610#[derive(Debug, Clone, PartialEq, Eq)]
611pub struct HarnessCall {
612 pub sub_handle: HarnessKind,
613 pub method: String,
614 pub args: Vec<String>,
615}
616
617impl HarnessCall {
618 fn new(sub_handle: HarnessKind, method: &str, args: Vec<String>) -> Self {
619 Self {
620 sub_handle,
621 method: method.to_string(),
622 args,
623 }
624 }
625}
626
627#[derive(Debug)]
628pub struct MockHarnessBuilder {
629 clock: Arc<PausedClock>,
630 env: BTreeMap<String, String>,
631 fs_reads: BTreeMap<String, Vec<u8>>,
632 net_gets: BTreeMap<String, String>,
633 random_u64: Vec<u64>,
634 capability_responses: BTreeMap<(harn_builtin_meta::CapabilityId, String), Vec<crate::VmValue>>,
635 stdin_lines: Vec<String>,
636}
637
638impl MockHarnessBuilder {
639 fn new() -> Self {
640 Self {
641 clock: paused_clock_at_unix_ms(0),
642 env: BTreeMap::new(),
643 fs_reads: BTreeMap::new(),
644 net_gets: BTreeMap::new(),
645 random_u64: Vec::new(),
646 capability_responses: BTreeMap::new(),
647 stdin_lines: Vec::new(),
648 }
649 }
650
651 pub fn clock_at_unix_ms(mut self, unix_ms: i64) -> Self {
652 self.clock = paused_clock_at_unix_ms(unix_ms);
653 self
654 }
655
656 pub fn clock_at(mut self, origin: OffsetDateTime) -> Self {
657 self.clock = PausedClock::new(origin);
658 self
659 }
660
661 pub fn env(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
662 self.env.insert(key.into(), value.into());
663 self
664 }
665
666 pub fn fs_read(mut self, path: impl Into<String>, data: impl Into<Vec<u8>>) -> Self {
667 self.fs_reads.insert(path.into(), data.into());
668 self
669 }
670
671 pub fn net_get(mut self, url: impl Into<String>, body: impl Into<String>) -> Self {
672 self.net_gets.insert(url.into(), body.into());
673 self
674 }
675
676 pub fn random_u64(mut self, value: u64) -> Self {
677 self.random_u64.push(value);
678 self
679 }
680
681 pub fn capability_response(
686 mut self,
687 capability: harn_builtin_meta::CapabilityId,
688 method: impl Into<String>,
689 value: crate::VmValue,
690 ) -> Self {
691 self.capability_responses
692 .entry((capability, method.into()))
693 .or_default()
694 .push(value);
695 self
696 }
697
698 pub fn stdin_line(mut self, line: impl Into<String>) -> Self {
704 self.stdin_lines.push(line.into());
705 self
706 }
707
708 pub fn build(self) -> Harness {
709 let clock = self.clock;
710 Harness::with_mode(
711 clock.clone() as Arc<dyn Clock>,
712 HarnessMode::Mock(Arc::new(MockHarnessState {
713 calls: Mutex::new(Vec::new()),
714 clock,
715 env: self.env,
716 fs_reads: self.fs_reads,
717 net_gets: self.net_gets,
718 random_u64: Mutex::new(self.random_u64.into()),
719 capability_responses: Mutex::new(
720 self.capability_responses
721 .into_iter()
722 .map(|(key, values)| (key, values.into()))
723 .collect(),
724 ),
725 stdin_lines: Mutex::new(self.stdin_lines.into()),
726 stdio: Mutex::new(String::new()),
727 stderr: Mutex::new(String::new()),
728 })),
729 )
730 }
731}
732
733#[derive(Debug, Clone)]
737pub struct Harness {
738 inner: Arc<HarnessInner>,
739}
740
741impl Harness {
742 pub fn real() -> Self {
748 Self::with_mode(Arc::new(RealClock::new()), HarnessMode::Real)
749 }
750
751 pub fn null() -> Self {
754 Self::with_mode(
755 paused_clock_at_unix_ms(0) as Arc<dyn Clock>,
756 HarnessMode::Null(NullHarnessState::default()),
757 )
758 }
759
760 pub fn mock() -> MockHarnessBuilder {
762 MockHarnessBuilder::new()
763 }
764
765 pub fn with_clock(clock: Arc<dyn Clock>) -> Self {
770 Self::with_mode(clock, HarnessMode::Real)
771 }
772
773 pub fn from_inner(inner: Arc<HarnessInner>) -> Self {
778 Self { inner }
779 }
780
781 fn with_mode(clock: Arc<dyn Clock>, mode: HarnessMode) -> Self {
782 let clock_control = Arc::new(HarnessClockRouter::new(clock));
783 let clock: Arc<dyn Clock> = clock_control.clone();
784 let inner = Arc::new(HarnessInner {
785 clock,
786 clock_control,
787 mode,
788 net_policy: None,
789 secret_provider: None,
790 quarantined: Mutex::new(false),
791 fixtures: Arc::new(CapabilityFixtureState::default()),
792 });
793 Self { inner }
794 }
795
796 pub fn with_net_policy(&self, policy: crate::harness_net::NetPolicy) -> Self {
810 let clock = Arc::clone(&self.inner.clock);
811 let clock_control = Arc::clone(&self.inner.clock_control);
812 let mode = self.clone_mode_for_child();
813 #[allow(clippy::arc_with_non_send_sync)]
815 let inner = Arc::new(HarnessInner {
816 clock,
817 clock_control,
818 mode,
819 net_policy: Some(policy),
820 secret_provider: self.inner.secret_provider.clone(),
821 quarantined: Mutex::new(self.is_quarantined()),
822 fixtures: Arc::clone(&self.inner.fixtures),
823 });
824 Self { inner }
825 }
826
827 pub fn with_secret_provider(&self, provider: Arc<dyn crate::secrets::SecretProvider>) -> Self {
833 let clock = Arc::clone(&self.inner.clock);
834 let clock_control = Arc::clone(&self.inner.clock_control);
835 let mode = self.clone_mode_for_child();
836 #[allow(clippy::arc_with_non_send_sync)]
837 let inner = Arc::new(HarnessInner {
838 clock,
839 clock_control,
840 mode,
841 net_policy: self.inner.net_policy.clone(),
842 secret_provider: Some(provider),
843 quarantined: Mutex::new(self.is_quarantined()),
844 fixtures: Arc::clone(&self.inner.fixtures),
845 });
846 Self { inner }
847 }
848
849 fn clone_mode_for_child(&self) -> HarnessMode {
850 match &self.inner.mode {
851 HarnessMode::Real => HarnessMode::Real,
852 HarnessMode::Null(_) => HarnessMode::Null(NullHarnessState::default()),
853 HarnessMode::Mock(state) => HarnessMode::Mock(Arc::clone(state)),
854 }
855 }
856
857 pub fn is_quarantined(&self) -> bool {
860 self.inner.is_quarantined()
861 }
862
863 pub fn deny_events(&self) -> Vec<DenyEvent> {
864 match self.inner.mode() {
865 HarnessMode::Null(state) => state.deny_events(),
866 HarnessMode::Real | HarnessMode::Mock(_) => Vec::new(),
867 }
868 }
869
870 pub fn calls(&self) -> Vec<HarnessCall> {
871 match self.inner.mode() {
872 HarnessMode::Mock(state) => state.calls(),
873 HarnessMode::Real | HarnessMode::Null(_) => Vec::new(),
874 }
875 }
876
877 pub fn captured_stdio(&self) -> String {
878 match self.inner.mode() {
879 HarnessMode::Mock(state) => state.stdio(),
880 HarnessMode::Real | HarnessMode::Null(_) => String::new(),
881 }
882 }
883
884 pub fn captured_stderr(&self) -> String {
885 match self.inner.mode() {
886 HarnessMode::Mock(state) => state.stderr(),
887 HarnessMode::Real | HarnessMode::Null(_) => String::new(),
888 }
889 }
890
891 pub fn test() -> (Self, Arc<PausedClock>) {
903 Self::with_paused_clock(OffsetDateTime::UNIX_EPOCH)
904 }
905
906 pub fn with_paused_clock(origin: OffsetDateTime) -> (Self, Arc<PausedClock>) {
910 let paused = PausedClock::new(origin);
911 let as_dyn: Arc<dyn Clock> = paused.clone();
912 (Self::with_clock(as_dyn), paused)
913 }
914
915 pub fn stdio(&self) -> HarnessStdio {
917 HarnessStdio {
918 inner: Arc::clone(&self.inner),
919 }
920 }
921
922 pub fn term(&self) -> HarnessTerm {
924 HarnessTerm {
925 inner: Arc::clone(&self.inner),
926 }
927 }
928
929 pub fn clock(&self) -> HarnessClock {
931 HarnessClock {
932 inner: Arc::clone(&self.inner),
933 }
934 }
935
936 pub fn fs(&self) -> HarnessFs {
938 HarnessFs {
939 inner: Arc::clone(&self.inner),
940 }
941 }
942
943 pub fn env(&self) -> HarnessEnv {
945 HarnessEnv {
946 inner: Arc::clone(&self.inner),
947 }
948 }
949
950 pub fn random(&self) -> HarnessRandom {
952 HarnessRandom {
953 inner: Arc::clone(&self.inner),
954 }
955 }
956
957 pub fn net(&self) -> HarnessNet {
959 HarnessNet {
960 inner: Arc::clone(&self.inner),
961 }
962 }
963
964 pub fn process(&self) -> HarnessProcess {
966 HarnessProcess {
967 inner: Arc::clone(&self.inner),
968 }
969 }
970
971 pub fn channels(&self) -> HarnessChannels {
973 HarnessChannels {
974 inner: Arc::clone(&self.inner),
975 }
976 }
977
978 pub fn system(&self) -> HarnessSystem {
980 HarnessSystem {
981 inner: Arc::clone(&self.inner),
982 }
983 }
984
985 pub fn secrets(&self) -> HarnessSecrets {
987 HarnessSecrets {
988 inner: Arc::clone(&self.inner),
989 }
990 }
991
992 pub fn llm(&self) -> HarnessLlm {
994 HarnessLlm {
995 inner: Arc::clone(&self.inner),
996 }
997 }
998
999 pub fn tenant(&self) -> HarnessTenant {
1001 HarnessTenant {
1002 inner: Arc::clone(&self.inner),
1003 }
1004 }
1005
1006 pub fn auth(&self) -> HarnessAuth {
1008 HarnessAuth {
1009 inner: Arc::clone(&self.inner),
1010 }
1011 }
1012
1013 pub fn obs(&self) -> HarnessObs {
1015 HarnessObs {
1016 inner: Arc::clone(&self.inner),
1017 }
1018 }
1019
1020 pub fn testing(&self) -> HarnessTesting {
1022 HarnessTesting {
1023 inner: Arc::clone(&self.inner),
1024 }
1025 }
1026
1027 pub fn memory(&self) -> HarnessMemory {
1029 HarnessMemory {
1030 inner: Arc::clone(&self.inner),
1031 }
1032 }
1033
1034 pub fn sqlite(&self) -> HarnessSqlite {
1035 HarnessSqlite {
1036 inner: Arc::clone(&self.inner),
1037 }
1038 }
1039
1040 pub fn postgres(&self) -> HarnessPostgres {
1041 HarnessPostgres {
1042 inner: Arc::clone(&self.inner),
1043 }
1044 }
1045
1046 pub fn agent(&self) -> HarnessAgent {
1047 HarnessAgent {
1048 inner: Arc::clone(&self.inner),
1049 }
1050 }
1051
1052 pub fn into_vm_value(self) -> crate::value::VmValue {
1054 crate::value::VmValue::harness(VmHarness {
1055 inner: self.inner,
1056 kind: HarnessKind::Root,
1057 })
1058 }
1059}
1060
1061fn paused_clock_at_unix_ms(unix_ms: i64) -> Arc<PausedClock> {
1062 let nanos = (unix_ms as i128).saturating_mul(1_000_000);
1063 let origin =
1064 OffsetDateTime::from_unix_timestamp_nanos(nanos).unwrap_or(OffsetDateTime::UNIX_EPOCH);
1065 PausedClock::new(origin)
1066}
1067
1068pub(crate) fn vm_string(value: impl Into<String>) -> crate::VmValue {
1069 crate::VmValue::String(arcstr::ArcStr::from(value.into()))
1070}
1071
1072impl Default for Harness {
1073 fn default() -> Self {
1074 Self::real()
1075 }
1076}
1077
1078#[derive(Debug, Clone)]
1081pub struct HarnessStdio {
1082 inner: Arc<HarnessInner>,
1083}
1084
1085#[derive(Debug, Clone)]
1087pub struct HarnessTerm {
1088 inner: Arc<HarnessInner>,
1089}
1090
1091#[derive(Debug, Clone)]
1093pub struct HarnessClock {
1094 inner: Arc<HarnessInner>,
1095}
1096
1097impl HarnessClock {
1098 pub fn clock(&self) -> &Arc<dyn Clock> {
1099 self.inner.clock()
1100 }
1101}
1102
1103include!("harness/fixtures.rs");
1104include!("harness/value.rs");
1105include!("harness/tests.rs");
1106#[derive(Debug, Clone)]
1109pub struct HarnessFs {
1110 inner: Arc<HarnessInner>,
1111}
1112
1113#[derive(Debug, Clone)]
1115pub struct HarnessEnv {
1116 inner: Arc<HarnessInner>,
1117}
1118
1119#[derive(Debug, Clone)]
1121pub struct HarnessRandom {
1122 inner: Arc<HarnessInner>,
1123}
1124
1125#[derive(Debug, Clone)]
1127pub struct HarnessNet {
1128 inner: Arc<HarnessInner>,
1129}
1130
1131#[derive(Debug, Clone)]
1133pub struct HarnessProcess {
1134 inner: Arc<HarnessInner>,
1135}
1136
1137#[derive(Debug, Clone)]
1139pub struct HarnessChannels {
1140 inner: Arc<HarnessInner>,
1141}
1142
1143#[derive(Debug, Clone)]
1149pub struct HarnessSystem {
1150 inner: Arc<HarnessInner>,
1151}
1152
1153#[derive(Debug, Clone)]
1155pub struct HarnessSecrets {
1156 inner: Arc<HarnessInner>,
1157}
1158
1159#[derive(Debug, Clone)]
1161pub struct HarnessLlm {
1162 inner: Arc<HarnessInner>,
1163}
1164
1165#[derive(Debug, Clone)]
1171pub struct HarnessTenant {
1172 inner: Arc<HarnessInner>,
1173}
1174
1175#[derive(Debug, Clone)]
1183pub struct HarnessAuth {
1184 inner: Arc<HarnessInner>,
1185}
1186
1187#[derive(Debug, Clone)]
1197pub struct HarnessObs {
1198 inner: Arc<HarnessInner>,
1199}
1200
1201#[derive(Debug, Clone)]
1205pub struct HarnessTesting {
1206 inner: Arc<HarnessInner>,
1207}
1208
1209#[derive(Debug, Clone)]
1213pub struct HarnessMemory {
1214 inner: Arc<HarnessInner>,
1215}
1216
1217#[derive(Debug, Clone)]
1218pub struct HarnessSqlite {
1219 inner: Arc<HarnessInner>,
1220}
1221
1222#[derive(Debug, Clone)]
1223pub struct HarnessPostgres {
1224 inner: Arc<HarnessInner>,
1225}
1226
1227#[derive(Debug, Clone)]
1228pub struct HarnessAgent {
1229 inner: Arc<HarnessInner>,
1230}
1231
1232macro_rules! sub_handle_inner {
1233 ($($ty:ty),* $(,)?) => {
1234 $(
1235 impl $ty {
1236 #[allow(dead_code)]
1237 pub(crate) fn inner(&self) -> &Arc<HarnessInner> {
1238 &self.inner
1239 }
1240 }
1241 )*
1242 };
1243}
1244sub_handle_inner!(
1245 HarnessStdio,
1246 HarnessTerm,
1247 HarnessFs,
1248 HarnessEnv,
1249 HarnessRandom,
1250 HarnessNet,
1251 HarnessProcess,
1252 HarnessChannels,
1253 HarnessSystem,
1254 HarnessSecrets,
1255 HarnessLlm,
1256 HarnessTenant,
1257 HarnessAuth,
1258 HarnessObs,
1259 HarnessMemory,
1260 HarnessSqlite,
1261 HarnessPostgres,
1262 HarnessAgent,
1263 HarnessTesting,
1264);
1265
1266impl HarnessClock {
1267 #[allow(dead_code)]
1268 pub(crate) fn inner(&self) -> &Arc<HarnessInner> {
1269 &self.inner
1270 }
1271}