jspg cleanup
This commit is contained in:
@ -116,17 +116,99 @@ fn parse_and_match_mocks(sql: &str, mocks: &[Value]) -> Option<Vec<Value>> {
|
||||
return None;
|
||||
}
|
||||
|
||||
// The merger's existence read emits one SELECT per probe (id / lookup key)
|
||||
// joined by UNION. Match each arm independently against its own tables and
|
||||
// WHERE clause; a mock matching any arm is returned once, mirroring UNION's
|
||||
// dedup semantics.
|
||||
let union_regex = Regex::new(r"(?i)\s+UNION\s+(?:ALL\s+)?").ok()?;
|
||||
let arms: Vec<&str> = union_regex.split(sql).collect();
|
||||
// 1. Extract table name
|
||||
let table_regex = Regex::new(r#"(?i)\s+FROM\s+(?:[a-zA-Z_]\w*\.)?"?([a-zA-Z_]\w*)"?"#).ok()?;
|
||||
let table = if let Some(caps) = table_regex.captures(sql) {
|
||||
caps.get(1)?.as_str()
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
|
||||
// 2. Extract WHERE conditions string
|
||||
let mut where_clause = String::new();
|
||||
if let Some(where_idx) = sql_upper.find(" WHERE ") {
|
||||
let mut where_end = sql_upper.find(" ORDER BY ").unwrap_or(sql_upper.len());
|
||||
if let Some(limit_idx) = sql_upper.find(" LIMIT ") {
|
||||
if limit_idx < where_end {
|
||||
where_end = limit_idx;
|
||||
}
|
||||
}
|
||||
where_clause = sql[where_idx + 7..where_end].to_string();
|
||||
}
|
||||
|
||||
// 3. Find matching mocks
|
||||
let mut matches = Vec::new();
|
||||
let or_regex = Regex::new(r"(?i)\s+OR\s+").ok()?;
|
||||
let and_regex = Regex::new(r"(?i)\s+AND\s+").ok()?;
|
||||
|
||||
for mock in mocks {
|
||||
if let Some(mock_obj) = mock.as_object() {
|
||||
if arms.iter().any(|arm| arm_matches_mock(arm, mock_obj)) {
|
||||
if let Some(t) = mock_obj.get("type") {
|
||||
if t.as_str() != Some(table) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if where_clause.is_empty() {
|
||||
matches.push(mock.clone());
|
||||
continue;
|
||||
}
|
||||
|
||||
let or_parts = or_regex.split(&where_clause);
|
||||
let mut any_branch_matched = false;
|
||||
|
||||
for or_part in or_parts {
|
||||
let branch_str = or_part.replace('(', "").replace(')', "");
|
||||
let mut branch_matches = true;
|
||||
|
||||
for part in and_regex.split(&branch_str) {
|
||||
if let Some(eq_idx) = part.find('=') {
|
||||
let left = part[..eq_idx]
|
||||
.trim()
|
||||
.split('.')
|
||||
.last()
|
||||
.unwrap_or("")
|
||||
.trim_matches('"');
|
||||
let right = part[eq_idx + 1..].trim().trim_matches('\'');
|
||||
|
||||
let mock_val_str = match mock_obj.get(left) {
|
||||
Some(Value::String(s)) => s.clone(),
|
||||
Some(Value::Number(n)) => n.to_string(),
|
||||
Some(Value::Bool(b)) => b.to_string(),
|
||||
Some(Value::Null) => "null".to_string(),
|
||||
_ => "".to_string(),
|
||||
};
|
||||
if mock_val_str != right {
|
||||
branch_matches = false;
|
||||
break;
|
||||
}
|
||||
} else if part.to_uppercase().contains(" IS NULL") {
|
||||
let left = part[..part.to_uppercase().find(" IS NULL").unwrap()]
|
||||
.trim()
|
||||
.split('.')
|
||||
.last()
|
||||
.unwrap_or("")
|
||||
.trim_matches('"');
|
||||
|
||||
let mock_val_str = match mock_obj.get(left) {
|
||||
Some(Value::Null) => "null".to_string(),
|
||||
_ => "".to_string(),
|
||||
};
|
||||
|
||||
if mock_val_str != "null" {
|
||||
branch_matches = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if branch_matches {
|
||||
any_branch_matched = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if any_branch_matched {
|
||||
matches.push(mock.clone());
|
||||
}
|
||||
}
|
||||
@ -134,103 +216,3 @@ fn parse_and_match_mocks(sql: &str, mocks: &[Value]) -> Option<Vec<Value>> {
|
||||
|
||||
Some(matches)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn arm_matches_mock(arm: &str, mock_obj: &serde_json::Map<String, Value>) -> bool {
|
||||
let arm_upper = arm.to_uppercase();
|
||||
|
||||
// 1. The mock's type must name one of the arm's tables. Hierarchy reads
|
||||
// SELECT FROM the root (entity) and JOIN the subtype tables, so every
|
||||
// FROM/JOIN table counts, not just the first.
|
||||
let table_regex = match Regex::new(r#"(?i)\b(?:FROM|JOIN)\s+(?:[a-zA-Z_]\w*\.)?"?([a-zA-Z_]\w*)"?"#) {
|
||||
Ok(r) => r,
|
||||
Err(_) => return false,
|
||||
};
|
||||
let tables: Vec<&str> = table_regex.captures_iter(arm).filter_map(|c| c.get(1)).map(|m| m.as_str()).collect();
|
||||
if tables.is_empty() {
|
||||
return false;
|
||||
}
|
||||
if let Some(t) = mock_obj.get("type") {
|
||||
if !tables.iter().any(|table| t.as_str() == Some(table)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Extract this arm's WHERE conditions
|
||||
let mut where_clause = String::new();
|
||||
if let Some(where_idx) = arm_upper.find(" WHERE ") {
|
||||
let mut where_end = arm_upper.find(" ORDER BY ").unwrap_or(arm_upper.len());
|
||||
if let Some(limit_idx) = arm_upper.find(" LIMIT ") {
|
||||
if limit_idx < where_end {
|
||||
where_end = limit_idx;
|
||||
}
|
||||
}
|
||||
where_clause = arm[where_idx + 7..where_end].to_string();
|
||||
}
|
||||
|
||||
if where_clause.is_empty() {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 3. Match the mock against the conditions
|
||||
let or_regex = match Regex::new(r"(?i)\s+OR\s+") {
|
||||
Ok(r) => r,
|
||||
Err(_) => return false,
|
||||
};
|
||||
let and_regex = match Regex::new(r"(?i)\s+AND\s+") {
|
||||
Ok(r) => r,
|
||||
Err(_) => return false,
|
||||
};
|
||||
|
||||
for or_part in or_regex.split(&where_clause) {
|
||||
let branch_str = or_part.replace('(', "").replace(')', "");
|
||||
let mut branch_matches = true;
|
||||
|
||||
for part in and_regex.split(&branch_str) {
|
||||
if let Some(eq_idx) = part.find('=') {
|
||||
let left = part[..eq_idx]
|
||||
.trim()
|
||||
.split('.')
|
||||
.last()
|
||||
.unwrap_or("")
|
||||
.trim_matches('"');
|
||||
let right = part[eq_idx + 1..].trim().trim_matches('\'');
|
||||
|
||||
let mock_val_str = match mock_obj.get(left) {
|
||||
Some(Value::String(s)) => s.clone(),
|
||||
Some(Value::Number(n)) => n.to_string(),
|
||||
Some(Value::Bool(b)) => b.to_string(),
|
||||
Some(Value::Null) => "null".to_string(),
|
||||
_ => "".to_string(),
|
||||
};
|
||||
if mock_val_str != right {
|
||||
branch_matches = false;
|
||||
break;
|
||||
}
|
||||
} else if part.to_uppercase().contains(" IS NULL") {
|
||||
let left = part[..part.to_uppercase().find(" IS NULL").unwrap()]
|
||||
.trim()
|
||||
.split('.')
|
||||
.last()
|
||||
.unwrap_or("")
|
||||
.trim_matches('"');
|
||||
|
||||
let mock_val_str = match mock_obj.get(left) {
|
||||
Some(Value::Null) => "null".to_string(),
|
||||
_ => "".to_string(),
|
||||
};
|
||||
|
||||
if mock_val_str != "null" {
|
||||
branch_matches = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if branch_matches {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user