summaryrefslogtreecommitdiff
path: root/test/cli.t
diff options
context:
space:
mode:
Diffstat (limited to 'test/cli.t')
-rw-r--r--test/cli.t252
1 files changed, 226 insertions, 26 deletions
diff --git a/test/cli.t b/test/cli.t
index dfe5caa..610ce5b 100644
--- a/test/cli.t
+++ b/test/cli.t
@@ -17,7 +17,7 @@ A year outside the supported domain is rejected (exit 2):
No/garbage arguments give a usage error (exit 2):
$ colitur
- colitur: usage: colitur easter <year> | colitur temporal <year> | colitur day <year> | colitur readings <year> | colitur emit --format FMT --from Y --to Y | colitur table --year Y --template FILE | colitur render --template FILE --year Y | colitur publish --from Y --to Y --out DIR | colitur check FILE | colitur new-overlay (try: colitur --help)
+ colitur: usage: colitur easter <year> | colitur temporal <year> | colitur day <year> | colitur readings <year> | colitur emit --format FMT --from Y --to Y | colitur table --year Y --template FILE | colitur render --template FILE --year Y | colitur publish --from Y --to Y --out DIR | colitur lang --list|--dump CODE|--check FILE | colitur config --show | colitur check FILE | colitur new-overlay (try: colitur --help)
[2]
The EF temporal cycle for a year, one line per day:
@@ -53,20 +53,20 @@ Easter is the observed day exactly once, and carries no commemoration (an
impeded I class day admits at most one PRIVILEGED commemoration, RG 111, and
nothing outranks Easter to be impeded by it in the first place):
- $ colitur day 2026 | grep -c '^2026-04-05 '
+ $ colitur day 2026 --raw | grep -c '^2026-04-05 '
1
- $ colitur day 2026 | grep '^2026-04-05 '
+ $ colitur day 2026 --raw | grep '^2026-04-05 '
2026-04-05 sunday paschaltide 1 ef-easter-sunday class-1 white
Ash Wednesday: I class (RG 91 entry 7), violet, no numbered week (it falls 4
days before Lent I's own origin -- rite_ef/temporal_ef.ml's [week]):
- $ colitur day 2026 | grep '^2026-02-18 '
+ $ colitur day 2026 --raw | grep '^2026-02-18 '
2026-02-18 wednesday lent - ef-ash-wednesday class-1 violet
Christmas: I class, white (RG 91 entry 1):
- $ colitur day 2026 | grep '^2026-12-25 '
+ $ colitur day 2026 --raw | grep '^2026-12-25 '
2026-12-25 friday christmastide - ef-nativity class-1 white
All Souls (2 Nov, RG 91 entry 8) end to end: 2 Nov 2025 is a Sunday (verified
@@ -77,11 +77,11 @@ itself is not re-asserted here, already covered by test_temporal_ef.ml); RG
95 then transfers All Souls (I class, not a Sunday, not a vigil) and RG 96
places it on 3 Nov, the next day that is not I or II class:
- $ colitur day 2025 | grep -c 'commemoration-of-all-souls'
+ $ colitur day 2025 --raw | grep -c 'commemoration-of-all-souls'
1
- $ colitur day 2025 | grep '^2025-11-02 ' | sed -E 's/ [0-9]+ ef-time-after-pentecost-sunday-[0-9]+ / <week> ef-time-after-pentecost-sunday-<n> /'
+ $ colitur day 2025 --raw | grep '^2025-11-02 ' | sed -E 's/ [0-9]+ ef-time-after-pentecost-sunday-[0-9]+ / <week> ef-time-after-pentecost-sunday-<n> /'
2025-11-02 sunday time-after-pentecost <week> ef-time-after-pentecost-sunday-<n> class-2 green
- $ colitur day 2025 | grep '^2025-11-03 '
+ $ colitur day 2025 --raw | grep '^2025-11-03 '
2025-11-03 monday time-after-pentecost 21 commemoration-of-all-souls class-1 black
(week 21: Pentecost 2025 is 8 June (colitur easter 2025); 8 Jun - 3 Nov is
@@ -93,9 +93,9 @@ All Souls observed directly on 2 Nov in a year where it does not fall on a
Sunday: 2 Nov 2026 is a Monday (1 Jan 2026 is a Thursday, same day-of-year
306 offset, (4+305) mod 7 = 1 = Monday):
- $ colitur day 2026 | grep -c 'commemoration-of-all-souls'
+ $ colitur day 2026 --raw | grep -c 'commemoration-of-all-souls'
1
- $ colitur day 2026 | grep '^2026-11-02 '
+ $ colitur day 2026 --raw | grep '^2026-11-02 '
2026-11-02 monday time-after-pentecost 23 commemoration-of-all-souls class-1 black
(week 23: Pentecost 2026 is 24 May (colitur easter 2026); 24 May - 2 Nov is
@@ -109,9 +109,9 @@ RG 96 target -- not the Monday after Low Sunday -- is what governs, since
the general walk never crosses Easter. Before this fix the unconditional
reading sent it to 30 April (Easter + 8) instead:
- $ colitur day 2057 | grep '^2057-03-26 '
+ $ colitur day 2057 --raw | grep '^2057-03-26 '
2057-03-26 monday lent 3 annunciation-of-the-blessed-virgin-mary class-1 white +ef-lent-3-monday
- $ colitur day 2057 | grep -c 'annunciation-of-the-blessed-virgin-mary'
+ $ colitur day 2057 --raw | grep -c 'annunciation-of-the-blessed-virgin-mary'
1
A year outside the supported domain is rejected (exit 2):
@@ -138,7 +138,7 @@ sabbato", assigned to Christmas through the Purification (RG 309(a)). It is
NOT the Christmastide feria's Mass, which is what colitur said before that
rule was implemented.
- $ colitur readings 2026 | head -3
+ $ colitur readings 2026 --raw | head -3
2026-01-01 ef-circumcision | Titus 2:11-15 | Luke 2:21
2026-01-02 ef-christmas-1-friday | Titus 2:11-15 | Luke 2:21
2026-01-03 ef-christmas-1-saturday | Titus 3:4-7 | Luke 2:15-20
@@ -160,16 +160,16 @@ test_golden.ml pins against the photographic scans (each literal there was
read out of the Missal before being compared with colitur -- these assert
that the CLI path agrees with the library path, nothing more):
- $ colitur readings 2038 | grep '^2038-03-08 '
+ $ colitur readings 2038 --raw | grep '^2038-03-08 '
2038-03-08 john-of-god | Ecclus 31:8-11 | Matt 22:34-46
- $ colitur readings 2026 | grep '^2026-02-23 '
+ $ colitur readings 2026 --raw | grep '^2026-02-23 '
2026-02-23 ef-lent-1-monday | Ezech 34:11-16 | Matt 25:31-46
- $ colitur readings 2025 | grep '^2025-12-01 '
+ $ colitur readings 2025 --raw | grep '^2025-12-01 '
2025-12-01 ef-advent-1-monday | Rom 13:11-14 | Luke 21:25-33
- $ colitur readings 2038 | grep '^2038-03-06 '
+ $ colitur readings 2038 --raw | grep '^2038-03-06 '
2038-03-06 sts-felicitas-perpetua | Ecclus 51:1-8, 12 | Matt 13:44-52
The Advent line above is the ferial resumption (chain step 3) and is the one
@@ -190,7 +190,7 @@ rather than the guarantee itself:
`colitur day`'s own format is unaffected by any of the above:
- $ colitur day 2026 | head -1
+ $ colitur day 2026 --raw | head -1
2026-01-01 thursday christmastide - ef-circumcision class-1 white
A year outside the supported domain is rejected (exit 2):
@@ -280,14 +280,14 @@ four while appearing merely to add a local feast.
A fixed-date local patron, observed on its own day:
- $ colitur day 2026 --overlay fixtures/overlay-example-diocesan.sexp | grep '^2026-07-11 '
+ $ colitur day 2026 --overlay fixtures/overlay-example-diocesan.sexp --raw | grep '^2026-07-11 '
2026-07-11 saturday time-after-pentecost 6 example-local-patron class-3 white +pius-i
A MOVABLE one -- the capability the Date_spec work existed to unlock. "The
first Sunday of October" is the shape a patronal or dedication feast usually
takes, and it lands on a different civil date every year:
- $ for y in 2026 2027 2028; do colitur day $y --overlay fixtures/overlay-example-diocesan.sexp | grep 'example-dedication'; done
+ $ for y in 2026 2027 2028; do colitur day $y --overlay fixtures/overlay-example-diocesan.sexp --raw | grep 'example-dedication'; done
2026-10-04 sunday time-after-pentecost 19 example-dedication class-1 white +ef-time-after-pentecost-sunday-19
2027-10-03 sunday time-after-pentecost 20 example-dedication class-1 white +ef-time-after-pentecost-sunday-20
2028-10-01 sunday time-after-pentecost 17 example-dedication class-1 white +ef-time-after-pentecost-sunday-17
@@ -295,7 +295,7 @@ takes, and it lands on a different civil date every year:
The shipped calendar is untouched without the flag -- the same date, no local
feast, the ordinary Sunday restored:
- $ colitur day 2026 | grep '^2026-10-04 '
+ $ colitur day 2026 --raw | grep '^2026-10-04 '
2026-10-04 sunday time-after-pentecost 19 ef-time-after-pentecost-sunday-19 class-2 green
And the shipped overlay's own entries survive alongside the user's, which is
@@ -327,14 +327,14 @@ A flag needing a value, given none:
$ colitur day 2026 --overlay
colitur: --overlay needs a file path
- colitur: usage: colitur easter <year> | colitur temporal <year> | colitur day <year> | colitur readings <year> | colitur emit --format FMT --from Y --to Y | colitur table --year Y --template FILE | colitur render --template FILE --year Y | colitur publish --from Y --to Y --out DIR | colitur check FILE | colitur new-overlay (try: colitur --help)
+ colitur: usage: colitur easter <year> | colitur temporal <year> | colitur day <year> | colitur readings <year> | colitur emit --format FMT --from Y --to Y | colitur table --year Y --template FILE | colitur render --template FILE --year Y | colitur publish --from Y --to Y --out DIR | colitur lang --list|--dump CODE|--check FILE | colitur config --show | colitur check FILE | colitur new-overlay (try: colitur --help)
[2]
An unknown option is rejected rather than treated as a positional word:
$ colitur day 2026 --diocese
colitur: unknown option --diocese
- colitur: usage: colitur easter <year> | colitur temporal <year> | colitur day <year> | colitur readings <year> | colitur emit --format FMT --from Y --to Y | colitur table --year Y --template FILE | colitur render --template FILE --year Y | colitur publish --from Y --to Y --out DIR | colitur check FILE | colitur new-overlay (try: colitur --help)
+ colitur: usage: colitur easter <year> | colitur temporal <year> | colitur day <year> | colitur readings <year> | colitur emit --format FMT --from Y --to Y | colitur table --year Y --template FILE | colitur render --template FILE --year Y | colitur publish --from Y --to Y --out DIR | colitur lang --list|--dump CODE|--check FILE | colitur config --show | colitur check FILE | colitur new-overlay (try: colitur --help)
[2]
The shipped example overlay is runnable documentation, and it must actually
@@ -383,7 +383,7 @@ own output before emitting it. The full pipeline, INI to a resolved day:
p.sexp: ok -- overlay my-parish, 1 directive(s): 1 add, 0 suppress, 0 replace, 0 edit
every directive found its target
add our-patron
- $ colitur day 2026 --overlay p.sexp | grep '^2026-07-11'
+ $ colitur day 2026 --overlay p.sexp --raw | grep '^2026-07-11'
2026-07-11 saturday time-after-pentecost 6 our-patron class-3 white +pius-i
What the INI form cannot express is refused by name, not dropped silently:
@@ -413,7 +413,7 @@ An overlay feast competes under the ordinary rules: the Benedictine Transitus
is I class and takes 21 March, with the Lenten feria commemorated rather than
displaced silently.
- $ colitur day 2026 --overlay ben.sexp | grep '^2026-03-21'
+ $ colitur day 2026 --overlay ben.sexp --raw | grep '^2026-03-21'
2026-03-21 saturday lent 4 transitus-of-our-holy-father-benedict class-1 white +ef-lent-4-saturday
CSV emits a header and one row per day:
@@ -501,12 +501,90 @@ silently ignored, the same discipline --overlay already gets:
day and readings are untouched:
- $ colitur day 2027 | head -1
+ $ colitur day 2027 --raw | head -1
2027-01-01 friday christmastide - ef-circumcision class-1 white
+ $ colitur readings 2027 --raw | head -1
+ 2027-01-01 ef-circumcision | Titus 2:11-15 | Luke 2:21
+
+Names are Latin by default (Task 6): `day` gains a trailing display name and
+`readings` a trailing " | name" field, appended rather than substituted into
+the slug's own position -- a name contains spaces, and inserting it there
+would break every fixed-position field after it for anyone parsing this row
+by column, the same reason `readings` is a separate command in the first
+place. 1 January 2027 is the Circumcision, kept within the Nativity octave:
+
+ $ colitur day 2027 | head -2
+ 2027-01-01 friday christmastide - ef-circumcision class-1 white In Octava Nativitatis Domini
+ 2027-01-02 saturday christmastide - ef-christmas-1-saturday class-4 white Officium sanctae Mariae in sabbato
+
$ colitur readings 2027 | head -1
+ 2027-01-01 ef-circumcision | Titus 2:11-15 | Luke 2:21 | In Octava Nativitatis Domini
+
+--raw restores the old byte-exact output -- no trailing field at all, not
+merely an empty one, because [Lang.raw] is the identity table (lang.mli):
+the resolved name equals the slug already printed, so day_line/readings_line
+omit the field entirely rather than repeat the slug a second time. This is
+not a special case keyed on --raw itself; it is a direct consequence of the
+identity table, verified against the pre-branch binary in the task report
+rather than merely asserted:
+
+ $ colitur day 2027 --raw | head -1
+ 2027-01-01 friday christmastide - ef-circumcision class-1 white
+
+ $ colitur readings 2027 --raw | head -1
2027-01-01 ef-circumcision | Titus 2:11-15 | Luke 2:21
+A language file by path (--lang accepts a bare code OR a file path -- a
+value containing '/' or ending ".ini" is read literally rather than looked
+up in the installed language directory). The synthetic file below declares
+`fallback = la`, so every slug it does not name itself still resolves
+through the chain to la.ini's real name, and only the one it DOES name
+shows the override:
+
+ $ printf '[meta]\nlang = xx\nfallback = la\n[celebration]\nef-circumcision = TEST FEAST\n' > lang-xx.ini
+ $ colitur day 2027 --lang ./lang-xx.ini | head -1
+ 2027-01-01 friday christmastide - ef-circumcision class-1 white TEST FEAST
+
+An unknown language is a usage error naming what is available, never a
+silent fallback to Latin: a booklet quietly printed in the wrong language is
+worse than one that refuses to print. The exact "(looked in ...)" path is
+not pinned here -- it names the cram sandbox's own build tree, the same
+reason the data-directory fallback test above is not reproduced literally
+either:
+
+ $ colitur day 2027 --lang nonexistent 2>&1 | grep -c 'no language "nonexistent"'
+ 1
+ $ colitur day 2027 --lang nonexistent >/dev/null 2>&1; echo "exit=$?"
+ exit=2
+
+A malformed language file is an error, not a crash:
+
+ $ printf '[celebration\nbroken' > bad-lang.ini
+ $ colitur day 2027 --lang ./bad-lang.ini
+ colitur: ./bad-lang.ini: line 1: "[celebration" looks like a section header but does not end with ']'
+ [2]
+
+--lang/--raw have no effect on a command that reads no display names at
+all, refused rather than silently ignored, the same discipline --overlay
+already gets:
+
+ $ colitur easter 2026 --lang la
+ colitur: --lang/--raw have no effect on `easter`; refusing rather than ignoring them
+ [2]
+
+ $ colitur temporal 2026 --raw
+ colitur: --lang/--raw have no effect on `temporal`; refusing rather than ignoring them
+ [2]
+
+The ICS SUMMARY line is localised too (found while reviewing the published
+site: it used to read the kernel's own unlocalised "(class-1, white)" where
+the JSON beside it already said "II classis"/"albus" -- emit_ics.ml was
+reading the wrong pair of view fields):
+
+ $ colitur emit --format ics --from 2027 --to 2027 | grep '^SUMMARY' | head -1
+ SUMMARY:In Octava Nativitatis Domini (I classis\, albus)
+
A minimal inline template renders -- table computes and renders in one
process (2 January 2027 is a Saturday, not a Sunday, so Holy Name Sunday
falls on the 3rd, not the 2nd, that year):
@@ -1098,3 +1176,125 @@ over-broad: it still prunes normally when stale.
$ test -d /tmp/pub-sec/ef/2028 || echo pruned-2028
pruned-2028
+
+`colitur lang` and `colitur config` (Task 7) -- what makes "anyone can write
+a language file" true rather than merely permitted, the same idea the
+overlay system already established with `new-overlay` and `check`.
+
+`--list` shows what language files this build can find:
+
+ $ colitur lang --list
+ en (falls back to la)
+ la
+
+`--dump CODE` scaffolds a complete file on stdout, sections in a fixed order
+so two dumps of the same table are byte-identical:
+
+ $ colitur lang --dump la > d.ini
+ $ head -4 d.ini
+ [meta]
+ lang = la
+
+ [celebration]
+ $ grep -c '^ef-epiphany ' d.ini
+ 1
+
+A dump round-trips -- it just IS la.ini's own content, so it covers every
+slug the engine can produce over 2020-2045:
+
+ $ colitur lang --check d.ini
+ d.ini: 725 of 725 celebrations named, 0 missing, 0 unknown
+
+`--check` reports what is MISSING (a real slug with no entry):
+
+ $ printf '[meta]\nlang = zz\n[celebration]\nef-epiphany = Test\n' > partial.ini
+ $ colitur lang --check partial.ini | tail -1
+ partial.ini: 1 of 725 celebrations named, 724 missing, 0 unknown
+
+`--check` REJECTS an unknown slug (exit 1), so a typo is visible rather than
+silently dead -- its author would otherwise never learn why the name they
+wrote never appears:
+
+ $ printf '[meta]\nlang = zz\n[celebration]\nnot-a-real-slug = X\n' > typo.ini
+ $ colitur lang --check typo.ini | grep 'unknown slug'
+ unknown slug: not-a-real-slug
+ $ colitur lang --check typo.ini >/dev/null 2>&1; echo "exit=$?"
+ exit=1
+
+`lang` requires exactly one of --list/--dump/--check:
+
+ $ colitur lang
+ colitur: lang requires one of --list, --dump CODE or --check FILE
+ [2]
+
+ $ colitur lang --list --dump la
+ colitur: lang takes only one of --list, --dump CODE or --check FILE
+ [2]
+
+`--dump`/`--check`/`--list`/`--show` have no effect on any other command,
+refused rather than silently ignored, the same discipline --overlay/--lang
+already get:
+
+ $ colitur day 2027 --dump la
+ colitur: --dump/--check/--list/--show have no effect on `day`; refusing rather than ignoring them
+ [2]
+
+`config --show` reports each effective setting, its resolved value, and
+where it came from -- flag, config or default -- via Config.resolve; there
+is deliberately no separate provenance function. Run through an isolated
+XDG_CONFIG_HOME so this does not depend on whatever the person running the
+suite happens to have in their own home directory (the same reason the
+data-directory fallback case earlier in this file is not reproduced
+literally either):
+
+ $ XDG_CONFIG_HOME=xdg-test colitur config --show
+ config file: xdg-test/colitur/config.ini (not found)
+ lang la (default)
+ template (none) (default)
+ format (none) (default)
+ overlay (none) (default)
+
+A flag on the SAME command line previews exactly what it would resolve to
+on any other command -- `overlay` has no single scalar value, so it is
+listed one line per effective entry instead:
+
+ $ XDG_CONFIG_HOME=xdg-test colitur config --show --lang fr --overlay mine.sexp
+ config file: xdg-test/colitur/config.ini (not found)
+ lang fr (flag)
+ template (none) (default)
+ format (none) (default)
+ overlay mine.sexp (flag)
+
+A real config file supplies a default that a command with no explicit flag
+then uses. An unrecognised key and an unrecognised section are each warned
+about and ignored, never silently dropped and never fatal on their own --
+two separate warnings, so a misspelled section (`[deafults]`) reads
+differently from a misspelled key inside a recognised one:
+
+ $ mkdir -p xdg-test/colitur
+ $ printf '[defaults]\nlang = en\noverlay = /nonexistent/parish.sexp\nbogus = 1\n\n[deafults]\nlang = xx\n' > xdg-test/colitur/config.ini
+ $ XDG_CONFIG_HOME=xdg-test colitur config --show
+ config file: xdg-test/colitur/config.ini (exists)
+ lang en (config)
+ template (none) (default)
+ format (none) (default)
+ overlay /nonexistent/parish.sexp (config)
+ colitur: xdg-test/colitur/config.ini: unknown setting "bogus" (ignored)
+ colitur: xdg-test/colitur/config.ini: unknown section [deafults] (ignored)
+
+`config` requires --show:
+
+ $ colitur config
+ colitur: config requires --show
+ [2]
+
+A config file that EXISTS but fails to parse is fatal -- a config the user
+wrote and colitur cannot honour is not something to carry on past silently
+-- and this is true for every command, not only `config` itself, because
+the file is loaded once, unconditionally, before dispatch:
+
+ $ mkdir -p xdg-bad/colitur
+ $ printf '[defaults\nbroken' > xdg-bad/colitur/config.ini
+ $ XDG_CONFIG_HOME=xdg-bad colitur day 2027 --raw
+ colitur: xdg-bad/colitur/config.ini: line 1: "[defaults" looks like a section header but does not end with ']'
+ [2]