style: format the whole tree with cargo fmt (stock defaults, #35)
One-time, mechanical reformat — no functional changes. The tree was not rustfmt-clean (~1800 hunks across ~100 files); this brings it to stock `cargo fmt` defaults so a `cargo fmt --check` CI gate can follow. Behaviour-preserving: 2509 pass / 0 fail / 1 ignored (unchanged baseline), clippy clean. A .git-blame-ignore-revs entry follows so `git blame` skips this commit.
This commit is contained in:
+376
-93
@@ -18,8 +18,7 @@ fn rt() -> tokio::runtime::Runtime {
|
||||
|
||||
fn open_project_db() -> (project::Project, Database, tempfile::TempDir) {
|
||||
let dir = tempfile::tempdir().expect("create tempdir");
|
||||
let project =
|
||||
project::open_or_create(None, Some(dir.path())).expect("open or create project");
|
||||
let project = project::open_or_create(None, Some(dir.path())).expect("open or create project");
|
||||
let persistence = Persistence::new(project.path().to_path_buf());
|
||||
let db = Database::open_with_persistence(project.db_path(), persistence)
|
||||
.expect("open db with persistence");
|
||||
@@ -76,7 +75,10 @@ fn seed_parses_with_and_without_count() {
|
||||
match parse_command("seed People").expect("`seed People` parses") {
|
||||
Command::Seed { table, count, .. } => {
|
||||
assert_eq!(table, "People");
|
||||
assert_eq!(count, None, "omitted count is None (executor defaults to 20)");
|
||||
assert_eq!(
|
||||
count, None,
|
||||
"omitted count is None (executor defaults to 20)"
|
||||
);
|
||||
}
|
||||
other => panic!("expected Command::Seed, got {other:?}"),
|
||||
}
|
||||
@@ -134,7 +136,10 @@ fn seed_set_fixed_value_override_parses() {
|
||||
let (_t, ov) = seed_overrides("seed users 5 set status = 'active'");
|
||||
assert_eq!(ov.len(), 1);
|
||||
assert_eq!(ov[0].column, "status");
|
||||
assert_eq!(ov[0].kind, SeedOverrideKind::Fixed(Value::Text("active".into())));
|
||||
assert_eq!(
|
||||
ov[0].kind,
|
||||
SeedOverrideKind::Fixed(Value::Text("active".into()))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -177,8 +182,7 @@ fn seed_set_numeric_range_override_parses() {
|
||||
#[test]
|
||||
fn seed_set_date_range_override_parses_with_quoted_dates() {
|
||||
// ADR-0048 D2 amendment: dates in the range form are quoted strings.
|
||||
let (_t, ov) =
|
||||
seed_overrides("seed users set signup between '2023-01-01' and '2024-12-31'");
|
||||
let (_t, ov) = seed_overrides("seed users set signup between '2023-01-01' and '2024-12-31'");
|
||||
assert_eq!(
|
||||
ov[0].kind,
|
||||
SeedOverrideKind::Range {
|
||||
@@ -207,7 +211,9 @@ fn seed_count_is_not_confused_by_a_range_value() {
|
||||
// No positional count, but `between 18 and 80` carries NumberLits —
|
||||
// they must not be read as the count (bounded to before `set`).
|
||||
match parse_command("seed users set age between 18 and 80").expect("parses") {
|
||||
Command::Seed { count, overrides, .. } => {
|
||||
Command::Seed {
|
||||
count, overrides, ..
|
||||
} => {
|
||||
assert_eq!(count, None, "the count is None, not 18");
|
||||
assert_eq!(overrides.len(), 1);
|
||||
}
|
||||
@@ -267,7 +273,14 @@ fn seed_populates_a_table_and_persists_rows() {
|
||||
create_people(&db, &rt);
|
||||
|
||||
let result = rt
|
||||
.block_on(db.seed("People".into(), None, Some(7), Vec::new(), Some(42), Some("seed People 7".into())))
|
||||
.block_on(db.seed(
|
||||
"People".into(),
|
||||
None,
|
||||
Some(7),
|
||||
Vec::new(),
|
||||
Some(42),
|
||||
Some("seed People 7".into()),
|
||||
))
|
||||
.expect("seed succeeds");
|
||||
assert_eq!(result.produced, 7);
|
||||
|
||||
@@ -278,22 +291,34 @@ fn seed_populates_a_table_and_persists_rows() {
|
||||
"CSV should hold 7 generated rows:\n{csv}"
|
||||
);
|
||||
// The generated `email` column produces address-shaped values.
|
||||
assert!(csv.contains('@'), "seeded emails should appear in the CSV:\n{csv}");
|
||||
assert!(
|
||||
csv.contains('@'),
|
||||
"seeded emails should appear in the CSV:\n{csv}"
|
||||
);
|
||||
}
|
||||
|
||||
/// Parse a seeded table's CSV into per-column value lists (simple
|
||||
/// comma-split — the values under test carry no commas/quotes).
|
||||
fn csv_columns(csv: &str) -> (Vec<String>, Vec<Vec<String>>) {
|
||||
let mut lines = csv.lines().filter(|l| !l.trim().is_empty());
|
||||
let header: Vec<String> = lines.next().unwrap().split(',').map(str::to_string).collect();
|
||||
let rows: Vec<Vec<String>> =
|
||||
lines.map(|l| l.split(',').map(str::to_string).collect()).collect();
|
||||
let header: Vec<String> = lines
|
||||
.next()
|
||||
.unwrap()
|
||||
.split(',')
|
||||
.map(str::to_string)
|
||||
.collect();
|
||||
let rows: Vec<Vec<String>> = lines
|
||||
.map(|l| l.split(',').map(str::to_string).collect())
|
||||
.collect();
|
||||
(header, rows)
|
||||
}
|
||||
|
||||
fn column_values(csv: &str, col: &str) -> Vec<String> {
|
||||
let (header, rows) = csv_columns(csv);
|
||||
let idx = header.iter().position(|h| h == col).expect("column present");
|
||||
let idx = header
|
||||
.iter()
|
||||
.position(|h| h == col)
|
||||
.expect("column present");
|
||||
rows.iter().map(|r| r[idx].clone()).collect()
|
||||
}
|
||||
|
||||
@@ -321,20 +346,36 @@ fn seed_year_and_choice_set_heuristics() {
|
||||
))
|
||||
.expect("create Records");
|
||||
|
||||
rt.block_on(db.seed("Records".into(), None, Some(30), Vec::new(), Some(99), Some("seed Records 30".into())))
|
||||
.expect("seed succeeds");
|
||||
rt.block_on(db.seed(
|
||||
"Records".into(),
|
||||
None,
|
||||
Some(30),
|
||||
Vec::new(),
|
||||
Some(99),
|
||||
Some("seed Records 30".into()),
|
||||
))
|
||||
.expect("seed succeeds");
|
||||
let csv = read_csv(&project, "Records").expect("Records CSV exists");
|
||||
|
||||
for y in column_values(&csv, "birth_year") {
|
||||
let n: i32 = y.parse().expect("birth_year is an int");
|
||||
assert!((1945..=2007).contains(&n), "birth_year {n} must be a plausible birth year");
|
||||
assert!(
|
||||
(1945..=2007).contains(&n),
|
||||
"birth_year {n} must be a plausible birth year"
|
||||
);
|
||||
}
|
||||
for y in column_values(&csv, "published") {
|
||||
let n: i32 = y.parse().expect("published is an int");
|
||||
assert!((1950..=2025).contains(&n), "published {n} must be a plausible recent year");
|
||||
assert!(
|
||||
(1950..=2025).contains(&n),
|
||||
"published {n} must be a plausible recent year"
|
||||
);
|
||||
}
|
||||
for p in column_values(&csv, "priority") {
|
||||
assert!(["low", "medium", "high"].contains(&p.as_str()), "priority `{p}` must be low/medium/high");
|
||||
assert!(
|
||||
["low", "medium", "high"].contains(&p.as_str()),
|
||||
"priority `{p}` must be low/medium/high"
|
||||
);
|
||||
}
|
||||
for s in column_values(&csv, "severity") {
|
||||
assert!(
|
||||
@@ -405,7 +446,14 @@ fn seed_count_defaults_to_twenty() {
|
||||
create_people(&db, &rt);
|
||||
|
||||
let result = rt
|
||||
.block_on(db.seed("People".into(), None, None, Vec::new(), Some(1), Some("seed People".into())))
|
||||
.block_on(db.seed(
|
||||
"People".into(),
|
||||
None,
|
||||
None,
|
||||
Vec::new(),
|
||||
Some(1),
|
||||
Some("seed People".into()),
|
||||
))
|
||||
.expect("seed succeeds");
|
||||
assert_eq!(result.produced, 20, "omitted count defaults to 20");
|
||||
let csv = read_csv(&project, "People").expect("People CSV exists");
|
||||
@@ -420,10 +468,24 @@ fn seed_is_reproducible_with_a_fixed_seed() {
|
||||
create_people(&db1, &rt);
|
||||
create_people(&db2, &rt);
|
||||
|
||||
rt.block_on(db1.seed("People".into(), None, Some(4), Vec::new(), Some(123), Some("seed People 4".into())))
|
||||
.expect("seed run 1");
|
||||
rt.block_on(db2.seed("People".into(), None, Some(4), Vec::new(), Some(123), Some("seed People 4".into())))
|
||||
.expect("seed run 2");
|
||||
rt.block_on(db1.seed(
|
||||
"People".into(),
|
||||
None,
|
||||
Some(4),
|
||||
Vec::new(),
|
||||
Some(123),
|
||||
Some("seed People 4".into()),
|
||||
))
|
||||
.expect("seed run 1");
|
||||
rt.block_on(db2.seed(
|
||||
"People".into(),
|
||||
None,
|
||||
Some(4),
|
||||
Vec::new(),
|
||||
Some(123),
|
||||
Some("seed People 4".into()),
|
||||
))
|
||||
.expect("seed run 2");
|
||||
|
||||
let csv1 = read_csv(&p1, "People").expect("csv 1");
|
||||
let csv2 = read_csv(&p2, "People").expect("csv 2");
|
||||
@@ -493,10 +555,24 @@ fn seed_fills_foreign_keys_from_existing_parents() {
|
||||
create_users_and_orders(&db, &rt, true);
|
||||
|
||||
// 5 parents → serial ids 1..=5.
|
||||
rt.block_on(db.seed("Users".into(), None, Some(5), Vec::new(), Some(1), Some("seed Users 5".into())))
|
||||
.expect("seed Users");
|
||||
rt.block_on(db.seed(
|
||||
"Users".into(),
|
||||
None,
|
||||
Some(5),
|
||||
Vec::new(),
|
||||
Some(1),
|
||||
Some("seed Users 5".into()),
|
||||
))
|
||||
.expect("seed Users");
|
||||
let res = rt
|
||||
.block_on(db.seed("Orders".into(), None, Some(10), Vec::new(), Some(2), Some("seed Orders 10".into())))
|
||||
.block_on(db.seed(
|
||||
"Orders".into(),
|
||||
None,
|
||||
Some(10),
|
||||
Vec::new(),
|
||||
Some(2),
|
||||
Some("seed Orders 10".into()),
|
||||
))
|
||||
.expect("seed Orders");
|
||||
assert_eq!(res.produced, 10, "every child row must insert (valid FK)");
|
||||
|
||||
@@ -520,10 +596,20 @@ fn seed_refuses_when_a_parent_table_is_empty() {
|
||||
|
||||
// Users is empty — no valid FK can be fabricated.
|
||||
let err = rt
|
||||
.block_on(db.seed("Orders".into(), None, Some(3), Vec::new(), Some(1), Some("seed Orders 3".into())))
|
||||
.block_on(db.seed(
|
||||
"Orders".into(),
|
||||
None,
|
||||
Some(3),
|
||||
Vec::new(),
|
||||
Some(1),
|
||||
Some("seed Orders 3".into()),
|
||||
))
|
||||
.expect_err("seed must refuse an empty parent");
|
||||
let msg = err.to_string();
|
||||
assert!(msg.contains("Users"), "error should name the empty parent: {msg}");
|
||||
assert!(
|
||||
msg.contains("Users"),
|
||||
"error should name the empty parent: {msg}"
|
||||
);
|
||||
let lower = msg.to_lowercase();
|
||||
assert!(
|
||||
lower.contains("no rows") || lower.contains("first"),
|
||||
@@ -546,7 +632,14 @@ fn seed_refuses_a_not_null_blob_column() {
|
||||
.expect("create Files");
|
||||
|
||||
let err = rt
|
||||
.block_on(db.seed("Files".into(), None, Some(2), Vec::new(), Some(1), Some("seed Files 2".into())))
|
||||
.block_on(db.seed(
|
||||
"Files".into(),
|
||||
None,
|
||||
Some(2),
|
||||
Vec::new(),
|
||||
Some(1),
|
||||
Some("seed Files 2".into()),
|
||||
))
|
||||
.expect_err("seed must refuse a NOT NULL blob");
|
||||
let msg = err.to_string();
|
||||
assert!(
|
||||
@@ -573,7 +666,14 @@ fn seed_omits_a_nullable_blob_column() {
|
||||
.expect("create Files");
|
||||
|
||||
let res = rt
|
||||
.block_on(db.seed("Files".into(), None, Some(3), Vec::new(), Some(1), Some("seed Files 3".into())))
|
||||
.block_on(db.seed(
|
||||
"Files".into(),
|
||||
None,
|
||||
Some(3),
|
||||
Vec::new(),
|
||||
Some(1),
|
||||
Some("seed Files 3".into()),
|
||||
))
|
||||
.expect("seed succeeds despite the nullable blob");
|
||||
assert_eq!(res.produced, 3);
|
||||
let csv = read_csv(&project, "Files").expect("Files CSV");
|
||||
@@ -607,14 +707,25 @@ fn seed_keeps_unique_columns_distinct() {
|
||||
.expect("create Tags");
|
||||
|
||||
let res = rt
|
||||
.block_on(db.seed("Tags".into(), None, Some(8), Vec::new(), Some(3), Some("seed Tags 8".into())))
|
||||
.block_on(db.seed(
|
||||
"Tags".into(),
|
||||
None,
|
||||
Some(8),
|
||||
Vec::new(),
|
||||
Some(3),
|
||||
Some("seed Tags 8".into()),
|
||||
))
|
||||
.expect("seed");
|
||||
assert_eq!(res.produced, 8);
|
||||
|
||||
let csv = read_csv(&project, "Tags").expect("Tags CSV");
|
||||
let labels = nth_column_values(&csv, 1);
|
||||
let distinct: std::collections::HashSet<&String> = labels.iter().collect();
|
||||
assert_eq!(distinct.len(), labels.len(), "UNIQUE column has duplicates:\n{csv}");
|
||||
assert_eq!(
|
||||
distinct.len(),
|
||||
labels.len(),
|
||||
"UNIQUE column has duplicates:\n{csv}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -636,7 +747,14 @@ fn seed_sequences_identifier_int_columns() {
|
||||
.expect("create Items");
|
||||
|
||||
let res = rt
|
||||
.block_on(db.seed("Items".into(), None, Some(5), Vec::new(), Some(1), Some("seed Items 5".into())))
|
||||
.block_on(db.seed(
|
||||
"Items".into(),
|
||||
None,
|
||||
Some(5),
|
||||
Vec::new(),
|
||||
Some(1),
|
||||
Some("seed Items 5".into()),
|
||||
))
|
||||
.expect("seed");
|
||||
assert_eq!(res.produced, 5);
|
||||
|
||||
@@ -646,7 +764,11 @@ fn seed_sequences_identifier_int_columns() {
|
||||
.map(|s| s.parse().expect("code is an int"))
|
||||
.collect();
|
||||
let distinct: std::collections::HashSet<i64> = codes.iter().copied().collect();
|
||||
assert_eq!(distinct.len(), 5, "identifier ints must be unique: {codes:?}");
|
||||
assert_eq!(
|
||||
distinct.len(),
|
||||
5,
|
||||
"identifier ints must be unique: {codes:?}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -667,14 +789,24 @@ fn seed_junction_produces_distinct_combinations_and_caps() {
|
||||
)
|
||||
.await
|
||||
.expect("create parent");
|
||||
db.seed(t.into(), None, Some(2), Vec::new(), Some(1), Some(format!("seed {t} 2")))
|
||||
.await
|
||||
.expect("seed parent");
|
||||
db.seed(
|
||||
t.into(),
|
||||
None,
|
||||
Some(2),
|
||||
Vec::new(),
|
||||
Some(1),
|
||||
Some(format!("seed {t} 2")),
|
||||
)
|
||||
.await
|
||||
.expect("seed parent");
|
||||
}
|
||||
// Junction with a compound PK over its two FK columns.
|
||||
db.create_table(
|
||||
"J".to_string(),
|
||||
vec![ColumnSpec::new("a", Type::Int), ColumnSpec::new("b", Type::Int)],
|
||||
vec![
|
||||
ColumnSpec::new("a", Type::Int),
|
||||
ColumnSpec::new("b", Type::Int),
|
||||
],
|
||||
vec!["a".to_string(), "b".to_string()],
|
||||
None,
|
||||
)
|
||||
@@ -709,11 +841,21 @@ fn seed_junction_produces_distinct_combinations_and_caps() {
|
||||
|
||||
// Requesting 10 caps at the 4 available distinct combinations.
|
||||
let res = db
|
||||
.seed("J".into(), None, Some(10), Vec::new(), Some(7), Some("seed J 10".into()))
|
||||
.seed(
|
||||
"J".into(),
|
||||
None,
|
||||
Some(10),
|
||||
Vec::new(),
|
||||
Some(7),
|
||||
Some("seed J 10".into()),
|
||||
)
|
||||
.await
|
||||
.expect("seed J");
|
||||
assert_eq!(res.produced, 4, "junction caps at available combos");
|
||||
assert_eq!(res.requested, 10, "the requested count is reported for the cap note");
|
||||
assert_eq!(
|
||||
res.requested, 10,
|
||||
"the requested count is reported for the cap note"
|
||||
);
|
||||
});
|
||||
|
||||
let csv = read_csv(&project, "J").expect("J CSV");
|
||||
@@ -724,7 +866,11 @@ fn seed_junction_produces_distinct_combinations_and_caps() {
|
||||
.map(str::to_string)
|
||||
.collect();
|
||||
let distinct: std::collections::HashSet<&String> = pairs.iter().collect();
|
||||
assert_eq!(distinct.len(), pairs.len(), "junction rows must be distinct:\n{csv}");
|
||||
assert_eq!(
|
||||
distinct.len(),
|
||||
pairs.len(),
|
||||
"junction rows must be distinct:\n{csv}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -743,9 +889,19 @@ fn seed_draws_enum_values_from_an_in_check() {
|
||||
|
||||
// Every generated status must satisfy the CHECK, so all rows insert.
|
||||
let res = rt
|
||||
.block_on(db.seed("Tickets".into(), None, Some(12), Vec::new(), Some(2), Some("seed Tickets 12".into())))
|
||||
.block_on(db.seed(
|
||||
"Tickets".into(),
|
||||
None,
|
||||
Some(12),
|
||||
Vec::new(),
|
||||
Some(2),
|
||||
Some("seed Tickets 12".into()),
|
||||
))
|
||||
.expect("seed");
|
||||
assert_eq!(res.produced, 12, "all rows insert — values satisfy the CHECK");
|
||||
assert_eq!(
|
||||
res.produced, 12,
|
||||
"all rows insert — values satisfy the CHECK"
|
||||
);
|
||||
|
||||
let csv = read_csv(&project, "Tickets").expect("Tickets CSV");
|
||||
for v in nth_column_values(&csv, 1) {
|
||||
@@ -780,7 +936,14 @@ fn seed_advises_on_enum_ish_columns() {
|
||||
.expect("create Tasks");
|
||||
|
||||
let res = rt
|
||||
.block_on(db.seed("Tasks".into(), None, Some(3), Vec::new(), Some(1), Some("seed Tasks 3".into())))
|
||||
.block_on(db.seed(
|
||||
"Tasks".into(),
|
||||
None,
|
||||
Some(3),
|
||||
Vec::new(),
|
||||
Some(1),
|
||||
Some("seed Tasks 3".into()),
|
||||
))
|
||||
.expect("seed");
|
||||
assert!(
|
||||
res.advisory_columns.contains(&"status".to_string()),
|
||||
@@ -795,7 +958,14 @@ fn seed_refuses_an_excessive_count() {
|
||||
let rt = rt();
|
||||
create_people(&db, &rt);
|
||||
let err = rt
|
||||
.block_on(db.seed("People".into(), None, Some(1_000_000), Vec::new(), Some(1), Some("seed People 1000000".into())))
|
||||
.block_on(db.seed(
|
||||
"People".into(),
|
||||
None,
|
||||
Some(1_000_000),
|
||||
Vec::new(),
|
||||
Some(1),
|
||||
Some("seed People 1000000".into()),
|
||||
))
|
||||
.expect_err("an excessive count must be refused");
|
||||
assert!(
|
||||
err.to_string().to_lowercase().contains("maximum"),
|
||||
@@ -810,7 +980,14 @@ fn seed_preview_is_capped_but_count_is_full() {
|
||||
create_people(&db, &rt);
|
||||
|
||||
let res = rt
|
||||
.block_on(db.seed("People".into(), None, Some(25), Vec::new(), Some(1), Some("seed People 25".into())))
|
||||
.block_on(db.seed(
|
||||
"People".into(),
|
||||
None,
|
||||
Some(25),
|
||||
Vec::new(),
|
||||
Some(1),
|
||||
Some("seed People 25".into()),
|
||||
))
|
||||
.expect("seed");
|
||||
assert_eq!(res.produced, 25, "the full count is produced");
|
||||
assert_eq!(res.data.rows.len(), 20, "the preview is capped at 20 rows");
|
||||
@@ -860,14 +1037,24 @@ fn seed_is_one_undo_step() {
|
||||
.expect("open db with undo");
|
||||
let rt = rt();
|
||||
create_people(&db, &rt);
|
||||
rt.block_on(db.seed("People".into(), None, Some(6), Vec::new(), Some(1), Some("seed People 6".into())))
|
||||
.expect("seed");
|
||||
rt.block_on(db.seed(
|
||||
"People".into(),
|
||||
None,
|
||||
Some(6),
|
||||
Vec::new(),
|
||||
Some(1),
|
||||
Some("seed People 6".into()),
|
||||
))
|
||||
.expect("seed");
|
||||
assert_eq!(data_row_count(&read_csv(&project, "People").unwrap()), 6);
|
||||
|
||||
// One undo removes the whole seed batch (ADR-0048 D15).
|
||||
rt.block_on(db.undo()).unwrap().expect("undo applied");
|
||||
let rows = read_csv(&project, "People").map_or(0, |c| data_row_count(&c));
|
||||
assert_eq!(rows, 0, "one undo must remove every seeded row in a single step");
|
||||
assert_eq!(
|
||||
rows, 0,
|
||||
"one undo must remove every seeded row in a single step"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -882,10 +1069,17 @@ fn seed_column_fill_is_one_undo_step() {
|
||||
create_members(&db, &rt);
|
||||
run_seed(&db, &rt, "seed Members 5 --seed 1").expect("seed");
|
||||
// Fill `status` across all 5 rows with a constant, then undo once.
|
||||
run_seed(&db, &rt, "seed Members.status set status = 'flagged' --seed 2")
|
||||
.expect("column-fill");
|
||||
run_seed(
|
||||
&db,
|
||||
&rt,
|
||||
"seed Members.status set status = 'flagged' --seed 2",
|
||||
)
|
||||
.expect("column-fill");
|
||||
let before = named_column_values(&read_csv(&project, "Members").unwrap(), "status");
|
||||
assert!(before.iter().all(|s| s == "flagged"), "all rows filled: {before:?}");
|
||||
assert!(
|
||||
before.iter().all(|s| s == "flagged"),
|
||||
"all rows filled: {before:?}"
|
||||
);
|
||||
|
||||
rt.block_on(db.undo()).unwrap().expect("undo applied");
|
||||
let after = named_column_values(&read_csv(&project, "Members").unwrap(), "status");
|
||||
@@ -893,7 +1087,11 @@ fn seed_column_fill_is_one_undo_step() {
|
||||
after.iter().all(|s| s != "flagged"),
|
||||
"one undo reverts the whole column-fill in a single step: {after:?}"
|
||||
);
|
||||
assert_eq!(after.len(), 5, "undo restores the original rows, not removes them");
|
||||
assert_eq!(
|
||||
after.len(),
|
||||
5,
|
||||
"undo restores the original rows, not removes them"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -930,10 +1128,23 @@ fn seed_rolls_back_atomically_on_a_constraint_failure() {
|
||||
))
|
||||
.expect("create Bad");
|
||||
|
||||
let res = rt.block_on(db.seed("Bad".into(), None, Some(5), Vec::new(), Some(1), Some("seed Bad 5".into())));
|
||||
assert!(res.is_err(), "seed must fail when generated rows violate the CHECK");
|
||||
let res = rt.block_on(db.seed(
|
||||
"Bad".into(),
|
||||
None,
|
||||
Some(5),
|
||||
Vec::new(),
|
||||
Some(1),
|
||||
Some("seed Bad 5".into()),
|
||||
));
|
||||
assert!(
|
||||
res.is_err(),
|
||||
"seed must fail when generated rows violate the CHECK"
|
||||
);
|
||||
let rows = read_csv(&project, "Bad").map_or(0, |c| data_row_count(&c));
|
||||
assert_eq!(rows, 0, "a failed seed must leave the table unchanged (atomic)");
|
||||
assert_eq!(
|
||||
rows, 0,
|
||||
"a failed seed must leave the table unchanged (atomic)"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -942,7 +1153,14 @@ fn seed_zero_is_a_no_op() {
|
||||
let rt = rt();
|
||||
create_people(&db, &rt);
|
||||
let res = rt
|
||||
.block_on(db.seed("People".into(), None, Some(0), Vec::new(), Some(1), Some("seed People 0".into())))
|
||||
.block_on(db.seed(
|
||||
"People".into(),
|
||||
None,
|
||||
Some(0),
|
||||
Vec::new(),
|
||||
Some(1),
|
||||
Some("seed People 0".into()),
|
||||
))
|
||||
.expect("seed 0 succeeds");
|
||||
assert_eq!(res.produced, 0);
|
||||
let rows = read_csv(&project, "People").map_or(0, |c| data_row_count(&c));
|
||||
@@ -967,7 +1185,14 @@ fn seed_advises_on_a_complex_check_column() {
|
||||
.expect("create Widgets");
|
||||
|
||||
let res = rt
|
||||
.block_on(db.seed("Widgets".into(), None, Some(3), Vec::new(), Some(1), Some("seed Widgets 3".into())))
|
||||
.block_on(db.seed(
|
||||
"Widgets".into(),
|
||||
None,
|
||||
Some(3),
|
||||
Vec::new(),
|
||||
Some(1),
|
||||
Some("seed Widgets 3".into()),
|
||||
))
|
||||
.expect("seed");
|
||||
assert!(
|
||||
res.advisory_columns.contains(&"label".to_string()),
|
||||
@@ -981,10 +1206,24 @@ fn seed_foreign_keys_are_reproducible_with_a_fixed_seed() {
|
||||
let rt = rt();
|
||||
let seed_one = |db: &Database| {
|
||||
create_users_and_orders(db, &rt, true);
|
||||
rt.block_on(db.seed("Users".into(), None, Some(4), Vec::new(), Some(1), Some("seed Users 4".into())))
|
||||
.expect("seed users");
|
||||
rt.block_on(db.seed("Orders".into(), None, Some(8), Vec::new(), Some(99), Some("seed Orders 8".into())))
|
||||
.expect("seed orders");
|
||||
rt.block_on(db.seed(
|
||||
"Users".into(),
|
||||
None,
|
||||
Some(4),
|
||||
Vec::new(),
|
||||
Some(1),
|
||||
Some("seed Users 4".into()),
|
||||
))
|
||||
.expect("seed users");
|
||||
rt.block_on(db.seed(
|
||||
"Orders".into(),
|
||||
None,
|
||||
Some(8),
|
||||
Vec::new(),
|
||||
Some(99),
|
||||
Some("seed Orders 8".into()),
|
||||
))
|
||||
.expect("seed orders");
|
||||
};
|
||||
let (p1, db1, _d1) = open_project_db();
|
||||
let (p2, db2, _d2) = open_project_db();
|
||||
@@ -1013,8 +1252,15 @@ fn seed_shortid_columns_are_reproducible_with_a_fixed_seed() {
|
||||
None,
|
||||
))
|
||||
.expect("create Contacts");
|
||||
rt.block_on(db.seed("Contacts".into(), None, Some(5), Vec::new(), Some(42), Some("seed Contacts 5".into())))
|
||||
.expect("seed");
|
||||
rt.block_on(db.seed(
|
||||
"Contacts".into(),
|
||||
None,
|
||||
Some(5),
|
||||
Vec::new(),
|
||||
Some(42),
|
||||
Some("seed Contacts 5".into()),
|
||||
))
|
||||
.expect("seed");
|
||||
};
|
||||
let (p1, db1, _d1) = open_project_db();
|
||||
let (p2, db2, _d2) = open_project_db();
|
||||
@@ -1023,13 +1269,20 @@ fn seed_shortid_columns_are_reproducible_with_a_fixed_seed() {
|
||||
|
||||
let csv1 = read_csv(&p1, "Contacts").unwrap();
|
||||
let csv2 = read_csv(&p2, "Contacts").unwrap();
|
||||
assert_eq!(csv1, csv2, "shortid values must reproduce under a fixed --seed");
|
||||
assert_eq!(
|
||||
csv1, csv2,
|
||||
"shortid values must reproduce under a fixed --seed"
|
||||
);
|
||||
|
||||
// The shortid PK is populated with distinct 10-char base58 ids.
|
||||
let codes = nth_column_values(&csv1, 0);
|
||||
assert_eq!(codes.len(), 5);
|
||||
let distinct: std::collections::HashSet<&String> = codes.iter().collect();
|
||||
assert_eq!(distinct.len(), 5, "shortid PK values must be distinct: {codes:?}");
|
||||
assert_eq!(
|
||||
distinct.len(),
|
||||
5,
|
||||
"shortid PK values must be distinct: {codes:?}"
|
||||
);
|
||||
for code in &codes {
|
||||
assert_eq!(code.len(), 10, "shortid should be 10 chars: {code}");
|
||||
}
|
||||
@@ -1105,7 +1358,10 @@ fn seed_set_fixed_value_fills_every_row() {
|
||||
let csv = read_csv(&project, "Members").unwrap();
|
||||
let statuses = named_column_values(&csv, "status");
|
||||
assert_eq!(statuses.len(), 6);
|
||||
assert!(statuses.iter().all(|s| s == "active"), "every status pinned: {statuses:?}");
|
||||
assert!(
|
||||
statuses.iter().all(|s| s == "active"),
|
||||
"every status pinned: {statuses:?}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1113,7 +1369,12 @@ fn seed_set_pick_list_draws_only_from_the_list() {
|
||||
let (project, db, _d) = open_project_db();
|
||||
let rt = rt();
|
||||
create_members(&db, &rt);
|
||||
run_seed(&db, &rt, "seed Members 20 set role in ('admin', 'user') --seed 2").expect("seed");
|
||||
run_seed(
|
||||
&db,
|
||||
&rt,
|
||||
"seed Members 20 set role in ('admin', 'user') --seed 2",
|
||||
)
|
||||
.expect("seed");
|
||||
let csv = read_csv(&project, "Members").unwrap();
|
||||
let roles = named_column_values(&csv, "role");
|
||||
assert!(
|
||||
@@ -1131,7 +1392,10 @@ fn seed_set_as_generator_forces_the_shape() {
|
||||
run_seed(&db, &rt, "seed Members 5 set name as email --seed 3").expect("seed");
|
||||
let csv = read_csv(&project, "Members").unwrap();
|
||||
let names = named_column_values(&csv, "name");
|
||||
assert!(names.iter().all(|n| n.contains('@')), "name forced to email shape: {names:?}");
|
||||
assert!(
|
||||
names.iter().all(|n| n.contains('@')),
|
||||
"name forced to email shape: {names:?}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1139,7 +1403,12 @@ fn seed_set_numeric_range_stays_within_bounds() {
|
||||
let (project, db, _d) = open_project_db();
|
||||
let rt = rt();
|
||||
create_members(&db, &rt);
|
||||
run_seed(&db, &rt, "seed Members 30 set age between 30 and 40 --seed 4").expect("seed");
|
||||
run_seed(
|
||||
&db,
|
||||
&rt,
|
||||
"seed Members 30 set age between 30 and 40 --seed 4",
|
||||
)
|
||||
.expect("seed");
|
||||
let csv = read_csv(&project, "Members").unwrap();
|
||||
for a in named_column_values(&csv, "age") {
|
||||
let n: i64 = a.parse().unwrap_or_else(|_| panic!("age `{a}` not an int"));
|
||||
@@ -1190,7 +1459,10 @@ fn seed_incompatible_range_is_a_friendly_error() {
|
||||
// A numeric range on a text column (`name`) is rejected.
|
||||
let err = run_seed(&db, &rt, "seed Members 3 set name between 1 and 10").unwrap_err();
|
||||
let msg = format!("{err}");
|
||||
assert!(msg.contains("between"), "range error should mention `between`: {msg}");
|
||||
assert!(
|
||||
msg.contains("between"),
|
||||
"range error should mention `between`: {msg}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1221,8 +1493,12 @@ fn seed_column_fill_updates_existing_rows_without_adding() {
|
||||
let before = data_row_count(&read_csv(&project, "Members").unwrap());
|
||||
assert_eq!(before, 5);
|
||||
|
||||
let res = run_seed(&db, &rt, "seed Members.status set status in ('x', 'y') --seed 2")
|
||||
.expect("column-fill");
|
||||
let res = run_seed(
|
||||
&db,
|
||||
&rt,
|
||||
"seed Members.status set status in ('x', 'y') --seed 2",
|
||||
)
|
||||
.expect("column-fill");
|
||||
assert_eq!(res.produced, 5, "column-fill touches the 5 existing rows");
|
||||
let csv = read_csv(&project, "Members").unwrap();
|
||||
assert_eq!(data_row_count(&csv), 5, "no new rows added");
|
||||
@@ -1240,7 +1516,10 @@ fn seed_column_fill_refuses_a_pk_target() {
|
||||
create_members(&db, &rt);
|
||||
run_seed(&db, &rt, "seed Members 3 --seed 1").expect("seed");
|
||||
let err = run_seed(&db, &rt, "seed Members.id").unwrap_err();
|
||||
assert!(format!("{err}").contains("primary key"), "PK target refused: {err}");
|
||||
assert!(
|
||||
format!("{err}").contains("primary key"),
|
||||
"PK target refused: {err}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1282,7 +1561,10 @@ fn seed_column_fill_rejects_a_row_count() {
|
||||
Some("seed Members.status 5".into()),
|
||||
))
|
||||
.unwrap_err();
|
||||
assert!(format!("{err}").contains("no row count"), "count refused: {err}");
|
||||
assert!(
|
||||
format!("{err}").contains("no row count"),
|
||||
"count refused: {err}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1298,7 +1580,11 @@ fn seed_column_fill_fk_target_samples_the_parent() {
|
||||
assert_eq!(res.produced, 8);
|
||||
let csv = read_csv(&project, "Orders").unwrap();
|
||||
let user_ids = named_column_values(&csv, "user_id");
|
||||
assert!(user_ids.iter().all(|v| (1..=4).contains(&v.parse::<i64>().unwrap())));
|
||||
assert!(
|
||||
user_ids
|
||||
.iter()
|
||||
.all(|v| (1..=4).contains(&v.parse::<i64>().unwrap()))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1310,14 +1596,11 @@ fn seed_fixed_override_on_unique_column_is_a_friendly_error() {
|
||||
let rt = rt();
|
||||
rt.block_on(db.create_table(
|
||||
"U".to_string(),
|
||||
vec![
|
||||
ColumnSpec::new("id", Type::Serial),
|
||||
{
|
||||
let mut c = ColumnSpec::new("email", Type::Text);
|
||||
c.unique = true;
|
||||
c
|
||||
},
|
||||
],
|
||||
vec![ColumnSpec::new("id", Type::Serial), {
|
||||
let mut c = ColumnSpec::new("email", Type::Text);
|
||||
c.unique = true;
|
||||
c
|
||||
}],
|
||||
vec!["id".to_string()],
|
||||
None,
|
||||
))
|
||||
@@ -1330,7 +1613,10 @@ fn seed_fixed_override_on_unique_column_is_a_friendly_error() {
|
||||
);
|
||||
// A short pick-list (< count) is likewise refused...
|
||||
let err2 = run_seed(&db, &rt, "seed U 5 set email in ('a@b.c', 'd@e.f')").unwrap_err();
|
||||
assert!(format!("{err2}").contains("distinct"), "short list refused: {err2}");
|
||||
assert!(
|
||||
format!("{err2}").contains("distinct"),
|
||||
"short list refused: {err2}"
|
||||
);
|
||||
// ...but a pick-list with enough distinct values succeeds.
|
||||
let ok = run_seed(
|
||||
&db,
|
||||
@@ -1354,14 +1640,11 @@ fn seed_column_fill_fixed_on_unique_column_is_a_friendly_error() {
|
||||
let rt = rt();
|
||||
rt.block_on(db.create_table(
|
||||
"U".to_string(),
|
||||
vec![
|
||||
ColumnSpec::new("id", Type::Serial),
|
||||
{
|
||||
let mut c = ColumnSpec::new("email", Type::Text);
|
||||
c.unique = true;
|
||||
c
|
||||
},
|
||||
],
|
||||
vec![ColumnSpec::new("id", Type::Serial), {
|
||||
let mut c = ColumnSpec::new("email", Type::Text);
|
||||
c.unique = true;
|
||||
c
|
||||
}],
|
||||
vec!["id".to_string()],
|
||||
None,
|
||||
))
|
||||
|
||||
Reference in New Issue
Block a user