use tempfile::TempDir;
use velesdb_core::collection::graph::GraphSchema;
use velesdb_core::{Database, GraphCollection, GraphEdge};
use crate::repl_commands::CommandResult;
use crate::repl_graph_cmds::cmd_graph;
fn setup_db() -> (TempDir, Database) {
let dir = TempDir::new().expect("test: create temp dir");
let db = Database::open(dir.path()).expect("test: open database");
db.create_graph_collection("kg", GraphSchema::schemaless())
.expect("test: create graph collection");
(dir, db)
}
fn populate(db: &Database) {
let col = db
.get_graph_collection("kg")
.expect("test: get graph collection");
for (id, src, tgt, lbl) in [
(100, 1, 2, "KNOWS"),
(101, 2, 3, "KNOWS"),
(102, 3, 4, "KNOWS"),
(103, 2, 5, "WROTE"),
] {
col.add_edge(GraphEdge::new(id, src, tgt, lbl).expect("valid edge"))
.expect("test: add edge");
}
}
fn graph_col(db: &Database) -> GraphCollection {
db.get_graph_collection("kg")
.expect("test: get graph collection")
}
fn assert_continue(result: &CommandResult) {
match result {
CommandResult::Continue => {}
CommandResult::Error(e) => panic!("Expected Continue, got Error: {e}"),
CommandResult::Quit => panic!("Expected Continue, got Quit"),
}
}
fn assert_error(result: &CommandResult) {
assert!(
matches!(result, CommandResult::Error(_)),
"Expected Error, got {:?}",
match result {
CommandResult::Continue => "Continue",
CommandResult::Quit => "Quit",
CommandResult::Error(e) => e.as_str(),
}
);
}
#[test]
fn test_repl_remove_edge_existing_removes_it() {
let (_dir, db) = setup_db();
populate(&db);
assert_eq!(graph_col(&db).edge_count(), 4);
let parts: Vec<&str> = vec![".graph", "remove-edge", "kg", "100"];
let result = cmd_graph(&db, &parts);
assert_continue(&result);
assert_eq!(graph_col(&db).edge_count(), 3);
}
#[test]
fn test_repl_remove_edge_nonexistent_no_error() {
let (_dir, db) = setup_db();
populate(&db);
let parts: Vec<&str> = vec![".graph", "remove-edge", "kg", "999"];
let result = cmd_graph(&db, &parts);
assert_continue(&result);
assert_eq!(graph_col(&db).edge_count(), 4);
}
#[test]
fn test_repl_remove_edge_twice_same_id() {
let (_dir, db) = setup_db();
populate(&db);
let parts: Vec<&str> = vec![".graph", "remove-edge", "kg", "100"];
assert_continue(&cmd_graph(&db, &parts));
let result = cmd_graph(&db, &parts);
assert_continue(&result);
assert_eq!(graph_col(&db).edge_count(), 3);
}
#[test]
fn test_repl_remove_edge_missing_args_shows_usage() {
let (_dir, db) = setup_db();
let parts: Vec<&str> = vec![".graph", "remove-edge"];
let result = cmd_graph(&db, &parts);
assert_continue(&result);
}
#[test]
fn test_repl_remove_edge_invalid_id_returns_error() {
let (_dir, db) = setup_db();
let parts: Vec<&str> = vec![".graph", "remove-edge", "kg", "not_a_number"];
let result = cmd_graph(&db, &parts);
assert_error(&result);
}
#[test]
fn test_repl_remove_edge_nonexistent_collection_returns_error() {
let (_dir, db) = setup_db();
let parts: Vec<&str> = vec![".graph", "remove-edge", "ghost", "1"];
let result = cmd_graph(&db, &parts);
assert_error(&result);
}
#[test]
fn test_repl_count_populated_graph() {
let (_dir, db) = setup_db();
populate(&db);
let parts: Vec<&str> = vec![".graph", "count", "kg"];
let result = cmd_graph(&db, &parts);
assert_continue(&result);
assert_eq!(graph_col(&db).edge_count(), 4);
}
#[test]
fn test_repl_count_empty_graph() {
let (_dir, db) = setup_db();
let parts: Vec<&str> = vec![".graph", "count", "kg"];
let result = cmd_graph(&db, &parts);
assert_continue(&result);
assert_eq!(graph_col(&db).edge_count(), 0);
}
#[test]
fn test_repl_count_nonexistent_collection_returns_error() {
let (_dir, db) = setup_db();
let parts: Vec<&str> = vec![".graph", "count", "ghost"];
let result = cmd_graph(&db, &parts);
assert_error(&result);
}
#[test]
fn test_repl_count_missing_args_shows_usage() {
let (_dir, db) = setup_db();
let parts: Vec<&str> = vec![".graph", "count"];
let result = cmd_graph(&db, &parts);
assert_continue(&result); }
#[test]
fn test_repl_search_graph_without_embeddings_returns_error() {
let (_dir, db) = setup_db();
let parts: Vec<&str> = vec![".graph", "search", "kg", "[1.0,0.0,0.0,0.0]"];
let result = cmd_graph(&db, &parts);
assert_error(&result);
}
#[test]
fn test_repl_search_invalid_vector_json_returns_error() {
let (_dir, db) = setup_db();
let parts: Vec<&str> = vec![".graph", "search", "kg", "not_json"];
let result = cmd_graph(&db, &parts);
assert_error(&result);
}
#[test]
fn test_repl_search_nonexistent_collection_returns_error() {
let (_dir, db) = setup_db();
let parts: Vec<&str> = vec![".graph", "search", "ghost", "[1.0]"];
let result = cmd_graph(&db, &parts);
assert_error(&result);
}
#[test]
fn test_repl_search_missing_args_shows_usage() {
let (_dir, db) = setup_db();
let parts: Vec<&str> = vec![".graph", "search"];
let result = cmd_graph(&db, &parts);
assert_continue(&result); }
#[test]
fn test_repl_store_payload_creates_payload() {
let (_dir, db) = setup_db();
let parts: Vec<&str> = vec![".graph", "store-payload", "kg", "42", r#"{"name":"Alice"}"#];
let result = cmd_graph(&db, &parts);
assert_continue(&result);
let payload = graph_col(&db).get_node_payload(42).unwrap().unwrap();
assert_eq!(payload["name"], "Alice");
}
#[test]
fn test_repl_store_payload_overwrites() {
let (_dir, db) = setup_db();
let parts1: Vec<&str> = vec![".graph", "store-payload", "kg", "1", r#"{"v":1}"#];
assert_continue(&cmd_graph(&db, &parts1));
let parts2: Vec<&str> = vec![".graph", "store-payload", "kg", "1", r#"{"v":2}"#];
let result = cmd_graph(&db, &parts2);
assert_continue(&result);
let payload = graph_col(&db).get_node_payload(1).unwrap().unwrap();
assert_eq!(payload["v"], 2);
}
#[test]
fn test_repl_store_payload_invalid_json_returns_error() {
let (_dir, db) = setup_db();
let parts: Vec<&str> = vec![".graph", "store-payload", "kg", "1", "not_json"];
let result = cmd_graph(&db, &parts);
assert_error(&result);
}
#[test]
fn test_repl_get_payload_existing_node() {
let (_dir, db) = setup_db();
graph_col(&db)
.upsert_node_payload(10, &serde_json::json!({"role": "admin"}))
.unwrap();
let parts: Vec<&str> = vec![".graph", "get-payload", "kg", "10"];
let result = cmd_graph(&db, &parts);
assert_continue(&result);
}
#[test]
fn test_repl_get_payload_nonexistent_node_prints_null() {
let (_dir, db) = setup_db();
let parts: Vec<&str> = vec![".graph", "get-payload", "kg", "999"];
let result = cmd_graph(&db, &parts);
assert_continue(&result);
}
#[test]
fn test_repl_nodes_with_payloads() {
let (_dir, db) = setup_db();
let col = graph_col(&db);
col.upsert_node_payload(1, &serde_json::json!({"name": "A"}))
.unwrap();
col.upsert_node_payload(2, &serde_json::json!({"name": "B"}))
.unwrap();
let parts: Vec<&str> = vec![".graph", "nodes", "kg"];
let result = cmd_graph(&db, &parts);
assert_continue(&result);
}
#[test]
fn test_repl_nodes_empty_graph() {
let (_dir, db) = setup_db();
let parts: Vec<&str> = vec![".graph", "nodes", "kg"];
let result = cmd_graph(&db, &parts);
assert_continue(&result);
}
#[test]
fn test_repl_nodes_nonexistent_collection_returns_error() {
let (_dir, db) = setup_db();
let parts: Vec<&str> = vec![".graph", "nodes", "ghost"];
let result = cmd_graph(&db, &parts);
assert_error(&result);
}
#[test]
fn test_repl_add_edge_creates_edge() {
let (_dir, db) = setup_db();
let parts: Vec<&str> = vec![".graph", "add-edge", "kg", "1", "10", "20", "KNOWS"];
let result = cmd_graph(&db, &parts);
assert_continue(&result);
assert_eq!(graph_col(&db).edge_count(), 1);
let edges = graph_col(&db).get_edges(Some("KNOWS"));
assert_eq!(edges.len(), 1);
assert_eq!(edges[0].source(), 10);
assert_eq!(edges[0].target(), 20);
}
#[test]
fn test_repl_edges_with_label_filter() {
let (_dir, db) = setup_db();
populate(&db);
let parts: Vec<&str> = vec![".graph", "edges", "kg", "--label", "KNOWS"];
let result = cmd_graph(&db, &parts);
assert_continue(&result);
let edges = graph_col(&db).get_edges(Some("KNOWS"));
assert_eq!(edges.len(), 3);
}
#[test]
fn test_repl_degree_shows_correct_values() {
let (_dir, db) = setup_db();
populate(&db);
let parts: Vec<&str> = vec![".graph", "degree", "kg", "2"];
let result = cmd_graph(&db, &parts);
assert_continue(&result);
let (in_deg, out_deg) = graph_col(&db).node_degree(2);
assert_eq!(in_deg, 1);
assert_eq!(out_deg, 2);
}
#[test]
fn test_repl_traverse_bfs_default() {
let (_dir, db) = setup_db();
populate(&db);
let parts: Vec<&str> = vec![".graph", "traverse", "kg", "1"];
let result = cmd_graph(&db, &parts);
assert_continue(&result);
}
#[test]
fn test_repl_traverse_dfs_with_depth() {
let (_dir, db) = setup_db();
populate(&db);
let parts: Vec<&str> = vec![
".graph", "traverse", "kg", "1", "--algo", "dfs", "--depth", "2",
];
let result = cmd_graph(&db, &parts);
assert_continue(&result);
}
#[test]
fn test_repl_neighbors_outgoing() {
let (_dir, db) = setup_db();
populate(&db);
let parts: Vec<&str> = vec![".graph", "neighbors", "kg", "2"];
let result = cmd_graph(&db, &parts);
assert_continue(&result);
assert_eq!(graph_col(&db).get_outgoing(2).len(), 2);
}
#[test]
fn test_repl_neighbors_incoming() {
let (_dir, db) = setup_db();
populate(&db);
let parts: Vec<&str> = vec![".graph", "neighbors", "kg", "3", "--direction", "in"];
let result = cmd_graph(&db, &parts);
assert_continue(&result);
assert_eq!(graph_col(&db).get_incoming(3).len(), 1);
}
#[test]
fn test_repl_graph_unknown_subcommand_returns_error() {
let (_dir, db) = setup_db();
let parts: Vec<&str> = vec![".graph", "foobar"];
let result = cmd_graph(&db, &parts);
assert_error(&result);
}
#[test]
fn test_repl_graph_no_subcommand_shows_help() {
let (_dir, db) = setup_db();
let parts: Vec<&str> = vec![".graph"];
let result = cmd_graph(&db, &parts);
assert_continue(&result);
}
#[test]
fn test_repl_upsert_creates_point() {
let dir = TempDir::new().expect("test: create temp dir");
let db = Database::open(dir.path()).expect("test: open database");
db.create_vector_collection("docs", 4, velesdb_core::DistanceMetric::Cosine)
.expect("test: create vector collection");
let parts: Vec<&str> = vec![
".upsert",
"docs",
"1",
"[1.0,0.0,0.0,0.0]",
r#"{"title":"hello"}"#,
];
let result = crate::repl_data_cmds::cmd_upsert(&db, &parts);
assert_continue(&result);
let col = db.get_vector_collection("docs").expect("get col");
let points = col.get(&[1]);
assert!(points[0].is_some());
assert_eq!(points[0].as_ref().unwrap().id, 1);
}
#[test]
fn test_repl_upsert_without_payload() {
let dir = TempDir::new().expect("test: create temp dir");
let db = Database::open(dir.path()).expect("test: open database");
db.create_vector_collection("docs", 4, velesdb_core::DistanceMetric::Cosine)
.expect("test: create vector collection");
let parts: Vec<&str> = vec![".upsert", "docs", "2", "[0.0,1.0,0.0,0.0]"];
let result = crate::repl_data_cmds::cmd_upsert(&db, &parts);
assert_continue(&result);
let col = db.get_vector_collection("docs").expect("get col");
let points = col.get(&[2]);
assert!(points[0].is_some());
}
#[test]
fn test_repl_upsert_invalid_vector_returns_error() {
let dir = TempDir::new().expect("test: create temp dir");
let db = Database::open(dir.path()).expect("test: open database");
db.create_vector_collection("docs", 4, velesdb_core::DistanceMetric::Cosine)
.expect("test: create vector collection");
let parts: Vec<&str> = vec![".upsert", "docs", "1", "not_json"];
let result = crate::repl_data_cmds::cmd_upsert(&db, &parts);
assert_error(&result);
}
#[test]
fn test_repl_upsert_nonexistent_collection_returns_error() {
let dir = TempDir::new().expect("test: create temp dir");
let db = Database::open(dir.path()).expect("test: open database");
let parts: Vec<&str> = vec![".upsert", "ghost", "1", "[1.0]"];
let result = crate::repl_data_cmds::cmd_upsert(&db, &parts);
assert_error(&result);
}
#[test]
fn test_repl_upsert_missing_args_shows_usage() {
let (_dir, db) = setup_db();
let parts: Vec<&str> = vec![".upsert"];
let result = crate::repl_data_cmds::cmd_upsert(&db, &parts);
assert_continue(&result); }