656 lines
24 KiB
Go
656 lines
24 KiB
Go
//go:build test && dev
|
|
|
|
package dav
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"testing"
|
|
"time"
|
|
|
|
"crussell/clock"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// ============================================================================
|
|
// Pure Function Tests: extractContactURIsFromICalendar
|
|
// ============================================================================
|
|
|
|
func TestExtractContactURIsFromICalendar_Empty(t *testing.T) {
|
|
result := extractContactURIsFromICalendar("")
|
|
assert.Empty(t, result)
|
|
}
|
|
|
|
func TestExtractContactURIsFromICalendar_SingleAttendee(t *testing.T) {
|
|
ical := "BEGIN:VCALENDAR\nVERSION:2.0\nBEGIN:VEVENT\nUID:1\nATTENDEE:mailto:user@example.com\nEND:VEVENT\nEND:VCALENDAR"
|
|
result := extractContactURIsFromICalendar(ical)
|
|
assert.Equal(t, []string{"mailto:user@example.com"}, result)
|
|
}
|
|
|
|
func TestExtractContactURIsFromICalendar_MultipleAttendees(t *testing.T) {
|
|
ical := "BEGIN:VCALENDAR\nVERSION:2.0\nBEGIN:VEVENT\nUID:1\nATTENDEE:mailto:alice@example.com\nATTENDEE:mailto:bob@example.com\nEND:VEVENT\nEND:VCALENDAR"
|
|
result := extractContactURIsFromICalendar(ical)
|
|
assert.Equal(t, []string{"mailto:alice@example.com", "mailto:bob@example.com"}, result)
|
|
}
|
|
|
|
func TestExtractContactURIsFromICalendar_NoAttendee(t *testing.T) {
|
|
ical := "BEGIN:VCALENDAR\nVERSION:2.0\nBEGIN:VEVENT\nUID:1\nSUMMARY:Test\nEND:VEVENT\nEND:VCALENDAR"
|
|
result := extractContactURIsFromICalendar(ical)
|
|
assert.Empty(t, result)
|
|
}
|
|
|
|
func TestExtractContactURIsFromICalendar_AttendeeWithCN(t *testing.T) {
|
|
ical := "BEGIN:VCALENDAR\nVERSION:2.0\nBEGIN:VEVENT\nUID:1\nATTENDEE;CN=John Doe:mailto:john@example.com\nEND:VEVENT\nEND:VCALENDAR"
|
|
result := extractContactURIsFromICalendar(ical)
|
|
assert.Equal(t, []string{"mailto:john@example.com"}, result)
|
|
}
|
|
|
|
func TestExtractContactURIsFromICalendar_AttendeeNoColon(t *testing.T) {
|
|
ical := "BEGIN:VCALENDAR\nVERSION:2.0\nBEGIN:VEVENT\nUID:1\nATTENDEE\nEND:VEVENT\nEND:VCALENDAR"
|
|
result := extractContactURIsFromICalendar(ical)
|
|
assert.Empty(t, result)
|
|
}
|
|
|
|
// ============================================================================
|
|
// Read-Only DB Query Tests
|
|
// ============================================================================
|
|
|
|
func TestGetContactByURI_Found(t *testing.T) {
|
|
ctx := context.Background()
|
|
uri := "test-contact-found-" + t.Name()
|
|
now := time.Now().Unix()
|
|
|
|
t.Cleanup(func() {
|
|
_, _ = testSvc.db.Exec(ctx, "DELETE FROM dav_cards WHERE addressbookid = $1 AND uri = $2", 1, uri)
|
|
})
|
|
|
|
_, err := testSvc.db.Exec(ctx,
|
|
`INSERT INTO dav_cards (addressbookid, uri, carddata, lastmodified, etag, size) VALUES ($1, $2, $3, $4, $5, $6)`,
|
|
1, uri, "BEGIN:VCARD\nVERSION:3.0\nUID:test\nFN:Test User\nEND:VCARD", now, fmt.Sprintf("%d", now), 100,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
contact, err := testSvc.GetContactByURI(1, uri)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, contact)
|
|
assert.Equal(t, 1, contact.AddressBookID)
|
|
assert.Equal(t, uri, contact.URI)
|
|
assert.Equal(t, now, contact.LastModified)
|
|
}
|
|
|
|
func TestGetContactByURI_NotFound(t *testing.T) {
|
|
_, err := testSvc.GetContactByURI(1, "non-existent-uri-"+t.Name())
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "contact not found")
|
|
}
|
|
|
|
func TestListAllContacts_Empty(t *testing.T) {
|
|
ctx := context.Background()
|
|
|
|
_, err := testSvc.db.Exec(ctx, "DELETE FROM dav_cards WHERE uri LIKE 'test-%'")
|
|
require.NoError(t, err)
|
|
|
|
contacts, err := testSvc.ListAllContacts()
|
|
require.NoError(t, err)
|
|
assert.Empty(t, contacts)
|
|
}
|
|
|
|
func TestListAllContacts(t *testing.T) {
|
|
ctx := context.Background()
|
|
now := time.Now().Unix()
|
|
|
|
uris := []string{
|
|
"test-contact-list-1-" + t.Name(),
|
|
"test-contact-list-2-" + t.Name(),
|
|
"test-contact-list-3-" + t.Name(),
|
|
}
|
|
|
|
for _, uri := range uris {
|
|
_, err := testSvc.db.Exec(ctx,
|
|
`INSERT INTO dav_cards (addressbookid, uri, carddata, lastmodified, etag, size) VALUES ($1, $2, $3, $4, $5, $6)`,
|
|
1, uri, "BEGIN:VCARD\nVERSION:3.0\nUID:test\nFN:Test User\nEND:VCARD", now, fmt.Sprintf("%d", now), 100,
|
|
)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
t.Cleanup(func() {
|
|
for _, uri := range uris {
|
|
_, _ = testSvc.db.Exec(ctx, "DELETE FROM dav_cards WHERE addressbookid = $1 AND uri = $2", 1, uri)
|
|
}
|
|
})
|
|
|
|
contacts, err := testSvc.ListAllContacts()
|
|
require.NoError(t, err)
|
|
|
|
uriSet := make(map[string]bool)
|
|
for _, u := range uris {
|
|
uriSet[u] = true
|
|
}
|
|
found := 0
|
|
for _, c := range contacts {
|
|
if uriSet[c.URI] {
|
|
found++
|
|
}
|
|
}
|
|
assert.Equal(t, 3, found)
|
|
}
|
|
|
|
func TestListEventsForMonth(t *testing.T) {
|
|
ctx := context.Background()
|
|
now := time.Now().Unix()
|
|
|
|
julyEventUID := "test-july-event-" + t.Name()
|
|
augEventUID := "test-aug-event-" + t.Name()
|
|
|
|
julyStart := time.Date(2026, 7, 15, 9, 0, 0, 0, time.UTC)
|
|
julyEnd := time.Date(2026, 7, 15, 10, 0, 0, 0, time.UTC)
|
|
augStart := time.Date(2026, 8, 15, 9, 0, 0, 0, time.UTC)
|
|
augEnd := time.Date(2026, 8, 15, 10, 0, 0, 0, time.UTC)
|
|
|
|
julyCalData := "BEGIN:VCALENDAR\nVERSION:2.0\nBEGIN:VEVENT\nUID:" + julyEventUID + "\nDTSTART:20260715T090000Z\nDTEND:20260715T100000Z\nSUMMARY:July Event\nEND:VEVENT\nEND:VCALENDAR"
|
|
augCalData := "BEGIN:VCALENDAR\nVERSION:2.0\nBEGIN:VEVENT\nUID:" + augEventUID + "\nDTSTART:20260815T090000Z\nDTEND:20260815T100000Z\nSUMMARY:August Event\nEND:VEVENT\nEND:VCALENDAR"
|
|
|
|
_, err := testSvc.db.Exec(ctx,
|
|
`INSERT INTO dav_calendarobjects (calendarid, uri, calendardata, lastmodified, etag, size, componenttype, firstoccurence, lastoccurence, uid) VALUES ($1, $2, $3, $4, $5, $6, 'VEVENT', $7, $8, $9)`,
|
|
1, julyEventUID+".ics", julyCalData, now, fmt.Sprintf("%d", now), len(julyCalData), julyStart.Unix(), julyEnd.Unix(), julyEventUID,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
_, err = testSvc.db.Exec(ctx,
|
|
`INSERT INTO dav_calendarobjects (calendarid, uri, calendardata, lastmodified, etag, size, componenttype, firstoccurence, lastoccurence, uid) VALUES ($1, $2, $3, $4, $5, $6, 'VEVENT', $7, $8, $9)`,
|
|
1, augEventUID+".ics", augCalData, now, fmt.Sprintf("%d", now), len(augCalData), augStart.Unix(), augEnd.Unix(), augEventUID,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
t.Cleanup(func() {
|
|
_, _ = testSvc.db.Exec(ctx, "DELETE FROM dav_calendarobjects WHERE uid = $1", julyEventUID)
|
|
_, _ = testSvc.db.Exec(ctx, "DELETE FROM dav_calendarobjects WHERE uid = $1", augEventUID)
|
|
})
|
|
|
|
events, err := testSvc.ListEventsForMonth(2026, time.July)
|
|
require.NoError(t, err)
|
|
|
|
var foundJuly, foundAug bool
|
|
for _, e := range events {
|
|
if e.UID == julyEventUID {
|
|
foundJuly = true
|
|
}
|
|
if e.UID == augEventUID {
|
|
foundAug = true
|
|
}
|
|
}
|
|
assert.True(t, foundJuly, "July event should be found for July 2026 query")
|
|
assert.False(t, foundAug, "August event should not be found for July 2026 query")
|
|
}
|
|
|
|
func TestListEventsBetween(t *testing.T) {
|
|
ctx := context.Background()
|
|
now := time.Now().Unix()
|
|
|
|
julyEventUID := "test-july-between-" + t.Name()
|
|
augEventUID := "test-aug-between-" + t.Name()
|
|
|
|
julyStart := time.Date(2026, 7, 15, 9, 0, 0, 0, time.UTC)
|
|
julyEnd := time.Date(2026, 7, 15, 10, 0, 0, 0, time.UTC)
|
|
augStart := time.Date(2026, 8, 15, 9, 0, 0, 0, time.UTC)
|
|
augEnd := time.Date(2026, 8, 15, 10, 0, 0, 0, time.UTC)
|
|
|
|
julyCalData := "BEGIN:VCALENDAR\nVERSION:2.0\nBEGIN:VEVENT\nUID:" + julyEventUID + "\nDTSTART:20260715T090000Z\nDTEND:20260715T100000Z\nSUMMARY:July Event\nEND:VEVENT\nEND:VCALENDAR"
|
|
augCalData := "BEGIN:VCALENDAR\nVERSION:2.0\nBEGIN:VEVENT\nUID:" + augEventUID + "\nDTSTART:20260815T090000Z\nDTEND:20260815T100000Z\nSUMMARY:August Event\nEND:VEVENT\nEND:VCALENDAR"
|
|
|
|
_, err := testSvc.db.Exec(ctx,
|
|
`INSERT INTO dav_calendarobjects (calendarid, uri, calendardata, lastmodified, etag, size, componenttype, firstoccurence, lastoccurence, uid) VALUES ($1, $2, $3, $4, $5, $6, 'VEVENT', $7, $8, $9)`,
|
|
1, julyEventUID+".ics", julyCalData, now, fmt.Sprintf("%d", now), len(julyCalData), julyStart.Unix(), julyEnd.Unix(), julyEventUID,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
_, err = testSvc.db.Exec(ctx,
|
|
`INSERT INTO dav_calendarobjects (calendarid, uri, calendardata, lastmodified, etag, size, componenttype, firstoccurence, lastoccurence, uid) VALUES ($1, $2, $3, $4, $5, $6, 'VEVENT', $7, $8, $9)`,
|
|
1, augEventUID+".ics", augCalData, now, fmt.Sprintf("%d", now), len(augCalData), augStart.Unix(), augEnd.Unix(), augEventUID,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
t.Cleanup(func() {
|
|
_, _ = testSvc.db.Exec(ctx, "DELETE FROM dav_calendarobjects WHERE uid = $1", julyEventUID)
|
|
_, _ = testSvc.db.Exec(ctx, "DELETE FROM dav_calendarobjects WHERE uid = $1", augEventUID)
|
|
})
|
|
|
|
start := time.Date(2026, 7, 1, 0, 0, 0, 0, time.UTC)
|
|
end := time.Date(2026, 7, 31, 23, 59, 59, 0, time.UTC)
|
|
|
|
events, err := testSvc.ListEventsBetween(start, end)
|
|
require.NoError(t, err)
|
|
|
|
var foundJuly, foundAug bool
|
|
for _, e := range events {
|
|
if e.UID == julyEventUID {
|
|
foundJuly = true
|
|
}
|
|
if e.UID == augEventUID {
|
|
foundAug = true
|
|
}
|
|
}
|
|
assert.True(t, foundJuly, "July event should be found within July range")
|
|
assert.False(t, foundAug, "August event should not be found within July range")
|
|
}
|
|
|
|
// ============================================================================
|
|
// Part A: Time-Sensitive Query Tests
|
|
// ============================================================================
|
|
|
|
func TestListEventsTomorrow(t *testing.T) {
|
|
ctx := context.Background()
|
|
now := clock.Now()
|
|
|
|
tomorrow := time.Date(now.Year(), now.Month(), now.Day()+1, 0, 0, 0, 0, time.UTC)
|
|
|
|
inUID := "test-tomorrow-in-" + t.Name()
|
|
inStart := tomorrow.Add(2 * time.Hour)
|
|
inEnd := inStart.Add(1 * time.Hour)
|
|
inCalData := "BEGIN:VCALENDAR\nVERSION:2.0\nBEGIN:VEVENT\nUID:" + inUID + "\nDTSTART:" + inStart.Format("20060102T150405Z") + "\nDTEND:" + inEnd.Format("20060102T150405Z") + "\nSUMMARY:Tomorrow Event\nEND:VEVENT\nEND:VCALENDAR"
|
|
|
|
outUID := "test-tomorrow-out-" + t.Name()
|
|
yesterday := now.AddDate(0, 0, -1)
|
|
outStart := time.Date(yesterday.Year(), yesterday.Month(), yesterday.Day(), 12, 0, 0, 0, time.UTC)
|
|
outEnd := outStart.Add(1 * time.Hour)
|
|
outCalData := "BEGIN:VCALENDAR\nVERSION:2.0\nBEGIN:VEVENT\nUID:" + outUID + "\nDTSTART:" + outStart.Format("20060102T150405Z") + "\nDTEND:" + outEnd.Format("20060102T150405Z") + "\nSUMMARY:Yesterday Event\nEND:VEVENT\nEND:VCALENDAR"
|
|
|
|
_, err := testSvc.db.Exec(ctx,
|
|
`INSERT INTO dav_calendarobjects (calendarid, uri, calendardata, lastmodified, etag, size, componenttype, firstoccurence, lastoccurence, uid) VALUES ($1, $2, $3, $4, $5, $6, 'VEVENT', $7, $8, $9)`,
|
|
1, inUID+".ics", inCalData, now.Unix(), fmt.Sprintf("%d", now.Unix()), len(inCalData), inStart.Unix(), inEnd.Unix(), inUID,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
_, err = testSvc.db.Exec(ctx,
|
|
`INSERT INTO dav_calendarobjects (calendarid, uri, calendardata, lastmodified, etag, size, componenttype, firstoccurence, lastoccurence, uid) VALUES ($1, $2, $3, $4, $5, $6, 'VEVENT', $7, $8, $9)`,
|
|
1, outUID+".ics", outCalData, now.Unix(), fmt.Sprintf("%d", now.Unix()), len(outCalData), outStart.Unix(), outEnd.Unix(), outUID,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
t.Cleanup(func() {
|
|
_, _ = testSvc.db.Exec(ctx, "DELETE FROM dav_calendarobjects WHERE uid = $1", inUID)
|
|
_, _ = testSvc.db.Exec(ctx, "DELETE FROM dav_calendarobjects WHERE uid = $1", outUID)
|
|
})
|
|
|
|
events, err := testSvc.ListEventsTomorrow()
|
|
require.NoError(t, err)
|
|
|
|
var foundIn, foundOut bool
|
|
for _, e := range events {
|
|
if e.UID == inUID {
|
|
foundIn = true
|
|
}
|
|
if e.UID == outUID {
|
|
foundOut = true
|
|
}
|
|
}
|
|
assert.True(t, foundIn, "event with firstoccurence tomorrow should be returned")
|
|
assert.False(t, foundOut, "event with firstoccurence yesterday should not be returned")
|
|
}
|
|
|
|
func TestListEventsThisWeek(t *testing.T) {
|
|
ctx := context.Background()
|
|
now := clock.Now()
|
|
|
|
weekday := int(now.Weekday())
|
|
if weekday == 0 {
|
|
weekday = 7
|
|
}
|
|
monday := now.AddDate(0, 0, -weekday+1)
|
|
monday = time.Date(monday.Year(), monday.Month(), monday.Day(), 0, 0, 0, 0, time.UTC)
|
|
|
|
inUID := "test-week-in-" + t.Name()
|
|
inStart := monday.Add(48 * time.Hour)
|
|
inEnd := inStart.Add(1 * time.Hour)
|
|
inCalData := "BEGIN:VCALENDAR\nVERSION:2.0\nBEGIN:VEVENT\nUID:" + inUID + "\nDTSTART:" + inStart.Format("20060102T150405Z") + "\nDTEND:" + inEnd.Format("20060102T150405Z") + "\nSUMMARY:This Week Event\nEND:VEVENT\nEND:VCALENDAR"
|
|
|
|
outUID := "test-week-out-" + t.Name()
|
|
outStart := monday.AddDate(0, 0, -2)
|
|
outEnd := outStart.Add(1 * time.Hour)
|
|
outCalData := "BEGIN:VCALENDAR\nVERSION:2.0\nBEGIN:VEVENT\nUID:" + outUID + "\nDTSTART:" + outStart.Format("20060102T150405Z") + "\nDTEND:" + outEnd.Format("20060102T150405Z") + "\nSUMMARY:Last Week Event\nEND:VEVENT\nEND:VCALENDAR"
|
|
|
|
_, err := testSvc.db.Exec(ctx,
|
|
`INSERT INTO dav_calendarobjects (calendarid, uri, calendardata, lastmodified, etag, size, componenttype, firstoccurence, lastoccurence, uid) VALUES ($1, $2, $3, $4, $5, $6, 'VEVENT', $7, $8, $9)`,
|
|
1, inUID+".ics", inCalData, now.Unix(), fmt.Sprintf("%d", now.Unix()), len(inCalData), inStart.Unix(), inEnd.Unix(), inUID,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
_, err = testSvc.db.Exec(ctx,
|
|
`INSERT INTO dav_calendarobjects (calendarid, uri, calendardata, lastmodified, etag, size, componenttype, firstoccurence, lastoccurence, uid) VALUES ($1, $2, $3, $4, $5, $6, 'VEVENT', $7, $8, $9)`,
|
|
1, outUID+".ics", outCalData, now.Unix(), fmt.Sprintf("%d", now.Unix()), len(outCalData), outStart.Unix(), outEnd.Unix(), outUID,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
t.Cleanup(func() {
|
|
_, _ = testSvc.db.Exec(ctx, "DELETE FROM dav_calendarobjects WHERE uid = $1", inUID)
|
|
_, _ = testSvc.db.Exec(ctx, "DELETE FROM dav_calendarobjects WHERE uid = $1", outUID)
|
|
})
|
|
|
|
events, err := testSvc.ListEventsThisWeek()
|
|
require.NoError(t, err)
|
|
|
|
var foundIn, foundOut bool
|
|
for _, e := range events {
|
|
if e.UID == inUID {
|
|
foundIn = true
|
|
}
|
|
if e.UID == outUID {
|
|
foundOut = true
|
|
}
|
|
}
|
|
assert.True(t, foundIn, "event within this week should be returned")
|
|
assert.False(t, foundOut, "event outside this week should not be returned")
|
|
}
|
|
|
|
func TestListRecentContacts(t *testing.T) {
|
|
ctx := context.Background()
|
|
now := time.Now()
|
|
|
|
recentURI := "test-recent-contact-" + t.Name()
|
|
oldURI := "test-old-contact-" + t.Name()
|
|
|
|
recentLastModified := now.AddDate(0, 0, -5).Unix()
|
|
oldLastModified := now.AddDate(0, 0, -60).Unix()
|
|
|
|
_, err := testSvc.db.Exec(ctx,
|
|
`INSERT INTO dav_cards (addressbookid, uri, carddata, lastmodified, etag, size) VALUES ($1, $2, $3, $4, $5, $6)`,
|
|
1, recentURI, "BEGIN:VCARD\nVERSION:3.0\nFN:Recent\nEND:VCARD", recentLastModified, fmt.Sprintf("%d", recentLastModified), 100,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
_, err = testSvc.db.Exec(ctx,
|
|
`INSERT INTO dav_cards (addressbookid, uri, carddata, lastmodified, etag, size) VALUES ($1, $2, $3, $4, $5, $6)`,
|
|
1, oldURI, "BEGIN:VCARD\nVERSION:3.0\nFN:Old\nEND:VCARD", oldLastModified, fmt.Sprintf("%d", oldLastModified), 100,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
t.Cleanup(func() {
|
|
_, _ = testSvc.db.Exec(ctx, "DELETE FROM dav_cards WHERE uri = $1", recentURI)
|
|
_, _ = testSvc.db.Exec(ctx, "DELETE FROM dav_cards WHERE uri = $1", oldURI)
|
|
})
|
|
|
|
contacts, err := testSvc.ListRecentContacts(30)
|
|
require.NoError(t, err)
|
|
|
|
var foundRecent, foundOld bool
|
|
for _, c := range contacts {
|
|
if c.URI == recentURI {
|
|
foundRecent = true
|
|
}
|
|
if c.URI == oldURI {
|
|
foundOld = true
|
|
}
|
|
}
|
|
assert.True(t, foundRecent, "recent contact (5 days old) should be returned with 30-day cutoff")
|
|
assert.False(t, foundOld, "old contact (60 days old) should not be returned with 30-day cutoff")
|
|
}
|
|
|
|
func TestListEventsForContact(t *testing.T) {
|
|
ctx := context.Background()
|
|
now := time.Now()
|
|
|
|
contactURI := "mailto:test-" + t.Name() + "@test.com"
|
|
eventUID := "test-contact-event-" + t.Name()
|
|
|
|
calData := "BEGIN:VCALENDAR\nVERSION:2.0\nBEGIN:VEVENT\nUID:" + eventUID + "\nATTENDEE:" + contactURI + "\nSUMMARY:Contact Event\nEND:VEVENT\nEND:VCALENDAR"
|
|
|
|
start := now.Add(24 * time.Hour)
|
|
end := start.Add(1 * time.Hour)
|
|
|
|
_, err := testSvc.db.Exec(ctx,
|
|
`INSERT INTO dav_calendarobjects (calendarid, uri, calendardata, lastmodified, etag, size, componenttype, firstoccurence, lastoccurence, uid) VALUES ($1, $2, $3, $4, $5, $6, 'VEVENT', $7, $8, $9)`,
|
|
1, eventUID+".ics", calData, now.Unix(), fmt.Sprintf("%d", now.Unix()), len(calData), start.Unix(), end.Unix(), eventUID,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
t.Cleanup(func() {
|
|
_, _ = testSvc.db.Exec(ctx, "DELETE FROM dav_calendarobjects WHERE uid = $1", eventUID)
|
|
})
|
|
|
|
events, err := testSvc.ListEventsForContact(contactURI)
|
|
require.NoError(t, err)
|
|
|
|
var found bool
|
|
for _, e := range events {
|
|
if e.UID == eventUID {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
assert.True(t, found, "event with matching contact URI in calendardata should be returned")
|
|
}
|
|
|
|
// ============================================================================
|
|
// Part B: Transactional Mutation Tests
|
|
// ============================================================================
|
|
|
|
func TestCreateContact_Success(t *testing.T) {
|
|
ctx := context.Background()
|
|
userID := "test-create-contact-" + t.Name()
|
|
uri := userID + ".vcf"
|
|
|
|
input := ContactInput{
|
|
UserID: userID,
|
|
FirstName: "John",
|
|
LastName: "Doe",
|
|
Email: "john@test.com",
|
|
Phone: "1234567890",
|
|
DOB: "1990-01-01",
|
|
}
|
|
|
|
err := testSvc.CreateContact(1, userID, input)
|
|
require.NoError(t, err)
|
|
|
|
t.Cleanup(func() {
|
|
_, _ = testSvc.db.Exec(ctx, "DELETE FROM dav_cards WHERE addressbookid = $1 AND uri = $2", 1, uri)
|
|
})
|
|
|
|
var id, addressBookID, size int
|
|
var cardData, scannedURI, etag string
|
|
var lastModified int64
|
|
err = testSvc.db.QueryRow(ctx,
|
|
"SELECT id, addressbookid, uri, carddata, lastmodified, etag, size FROM dav_cards WHERE addressbookid = $1 AND uri = $2",
|
|
1, uri,
|
|
).Scan(&id, &addressBookID, &scannedURI, &cardData, &lastModified, &etag, &size)
|
|
require.NoError(t, err, "inserted contact should be queryable")
|
|
|
|
assert.Equal(t, 1, addressBookID)
|
|
assert.Equal(t, uri, scannedURI)
|
|
assert.Equal(t, len(cardData), size)
|
|
assert.Equal(t, etag, fmt.Sprintf("%d", lastModified))
|
|
assert.Contains(t, cardData, "FN:John Doe")
|
|
assert.Contains(t, cardData, "N:Doe;John;;;")
|
|
assert.Contains(t, cardData, "EMAIL;TYPE=INTERNET:john@test.com")
|
|
assert.Contains(t, cardData, "TEL;TYPE=CELL:1234567890")
|
|
assert.Contains(t, cardData, "BDAY:1990-01-01")
|
|
}
|
|
|
|
func TestUpdateContact_Success(t *testing.T) {
|
|
ctx := context.Background()
|
|
userID := "test-update-contact-" + t.Name()
|
|
uri := userID + ".vcf"
|
|
now := time.Now().Unix()
|
|
|
|
_, err := testSvc.db.Exec(ctx,
|
|
`INSERT INTO dav_cards (addressbookid, uri, carddata, lastmodified, etag, size) VALUES ($1, $2, $3, $4, $5, $6)`,
|
|
1, uri, "BEGIN:VCARD\nVERSION:3.0\nFN:Old Name\nEND:VCARD", now, fmt.Sprintf("%d", now), 100,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
t.Cleanup(func() {
|
|
_, _ = testSvc.db.Exec(ctx, "DELETE FROM dav_cards WHERE addressbookid = $1 AND uri = $2", 1, uri)
|
|
})
|
|
|
|
updatedInput := ContactInput{
|
|
UserID: userID,
|
|
FirstName: "Jane",
|
|
LastName: "Doe",
|
|
Email: "jane@test.com",
|
|
Phone: "0987654321",
|
|
DOB: "1995-05-05",
|
|
}
|
|
|
|
err = testSvc.UpdateContact(1, uri, updatedInput)
|
|
require.NoError(t, err)
|
|
|
|
var cardData string
|
|
var lastModified int64
|
|
var etag string
|
|
var size int
|
|
err = testSvc.db.QueryRow(ctx,
|
|
"SELECT carddata, lastmodified, etag, size FROM dav_cards WHERE addressbookid = $1 AND uri = $2",
|
|
1, uri,
|
|
).Scan(&cardData, &lastModified, &etag, &size)
|
|
require.NoError(t, err)
|
|
|
|
assert.Contains(t, cardData, "FN:Jane Doe")
|
|
assert.Contains(t, cardData, "N:Doe;Jane;;;")
|
|
assert.NotContains(t, cardData, "FN:Old Name")
|
|
assert.Equal(t, len(cardData), size)
|
|
assert.Equal(t, etag, fmt.Sprintf("%d", lastModified))
|
|
assert.GreaterOrEqual(t, lastModified, now, "lastmodified should be updated to current time or later")
|
|
}
|
|
|
|
func TestDeleteContact_Success(t *testing.T) {
|
|
ctx := context.Background()
|
|
userID := "test-delete-contact-" + t.Name()
|
|
uri := userID + ".vcf"
|
|
now := time.Now().Unix()
|
|
|
|
_, err := testSvc.db.Exec(ctx,
|
|
`INSERT INTO dav_cards (addressbookid, uri, carddata, lastmodified, etag, size) VALUES ($1, $2, $3, $4, $5, $6)`,
|
|
1, uri, "BEGIN:VCARD\nVERSION:3.0\nFN:Delete Me\nEND:VCARD", now, fmt.Sprintf("%d", now), 100,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
var count int
|
|
err = testSvc.db.QueryRow(ctx, "SELECT COUNT(*) FROM dav_cards WHERE addressbookid = $1 AND uri = $2", 1, uri).Scan(&count)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 1, count, "contact should exist before deletion")
|
|
|
|
err = testSvc.DeleteContact(1, uri)
|
|
require.NoError(t, err)
|
|
|
|
err = testSvc.db.QueryRow(ctx, "SELECT COUNT(*) FROM dav_cards WHERE addressbookid = $1 AND uri = $2", 1, uri).Scan(&count)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 0, count, "contact should be deleted")
|
|
}
|
|
|
|
func TestCreateEvent_Success(t *testing.T) {
|
|
ctx := context.Background()
|
|
now := time.Now()
|
|
uniqueSummary := "TestCreateEvent-" + t.Name()
|
|
|
|
input := EventInput{
|
|
Summary: uniqueSummary,
|
|
Description: "Test description",
|
|
Location: "Test location",
|
|
Start: now.Add(24 * time.Hour),
|
|
End: now.Add(25 * time.Hour),
|
|
AllDay: false,
|
|
}
|
|
|
|
err := testSvc.CreateEvent(1, input)
|
|
require.NoError(t, err)
|
|
|
|
var uid, uri, calendarData string
|
|
var firstOcc, lastOcc int64
|
|
var componentType string
|
|
err = testSvc.db.QueryRow(ctx,
|
|
"SELECT uid, uri, calendardata, firstoccurence, lastoccurence, componenttype FROM dav_calendarobjects WHERE calendarid = $1 AND calendardata LIKE $2",
|
|
1, "%SUMMARY:"+uniqueSummary+"%",
|
|
).Scan(&uid, &uri, &calendarData, &firstOcc, &lastOcc, &componentType)
|
|
require.NoError(t, err, "created event should be queryable by summary in calendardata")
|
|
|
|
t.Cleanup(func() {
|
|
_, _ = testSvc.db.Exec(ctx, "DELETE FROM dav_calendarobjects WHERE uid = $1", uid)
|
|
})
|
|
|
|
assert.NotEmpty(t, uid)
|
|
assert.Equal(t, uid+".ics", uri)
|
|
assert.Equal(t, "VEVENT", componentType)
|
|
assert.Equal(t, input.Start.Unix(), firstOcc)
|
|
assert.Equal(t, input.End.Unix(), lastOcc)
|
|
assert.Contains(t, calendarData, "SUMMARY:"+uniqueSummary)
|
|
assert.Contains(t, calendarData, "DESCRIPTION:Test description")
|
|
assert.Contains(t, calendarData, "LOCATION:Test location")
|
|
}
|
|
|
|
func TestUpdateEvent_Success(t *testing.T) {
|
|
ctx := context.Background()
|
|
knownUID := "test-update-event-" + t.Name()
|
|
uri := knownUID + ".ics"
|
|
now := time.Now()
|
|
|
|
start := now.Add(48 * time.Hour)
|
|
end := now.Add(49 * time.Hour)
|
|
calData := "BEGIN:VCALENDAR\nVERSION:2.0\nBEGIN:VEVENT\nUID:" + knownUID + "\nSUMMARY:Original\nEND:VEVENT\nEND:VCALENDAR"
|
|
|
|
_, err := testSvc.db.Exec(ctx,
|
|
`INSERT INTO dav_calendarobjects (calendarid, uri, calendardata, lastmodified, etag, size, componenttype, firstoccurence, lastoccurence, uid) VALUES ($1, $2, $3, $4, $5, $6, 'VEVENT', $7, $8, $9)`,
|
|
1, uri, calData, now.Unix(), fmt.Sprintf("%d", now.Unix()), len(calData), start.Unix(), end.Unix(), knownUID,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
t.Cleanup(func() {
|
|
_, _ = testSvc.db.Exec(ctx, "DELETE FROM dav_calendarobjects WHERE uid = $1", knownUID)
|
|
})
|
|
|
|
updatedInput := EventInput{
|
|
Summary: "Updated Event",
|
|
Description: "Updated description",
|
|
Location: "Updated location",
|
|
Start: now.Add(72 * time.Hour),
|
|
End: now.Add(73 * time.Hour),
|
|
AllDay: false,
|
|
}
|
|
|
|
err = testSvc.UpdateEvent(1, knownUID, updatedInput)
|
|
require.NoError(t, err)
|
|
|
|
var calendarData string
|
|
var firstOcc, lastOcc int64
|
|
err = testSvc.db.QueryRow(ctx,
|
|
"SELECT calendardata, firstoccurence, lastoccurence FROM dav_calendarobjects WHERE uid = $1",
|
|
knownUID,
|
|
).Scan(&calendarData, &firstOcc, &lastOcc)
|
|
require.NoError(t, err)
|
|
|
|
assert.Contains(t, calendarData, "SUMMARY:Updated Event")
|
|
assert.Contains(t, calendarData, "DESCRIPTION:Updated description")
|
|
assert.NotContains(t, calendarData, "SUMMARY:Original")
|
|
assert.Equal(t, updatedInput.Start.Unix(), firstOcc, "firstoccurence should be updated")
|
|
assert.Equal(t, updatedInput.End.Unix(), lastOcc, "lastoccurence should be updated")
|
|
}
|
|
|
|
func TestDeleteEvent_Success(t *testing.T) {
|
|
ctx := context.Background()
|
|
knownUID := "test-delete-event-" + t.Name()
|
|
uri := knownUID + ".ics"
|
|
now := time.Now()
|
|
|
|
start := now.Add(48 * time.Hour)
|
|
end := now.Add(49 * time.Hour)
|
|
calData := "BEGIN:VCALENDAR\nVERSION:2.0\nBEGIN:VEVENT\nUID:" + knownUID + "\nSUMMARY:Delete Me\nEND:VEVENT\nEND:VCALENDAR"
|
|
|
|
_, err := testSvc.db.Exec(ctx,
|
|
`INSERT INTO dav_calendarobjects (calendarid, uri, calendardata, lastmodified, etag, size, componenttype, firstoccurence, lastoccurence, uid) VALUES ($1, $2, $3, $4, $5, $6, 'VEVENT', $7, $8, $9)`,
|
|
1, uri, calData, now.Unix(), fmt.Sprintf("%d", now.Unix()), len(calData), start.Unix(), end.Unix(), knownUID,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
var count int
|
|
err = testSvc.db.QueryRow(ctx, "SELECT COUNT(*) FROM dav_calendarobjects WHERE uid = $1", knownUID).Scan(&count)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 1, count, "event should exist before deletion")
|
|
|
|
err = testSvc.DeleteEvent(1, knownUID)
|
|
require.NoError(t, err)
|
|
|
|
err = testSvc.db.QueryRow(ctx, "SELECT COUNT(*) FROM dav_calendarobjects WHERE uid = $1", knownUID).Scan(&count)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 0, count, "event should be deleted")
|
|
}
|