use serde_json::Value; use pgrx::JsonB; // Simple test helpers for cleaner test code pub fn assert_success(result: &JsonB) { let json = &result.0; if !json.get("response").is_some() || json.get("errors").is_some() { let pretty = serde_json::to_string_pretty(json).unwrap_or_else(|_| format!("{:?}", json)); panic!("Expected success but got:\n{}", pretty); } } pub fn assert_failure(result: &JsonB) { let json = &result.0; if json.get("response").is_some() || !json.get("errors").is_some() { let pretty = serde_json::to_string_pretty(json).unwrap_or_else(|_| format!("{:?}", json)); panic!("Expected failure but got:\n{}", pretty); } } pub fn assert_error_count(result: &JsonB, expected_count: usize) { assert_failure(result); let errors = get_errors(result); if errors.len() != expected_count { let pretty = serde_json::to_string_pretty(&result.0).unwrap_or_else(|_| format!("{:?}", result.0)); panic!("Expected {} errors, got {}:\n{}", expected_count, errors.len(), pretty); } } pub fn get_errors(result: &JsonB) -> &Vec { result.0["errors"].as_array().expect("errors should be an array") } pub fn has_error_with_code(result: &JsonB, code: &str) -> bool { get_errors(result).iter().any(|e| e["code"] == code) } pub fn has_error_with_code_and_path(result: &JsonB, code: &str, path: &str) -> bool { get_errors(result).iter().any(|e| e["code"] == code && e["details"]["path"] == path) } pub fn assert_has_error(result: &JsonB, code: &str, path: &str) { if !has_error_with_code_and_path(result, code, path) { let pretty = serde_json::to_string_pretty(&result.0).unwrap_or_else(|_| format!("{:?}", result.0)); panic!("Expected error with code='{}' and path='{}' but not found:\n{}", code, path, pretty); } } pub fn find_error_with_code<'a>(result: &'a JsonB, code: &str) -> &'a Value { get_errors(result).iter().find(|e| e["code"] == code) .unwrap_or_else(|| panic!("No error found with code '{}'", code)) } pub fn find_error_with_code_and_path<'a>(result: &'a JsonB, code: &str, path: &str) -> &'a Value { get_errors(result).iter().find(|e| e["code"] == code && e["details"]["path"] == path) .unwrap_or_else(|| panic!("No error found with code '{}' and path '{}'", code, path)) } pub fn assert_error_detail(error: &Value, detail_key: &str, expected_value: &str) { let actual = error["details"][detail_key].as_str() .unwrap_or_else(|| panic!("Error detail '{}' is not a string", detail_key)); assert_eq!(actual, expected_value, "Error detail '{}' mismatch", detail_key); } // Additional convenience helpers for common patterns pub fn assert_error_message_contains(error: &Value, substring: &str) { let message = error["message"].as_str().expect("error should have message"); assert!(message.contains(substring), "Expected message to contain '{}', got '{}'", substring, message); } pub fn assert_error_cause_json(error: &Value, expected_cause: &Value) { let cause = &error["details"]["cause"]; assert!(cause.is_object(), "cause should be JSON object"); assert_eq!(cause, expected_cause, "cause mismatch"); } pub fn assert_error_context(error: &Value, expected_context: &Value) { assert_eq!(&error["details"]["context"], expected_context, "context mismatch"); } pub fn jsonb(val: Value) -> JsonB { JsonB(val) }