summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/cli.t12
-rw-r--r--test/test_lang_coverage.ml111
2 files changed, 122 insertions, 1 deletions
diff --git a/test/cli.t b/test/cli.t
index 8671694..86291a6 100644
--- a/test/cli.t
+++ b/test/cli.t
@@ -232,6 +232,18 @@ The OF rite resolves too, same two commands, its own vocabulary:
$ colitur readings --rite of 2026 | head -1
2026-01-01 of-mary-mother-of-god | Num 6:22-27 | Luc 2:16-21
+Fix wave I1 (final-review.md): a sanctoral-origin OF day names through its
+OWN transcribed title (calendar-2002.sexp, amended by 011's own rename),
+never through lang/la.ini's EF (1962) table -- 29 July, Martha, is one of
+the 18 real slug collisions between the two vocabularies the review found;
+la.ini's own entry for "martha" is the pre-2021 EF title "S. Marthae Virg.",
+and printing it here would be exactly the regression this guards.
+
+ $ colitur day --rite of 2026 | grep '^2026-07-29 '
+ 2026-07-29 wednesday ordinary-time 17 martha memoria-obligatoria white Ss. Marthae, Mariae et Lazari
+ $ colitur day --rite of 2026 --lang en | grep '^2026-07-29 '
+ 2026-07-29 wednesday ordinary-time 17 martha memoria-obligatoria white Saints Martha, Mary and Lazarus
+
An unrecognised --rite value is a usage error, not a silent fallback to ef
(exit 2):
diff --git a/test/test_lang_coverage.ml b/test/test_lang_coverage.ml
index d41f7e7..01aa9b7 100644
--- a/test/test_lang_coverage.ml
+++ b/test/test_lang_coverage.ml
@@ -295,6 +295,113 @@ let test_en_falls_back_to_latin () =
Alcotest.(check bool) "empty table falls back to la.ini for a real slug" true
(L.celebration chained "hilary" <> "hilary")
+(* ------------------------------------------------------------------ *)
+(* Fix wave I1 (final-review.md, 2026-08-25-colitur-of-phases-3-5): *)
+(* --rite of used to resolve every displayed name through lang/la.ini, *)
+(* EF's own independently-cited 1962-vocabulary table, keyed on slug. *)
+(* 18 of 222 shipped OF sanctoral slugs happen to collide with an EF *)
+(* slug there and all 18 printed the WRONG (1962) title -- e.g. *)
+(* "S. Marthae Virg." (a title the 1969 reform deleted, and which also *)
+(* silently overrode amendment 011's own rename) instead of *)
+(* calendar-2002.sexp's own "Ss. Marthae, Mariae et Lazari". The other *)
+(* 204 had no entry there at all and degraded to a bare slug, so none *)
+(* of the 208 hand-verified titles Task 1 transcribed (cited against *)
+(* the 2002 Missal) was ever reachable. *)
+(* *)
+(* bin/main.ml's [observed_name_of] now prefers the observed *)
+(* celebration's OWN {!Colitur_kernel.Celebration.names}, falling back *)
+(* to the lang table only on a miss. That priority order is duplicated *)
+(* here -- bin/ and test/ are separate dune stanzas that do not share *)
+(* code, the same reasoning test_support.ml's own header gives for why *)
+(* it duplicates bin/main.ml's EF loaders rather than requiring them -- *)
+(* and checked against every entry the REAL, assembled OF layer *)
+(* actually carries (calendar-2002.sexp + all 13 amendments), not a *)
+(* synthetic fixture. Walking {!Colitur_kernel.Layer.t.entries} directly *)
+(* rather than a resolved calendar year: several of the 18 real *)
+(* collisions (e.g. Margaret of Scotland, 16 November) never win the *)
+(* alphabetical tie-break against a same-day rival in ANY year and so *)
+(* are never the OBSERVED day at all -- the collision in the DATA is *)
+(* real and load-bearing regardless (a future tie-break or Calendar *)
+(* change could make her win one), so this test does not depend on *)
+(* which slugs happen to be observed in any particular year range. *)
+let of_base_path = "../data/of/calendar-2002.sexp"
+let of_amendments_dir = "../data/of/amendments/"
+let of_amendment_files =
+ [ "001-padre-pio.sexp"; "002-juan-diego-cuauhtlatoatzin.sexp"; "003-our-lady-of-guadalupe.sexp";
+ "004-john-xxiii-john-paul-ii.sexp"; "005-mary-magdalene-rank.sexp"; "006-mary-mother-of-the-church.sexp";
+ "007-paul-vi.sexp"; "008-our-lady-of-loreto.sexp"; "009-faustina-kowalska.sexp";
+ "010-narek-avila-hildegard.sexp"; "011-martha-mary-lazarus.sexp"; "012-teresa-of-calcutta.sexp";
+ "013-john-henry-newman.sexp" ]
+
+let of_layer () =
+ match Colitur_kernel.Layer.load Rite_of.Vocab_of.rank_of_sexp of_base_path with
+ | Error e -> Alcotest.failf "%s: %s" of_base_path e
+ | Ok base ->
+ let overlays =
+ List.map
+ (fun name ->
+ let path = of_amendments_dir ^ name in
+ match Colitur_kernel.Overlay.load Rite_of.Vocab_of.rank_of_sexp path with
+ | Ok o -> o
+ | Error e -> Alcotest.failf "%s: %s" path e)
+ of_amendment_files
+ in
+ let layer, diagnostics = Colitur_kernel.Overlay.merge base overlays in
+ if diagnostics <> [] then Alcotest.failf "unexpected OF amendment diagnostics on the shipped data";
+ layer
+
+let la_lang = Colitur_kernel.Lang.of_string_exn "la"
+
+(* [observed_name_of]'s own resolution order (bin/main.ml), duplicated
+ rather than called -- see this section's own header. *)
+let resolve_like_cli t (cel : Rite_of.Vocab_of.rank Colitur_kernel.Celebration.t) slug_s =
+ match Colitur_kernel.Names.find cel.Colitur_kernel.Celebration.names la_lang with
+ | Some n -> n
+ | None -> L.celebration t slug_s
+
+let test_of_names_win_over_ef_lang_table () =
+ let t = la () in
+ let layer = of_layer () in
+ let collisions = ref [] in
+ List.iter
+ (fun (e : _ Colitur_kernel.Layer.entry) ->
+ let cel = e.Colitur_kernel.Layer.cel in
+ let slug_s = Colitur_kernel.Slug.to_string cel.Colitur_kernel.Celebration.slug in
+ (* A miss returns the key itself (lang.mli's own TOTAL-lookup
+ contract): [L.celebration t slug_s <> slug_s] means la.ini really
+ does carry an entry for this OF slug -- a genuine collision, not
+ a hypothetical one. *)
+ if L.celebration t slug_s <> slug_s then collisions := slug_s :: !collisions)
+ layer.Colitur_kernel.Layer.entries;
+ (* Not asserting the collision count -- two independently-authored
+ vocabularies sharing a slug is expected (the whole reason this fix
+ exists) and not itself a defect; what matters, and what regresses
+ silently if [observed_name_of]'s priority order is ever reverted, is
+ what happens on EVERY one of them: *)
+ if !collisions = [] then
+ Alcotest.fail
+ "expected at least one real OF/EF slug collision (e.g. \"martha\") -- if this is now empty, \
+ either lang/la.ini or calendar-2002.sexp changed and this test's own premise needs re-checking, \
+ not silently passing on zero cases";
+ List.iter
+ (fun slug_s ->
+ match Colitur_kernel.Layer.find layer (Colitur_kernel.Slug.of_string_exn slug_s) with
+ | None -> Alcotest.failf "%s: found in entries but not via Layer.find" slug_s
+ | Some (e : _ Colitur_kernel.Layer.entry) -> (
+ let cel = e.Colitur_kernel.Layer.cel in
+ match Colitur_kernel.Names.find cel.Colitur_kernel.Celebration.names la_lang with
+ | None ->
+ Alcotest.failf
+ "%s: collides with lang/la.ini and calendar-2002.sexp carries NO own `la` name for it \
+ -- would silently print EF's 1962 title (%s)"
+ slug_s (L.celebration t slug_s)
+ | Some own_name ->
+ Alcotest.(check string)
+ (Printf.sprintf "%s: resolves to the OF's own transcribed name, not lang/la.ini's EF one"
+ slug_s)
+ own_name (resolve_like_cli t cel slug_s)))
+ !collisions
+
let suite =
( "Lang/coverage",
[ Alcotest.test_case "every slug has a Latin name" `Slow test_every_slug_has_a_latin_name;
@@ -307,4 +414,6 @@ let suite =
test_no_two_books_share_a_name;
Alcotest.test_case "shipped styles round-trip" `Quick
test_shipped_styles_round_trip;
- Alcotest.test_case "en.ini falls back to Latin" `Quick test_en_falls_back_to_latin ] )
+ Alcotest.test_case "en.ini falls back to Latin" `Quick test_en_falls_back_to_latin;
+ Alcotest.test_case "OF names win over lang/la.ini on every real slug collision" `Quick
+ test_of_names_win_over_ef_lang_table ] )