ultisuite-backend/internal/nextcloud/calendar_test.go
R3D347HR4Y 1d063237b9
Some checks are pending
CI / Go tests (push) Waiting to run
CI / Integration tests (push) Waiting to run
CI / DB migrations (push) Waiting to run
feat(transcription): integrate Faster Whisper for Jitsi transcriptions
- Added support for Faster Whisper transcription via Jigasi and Skynet.
- Updated .env.example to include new environment variables for transcription settings.
- Enhanced Jitsi Docker Compose configuration to include Skynet and Jigasi services.
- Introduced new API endpoints for managing organizational folders in the drive service.
- Updated Nextcloud initialization script to enable external file mounting.
- Improved error handling and response structures in the drive API.
- Added new properties for organization settings related to transcription and agenda management.
2026-06-12 19:10:18 +02:00

232 lines
6.2 KiB
Go

package nextcloud
import (
"strings"
"testing"
)
func TestParseICSBasics(t *testing.T) {
ics := strings.Join([]string{
"BEGIN:VCALENDAR",
"VERSION:2.0",
"BEGIN:VTIMEZONE",
"TZID:Europe/Paris",
"BEGIN:STANDARD",
"DTSTART:19961027T030000",
"END:STANDARD",
"END:VTIMEZONE",
"BEGIN:VEVENT",
"UID:abc-123",
"SUMMARY:Réunion\\, équipe",
"DESCRIPTION:Ligne 1\\nLigne 2",
"DTSTART;TZID=Europe/Paris:20260611T100000",
"DTEND;TZID=Europe/Paris:20260611T110000",
"RRULE:FREQ=WEEKLY;BYDAY=TH",
"EXDATE;TZID=Europe/Paris:20260618T100000",
"END:VEVENT",
"END:VCALENDAR",
}, "\r\n")
e := parseICS(ics)
if e.UID != "abc-123" {
t.Fatalf("UID = %q", e.UID)
}
if e.Summary != "Réunion, équipe" {
t.Fatalf("Summary = %q", e.Summary)
}
if e.Description != "Ligne 1\nLigne 2" {
t.Fatalf("Description = %q", e.Description)
}
// VTIMEZONE DTSTART must not leak into the event.
if e.Start != "20260611T080000Z" {
t.Fatalf("Start = %q, want UTC-converted 20260611T080000Z", e.Start)
}
if e.End != "20260611T090000Z" {
t.Fatalf("End = %q", e.End)
}
if e.AllDay {
t.Fatal("AllDay should be false")
}
if e.RRule != "FREQ=WEEKLY;BYDAY=TH" {
t.Fatalf("RRule = %q", e.RRule)
}
if len(e.ExDates) != 1 || e.ExDates[0] != "20260618T080000Z" {
t.Fatalf("ExDates = %v", e.ExDates)
}
}
func TestParseICSAllDayAndFolding(t *testing.T) {
ics := strings.Join([]string{
"BEGIN:VCALENDAR",
"BEGIN:VEVENT",
"UID:x",
"SUMMARY:Long title that",
" continues folded",
"DTSTART;VALUE=DATE:20260611",
"DTEND;VALUE=DATE:20260612",
"END:VEVENT",
"END:VCALENDAR",
}, "\r\n")
e := parseICS(ics)
if !e.AllDay {
t.Fatal("AllDay should be true")
}
if e.Start != "20260611" || e.End != "20260612" {
t.Fatalf("Start/End = %q/%q", e.Start, e.End)
}
if e.Summary != "Long title thatcontinues folded" {
t.Fatalf("Summary = %q", e.Summary)
}
}
func TestParseICSPrefersMasterVEvent(t *testing.T) {
ics := strings.Join([]string{
"BEGIN:VCALENDAR",
"BEGIN:VEVENT",
"UID:rec",
"RECURRENCE-ID:20260618T100000Z",
"SUMMARY:Exception",
"DTSTART:20260618T120000Z",
"DTEND:20260618T130000Z",
"END:VEVENT",
"BEGIN:VEVENT",
"UID:rec",
"SUMMARY:Master",
"DTSTART:20260611T100000Z",
"DTEND:20260611T110000Z",
"RRULE:FREQ=DAILY",
"END:VEVENT",
"END:VCALENDAR",
}, "\r\n")
e := parseICS(ics)
if e.Summary != "Master" {
t.Fatalf("Summary = %q, want master VEVENT", e.Summary)
}
}
func TestBuildICSRoundTrip(t *testing.T) {
event := &Event{
UID: "round-trip",
Summary: "Point; hebdo, équipe",
Description: "Ordre du jour:\npoint 1",
Location: "Salle A",
Start: "20260611T100000Z",
End: "20260611T110000Z",
RRule: "FREQ=WEEKLY;BYDAY=TH",
ExDates: []string{"20260618T100000Z"},
Color: "#1A73E8",
Attendees: []EventAttendee{
{Email: "invite@example.com", Name: "Invité", Status: "ACCEPTED"},
},
}
parsed := parseICS(buildICS(event))
if parsed.Summary != event.Summary {
t.Fatalf("Summary = %q", parsed.Summary)
}
if parsed.Description != event.Description {
t.Fatalf("Description = %q", parsed.Description)
}
if parsed.RRule != event.RRule {
t.Fatalf("RRule = %q", parsed.RRule)
}
if parsed.Color != "" {
t.Fatalf("Color should not be serialized in ICS, got %q", parsed.Color)
}
if len(parsed.ExDates) != 1 || parsed.ExDates[0] != "20260618T100000Z" {
t.Fatalf("ExDates = %v", parsed.ExDates)
}
if len(parsed.Attendees) != 1 || parsed.Attendees[0].Email != "invite@example.com" || parsed.Attendees[0].Status != "ACCEPTED" {
t.Fatalf("Attendees = %+v", parsed.Attendees)
}
if parsed.Start != event.Start || parsed.End != event.End {
t.Fatalf("Start/End = %q/%q", parsed.Start, parsed.End)
}
}
func TestMergeEventPreservesUID(t *testing.T) {
existing := &Event{
UID: "abc@ulti",
Summary: "Original",
Start: "20260611T100000Z",
End: "20260611T110000Z",
Path: "/remote.php/dav/calendars/user/personal/abc@ulti.ics",
MeetURL: "https://meet.example/room",
ExDates: []string{"20260618T100000Z"},
}
patch := &Event{
Summary: "Updated title",
Start: "20260612T100000Z",
End: "20260612T110000Z",
}
merged := MergeEvent(existing, patch)
if merged.UID != "abc@ulti" {
t.Fatalf("UID = %q", merged.UID)
}
if merged.Summary != "Updated title" {
t.Fatalf("Summary = %q", merged.Summary)
}
if merged.MeetURL != "https://meet.example/room" {
t.Fatalf("MeetURL should be preserved, got %q", merged.MeetURL)
}
if len(merged.ExDates) != 1 {
t.Fatalf("ExDates = %v", merged.ExDates)
}
}
func TestMergeEventUIDFromPath(t *testing.T) {
existing := &Event{
Summary: "Keep",
Path: "/remote.php/dav/calendars/user/personal/fallback@ulti.ics",
}
patch := &Event{Summary: "New"}
merged := MergeEvent(existing, patch)
if merged.UID != "fallback@ulti" {
t.Fatalf("UID = %q", merged.UID)
}
}
func TestBuildICSAllDay(t *testing.T) {
ics := buildICS(&Event{UID: "ad", Summary: "Férié", Start: "20260714", End: "20260715", AllDay: true})
if !strings.Contains(ics, "DTSTART;VALUE=DATE:20260714") {
t.Fatalf("missing all-day DTSTART:\n%s", ics)
}
parsed := parseICS(ics)
if !parsed.AllDay {
t.Fatal("AllDay should round-trip")
}
}
func TestParseCalendarListNormalizesCloudPrefix(t *testing.T) {
basePath := "/remote.php/dav/calendars/user@example.com/"
raw := `<?xml version="1.0" encoding="UTF-8"?>
<d:multistatus xmlns:d="DAV:" xmlns:apple="http://apple.com/ns/ical/">
<d:response>
<d:href>/cloud/remote.php/dav/calendars/user@example.com/</d:href>
<d:propstat><d:prop><d:displayname>Root</d:displayname></d:prop></d:propstat>
</d:response>
<d:response>
<d:href>/cloud/remote.php/dav/calendars/user@example.com/personal/</d:href>
<d:propstat><d:prop>
<d:displayname>Personal</d:displayname>
<apple:calendar-color>#1a73e8</apple:calendar-color>
</d:prop></d:propstat>
</d:response>
</d:multistatus>`
cals, err := parseCalendarList(strings.NewReader(raw), basePath)
if err != nil {
t.Fatal(err)
}
if len(cals) != 1 {
t.Fatalf("len = %d, want 1", len(cals))
}
if cals[0].ID != "personal" {
t.Fatalf("ID = %q, want personal", cals[0].ID)
}
if cals[0].Path != "/remote.php/dav/calendars/user@example.com/personal/" {
t.Fatalf("Path = %q", cals[0].Path)
}
}