mecha10-cli 0.6.3

Mecha10 CLI tool
Documentation
//! Tests for the per-environment `environments.<env>.logging.disk_logging_enabled`
//! project config toggle (LAB-2041)
//!
//! Edge/robot deployments already ship logs to a remote server, so writing
//! them to local disk too is redundant, can fill the device's disk, and
//! wears out SD/USB storage. `ProcessService::spawn_with_env` is the
//! chokepoint that redirects a spawned node's stdout/stderr to a file under
//! `logs/` — these tests verify that redirection is skipped entirely (no
//! directory created, no file opened) when the *current* environment's
//! config disables it, and that the existing behavior (disk logging on) is
//! unchanged by default and unaffected by an override on a different
//! environment.

use mecha10_cli::services::process::ProcessService;
use serial_test::serial;
use std::collections::HashMap;
use std::fs;
use std::path::Path;
use tempfile::TempDir;

/// Write a minimal mecha10.json whose `dev` environment optionally has an explicit
/// `logging.disk_logging_enabled` value. `None` omits the `logging` section entirely, to
/// exercise the default. Tests in this file don't set `MECHA10_ENV`, so `dev` is the
/// current environment (see `EnvironmentsConfig::current_env_name`'s default).
fn write_project_config(dir: &Path, disk_logging_enabled: Option<bool>) {
    let logging_section = match disk_logging_enabled {
        Some(enabled) => format!(r#", "logging": {{ "disk_logging_enabled": {} }}"#, enabled),
        None => String::new(),
    };

    let json = format!(
        r#"{{
            "name": "test-project",
            "version": "0.1.0",
            "robot": {{ "id": "test-robot" }},
            "environments": {{
                "dev": {{ "control_plane": "https://dev.example.com"{} }}
            }}
        }}"#,
        logging_section
    );

    fs::write(dir.join("mecha10.json"), json).expect("write mecha10.json");
}

/// Spawn a short-lived process via `spawn_with_env` from within `dir` and wait
/// for it to actually run (so any log file it would create has been opened).
fn spawn_and_settle(service: &mut ProcessService, name: &str) {
    let mut env = HashMap::new();
    env.insert("TEST_VAR".to_string(), "1".to_string());

    service
        .spawn_with_env(name, "sleep", &["1"], env)
        .expect("spawn_with_env should succeed");

    std::thread::sleep(std::time::Duration::from_millis(150));
}

#[test]
#[serial]
fn disk_logging_enabled_by_default_when_no_logging_section() {
    let temp = TempDir::new().unwrap();
    write_project_config(temp.path(), None);

    let original_dir = std::env::current_dir().unwrap();
    std::env::set_current_dir(temp.path()).unwrap();

    let mut service = ProcessService::new();
    spawn_and_settle(&mut service, "default-node");

    let log_path = temp.path().join("logs").join("default-node.log");
    assert!(
        log_path.exists(),
        "expected disk log file to be created by default (existing behavior unchanged)"
    );

    service.cleanup();
    std::env::set_current_dir(original_dir).unwrap();
}

#[test]
#[serial]
fn disk_logging_explicitly_enabled_writes_log_file() {
    let temp = TempDir::new().unwrap();
    write_project_config(temp.path(), Some(true));

    let original_dir = std::env::current_dir().unwrap();
    std::env::set_current_dir(temp.path()).unwrap();

    let mut service = ProcessService::new();
    spawn_and_settle(&mut service, "enabled-node");

    let log_path = temp.path().join("logs").join("enabled-node.log");
    assert!(log_path.exists(), "expected disk log file when explicitly enabled");

    service.cleanup();
    std::env::set_current_dir(original_dir).unwrap();
}

/// LAB-2041's core requirement: the override is resolved off the *current* environment,
/// not applied globally. A `logging.disk_logging_enabled: false` override on `dev` must
/// have no effect when `MECHA10_ENV=production` selects `prod`, which has no override of
/// its own (so it keeps the default of enabled).
#[test]
#[serial]
fn disk_logging_resolves_off_current_environment_not_other_environments() {
    let temp = TempDir::new().unwrap();
    let json = r#"{
        "name": "test-project",
        "version": "0.1.0",
        "robot": { "id": "test-robot" },
        "environments": {
            "dev": { "control_plane": "https://dev.example.com", "logging": { "disk_logging_enabled": false } },
            "prod": { "control_plane": "https://prod.example.com" }
        }
    }"#;
    fs::write(temp.path().join("mecha10.json"), json).expect("write mecha10.json");

    let original_dir = std::env::current_dir().unwrap();
    std::env::set_current_dir(temp.path()).unwrap();
    std::env::set_var("MECHA10_ENV", "production");

    let mut service = ProcessService::new();
    spawn_and_settle(&mut service, "prod-node");

    let log_path = temp.path().join("logs").join("prod-node.log");
    assert!(
        log_path.exists(),
        "prod has no logging override, so it should log to disk regardless of dev's override"
    );

    service.cleanup();
    std::env::remove_var("MECHA10_ENV");
    std::env::set_current_dir(original_dir).unwrap();
}

#[test]
#[serial]
fn disk_logging_disabled_creates_no_log_file_or_directory() {
    let temp = TempDir::new().unwrap();
    write_project_config(temp.path(), Some(false));

    let original_dir = std::env::current_dir().unwrap();
    std::env::set_current_dir(temp.path()).unwrap();

    let mut service = ProcessService::new();
    spawn_and_settle(&mut service, "quiet-node");

    let logs_dir = temp.path().join("logs");
    assert!(
        !logs_dir.exists(),
        "logs/ directory should never be created when disk logging is disabled"
    );

    service.cleanup();
    std::env::set_current_dir(original_dir).unwrap();
}