osp-cli 1.5.1

CLI and REPL for querying and managing OSP infrastructure data
Documentation
use miette::{Result, miette};

use crate::app::AppSession;
use crate::app::CliCommandResult;
use crate::cli::HistoryArgs;
use crate::repl::history as repl_history;

pub(crate) fn run_history_command(_args: HistoryArgs) -> Result<CliCommandResult> {
    Err(miette!(
        "history commands are REPL-only (start the REPL with `osp`)"
    ))
}

pub(crate) fn run_history_repl_command(
    session: &mut AppSession,
    args: HistoryArgs,
    history: &crate::repl::SharedHistory,
) -> Result<CliCommandResult> {
    let output = repl_history::run_history_repl_command(session, args, history)?;
    Ok(CliCommandResult {
        exit_code: 0,
        messages: crate::ui::messages::MessageBuffer::default(),
        output: Some(output),
        stderr_text: None,
        failure_report: None,
    })
}

#[cfg(test)]
mod tests {
    use super::{run_history_command, run_history_repl_command};
    use crate::app::AppSession;
    use crate::app::{CliCommandResult, ReplCommandOutput};
    use crate::cli::{HistoryArgs, HistoryCommands};
    use crate::core::row::Row;
    use crate::repl::HistoryConfig;

    fn extract_output_rows(result: CliCommandResult) -> Option<Vec<Row>> {
        let output = match result.output? {
            ReplCommandOutput::Output(output) => output.output,
            ReplCommandOutput::Json(_) | ReplCommandOutput::Text(_) => return None,
        };
        output.into_rows()
    }

    #[test]
    fn history_command_is_repl_only_unit() {
        let err = run_history_command(HistoryArgs {
            command: HistoryCommands::List,
        })
        .expect_err("history command should be rejected outside the repl");

        assert!(err.to_string().contains("history commands are REPL-only"));
    }

    #[test]
    fn history_output_helpers_wrap_rows_and_ignore_text_results_unit() {
        let temp_dir = make_temp_dir("osp-cli-history-wrapper");
        let history = crate::repl::SharedHistory::new(
            HistoryConfig {
                path: Some(temp_dir.join("history.jsonl")),
                max_entries: 32,
                enabled: true,
                dedupe: true,
                profile_scoped: false,
                exclude_patterns: Vec::new(),
                profile: None,
                terminal: None,
                shell_context: crate::repl::HistoryShellContext::default(),
            }
            .normalized(),
        );
        history
            .save_command_line("config show")
            .expect("history seed should save");

        let mut session = AppSession::with_cache_limit(8);
        let result = run_history_repl_command(
            &mut session,
            HistoryArgs {
                command: HistoryCommands::List,
            },
            &history,
        )
        .expect("history list should succeed");

        assert_eq!(result.exit_code, 0);
        let rows = extract_output_rows(result).expect("history list should emit rows");
        assert_eq!(rows.len(), 1);
        assert_eq!(
            rows[0]["command"],
            serde_json::Value::String("config show".into())
        );
        assert!(extract_output_rows(CliCommandResult::text("hello")).is_none());
    }

    fn make_temp_dir(prefix: &str) -> crate::tests::TestTempDir {
        crate::tests::make_temp_dir(prefix)
    }
}