157 lines
6.5 KiB
Rust
157 lines
6.5 KiB
Rust
//! Tests of the `tp.date` module (spec D3).
|
|
|
|
use notescraft_bases::Value;
|
|
use notescraft_templater::{DateCtx, Dur, add_dur, call_date, parse_iso_duration};
|
|
use notescraft_templates::{Date, DateTime, Weekday};
|
|
|
|
/// The reference moment of the tests: Tuesday, 2026-10-13 15:04:05.
|
|
fn now() -> DateTime {
|
|
DateTime::new(Date::new(2026, 10, 13).unwrap(), 15, 4, 5).unwrap()
|
|
}
|
|
|
|
/// A context whose week starts on Monday.
|
|
fn monday_ctx() -> DateCtx {
|
|
DateCtx { now: now(), week_start: Weekday::Monday }
|
|
}
|
|
|
|
/// A context whose week starts on Sunday.
|
|
fn sunday_ctx() -> DateCtx {
|
|
DateCtx { now: now(), week_start: Weekday::Sunday }
|
|
}
|
|
|
|
/// A number argument.
|
|
fn number(value: f64) -> Value {
|
|
Value::Number(value)
|
|
}
|
|
|
|
/// A string argument.
|
|
fn text(value: &str) -> Value {
|
|
Value::Str(value.to_owned())
|
|
}
|
|
|
|
/// Calls the module and returns the formatted date.
|
|
fn call(name: &str, args: &[Value], ctx: &DateCtx) -> String {
|
|
match call_date(name, args, ctx) {
|
|
Ok(Value::Str(text)) => text,
|
|
Ok(other) => panic!("tp.date.{name} returned {other:?}"),
|
|
Err(error) => panic!("tp.date.{name} failed: {error}"),
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn now_formats_the_current_date() {
|
|
let ctx = monday_ctx();
|
|
assert_eq!(call("now", &[text("YYYY-MM-DD")], &ctx), "2026-10-13");
|
|
assert_eq!(call("now", &[text("dddd")], &ctx), "Tuesday");
|
|
assert_eq!(call("now", &[], &ctx), "2026-10-13");
|
|
assert_eq!(call("now", &[text("YYYY-MM-DDTHH:mm:ss")], &ctx), "2026-10-13T15:04:05");
|
|
}
|
|
|
|
#[test]
|
|
fn now_takes_a_day_offset() {
|
|
let ctx = monday_ctx();
|
|
assert_eq!(call("now", &[text("YYYY-MM-DD"), number(7.0)], &ctx), "2026-10-20");
|
|
assert_eq!(call("now", &[text("YYYY-MM-DD"), number(-7.0)], &ctx), "2026-10-06");
|
|
assert_eq!(call("now", &[text("YYYY-MM-DD"), number(0.0)], &ctx), "2026-10-13");
|
|
}
|
|
|
|
#[test]
|
|
fn now_takes_an_iso_offset() {
|
|
let ctx = monday_ctx();
|
|
assert_eq!(call("now", &[text("YYYY-MM-DD"), text("P1Y")], &ctx), "2027-10-13");
|
|
assert_eq!(call("now", &[text("YYYY-MM-DD"), text("-P1W")], &ctx), "2026-10-06");
|
|
assert_eq!(call("now", &[text("YYYY-MM-DD"), text("P1M2D")], &ctx), "2026-11-15");
|
|
assert_eq!(call("now", &[text("YYYY-MM-DD HH:mm"), text("PT2H")], &ctx), "2026-10-13 17:04");
|
|
}
|
|
|
|
#[test]
|
|
fn tomorrow_and_yesterday_move_one_day() {
|
|
let ctx = monday_ctx();
|
|
assert_eq!(call("tomorrow", &[text("YYYY-MM-DD")], &ctx), "2026-10-14");
|
|
assert_eq!(call("yesterday", &[text("YYYY-MM-DD")], &ctx), "2026-10-12");
|
|
assert_eq!(call("tomorrow", &[text("YYYY-MM-DD"), number(2.0)], &ctx), "2026-10-16");
|
|
assert_eq!(call("yesterday", &[text("YYYY-MM-DD"), text("-P1W")], &ctx), "2026-10-05");
|
|
}
|
|
|
|
#[test]
|
|
fn weekday_counts_from_a_monday_week() {
|
|
let ctx = monday_ctx();
|
|
assert_eq!(call("weekday", &[text("YYYY-MM-DD"), number(0.0)], &ctx), "2026-10-12");
|
|
assert_eq!(call("weekday", &[text("YYYY-MM-DD"), number(1.0)], &ctx), "2026-10-13");
|
|
assert_eq!(call("weekday", &[text("YYYY-MM-DD"), number(7.0)], &ctx), "2026-10-19");
|
|
assert_eq!(call("weekday", &[text("YYYY-MM-DD"), number(-7.0)], &ctx), "2026-10-05");
|
|
assert_eq!(call("weekday", &[], &ctx), "Monday");
|
|
}
|
|
|
|
#[test]
|
|
fn weekday_counts_from_a_sunday_week() {
|
|
let ctx = sunday_ctx();
|
|
assert_eq!(call("weekday", &[text("YYYY-MM-DD"), number(0.0)], &ctx), "2026-10-11");
|
|
assert_eq!(call("weekday", &[text("YYYY-MM-DD"), number(1.0)], &ctx), "2026-10-12");
|
|
assert_eq!(call("weekday", &[text("YYYY-MM-DD"), number(7.0)], &ctx), "2026-10-18");
|
|
assert_eq!(call("weekday", &[text("YYYY-MM-DD"), number(-7.0)], &ctx), "2026-10-04");
|
|
}
|
|
|
|
#[test]
|
|
fn a_reference_replaces_the_current_date() {
|
|
let ctx = monday_ctx();
|
|
let args = [text("YYYY-MM-DD"), number(0.0), text("2020-05-01"), text("YYYY-MM-DD")];
|
|
assert_eq!(call("now", &args, &ctx), "2020-05-01");
|
|
let shifted = [text("YYYY-MM-DD"), number(1.0), text("2020-05-01"), text("YYYY-MM-DD")];
|
|
assert_eq!(call("now", &shifted, &ctx), "2020-05-02");
|
|
let default_format = [text("YYYY-MM-DD"), number(0.0), text("2020-05-01")];
|
|
assert_eq!(call("now", &default_format, &ctx), "2020-05-01");
|
|
}
|
|
|
|
#[test]
|
|
fn a_reference_reads_a_date_out_of_a_title() {
|
|
let ctx = monday_ctx();
|
|
let title = [text("YYYY-MM-DD"), number(0.0), text("Sprint 2020-05-01 review"), text("YYYY-MM-DD")];
|
|
assert_eq!(call("now", &title, &ctx), "2020-05-01");
|
|
let stamp = [text("YYYY-MM-DD HH:mm"), number(0.0), text("Release 2020-05-01 09:30"), text("YYYY-MM-DD HH:mm")];
|
|
assert_eq!(call("now", &stamp, &ctx), "2020-05-01 09:30");
|
|
}
|
|
|
|
#[test]
|
|
fn a_month_ends_on_the_last_day_of_its_month() {
|
|
let ctx = monday_ctx();
|
|
let january = [text("YYYY-MM-DD"), text("P1M"), text("2026-01-31"), text("YYYY-MM-DD")];
|
|
assert_eq!(call("now", &january, &ctx), "2026-02-28");
|
|
let leap = [text("YYYY-MM-DD"), text("P1M"), text("2024-01-31"), text("YYYY-MM-DD")];
|
|
assert_eq!(call("now", &leap, &ctx), "2024-02-29");
|
|
let start = DateTime::new(Date::new(2026, 1, 31).unwrap(), 12, 0, 0).unwrap();
|
|
let month = parse_iso_duration("P1M").unwrap();
|
|
let shifted = add_dur(start, month).unwrap();
|
|
assert_eq!(shifted.date(), Date::new(2026, 2, 28).unwrap());
|
|
}
|
|
|
|
#[test]
|
|
fn iso_durations_parse() {
|
|
assert_eq!(parse_iso_duration("P1Y"), Some(Dur { years: 1, months: 0, days: 0, secs: 0 }));
|
|
assert_eq!(parse_iso_duration("-P1W"), Some(Dur { years: 0, months: 0, days: -7, secs: 0 }));
|
|
assert_eq!(parse_iso_duration("P1M2D"), Some(Dur { years: 0, months: 1, days: 2, secs: 0 }));
|
|
assert_eq!(parse_iso_duration("PT2H"), Some(Dur { years: 0, months: 0, days: 0, secs: 7200 }));
|
|
assert_eq!(parse_iso_duration("P1Y2M3DT4H5M6S"), Some(Dur { years: 1, months: 2, days: 3, secs: 14_706 }));
|
|
for text in ["", "P", "PT", "1Y", "P1Q", "P1H"] {
|
|
assert_eq!(parse_iso_duration(text), None, "{text}");
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn bad_arguments_are_refused() {
|
|
let ctx = monday_ctx();
|
|
assert!(call_date("now", &[text("YYYY-MM-DD"), text("P1Q")], &ctx).is_err());
|
|
assert!(call_date("now", &[Value::Bool(true)], &ctx).is_err());
|
|
assert!(call_date("now", &[text("YYYY-MM-DD"), number(0.0), text("not a date")], &ctx).is_err());
|
|
assert!(call_date("now", &[text("YYYY-MM-DD"), Value::Bool(true)], &ctx).is_err());
|
|
assert!(call_date("weekday", &[text("YYYY-MM-DD"), text("monday")], &ctx).is_err());
|
|
assert!(call_date("unknown", &[], &ctx).is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn unknown_format_characters_pass_through() {
|
|
let ctx = monday_ctx();
|
|
assert_eq!(call("now", &[text("[YYYY] YYYY!")], &ctx), "YYYY 2026!");
|
|
assert_eq!(call("now", &[text("QQQ")], &ctx), "QQQ");
|
|
}
|