package caldav
import (
"context"
"fmt"
"io"
"io/ioutil"
"net/http/httptest"
"strings"
"testing"
"time"
"github.com/emersion/go-ical"
)
var propFindSupportedCalendarComponentRequest = `
`
var testPropFindSupportedCalendarComponentCases = map[*Calendar][]string{
&Calendar{Path: "/user/calendars/cal"}: []string{"VEVENT"},
&Calendar{Path: "/user/calendars/cal", SupportedComponentSet: []string{"VTODO"}}: []string{"VTODO"},
&Calendar{Path: "/user/calendars/cal", SupportedComponentSet: []string{"VEVENT", "VTODO"}}: []string{"VEVENT", "VTODO"},
}
func TestPropFindSupportedCalendarComponent(t *testing.T) {
for calendar, expected := range testPropFindSupportedCalendarComponentCases {
req := httptest.NewRequest("PROPFIND", calendar.Path, nil)
req.Body = io.NopCloser(strings.NewReader(propFindSupportedCalendarComponentRequest))
req.Header.Set("Content-Type", "application/xml")
w := httptest.NewRecorder()
handler := Handler{Backend: testBackend{calendars: []Calendar{*calendar}}}
handler.ServeHTTP(w, req)
res := w.Result()
defer res.Body.Close()
data, err := ioutil.ReadAll(res.Body)
if err != nil {
t.Error(err)
}
resp := string(data)
for _, comp := range expected {
// Would be nicer to do a proper XML-decoding here, but this is probably good enough for now.
if !strings.Contains(resp, comp) {
t.Errorf("Expected component: %v not found in response:\n%v", comp, resp)
}
}
}
}
var propFindUserPrincipal = `
`
func TestPropFindRoot(t *testing.T) {
req := httptest.NewRequest("PROPFIND", "/", strings.NewReader(propFindUserPrincipal))
req.Header.Set("Content-Type", "application/xml")
w := httptest.NewRecorder()
calendar := &Calendar{}
handler := Handler{Backend: testBackend{calendars: []Calendar{*calendar}}}
handler.ServeHTTP(w, req)
res := w.Result()
defer res.Body.Close()
data, err := ioutil.ReadAll(res.Body)
if err != nil {
t.Error(err)
}
resp := string(data)
if !strings.Contains(resp, `/user/`) {
t.Errorf("No user-principal returned when doing a PROPFIND against root, response:\n%s", resp)
}
}
var reportCalendarData = `
%s
`
func TestMultiCalendarBackend(t *testing.T) {
calendarB := Calendar{Path: "/user/calendars/b", SupportedComponentSet: []string{"VTODO"}}
calendars := []Calendar{
Calendar{Path: "/user/calendars/a"},
calendarB,
}
eventSummary := "This is a todo"
event := ical.NewEvent()
event.Name = ical.CompToDo
event.Props.SetText(ical.PropUID, "46bbf47a-1861-41a3-ae06-8d8268c6d41e")
event.Props.SetDateTime(ical.PropDateTimeStamp, time.Now())
event.Props.SetText(ical.PropSummary, eventSummary)
cal := ical.NewCalendar()
cal.Props.SetText(ical.PropVersion, "2.0")
cal.Props.SetText(ical.PropProductID, "-//xyz Corp//NONSGML PDA Calendar Version 1.0//EN")
cal.Children = []*ical.Component{
event.Component,
}
object := CalendarObject{
Path: "/user/calendars/b/test.ics",
Data: cal,
}
req := httptest.NewRequest("PROPFIND", "/user/calendars/", strings.NewReader(propFindUserPrincipal))
req.Header.Set("Content-Type", "application/xml")
w := httptest.NewRecorder()
handler := Handler{Backend: testBackend{
calendars: calendars,
objectMap: map[string][]CalendarObject{
calendarB.Path: []CalendarObject{object},
},
}}
handler.ServeHTTP(w, req)
res := w.Result()
defer res.Body.Close()
data, err := ioutil.ReadAll(res.Body)
if err != nil {
t.Error(err)
}
resp := string(data)
for _, calendar := range calendars {
if !strings.Contains(resp, fmt.Sprintf(`%s`, calendar.Path)) {
t.Errorf("Calendar: %v not returned in PROPFIND, response:\n%s", calendar, resp)
}
}
// Now do a PROPFIND for the last calendar
req = httptest.NewRequest("PROPFIND", calendarB.Path, strings.NewReader(propFindSupportedCalendarComponentRequest))
req.Header.Set("Content-Type", "application/xml")
w = httptest.NewRecorder()
handler.ServeHTTP(w, req)
res = w.Result()
defer res.Body.Close()
data, err = ioutil.ReadAll(res.Body)
if err != nil {
t.Error(err)
}
resp = string(data)
if !strings.Contains(resp, "VTODO") {
t.Errorf("Expected component: VTODO not found in response:\n%v", resp)
}
if !strings.Contains(resp, object.Path) {
t.Errorf("Expected calendar object: %v not found in response:\n%v", object, resp)
}
// Now do a REPORT to get the actual data for the event
req = httptest.NewRequest("REPORT", calendarB.Path, strings.NewReader(fmt.Sprintf(reportCalendarData, object.Path)))
req.Header.Set("Content-Type", "application/xml")
w = httptest.NewRecorder()
handler.ServeHTTP(w, req)
res = w.Result()
defer res.Body.Close()
data, err = ioutil.ReadAll(res.Body)
if err != nil {
t.Error(err)
}
resp = string(data)
if !strings.Contains(resp, fmt.Sprintf("SUMMARY:%s", eventSummary)) {
t.Errorf("ICAL content not properly returned in response:\n%v", resp)
}
}
type testBackend struct {
calendars []Calendar
objectMap map[string][]CalendarObject
}
func (t testBackend) ListCalendars(ctx context.Context) ([]Calendar, error) {
return t.calendars, nil
}
func (t testBackend) GetCalendar(ctx context.Context, path string) (*Calendar, error) {
for _, cal := range t.calendars {
if cal.Path == path {
return &cal, nil
}
}
return nil, fmt.Errorf("Calendar for path: %s not found", path)
}
func (t testBackend) CalendarHomeSetPath(ctx context.Context) (string, error) {
return "/user/calendars/", nil
}
func (t testBackend) CurrentUserPrincipal(ctx context.Context) (string, error) {
return "/user/", nil
}
func (t testBackend) DeleteCalendarObject(ctx context.Context, path string) error {
return nil
}
func (t testBackend) GetCalendarObject(ctx context.Context, path string, req *CalendarCompRequest) (*CalendarObject, error) {
for _, objs := range t.objectMap {
for _, obj := range objs {
if obj.Path == path {
return &obj, nil
}
}
}
return nil, fmt.Errorf("Couldn't find calendar object at: %s", path)
}
func (t testBackend) PutCalendarObject(ctx context.Context, path string, calendar *ical.Calendar, opts *PutCalendarObjectOptions) (string, error) {
return "", nil
}
func (t testBackend) ListCalendarObjects(ctx context.Context, path string, req *CalendarCompRequest) ([]CalendarObject, error) {
return t.objectMap[path], nil
}
func (t testBackend) QueryCalendarObjects(ctx context.Context, path string, query *CalendarQuery) ([]CalendarObject, error) {
return nil, nil
}