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