progress
This commit is contained in:
@ -1463,6 +1463,18 @@ fn test_queryer_0_8() {
|
||||
crate::tests::runner::run_test_case(&path, 0, 8).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_queryer_0_9() {
|
||||
let path = format!("{}/fixtures/queryer.json", env!("CARGO_MANIFEST_DIR"));
|
||||
crate::tests::runner::run_test_case(&path, 0, 9).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_queryer_0_10() {
|
||||
let path = format!("{}/fixtures/queryer.json", env!("CARGO_MANIFEST_DIR"));
|
||||
crate::tests::runner::run_test_case(&path, 0, 10).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_not_0_0() {
|
||||
let path = format!("{}/fixtures/not.json", env!("CARGO_MANIFEST_DIR"));
|
||||
|
||||
@ -14,7 +14,7 @@ where
|
||||
}
|
||||
|
||||
// Type alias for easier reading
|
||||
type CompiledSuite = Arc<Vec<(Suite, Arc<crate::database::Database>)>>;
|
||||
type CompiledSuite = Arc<Vec<(Suite, Arc<Result<Arc<crate::database::Database>, crate::drop::Drop>>)>>;
|
||||
|
||||
// Global cache mapping filename -> Vector of (Parsed JSON suite, Compiled Database)
|
||||
static CACHE: OnceLock<RwLock<HashMap<String, CompiledSuite>>> = OnceLock::new();
|
||||
@ -43,19 +43,11 @@ fn get_cached_file(path: &str) -> CompiledSuite {
|
||||
let mut compiled_suites = Vec::new();
|
||||
for suite in suites {
|
||||
let db_result = crate::database::Database::new(&suite.database);
|
||||
if let Err(drop) = db_result {
|
||||
let error_messages: Vec<String> = drop
|
||||
.errors
|
||||
.into_iter()
|
||||
.map(|e| format!("Error {} at path {}: {}", e.code, e.details.path, e.message))
|
||||
.collect();
|
||||
panic!(
|
||||
"System Setup Compilation failed for {}:\n{}",
|
||||
path,
|
||||
error_messages.join("\n")
|
||||
);
|
||||
}
|
||||
compiled_suites.push((suite, Arc::new(db_result.unwrap())));
|
||||
let compiled_db = match db_result {
|
||||
Ok(db) => Ok(Arc::new(db)),
|
||||
Err(drop) => Err(drop),
|
||||
};
|
||||
compiled_suites.push((suite, Arc::new(compiled_db)));
|
||||
}
|
||||
|
||||
let new_data = Arc::new(compiled_suites);
|
||||
@ -85,11 +77,36 @@ pub fn run_test_case(path: &str, suite_idx: usize, case_idx: usize) -> Result<()
|
||||
let test = &group.tests[case_idx];
|
||||
let mut failures = Vec::<String>::new();
|
||||
|
||||
// For validate/merge/query, if setup failed we must structurally fail this test
|
||||
let db_unwrapped = if test.action.as_str() != "compile" && test.action.as_str() != "database_compile" {
|
||||
match &**db {
|
||||
Ok(valid_db) => Some(valid_db.clone()),
|
||||
Err(drop) => {
|
||||
let error_messages: Vec<String> = drop
|
||||
.errors
|
||||
.iter()
|
||||
.map(|e| format!("Error {} at path {}: {}", e.code, e.details.path, e.message))
|
||||
.collect();
|
||||
failures.push(format!(
|
||||
"[{}] Cannot run '{}' test '{}': System Setup Compilation structurally failed:\n{}",
|
||||
group.description, test.action, test.description, error_messages.join("\n")
|
||||
));
|
||||
None
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if !failures.is_empty() {
|
||||
return Err(failures.join("\n"));
|
||||
}
|
||||
|
||||
// 4. Run Tests
|
||||
|
||||
match test.action.as_str() {
|
||||
"compile" => {
|
||||
let result = test.run_compile(db.clone());
|
||||
"compile" | "database_compile" => {
|
||||
let result = test.run_compile(db);
|
||||
if let Err(e) = result {
|
||||
println!("TEST COMPILE ERROR FOR '{}': {}", test.description, e);
|
||||
failures.push(format!(
|
||||
@ -99,7 +116,7 @@ pub fn run_test_case(path: &str, suite_idx: usize, case_idx: usize) -> Result<()
|
||||
}
|
||||
}
|
||||
"validate" => {
|
||||
let result = test.run_validate(db.clone());
|
||||
let result = test.run_validate(db_unwrapped.unwrap());
|
||||
if let Err(e) = result {
|
||||
println!("TEST VALIDATE ERROR FOR '{}': {}", test.description, e);
|
||||
failures.push(format!(
|
||||
@ -109,7 +126,7 @@ pub fn run_test_case(path: &str, suite_idx: usize, case_idx: usize) -> Result<()
|
||||
}
|
||||
}
|
||||
"merge" => {
|
||||
let result = test.run_merge(db.clone());
|
||||
let result = test.run_merge(db_unwrapped.unwrap());
|
||||
if let Err(e) = result {
|
||||
println!("TEST MERGE ERROR FOR '{}': {}", test.description, e);
|
||||
failures.push(format!(
|
||||
@ -119,7 +136,7 @@ pub fn run_test_case(path: &str, suite_idx: usize, case_idx: usize) -> Result<()
|
||||
}
|
||||
}
|
||||
"query" => {
|
||||
let result = test.run_query(db.clone());
|
||||
let result = test.run_query(db_unwrapped.unwrap());
|
||||
if let Err(e) = result {
|
||||
println!("TEST QUERY ERROR FOR '{}': {}", test.description, e);
|
||||
failures.push(format!(
|
||||
|
||||
@ -35,21 +35,21 @@ fn default_action() -> String {
|
||||
}
|
||||
|
||||
impl Case {
|
||||
pub fn run_compile(&self, _db: Arc<Database>) -> Result<(), String> {
|
||||
let expected_success = self.expect.as_ref().map(|e| e.success).unwrap_or(false);
|
||||
pub fn run_compile(
|
||||
&self,
|
||||
db_res: &Result<Arc<Database>, crate::drop::Drop>,
|
||||
) -> Result<(), String> {
|
||||
let expect = match &self.expect {
|
||||
Some(e) => e,
|
||||
None => return Ok(()),
|
||||
};
|
||||
|
||||
// We assume db has already been setup and compiled successfully by runner.rs's `jspg_setup`
|
||||
// We just need to check if there are compilation errors vs expected success
|
||||
let got_success = true; // Setup ensures success unless setup fails, which runner handles
|
||||
let result = match db_res {
|
||||
Ok(_) => crate::drop::Drop::success(),
|
||||
Err(d) => d.clone(),
|
||||
};
|
||||
|
||||
if expected_success != got_success {
|
||||
return Err(format!(
|
||||
"Expected success: {}, Got: {}",
|
||||
expected_success, got_success
|
||||
));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
expect.assert_drop(&result)
|
||||
}
|
||||
|
||||
pub fn run_validate(&self, db: Arc<Database>) -> Result<(), String> {
|
||||
@ -57,8 +57,6 @@ impl Case {
|
||||
|
||||
let validator = Validator::new(db);
|
||||
|
||||
let expected_success = self.expect.as_ref().map(|e| e.success).unwrap_or(false);
|
||||
|
||||
let schema_id = &self.schema_id;
|
||||
if !validator.db.schemas.contains_key(schema_id) {
|
||||
return Err(format!(
|
||||
@ -70,19 +68,8 @@ impl Case {
|
||||
let test_data = self.data.clone().unwrap_or(Value::Null);
|
||||
let result = validator.validate(schema_id, &test_data);
|
||||
|
||||
let got_valid = result.errors.is_empty();
|
||||
|
||||
if got_valid != expected_success {
|
||||
let error_msg = if result.errors.is_empty() {
|
||||
"None".to_string()
|
||||
} else {
|
||||
format!("{:?}", result.errors)
|
||||
};
|
||||
|
||||
return Err(format!(
|
||||
"Expected: {}, Got: {}. Errors: {}",
|
||||
expected_success, got_valid, error_msg
|
||||
));
|
||||
if let Some(expect) = &self.expect {
|
||||
expect.assert_drop(&result)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -101,24 +88,16 @@ impl Case {
|
||||
let test_data = self.data.clone().unwrap_or(Value::Null);
|
||||
let result = merger.merge(&self.schema_id, test_data);
|
||||
|
||||
let expected_success = self.expect.as_ref().map(|e| e.success).unwrap_or(false);
|
||||
let got_success = result.errors.is_empty();
|
||||
|
||||
let error_msg = if result.errors.is_empty() {
|
||||
"None".to_string()
|
||||
} else {
|
||||
format!("{:?}", result.errors)
|
||||
};
|
||||
|
||||
let return_val = if expected_success != got_success {
|
||||
Err(format!(
|
||||
"Merge Expected: {}, Got: {}. Errors: {}",
|
||||
expected_success, got_success, error_msg
|
||||
))
|
||||
} else if let Some(expect) = &self.expect {
|
||||
let queries = db.executor.get_queries();
|
||||
expect.assert_pattern(&queries)?;
|
||||
expect.assert_sql(&queries)
|
||||
let return_val = if let Some(expect) = &self.expect {
|
||||
if let Err(e) = expect.assert_drop(&result) {
|
||||
Err(format!("Merge {}", e))
|
||||
} else if result.errors.is_empty() {
|
||||
// Only assert SQL if merge succeeded
|
||||
let queries = db.executor.get_queries();
|
||||
expect.assert_pattern(&queries).and_then(|_| expect.assert_sql(&queries))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
} else {
|
||||
Ok(())
|
||||
};
|
||||
@ -139,24 +118,15 @@ impl Case {
|
||||
|
||||
let result = queryer.query(&self.schema_id, self.filters.as_ref());
|
||||
|
||||
let expected_success = self.expect.as_ref().map(|e| e.success).unwrap_or(false);
|
||||
let got_success = result.errors.is_empty();
|
||||
|
||||
let error_msg = if result.errors.is_empty() {
|
||||
"None".to_string()
|
||||
} else {
|
||||
format!("{:?}", result.errors)
|
||||
};
|
||||
|
||||
let return_val = if expected_success != got_success {
|
||||
Err(format!(
|
||||
"Query Expected: {}, Got: {}. Errors: {}",
|
||||
expected_success, got_success, error_msg
|
||||
))
|
||||
} else if let Some(expect) = &self.expect {
|
||||
let queries = db.executor.get_queries();
|
||||
expect.assert_pattern(&queries)?;
|
||||
expect.assert_sql(&queries)
|
||||
let return_val = if let Some(expect) = &self.expect {
|
||||
if let Err(e) = expect.assert_drop(&result) {
|
||||
Err(format!("Query {}", e))
|
||||
} else if result.errors.is_empty() {
|
||||
let queries = db.executor.get_queries();
|
||||
expect.assert_pattern(&queries).and_then(|_| expect.assert_sql(&queries))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
} else {
|
||||
Ok(())
|
||||
};
|
||||
|
||||
87
src/tests/types/expect/drop.rs
Normal file
87
src/tests/types/expect/drop.rs
Normal file
@ -0,0 +1,87 @@
|
||||
use super::Expect;
|
||||
|
||||
impl Expect {
|
||||
pub fn assert_drop(&self, drop: &crate::drop::Drop) -> Result<(), String> {
|
||||
let got_success = drop.errors.is_empty();
|
||||
|
||||
if self.success != got_success {
|
||||
let mut err_msg = format!("Expected success: {}, Got: {}.", self.success, got_success);
|
||||
if !drop.errors.is_empty() {
|
||||
err_msg.push_str(&format!(" Actual Errors: {:?}", drop.errors));
|
||||
}
|
||||
return Err(err_msg);
|
||||
}
|
||||
|
||||
if !self.success {
|
||||
if let Some(expected_errors) = &self.errors {
|
||||
let actual_values: Vec<serde_json::Value> = drop.errors
|
||||
.iter()
|
||||
.map(|e| serde_json::to_value(e).unwrap())
|
||||
.collect();
|
||||
|
||||
for (i, expected_val) in expected_errors.iter().enumerate() {
|
||||
let mut matched = false;
|
||||
|
||||
for actual_val in &actual_values {
|
||||
if subset_match(expected_val, actual_val) {
|
||||
matched = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if !matched {
|
||||
return Err(format!(
|
||||
"Expected error {} was not found in actual errors.\nExpected subset: {}\nActual full errors: {:?}",
|
||||
i,
|
||||
serde_json::to_string_pretty(expected_val).unwrap(),
|
||||
drop.errors,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// Helper to check if `expected` is a structural subset of `actual`
|
||||
fn subset_match(expected: &serde_json::Value, actual: &serde_json::Value) -> bool {
|
||||
match (expected, actual) {
|
||||
(serde_json::Value::Object(exp_map), serde_json::Value::Object(act_map)) => {
|
||||
for (k, v) in exp_map {
|
||||
let mut act_v = act_map.get(k);
|
||||
|
||||
// Transparent fallback: if testing legacy flat "path", gracefully check inside "details"
|
||||
if act_v.is_none() && k == "path" {
|
||||
if let Some(serde_json::Value::Object(details)) = act_map.get("details") {
|
||||
act_v = details.get("path");
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(target) = act_v {
|
||||
if !subset_match(v, target) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
(serde_json::Value::Array(exp_arr), serde_json::Value::Array(act_arr)) => {
|
||||
// Basic check: array sizes and elements must match exactly in order
|
||||
if exp_arr.len() != act_arr.len() {
|
||||
return false;
|
||||
}
|
||||
for (e, a) in exp_arr.iter().zip(act_arr.iter()) {
|
||||
if !subset_match(e, a) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
// For primitives, exact match
|
||||
(e, a) => e == a,
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
pub mod pattern;
|
||||
pub mod sql;
|
||||
pub mod drop;
|
||||
|
||||
use serde::Deserialize;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user