Luigit
repositories / smith

smith

There are many coding harnesses - but this one is fast

owned by admin

smith-cli/tests/e2e_sessions.rs

Raw
//! Read-only inspection through the built binary (`SMH-SPEC-SPEC0001`, Session inspection).
use assert_cmd::Command;
use serde_json::Value;
use std::path::Path;

fn smith(home: &Path, args: &[&str]) -> std::process::Output {
    Command::cargo_bin("smith")
        .unwrap()
        .env("XDG_DATA_HOME", home)
        .env_remove("SMITH_PROVIDER")
        .env_remove("SMITH_MODEL")
        .env_remove("SMITH_BASE_URL")
        .env_remove("OPENAI_API_KEY")
        .env_remove("ANTHROPIC_API_KEY")
        .env_remove("GEMINI_API_KEY")
        .args(args)
        .timeout(std::time::Duration::from_secs(30))
        .output()
        .unwrap()
}

#[test]
fn session_list_limit_reports_capacity_not_corruption() {
    // Create real sessions through eval; inspection must reject capacity without claiming damage.
    let home = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
        .join("../target/style-exec")
        .join(format!("list-limit-e2e-{}", std::process::id()));
    let deadline = std::time::Instant::now() + std::time::Duration::from_secs(120);
    for _ in 0..1025 {
        assert!(
            std::time::Instant::now() < deadline,
            "session fixture deadline"
        );
        let created = smith(&home, &["eval", "--mock", "fixture"]);
        assert!(
            created.status.success(),
            "{}",
            String::from_utf8_lossy(&created.stderr)
        );
    }
    let listed = smith(&home, &["sessions", "list", "--json"]);
    assert!(!listed.status.success());
    let diagnostic = String::from_utf8(listed.stderr).unwrap();
    assert!(diagnostic.contains("SESSION_LIST_LIMIT"));
    assert!(diagnostic.contains("inspect sessions individually"));
    assert!(!diagnostic.contains("SESSION_CORRUPT"));
    let records = String::from_utf8(listed.stdout).unwrap();
    assert_eq!(records.lines().count(), 1024);
    assert!(
        records
            .lines()
            .all(|line| serde_json::from_str::<Value>(line).is_ok())
    );
    std::fs::remove_dir_all(home).unwrap();
}

#[test]
fn sessions_list_get_grep_jsonl_and_no_match() {
    let home = std::env::temp_dir().join(format!("smith-sessions-e2e-{}", std::process::id()));
    let _ = std::fs::remove_dir_all(&home);
    assert!(
        smith(&home, &["eval", "--mock", "needle \u{1b}[31m in haystack"])
            .status
            .success()
    );
    assert!(
        smith(&home, &["eval", "--mock", "other haystack"])
            .status
            .success()
    );
    let listed = smith(&home, &["sessions", "list", "--json"]);
    assert!(
        listed.status.success(),
        "{}",
        String::from_utf8_lossy(&listed.stderr)
    );
    let listed = String::from_utf8(listed.stdout).unwrap();
    let lines: Vec<Value> = listed
        .lines()
        .map(|line| serde_json::from_str(line).unwrap())
        .collect();
    assert_eq!(lines.len(), 2);
    let id = lines[0]["session"].as_str().unwrap();
    let human_list = smith(&home, &["sessions", "list"]);
    assert!(human_list.status.success());
    let (mut json_bytes, mut human_bytes) = (String::new(), String::new());
    for line in &lines {
        let (session, bytes) = (line["session"].as_str().unwrap(), &line["bytes"]);
        json_bytes += &format!("{{\"session\":\"{session}\",\"bytes\":{bytes}}}\n");
        human_bytes += &format!("{session} {bytes} bytes\n");
    }
    assert_eq!(listed, json_bytes, "JSON list lines keep their exact bytes");
    assert_eq!(
        String::from_utf8(human_list.stdout).unwrap(),
        human_bytes,
        "human list lines keep their exact bytes"
    );
    let retrieved = smith(&home, &["sessions", "get", id, "--json"]);
    assert!(
        retrieved.status.success(),
        "{}",
        String::from_utf8_lossy(&retrieved.stderr)
    );
    let records: Vec<Value> = String::from_utf8(retrieved.stdout)
        .unwrap()
        .lines()
        .map(|line| serde_json::from_str(line).unwrap())
        .collect();
    assert_eq!(records.len(), 2);
    assert!(
        records
            .iter()
            .all(|record| record["content"]["kind"] == "Message")
    );
    assert!(
        records
            .iter()
            .all(|record| record["content"]["data"]["blocks"][0]["type"] == "Text")
    );
    assert!(
        records
            .iter()
            .all(|record| record["content"]["data"]["blocks"][0]["data"].is_string())
    );
    let human_get = smith(&home, &["sessions", "get", id]);
    assert!(human_get.status.success());
    assert!(!human_get.stdout.contains(&0x1b));
    let matches = smith(&home, &["sessions", "grep", "needle", "--json"]);
    assert!(
        matches.status.success(),
        "{}",
        String::from_utf8_lossy(&matches.stderr)
    );
    let found: Vec<Value> = String::from_utf8(matches.stdout)
        .unwrap()
        .lines()
        .map(|line| serde_json::from_str(line).unwrap())
        .collect();
    assert_eq!(found.len(), 1);
    assert!(found[0]["entry"].is_string());
    assert_eq!(
        found[0]["content"]["data"]["blocks"][0]["data"],
        "needle \u{1b}[31m in haystack"
    );
    let human_grep = smith(&home, &["sessions", "grep", "needle"]);
    assert!(human_grep.status.success());
    assert!(
        String::from_utf8(human_grep.stdout)
            .unwrap()
            .contains("needle")
    );
    let none = smith(&home, &["sessions", "grep", "not-in-any-session"]);
    assert_eq!(none.status.code(), Some(1));
    assert!(none.stdout.is_empty());
    assert!(none.stderr.is_empty());
    let _ = std::fs::remove_dir_all(home);
}

#[test]
fn corrupt_session_is_reported_without_repairing_source() {
    // SMH-SPEC-SPEC0001: Session inspection never repairs or hides corruption.
    let home = std::env::temp_dir().join(format!("smith-corrupt-e2e-{}", std::process::id()));
    let _ = std::fs::remove_dir_all(&home);
    assert!(smith(&home, &["eval", "--mock", "before"]).status.success());
    let dir = home.join("smith/sessions/run");
    let path = std::fs::read_dir(&dir)
        .unwrap()
        .map(|entry| entry.unwrap().path())
        .find(|path| path.extension().is_some_and(|ext| ext == "smh"))
        .unwrap();
    let mut bytes = std::fs::read(&path).unwrap();
    let last = bytes.len() - 1;
    bytes[last] ^= 0xff;
    std::fs::write(&path, &bytes).unwrap();
    let id = path.file_stem().unwrap().to_str().unwrap();
    let output = smith(&home, &["sessions", "get", id]);
    assert_eq!(output.status.code(), Some(2));
    assert!(String::from_utf8_lossy(&output.stderr).contains("SESSION_CORRUPT"));
    assert_eq!(std::fs::read(path).unwrap(), bytes);
    let _ = std::fs::remove_dir_all(home);
}