Files
Crussell/backend/internal/dav/shared_test.go
T

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")
}