oficina / calendar Public

Clone
git clone https://amadan.net/oficina/calendar

Plain git — no account needed to clone.

Download

Download this file

1package calendar
2
3import (
4 "bytes"
5 "encoding/base64"
6 "io"
7 "log/slog"
8 "path/filepath"
9 "strings"
10 "testing"
11 "time"
12)
13
14func TestFeedExportsOneMasterAndAllOverrides(t *testing.T) {
15 a := NewForTest(t, t.TempDir())
16 ctx := t.Context()
17 p, err := a.store.EnsurePersonSetup(ctx, "feed@example.test")
18 if err != nil {
19 t.Fatal(err)
20 }
21 cal, err := a.store.DefaultCalendarID(ctx, p.ID)
22 if err != nil {
23 t.Fatal(err)
24 }
25 in := EventInput{CalendarID: cal, Title: "Weekly", StartLocal: "2026-09-01T09:00", EndLocal: "2026-09-01T10:00", TZ: "UTC", RRule: "FREQ=DAILY;COUNT=30", Kind: KindMeeting, Status: StatusConfirmed}
26 master, err := a.store.CreateEvent(ctx, p.ID, in)
27 if err != nil {
28 t.Fatal(err)
29 }
30 in.RRule = ""
31 in.StartLocal, in.EndLocal = "2026-10-05T11:00", "2026-10-05T12:00"
32 if err := a.store.UpdateOccurrence(ctx, p.ID, master.ID, "2026-09-03T09:00", in); err != nil {
33 t.Fatal(err)
34 }
35 from := time.Date(2026, 9, 1, 0, 0, 0, 0, time.UTC).Unix()
36 rows, err := a.store.EventsForFeed(ctx, p.ID, cal, from, from+7*86400)
37 if err != nil {
38 t.Fatal(err)
39 }
40 out := WriteICS("Test", rows, false)
41 if len(rows) != 2 || strings.Count(out, "RRULE:") != 1 || strings.Count(out, "BEGIN:VEVENT") != 2 || !strings.Contains(out, "RECURRENCE-ID;TZID=UTC:20260903T0900") || !strings.Contains(out, "DTSTART;TZID=UTC:20261005T1100") {
42 t.Fatalf("lost master or moved override: %s", out)
43 }
44 if _, err := a.store.EventsForFeed(ctx, p.ID+100, cal, from, from+7*86400); err == nil {
45 t.Fatal("foreign feed accepted")
46 }
47 var overrideID int64
48 for _, row := range rows {
49 if row.SeriesID != nil {
50 overrideID = row.ID
51 }
52 }
53 if err := a.store.DeleteEvent(ctx, p.ID, overrideID); err != nil {
54 t.Fatal(err)
55 }
56 rows, err = a.store.EventsForFeed(ctx, p.ID, cal, from, from+7*86400)
57 if err != nil {
58 t.Fatal(err)
59 }
60 for _, row := range rows {
61 if row.ID == overrideID && row.Sequence <= master.Sequence+1 {
62 t.Fatal("cancellation did not supersede the live override")
63 }
64 }
65 if !strings.Contains(WriteICS("Test", rows, false), "STATUS:CANCELLED") {
66 t.Fatal("deleted override resurrected")
67 }
68}
69
70func TestBootRejectsWrongDataKeyWithAndWithoutSentinel(t *testing.T) {
71 for _, legacy := range []bool{false, true} {
72 t.Run(map[bool]string{false: "current", true: "legacy"}[legacy], func(t *testing.T) {
73 cfg := Config{DBPath: filepath.Join(t.TempDir(), "calendar.db"), Env: "development", Origin: TestOrigin, Logger: slog.New(slog.NewTextHandler(io.Discard, nil)), DEK: base64.StdEncoding.EncodeToString(bytes.Repeat([]byte{8}, 32))}
74 a, err := New(cfg)
75 if err != nil {
76 t.Fatal(err)
77 }
78 if _, err := a.store.EnsurePersonSetup(t.Context(), "key@example.test"); err != nil {
79 t.Fatal(err)
80 }
81 if legacy {
82 if err := a.db.G.Where("key = ?", keyCheckMeta).Delete(&Meta{}).Error; err != nil {
83 t.Fatal(err)
84 }
85 }
86 a.Close()
87 wrong := cfg
88 wrong.DEK = base64.StdEncoding.EncodeToString(bytes.Repeat([]byte{9}, 32))
89 if a, err := New(wrong); err == nil {
90 a.Close()
91 t.Fatal("boot accepted wrong key")
92 }
93 a, err = New(cfg)
94 if err != nil {
95 t.Fatal("correct key failed after refusal", err)
96 }
97 a.Close()
98 })
99 }
100}
101