Files
luxtools-plugin/_test/ChronologicalIcsEventsTest.php
2026-02-16 13:39:26 +01:00

223 lines
8.4 KiB
PHP

<?php
namespace dokuwiki\plugin\luxtools\test;
use dokuwiki\plugin\luxtools\ChronologicalIcsEvents;
use DokuWikiTest;
require_once(__DIR__ . '/../autoload.php');
/**
* Tests for local ICS event parsing.
*
* @group plugin_luxtools
* @group plugins
*/
class ChronologicalIcsEventsTest extends DokuWikiTest
{
public function testEventsForDateParsesAllDayAndTimedEntries(): void
{
$dir = TMP_DIR . '/chrono_ics/' . uniqid('case_', true);
@mkdir($dir, 0777, true);
$ics = $dir . '/calendar.ics';
$content = "BEGIN:VCALENDAR\n"
. "BEGIN:VEVENT\n"
. "DTSTART;VALUE=DATE:20260216\n"
. "SUMMARY:Ganztag Event\n"
. "END:VEVENT\n"
. "BEGIN:VEVENT\n"
. "DTSTART:20260216T134500\n"
. "SUMMARY:Termin A\n"
. "END:VEVENT\n"
. "BEGIN:VEVENT\n"
. "DTSTART:20260217T090000\n"
. "SUMMARY:Anderer Tag\n"
. "END:VEVENT\n"
. "END:VCALENDAR\n";
@file_put_contents($ics, $content);
$events = ChronologicalIcsEvents::eventsForDate($ics, '2026-02-16');
if (count($events) !== 2) {
throw new \Exception('Expected 2 events for 2026-02-16, got ' . count($events));
}
$summaries = array_map(static fn(array $e): string => (string)$e['summary'], $events);
if (!in_array('Ganztag Event', $summaries, true)) {
throw new \Exception('Missing all-day event summary');
}
if (!in_array('Termin A', $summaries, true)) {
throw new \Exception('Missing timed event summary');
}
$timed = null;
foreach ($events as $event) {
if ((string)($event['summary'] ?? '') === 'Termin A') {
$timed = $event;
break;
}
}
if (!is_array($timed)) {
throw new \Exception('Timed event payload missing');
}
if (trim((string)($timed['startIso'] ?? '')) === '') {
throw new \Exception('Timed event should expose startIso for client-side timezone conversion');
}
}
public function testEventsForDateReadsMultipleConfiguredFiles(): void
{
$dir = TMP_DIR . '/chrono_ics/' . uniqid('multi_', true);
@mkdir($dir, 0777, true);
$ics1 = $dir . '/one.ics';
$ics2 = $dir . '/two.ics';
@file_put_contents($ics1, "BEGIN:VCALENDAR\nBEGIN:VEVENT\nDTSTART:20260218T100000\nSUMMARY:Eins\nEND:VEVENT\nEND:VCALENDAR\n");
@file_put_contents($ics2, "BEGIN:VCALENDAR\nBEGIN:VEVENT\nDTSTART:20260218T110000\nSUMMARY:Zwei\nEND:VEVENT\nEND:VCALENDAR\n");
$config = $ics1 . "\n" . $ics2;
$events = ChronologicalIcsEvents::eventsForDate($config, '2026-02-18');
if (count($events) !== 2) {
throw new \Exception('Expected 2 events from two files, got ' . count($events));
}
}
public function testEventsForDateSupportsWeeklyRecurrence(): void
{
$dir = TMP_DIR . '/chrono_ics/' . uniqid('rrule_', true);
@mkdir($dir, 0777, true);
$ics = $dir . '/recurring.ics';
$content = "BEGIN:VCALENDAR\n"
. "BEGIN:VEVENT\n"
. "UID:weekly-1\n"
. "DTSTART:20260205T090000\n"
. "RRULE:FREQ=WEEKLY;INTERVAL=1\n"
. "SUMMARY:Wiederkehrender Termin\n"
. "END:VEVENT\n"
. "END:VCALENDAR\n";
@file_put_contents($ics, $content);
$events = ChronologicalIcsEvents::eventsForDate($ics, '2026-02-12');
if (count($events) < 1) {
throw new \Exception('Expected recurring event on 2026-02-12, got none');
}
$summaries = array_map(static fn(array $e): string => (string)$e['summary'], $events);
if (!in_array('Wiederkehrender Termin', $summaries, true)) {
throw new \Exception('Recurring summary not found on matching date');
}
}
public function testEventsForDateRespectsExdateForRecurringEvent(): void
{
$dir = TMP_DIR . '/chrono_ics/' . uniqid('exdate_', true);
@mkdir($dir, 0777, true);
$ics = $dir . '/recurring-exdate.ics';
$content = "BEGIN:VCALENDAR\n"
. "BEGIN:VEVENT\n"
. "UID:weekly-2\n"
. "DTSTART:20260205T090000\n"
. "RRULE:FREQ=WEEKLY;COUNT=4\n"
. "EXDATE:20260212T090000\n"
. "SUMMARY:Termin mit Ausnahme\n"
. "END:VEVENT\n"
. "END:VCALENDAR\n";
@file_put_contents($ics, $content);
$events = ChronologicalIcsEvents::eventsForDate($ics, '2026-02-12');
$summaries = array_map(static fn(array $e): string => (string)$e['summary'], $events);
if (in_array('Termin mit Ausnahme', $summaries, true)) {
throw new \Exception('Recurring event with EXDATE should not appear on excluded day');
}
}
public function testEventsForDateKeepsUtcDateAndTimeAsIs(): void
{
$previousTimezone = date_default_timezone_get();
date_default_timezone_set('Europe/Berlin');
try {
$dir = TMP_DIR . '/chrono_ics/' . uniqid('tz_', true);
@mkdir($dir, 0777, true);
$ics = $dir . '/timezone.ics';
$content = "BEGIN:VCALENDAR\n"
. "BEGIN:VEVENT\n"
. "UID:utc-shift\n"
. "DTSTART:20260216T233000Z\n"
. "SUMMARY:UTC Spaet\n"
. "END:VEVENT\n"
. "END:VCALENDAR\n";
@file_put_contents($ics, $content);
$eventsOn16 = ChronologicalIcsEvents::eventsForDate($ics, '2026-02-16');
$eventsOn17 = ChronologicalIcsEvents::eventsForDate($ics, '2026-02-17');
$summaries16 = array_map(static fn(array $e): string => (string)$e['summary'], $eventsOn16);
$summaries17 = array_map(static fn(array $e): string => (string)$e['summary'], $eventsOn17);
if (!in_array('UTC Spaet', $summaries16, true)) {
throw new \Exception('UTC event should stay on its own UTC date');
}
if (in_array('UTC Spaet', $summaries17, true)) {
throw new \Exception('UTC event should not be shifted to next day by server timezone');
}
$utcEvent = null;
foreach ($eventsOn16 as $entry) {
if ((string)($entry['summary'] ?? '') === 'UTC Spaet') {
$utcEvent = $entry;
break;
}
}
if (!is_array($utcEvent)) {
throw new \Exception('UTC event payload missing after day match');
}
if ((string)($utcEvent['time'] ?? '') !== '23:30') {
throw new \Exception('UTC event time should remain unchanged (expected 23:30)');
}
} finally {
date_default_timezone_set($previousTimezone);
}
}
public function testEventsForDateShowsMultiDayAllDayEventOnOverlappingDays(): void
{
$dir = TMP_DIR . '/chrono_ics/' . uniqid('multiday_', true);
@mkdir($dir, 0777, true);
$ics = $dir . '/multiday.ics';
$content = "BEGIN:VCALENDAR\n"
. "BEGIN:VEVENT\n"
. "UID:multi-day-1\n"
. "DTSTART;VALUE=DATE:20260216\n"
. "DTEND;VALUE=DATE:20260218\n"
. "SUMMARY:Mehrtagesereignis\n"
. "END:VEVENT\n"
. "END:VCALENDAR\n";
@file_put_contents($ics, $content);
$eventsOn16 = ChronologicalIcsEvents::eventsForDate($ics, '2026-02-16');
$eventsOn17 = ChronologicalIcsEvents::eventsForDate($ics, '2026-02-17');
$eventsOn18 = ChronologicalIcsEvents::eventsForDate($ics, '2026-02-18');
$summaries16 = array_map(static fn(array $e): string => (string)$e['summary'], $eventsOn16);
$summaries17 = array_map(static fn(array $e): string => (string)$e['summary'], $eventsOn17);
$summaries18 = array_map(static fn(array $e): string => (string)$e['summary'], $eventsOn18);
if (!in_array('Mehrtagesereignis', $summaries16, true)) {
throw new \Exception('Multi-day all-day event should appear on start day');
}
if (!in_array('Mehrtagesereignis', $summaries17, true)) {
throw new \Exception('Multi-day all-day event should appear on overlapping day');
}
if (in_array('Mehrtagesereignis', $summaries18, true)) {
throw new \Exception('Multi-day all-day event should respect exclusive DTEND day');
}
}
}