- 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.
232 lines
6.2 KiB
Go
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)
|
|
}
|
|
}
|