ruvector-cli 2.0.4

CLI and MCP server for Ruvector
//! Integration tests for Ruvector CLI

use assert_cmd::Command;
use predicates::prelude::*;
use std::fs;
use tempfile::tempdir;

#[test]
fn test_cli_version() {
    let mut cmd = Command::cargo_bin("ruvector").unwrap();
    cmd.arg("--version");
    cmd.assert()
        .success()
        .stdout(predicate::str::contains("ruvector"));
}

#[test]
fn test_cli_help() {
    let mut cmd = Command::cargo_bin("ruvector").unwrap();
    cmd.arg("--help");
    cmd.assert().success().stdout(predicate::str::contains(
        "High-performance Rust vector database",
    ));
}

#[test]
fn test_create_database() {
    let dir = tempdir().unwrap();
    let db_path = dir.path().join("test.db");

    let mut cmd = Command::cargo_bin("ruvector").unwrap();
    cmd.arg("create")
        .arg("--path")
        .arg(db_path.to_str().unwrap())
        .arg("--dimensions")
        .arg("128");

    cmd.assert()
        .success()
        .stdout(predicate::str::contains("Database created successfully"));

    // Verify database file exists
    assert!(db_path.exists());
}

#[test]
fn test_info_command() {
    let dir = tempdir().unwrap();
    let db_path = dir.path().join("test.db");

    // Create database first
    let mut cmd = Command::cargo_bin("ruvector").unwrap();
    cmd.arg("create")
        .arg("--path")
        .arg(db_path.to_str().unwrap())
        .arg("--dimensions")
        .arg("64");
    cmd.assert().success();

    // Check info
    let mut cmd = Command::cargo_bin("ruvector").unwrap();
    cmd.arg("info").arg("--db").arg(db_path.to_str().unwrap());

    cmd.assert()
        .success()
        .stdout(predicate::str::contains("Database Statistics"))
        .stdout(predicate::str::contains("Dimensions: 64"));
}

#[test]
fn test_insert_from_json() {
    let dir = tempdir().unwrap();
    let db_path = dir.path().join("test.db");
    let json_path = dir.path().join("vectors.json");

    // Create test JSON file
    let test_data = r#"[
        {
            "id": "v1",
            "vector": [1.0, 2.0, 3.0],
            "metadata": {"label": "test1"}
        },
        {
            "id": "v2",
            "vector": [4.0, 5.0, 6.0],
            "metadata": {"label": "test2"}
        }
    ]"#;
    fs::write(&json_path, test_data).unwrap();

    // Create database
    let mut cmd = Command::cargo_bin("ruvector").unwrap();
    cmd.arg("create")
        .arg("--path")
        .arg(db_path.to_str().unwrap())
        .arg("--dimensions")
        .arg("3");
    cmd.assert().success();

    // Insert vectors
    let mut cmd = Command::cargo_bin("ruvector").unwrap();
    cmd.arg("insert")
        .arg("--db")
        .arg(db_path.to_str().unwrap())
        .arg("--input")
        .arg(json_path.to_str().unwrap())
        .arg("--format")
        .arg("json")
        .arg("--no-progress");

    cmd.assert()
        .success()
        .stdout(predicate::str::contains("Inserted 2 vectors"));
}

#[test]
fn test_search_command() {
    let dir = tempdir().unwrap();
    let db_path = dir.path().join("test.db");
    let json_path = dir.path().join("vectors.json");

    // Create test data
    let test_data = r#"[
        {"id": "v1", "vector": [1.0, 0.0, 0.0]},
        {"id": "v2", "vector": [0.0, 1.0, 0.0]},
        {"id": "v3", "vector": [0.0, 0.0, 1.0]}
    ]"#;
    fs::write(&json_path, test_data).unwrap();

    // Create and populate database
    let mut cmd = Command::cargo_bin("ruvector").unwrap();
    cmd.arg("create")
        .arg("--path")
        .arg(db_path.to_str().unwrap())
        .arg("--dimensions")
        .arg("3");
    cmd.assert().success();

    let mut cmd = Command::cargo_bin("ruvector").unwrap();
    cmd.arg("insert")
        .arg("--db")
        .arg(db_path.to_str().unwrap())
        .arg("--input")
        .arg(json_path.to_str().unwrap())
        .arg("--format")
        .arg("json")
        .arg("--no-progress");
    cmd.assert().success();

    // Search
    let mut cmd = Command::cargo_bin("ruvector").unwrap();
    cmd.arg("search")
        .arg("--db")
        .arg(db_path.to_str().unwrap())
        .arg("--query")
        .arg("[1.0, 0.0, 0.0]")
        .arg("--top-k")
        .arg("2");

    cmd.assert()
        .success()
        .stdout(predicate::str::contains("v1"));
}

#[test]
fn test_benchmark_command() {
    let dir = tempdir().unwrap();
    let db_path = dir.path().join("test.db");

    // Create database
    let mut cmd = Command::cargo_bin("ruvector").unwrap();
    cmd.arg("create")
        .arg("--path")
        .arg(db_path.to_str().unwrap())
        .arg("--dimensions")
        .arg("128");
    cmd.assert().success();

    // Run benchmark
    let mut cmd = Command::cargo_bin("ruvector").unwrap();
    cmd.arg("benchmark")
        .arg("--db")
        .arg(db_path.to_str().unwrap())
        .arg("--queries")
        .arg("100");

    cmd.assert()
        .success()
        .stdout(predicate::str::contains("Benchmark Results"))
        .stdout(predicate::str::contains("Queries per second"));
}

#[test]
fn test_error_handling() {
    // Test with invalid database path - /dev/null is a device file, not a directory,
    // so we cannot create a database file inside it. This guarantees failure
    // regardless of user permissions.
    let mut cmd = Command::cargo_bin("ruvector").unwrap();
    cmd.arg("info").arg("--db").arg("/dev/null/db.db");

    cmd.assert()
        .failure()
        .stderr(predicate::str::contains("Error"));
}