aboutsummaryrefslogtreecommitdiff
path: root/mobile/mobile_test.go
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-08-03 23:59:09 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-08-03 23:59:09 +0200
commit27ceffcb90c0716179342109163c559d492a8abc (patch)
treefe34486e7cf7bbb4f49d624512fb3782c29cc319 /mobile/mobile_test.go
parent13d5e99ab90ab872c8cc072a0246348f0d24fa9a (diff)
downloadlectio-27ceffcb90c0716179342109163c559d492a8abc.tar.gz
lectio-27ceffcb90c0716179342109163c559d492a8abc.zip
mobile: add Days, a text-free week projection for the calendar
A week through Days benchmarks at ~9-10ms/op, versus ~23ms for seven full days through Day.
Diffstat (limited to 'mobile/mobile_test.go')
-rw-r--r--mobile/mobile_test.go83
1 files changed, 83 insertions, 0 deletions
diff --git a/mobile/mobile_test.go b/mobile/mobile_test.go
index c7df63c..57e835a 100644
--- a/mobile/mobile_test.go
+++ b/mobile/mobile_test.go
@@ -2,6 +2,7 @@ package mobile
import (
"encoding/json"
+ "strings"
"testing"
)
@@ -40,3 +41,85 @@ func TestDayHasColourKeyButNoColourLabel(t *testing.T) {
t.Error("colour_label must not be returned: the app draws a swatch")
}
}
+
+func decodeDays(t *testing.T, s string) []map[string]any {
+ t.Helper()
+ var a []map[string]any
+ if err := json.Unmarshal([]byte(s), &a); err != nil {
+ t.Fatalf("Days returned invalid JSON: %v\n%s", err, s)
+ }
+ return a
+}
+
+func TestDaysReturnsConsecutiveDates(t *testing.T) {
+ a := decodeDays(t, Days("2026-07-27", 7, "of", "pl"))
+ if len(a) != 7 {
+ t.Fatalf("got %d elements, want 7", len(a))
+ }
+ want := []string{"2026-07-27", "2026-07-28", "2026-07-29", "2026-07-30",
+ "2026-07-31", "2026-08-01", "2026-08-02"}
+ for i, w := range want {
+ if a[i]["date"] != w {
+ t.Errorf("element %d date = %v, want %v", i, a[i]["date"], w)
+ }
+ }
+}
+
+// Days must agree with Day about the same date -- it is a cheaper projection of
+// the same computation, not a second implementation.
+func TestDaysAgreesWithDay(t *testing.T) {
+ one := decodeDay(t, Day("2026-08-01", "of", "vul", "pl"))
+ week := decodeDays(t, Days("2026-08-01", 1, "of", "pl"))
+ if len(week) != 1 {
+ t.Fatalf("got %d elements, want 1", len(week))
+ }
+ row := week[0]
+ for _, k := range []string{"name", "colour", "rank", "rank_label"} {
+ if row[k] != one[k] {
+ t.Errorf("%s: Days = %v, Day = %v", k, row[k], one[k])
+ }
+ }
+ parts := row["parts"].([]any)
+ readings := one["readings"].([]any)
+ if len(parts) != len(readings) {
+ t.Fatalf("parts = %d, readings = %d", len(parts), len(readings))
+ }
+ for i := range parts {
+ p := parts[i].(map[string]any)
+ r := readings[i].(map[string]any)
+ if p["part"] != r["part"] || p["citation"] != r["citation"] {
+ t.Errorf("element %d: Days %v/%v vs Day %v/%v",
+ i, p["part"], p["citation"], r["part"], r["citation"])
+ }
+ }
+}
+
+// No reading text may cross the boundary: that is the whole point of Days.
+func TestDaysCarriesNoReadingText(t *testing.T) {
+ s := Days("2026-08-01", 7, "of", "pl")
+ if strings.Contains(s, `"text"`) {
+ t.Error("Days must not return reading text")
+ }
+ if strings.Contains(s, "colour_label") {
+ t.Error("Days must not return a colour label")
+ }
+}
+
+func TestDaysRejectsBadInput(t *testing.T) {
+ for _, c := range []struct {
+ start string
+ count int
+ }{
+ {"not-a-date", 7}, {"2026-08-01", 0}, {"2026-08-01", -1}, {"2026-08-01", 400},
+ } {
+ if got := Days(c.start, c.count, "of", "pl"); got != "[]" {
+ t.Errorf("Days(%q, %d) = %s, want []", c.start, c.count, got)
+ }
+ }
+}
+
+func BenchmarkDaysWeek(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ Days("2026-07-27", 7, "of", "pl")
+ }
+}