|
1 | 1 | use std::collections::HashSet; |
2 | 2 |
|
3 | | -use vespertide_core::{IndexDef, TableConstraint, TableDef}; |
| 3 | +use vespertide_core::{IndexDef, MigrationAction, MigrationPlan, TableConstraint, TableDef}; |
4 | 4 |
|
5 | 5 | use crate::error::PlannerError; |
6 | 6 |
|
@@ -514,4 +514,130 @@ mod tests { |
514 | 514 | } |
515 | 515 | } |
516 | 516 | } |
| 517 | + |
| 518 | + #[test] |
| 519 | + fn validate_migration_plan_missing_fill_with() { |
| 520 | + use vespertide_core::{ColumnDef, ColumnType, MigrationAction, MigrationPlan}; |
| 521 | + |
| 522 | + let plan = MigrationPlan { |
| 523 | + comment: None, |
| 524 | + created_at: None, |
| 525 | + version: 1, |
| 526 | + actions: vec![MigrationAction::AddColumn { |
| 527 | + table: "users".into(), |
| 528 | + column: ColumnDef { |
| 529 | + name: "email".into(), |
| 530 | + r#type: ColumnType::Text, |
| 531 | + nullable: false, |
| 532 | + default: None, |
| 533 | + }, |
| 534 | + fill_with: None, |
| 535 | + }], |
| 536 | + }; |
| 537 | + |
| 538 | + let result = validate_migration_plan(&plan); |
| 539 | + assert!(result.is_err()); |
| 540 | + match result.unwrap_err() { |
| 541 | + PlannerError::MissingFillWith(table, column) => { |
| 542 | + assert_eq!(table, "users"); |
| 543 | + assert_eq!(column, "email"); |
| 544 | + } |
| 545 | + _ => panic!("expected MissingFillWith error"), |
| 546 | + } |
| 547 | + } |
| 548 | + |
| 549 | + #[test] |
| 550 | + fn validate_migration_plan_with_fill_with() { |
| 551 | + use vespertide_core::{ColumnDef, ColumnType, MigrationAction, MigrationPlan}; |
| 552 | + |
| 553 | + let plan = MigrationPlan { |
| 554 | + comment: None, |
| 555 | + created_at: None, |
| 556 | + version: 1, |
| 557 | + actions: vec![MigrationAction::AddColumn { |
| 558 | + table: "users".into(), |
| 559 | + column: ColumnDef { |
| 560 | + name: "email".into(), |
| 561 | + r#type: ColumnType::Text, |
| 562 | + nullable: false, |
| 563 | + default: None, |
| 564 | + }, |
| 565 | + fill_with : Some("[email protected]".into ()), |
| 566 | + }], |
| 567 | + }; |
| 568 | + |
| 569 | + let result = validate_migration_plan(&plan); |
| 570 | + assert!(result.is_ok()); |
| 571 | + } |
| 572 | + |
| 573 | + #[test] |
| 574 | + fn validate_migration_plan_nullable_column() { |
| 575 | + use vespertide_core::{ColumnDef, ColumnType, MigrationAction, MigrationPlan}; |
| 576 | + |
| 577 | + let plan = MigrationPlan { |
| 578 | + comment: None, |
| 579 | + created_at: None, |
| 580 | + version: 1, |
| 581 | + actions: vec![MigrationAction::AddColumn { |
| 582 | + table: "users".into(), |
| 583 | + column: ColumnDef { |
| 584 | + name: "email".into(), |
| 585 | + r#type: ColumnType::Text, |
| 586 | + nullable: true, |
| 587 | + default: None, |
| 588 | + }, |
| 589 | + fill_with: None, |
| 590 | + }], |
| 591 | + }; |
| 592 | + |
| 593 | + let result = validate_migration_plan(&plan); |
| 594 | + assert!(result.is_ok()); |
| 595 | + } |
| 596 | + |
| 597 | + #[test] |
| 598 | + fn validate_migration_plan_with_default() { |
| 599 | + use vespertide_core::{ColumnDef, ColumnType, MigrationAction, MigrationPlan}; |
| 600 | + |
| 601 | + let plan = MigrationPlan { |
| 602 | + comment: None, |
| 603 | + created_at: None, |
| 604 | + version: 1, |
| 605 | + actions: vec![MigrationAction::AddColumn { |
| 606 | + table: "users".into(), |
| 607 | + column: ColumnDef { |
| 608 | + name: "email".into(), |
| 609 | + r#type: ColumnType::Text, |
| 610 | + nullable: false, |
| 611 | + default: Some("[email protected]".into ()), |
| 612 | + }, |
| 613 | + fill_with: None, |
| 614 | + }], |
| 615 | + }; |
| 616 | + |
| 617 | + let result = validate_migration_plan(&plan); |
| 618 | + assert!(result.is_ok()); |
| 619 | + } |
| 620 | +} |
| 621 | + |
| 622 | +/// Validate a migration plan for correctness. |
| 623 | +/// Checks for: |
| 624 | +/// - AddColumn actions with NOT NULL columns without default must have fill_with |
| 625 | +pub fn validate_migration_plan(plan: &MigrationPlan) -> Result<(), PlannerError> { |
| 626 | + for action in &plan.actions { |
| 627 | + if let MigrationAction::AddColumn { |
| 628 | + table, |
| 629 | + column, |
| 630 | + fill_with, |
| 631 | + } = action |
| 632 | + { |
| 633 | + // If column is NOT NULL and has no default, fill_with is required |
| 634 | + if !column.nullable && column.default.is_none() && fill_with.is_none() { |
| 635 | + return Err(PlannerError::MissingFillWith( |
| 636 | + table.clone(), |
| 637 | + column.name.clone(), |
| 638 | + )); |
| 639 | + } |
| 640 | + } |
| 641 | + } |
| 642 | + Ok(()) |
517 | 643 | } |
0 commit comments