157 lines
4.0 KiB
Rust
157 lines
4.0 KiB
Rust
use super::{alter, history, schema};
|
|
use crate::database::{Column, Kind, Migration, Plan, Sql, Statement};
|
|
use std::collections::BTreeMap;
|
|
|
|
mod live;
|
|
mod registry;
|
|
|
|
const EMPTY: Plan = Plan {
|
|
create: Sql::same(""),
|
|
indexes: Sql::same(""),
|
|
postgres_columns: &[],
|
|
renames: &[],
|
|
migrations: &[],
|
|
seeds: &[],
|
|
seed_revision: "empty",
|
|
};
|
|
|
|
const fn column(name: &'static str, kind: &'static str) -> Column {
|
|
Column {
|
|
name,
|
|
kind,
|
|
default: None,
|
|
nullable: false,
|
|
generated: false,
|
|
constraints: &[],
|
|
}
|
|
}
|
|
|
|
const fn step(
|
|
name: &'static str,
|
|
revision: &'static str,
|
|
drops: &'static [&'static str],
|
|
) -> Migration {
|
|
Migration {
|
|
name,
|
|
revision,
|
|
drops,
|
|
before: &[],
|
|
after: &[],
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn migration_history_is_an_immutable_contiguous_prefix() {
|
|
let plan = const {
|
|
Plan {
|
|
migrations: &[step("first", "one", &[]), step("second", "two", &[])],
|
|
..EMPTY
|
|
}
|
|
};
|
|
assert_eq!(history::validate(&[], &plan).unwrap(), 0);
|
|
assert_eq!(
|
|
history::validate(&[(0, "first".into(), "one".into())], &plan).unwrap(),
|
|
1
|
|
);
|
|
for records in [
|
|
vec![(1, "first".into(), "one".into())],
|
|
vec![(0, "second".into(), "two".into())],
|
|
vec![(0, "first".into(), "modified".into())],
|
|
vec![
|
|
(0, "first".into(), "one".into()),
|
|
(2, "second".into(), "two".into()),
|
|
],
|
|
] {
|
|
assert_eq!(
|
|
history::validate(&records, &plan).unwrap_err().kind,
|
|
Kind::Migration
|
|
);
|
|
}
|
|
assert_eq!(
|
|
history::validate(&[(0, "first".into(), "one".into())], &EMPTY)
|
|
.unwrap_err()
|
|
.kind,
|
|
Kind::Migration
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn column_mapping_requires_exact_unused_drops_and_unambiguous_renames() {
|
|
let old = BTreeMap::from([
|
|
("id".into(), "bigint".into()),
|
|
("name".into(), "text".into()),
|
|
("removed".into(), "bigint".into()),
|
|
]);
|
|
let plan = const {
|
|
Plan {
|
|
postgres_columns: &[
|
|
column("id", "bigint"),
|
|
column("title", "text"),
|
|
column("new", "uuid"),
|
|
],
|
|
renames: &[("title", "name")],
|
|
migrations: &[step("remove", "one", &["removed"])],
|
|
..EMPTY
|
|
}
|
|
};
|
|
assert_eq!(
|
|
alter::sources(&old, &plan, 0).unwrap(),
|
|
[Some("id"), Some("name"), None]
|
|
);
|
|
assert_eq!(
|
|
alter::sources(&old, &plan, 1).unwrap_err().kind,
|
|
Kind::Migration
|
|
);
|
|
let mut ambiguous = old.clone();
|
|
ambiguous.insert("title".into(), "text".into());
|
|
assert_eq!(
|
|
alter::sources(&ambiguous, &plan, 0).unwrap_err().kind,
|
|
Kind::Migration
|
|
);
|
|
let missing = Plan {
|
|
renames: &[("title", "absent")],
|
|
..plan
|
|
};
|
|
assert_eq!(
|
|
alter::sources(&old, &missing, 0).unwrap_err().kind,
|
|
Kind::Migration
|
|
);
|
|
let duplicated = Plan {
|
|
postgres_columns: const {
|
|
&[
|
|
column("id", "bigint"),
|
|
column("title", "text"),
|
|
column("other", "text"),
|
|
]
|
|
},
|
|
renames: &[("title", "name"), ("other", "name")],
|
|
..missing
|
|
};
|
|
assert_eq!(
|
|
alter::sources(&old, &duplicated, 0).unwrap_err().kind,
|
|
Kind::Migration
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn only_supported_storage_conversions_reach_postgres_ddl() {
|
|
for (old, next) in [
|
|
("text", "text"),
|
|
("numeric(6,2)", "numeric(8,3)"),
|
|
("numeric(8,3)", "numeric(6,2)"),
|
|
] {
|
|
assert!(alter::conversion(old, next));
|
|
}
|
|
for (old, next) in [
|
|
("double precision", "bigint"),
|
|
("bigint", "double precision"),
|
|
("text", "uuid"),
|
|
("bigint", "numeric(8,2)"),
|
|
("bytea", "text"),
|
|
("numeric(6,2)", "numeric(3,4)"),
|
|
] {
|
|
assert!(!alter::conversion(old, next));
|
|
}
|
|
assert_eq!(schema::quote("a\"b"), "\"a\"\"b\"");
|
|
}
|