calendar_test.go

  1package calendar
  2
  3import (
  4	"os"
  5	"strings"
  6	"testing"
  7	"time"
  8)
  9
 10func TestParseICS_Simple(t *testing.T) {
 11	data, err := os.ReadFile("../testdata/invites/simple.ics")
 12	if err != nil {
 13		t.Fatalf("Failed to read test fixture: %v", err)
 14	}
 15
 16	event, err := ParseICS(data)
 17	if err != nil {
 18		t.Fatalf("ParseICS failed: %v", err)
 19	}
 20
 21	if event.UID != "test-event-123@example.com" {
 22		t.Errorf("Expected UID test-event-123@example.com, got %s", event.UID)
 23	}
 24
 25	if event.Summary != "Q2 Planning Meeting" {
 26		t.Errorf("Expected summary 'Q2 Planning Meeting', got %s", event.Summary)
 27	}
 28
 29	if event.Location != "Conference Room A" {
 30		t.Errorf("Expected location 'Conference Room A', got %s", event.Location)
 31	}
 32
 33	if event.Organizer != "alice@company.com" {
 34		t.Errorf("Expected organizer alice@company.com, got %s", event.Organizer)
 35	}
 36
 37	if event.Status != "CONFIRMED" {
 38		t.Errorf("Expected status CONFIRMED, got %s", event.Status)
 39	}
 40
 41	if event.Method != "REQUEST" {
 42		t.Errorf("Expected method REQUEST, got %s", event.Method)
 43	}
 44
 45	expectedStart := time.Date(2026, 4, 21, 14, 0, 0, 0, time.UTC)
 46	if !event.Start.Equal(expectedStart) {
 47		t.Errorf("Expected start %v, got %v", expectedStart, event.Start)
 48	}
 49
 50	expectedEnd := time.Date(2026, 4, 21, 15, 30, 0, 0, time.UTC)
 51	if !event.End.Equal(expectedEnd) {
 52		t.Errorf("Expected end %v, got %v", expectedEnd, event.End)
 53	}
 54}
 55
 56func TestParseICS_NoEvent(t *testing.T) {
 57	data := []byte(`BEGIN:VCALENDAR
 58VERSION:2.0
 59PRODID:-//Test//Test//EN
 60END:VCALENDAR`)
 61
 62	_, err := ParseICS(data)
 63	if err == nil {
 64		t.Error("Expected error for calendar with no VEVENT")
 65	}
 66	if !strings.Contains(err.Error(), "no VEVENT") {
 67		t.Errorf("Expected 'no VEVENT' error, got: %v", err)
 68	}
 69}
 70
 71func TestParseICS_Malformed(t *testing.T) {
 72	data := []byte(`INVALID ICAL DATA`)
 73
 74	_, err := ParseICS(data)
 75	if err == nil {
 76		t.Error("Expected error for malformed iCalendar data")
 77	}
 78}
 79
 80func TestGenerateRSVP(t *testing.T) {
 81	data, err := os.ReadFile("../testdata/invites/simple.ics")
 82	if err != nil {
 83		t.Fatalf("Failed to read test fixture: %v", err)
 84	}
 85
 86	responses := []string{"ACCEPTED", "DECLINED", "TENTATIVE"}
 87
 88	for _, response := range responses {
 89		t.Run(response, func(t *testing.T) {
 90			rsvpData, err := GenerateRSVP(data, "bob@company.com", response)
 91			if err != nil {
 92				t.Fatalf("GenerateRSVP failed for %s: %v", response, err)
 93			}
 94
 95			rsvpStr := string(rsvpData)
 96
 97			// Check METHOD:REPLY is set
 98			if !strings.Contains(rsvpStr, "METHOD:REPLY") {
 99				t.Error("Expected METHOD:REPLY in RSVP")
100			}
101
102			// Check PARTSTAT is updated
103			if !strings.Contains(rsvpStr, "PARTSTAT="+response) {
104				t.Errorf("Expected PARTSTAT=%s in RSVP", response)
105			}
106
107			// RFC 6047: only the responding attendee should remain
108			attendeeCount := strings.Count(rsvpStr, "ATTENDEE")
109			if attendeeCount != 1 {
110				t.Errorf("Expected exactly 1 ATTENDEE in RSVP, got %d", attendeeCount)
111			}
112
113			// Should contain responding user's email
114			if !strings.Contains(rsvpStr, "bob@company.com") {
115				t.Error("Expected bob@company.com in RSVP attendee")
116			}
117
118			// Should NOT contain other attendees
119			if strings.Contains(rsvpStr, "carol@company.com") {
120				t.Error("RSVP should not contain other attendees")
121			}
122
123			// Verify it's still valid iCalendar
124			_, err = ParseICS(rsvpData)
125			if err != nil {
126				t.Errorf("Generated RSVP is not valid iCalendar: %v", err)
127			}
128		})
129	}
130}
131
132func TestExtractEmail(t *testing.T) {
133	tests := []struct {
134		input    string
135		expected string
136	}{
137		{"mailto:user@example.com", "user@example.com"},
138		{"MAILTO:user@example.com", "user@example.com"},
139		{"CN=John Doe:user@example.com", "user@example.com"},
140		{"user@example.com", "user@example.com"},
141		{"  user@example.com  ", "user@example.com"},
142	}
143
144	for _, tt := range tests {
145		t.Run(tt.input, func(t *testing.T) {
146			result := extractEmail(tt.input)
147			if result != tt.expected {
148				t.Errorf("extractEmail(%q) = %q, want %q", tt.input, result, tt.expected)
149			}
150		})
151	}
152}