aboutsummaryrefslogtreecommitdiff
path: root/test/test_names.ml
blob: f340c629c762a99e7c61b4b35e80b28493aa6f97 (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
module N = Colitur_kernel.Names
module L = Colitur_kernel.Lang
module C = Colitur_kernel.Citation
module DS = Colitur_kernel.Date_spec
module D = Colitur_kernel.Date

let lang s = L.of_string_exn s

let test_names_basics () =
  let n = N.of_list [ (lang "pl", "Wielkanoc"); (lang "la", "Pascha") ] in
  Alcotest.(check (option string)) "find la" (Some "Pascha") (N.find n (lang "la"));
  Alcotest.(check (option string)) "find en" None (N.find n (lang "en"));
  Alcotest.(check (option string)) "fallback la then en"
    (Some "Pascha") (N.find_first n [ lang "en"; lang "la" ]);
  (* canonical order is by language code, so sexp output is byte-stable *)
  Alcotest.(check (list string)) "canonical order" [ "la"; "pl" ]
    (List.map (fun (l, _) -> L.to_string l) (N.to_list n))

let test_names_set_remove () =
  let n = N.of_list [ (lang "la", "Pascha") ] in
  let n = N.set n (lang "en") "Easter" in
  Alcotest.(check (option string)) "set en" (Some "Easter") (N.find n (lang "en"));
  let n = N.set n (lang "en") "Easter Sunday" in
  Alcotest.(check (option string)) "last writer wins" (Some "Easter Sunday") (N.find n (lang "en"));
  let n = N.remove n (lang "en") in
  Alcotest.(check (option string)) "removed" None (N.find n (lang "en"))

let test_names_of_list_duplicates () =
  (* of_list with duplicate language: last entry wins *)
  let n = N.of_list [ (lang "en", "Easter"); (lang "en", "Easter Sunday") ] in
  Alcotest.(check (option string)) "of_list last wins" (Some "Easter Sunday") (N.find n (lang "en"))

let test_names_sexp_canonical () =
  (* Two Names.t with different input order must serialize identically *)
  let n1 = N.of_list [ (lang "pl", "Wielkanoc"); (lang "la", "Pascha"); (lang "en", "Easter") ] in
  let n2 = N.of_list [ (lang "en", "Easter"); (lang "pl", "Wielkanoc"); (lang "la", "Pascha") ] in
  Alcotest.(check bool) "same sexp despite different input order"
    true (N.sexp_of_t n1 = N.sexp_of_t n2)

let test_names_sexp_roundtrip () =
  let n = N.of_list [ (lang "la", "Pascha"); (lang "en", "Easter") ] in
  let sexp = N.sexp_of_t n in
  let n' = N.t_of_sexp sexp in
  Alcotest.(check bool) "sexp roundtrip" true (n = n')

let test_citation () =
  let c = { C.part = C.Gospel; reference = "Jn 3:16" } in
  Alcotest.(check bool) "sexp roundtrip" true (C.t_of_sexp (C.sexp_of_t c) = c);
  Alcotest.(check string) "part string" "gospel" (C.part_to_string C.Gospel);
  Alcotest.(check bool) "part of_string" true (C.part_of_string "first" = Some C.First)

let test_date_spec () =
  (match DS.fixed ~month:3 ~day:25 with
   | Ok ds -> (
       match DS.resolve ds ~year:2026 with
       | Some d -> Alcotest.(check string) "resolve" "2026-03-25" (D.to_iso8601 d)
       | None -> Alcotest.fail "resolve returned None")
   | Error e -> Alcotest.failf "fixed: %s" e);
  (* Feb 29 is a legitimate fixed date that simply does not occur every year. *)
  (match DS.fixed ~month:2 ~day:29 with
   | Ok ds ->
       Alcotest.(check bool) "Feb 29 resolves in 2024" true (DS.resolve ds ~year:2024 <> None);
       Alcotest.(check bool) "Feb 29 absent in 2026" true (DS.resolve ds ~year:2026 = None)
   | Error e -> Alcotest.failf "Feb 29 must be constructible: %s" e);
  Alcotest.(check bool) "reject Feb 30" true (Result.is_error (DS.fixed ~month:2 ~day:30));
  Alcotest.(check bool) "reject Apr 31" true (Result.is_error (DS.fixed ~month:4 ~day:31));
  Alcotest.(check bool) "reject month 13" true (Result.is_error (DS.fixed ~month:13 ~day:1))

let test_date_spec_sexp_roundtrip () =
  (match DS.fixed ~month:3 ~day:25 with
   | Ok ds ->
       let sexp = DS.sexp_of_t ds in
       let ds' = DS.t_of_sexp sexp in
       Alcotest.(check bool) "sexp roundtrip" true (ds = ds')
   | Error e -> Alcotest.failf "fixed: %s" e)

module Cel = Colitur_kernel.Celebration
module S = Colitur_kernel.Slug
module Col = Colitur_kernel.Colour
module Sub = Colitur_kernel.Subject

(* A throwaway rank vocabulary, to exercise the parametric type. *)
type demo_rank = High | Low [@@deriving sexp]

let test_celebration () =
  let c =
    Cel.make ~slug:(S.of_string_exn "ef-easter-sunday")
      ~names:(N.of_list [ (lang "la", "Dominica Resurrectionis") ])
      ~rank:High ~colour:Col.White ~subject:Sub.Lord ~layer:"temporal" ()
  in
  Alcotest.(check string) "slug" "ef-easter-sunday" (S.to_string c.Cel.slug);
  Alcotest.(check bool) "default citations empty" true (c.Cel.citations = []);
  let sexp = Cel.sexp_of_t sexp_of_demo_rank c in
  Alcotest.(check bool) "sexp roundtrip" true (Cel.t_of_sexp demo_rank_of_sexp sexp = c)

let suite =
  ( "Names/Citation/DateSpec",
    [ Alcotest.test_case "names basics" `Quick test_names_basics;
      Alcotest.test_case "names set/remove" `Quick test_names_set_remove;
      Alcotest.test_case "names of_list duplicates" `Quick test_names_of_list_duplicates;
      Alcotest.test_case "names sexp canonical" `Quick test_names_sexp_canonical;
      Alcotest.test_case "names sexp roundtrip" `Quick test_names_sexp_roundtrip;
      Alcotest.test_case "citation" `Quick test_citation;
      Alcotest.test_case "date_spec" `Quick test_date_spec;
      Alcotest.test_case "date_spec sexp roundtrip" `Quick test_date_spec_sexp_roundtrip;
      Alcotest.test_case "celebration" `Quick test_celebration ] )