Two additive D7 catalogue rules, surfaced while writing the website seed docs. No change to the type fallback, executor, or grammar. #33 — year-like int columns. `published`/`birth_year` were just `int`, so they fell to the unbounded int path and produced nonsense (`9419`). Add an int-gated year rule (after the quantity rule, so `year_count` stays a count): `year`/`*_year`/`published`/`founded` -> a bounded 1950-2025 year (new `YearRecent`), or the dob-style birth window 1945-2007 for `birth`/`born`/`dob` (new `YearBirth`). Plain int; not added to the D9 named-generator vocabulary. #34 — conventional choice sets. A few enum-ish names have a near-canonical small set that reads far better than lorem text. Add a type-gated PickFrom lookup (reusing the existing generator): priority/prio, severity, rating/stars. `status` is deliberately excluded (values too domain-specific) and keeps the D12 advisory; a user IN-CHECK still wins. `priority` leaves ENUM_TOKENS. ADR-0048 Amendment 1; +8 tests (incl. a column-fill integration test that also closes a pre-existing gap on that path).
This commit is contained in:
@@ -31,6 +31,16 @@ const RECENT_WINDOW_DAYS: i64 = 3 * 365;
|
||||
const ADULT_MIN_DAYS: i64 = 18 * 365;
|
||||
const ADULT_MAX_DAYS: i64 = 80 * 365;
|
||||
|
||||
/// Year windows for the `int`-typed year heuristics (issue #33),
|
||||
/// expressed relative to [`REF_YEAR`] so they advance with releases —
|
||||
/// the year siblings of the `DateRecent` / `DateAdult` windows above.
|
||||
/// `YearRecent` spans ~75 years (1950–2025 at REF_YEAR=2025), wide
|
||||
/// enough for `published` / `founded` / `release_year`; `YearBirth`
|
||||
/// mirrors the adult birth window (1945–2007).
|
||||
const YEAR_RECENT_SPAN: i32 = 75;
|
||||
const YEAR_BIRTH_MIN_AGE: i32 = 18;
|
||||
const YEAR_BIRTH_MAX_AGE: i32 = 80;
|
||||
|
||||
/// Produce one value for `generator` against destination type `ty`.
|
||||
#[must_use]
|
||||
pub fn generate_value(generator: &Generator, ty: Type, rng: &mut SeedRng) -> Value {
|
||||
@@ -71,6 +81,13 @@ pub fn generate_value(generator: &Generator, ty: Type, rng: &mut SeedRng) -> Val
|
||||
Generator::CurrencyAmount => currency_amount(ty, rng),
|
||||
Generator::Age => Value::Number(rng.random_range(18..=80).to_string()),
|
||||
Generator::SmallInt => Value::Number(rng.random_range(1..=100).to_string()),
|
||||
Generator::YearRecent => {
|
||||
Value::Number(rng.random_range((REF_YEAR - YEAR_RECENT_SPAN)..=REF_YEAR).to_string())
|
||||
}
|
||||
Generator::YearBirth => Value::Number(
|
||||
rng.random_range((REF_YEAR - YEAR_BIRTH_MAX_AGE)..=(REF_YEAR - YEAR_BIRTH_MIN_AGE))
|
||||
.to_string(),
|
||||
),
|
||||
Generator::DateRecent => Value::Text(format_date(random_past_date(rng, 0, RECENT_WINDOW_DAYS))),
|
||||
Generator::DateAdult => {
|
||||
Value::Text(format_date(random_past_date(rng, ADULT_MIN_DAYS, ADULT_MAX_DAYS)))
|
||||
@@ -489,6 +506,41 @@ mod tests {
|
||||
assert!(matches!(v, Value::Number(_)), "numeric pick should be a Number: {v:?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn year_generators_stay_within_their_bounded_windows() {
|
||||
// Issue #33: both year generators emit a plain `int` inside a
|
||||
// bounded, plausible window — never the unbounded-int nonsense.
|
||||
let mut rng = make_rng(Some(7));
|
||||
for _ in 0..300 {
|
||||
let Value::Number(s) = generate_value(&Generator::YearRecent, Type::Int, &mut rng)
|
||||
else {
|
||||
panic!("YearRecent must be a Number")
|
||||
};
|
||||
let n: i32 = s.parse().unwrap();
|
||||
assert!((1950..=2025).contains(&n), "YearRecent {n} out of [1950,2025]");
|
||||
}
|
||||
for _ in 0..300 {
|
||||
let Value::Number(s) = generate_value(&Generator::YearBirth, Type::Int, &mut rng)
|
||||
else {
|
||||
panic!("YearBirth must be a Number")
|
||||
};
|
||||
let n: i32 = s.parse().unwrap();
|
||||
assert!((1945..=2007).contains(&n), "YearBirth {n} out of [1945,2007]");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn year_generators_are_deterministic_for_a_fixed_seed() {
|
||||
assert_eq!(
|
||||
gen_once(&Generator::YearRecent, Type::Int, 42),
|
||||
gen_once(&Generator::YearRecent, Type::Int, 42),
|
||||
);
|
||||
assert_eq!(
|
||||
gen_once(&Generator::YearBirth, Type::Int, 42),
|
||||
gen_once(&Generator::YearBirth, Type::Int, 42),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn int_range_stays_within_inclusive_bounds() {
|
||||
let g = Generator::Range { low: "10".into(), high: "20".into() };
|
||||
|
||||
Reference in New Issue
Block a user