Skip to main content

xacli_testing/testing/
ctx.rs

1//! Mock context for testing CLI applications
2//!
3//! `MockContext` implements `xacli_core::Context` trait and provides:
4//! - Queued input events that are returned sequentially from `read_event()`
5//! - Captured stdout/stderr output for assertions
6//! - Configurable TTY mode
7
8use std::{
9    cell::RefCell,
10    collections::VecDeque,
11    io::{self, Write},
12    rc::Rc,
13};
14
15use xacli_core::{Context, ContextInfo, InputEvent, Output, OutputCommand, ReadEvent};
16
17/// A mock context for testing CLI applications
18///
19/// This context captures all output and allows injecting input events
20/// for testing interactive CLI components.
21pub struct MockContext {
22    /// Whether to simulate a TTY environment
23    #[allow(dead_code)]
24    pub is_tty: bool,
25    /// Context metadata
26    pub info: ContextInfo,
27    /// Queue of input events to return from `read_event()`
28    pub input_events: VecDeque<InputEvent>,
29    /// Captured stdout output
30    pub stdout_buffer: Rc<RefCell<Vec<u8>>>,
31    /// Captured stderr output
32    pub stderr_buffer: Rc<RefCell<Vec<u8>>>,
33    /// Queued output commands
34    #[allow(dead_code)]
35    pub queued_commands: Vec<OutputCommand>,
36}
37
38impl MockContext {
39    /// Create a new MockContext with the given parameters
40    pub fn new(info: ContextInfo, input_events: VecDeque<InputEvent>, is_tty: bool) -> Self {
41        Self {
42            is_tty,
43            info,
44            input_events,
45            stdout_buffer: Rc::new(RefCell::new(Vec::new())),
46            stderr_buffer: Rc::new(RefCell::new(Vec::new())),
47            queued_commands: Vec::new(),
48        }
49    }
50
51    /// Get the captured stdout as a plain string
52    pub fn stdout_plain(&self) -> String {
53        String::from_utf8_lossy(&self.stdout_buffer.borrow()).to_string()
54    }
55
56    /// Get the captured stderr as a plain string
57    pub fn stderr_plain(&self) -> String {
58        String::from_utf8_lossy(&self.stderr_buffer.borrow()).to_string()
59    }
60}
61
62impl Context for MockContext {
63    fn info(&self) -> &ContextInfo {
64        &self.info
65    }
66
67    fn stdout(&mut self) -> Output {
68        Output::new(Box::new(SharedWriter(self.stdout_buffer.clone())))
69    }
70
71    fn stderr(&mut self) -> Output {
72        Output::new(Box::new(SharedWriter(self.stderr_buffer.clone())))
73    }
74}
75
76struct SharedWriter(Rc<RefCell<Vec<u8>>>);
77
78impl Write for SharedWriter {
79    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
80        self.0.borrow_mut().extend_from_slice(buf);
81        Ok(buf.len())
82    }
83
84    fn flush(&mut self) -> io::Result<()> {
85        Ok(())
86    }
87}
88
89impl ReadEvent for MockContext {
90    fn read_event(&mut self) -> xacli_core::Result<InputEvent> {
91        self.input_events.pop_front().ok_or_else(|| {
92            xacli_core::Error::IoError(io::Error::new(
93                io::ErrorKind::UnexpectedEof,
94                "No more input events in queue",
95            ))
96        })
97    }
98}