Skip to content
39 changes: 39 additions & 0 deletions rust/cubesqlplanner/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions rust/cubesqlplanner/cubesqlplanner/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ lazy_static = "1.4.0"
regex = "1.3.9"
typed-builder = "0.21.2"

[dev-dependencies]
petgraph = "0.6"

[dependencies.neon]
version = "=1"
default-features = false
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use crate::cube_bridge::cube_definition::{CubeDefinition, CubeDefinitionStatic};
use crate::cube_bridge::member_sql::MemberSql;
use crate::impl_static_data;
use crate::test_fixtures::cube_bridge::MockMemberSql;
use crate::test_fixtures::cube_bridge::{MockJoinItemDefinition, MockMemberSql};
use cubenativeutils::CubeError;
use std::any::Any;
use std::collections::HashMap;
use std::rc::Rc;
use typed_builder::TypedBuilder;

Expand All @@ -26,6 +27,10 @@ pub struct MockCubeDefinition {
sql_table: Option<String>,
#[builder(default, setter(strip_option))]
sql: Option<String>,

// Joins field for mock testing
#[builder(default)]
joins: HashMap<String, MockJoinItemDefinition>,
}

impl_static_data!(
Expand Down Expand Up @@ -68,9 +73,23 @@ impl CubeDefinition for MockCubeDefinition {
}
}

impl MockCubeDefinition {
/// Get all joins for this cube
pub fn joins(&self) -> &HashMap<String, MockJoinItemDefinition> {
&self.joins
}

/// Get a specific join by name
pub fn get_join(&self, name: &str) -> Option<&MockJoinItemDefinition> {
self.joins.get(name)
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::cube_bridge::join_item_definition::JoinItemDefinition;
use std::collections::HashMap;

#[test]
fn test_basic_cube() {
Expand Down Expand Up @@ -170,4 +189,100 @@ mod tests {
let sql_table = cube.sql_table().unwrap().unwrap();
assert_eq!(sql_table.args_names(), &vec!["database"]);
}

#[test]
fn test_cube_with_single_join() {
let mut joins = HashMap::new();
joins.insert(
"users".to_string(),
MockJoinItemDefinition::builder()
.relationship("many_to_one".to_string())
.sql("{CUBE}.user_id = {users.id}".to_string())
.build(),
);

let cube = MockCubeDefinition::builder()
.name("orders".to_string())
.sql_table("public.orders".to_string())
.joins(joins)
.build();

assert_eq!(cube.joins().len(), 1);
assert!(cube.get_join("users").is_some());

let users_join = cube.get_join("users").unwrap();
assert_eq!(users_join.static_data().relationship, "many_to_one");
}

#[test]
fn test_cube_with_multiple_joins() {
let mut joins = HashMap::new();
joins.insert(
"users".to_string(),
MockJoinItemDefinition::builder()
.relationship("many_to_one".to_string())
.sql("{CUBE}.user_id = {users.id}".to_string())
.build(),
);
joins.insert(
"products".to_string(),
MockJoinItemDefinition::builder()
.relationship("many_to_one".to_string())
.sql("{CUBE}.product_id = {products.id}".to_string())
.build(),
);

let cube = MockCubeDefinition::builder()
.name("orders".to_string())
.sql_table("public.orders".to_string())
.joins(joins)
.build();

assert_eq!(cube.joins().len(), 2);
assert!(cube.get_join("users").is_some());
assert!(cube.get_join("products").is_some());
assert!(cube.get_join("nonexistent").is_none());
}

#[test]
fn test_join_accessor_methods() {
let mut joins = HashMap::new();
joins.insert(
"countries".to_string(),
MockJoinItemDefinition::builder()
.relationship("many_to_one".to_string())
.sql("{CUBE}.country_id = {countries.id}".to_string())
.build(),
);

let cube = MockCubeDefinition::builder()
.name("users".to_string())
.sql_table("public.users".to_string())
.joins(joins)
.build();

// Test joins() method
let all_joins = cube.joins();
assert_eq!(all_joins.len(), 1);
assert!(all_joins.contains_key("countries"));

// Test get_join() method
let country_join = cube.get_join("countries").unwrap();
let sql = country_join.sql().unwrap();
assert_eq!(sql.args_names(), &vec!["CUBE", "countries"]);

// Test nonexistent join
assert!(cube.get_join("nonexistent").is_none());
}

#[test]
fn test_cube_without_joins() {
let cube = MockCubeDefinition::builder()
.name("users".to_string())
.sql_table("public.users".to_string())
.build();

assert_eq!(cube.joins().len(), 0);
assert!(cube.get_join("any").is_none());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use crate::cube_bridge::pre_aggregation_description::PreAggregationDescription;
use crate::cube_bridge::segment_definition::SegmentDefinition;
use crate::impl_static_data;
use crate::test_fixtures::cube_bridge::mock_schema::MockSchema;
use crate::test_fixtures::cube_bridge::MockJoinGraph;
use cubenativeutils::CubeError;
use std::any::Any;
use std::collections::HashMap;
Expand All @@ -17,6 +18,7 @@ use std::rc::Rc;
pub struct MockCubeEvaluator {
schema: MockSchema,
primary_keys: HashMap<String, Vec<String>>,
join_graph: Option<Rc<MockJoinGraph>>,
}

impl MockCubeEvaluator {
Expand All @@ -25,6 +27,7 @@ impl MockCubeEvaluator {
Self {
schema,
primary_keys: HashMap::new(),
join_graph: None,
}
}

Expand All @@ -36,9 +39,54 @@ impl MockCubeEvaluator {
Self {
schema,
primary_keys,
join_graph: None,
}
}

/// Create a new MockCubeEvaluator with join graph
///
/// This constructor creates an evaluator with a compiled join graph,
/// enabling join path resolution in tests.
///
/// # Arguments
/// * `schema` - The mock schema containing cubes
/// * `primary_keys` - Primary key definitions for cubes
/// * `join_graph` - Compiled join graph
///
/// # Returns
/// * `MockCubeEvaluator` - Evaluator with compiled join graph
pub fn with_join_graph(
schema: MockSchema,
primary_keys: HashMap<String, Vec<String>>,
join_graph: MockJoinGraph,
) -> Self {
Self {
schema,
primary_keys,
join_graph: Some(Rc::new(join_graph)),
}
}

/// Get the join graph if available
///
/// # Returns
/// * `Some(Rc<MockJoinGraph>)` - If join graph was created
/// * `None` - If evaluator was created without join graph
pub fn join_graph(&self) -> Option<Rc<MockJoinGraph>> {
self.join_graph.clone()
}

/// Get all measures for a cube
pub fn measures_for_cube(
&self,
cube_name: &str,
) -> HashMap<String, Rc<crate::test_fixtures::cube_bridge::MockMeasureDefinition>> {
self.schema
.get_cube(cube_name)
.map(|cube| cube.measures.clone())
.unwrap_or_default()
}

/// Parse a path string like "cube.member" into ["cube", "member"]
/// Returns error if the path doesn't exist in schema for the given type
fn parse_and_validate_path(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use std::rc::Rc;
use typed_builder::TypedBuilder;

/// Mock implementation of JoinDefinition for testing
#[derive(TypedBuilder)]
#[derive(Debug, TypedBuilder)]
pub struct MockJoinDefinition {
// Fields from JoinDefinitionStatic
root: String,
Expand Down
Loading
Loading