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 let paused = self
312 .override_clock
313 .lock()
314 .expect("harness clock override poisoned")
315 .clone();
316 if let Some(paused) = paused {
317 paused.advance(duration);
318 tokio::task::yield_now().await;
322 return;
323 }
324 }
325 clock.sleep(duration).await;
326 }
327
328 async fn sleep_until_utc(&self, deadline: OffsetDateTime) {
329 let clock = self.active();
330 if let Some(paused) = self
331 .override_clock
332 .lock()
333 .expect("harness clock override poisoned")
334 .clone()
335 {
336 let now = paused.now_utc();
337 if deadline > now {
338 paused.advance_time(deadline - now);
339 }
340 return;
341 }
342 clock.sleep_until_utc(deadline).await;
343 }
344}
345
346pub struct HarnessInner {
352 clock: Arc<dyn Clock>,
353 clock_control: Arc<HarnessClockRouter>,
354 mode: HarnessMode,
355 net_policy: Option<crate::harness_net::NetPolicy>,
360 secret_provider: Option<Arc<dyn crate::secrets::SecretProvider>>,
363 quarantined: Mutex<bool>,
370 fixtures: Arc<CapabilityFixtureState>,
371}
372
373impl fmt::Debug for HarnessInner {
374 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
375 f.debug_struct("HarnessInner")
376 .field("clock", &"<dyn Clock>")
377 .field("mode", &self.mode)
378 .field("net_policy", &self.net_policy)
379 .field(
380 "secret_provider",
381 &self
382 .secret_provider
383 .as_ref()
384 .map(|provider| provider.namespace().to_string()),
385 )
386 .field("quarantined", &self.is_quarantined())
387 .field("fixtures", &self.fixtures)
388 .finish()
389 }
390}
391
392impl HarnessInner {
393 pub fn clock(&self) -> &Arc<dyn Clock> {
394 &self.clock
395 }
396
397 pub(crate) fn set_test_clock(&self, unix_ms: i64) -> Result<(), crate::VmError> {
398 self.clock_control.set_unix_ms(unix_ms)
399 }
400
401 pub(crate) fn advance_test_clock(&self, milliseconds: i64) -> Result<i64, crate::VmError> {
402 self.clock_control.advance_ms(milliseconds)
403 }
404
405 pub(crate) fn clear_test_clock(&self) {
406 self.clock_control.clear_override();
407 }
408
409 pub(crate) async fn wait_for_clock_advance(&self, duration: Duration) {
410 self.clock_control.wait_for_advance(duration).await;
411 }
412
413 pub(crate) fn mode(&self) -> &HarnessMode {
414 &self.mode
415 }
416
417 pub fn net_policy(&self) -> Option<&crate::harness_net::NetPolicy> {
418 self.net_policy.as_ref()
419 }
420
421 pub fn secret_provider(&self) -> Option<&Arc<dyn crate::secrets::SecretProvider>> {
422 self.secret_provider.as_ref()
423 }
424
425 pub(crate) fn mark_quarantined(&self) {
426 if let Ok(mut guard) = self.quarantined.lock() {
427 *guard = true;
428 }
429 }
430
431 pub fn is_quarantined(&self) -> bool {
432 self.quarantined.lock().map(|guard| *guard).unwrap_or(false)
433 }
434
435 pub(crate) fn fixtures(&self) -> &CapabilityFixtureState {
436 &self.fixtures
437 }
438
439 pub(crate) fn fixtures_arc(&self) -> Arc<CapabilityFixtureState> {
440 Arc::clone(&self.fixtures)
441 }
442}
443
444#[derive(Debug)]
445pub(crate) enum HarnessMode {
446 Real,
447 Null(NullHarnessState),
448 Mock(Arc<MockHarnessState>),
449}
450
451#[derive(Debug, Default)]
452pub(crate) struct NullHarnessState {
453 deny_events: Mutex<Vec<DenyEvent>>,
454}
455
456impl NullHarnessState {
457 pub(crate) fn record_deny(
458 &self,
459 sub_handle: HarnessKind,
460 method: &str,
461 args: &[crate::VmValue],
462 ) {
463 self.deny_events
464 .lock()
465 .expect("deny events poisoned")
466 .push(DenyEvent::new(
467 sub_handle,
468 method,
469 args.iter().map(crate::VmValue::display).collect(),
470 ));
471 }
472
473 pub(crate) fn deny_events(&self) -> Vec<DenyEvent> {
474 self.deny_events
475 .lock()
476 .expect("deny events poisoned")
477 .clone()
478 }
479}
480
481#[derive(Debug, Clone, PartialEq, Eq)]
482pub struct DenyEvent {
483 pub sub_handle: HarnessKind,
484 pub method: String,
485 pub args: Vec<String>,
486}
487
488impl DenyEvent {
489 fn new(sub_handle: HarnessKind, method: &str, args: Vec<String>) -> Self {
490 Self {
491 sub_handle,
492 method: method.to_string(),
493 args,
494 }
495 }
496}
497
498#[derive(Debug)]
499pub(crate) struct MockHarnessState {
500 calls: Mutex<Vec<HarnessCall>>,
501 clock: Arc<PausedClock>,
502 env: BTreeMap<String, String>,
503 fs_reads: BTreeMap<String, Vec<u8>>,
504 net_gets: BTreeMap<String, String>,
505 random_u64: Mutex<VecDeque<u64>>,
506 capability_responses:
507 Mutex<BTreeMap<(harn_builtin_meta::CapabilityId, String), VecDeque<crate::VmValue>>>,
508 stdin_lines: Mutex<VecDeque<String>>,
509 stdio: Mutex<String>,
510 stderr: Mutex<String>,
511}
512
513impl MockHarnessState {
514 pub(crate) fn record_call(
515 &self,
516 sub_handle: HarnessKind,
517 method: &str,
518 args: &[crate::VmValue],
519 ) {
520 self.calls
521 .lock()
522 .expect("calls poisoned")
523 .push(HarnessCall::new(
524 sub_handle,
525 method,
526 args.iter().map(crate::VmValue::display).collect(),
527 ));
528 }
529
530 pub(crate) fn calls(&self) -> Vec<HarnessCall> {
531 self.calls.lock().expect("calls poisoned").clone()
532 }
533
534 pub(crate) fn env_get(&self, key: &str) -> Option<&str> {
535 self.env.get(key).map(String::as_str)
536 }
537
538 pub(crate) fn fs_read(&self, path: &str) -> Option<&[u8]> {
539 self.fs_reads.get(path).map(Vec::as_slice)
540 }
541
542 pub(crate) fn net_get(&self, url: &str) -> Option<&str> {
543 self.net_gets.get(url).map(String::as_str)
544 }
545
546 pub(crate) fn next_random_u64(&self) -> Option<u64> {
547 let mut values = self.random_u64.lock().expect("random values poisoned");
548 values.pop_front()
549 }
550
551 pub(crate) fn capability_response(
552 &self,
553 capability: harn_builtin_meta::CapabilityId,
554 method: &str,
555 ) -> Option<crate::VmValue> {
556 self.capability_responses
557 .lock()
558 .expect("capability responses poisoned")
559 .get_mut(&(capability, method.to_string()))
560 .and_then(VecDeque::pop_front)
561 }
562
563 pub(crate) fn has_capability_response(
569 &self,
570 capability: harn_builtin_meta::CapabilityId,
571 method: &str,
572 ) -> bool {
573 self.capability_responses
574 .lock()
575 .expect("capability responses poisoned")
576 .get(&(capability, method.to_string()))
577 .is_some_and(|queued| !queued.is_empty())
578 }
579
580 pub(crate) fn advance_clock(&self, duration: std::time::Duration) {
581 self.clock.advance(duration);
582 }
583
584 pub(crate) fn push_stdio(&self, text: &str) {
585 self.stdio
586 .lock()
587 .expect("stdio buffer poisoned")
588 .push_str(text);
589 }
590
591 pub(crate) fn stdio(&self) -> String {
592 self.stdio.lock().expect("stdio buffer poisoned").clone()
593 }
594
595 pub(crate) fn push_stderr(&self, text: &str) {
596 self.stderr
597 .lock()
598 .expect("stderr buffer poisoned")
599 .push_str(text);
600 }
601
602 pub(crate) fn stderr(&self) -> String {
603 self.stderr.lock().expect("stderr buffer poisoned").clone()
604 }
605
606 pub(crate) fn pop_stdin_line(&self) -> Option<String> {
607 self.stdin_lines
608 .lock()
609 .expect("stdin queue poisoned")
610 .pop_front()
611 }
612}
613
614#[derive(Debug, Clone, PartialEq, Eq)]
615pub struct HarnessCall {
616 pub sub_handle: HarnessKind,
617 pub method: String,
618 pub args: Vec<String>,
619}
620
621impl HarnessCall {
622 fn new(sub_handle: HarnessKind, method: &str, args: Vec<String>) -> Self {
623 Self {
624 sub_handle,
625 method: method.to_string(),
626 args,
627 }
628 }
629}
630
631#[derive(Debug)]
632pub struct MockHarnessBuilder {
633 clock: Arc<PausedClock>,
634 env: BTreeMap<String, String>,
635 fs_reads: BTreeMap<String, Vec<u8>>,
636 net_gets: BTreeMap<String, String>,
637 random_u64: Vec<u64>,
638 capability_responses: BTreeMap<(harn_builtin_meta::CapabilityId, String), Vec<crate::VmValue>>,
639 stdin_lines: Vec<String>,
640}
641
642impl MockHarnessBuilder {
643 fn new() -> Self {
644 Self {
645 clock: paused_clock_at_unix_ms(0),
646 env: BTreeMap::new(),
647 fs_reads: BTreeMap::new(),
648 net_gets: BTreeMap::new(),
649 random_u64: Vec::new(),
650 capability_responses: BTreeMap::new(),
651 stdin_lines: Vec::new(),
652 }
653 }
654
655 pub fn clock_at_unix_ms(mut self, unix_ms: i64) -> Self {
656 self.clock = paused_clock_at_unix_ms(unix_ms);
657 self
658 }
659
660 pub fn clock_at(mut self, origin: OffsetDateTime) -> Self {
661 self.clock = PausedClock::new(origin);
662 self
663 }
664
665 pub fn env(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
666 self.env.insert(key.into(), value.into());
667 self
668 }
669
670 pub fn fs_read(mut self, path: impl Into<String>, data: impl Into<Vec<u8>>) -> Self {
671 self.fs_reads.insert(path.into(), data.into());
672 self
673 }
674
675 pub fn net_get(mut self, url: impl Into<String>, body: impl Into<String>) -> Self {
676 self.net_gets.insert(url.into(), body.into());
677 self
678 }
679
680 pub fn random_u64(mut self, value: u64) -> Self {
681 self.random_u64.push(value);
682 self
683 }
684
685 pub fn capability_response(
690 mut self,
691 capability: harn_builtin_meta::CapabilityId,
692 method: impl Into<String>,
693 value: crate::VmValue,
694 ) -> Self {
695 self.capability_responses
696 .entry((capability, method.into()))
697 .or_default()
698 .push(value);
699 self
700 }
701
702 pub fn stdin_line(mut self, line: impl Into<String>) -> Self {
708 self.stdin_lines.push(line.into());
709 self
710 }
711
712 pub fn build(self) -> Harness {
713 let clock = self.clock;
714 Harness::with_mode(
715 clock.clone() as Arc<dyn Clock>,
716 HarnessMode::Mock(Arc::new(MockHarnessState {
717 calls: Mutex::new(Vec::new()),
718 clock,
719 env: self.env,
720 fs_reads: self.fs_reads,
721 net_gets: self.net_gets,
722 random_u64: Mutex::new(self.random_u64.into()),
723 capability_responses: Mutex::new(
724 self.capability_responses
725 .into_iter()
726 .map(|(key, values)| (key, values.into()))
727 .collect(),
728 ),
729 stdin_lines: Mutex::new(self.stdin_lines.into()),
730 stdio: Mutex::new(String::new()),
731 stderr: Mutex::new(String::new()),
732 })),
733 )
734 }
735}
736
737#[derive(Debug, Clone)]
741pub struct Harness {
742 inner: Arc<HarnessInner>,
743}
744
745impl Harness {
746 pub fn real() -> Self {
752 Self::with_mode(Arc::new(RealClock::new()), HarnessMode::Real)
753 }
754
755 pub fn null() -> Self {
758 Self::with_mode(
759 paused_clock_at_unix_ms(0) as Arc<dyn Clock>,
760 HarnessMode::Null(NullHarnessState::default()),
761 )
762 }
763
764 pub fn mock() -> MockHarnessBuilder {
766 MockHarnessBuilder::new()
767 }
768
769 pub fn with_clock(clock: Arc<dyn Clock>) -> Self {
774 Self::with_mode(clock, HarnessMode::Real)
775 }
776
777 pub fn from_inner(inner: Arc<HarnessInner>) -> Self {
782 Self { inner }
783 }
784
785 fn with_mode(clock: Arc<dyn Clock>, mode: HarnessMode) -> Self {
786 let clock_control = Arc::new(HarnessClockRouter::new(clock));
787 let clock: Arc<dyn Clock> = clock_control.clone();
788 let inner = Arc::new(HarnessInner {
789 clock,
790 clock_control,
791 mode,
792 net_policy: None,
793 secret_provider: None,
794 quarantined: Mutex::new(false),
795 fixtures: Arc::new(CapabilityFixtureState::default()),
796 });
797 Self { inner }
798 }
799
800 pub fn with_net_policy(&self, policy: crate::harness_net::NetPolicy) -> Self {
814 let clock = Arc::clone(&self.inner.clock);
815 let clock_control = Arc::clone(&self.inner.clock_control);
816 let mode = self.clone_mode_for_child();
817 #[allow(clippy::arc_with_non_send_sync)]
819 let inner = Arc::new(HarnessInner {
820 clock,
821 clock_control,
822 mode,
823 net_policy: Some(policy),
824 secret_provider: self.inner.secret_provider.clone(),
825 quarantined: Mutex::new(self.is_quarantined()),
826 fixtures: Arc::clone(&self.inner.fixtures),
827 });
828 Self { inner }
829 }
830
831 pub fn secret_provider(&self) -> Option<&Arc<dyn crate::secrets::SecretProvider>> {
833 self.inner.secret_provider()
834 }
835
836 pub fn with_secret_provider(&self, provider: Arc<dyn crate::secrets::SecretProvider>) -> Self {
842 let clock = Arc::clone(&self.inner.clock);
843 let clock_control = Arc::clone(&self.inner.clock_control);
844 let mode = self.clone_mode_for_child();
845 #[allow(clippy::arc_with_non_send_sync)]
846 let inner = Arc::new(HarnessInner {
847 clock,
848 clock_control,
849 mode,
850 net_policy: self.inner.net_policy.clone(),
851 secret_provider: Some(provider),
852 quarantined: Mutex::new(self.is_quarantined()),
853 fixtures: Arc::clone(&self.inner.fixtures),
854 });
855 Self { inner }
856 }
857
858 fn clone_mode_for_child(&self) -> HarnessMode {
859 match &self.inner.mode {
860 HarnessMode::Real => HarnessMode::Real,
861 HarnessMode::Null(_) => HarnessMode::Null(NullHarnessState::default()),
862 HarnessMode::Mock(state) => HarnessMode::Mock(Arc::clone(state)),
863 }
864 }
865
866 pub fn is_quarantined(&self) -> bool {
869 self.inner.is_quarantined()
870 }
871
872 pub fn deny_events(&self) -> Vec<DenyEvent> {
873 match self.inner.mode() {
874 HarnessMode::Null(state) => state.deny_events(),
875 HarnessMode::Real | HarnessMode::Mock(_) => Vec::new(),
876 }
877 }
878
879 pub fn calls(&self) -> Vec<HarnessCall> {
880 match self.inner.mode() {
881 HarnessMode::Mock(state) => state.calls(),
882 HarnessMode::Real | HarnessMode::Null(_) => Vec::new(),
883 }
884 }
885
886 pub fn captured_stdio(&self) -> String {
887 match self.inner.mode() {
888 HarnessMode::Mock(state) => state.stdio(),
889 HarnessMode::Real | HarnessMode::Null(_) => String::new(),
890 }
891 }
892
893 pub fn captured_stderr(&self) -> String {
894 match self.inner.mode() {
895 HarnessMode::Mock(state) => state.stderr(),
896 HarnessMode::Real | HarnessMode::Null(_) => String::new(),
897 }
898 }
899
900 pub fn test() -> (Self, Arc<PausedClock>) {
912 Self::with_paused_clock(OffsetDateTime::UNIX_EPOCH)
913 }
914
915 pub fn with_paused_clock(origin: OffsetDateTime) -> (Self, Arc<PausedClock>) {
919 let paused = PausedClock::new(origin);
920 let as_dyn: Arc<dyn Clock> = paused.clone();
921 (Self::with_clock(as_dyn), paused)
922 }
923
924 pub fn stdio(&self) -> HarnessStdio {
926 HarnessStdio {
927 inner: Arc::clone(&self.inner),
928 }
929 }
930
931 pub fn term(&self) -> HarnessTerm {
933 HarnessTerm {
934 inner: Arc::clone(&self.inner),
935 }
936 }
937
938 pub fn clock(&self) -> HarnessClock {
940 HarnessClock {
941 inner: Arc::clone(&self.inner),
942 }
943 }
944
945 pub fn fs(&self) -> HarnessFs {
947 HarnessFs {
948 inner: Arc::clone(&self.inner),
949 }
950 }
951
952 pub fn env(&self) -> HarnessEnv {
954 HarnessEnv {
955 inner: Arc::clone(&self.inner),
956 }
957 }
958
959 pub fn random(&self) -> HarnessRandom {
961 HarnessRandom {
962 inner: Arc::clone(&self.inner),
963 }
964 }
965
966 pub fn net(&self) -> HarnessNet {
968 HarnessNet {
969 inner: Arc::clone(&self.inner),
970 }
971 }
972
973 pub fn process(&self) -> HarnessProcess {
975 HarnessProcess {
976 inner: Arc::clone(&self.inner),
977 }
978 }
979
980 pub fn channels(&self) -> HarnessChannels {
982 HarnessChannels {
983 inner: Arc::clone(&self.inner),
984 }
985 }
986
987 pub fn system(&self) -> HarnessSystem {
989 HarnessSystem {
990 inner: Arc::clone(&self.inner),
991 }
992 }
993
994 pub fn secrets(&self) -> HarnessSecrets {
996 HarnessSecrets {
997 inner: Arc::clone(&self.inner),
998 }
999 }
1000
1001 pub fn llm(&self) -> HarnessLlm {
1003 HarnessLlm {
1004 inner: Arc::clone(&self.inner),
1005 }
1006 }
1007
1008 pub fn tenant(&self) -> HarnessTenant {
1010 HarnessTenant {
1011 inner: Arc::clone(&self.inner),
1012 }
1013 }
1014
1015 pub fn auth(&self) -> HarnessAuth {
1017 HarnessAuth {
1018 inner: Arc::clone(&self.inner),
1019 }
1020 }
1021
1022 pub fn obs(&self) -> HarnessObs {
1024 HarnessObs {
1025 inner: Arc::clone(&self.inner),
1026 }
1027 }
1028
1029 pub fn testing(&self) -> HarnessTesting {
1031 HarnessTesting {
1032 inner: Arc::clone(&self.inner),
1033 }
1034 }
1035
1036 pub fn memory(&self) -> HarnessMemory {
1038 HarnessMemory {
1039 inner: Arc::clone(&self.inner),
1040 }
1041 }
1042
1043 pub fn sqlite(&self) -> HarnessSqlite {
1044 HarnessSqlite {
1045 inner: Arc::clone(&self.inner),
1046 }
1047 }
1048
1049 pub fn postgres(&self) -> HarnessPostgres {
1050 HarnessPostgres {
1051 inner: Arc::clone(&self.inner),
1052 }
1053 }
1054
1055 pub fn agent(&self) -> HarnessAgent {
1056 HarnessAgent {
1057 inner: Arc::clone(&self.inner),
1058 }
1059 }
1060
1061 pub fn into_vm_value(self) -> crate::value::VmValue {
1063 crate::value::VmValue::harness(VmHarness {
1064 inner: self.inner,
1065 kind: HarnessKind::Root,
1066 })
1067 }
1068}
1069
1070fn paused_clock_at_unix_ms(unix_ms: i64) -> Arc<PausedClock> {
1071 let nanos = (unix_ms as i128).saturating_mul(1_000_000);
1072 let origin =
1073 OffsetDateTime::from_unix_timestamp_nanos(nanos).unwrap_or(OffsetDateTime::UNIX_EPOCH);
1074 PausedClock::new(origin)
1075}
1076
1077pub(crate) fn vm_string(value: impl Into<String>) -> crate::VmValue {
1078 crate::VmValue::String(arcstr::ArcStr::from(value.into()))
1079}
1080
1081impl Default for Harness {
1082 fn default() -> Self {
1083 Self::real()
1084 }
1085}
1086
1087#[derive(Debug, Clone)]
1090pub struct HarnessStdio {
1091 inner: Arc<HarnessInner>,
1092}
1093
1094#[derive(Debug, Clone)]
1096pub struct HarnessTerm {
1097 inner: Arc<HarnessInner>,
1098}
1099
1100#[derive(Debug, Clone)]
1102pub struct HarnessClock {
1103 inner: Arc<HarnessInner>,
1104}
1105
1106impl HarnessClock {
1107 pub fn clock(&self) -> &Arc<dyn Clock> {
1108 self.inner.clock()
1109 }
1110}
1111
1112include!("harness/fixtures.rs");
1113include!("harness/value.rs");
1114include!("harness/tests.rs");
1115#[derive(Debug, Clone)]
1118pub struct HarnessFs {
1119 inner: Arc<HarnessInner>,
1120}
1121
1122#[derive(Debug, Clone)]
1124pub struct HarnessEnv {
1125 inner: Arc<HarnessInner>,
1126}
1127
1128#[derive(Debug, Clone)]
1130pub struct HarnessRandom {
1131 inner: Arc<HarnessInner>,
1132}
1133
1134#[derive(Debug, Clone)]
1136pub struct HarnessNet {
1137 inner: Arc<HarnessInner>,
1138}
1139
1140#[derive(Debug, Clone)]
1142pub struct HarnessProcess {
1143 inner: Arc<HarnessInner>,
1144}
1145
1146#[derive(Debug, Clone)]
1148pub struct HarnessChannels {
1149 inner: Arc<HarnessInner>,
1150}
1151
1152#[derive(Debug, Clone)]
1158pub struct HarnessSystem {
1159 inner: Arc<HarnessInner>,
1160}
1161
1162#[derive(Debug, Clone)]
1164pub struct HarnessSecrets {
1165 inner: Arc<HarnessInner>,
1166}
1167
1168#[derive(Debug, Clone)]
1170pub struct HarnessLlm {
1171 inner: Arc<HarnessInner>,
1172}
1173
1174#[derive(Debug, Clone)]
1180pub struct HarnessTenant {
1181 inner: Arc<HarnessInner>,
1182}
1183
1184#[derive(Debug, Clone)]
1192pub struct HarnessAuth {
1193 inner: Arc<HarnessInner>,
1194}
1195
1196#[derive(Debug, Clone)]
1206pub struct HarnessObs {
1207 inner: Arc<HarnessInner>,
1208}
1209
1210#[derive(Debug, Clone)]
1214pub struct HarnessTesting {
1215 inner: Arc<HarnessInner>,
1216}
1217
1218#[derive(Debug, Clone)]
1222pub struct HarnessMemory {
1223 inner: Arc<HarnessInner>,
1224}
1225
1226#[derive(Debug, Clone)]
1227pub struct HarnessSqlite {
1228 inner: Arc<HarnessInner>,
1229}
1230
1231#[derive(Debug, Clone)]
1232pub struct HarnessPostgres {
1233 inner: Arc<HarnessInner>,
1234}
1235
1236#[derive(Debug, Clone)]
1237pub struct HarnessAgent {
1238 inner: Arc<HarnessInner>,
1239}
1240
1241macro_rules! sub_handle_inner {
1242 ($($ty:ty),* $(,)?) => {
1243 $(
1244 impl $ty {
1245 #[allow(dead_code)]
1246 pub(crate) fn inner(&self) -> &Arc<HarnessInner> {
1247 &self.inner
1248 }
1249 }
1250 )*
1251 };
1252}
1253sub_handle_inner!(
1254 HarnessStdio,
1255 HarnessTerm,
1256 HarnessFs,
1257 HarnessEnv,
1258 HarnessRandom,
1259 HarnessNet,
1260 HarnessProcess,
1261 HarnessChannels,
1262 HarnessSystem,
1263 HarnessSecrets,
1264 HarnessLlm,
1265 HarnessTenant,
1266 HarnessAuth,
1267 HarnessObs,
1268 HarnessMemory,
1269 HarnessSqlite,
1270 HarnessPostgres,
1271 HarnessAgent,
1272 HarnessTesting,
1273);
1274
1275impl HarnessClock {
1276 #[allow(dead_code)]
1277 pub(crate) fn inner(&self) -> &Arc<HarnessInner> {
1278 &self.inner
1279 }
1280}