summaryrefslogtreecommitdiff
path: root/test/test_support.ml
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-08-19 11:48:30 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-08-19 11:48:30 +0200
commit6762ce46af3cb12bc6ae37cda762c5d95add7903 (patch)
treebc1d8c86050d0149ff961a9a4ff838f9c474ac2a /test/test_support.ml
parent897c274fd28402159ca6d45eedc1257b1ce98696 (diff)
parent390bc6ac5196a946c473d0dbe7760fa41837c428 (diff)
downloadcolitur-6762ce46af3cb12bc6ae37cda762c5d95add7903.tar.gz
colitur-6762ce46af3cb12bc6ae37cda762c5d95add7903.zip
feat: output, rendering and publishing
Gives colitur a publishable exit. Until now its only output was terminal rows; it can now print an ordo booklet and a wall calendar, publish an iCalendar feed people subscribe to, and serve a static JSON/XML API. lib/render escaping (six flavours + RFC 5545 folding), a deliberately logic-less template engine, the view model, and five emitters (CSV, JSON, XML, iCalendar, S-expression) CLI emit, table, render, publish -- all accepting --overlay templates ordo booklet in six flavours, wall grid in three schema day-v1.json and colitur-v1.xsd, the published contract man colitur-templates.5, plus colitur.1 updates The view model is why the engine can stay logic-less: a month grid needs leading blank cells, week bucketing and an in-month test, and a logic-less template can compute none of it. Shaping the data in OCaml keeps the engine safe for untrusted templates and makes the grid trivial. Formats split by whether correctness is mechanical. Presentation goes through templates; iCalendar and XML get dedicated emitters, because folding, exclusive DTEND, stable UIDs and schema fidelity are rules a template cannot enforce and each fails silently in a subscriber's client rather than loudly at generation. publish is deterministic and non-destructive: two runs produce a byte-identical tree, and --prune removes only files a previous run created, refusing any manifest entry that escapes the output directory. No new dependencies. The kernel and rite modules are untouched, and colitur day and colitur readings remain byte-identical.
Diffstat (limited to 'test/test_support.ml')
-rw-r--r--test/test_support.ml65
1 files changed, 65 insertions, 0 deletions
diff --git a/test/test_support.ml b/test/test_support.ml
new file mode 100644
index 0000000..7d7662f
--- /dev/null
+++ b/test/test_support.ml
@@ -0,0 +1,65 @@
+(* Loaders for the real EF data (data/ef/*.sexp), extracted from
+ bin/main.ml's [load_ef_layer]/[load_ef_lectionary]/[load_ef_commons]
+ (bin/main.ml:154, :193, :206) and simplified to what the test suite
+ needs: no [~user_overlays], no COLITUR_DATA_DIR/installed-prefix
+ indirection (that machinery exists so the CLI finds its data next to
+ wherever it was installed or built -- tests always run from test/, where
+ test/dune already declares the four data/ef/*.sexp paths as [deps]).
+ Reading them via the same "../data/ef/..." relative paths test_calendar.ml,
+ test_rite_ef.ml and test_lectionary_ef.ml already use, not [bin/]'s own
+ [data_dir ()].
+
+ Deliberately NOT shared as a library with bin/main.ml: [bin/] and [test/]
+ are separate dune stanzas, and three small loaders duplicated here is
+ cheaper than a new library built just to serve them. bin/main.ml is not
+ changed to use this module -- the CLI keeps its own copy, already pinned
+ by test/cli.t. *)
+
+let sanctoral_path = "../data/ef/sanctoral.sexp"
+let adjustments_path = "../data/ef/adjustments.sexp"
+let lectionary_path = "../data/ef/lectionary.sexp"
+let commons_path = "../data/ef/commons.sexp"
+
+(* Mirrors bin/main.ml's [load_ef_layer]: the shipped sanctoral layer with
+ the shipped adjustments overlay merged on top (RG 110's 30 June
+ companion, the Major Litanies, Barbara, Rogation Wednesday -- see
+ adjustments.sexp's own header). No user overlays: the test suite always
+ wants the plain shipped calendar. Diagnostics are discarded rather than
+ surfaced -- the shipped overlay is asserted elsewhere (test_overlay.ml,
+ the "check" path) to merge clean; a test that wants to see them can
+ still call {!Colitur_kernel.Overlay.merge} directly. *)
+let load_ef_layer () =
+ match Colitur_kernel.Layer.load Rite_ef.Vocab_ef.rank_of_sexp sanctoral_path with
+ | Error e -> Error (Printf.sprintf "failed to load %s: %s" sanctoral_path e)
+ | Ok layer -> (
+ match Colitur_kernel.Overlay.load Rite_ef.Vocab_ef.rank_of_sexp adjustments_path with
+ | Error e -> Error (Printf.sprintf "failed to load %s: %s" adjustments_path e)
+ | Ok overlay ->
+ let layer, _diagnostics = Colitur_kernel.Overlay.merge layer [ overlay ] in
+ Ok layer)
+
+(* Mirrors bin/main.ml's [load_ef_lectionary]. *)
+let load_ef_lectionary () =
+ match Colitur_kernel.Lectionary.load lectionary_path with
+ | Error e -> Error (Printf.sprintf "failed to load %s: %s" lectionary_path e)
+ | Ok l -> Ok l
+
+(* Mirrors bin/main.ml's [load_ef_commons]. *)
+let load_ef_commons () =
+ match Rite_ef.Lectionary_ef.Commons.load commons_path with
+ | Error e -> Error (Printf.sprintf "failed to load %s: %s" commons_path e)
+ | Ok c -> Ok c
+
+(* Assembles [Rite_ef.context] the way bin/main.ml:328 does, over the real
+ shipped lectionary and Commons. Raises on a load failure rather than
+ returning a [result]: the four data files are declared [deps] in
+ test/dune, so a failure here means the test tree itself is broken, the
+ same condition test_calendar.ml's and test_rite_ef.ml's own
+ module-level loaders already treat as fatal. *)
+let ef_context () =
+ match load_ef_lectionary () with
+ | Error e -> failwith e
+ | Ok lectionary -> (
+ match load_ef_commons () with
+ | Error e -> failwith e
+ | Ok commons -> Rite_ef.context ~lectionary ~commons)