326 lines
8.0 KiB
V
326 lines
8.0 KiB
V
module caldav
|
|
|
|
import calendar.calbox
|
|
|
|
fn test_format_event() {
|
|
// Create a test event with all fields
|
|
event := calbox.Event{
|
|
CalendarComponent: calbox.CalendarComponent{
|
|
uid: 'test@example.com'
|
|
created: 1708074000 // 2024-02-16 10:00:00 UTC
|
|
modified: 1708074000
|
|
summary: 'Test Event'
|
|
description: 'Test Description'
|
|
categories: ['Work', 'Meeting']
|
|
status: .confirmed
|
|
class: .public
|
|
location: 'Conference Room'
|
|
alarms: [
|
|
calbox.Alarm{
|
|
action: .display
|
|
trigger: '-PT15M'
|
|
description: 'Meeting starts in 15 minutes'
|
|
},
|
|
]
|
|
}
|
|
start_time: 1708074000
|
|
end_time: 1708077600 // 1 hour later
|
|
transp: .opaque
|
|
attendees: [
|
|
calbox.Attendee{
|
|
email: 'john@example.com'
|
|
name: 'John Doe'
|
|
role: .req_participant
|
|
partstat: .accepted
|
|
rsvp: true
|
|
},
|
|
]
|
|
organizer: calbox.Attendee{
|
|
email: 'boss@example.com'
|
|
name: 'The Boss'
|
|
role: .chair
|
|
partstat: .accepted
|
|
}
|
|
}
|
|
|
|
obj := calbox.CalendarObject{
|
|
comp_type: 'VEVENT'
|
|
event: event
|
|
}
|
|
|
|
ical := to_ical(obj)
|
|
|
|
// Verify required fields
|
|
assert ical.contains('BEGIN:VCALENDAR')
|
|
assert ical.contains('VERSION:2.0')
|
|
assert ical.contains('BEGIN:VEVENT')
|
|
assert ical.contains('UID:test@example.com')
|
|
assert ical.contains('DTSTART:20240216T100000Z')
|
|
assert ical.contains('DTEND:20240216T110000Z')
|
|
assert ical.contains('SUMMARY:Test Event')
|
|
assert ical.contains('STATUS:CONFIRMED')
|
|
assert ical.contains('CLASS:PUBLIC')
|
|
assert ical.contains('TRANSP:OPAQUE')
|
|
assert ical.contains('END:VEVENT')
|
|
assert ical.contains('END:VCALENDAR')
|
|
|
|
// Parse back
|
|
parsed := from_ical(ical)!
|
|
assert parsed.comp_type == 'VEVENT'
|
|
|
|
if e := parsed.event {
|
|
assert e.uid == event.uid
|
|
assert e.summary == event.summary
|
|
assert e.start_time == event.start_time
|
|
assert e.end_time? == event.end_time?
|
|
assert e.status == event.status
|
|
assert e.class == event.class
|
|
assert e.transp == event.transp
|
|
assert e.attendees.len == event.attendees.len
|
|
assert e.attendees[0].role == event.attendees[0].role
|
|
assert e.attendees[0].partstat == event.attendees[0].partstat
|
|
assert e.organizer?.role == event.organizer?.role
|
|
} else {
|
|
assert false, 'Failed to parse event'
|
|
}
|
|
}
|
|
|
|
fn test_format_recurring_event() {
|
|
// Create a recurring event
|
|
event := calbox.Event{
|
|
CalendarComponent: calbox.CalendarComponent{
|
|
uid: 'recurring@example.com'
|
|
created: 1708074000
|
|
summary: 'Daily Meeting'
|
|
status: .confirmed
|
|
class: .public
|
|
}
|
|
start_time: 1708074000
|
|
duration: 'PT1H'
|
|
transp: .opaque
|
|
rrule: calbox.RecurrenceRule{
|
|
frequency: .daily
|
|
interval: 1
|
|
count: 5
|
|
by_day: ['MO', 'WE', 'FR']
|
|
}
|
|
}
|
|
|
|
obj := calbox.CalendarObject{
|
|
comp_type: 'VEVENT'
|
|
event: event
|
|
}
|
|
|
|
ical := to_ical(obj)
|
|
|
|
// Verify recurrence fields
|
|
assert ical.contains('RRULE:FREQ=DAILY;INTERVAL=1;COUNT=5;BYDAY=MO,WE,FR')
|
|
assert ical.contains('DURATION:PT1H')
|
|
|
|
// Parse back
|
|
parsed := from_ical(ical)!
|
|
if e := parsed.event {
|
|
assert e.duration? == event.duration?
|
|
if rule := e.rrule {
|
|
assert rule.frequency == .daily
|
|
assert rule.interval == 1
|
|
assert rule.count? == 5
|
|
assert rule.by_day == ['MO', 'WE', 'FR']
|
|
} else {
|
|
assert false, 'Failed to parse recurrence rule'
|
|
}
|
|
} else {
|
|
assert false, 'Failed to parse event'
|
|
}
|
|
}
|
|
|
|
fn test_format_todo() {
|
|
// Create a todo
|
|
todo := calbox.Todo{
|
|
CalendarComponent: calbox.CalendarComponent{
|
|
uid: 'todo@example.com'
|
|
created: 1708074000
|
|
summary: 'Test Todo'
|
|
status: .needs_action
|
|
class: .private
|
|
}
|
|
due_time: 1708160400 // Next day
|
|
percent: 0
|
|
}
|
|
|
|
obj := calbox.CalendarObject{
|
|
comp_type: 'VTODO'
|
|
todo: todo
|
|
}
|
|
|
|
ical := to_ical(obj)
|
|
|
|
// Verify todo fields
|
|
assert ical.contains('BEGIN:VTODO')
|
|
assert ical.contains('DUE:20240217T100000Z')
|
|
assert ical.contains('PERCENT-COMPLETE:0')
|
|
assert ical.contains('STATUS:NEEDS-ACTION')
|
|
assert ical.contains('CLASS:PRIVATE')
|
|
|
|
// Parse back
|
|
parsed := from_ical(ical)!
|
|
if t := parsed.todo {
|
|
assert t.uid == todo.uid
|
|
assert t.summary == todo.summary
|
|
assert t.due_time? == todo.due_time?
|
|
assert t.percent? == todo.percent?
|
|
assert t.status == todo.status
|
|
assert t.class == todo.class
|
|
} else {
|
|
assert false, 'Failed to parse todo'
|
|
}
|
|
}
|
|
|
|
fn test_format_journal() {
|
|
// Create a journal entry
|
|
journal := calbox.Journal{
|
|
CalendarComponent: calbox.CalendarComponent{
|
|
uid: 'journal@example.com'
|
|
created: 1708074000
|
|
summary: 'Test Journal'
|
|
description: 'Today we discussed...'
|
|
categories: ['Notes', 'Work']
|
|
status: .draft
|
|
class: .confidential
|
|
}
|
|
start_time: 1708074000
|
|
}
|
|
|
|
obj := calbox.CalendarObject{
|
|
comp_type: 'VJOURNAL'
|
|
journal: journal
|
|
}
|
|
|
|
ical := to_ical(obj)
|
|
|
|
// Verify journal fields
|
|
assert ical.contains('BEGIN:VJOURNAL')
|
|
assert ical.contains('DTSTART:20240216T100000Z')
|
|
assert ical.contains('CATEGORIES:Notes,Work')
|
|
assert ical.contains('STATUS:DRAFT')
|
|
assert ical.contains('CLASS:CONFIDENTIAL')
|
|
|
|
// Parse back
|
|
parsed := from_ical(ical)!
|
|
if j := parsed.journal {
|
|
assert j.uid == journal.uid
|
|
assert j.summary == journal.summary
|
|
assert j.description == journal.description
|
|
assert j.categories == journal.categories
|
|
assert j.start_time == journal.start_time
|
|
assert j.status == journal.status
|
|
assert j.class == journal.class
|
|
} else {
|
|
assert false, 'Failed to parse journal'
|
|
}
|
|
}
|
|
|
|
fn test_parse_attendees() {
|
|
ical := 'BEGIN:VCALENDAR
|
|
VERSION:2.0
|
|
BEGIN:VEVENT
|
|
UID:test@example.com
|
|
DTSTAMP:20240216T100000Z
|
|
DTSTART:20240216T100000Z
|
|
DTEND:20240216T110000Z
|
|
SUMMARY:Test Event
|
|
ATTENDEE;CN=John Doe;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;RSVP=TRUE:mailto:john@example.com
|
|
ORGANIZER;CN=The Boss;ROLE=CHAIR;PARTSTAT=ACCEPTED:mailto:boss@example.com
|
|
END:VEVENT
|
|
END:VCALENDAR'
|
|
|
|
obj := from_ical(ical)!
|
|
if event := obj.event {
|
|
assert event.attendees.len == 1
|
|
attendee := event.attendees[0]
|
|
assert attendee.email == 'john@example.com'
|
|
assert attendee.name == 'John Doe'
|
|
assert attendee.role == .req_participant
|
|
assert attendee.partstat == .accepted
|
|
assert attendee.rsvp == true
|
|
|
|
if org := event.organizer {
|
|
assert org.email == 'boss@example.com'
|
|
assert org.name == 'The Boss'
|
|
assert org.role == .chair
|
|
assert org.partstat == .accepted
|
|
} else {
|
|
assert false, 'Failed to parse organizer'
|
|
}
|
|
} else {
|
|
assert false, 'Failed to parse event'
|
|
}
|
|
}
|
|
|
|
fn test_parse_alarms() {
|
|
ical := 'BEGIN:VCALENDAR
|
|
VERSION:2.0
|
|
BEGIN:VEVENT
|
|
UID:test@example.com
|
|
DTSTAMP:20240216T100000Z
|
|
DTSTART:20240216T100000Z
|
|
DTEND:20240216T110000Z
|
|
SUMMARY:Test Event
|
|
BEGIN:VALARM
|
|
ACTION:DISPLAY
|
|
TRIGGER:-PT15M
|
|
DESCRIPTION:Meeting starts in 15 minutes
|
|
END:VALARM
|
|
END:VEVENT
|
|
END:VCALENDAR'
|
|
|
|
obj := from_ical(ical)!
|
|
if event := obj.event {
|
|
assert event.alarms.len == 1
|
|
alarm := event.alarms[0]
|
|
assert alarm.action == .display
|
|
assert alarm.trigger == '-PT15M'
|
|
assert alarm.description == 'Meeting starts in 15 minutes'
|
|
} else {
|
|
assert false, 'Failed to parse event'
|
|
}
|
|
}
|
|
|
|
fn test_line_folding() {
|
|
// Test long description that should be folded
|
|
event := calbox.Event{
|
|
CalendarComponent: calbox.CalendarComponent{
|
|
uid: 'test@example.com'
|
|
created: 1708074000
|
|
summary: 'Test Event'
|
|
description: 'This is a very long description that should be folded into multiple lines according to the iCalendar specification which states that lines longer than 75 characters should be folded'
|
|
status: .confirmed
|
|
class: .public
|
|
}
|
|
start_time: 1708074000
|
|
end_time: 1708077600
|
|
transp: .opaque
|
|
}
|
|
|
|
obj := calbox.CalendarObject{
|
|
comp_type: 'VEVENT'
|
|
event: event
|
|
}
|
|
|
|
ical := to_ical(obj)
|
|
lines := ical.split_into_lines()
|
|
|
|
// Verify no line is longer than 75 characters
|
|
for line in lines {
|
|
assert line.len <= 75, 'Line exceeds 75 characters: ${line}'
|
|
}
|
|
|
|
// Parse back and verify description is reconstructed
|
|
parsed := from_ical(ical)!
|
|
if e := parsed.event {
|
|
assert e.description == event.description
|
|
} else {
|
|
assert false, 'Failed to parse event'
|
|
}
|
|
}
|