go-webdav/caldav/match_test.go

312 lines
7.2 KiB
Go
Raw Normal View History

package caldav
import (
"reflect"
"strings"
"testing"
"time"
"github.com/emersion/go-ical"
)
var dateFormat = "20060102T150405Z"
func toDate(t *testing.T, date string) time.Time {
res, err := time.ParseInLocation(dateFormat, date, time.UTC)
if err != nil {
t.Fatal(err)
}
return res
}
// Test data taken from https://datatracker.ietf.org/doc/html/rfc4791#appendix-B
// TODO add missing data
func TestFilter(t *testing.T) {
newCO := func(str string) CalendarObject {
cal, err := ical.NewDecoder(strings.NewReader(str)).Decode()
if err != nil {
t.Fatal(err)
}
return CalendarObject{
Data: cal,
}
}
event1 := newCO(`BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Example Corp.//CalDAV Client//EN
BEGIN:VTIMEZONE
LAST-MODIFIED:20040110T032845Z
TZID:US/Eastern
BEGIN:DAYLIGHT
DTSTART:20000404T020000
RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:20001026T020000
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
END:VTIMEZONE
BEGIN:VEVENT
DTSTAMP:20060206T001102Z
DTSTART;TZID=US/Eastern:20060102T100000
DURATION:PT1H
SUMMARY:Event #1
Description:Go Steelers!
UID:74855313FA803DA593CD579A@example.com
END:VEVENT
END:VCALENDAR`)
event2 := newCO(`BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Example Corp.//CalDAV Client//EN
BEGIN:VTIMEZONE
LAST-MODIFIED:20040110T032845Z
TZID:US/Eastern
BEGIN:DAYLIGHT
DTSTART:20000404T020000
RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:20001026T020000
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
END:VTIMEZONE
BEGIN:VEVENT
DTSTAMP:20060206T001121Z
DTSTART;TZID=US/Eastern:20060102T120000
DURATION:PT1H
RRULE:FREQ=DAILY;COUNT=5
SUMMARY:Event #2
UID:00959BC664CA650E933C892C@example.com
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20060206T001121Z
DTSTART;TZID=US/Eastern:20060104T140000
DURATION:PT1H
RECURRENCE-ID;TZID=US/Eastern:20060104T120000
SUMMARY:Event #2 bis
UID:00959BC664CA650E933C892C@example.com
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20060206T001121Z
DTSTART;TZID=US/Eastern:20060106T140000
DURATION:PT1H
RECURRENCE-ID;TZID=US/Eastern:20060106T120000
SUMMARY:Event #2 bis bis
UID:00959BC664CA650E933C892C@example.com
END:VEVENT
END:VCALENDAR`)
event3 := newCO(`BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Example Corp.//CalDAV Client//EN
BEGIN:VTIMEZONE
LAST-MODIFIED:20040110T032845Z
TZID:US/Eastern
BEGIN:DAYLIGHT
DTSTART:20000404T020000
RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:20001026T020000
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
END:VTIMEZONE
BEGIN:VEVENT
ATTENDEE;PARTSTAT=ACCEPTED;ROLE=CHAIR:mailto:cyrus@example.com
ATTENDEE;PARTSTAT=NEEDS-ACTION:mailto:lisa@example.com
DTSTAMP:20060206T001220Z
DTSTART;TZID=US/Eastern:20060104T100000
DURATION:PT1H
LAST-MODIFIED:20060206T001330Z
ORGANIZER:mailto:cyrus@example.com
SEQUENCE:1
STATUS:TENTATIVE
SUMMARY:Event #3
UID:DC6C50A017428C5216A2F1CD@example.com
END:VEVENT
END:VCALENDAR`)
todo1 := newCO(`BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Example Corp.//CalDAV Client//EN
BEGIN:VTODO
DTSTAMP:20060205T235335Z
DUE;VALUE=DATE:20060104
STATUS:NEEDS-ACTION
SUMMARY:Task #1
UID:DDDEEB7915FA61233B861457@example.com
BEGIN:VALARM
ACTION:AUDIO
TRIGGER;RELATED=START:-PT10M
END:VALARM
END:VTODO
END:VCALENDAR`)
for _, tc := range []struct {
name string
query *CalendarQuery
addrs []CalendarObject
want []CalendarObject
err error
}{
{
name: "nil-query",
query: nil,
addrs: []CalendarObject{event1, event2, event3, todo1},
want: []CalendarObject{event1, event2, event3, todo1},
},
{
// https://datatracker.ietf.org/doc/html/rfc4791#section-7.8.8
name: "events only",
query: &CalendarQuery{
CompFilter: CompFilter{
Name: "VCALENDAR",
Comps: []CompFilter{
CompFilter{
Name: "VEVENT",
},
},
},
},
addrs: []CalendarObject{event1, event2, event3, todo1},
want: []CalendarObject{event1, event2, event3},
},
{
// https://datatracker.ietf.org/doc/html/rfc4791#section-7.8.1
name: "events in time range",
query: &CalendarQuery{
CompFilter: CompFilter{
Name: "VCALENDAR",
Comps: []CompFilter{
CompFilter{
Name: "VEVENT",
Start: toDate(t, "20060104T000000Z"),
End: toDate(t, "20060105T000000Z"),
},
},
},
},
addrs: []CalendarObject{event1, event2, event3, todo1},
want: []CalendarObject{event2, event3},
},
{
// https://datatracker.ietf.org/doc/html/rfc4791#section-7.8.1
name: "events in open time range (no end date)",
query: &CalendarQuery{
CompFilter: CompFilter{
Name: "VCALENDAR",
Comps: []CompFilter{
CompFilter{
Name: "VEVENT",
Start: toDate(t, "20060104T000000Z"),
},
},
},
},
addrs: []CalendarObject{event1, event2, event3, todo1},
want: []CalendarObject{event2, event3},
},
{
// https://datatracker.ietf.org/doc/html/rfc4791#section-7.8.6
name: "events by UID",
query: &CalendarQuery{
CompFilter: CompFilter{
Name: "VCALENDAR",
Comps: []CompFilter{
CompFilter{
Name: "VEVENT",
Props: []PropFilter{{
Name: "UID",
TextMatch: &TextMatch{
Text: "DC6C50A017428C5216A2F1CD@example.com",
},
}},
},
},
},
},
addrs: []CalendarObject{event1, event2, event3, todo1},
want: []CalendarObject{event3},
},
{
// https://datatracker.ietf.org/doc/html/rfc4791#section-7.8.6
name: "events by description substring",
query: &CalendarQuery{
CompFilter: CompFilter{
Name: "VCALENDAR",
Comps: []CompFilter{
CompFilter{
Name: "VEVENT",
Props: []PropFilter{{
Name: "Description",
TextMatch: &TextMatch{
Text: "Steelers",
},
}},
},
},
},
},
addrs: []CalendarObject{event1, event2, event3, todo1},
want: []CalendarObject{event1},
},
{
// Query a time range that only returns a result if recurrence is properly evaluated.
name: "recurring events in time range",
query: &CalendarQuery{
CompFilter: CompFilter{
Name: "VCALENDAR",
Comps: []CompFilter{
CompFilter{
Name: "VEVENT",
Start: toDate(t, "20060103T000000Z"),
End: toDate(t, "20060104T000000Z"),
},
},
},
},
addrs: []CalendarObject{event1, event2, event3, todo1},
want: []CalendarObject{event2},
},
// TODO add more examples
} {
t.Run(tc.name, func(t *testing.T) {
got, err := Filter(tc.query, tc.addrs)
switch {
case err != nil && tc.err == nil:
t.Fatalf("unexpected error: %+v", err)
case err != nil && tc.err != nil:
if got, want := err.Error(), tc.err.Error(); got != want {
t.Fatalf("invalid error:\ngot= %q\nwant=%q", got, want)
}
case err == nil && tc.err != nil:
t.Fatalf("expected an error:\ngot= %+v\nwant=%+v", err, tc.err)
case err == nil && tc.err == nil:
if got, want := got, tc.want; !reflect.DeepEqual(got, want) {
t.Fatalf("invalid filter values:\ngot= %+v\nwant=%+v", got, want)
}
}
})
}
}