#![allow(clippy::arc_with_non_send_sync)]
mod common;
use common::{assertions::assert_ok, TestStorage};
use linkrs_core::types::SpaceInfo;
use linkrs_metrics::{QueryStatus, StatsManager};
use linkrs_query::optimizer::OptimizerEngine;
use linkrs_query::parser::Parser;
use linkrs_query::pipeline::QueryPipelineManager;
use linkrs_query::planning::PlannerConfig;
use linkrs_query::storage::StorageSchemaOps;
use linkrs_query::QueryContext;
use linkrs_query::QueryRequestContext;
use std::sync::Arc;
fn create_test_query_context() -> Arc<QueryContext> {
let request_context = Arc::new(QueryRequestContext::new("TEST".to_string()));
let mut qctx = QueryContext::new(request_context);
let space_info = SpaceInfo::new("test_space".to_string());
qctx.set_space_info(space_info);
Arc::new(qctx)
}
#[test]
fn test_parser_match_statement_basic() {
let query = "MATCH (n:Person) RETURN n";
let mut parser = Parser::new(query);
let result = parser.parse();
assert!(result.is_ok(), "MATCH should parse: {:?}", result.err());
}
#[test]
fn test_parser_go_statement() {
let query = "GO FROM 1 OVER KNOWS";
let mut parser = Parser::new(query);
let result = parser.parse();
assert!(result.is_ok(), "GO should parse: {:?}", result.err());
}
#[test]
fn test_parser_use_statement() {
let query = "USE test_space";
let mut parser = Parser::new(query);
let result = parser.parse();
assert!(result.is_ok(), "USE should parse: {:?}", result.err());
}
#[test]
fn test_parser_create_tag() {
let queries = vec![
"CREATE TAG test_tag(name: STRING)",
"CREATE TAG IF NOT EXISTS test_tag(name STRING)",
];
for query in queries {
let mut parser = Parser::new(query);
let result = parser.parse();
assert!(
result.is_ok(),
"CREATE TAG variant should parse '{query}': {:?}",
result.err()
);
}
}
#[test]
fn test_parser_show_statements() {
let queries = vec!["SHOW SPACES", "SHOW TAGS", "SHOW EDGES"];
for query in queries {
let mut parser = Parser::new(query);
let result = parser.parse();
assert!(
result.is_ok(),
"SHOW variant should parse '{query}': {:?}",
result.err()
);
}
}
#[test]
fn test_parser_insert_vertex() {
let query = "INSERT VERTEX Person(name, age) VALUES 1:('Alice', 25)";
let mut parser = Parser::new(query);
let result = parser.parse();
assert!(
result.is_ok(),
"INSERT VERTEX should parse: {:?}",
result.err()
);
}
#[test]
fn test_parser_invalid_syntax() {
let query = "INVALID SYNTAX HERE";
let mut parser = Parser::new(query);
let result = parser.parse();
assert!(result.is_err(), "Invalid syntax should return an error");
}
#[test]
fn test_planner_config_creation() {
let config = PlannerConfig::default();
let _ = config;
}
#[test]
fn test_planner_match_statement() {
let test_storage = TestStorage::new().expect("Failed to create test storage");
let storage = test_storage.storage();
let mut space_info = common::storage_helpers::create_test_space("planner_test_space");
{
let mut storage_guard = storage.write();
assert_ok(storage_guard.create_space(&mut space_info));
}
let query = "MATCH (n:Person) RETURN n";
let mut parser = Parser::new(query);
let result = parser.parse();
if result.is_err() {
return;
}
let _stmt = result.expect("Failed to parse query");
let _query_context = create_test_query_context();
}
#[test]
fn test_pipeline_manager_creation() {
let test_storage = TestStorage::new().expect("Failed to create test storage");
let storage = test_storage.storage();
let stats_manager = Arc::new(StatsManager::new());
let _pipeline_manager = QueryPipelineManager::with_optimizer(
storage,
stats_manager,
Arc::new(OptimizerEngine::default()),
);
}
#[test]
fn test_pipeline_manager_create_tag() {
use crate::common::test_scenario::TestScenario;
TestScenario::new()
.expect("Failed to create test scenario")
.setup_space("pipeline_tag_space")
.exec_ddl("CREATE TAG pipeline_test_tag(name STRING, age INT)")
.assert_success()
.assert_tag_exists("pipeline_test_tag");
}
#[test]
fn test_pipeline_manager_use_space() {
let test_storage = TestStorage::new().expect("Failed to create test storage");
let storage = test_storage.storage();
let stats_manager = Arc::new(StatsManager::new());
{
let mut storage_guard = storage.write();
let mut space_info = common::storage_helpers::create_test_space("use_test_space");
let _ = storage_guard.create_space(&mut space_info);
}
let mut pipeline_manager = QueryPipelineManager::with_optimizer(
storage,
stats_manager,
Arc::new(OptimizerEngine::default()),
);
let query = "USE use_test_space";
let result = pipeline_manager.execute_query(query);
assert!(
result.is_ok(),
"Execution should return Ok result: {:?}",
result.err()
);
}
#[test]
fn test_complete_query_flow_show_spaces() {
let test_storage = TestStorage::new().expect("Failed to create test storage");
let storage = test_storage.storage();
let stats_manager = Arc::new(StatsManager::new());
let mut pipeline_manager = QueryPipelineManager::with_optimizer(
storage,
stats_manager,
Arc::new(OptimizerEngine::default()),
);
let query = "SHOW SPACES";
let result = pipeline_manager.execute_query(query);
match result {
Ok(_exec_result) => {
}
Err(_e) => {
}
}
}
#[test]
fn test_complete_query_flow_reports_stage_profile() {
let test_storage = TestStorage::new().expect("Failed to create test storage");
let storage = test_storage.storage();
let stats_manager = Arc::new(StatsManager::new());
let mut pipeline_manager = QueryPipelineManager::with_optimizer(
storage,
stats_manager,
Arc::new(OptimizerEngine::default()),
);
let (_result, _metrics, profile) = pipeline_manager
.execute_query_with_profile("SHOW SPACES", 0, None)
.expect("SHOW SPACES should execute");
assert_eq!(profile.status, QueryStatus::Success);
assert!(profile.stages.parse_us > 0);
assert!(profile.total_duration_us >= profile.stages.parse_us);
}
#[test]
fn test_query_flow_create_and_desc_tag() {
use crate::common::test_scenario::TestScenario;
TestScenario::new()
.expect("Failed to create test scenario")
.setup_space("desc_flow_space")
.exec_ddl("CREATE TAG desc_test_tag(name STRING)")
.assert_success()
.query("DESC TAG desc_test_tag")
.assert_success()
.assert_result_count(1);
}
#[test]
fn test_query_error_invalid_syntax() {
let test_storage = TestStorage::new().expect("Failed to create test storage");
let storage = test_storage.storage();
let stats_manager = Arc::new(StatsManager::new());
let mut pipeline_manager = QueryPipelineManager::with_optimizer(
storage,
stats_manager,
Arc::new(OptimizerEngine::default()),
);
let query = "INVALID SYNTAX HERE";
let result = pipeline_manager.execute_query(query);
assert!(result.is_err(), "Invalid syntax should result in an error.");
}
#[test]
fn test_query_error_nonexistent_space() {
let test_storage = TestStorage::new().expect("Failed to create test storage");
let storage = test_storage.storage();
let stats_manager = Arc::new(StatsManager::new());
let mut pipeline_manager = QueryPipelineManager::with_optimizer(
storage,
stats_manager,
Arc::new(OptimizerEngine::default()),
);
let query = "USE nonexistent_space_xyz";
let result = pipeline_manager.execute_query(query);
match result {
Ok(_) => panic!("USE of a missing space should not succeed"),
Err(e) => assert!(
format!("{e:?}").contains("Space not found"),
"USE missing space should report Space not found, got: {e:?}"
),
};
}
#[test]
fn test_query_pipeline_performance() {
let test_storage = TestStorage::new().expect("Failed to create test storage");
let storage = test_storage.storage();
let stats_manager = Arc::new(StatsManager::new());
let mut pipeline_manager = QueryPipelineManager::with_optimizer(
storage,
stats_manager,
Arc::new(OptimizerEngine::default()),
);
let query = "SHOW SPACES";
let iterations = 10;
for i in 0..iterations {
let result = pipeline_manager.execute_query(query);
assert!(
result.is_ok(),
"Query {} should execute: {:?}",
i,
result.err()
);
}
}
#[test]
fn test_sequential_query_execution() {
let test_storage = TestStorage::new().expect("Failed to create test storage");
let storage = test_storage.storage();
let stats_manager = Arc::new(StatsManager::new());
let mut pipeline_manager = QueryPipelineManager::with_optimizer(
storage,
stats_manager,
Arc::new(OptimizerEngine::default()),
);
for _i in 0..5 {
let query = "SHOW SPACES";
let _result = pipeline_manager.execute_query(query);
}
}