aboutsummaryrefslogtreecommitdiff
path: root/test/test_rite_ef.ml
blob: bfc97dc0b442f62b2029488ae94032cd3cf0a4ec (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
(* Coordinator review (Task 11 fix round): integration tests wiring
   [Rite_ef.context] together with the REAL data/ef/sanctoral.sexp +
   data/ef/adjustments.sexp through [Colitur_kernel.Calendar] -- the same
   pipeline `colitur day` uses, proven here at the OCaml level so these
   properties are pinned by the test suite, not merely observable in CLI
   output (which is exactly what finding 3 flagged: with the overlay's one
   directive replaced by [()], `colitur day 2026`'s stdout is byte-identical
   for all 365 days, since [ef-nativity-vigil] always outranks
   [vigil-of-christmas] regardless -- the suppression's only observable
   effect is on [Liturgical_day.omitted], which no cram assertion reads). *)

module Cal = Colitur_kernel.Calendar
module Layer = Colitur_kernel.Layer
module Overlay = Colitur_kernel.Overlay
module LD = Colitur_kernel.Liturgical_day
module Slug = Colitur_kernel.Slug
module Date = Colitur_kernel.Date
module Date_spec = Colitur_kernel.Date_spec
module Cel = Colitur_kernel.Celebration
module Colour = Colitur_kernel.Colour
module V = Rite_ef.Vocab_ef

(* Relative to this test's own build directory (_build/default/test/), same
   convention test_sanctoral_ef.ml uses -- test/dune declares both as deps
   of the (test ...) stanza. *)
let sanctoral_path = "../data/ef/sanctoral.sexp"
let adjustments_path = "../data/ef/adjustments.sexp"

let mk y m d = match Date.make ~year:y ~month:m ~day:d with Ok t -> t | Error e -> failwith e

let real_layer () =
  let layer =
    match Layer.load V.rank_of_sexp sanctoral_path with
    | Ok l -> l
    | Error e -> Alcotest.failf "%s: failed to load: %s" sanctoral_path e
  in
  let overlay =
    match Overlay.load V.rank_of_sexp adjustments_path with
    | Ok o -> o
    | Error e -> Alcotest.failf "%s: failed to load: %s" adjustments_path e
  in
  let layer, diagnostics = Overlay.apply layer overlay in
  Alcotest.(check (list string)) "the committed overlay applies cleanly, no diagnostics" []
    (List.map Overlay.diagnostic_to_string diagnostics);
  layer

let slug_of (c : V.rank Cel.t) = Slug.to_string c.Cel.slug

(* Finding 3: the suppression's ONLY observable effect is on 24 December's
   [omitted] (and, belt-and-braces, on every OTHER field of every OTHER day
   too -- vigil-of-christmas must not surface anywhere at all, since the
   overlay removes it from the layer before resolution ever runs, unlike an
   ordinary occurrence loss). Mutation-verified (see the task report):
   substituting `(directives ())` for the real overlay makes this test's
   first assertion fail (24 December's [omitted] gains
   "vigil-of-christmas"), and confirmed the whole cram suite (test/cli.t)
   still passes under that same mutation -- so this closes the "removing
   the deliverable breaks no test" gap the coordinator flagged, which cram
   alone structurally cannot. *)
let test_vigil_of_christmas_suppressed () =
  let layer = real_layer () in
  let days = Cal.year Rite_ef.context layer 2026 in
  let christmas_eve =
    Array.to_list days |> List.find (fun d -> Date.compare d.LD.date (mk 2026 12 24) = 0)
  in
  Alcotest.(check string) "ef-nativity-vigil is still the observed day" "ef-nativity-vigil"
    (slug_of christmas_eve.LD.observed);
  Alcotest.(check (list string))
    "24 December has nothing omitted -- vigil-of-christmas never enters the RG91 contest at all"
    []
    (List.map (fun (c, _) -> slug_of c) christmas_eve.LD.omitted);
  let appears_anywhere =
    Array.to_list days
    |> List.exists (fun d ->
           let is_it c = slug_of c = "vigil-of-christmas" in
           is_it d.LD.observed
           || List.exists (fun (c, _) -> is_it c) d.LD.commemorations
           || List.exists (fun (c, _) -> is_it c) d.LD.omitted
           || (match d.LD.transferred_in with Some c -> is_it c | None -> false)
           || List.exists (fun (c, _) -> is_it c) d.LD.transferred_out)
  in
  Alcotest.(check bool) "vigil-of-christmas appears NOWHERE in the resolved year" false
    appears_anywhere

(* Coordinator review, finding 2, reproduced through the project's OWN
   extension path (an overlay), the same way the reviewer found it: adding
   an I-class feast on 25 December (competing against, and losing to, the
   real Nativity) forces an RG 96 search starting 26 December -- which, with
   the real sanctoral data (Stephen/John/the Innocents, all II class) plus
   the temporal cycle's own Nativity-octave-day entries (29-31 Dec, also II
   class), is blocking every single day through 31 December 9999. Before the
   domain-ceiling fix this raised (Computus: year 10000 out of range);
   confirmed by mutation-testing at the precedence_ef.ml unit level (see the
   task report) -- this is the same defect reproduced end to end, through
   Calendar, with real data, exactly as the review found it. *)
let test_transfer_search_does_not_raise_at_domain_ceiling () =
  let layer = real_layer () in
  let impeding_entry : V.rank Layer.entry =
    { Layer.date = (match Date_spec.fixed ~month:12 ~day:25 with Ok d -> d | Error e -> failwith e);
      cel =
        Cel.make ~slug:(Slug.of_string_exn "test-domain-ceiling-impeder") ~rank:V.Class1
          ~colour:Colour.White ~layer:Rite_ef.Precedence_ef.universal_layer () }
  in
  let overlay : V.rank Overlay.t =
    { Overlay.id = "test-domain-ceiling"; directives = [ Overlay.Add impeding_entry ] }
  in
  let layer, _diagnostics = Overlay.apply layer overlay in
  (* Must not raise -- the whole point of the fix. *)
  let days = Cal.year Rite_ef.context layer 9999 in
  Alcotest.(check bool) "year 9999 resolves without raising, even with an impeded Christmas Day"
    true (Array.length days > 0);
  let impeder_placed_or_recorded =
    Array.to_list days
    |> List.exists (fun d ->
           slug_of d.LD.observed = "test-domain-ceiling-impeder"
           || List.exists (fun (c, _) -> slug_of c = "test-domain-ceiling-impeder") d.LD.omitted)
  in
  Alcotest.(check bool) "the impeding candidate is accounted for (observed somewhere, or omitted \
                         with a recorded reason) -- never silently dropped"
    true impeder_placed_or_recorded

let suite =
  ( "Rite_ef (real data: overlay-in-effect, domain-ceiling)",
    [ Alcotest.test_case "the overlay suppression is observably in effect" `Quick
        test_vigil_of_christmas_suppressed;
      Alcotest.test_case "RG96 search does not raise at the domain ceiling (real data)" `Quick
        test_transfer_search_does_not_raise_at_domain_ceiling ] )