coverage.rs (23119B)
1 #[cfg(feature = "native")] 2 use radroots_sql_core::SqlxSqliteExecutor; 3 use radroots_sql_core::error::SqlError; 4 use radroots_sql_core::migrations::{Migration, migrations_run_all_down, migrations_run_all_up}; 5 use radroots_sql_core::utils::{ 6 build_insert_query_with_meta, build_select_query_with_meta, build_where_clause_eq, parse_json, 7 parse_query_value, time_created_on, to_db_bind_value, to_object_map, to_params_json, 8 to_partial_object_map, uuidv4, with_transaction, 9 }; 10 use radroots_sql_core::{ExecOutcome, SqlExecutor}; 11 use serde::ser::{SerializeMap, SerializeSeq}; 12 use serde::{Deserialize, Serialize, Serializer}; 13 use serde_json::{Map, Value, json}; 14 use std::collections::BTreeSet; 15 use std::sync::Mutex; 16 17 #[derive(Debug, Clone, PartialEq)] 18 struct ExecutorSnapshot { 19 exec_sql: Vec<String>, 20 begin_count: usize, 21 commit_count: usize, 22 rollback_count: usize, 23 applied: BTreeSet<String>, 24 } 25 26 #[derive(Debug, Clone, Default)] 27 struct ExecutorState { 28 exec_sql: Vec<String>, 29 begin_count: usize, 30 commit_count: usize, 31 rollback_count: usize, 32 applied: BTreeSet<String>, 33 fail_begin: bool, 34 fail_commit: bool, 35 fail_rollback: bool, 36 fail_sql_contains: Option<String>, 37 query_override: Option<Result<String, SqlError>>, 38 } 39 40 #[derive(Debug, Default)] 41 struct MockExecutor { 42 state: Mutex<ExecutorState>, 43 } 44 45 impl MockExecutor { 46 fn new() -> Self { 47 Self::default() 48 } 49 50 fn with_fail_sql(mut self, needle: &str) -> Self { 51 let state = self.state.get_mut().expect("state"); 52 state.fail_sql_contains = Some(needle.to_string()); 53 self 54 } 55 56 fn set_fail_begin(&self, value: bool) { 57 let mut state = self.state.lock().expect("state"); 58 state.fail_begin = value; 59 } 60 61 fn set_fail_commit(&self, value: bool) { 62 let mut state = self.state.lock().expect("state"); 63 state.fail_commit = value; 64 } 65 66 fn set_fail_rollback(&self, value: bool) { 67 let mut state = self.state.lock().expect("state"); 68 state.fail_rollback = value; 69 } 70 71 fn set_query_override(&self, value: Option<Result<String, SqlError>>) { 72 let mut state = self.state.lock().expect("state"); 73 state.query_override = value; 74 } 75 76 fn mark_applied(&self, name: &str) { 77 let mut state = self.state.lock().expect("state"); 78 state.applied.insert(name.to_string()); 79 } 80 81 fn snapshot(&self) -> ExecutorSnapshot { 82 let state = self.state.lock().expect("state"); 83 ExecutorSnapshot { 84 exec_sql: state.exec_sql.clone(), 85 begin_count: state.begin_count, 86 commit_count: state.commit_count, 87 rollback_count: state.rollback_count, 88 applied: state.applied.clone(), 89 } 90 } 91 } 92 93 impl SqlExecutor for MockExecutor { 94 fn exec(&self, sql: &str, params_json: &str) -> Result<ExecOutcome, SqlError> { 95 let mut state = self.state.lock().expect("state"); 96 state.exec_sql.push(sql.to_string()); 97 if let Some(needle) = &state.fail_sql_contains 98 && sql.contains(needle) 99 { 100 return Err(SqlError::InvalidQuery(sql.to_string())); 101 } 102 103 if sql.contains("insert or ignore into __migrations(name)") { 104 let params: Vec<String> = serde_json::from_str(params_json).map_err(SqlError::from)?; 105 if let Some(name) = params.first() { 106 state.applied.insert(name.clone()); 107 } 108 } 109 110 if sql.contains("delete from __migrations where name = ?") { 111 let params: Vec<String> = serde_json::from_str(params_json).map_err(SqlError::from)?; 112 if let Some(name) = params.first() { 113 state.applied.remove(name); 114 } 115 } 116 117 Ok(ExecOutcome { 118 changes: 1, 119 last_insert_id: 11, 120 }) 121 } 122 123 fn query_raw(&self, _sql: &str, params_json: &str) -> Result<String, SqlError> { 124 let state = self.state.lock().expect("state"); 125 if let Some(override_value) = &state.query_override { 126 return override_value.clone(); 127 } 128 let params: Vec<String> = serde_json::from_str(params_json).map_err(SqlError::from)?; 129 let Some(name) = params.first() else { 130 return Ok(String::new()); 131 }; 132 if state.applied.contains(name) { 133 Ok(json!([{ "applied": 1 }]).to_string()) 134 } else { 135 Ok("[]".to_string()) 136 } 137 } 138 139 fn begin(&self) -> Result<(), SqlError> { 140 let mut state = self.state.lock().expect("state"); 141 state.begin_count += 1; 142 if state.fail_begin { 143 return Err(SqlError::Internal); 144 } 145 Ok(()) 146 } 147 148 fn commit(&self) -> Result<(), SqlError> { 149 let mut state = self.state.lock().expect("state"); 150 state.commit_count += 1; 151 if state.fail_commit { 152 return Err(SqlError::Internal); 153 } 154 Ok(()) 155 } 156 157 fn rollback(&self) -> Result<(), SqlError> { 158 let mut state = self.state.lock().expect("state"); 159 state.rollback_count += 1; 160 if state.fail_rollback { 161 return Err(SqlError::Internal); 162 } 163 Ok(()) 164 } 165 } 166 167 #[test] 168 fn sql_executor_reference_impl_forwards_all_methods() { 169 let exec = MockExecutor::new(); 170 let exec_ref = &exec; 171 172 let exec_result = <&MockExecutor as SqlExecutor>::exec(&exec_ref, "select 1", "[]") 173 .expect("reference exec should forward"); 174 assert_eq!(exec_result.changes, 1); 175 176 let query_result = <&MockExecutor as SqlExecutor>::query_raw(&exec_ref, "select 1", "[]") 177 .expect("reference query should forward"); 178 assert_eq!(query_result, String::new()); 179 180 <&MockExecutor as SqlExecutor>::begin(&exec_ref).expect("reference begin should forward"); 181 <&MockExecutor as SqlExecutor>::commit(&exec_ref).expect("reference commit should forward"); 182 <&MockExecutor as SqlExecutor>::rollback(&exec_ref).expect("reference rollback should forward"); 183 184 let snapshot = exec.snapshot(); 185 assert_eq!(snapshot.begin_count, 1); 186 assert_eq!(snapshot.commit_count, 1); 187 assert_eq!(snapshot.rollback_count, 1); 188 assert!(snapshot.exec_sql.iter().any(|sql| sql == "select 1")); 189 } 190 191 #[cfg(feature = "native")] 192 #[test] 193 fn sqlx_sqlite_executor_exec_runs_multi_statement_batches_without_params() { 194 let exec = SqlxSqliteExecutor::open_memory().expect("open sqlite memory"); 195 196 let outcome = exec 197 .exec( 198 "create table demo (id integer primary key, name text not null);\ncreate unique index demo_name_idx on demo(name);", 199 "[]", 200 ) 201 .expect("multi-statement batch should succeed"); 202 assert_eq!(outcome.changes, 0); 203 204 let insert = exec 205 .exec("insert into demo(name) values ('alpha')", "[]") 206 .expect("insert should succeed"); 207 assert_eq!(insert.changes, 1); 208 209 let index_rows = exec 210 .query_raw( 211 "select name from sqlite_master where type = 'index' and name = 'demo_name_idx'", 212 "[]", 213 ) 214 .expect("index metadata query should succeed"); 215 assert_eq!(index_rows, json!([{ "name": "demo_name_idx" }]).to_string()); 216 } 217 218 #[derive(Debug, Serialize, Deserialize, PartialEq)] 219 struct Payload { 220 id: String, 221 amount: Option<i64>, 222 } 223 224 #[derive(Debug, Clone, Copy)] 225 enum FilterMode { 226 Object, 227 Array, 228 Error, 229 } 230 231 #[derive(Debug, Clone)] 232 struct FilterInput { 233 mode: FilterMode, 234 id: Option<String>, 235 amount: Option<i64>, 236 } 237 238 impl FilterInput { 239 fn object(id: Option<&str>, amount: Option<i64>) -> Self { 240 Self { 241 mode: FilterMode::Object, 242 id: id.map(str::to_string), 243 amount, 244 } 245 } 246 247 fn array() -> Self { 248 Self { 249 mode: FilterMode::Array, 250 id: None, 251 amount: None, 252 } 253 } 254 255 fn error() -> Self { 256 Self { 257 mode: FilterMode::Error, 258 id: None, 259 amount: None, 260 } 261 } 262 } 263 264 impl Serialize for FilterInput { 265 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 266 where 267 S: Serializer, 268 { 269 match self.mode { 270 FilterMode::Error => Err(serde::ser::Error::custom("serialize fail")), 271 FilterMode::Array => { 272 let mut seq = serializer.serialize_seq(Some(2))?; 273 seq.serialize_element(&1)?; 274 seq.serialize_element(&2)?; 275 seq.end() 276 } 277 FilterMode::Object => { 278 let mut map = serializer.serialize_map(Some(2))?; 279 map.serialize_entry("id", &self.id)?; 280 map.serialize_entry("amount", &self.amount)?; 281 map.end() 282 } 283 } 284 } 285 } 286 287 #[test] 288 fn sql_error_code_and_to_json_cover_all_variants() { 289 let errors = vec![ 290 SqlError::InvalidArgument("a".to_string()), 291 SqlError::NotFound("b".to_string()), 292 SqlError::SerializationError("c".to_string()), 293 SqlError::InvalidQuery("d".to_string()), 294 SqlError::Internal, 295 SqlError::UnsupportedPlatform, 296 ]; 297 let expected = vec![ 298 "ERR_INVALID_ARGUMENT", 299 "ERR_NOT_FOUND", 300 "ERR_SERIALIZATION", 301 "ERR_INVALID_QUERY", 302 "ERR_INTERNAL", 303 "ERR_UNSUPPORTED_PLATFORM", 304 ]; 305 306 for (err, code) in errors.into_iter().zip(expected) { 307 assert_eq!(err.code(), code); 308 let json_value = err.to_json(); 309 assert_eq!(json_value.get("code").and_then(|v| v.as_str()), Some(code)); 310 assert!(json_value.get("message").and_then(|v| v.as_str()).is_some()); 311 } 312 } 313 314 #[test] 315 fn parse_json_and_identifiers_work() { 316 let parsed: Payload = parse_json(r#"{"id":"p1","amount":3}"#).expect("payload should parse"); 317 assert_eq!( 318 parsed, 319 Payload { 320 id: "p1".to_string(), 321 amount: Some(3), 322 } 323 ); 324 325 let err = parse_json::<Payload>("not-json").expect_err("invalid json should fail"); 326 assert!(matches!(err, SqlError::SerializationError(_))); 327 328 let first = uuidv4(); 329 let second = uuidv4(); 330 assert_ne!(first, second); 331 assert_eq!(first.len(), 36); 332 333 let created_on = time_created_on(); 334 assert!(created_on.ends_with('Z')); 335 } 336 337 #[test] 338 fn object_map_helpers_cover_success_and_error_paths() { 339 let payload = FilterInput::object(Some("row-1"), Some(8)); 340 let object = to_object_map(payload).expect("to object map"); 341 assert_eq!(object.get("id"), Some(&Value::String("row-1".to_string()))); 342 343 let err = to_object_map(FilterInput::array()).expect_err("array should fail"); 344 assert!(matches!(err, SqlError::SerializationError(_))); 345 let err = to_object_map(FilterInput::error()).expect_err("serialize fail should surface"); 346 assert!(matches!(err, SqlError::SerializationError(_))); 347 348 let partial = 349 to_partial_object_map(FilterInput::object(Some("row-2"), None)).expect("to partial map"); 350 assert_eq!(partial.get("id"), Some(&Value::String("row-2".to_string()))); 351 assert!(!partial.contains_key("amount")); 352 353 let err_partial = to_partial_object_map(FilterInput::array()).expect_err("array should fail"); 354 assert!(matches!(err_partial, SqlError::SerializationError(_))); 355 let err_partial = 356 to_partial_object_map(FilterInput::error()).expect_err("serialize fail should surface"); 357 assert!(matches!(err_partial, SqlError::SerializationError(_))); 358 } 359 360 #[test] 361 fn bind_value_helpers_cover_all_value_paths() { 362 assert_eq!(to_db_bind_value(&Value::Bool(true)), Value::from(1)); 363 assert_eq!(to_db_bind_value(&Value::Bool(false)), Value::from(0)); 364 assert_eq!(to_db_bind_value(&json!(5_i64)), Value::from(5_u32)); 365 assert_eq!(to_db_bind_value(&json!(-5_i64)), Value::from(-5_i64)); 366 assert_eq!(to_db_bind_value(&json!(7.25_f64)), Value::from(7.25_f64)); 367 assert_eq!( 368 to_db_bind_value(&json!(u32::MAX as u64)), 369 Value::from(u32::MAX) 370 ); 371 assert_eq!( 372 to_db_bind_value(&json!((u32::MAX as u64) + 1)), 373 Value::from((u32::MAX as u64) + 1) 374 ); 375 assert_eq!( 376 to_db_bind_value(&Value::String("x".to_string())), 377 Value::String("x".to_string()) 378 ); 379 assert_eq!(to_db_bind_value(&json!({"x":1})), Value::Null); 380 } 381 382 #[test] 383 fn query_builder_helpers_cover_empty_and_non_empty_paths() { 384 let empty_filter = FilterInput::object(None, None); 385 let (where_empty, binds_empty) = build_where_clause_eq(&empty_filter).expect("where empty"); 386 assert_eq!(where_empty, ""); 387 assert!(binds_empty.is_empty()); 388 389 let err_filter = FilterInput::error(); 390 let err = build_where_clause_eq(&err_filter).expect_err("where error"); 391 assert!(matches!(err, SqlError::SerializationError(_))); 392 393 let mut fields = Map::new(); 394 fields.insert("name".to_string(), Value::String("alpha".to_string())); 395 fields.insert("weight".to_string(), Value::from(12)); 396 let (insert_sql, insert_binds) = build_insert_query_with_meta( 397 "items", 398 &[("uuid", Value::String("u-1".to_string()))], 399 &fields, 400 ); 401 assert!(insert_sql.contains("INSERT INTO items")); 402 assert_eq!(insert_binds.len(), 3); 403 404 let (select_all, select_binds_all) = build_select_query_with_meta::<FilterInput>("items", None); 405 assert_eq!(select_all, "SELECT * FROM items;"); 406 assert!(select_binds_all.is_empty()); 407 408 let filter = FilterInput::object(Some("row-3"), Some(10)); 409 let (select_filtered, select_binds_filtered) = 410 build_select_query_with_meta("items", Some(&filter)); 411 assert!(select_filtered.contains(" WHERE ")); 412 assert_eq!(select_binds_filtered.len(), 2); 413 414 let array_filter = FilterInput::array(); 415 let (select_error_path, select_error_binds) = 416 build_select_query_with_meta("items", Some(&array_filter)); 417 assert_eq!(select_error_path, "SELECT * FROM items;"); 418 assert!(select_error_binds.is_empty()); 419 } 420 421 #[test] 422 fn parse_query_and_params_helpers_cover_success_and_error_paths() { 423 assert_eq!( 424 parse_query_value(&Value::Bool(true)).expect("bool true"), 425 json!(1) 426 ); 427 assert_eq!( 428 parse_query_value(&Value::Bool(false)).expect("bool false"), 429 json!(0) 430 ); 431 assert_eq!(parse_query_value(&Value::Null).expect("null"), Value::Null); 432 assert_eq!(parse_query_value(&json!(7)).expect("number"), json!(7)); 433 assert_eq!( 434 parse_query_value(&Value::String("ok".to_string())).expect("string"), 435 Value::String("ok".to_string()) 436 ); 437 438 let err = parse_query_value(&json!({"bad": true})).expect_err("object should fail"); 439 assert!(matches!(err, SqlError::InvalidArgument(_))); 440 441 let params_json = to_params_json(FilterInput::object(Some("a"), Some(1))).expect("params json"); 442 let params_value: Value = serde_json::from_str(¶ms_json).expect("params json parse"); 443 assert_eq!(params_value, json!({"id":"a","amount":1})); 444 445 let err_params = 446 to_params_json(FilterInput::error()).expect_err("serialize fail should surface"); 447 assert!(matches!(err_params, SqlError::SerializationError(_))); 448 } 449 450 #[test] 451 fn with_transaction_covers_commit_and_rollback_paths() { 452 let ok_exec = MockExecutor::new(); 453 let value = with_transaction(&ok_exec, || Ok::<_, SqlError>(41)).expect("tx should commit"); 454 assert_eq!(value, 41); 455 let ok_snapshot = ok_exec.snapshot(); 456 assert_eq!(ok_snapshot.begin_count, 1); 457 assert_eq!(ok_snapshot.commit_count, 1); 458 assert_eq!(ok_snapshot.rollback_count, 0); 459 460 let err_exec = MockExecutor::new(); 461 let err = with_transaction(&err_exec, || { 462 Err::<i32, SqlError>(SqlError::InvalidQuery("bad".to_string())) 463 }) 464 .expect_err("tx should rollback"); 465 assert!(matches!(err, SqlError::InvalidQuery(_))); 466 let err_snapshot = err_exec.snapshot(); 467 assert_eq!(err_snapshot.begin_count, 1); 468 assert_eq!(err_snapshot.commit_count, 0); 469 assert_eq!(err_snapshot.rollback_count, 1); 470 471 let rollback_err_exec = MockExecutor::new(); 472 rollback_err_exec.set_fail_rollback(true); 473 let _ = with_transaction(&rollback_err_exec, || { 474 Err::<i32, SqlError>(SqlError::InvalidQuery("err".to_string())) 475 }) 476 .expect_err("tx should still return original error"); 477 let rollback_snapshot = rollback_err_exec.snapshot(); 478 assert_eq!(rollback_snapshot.rollback_count, 1); 479 } 480 481 #[test] 482 fn with_transaction_surfaces_begin_error() { 483 let exec = MockExecutor::new(); 484 exec.set_fail_begin(true); 485 let err = with_transaction(&exec, || Ok::<_, SqlError>(1)).expect_err("begin should fail"); 486 assert!(matches!(err, SqlError::Internal)); 487 } 488 489 #[test] 490 fn with_transaction_surfaces_commit_error() { 491 let exec = MockExecutor::new(); 492 exec.set_fail_commit(true); 493 let err = with_transaction(&exec, || Ok::<_, SqlError>(1)).expect_err("commit should fail"); 494 assert!(matches!(err, SqlError::Internal)); 495 } 496 497 fn sample_migrations() -> Vec<Migration> { 498 vec![ 499 Migration { 500 name: "001", 501 up_sql: "create table m1(x integer)", 502 down_sql: "drop table m1", 503 }, 504 Migration { 505 name: "002", 506 up_sql: "create table m2(y integer)", 507 down_sql: "drop table m2", 508 }, 509 ] 510 } 511 512 #[test] 513 fn migrations_run_all_up_applies_pending_and_skips_existing() { 514 let exec = MockExecutor::new(); 515 let migrations = sample_migrations(); 516 517 migrations_run_all_up(&exec, &migrations).expect("first run up"); 518 migrations_run_all_up(&exec, &migrations).expect("second run up"); 519 520 let snapshot = exec.snapshot(); 521 assert!(snapshot.applied.contains("001")); 522 assert!(snapshot.applied.contains("002")); 523 let up_calls = snapshot 524 .exec_sql 525 .iter() 526 .filter(|sql| sql.starts_with("create table m")) 527 .count(); 528 assert_eq!(up_calls, 2); 529 } 530 531 #[test] 532 fn migrations_run_all_up_surfaces_ensure_table_error() { 533 let exec = MockExecutor::new().with_fail_sql("create table if not exists __migrations"); 534 let migrations = sample_migrations(); 535 let err = migrations_run_all_up(&exec, &migrations).expect_err("ensure table should fail"); 536 assert!(matches!(err, SqlError::InvalidQuery(_))); 537 } 538 539 #[test] 540 fn migrations_run_all_up_surfaces_begin_error() { 541 let exec = MockExecutor::new(); 542 exec.set_fail_begin(true); 543 let migrations = sample_migrations(); 544 let err = migrations_run_all_up(&exec, &migrations).expect_err("begin should fail"); 545 assert!(matches!(err, SqlError::Internal)); 546 } 547 548 #[test] 549 fn migrations_run_all_up_surfaces_commit_error() { 550 let exec = MockExecutor::new(); 551 exec.set_fail_commit(true); 552 let migrations = sample_migrations(); 553 let err = migrations_run_all_up(&exec, &migrations).expect_err("commit should fail"); 554 assert!(matches!(err, SqlError::Internal)); 555 } 556 557 #[test] 558 fn migrations_run_all_up_surfaces_mark_applied_error() { 559 let exec = MockExecutor::new().with_fail_sql("insert or ignore into __migrations"); 560 let migrations = sample_migrations(); 561 let err = migrations_run_all_up(&exec, &migrations).expect_err("mark applied should fail"); 562 assert!(matches!(err, SqlError::InvalidQuery(_))); 563 } 564 565 #[test] 566 fn migrations_run_all_up_rolls_back_on_failure() { 567 let exec = MockExecutor::new().with_fail_sql("create table m2"); 568 let migrations = sample_migrations(); 569 570 let err = migrations_run_all_up(&exec, &migrations).expect_err("second migration should fail"); 571 assert!(matches!(err, SqlError::InvalidQuery(_))); 572 573 let snapshot = exec.snapshot(); 574 assert!(snapshot.applied.contains("001")); 575 assert!(!snapshot.applied.contains("002")); 576 assert!(snapshot.rollback_count >= 1); 577 } 578 579 #[test] 580 fn migrations_run_all_up_surfaces_query_parse_error() { 581 let exec = MockExecutor::new(); 582 exec.set_query_override(Some(Ok("not-json".to_string()))); 583 let migrations = sample_migrations(); 584 let err = migrations_run_all_up(&exec, &migrations).expect_err("query parse should fail"); 585 assert!(matches!(err, SqlError::SerializationError(_))); 586 } 587 588 #[test] 589 fn migrations_run_all_up_surfaces_query_error() { 590 let exec = MockExecutor::new(); 591 exec.set_query_override(Some(Err(SqlError::Internal))); 592 let migrations = sample_migrations(); 593 let err = migrations_run_all_up(&exec, &migrations).expect_err("query should fail"); 594 assert!(matches!(err, SqlError::Internal)); 595 } 596 597 #[test] 598 fn migrations_run_all_up_handles_empty_query_rows() { 599 let exec = MockExecutor::new(); 600 exec.set_query_override(Some(Ok(String::new()))); 601 let migrations = sample_migrations(); 602 migrations_run_all_up(&exec, &migrations).expect("empty rows should count as not applied"); 603 let snapshot = exec.snapshot(); 604 assert!(snapshot.applied.contains("001")); 605 assert!(snapshot.applied.contains("002")); 606 } 607 608 #[test] 609 fn migrations_run_all_down_reverses_and_commits() { 610 let exec = MockExecutor::new(); 611 exec.mark_applied("001"); 612 exec.mark_applied("002"); 613 614 let migrations = sample_migrations(); 615 migrations_run_all_down(&exec, &migrations).expect("run down"); 616 617 let snapshot = exec.snapshot(); 618 assert!(!snapshot.applied.contains("001")); 619 assert!(!snapshot.applied.contains("002")); 620 assert!(snapshot.commit_count >= 1); 621 let down_calls: Vec<&String> = snapshot 622 .exec_sql 623 .iter() 624 .filter(|sql| sql.starts_with("drop table")) 625 .collect(); 626 assert_eq!(down_calls.len(), 2); 627 assert_eq!(down_calls[0].as_str(), "drop table m2"); 628 assert_eq!(down_calls[1].as_str(), "drop table m1"); 629 } 630 631 #[test] 632 fn migrations_run_all_down_surfaces_ensure_table_error() { 633 let exec = MockExecutor::new().with_fail_sql("create table if not exists __migrations"); 634 let migrations = sample_migrations(); 635 let err = migrations_run_all_down(&exec, &migrations).expect_err("ensure table should fail"); 636 assert!(matches!(err, SqlError::InvalidQuery(_))); 637 } 638 639 #[test] 640 fn migrations_run_all_down_surfaces_delete_error() { 641 let exec = MockExecutor::new().with_fail_sql("delete from __migrations"); 642 let migrations = sample_migrations(); 643 let err = migrations_run_all_down(&exec, &migrations).expect_err("delete should fail"); 644 assert!(matches!(err, SqlError::InvalidQuery(_))); 645 } 646 647 #[test] 648 fn migrations_run_all_down_surfaces_down_sql_error() { 649 let exec = MockExecutor::new().with_fail_sql("drop table m2"); 650 let migrations = sample_migrations(); 651 let err = migrations_run_all_down(&exec, &migrations).expect_err("down sql should fail"); 652 assert!(matches!(err, SqlError::InvalidQuery(_))); 653 } 654 655 #[test] 656 fn migrations_run_all_down_surfaces_begin_error() { 657 let exec = MockExecutor::new(); 658 exec.set_fail_begin(true); 659 let migrations = sample_migrations(); 660 let err = migrations_run_all_down(&exec, &migrations).expect_err("begin should fail"); 661 assert!(matches!(err, SqlError::Internal)); 662 } 663 664 #[test] 665 fn migrations_run_all_down_surfaces_commit_error() { 666 let exec = MockExecutor::new(); 667 exec.set_fail_commit(true); 668 let migrations = sample_migrations(); 669 let err = migrations_run_all_down(&exec, &migrations).expect_err("commit should fail"); 670 assert!(matches!(err, SqlError::Internal)); 671 }