aboutsummaryrefslogtreecommitdiff
path: root/test/test_view.ml
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-08-19 15:29:33 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-08-19 15:29:33 +0200
commit6d367ab8e90f6e713d262a7f19fb908b28d4796a (patch)
treef5377f426ebe5ba32b7a8532850eafc9c103b9f9 /test/test_view.ml
parent92559825f9ad3e0f751d2c2022ffe000f23358ef (diff)
downloadcolitur-6d367ab8e90f6e713d262a7f19fb908b28d4796a.tar.gz
colitur-6d367ab8e90f6e713d262a7f19fb908b28d4796a.zip
feat(render): names reach the view and every emitter
The view's name is now the RESOLVED display string and slug is untouched, so machine formats carry both -- a script keeps the stable key, a human reads the name. name is a plain string, not a lang-keyed object. That removes the shadowing hazard outright: a dotted {{name.la}} used to fall back WHOLESALE to the enclosing month's name.la and print Ianuarius on every unnamed day, which is how a printed booklet came to show the month where the feast belonged. weekday, season, rank and colour all gain localised companions, because a calendar in a language needs more than feast names, and templates gain a term vocabulary so fixed strings need no template edit to translate. Asserted over a whole year: no day renders its slug as its name. Beyond the brief's own code sample: - comm_value's own name is now resolved through the same lang.celebration table too (not only the observed day's), because Task 8's own ordo template interpolates a plain {{name}} inside {{#comms}} -- an Obj there would render silently blank. A commemoration slug without Latin coverage still degrades to the slug, same as everywhere else in this system; that is a lang/la.ini DATA gap (113 of 327 sanctoral slugs, measured), not a regression this task introduced. - bin/main.ml's emit/table/publish call sites needed ~lang to compile at all, which is collateral from the of_days signature change, not this task's own file list. Rather than pass Lang.raw and ship the very slug-as-name defect this branch exists to fix, they load the shipped Latin table by the same probe order data_dir() already uses -- a deliberate, commented BRIDGE that Task 6 replaces wholesale with real --lang/--raw/config resolution. bin/dune gained colitur_naming accordingly. - test/cli.t needed two related fixes to stay green: the CSV header/row example, and a table/LaTeX escaping demonstration that relied on the kernel's own English name for Sts Peter & Paul -- gone from the view now that name resolves through lang tables only, and the Missal's own Latin spells the feast with et, never an ampersand. Escaping itself is still proved live on 2035 data in test_emit.ml. - Both schemas gained the new day/week/top-level keys (season_name, weekday, rank_name, colour_name, term, weekday_headings, month_num, month_name), not only the name shape change; schema/colitur-v1.xsd verified against real emitted XML via xmllint (make check-schema). Render/golden's 9 cases (the shipped ordo/grid templates, all six flavours) now fail as expected: their old {{name.la}} / {{#name}}... idiom finds nothing on a plain string. That is Tasks 8/9's own scope to rewrite, per the plan's own pre-flight conflict scan -- not fixed here, and not silently pinned by regenerating goldens off broken output. 495 tests run (490 + 5 new), 486 pass; the 9 failures are exactly Render/golden's ordo/grid cases.
Diffstat (limited to 'test/test_view.ml')
-rw-r--r--test/test_view.ml92
1 files changed, 91 insertions, 1 deletions
diff --git a/test/test_view.ml b/test/test_view.ml
index e595357..b49b090 100644
--- a/test/test_view.ml
+++ b/test/test_view.ml
@@ -24,8 +24,29 @@ let days_of_year y =
done;
List.rev !out
+(* Shared across this file and test_emit.ml: the ENGLISH table, chained to
+ Latin (en.ini's own [meta] fallback = la), so a slug en.ini does not name
+ directly still resolves through the chain rather than degrading to its
+ slug. English (not Latin, and not Lang.raw) is deliberate: it keeps the
+ emitter tests' own literal expectations -- "St. Joseph, Spouse of the Bl.
+ Virgin Mary" (a comma, for CSV quoting), "Sts. Fabian & Sebastian" (an
+ ampersand, for XML escaping) -- byte-identical to lang/en.ini's own
+ [celebration] entries, verified by grep against the shipped file rather
+ than assumed. *)
+let read_lang path =
+ let ic = open_in_bin path in
+ let s = really_input_string ic (in_channel_length ic) in
+ close_in ic;
+ match Colitur_naming.Lang.of_string s with
+ | Ok t -> t
+ | Error e -> Alcotest.failf "%s: %s" path e
+
+let default_lang =
+ lazy (Colitur_naming.Lang.with_fallback (read_lang "../lang/en.ini") (read_lang "../lang/la.ini"))
+
let view_of y =
- V.of_days ~vocab:Rite_ef.Vocab_ef.vocab ~rite:"ef" ~year:y (days_of_year y)
+ V.of_days ~lang:(Lazy.force default_lang) ~vocab:Rite_ef.Vocab_ef.vocab ~rite:"ef" ~year:y
+ (days_of_year y)
let get path v =
let rec go v = function
@@ -110,10 +131,79 @@ let test_exactly_one_colour_flag () =
if n <> 1 then Alcotest.failf "%s has %d colour flags set" (as_str (get [ "iso" ] d)) n)
(as_list (get [ "days" ] v))
+(* Padding cells and real days must carry the SAME key set: a template that
+ walks a grid row must never hit a missing key on a padding cell. Compares
+ the sorted key lists of a real day and a padding cell (the first cell of
+ January 2027's first week -- 1 Jan 2027 is a Friday, so that cell IS a
+ padding cell, see test_padding_cells_are_flagged above). *)
+let keys_of = function
+ | T.Obj kvs -> List.sort compare (List.map fst kvs)
+ | _ -> Alcotest.fail "expected an object"
+
+let test_padding_and_real_share_key_set () =
+ let v = view_of 2027 in
+ let jan = List.hd (as_list (get [ "months" ] v)) in
+ let first_week = List.hd (as_list (get [ "weeks" ] jan)) in
+ let cells = as_list (get [ "days" ] first_week) in
+ let padding = List.find (fun c -> not (as_bool (get [ "in_month" ] c))) cells in
+ let real = List.find (fun c -> as_bool (get [ "in_month" ] c)) cells in
+ Alcotest.(check (list string)) "padding and real days have the same key set" (keys_of real)
+ (keys_of padding)
+
+let latin () =
+ let ic = open_in_bin "../lang/la.ini" in
+ let s = really_input_string ic (in_channel_length ic) in
+ close_in ic;
+ match Colitur_naming.Lang.of_string s with
+ | Ok t -> t | Error e -> Alcotest.failf "la.ini: %s" e
+
+let view_named y = V.of_days ~lang:(latin ()) ~vocab:Rite_ef.Vocab_ef.vocab ~rite:"ef" ~year:y (days_of_year y)
+
+(* 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 () =
+ let v = view_named 2027 in
+ List.iter
+ (fun d ->
+ let name = as_str (get [ "name" ] d) and slug = as_str (get [ "slug" ] d) in
+ if name = slug then Alcotest.failf "%s renders its slug as its name" (as_str (get [ "iso" ] d));
+ if name = "" then Alcotest.failf "%s has an empty name" (as_str (get [ "iso" ] d)))
+ (as_list (get [ "days" ] v))
+
+let test_slug_is_unchanged_by_naming () =
+ let raw = V.of_days ~lang:Colitur_naming.Lang.raw ~vocab:Rite_ef.Vocab_ef.vocab ~rite:"ef" ~year:2027 (days_of_year 2027) in
+ let named = view_named 2027 in
+ List.iter2
+ (fun a b -> Alcotest.(check string) "slug identical" (as_str (get [ "slug" ] a)) (as_str (get [ "slug" ] b)))
+ (as_list (get [ "days" ] raw)) (as_list (get [ "days" ] named))
+
+(* Under --raw the name IS the slug: that is what makes raw output byte-stable. *)
+let test_raw_name_equals_slug () =
+ let raw = V.of_days ~lang:Colitur_naming.Lang.raw ~vocab:Rite_ef.Vocab_ef.vocab ~rite:"ef" ~year:2027 (days_of_year 2027) in
+ List.iter
+ (fun d -> Alcotest.(check string) "raw" (as_str (get [ "slug" ] d)) (as_str (get [ "name" ] d)))
+ (as_list (get [ "days" ] raw))
+
+(* Weekday, month, season, rank and colour must localise too -- a calendar in a
+ language needs more than feast names. *)
+let test_vocabularies_localise () =
+ let v = view_named 2027 in
+ let jan = List.hd (as_list (get [ "months" ] v)) in
+ Alcotest.(check string) "month name" "Ianuarius" (as_str (get [ "name" ] jan));
+ let d1 = List.hd (as_list (get [ "days" ] v)) in
+ Alcotest.(check string) "weekday" "Feria VI" (as_str (get [ "weekday" ] d1));
+ Alcotest.(check string) "rank" "I classis" (as_str (get [ "rank_name" ] d1));
+ Alcotest.(check string) "colour" "albus" (as_str (get [ "colour_name" ] d1))
+
let suite =
( "View",
[ Alcotest.test_case "year shape" `Quick test_year_shape;
Alcotest.test_case "weeks flatten to days" `Quick test_weeks_flatten_to_days;
Alcotest.test_case "padding cells flagged" `Quick test_padding_cells_are_flagged;
+ Alcotest.test_case "padding and real days share key set" `Quick test_padding_and_real_share_key_set;
+ 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;
+ Alcotest.test_case "vocabularies localise" `Quick test_vocabularies_localise;
Alcotest.test_case "day fields" `Quick test_day_fields;
Alcotest.test_case "exactly one colour flag" `Quick test_exactly_one_colour_flag ] )