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 secret_provider(&self) -> Option<&Arc<dyn crate::secrets::SecretProvider>> {
829 self.inner.secret_provider()
830 }
831
832 pub fn with_secret_provider(&self, provider: Arc<dyn crate::secrets::SecretProvider>) -> Self {
838 let clock = Arc::clone(&self.inner.clock);
839 let clock_control = Arc::clone(&self.inner.clock_control);
840 let mode = self.clone_mode_for_child();
841 #[allow(clippy::arc_with_non_send_sync)]
842 let inner = Arc::new(HarnessInner {
843 clock,
844 clock_control,
845 mode,
846 net_policy: self.inner.net_policy.clone(),
847 secret_provider: Some(provider),
848 quarantined: Mutex::new(self.is_quarantined()),
849 fixtures: Arc::clone(&self.inner.fixtures),
850 });
851 Self { inner }
852 }
853
854 fn clone_mode_for_child(&self) -> HarnessMode {
855 match &self.inner.mode {
856 HarnessMode::Real => HarnessMode::Real,
857 HarnessMode::Null(_) => HarnessMode::Null(NullHarnessState::default()),
858 HarnessMode::Mock(state) => HarnessMode::Mock(Arc::clone(state)),
859 }
860 }
861
862 pub fn is_quarantined(&self) -> bool {
865 self.inner.is_quarantined()
866 }
867
868 pub fn deny_events(&self) -> Vec<DenyEvent> {
869 match self.inner.mode() {
870 HarnessMode::Null(state) => state.deny_events(),
871 HarnessMode::Real | HarnessMode::Mock(_) => Vec::new(),
872 }
873 }
874
875 pub fn calls(&self) -> Vec<HarnessCall> {
876 match self.inner.mode() {
877 HarnessMode::Mock(state) => state.calls(),
878 HarnessMode::Real | HarnessMode::Null(_) => Vec::new(),
879 }
880 }
881
882 pub fn captured_stdio(&self) -> String {
883 match self.inner.mode() {
884 HarnessMode::Mock(state) => state.stdio(),
885 HarnessMode::Real | HarnessMode::Null(_) => String::new(),
886 }
887 }
888
889 pub fn captured_stderr(&self) -> String {
890 match self.inner.mode() {
891 HarnessMode::Mock(state) => state.stderr(),
892 HarnessMode::Real | HarnessMode::Null(_) => String::new(),
893 }
894 }
895
896 pub fn test() -> (Self, Arc<PausedClock>) {
908 Self::with_paused_clock(OffsetDateTime::UNIX_EPOCH)
909 }
910
911 pub fn with_paused_clock(origin: OffsetDateTime) -> (Self, Arc<PausedClock>) {
915 let paused = PausedClock::new(origin);
916 let as_dyn: Arc<dyn Clock> = paused.clone();
917 (Self::with_clock(as_dyn), paused)
918 }
919
920 pub fn stdio(&self) -> HarnessStdio {
922 HarnessStdio {
923 inner: Arc::clone(&self.inner),
924 }
925 }
926
927 pub fn term(&self) -> HarnessTerm {
929 HarnessTerm {
930 inner: Arc::clone(&self.inner),
931 }
932 }
933
934 pub fn clock(&self) -> HarnessClock {
936 HarnessClock {
937 inner: Arc::clone(&self.inner),
938 }
939 }
940
941 pub fn fs(&self) -> HarnessFs {
943 HarnessFs {
944 inner: Arc::clone(&self.inner),
945 }
946 }
947
948 pub fn env(&self) -> HarnessEnv {
950 HarnessEnv {
951 inner: Arc::clone(&self.inner),
952 }
953 }
954
955 pub fn random(&self) -> HarnessRandom {
957 HarnessRandom {
958 inner: Arc::clone(&self.inner),
959 }
960 }
961
962 pub fn net(&self) -> HarnessNet {
964 HarnessNet {
965 inner: Arc::clone(&self.inner),
966 }
967 }
968
969 pub fn process(&self) -> HarnessProcess {
971 HarnessProcess {
972 inner: Arc::clone(&self.inner),
973 }
974 }
975
976 pub fn channels(&self) -> HarnessChannels {
978 HarnessChannels {
979 inner: Arc::clone(&self.inner),
980 }
981 }
982
983 pub fn system(&self) -> HarnessSystem {
985 HarnessSystem {
986 inner: Arc::clone(&self.inner),
987 }
988 }
989
990 pub fn secrets(&self) -> HarnessSecrets {
992 HarnessSecrets {
993 inner: Arc::clone(&self.inner),
994 }
995 }
996
997 pub fn llm(&self) -> HarnessLlm {
999 HarnessLlm {
1000 inner: Arc::clone(&self.inner),
1001 }
1002 }
1003
1004 pub fn tenant(&self) -> HarnessTenant {
1006 HarnessTenant {
1007 inner: Arc::clone(&self.inner),
1008 }
1009 }
1010
1011 pub fn auth(&self) -> HarnessAuth {
1013 HarnessAuth {
1014 inner: Arc::clone(&self.inner),
1015 }
1016 }
1017
1018 pub fn obs(&self) -> HarnessObs {
1020 HarnessObs {
1021 inner: Arc::clone(&self.inner),
1022 }
1023 }
1024
1025 pub fn testing(&self) -> HarnessTesting {
1027 HarnessTesting {
1028 inner: Arc::clone(&self.inner),
1029 }
1030 }
1031
1032 pub fn memory(&self) -> HarnessMemory {
1034 HarnessMemory {
1035 inner: Arc::clone(&self.inner),
1036 }
1037 }
1038
1039 pub fn sqlite(&self) -> HarnessSqlite {
1040 HarnessSqlite {
1041 inner: Arc::clone(&self.inner),
1042 }
1043 }
1044
1045 pub fn postgres(&self) -> HarnessPostgres {
1046 HarnessPostgres {
1047 inner: Arc::clone(&self.inner),
1048 }
1049 }
1050
1051 pub fn agent(&self) -> HarnessAgent {
1052 HarnessAgent {
1053 inner: Arc::clone(&self.inner),
1054 }
1055 }
1056
1057 pub fn into_vm_value(self) -> crate::value::VmValue {
1059 crate::value::VmValue::harness(VmHarness {
1060 inner: self.inner,
1061 kind: HarnessKind::Root,
1062 })
1063 }
1064}
1065
1066fn paused_clock_at_unix_ms(unix_ms: i64) -> Arc<PausedClock> {
1067 let nanos = (unix_ms as i128).saturating_mul(1_000_000);
1068 let origin =
1069 OffsetDateTime::from_unix_timestamp_nanos(nanos).unwrap_or(OffsetDateTime::UNIX_EPOCH);
1070 PausedClock::new(origin)
1071}
1072
1073pub(crate) fn vm_string(value: impl Into<String>) -> crate::VmValue {
1074 crate::VmValue::String(arcstr::ArcStr::from(value.into()))
1075}
1076
1077impl Default for Harness {
1078 fn default() -> Self {
1079 Self::real()
1080 }
1081}
1082
1083#[derive(Debug, Clone)]
1086pub struct HarnessStdio {
1087 inner: Arc<HarnessInner>,
1088}
1089
1090#[derive(Debug, Clone)]
1092pub struct HarnessTerm {
1093 inner: Arc<HarnessInner>,
1094}
1095
1096#[derive(Debug, Clone)]
1098pub struct HarnessClock {
1099 inner: Arc<HarnessInner>,
1100}
1101
1102impl HarnessClock {
1103 pub fn clock(&self) -> &Arc<dyn Clock> {
1104 self.inner.clock()
1105 }
1106}
1107
1108include!("harness/fixtures.rs");
1109include!("harness/value.rs");
1110include!("harness/tests.rs");
1111#[derive(Debug, Clone)]
1114pub struct HarnessFs {
1115 inner: Arc<HarnessInner>,
1116}
1117
1118#[derive(Debug, Clone)]
1120pub struct HarnessEnv {
1121 inner: Arc<HarnessInner>,
1122}
1123
1124#[derive(Debug, Clone)]
1126pub struct HarnessRandom {
1127 inner: Arc<HarnessInner>,
1128}
1129
1130#[derive(Debug, Clone)]
1132pub struct HarnessNet {
1133 inner: Arc<HarnessInner>,
1134}
1135
1136#[derive(Debug, Clone)]
1138pub struct HarnessProcess {
1139 inner: Arc<HarnessInner>,
1140}
1141
1142#[derive(Debug, Clone)]
1144pub struct HarnessChannels {
1145 inner: Arc<HarnessInner>,
1146}
1147
1148#[derive(Debug, Clone)]
1154pub struct HarnessSystem {
1155 inner: Arc<HarnessInner>,
1156}
1157
1158#[derive(Debug, Clone)]
1160pub struct HarnessSecrets {
1161 inner: Arc<HarnessInner>,
1162}
1163
1164#[derive(Debug, Clone)]
1166pub struct HarnessLlm {
1167 inner: Arc<HarnessInner>,
1168}
1169
1170#[derive(Debug, Clone)]
1176pub struct HarnessTenant {
1177 inner: Arc<HarnessInner>,
1178}
1179
1180#[derive(Debug, Clone)]
1188pub struct HarnessAuth {
1189 inner: Arc<HarnessInner>,
1190}
1191
1192#[derive(Debug, Clone)]
1202pub struct HarnessObs {
1203 inner: Arc<HarnessInner>,
1204}
1205
1206#[derive(Debug, Clone)]
1210pub struct HarnessTesting {
1211 inner: Arc<HarnessInner>,
1212}
1213
1214#[derive(Debug, Clone)]
1218pub struct HarnessMemory {
1219 inner: Arc<HarnessInner>,
1220}
1221
1222#[derive(Debug, Clone)]
1223pub struct HarnessSqlite {
1224 inner: Arc<HarnessInner>,
1225}
1226
1227#[derive(Debug, Clone)]
1228pub struct HarnessPostgres {
1229 inner: Arc<HarnessInner>,
1230}
1231
1232#[derive(Debug, Clone)]
1233pub struct HarnessAgent {
1234 inner: Arc<HarnessInner>,
1235}
1236
1237macro_rules! sub_handle_inner {
1238 ($($ty:ty),* $(,)?) => {
1239 $(
1240 impl $ty {
1241 #[allow(dead_code)]
1242 pub(crate) fn inner(&self) -> &Arc<HarnessInner> {
1243 &self.inner
1244 }
1245 }
1246 )*
1247 };
1248}
1249sub_handle_inner!(
1250 HarnessStdio,
1251 HarnessTerm,
1252 HarnessFs,
1253 HarnessEnv,
1254 HarnessRandom,
1255 HarnessNet,
1256 HarnessProcess,
1257 HarnessChannels,
1258 HarnessSystem,
1259 HarnessSecrets,
1260 HarnessLlm,
1261 HarnessTenant,
1262 HarnessAuth,
1263 HarnessObs,
1264 HarnessMemory,
1265 HarnessSqlite,
1266 HarnessPostgres,
1267 HarnessAgent,
1268 HarnessTesting,
1269);
1270
1271impl HarnessClock {
1272 #[allow(dead_code)]
1273 pub(crate) fn inner(&self) -> &Arc<HarnessInner> {
1274 &self.inner
1275 }
1276}