malvin 0.2.4

Non-interactive research and coding agent
use super::entrypoint::try_tokio_runtime;
use super::{Cli, Commands};

fn smoke_has_source_files_empty_dir() {
    let tmp = tempfile::tempdir().unwrap();
    assert!(!crate::source_detect::has_source_files(tmp.path()));
}

fn smoke_has_source_files_detects_rs() {
    let tmp = tempfile::tempdir().unwrap();
    std::fs::write(tmp.path().join("x.rs"), "").unwrap();
    assert!(crate::source_detect::has_source_files(tmp.path()));
}

fn smoke_merge_acp_and_timing_results() {
    use crate::acp_post_run::merge_acp_and_timing_results;
    assert_eq!(merge_acp_and_timing_results(Ok(()), Ok(())), Ok(()));
    assert_eq!(
        merge_acp_and_timing_results(Err("acp".into()), Err(std::io::Error::other("io"))),
        Err("acp".into())
    );
}

fn smoke_prefer_primary_over_secondary() {
    use crate::acp_post_run::prefer_primary_over_secondary;
    assert_eq!(prefer_primary_over_secondary(Ok(()), Ok(()), "x"), Ok(()));
    assert_eq!(
        prefer_primary_over_secondary(Ok(()), Err("b".into()), "x"),
        Err("b".into())
    );
}

fn smoke_merge_acp_with_workspace_session_restore() {
    let work = tempfile::tempdir().unwrap();
    let backups = crate::test_utils::empty_session_dotfile_backups(work.path());
    assert!(
        crate::acp_post_run::merge_acp_with_workspace_session_restore(
            Ok(()),
            work.path(),
            &backups
        )
        .is_ok()
    );
}

fn smoke_merge_acp_with_workspace_session_restore_and_check_abort_no_result_file() {
    let work = tempfile::tempdir().unwrap();
    let missing = work.path().join("no_such_result.md");
    let backups = crate::test_utils::empty_session_dotfile_backups(work.path());
    assert!(
        crate::acp_post_run::merge_acp_with_workspace_session_restore_and_check_abort(
            Ok(()),
            work.path(),
            &backups,
            &missing,
        )
        .is_ok()
    );
}

fn smoke_agent_io_options_maps_flags() {
    use super::{AgentStdoutTeeFlags, WorkflowCliOptions, agent_io_options};
    let shared = super::SharedOpts {
        model: crate::model_id::parse_model_id("cursor:m").expect("model"),
        no_force: false,
        no_tenacious: false,
        gates: false,

        quiet: false,
        verbose: false,
        max_acp_retries: crate::config::DEFAULT_MAX_ACP_RETRIES,
        doc: false,
        name: None,
        git: false,
        creative: false,
    };
    let io = agent_io_options(
        &shared,
        WorkflowCliOptions { force: true },
        AgentStdoutTeeFlags {
            emit_stdout_markdown: true,
            raw_output: true,
            show_thoughts_on_stdout: false,
        },
    );
    assert!(io.force);
    assert!(!io.no_tee);
    assert!(io.raw_output);
    assert!(!io.show_thoughts_on_stdout);
    assert!(io.emit_stdout_markdown);
    assert!(!io.log_full_outgoing_prompts);
}

fn init_is_not_a_subcommand_and_parses_as_bare_request() {
    use clap::Parser;
    let cli = Cli::try_parse_from(["malvin", "init"]).expect("parse");
    assert!(cli.command.is_none());
    assert_eq!(cli.request.as_deref(), Some("init"));
}

fn smoke_cli_parse_models_subcommand() {
    use clap::Parser;
    let cli = Cli::try_parse_from(["malvin", "models"]).unwrap();
    assert!(matches!(cli.command, Some(Commands::Models(_))));
}

fn smoke_try_tokio_runtime_builds_multi_thread() {
    let _rt = try_tokio_runtime().expect("tokio runtime");
}

fn smoke_tidy_effective_max_loops() {
    assert_eq!(super::tidy_flow::effective_tidy_max_loops(0), 1);
    assert_eq!(super::tidy_flow::effective_tidy_max_loops(3), 3);
}

fn smoke_emit_command_line_writes_log() {
    use std::path::Path;
    let tmp = tempfile::tempdir().expect("tempdir");
    let run_dir = tmp.path().join("run");
    std::fs::create_dir_all(&run_dir).expect("mkdir");
    super::run_emit::emit_command_line(Path::new(&run_dir), false).expect("emit");
    assert!(run_dir.join("command.log").is_file());
}

fn smoke_format_logs_dir_under_run_dir() {
    let tmp = tempfile::tempdir().expect("tempdir");
    let run_dir = tmp.path().join("run");
    std::fs::create_dir_all(&run_dir).expect("mkdir");
    let logs = crate::format_logs_dir(&run_dir).expect("logs dir");
    assert!(logs.contains("run"));
}

fn smoke_run_emit_echo_primary_noop_when_not_plain() {
    let tmp = tempfile::tempdir().expect("tempdir");
    let plan = tmp.path().join("plan.md");
    std::fs::write(&plan, "plan").expect("write plan");
    super::run_emit::echo_primary_to_stdout(&plan, false).expect("echo");
}

fn smoke_print_command_error_writes_run_log() {
    let tmp = tempfile::tempdir().expect("tempdir");
    let run_dir = tmp.path().join("run");
    std::fs::create_dir_all(&run_dir).expect("mkdir");
    super::error_run_log::set_command_error_run_dir(Some(run_dir.clone()));
    super::entrypoint::print_command_error("gate failed");
    let log = run_dir.join("malvin_error.log");
    assert!(log.is_file());
    let body = std::fs::read_to_string(&log).expect("read");
    assert!(body.contains("gate failed"));
    super::error_run_log::clear_command_error_run_dir();
}

fn smoke_prepare_do_prompt_store_loads_defaults() {
    assert!(crate::do_flow::prepare_do_prompt_store().is_ok());
}

fn smoke_prepare_router_prompt_store_loads_defaults() {
    assert!(crate::router_flow::prepare_router_prompt_store().is_ok());
}

#[test]
fn kiss_bundled_cli_cli_smoke_cov() {
    smoke_has_source_files_empty_dir();
    smoke_has_source_files_detects_rs();
    smoke_merge_acp_and_timing_results();
    smoke_prefer_primary_over_secondary();
    smoke_merge_acp_with_workspace_session_restore();
    smoke_merge_acp_with_workspace_session_restore_and_check_abort_no_result_file();
    smoke_agent_io_options_maps_flags();
    init_is_not_a_subcommand_and_parses_as_bare_request();
    smoke_cli_parse_models_subcommand();
    smoke_try_tokio_runtime_builds_multi_thread();
    smoke_tidy_effective_max_loops();
    smoke_emit_command_line_writes_log();
    smoke_format_logs_dir_under_run_dir();
    smoke_run_emit_echo_primary_noop_when_not_plain();
    smoke_print_command_error_writes_run_log();
    smoke_prepare_do_prompt_store_loads_defaults();
    smoke_prepare_router_prompt_store_loads_defaults();
}