diff options
| author | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-08-20 11:32:24 +0200 |
|---|---|---|
| committer | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-08-20 11:32:24 +0200 |
| commit | e2e56ac5af290b60ea01037e58e8bc00545c021c (patch) | |
| tree | 6346f4ee62809323e8fb0f9041b28e35787d9828 /test/test_view.ml | |
| parent | c30a35e1da315f2a44e5b53c714bdcf4d8992d2c (diff) | |
| download | colitur-e2e56ac5af290b60ea01037e58e8bc00545c021c.tar.gz colitur-e2e56ac5af290b60ea01037e58e8bc00545c021c.zip | |
feat(render): Roman week numbers and date spans, as data
The ordo booklet's week header repeated the month name on every week
even though the heading above already established it -- pure noise.
Replace it with a Roman week number plus the span of dates the week
covers, e.g. "Hebdomada I (Ian 1-2)", following the project's own rule
that a presentation choice is data, not code.
lang/{la,en}.ini gain a [month_abbr] section (three-letter month
abbreviations); Lang.month_abbr follows Lang.month's exact shape,
including the out-of-range and miss-returns-the-key contracts. The
coverage test now fails loudly if an abbreviation goes missing, the
same as [month] already does.
Every week object in the view gains num_roman (Roman numeral, num
stays as the arabic original -- Roman is a presentation choice, not an
engine change), first_dom/last_dom (the day-of-month of the week's
first and last IN-MONTH days, padding excluded), month_abbr (resolved
through Lang.month_abbr), and single_day (true when the week holds
exactly one in-month day). single_day is a flag, not a preformatted
span string: the engine is logic-less and cannot itself decide between
"Ian 1" and "Ian 1-2", so a template makes that call from the flag
instead -- the same "shape the data, not the template" discipline
in_month and last already follow.
Weeks are built per month with padding only at the two ends, so a
week's in-month days never cross a month boundary -- verified, not
assumed: every week always has at least one real day since no month is
shorter than a single week.
Covered by three new View tests, including a real single-day-week
witness (January 2027's own trailing week is a lone Sunday, the 31st).
Diffstat (limited to 'test/test_view.ml')
| -rw-r--r-- | test/test_view.ml | 65 |
1 files changed, 65 insertions, 0 deletions
diff --git a/test/test_view.ml b/test/test_view.ml index 24e1d85..37fb284 100644 --- a/test/test_view.ml +++ b/test/test_view.ml @@ -182,6 +182,68 @@ let latin () = let view_named y = V.of_days ~lang:(latin ()) ~vocab:Rite_ef.Vocab_ef.vocab ~rite:"ef" ~year:y (days_of_year y) +(* The ordo booklet's week header (Hebdomada I (Ian 1-2)): a Roman numeral + beside the existing arabic one, and month_num/month_name/month_abbr + carried onto every week the same way (there is no {{../}} parent-path + syntax to reach the enclosing month otherwise). January 2027 has six + weeks (1 Jan 2027 is a Friday, 31 Jan a lone trailing Sunday), so I-VI + is a real witness, not a guess capped at a small sample. *) +let test_week_num_roman_and_month_fields () = + let v = view_named 2027 in + let jan = List.hd (as_list (get [ "months" ] v)) in + let weeks = as_list (get [ "weeks" ] jan) in + Alcotest.(check int) "January 2027 has six weeks" 6 (List.length weeks); + Alcotest.(check (list string)) "I..VI" [ "I"; "II"; "III"; "IV"; "V"; "VI" ] + (List.map (fun w -> as_str (get [ "num_roman" ] w)) weeks); + List.iter + (fun w -> + Alcotest.(check string) "month_num" "1" (as_str (get [ "month_num" ] w)); + Alcotest.(check string) "month_name" "Ianuarius" (as_str (get [ "month_name" ] w)); + Alcotest.(check string) "month_abbr" "Ian" (as_str (get [ "month_abbr" ] w))) + weeks + +(* The date-span fields a template needs to print "(Ian 1-2)" without ever + being handed a preformatted string (spec: the engine is logic-less, so a + punctuation choice between "Ian 1" and "Ian 1-2" must stay data the + template or a language file can still change). Verified against real + 1 January 2027 = Friday / 31 January 2027 = Sunday arithmetic: the + month's first week holds only its own leading two in-month days (1-2), + and its own LAST week -- a lone trailing Sunday, 31 -- is this whole + suite's single-day-week witness. *) +let test_week_date_span () = + let v = view_named 2027 in + let jan = List.hd (as_list (get [ "months" ] v)) in + let weeks = as_list (get [ "weeks" ] jan) in + let first_week = List.hd weeks in + Alcotest.(check string) "week 1 first_dom" "1" (as_str (get [ "first_dom" ] first_week)); + Alcotest.(check string) "week 1 last_dom" "2" (as_str (get [ "last_dom" ] first_week)); + Alcotest.(check bool) "week 1 is not a single day" false (as_bool (get [ "single_day" ] first_week)); + let last_week = List.nth weeks (List.length weeks - 1) in + Alcotest.(check string) "week 6 first_dom" "31" (as_str (get [ "first_dom" ] last_week)); + Alcotest.(check string) "week 6 last_dom" "31" (as_str (get [ "last_dom" ] last_week)); + Alcotest.(check bool) "week 6 IS a single day" true (as_bool (get [ "single_day" ] last_week)) + +(* Property, not a one-off sample: over every week of every month of a whole + year, [single_day] must agree exactly with [first_dom] = [last_dom], and + the count of in-month cells in [days] must match what [single_day] claims + -- proves the flag is computed FROM the same in-month cells a template + walks, not from a separately-derived (and possibly drifting) count. *) +let test_single_day_agrees_with_span_and_cells () = + let v = view_of 2027 in + List.iter + (fun m -> + List.iter + (fun w -> + let first_dom = as_str (get [ "first_dom" ] w) and last_dom = as_str (get [ "last_dom" ] w) in + let single = as_bool (get [ "single_day" ] w) in + Alcotest.(check bool) "single_day iff first_dom = last_dom" (first_dom = last_dom) single; + let real_cells = + List.filter (fun c -> as_bool (get [ "in_month" ] c)) (as_list (get [ "days" ] w)) + in + if single then Alcotest.(check int) "single day: exactly one real cell" 1 (List.length real_cells)) + (as_list (get [ "weeks" ] m))) + (as_list (get [ "months" ] v)) + (* The defect this whole branch exists to fix: no rendered day may show a slug where a name exists. Asserted over a whole year, not a sample. *) let test_no_day_shows_a_slug () = @@ -227,6 +289,9 @@ let suite = Alcotest.test_case "padding cells flagged" `Quick test_padding_cells_are_flagged; Alcotest.test_case "first week flagged" `Quick test_first_week_flag; Alcotest.test_case "padding and real days share key set" `Quick test_padding_and_real_share_key_set; + Alcotest.test_case "week num_roman and month fields" `Quick test_week_num_roman_and_month_fields; + Alcotest.test_case "week date span" `Quick test_week_date_span; + Alcotest.test_case "single_day agrees with span and cells" `Quick test_single_day_agrees_with_span_and_cells; Alcotest.test_case "no day shows a slug" `Quick test_no_day_shows_a_slug; Alcotest.test_case "slug unchanged by naming" `Quick test_slug_is_unchanged_by_naming; Alcotest.test_case "raw name equals slug" `Quick test_raw_name_equals_slug; |
