feat(seed): --seed flag, ambient wiring, and /runda hardening (ADR-0048 P1.4 + DA)
P1.4 — user-visible surface: - Grammar: `seed <table> [count] [--seed <n>]` (the first DSL flag with a value); build_seed disambiguates the seed value from the positional count. - Verified the auto-wired surface: table-name completion, --seed offered as a candidate, validity consistent with `show data`, an ADR-0042 near-miss row for bare `seed`, and render tests for the seed outcome. /runda hardening — eight DA findings, all resolved: - FK sampling now uses ORDER BY so --seed reproducibility no longer relies on SQLite's unspecified DISTINCT order (D4). - shortid columns now generate from seed's seeded RNG (new shortid::generate_with_rng) — D4 now holds with no exceptions. - Added the missing coverage the DA flagged: undo-one-step (D15), replay re-runs a seed line (D16), advanced-mode (D5), atomic rollback on a constraint failure, seed 0 no-op, complex-CHECK advisory (D17), and FK + shortid reproducibility. 2358 pass / 0 fail / 0 skip, clippy all-targets clean.
This commit is contained in:
+70
@@ -6258,6 +6258,76 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn seed_success_renders_count_preview_and_advisory() {
|
||||
// ADR-0048: handle_dsl_seed_success renders the seeded-row count,
|
||||
// the preview table, and the enum/CHECK advisory.
|
||||
let mut app = App::new();
|
||||
app.output
|
||||
.push_back(OutputLine::echo("seed users 20", crate::mode::Mode::Simple));
|
||||
app.update(AppEvent::DslSeedSucceeded {
|
||||
command: Command::Seed {
|
||||
table: "users".to_string(),
|
||||
count: Some(20),
|
||||
rng_seed: None,
|
||||
},
|
||||
result: crate::db::SeedResult {
|
||||
table: "users".to_string(),
|
||||
requested: 20,
|
||||
produced: 20,
|
||||
data: crate::db::DataResult {
|
||||
table_name: "users".to_string(),
|
||||
columns: vec!["name".to_string()],
|
||||
column_types: vec![None],
|
||||
rows: vec![vec![Some("Alice".to_string())]],
|
||||
},
|
||||
advisory_columns: vec!["status".to_string()],
|
||||
},
|
||||
});
|
||||
let texts: Vec<String> = app.output.iter().map(|l| l.text.clone()).collect();
|
||||
assert!(
|
||||
texts.iter().any(|t| t.contains("20 row(s) seeded into users")),
|
||||
"seeded-row count surfaced: {texts:?}",
|
||||
);
|
||||
assert!(
|
||||
texts.iter().any(|t| t.contains("status") && t.contains("generic text")),
|
||||
"the advisory names the enum-ish column: {texts:?}",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn seed_success_reports_a_cap() {
|
||||
// produced < requested → the cap note appears next to the count.
|
||||
let mut app = App::new();
|
||||
app.output
|
||||
.push_back(OutputLine::echo("seed J 10", crate::mode::Mode::Simple));
|
||||
app.update(AppEvent::DslSeedSucceeded {
|
||||
command: Command::Seed {
|
||||
table: "J".to_string(),
|
||||
count: Some(10),
|
||||
rng_seed: None,
|
||||
},
|
||||
result: crate::db::SeedResult {
|
||||
table: "J".to_string(),
|
||||
requested: 10,
|
||||
produced: 4,
|
||||
data: crate::db::DataResult {
|
||||
table_name: "J".to_string(),
|
||||
columns: Vec::new(),
|
||||
column_types: Vec::new(),
|
||||
rows: Vec::new(),
|
||||
},
|
||||
advisory_columns: Vec::new(),
|
||||
},
|
||||
});
|
||||
let texts: Vec<String> = app.output.iter().map(|l| l.text.clone()).collect();
|
||||
assert!(
|
||||
texts.iter().any(|t| t.contains("4 row(s) seeded into J")
|
||||
&& t.contains("of 10 requested")),
|
||||
"the cap note surfaces requested vs produced: {texts:?}",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sql_delete_returning_renders_cascade_and_result_table() {
|
||||
// ADR-0033 3g: a DELETE … RETURNING surfaces BOTH the cascade
|
||||
|
||||
Reference in New Issue
Block a user