calendar-plugin/fixture-tests/fixture_smoke.sh

331 lines
13 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
BASE_URL="${BASE_URL:-http://127.0.0.1:8080}"
echo "[smoke] checking api list"
curl -fsS -H 'X-WP-User: admin' "$BASE_URL/wp-json/calendar/v1/events" >/dev/null
echo "[smoke] checking ics"
curl -fsS "$BASE_URL/calendar.ics" | grep -q 'BEGIN:VCALENDAR'
echo "[smoke] checking caldav read"
curl -fsS -u rw_user@example.test:rwpass123456 "$BASE_URL/caldav/calendars/public/1.ics" >/dev/null
echo "[smoke] checking caldav multiget href filtering"
TMP_XML="$(mktemp)"
cleanup_tmp_xml() { rm -f "$TMP_XML"; }
trap cleanup_tmp_xml EXIT
cat > "$TMP_XML" <<'XML'
<?xml version="1.0" encoding="UTF-8"?>
<C:calendar-multiget xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
<D:prop><D:getetag/><C:calendar-data/></D:prop>
<D:href>/caldav/calendars/public/4.ics</D:href>
</C:calendar-multiget>
XML
REPORT_XML="$(curl -fsS -u rw_user@example.test:rwpass123456 -X REPORT \
"$BASE_URL/caldav/calendars/public/" \
-H 'Content-Type: text/xml; charset=utf-8' \
-H 'Depth: 1' \
--data-binary @"$TMP_XML")"
echo "$REPORT_XML" | grep -q '/caldav/calendars/public/4.ics'
if echo "$REPORT_XML" | grep -q '/caldav/calendars/public/1.ics'; then
echo "[smoke] multiget returned unrelated resources"
exit 1
fi
trap - EXIT
cleanup_tmp_xml
echo "[smoke] checking recurrence exception delete"
curl -fsS -X DELETE -H 'X-WP-User: admin' \
"$BASE_URL/wp-json/calendar/v1/events/10/occurrences/2026-04-17T14:00:00+01:00" \
-o /dev/null -w '%{http_code}' | grep -q '^204$'
echo "[smoke] checking event occurrences endpoint + idempotent delete"
CREATE_JSON='{"title":"Smoke Occurrences API","description":"occ-endpoint","location":"","category":"","all_day_event":false,"start_datetime":"2026-04-01T10:00:00+01:00","end_datetime":"2026-04-01T11:00:00+01:00","repeat_type":"daily","repeat_interval":1,"repeat_range_mode":"count","repeat_count":3}'
CREATE_RESP="$(curl -fsS -H 'Content-Type: application/json' -H 'X-WP-User: admin' \
-d "$CREATE_JSON" "$BASE_URL/wp-json/calendar/v1/events")"
EVENT_ID="$(python3 - <<'PY' "$CREATE_RESP"
import json,sys
print(json.loads(sys.argv[1])["data"]["id"])
PY
)"
OCC_RESP="$(curl -fsS -H 'X-WP-User: admin' \
"$BASE_URL/wp-json/calendar/v1/events/${EVENT_ID}/occurrences?from=2026-04-01&months=1")"
python3 - <<'PY' "$OCC_RESP"
import json,sys
data=json.loads(sys.argv[1])
days=[x["occurrence_start"][:10] for x in data["data"]]
assert len(data["data"]) >= 3
assert "2026-04-02" in days
PY
curl -fsS -X DELETE -H 'X-WP-User: admin' \
"$BASE_URL/wp-json/calendar/v1/events/${EVENT_ID}/occurrences/2026-04-02T10:00:00+01:00" \
-o /dev/null -w '%{http_code}' | grep -q '^204$'
curl -fsS -X DELETE -H 'X-WP-User: admin' \
"$BASE_URL/wp-json/calendar/v1/events/${EVENT_ID}/occurrences/2026-04-02T10:00:00+01:00" \
-o /dev/null -w '%{http_code}' | grep -q '^204$'
OCC_RESP="$(curl -fsS -H 'X-WP-User: admin' \
"$BASE_URL/wp-json/calendar/v1/events/${EVENT_ID}/occurrences?from=2026-04-01&months=1")"
python3 - <<'PY' "$OCC_RESP"
import json,sys
days=[x["occurrence_start"][:10] for x in json.loads(sys.argv[1])["data"]]
assert "2026-04-02" not in days
PY
echo "[smoke] checking caldav vtimezone parsing regression"
TMP_ICS="$(mktemp)"
cleanup_tmp_ics() { rm -f "$TMP_ICS"; }
trap cleanup_tmp_ics EXIT
cat > "$TMP_ICS" <<'ICS'
BEGIN:VCALENDAR
PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN
VERSION:2.0
BEGIN:VTIMEZONE
TZID:Europe/London
BEGIN:STANDARD
DTSTART:18471201T000000
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=9
TZOFFSETFROM:+0115
TZOFFSETTO:+0000
TZNAME:GMT
END:STANDARD
END:VTIMEZONE
BEGIN:VEVENT
UID:smoke-vtimezone-parser-001
SUMMARY:Smoke VTIMEZONE Parse
DTSTART;TZID=Europe/London:20260423T150000
DTEND;TZID=Europe/London:20260423T160000
END:VEVENT
END:VCALENDAR
ICS
curl -fsS -u rw_user@example.test:rwpass123456 -X PUT \
-H 'Content-Type: text/calendar; charset=utf-8' \
--data-binary @"$TMP_ICS" \
"$BASE_URL/caldav/calendars/public/smoke-vtimezone.ics" >/dev/null
CALDAV_ICS="$(curl -fsS -u rw_user@example.test:rwpass123456 \
"$BASE_URL/caldav/calendars/public/smoke-vtimezone.ics")"
echo "$CALDAV_ICS" | grep -q 'DTSTART[^[:space:]]*:20260423T150000'
if echo "$CALDAV_ICS" | grep -q 'RRULE'; then
echo "[smoke] unexpected RRULE found in VEVENT-only upload"
exit 1
fi
trap - EXIT
cleanup_tmp_ics
echo "[smoke] checking caldav monthly nth-weekday roundtrip"
TMP_ICS="$(mktemp)"
cleanup_tmp_ics() { rm -f "$TMP_ICS"; }
trap cleanup_tmp_ics EXIT
cat > "$TMP_ICS" <<'ICS'
BEGIN:VCALENDAR
PRODID:-//Smoke//EN
VERSION:2.0
BEGIN:VEVENT
UID:smoke-monthly-nth-001
SUMMARY:Smoke Monthly Nth
DTSTART;TZID=Europe/London:20260402T150000
DTEND;TZID=Europe/London:20260402T160000
RRULE:FREQ=MONTHLY;BYDAY=SU;BYSETPOS=4
END:VEVENT
END:VCALENDAR
ICS
curl -fsS -u rw_user@example.test:rwpass123456 -X PUT \
-H 'Content-Type: text/calendar; charset=utf-8' \
--data-binary @"$TMP_ICS" \
"$BASE_URL/caldav/calendars/public/smoke-monthly-nth.ics" >/dev/null
CALDAV_ICS="$(curl -fsS -u rw_user@example.test:rwpass123456 \
"$BASE_URL/caldav/calendars/public/smoke-monthly-nth.ics")"
echo "$CALDAV_ICS" | grep -q 'RRULE:FREQ=MONTHLY;BYDAY=SU;BYSETPOS=4'
trap - EXIT
cleanup_tmp_ics
echo "[smoke] checking caldav monthly BYDAY ordinal import"
TMP_ICS="$(mktemp)"
cleanup_tmp_ics() { rm -f "$TMP_ICS"; }
trap cleanup_tmp_ics EXIT
cat > "$TMP_ICS" <<'ICS'
BEGIN:VCALENDAR
PRODID:-//Smoke//EN
VERSION:2.0
BEGIN:VEVENT
UID:smoke-monthly-ordinal-001
SUMMARY:Smoke Monthly Ordinal
DTSTART;TZID=Europe/London:20260411T150000
DTEND;TZID=Europe/London:20260411T160000
RRULE:FREQ=MONTHLY;BYDAY=2SA
END:VEVENT
END:VCALENDAR
ICS
curl -fsS -u rw_user@example.test:rwpass123456 -X PUT \
-H 'Content-Type: text/calendar; charset=utf-8' \
--data-binary @"$TMP_ICS" \
"$BASE_URL/caldav/calendars/public/smoke-monthly-ordinal.ics" >/dev/null
CALDAV_ICS="$(curl -fsS -u rw_user@example.test:rwpass123456 \
"$BASE_URL/caldav/calendars/public/smoke-monthly-ordinal.ics")"
echo "$CALDAV_ICS" | grep -q 'RRULE:FREQ=MONTHLY;BYDAY=SA;BYSETPOS=2'
trap - EXIT
cleanup_tmp_ics
echo "[smoke] checking caldav monthly last-weekday roundtrip"
TMP_ICS="$(mktemp)"
cleanup_tmp_ics() { rm -f "$TMP_ICS"; }
trap cleanup_tmp_ics EXIT
cat > "$TMP_ICS" <<'ICS'
BEGIN:VCALENDAR
PRODID:-//Smoke//EN
VERSION:2.0
BEGIN:VEVENT
UID:smoke-monthly-last-001
SUMMARY:Smoke Monthly Last
DTSTART;TZID=Europe/London:20260425T150000
DTEND;TZID=Europe/London:20260425T160000
RRULE:FREQ=MONTHLY;BYDAY=SA;BYSETPOS=-1
END:VEVENT
END:VCALENDAR
ICS
curl -fsS -u rw_user@example.test:rwpass123456 -X PUT \
-H 'Content-Type: text/calendar; charset=utf-8' \
--data-binary @"$TMP_ICS" \
"$BASE_URL/caldav/calendars/public/smoke-monthly-last.ics" >/dev/null
CALDAV_ICS="$(curl -fsS -u rw_user@example.test:rwpass123456 \
"$BASE_URL/caldav/calendars/public/smoke-monthly-last.ics")"
echo "$CALDAV_ICS" | grep -q 'RRULE:FREQ=MONTHLY;BYDAY=SA;BYSETPOS=-1'
trap - EXIT
cleanup_tmp_ics
echo "[smoke] checking monthly nth-weekday delete-exception preserves rule"
CREATE_JSON='{"title":"Smoke 4th Sunday Anchor","description":"anchor-normalization","location":"","category":"","all_day_event":false,"start_datetime":"2026-05-19T15:00:00+01:00","end_datetime":"2026-05-19T16:00:00+01:00","repeat_type":"monthly","repeat_interval":1,"repeat_nth_mode":"weekday_of_month","repeat_nth_pos":4,"repeat_nth_weekday":0,"repeat_range_mode":"no_end"}'
CREATE_RESP="$(curl -fsS -H 'Content-Type: application/json' -H 'X-WP-User: admin' \
-d "$CREATE_JSON" "$BASE_URL/wp-json/calendar/v1/events")"
python3 - <<'PY' "$CREATE_RESP"
import json,sys
data=json.loads(sys.argv[1])["data"]
assert data["start_datetime"] == "2026-05-24T15:00:00+01:00"
PY
EVENT_ID="$(python3 - <<'PY' "$CREATE_RESP"
import json,sys
print(json.loads(sys.argv[1])["data"]["id"])
PY
)"
curl -fsS -X DELETE -H 'X-WP-User: admin' \
"$BASE_URL/wp-json/calendar/v1/events/${EVENT_ID}/occurrences/2026-05-24T15:00:00+01:00" \
-o /dev/null -w '%{http_code}' | grep -q '^204$'
CALDAV_ICS="$(curl -fsS -u rw_user@example.test:rwpass123456 \
"$BASE_URL/caldav/calendars/public/${EVENT_ID}.ics")"
echo "$CALDAV_ICS" | grep -q 'RRULE:FREQ=MONTHLY;BYDAY=SU;BYSETPOS=4'
echo "$CALDAV_ICS" | grep -q 'EXDATE;TZID=Europe/London:20260524T150000'
echo "[smoke] checking delete-exception canonical key matching"
CREATE_JSON='{"title":"Smoke Daily Exception TZ","description":"tz-key","location":"","category":"","all_day_event":false,"start_datetime":"2026-03-02T10:00:00+00:00","end_datetime":"2026-03-02T11:00:00+00:00","repeat_type":"daily","repeat_interval":1,"repeat_range_mode":"until","repeat_until":"2026-03-19"}'
CREATE_RESP="$(curl -fsS -H 'Content-Type: application/json' -H 'X-WP-User: admin' \
-d "$CREATE_JSON" "$BASE_URL/wp-json/calendar/v1/events")"
EVENT_ID="$(python3 - <<'PY' "$CREATE_RESP"
import json,sys
print(json.loads(sys.argv[1])["data"]["id"])
PY
)"
curl -fsS -X DELETE -H 'X-WP-User: admin' \
"$BASE_URL/wp-json/calendar/v1/events/${EVENT_ID}/occurrences/2026-03-11T11:00:00+01:00" \
-o /dev/null -w '%{http_code}' | grep -q '^204$'
OCC_RESP="$(curl -fsS -H 'X-WP-User: admin' \
"$BASE_URL/wp-json/calendar/v1/events/${EVENT_ID}/occurrences?from=2026-03-01&months=1")"
python3 - <<'PY' "$OCC_RESP"
import json,sys
days=[x["occurrence_start"][:10] for x in json.loads(sys.argv[1])["data"]]
assert "2026-03-11" not in days
PY
curl -fsS -X DELETE -H 'X-WP-User: admin' \
"$BASE_URL/wp-json/calendar/v1/events/${EVENT_ID}/occurrences/2026-03-10" \
-o /dev/null -w '%{http_code}' | grep -q '^204$'
OCC_RESP="$(curl -fsS -H 'X-WP-User: admin' \
"$BASE_URL/wp-json/calendar/v1/events/${EVENT_ID}/occurrences?from=2026-03-01&months=1")"
python3 - <<'PY' "$OCC_RESP"
import json,sys
days=[x["occurrence_start"][:10] for x in json.loads(sys.argv[1])["data"]]
assert "2026-03-10" not in days
PY
echo "[smoke] checking caldav EXDATE import to recurrence exceptions"
TMP_ICS="$(mktemp)"
cleanup_tmp_ics() { rm -f "$TMP_ICS"; }
trap cleanup_tmp_ics EXIT
cat > "$TMP_ICS" <<'ICS'
BEGIN:VCALENDAR
PRODID:-//Smoke//EN
VERSION:2.0
BEGIN:VEVENT
UID:smoke-exdate-import-001
SUMMARY:Smoke EXDATE Import
DTSTART;TZID=Europe/London:20260302T100000
DTEND;TZID=Europe/London:20260302T110000
RRULE:FREQ=DAILY;UNTIL=20260319T235959
EXDATE;TZID=Europe/London:20260310T100000,20260311T100000
END:VEVENT
END:VCALENDAR
ICS
curl -fsS -u rw_user@example.test:rwpass123456 -X PUT \
-H 'Content-Type: text/calendar; charset=utf-8' \
--data-binary @"$TMP_ICS" \
"$BASE_URL/caldav/calendars/public/smoke-exdate-import.ics" >/dev/null
EVENT_ID="$(curl -fsS -H 'X-WP-User: admin' "$BASE_URL/wp-json/calendar/v1/events" | python3 -c "import json,sys; data=json.load(sys.stdin)['data']; print([e for e in data if e.get('uid')=='smoke-exdate-import-001'][0]['id'])")"
OCC_RESP="$(curl -fsS -H 'X-WP-User: admin' \
"$BASE_URL/wp-json/calendar/v1/events/${EVENT_ID}/occurrences?from=2026-03-01&months=1")"
python3 - <<'PY' "$OCC_RESP"
import json,sys
days=[x["occurrence_start"][:10] for x in json.loads(sys.argv[1])["data"]]
assert "2026-03-10" not in days
assert "2026-03-11" not in days
PY
trap - EXIT
cleanup_tmp_ics
echo "[smoke] checking caldav cancelled-occurrence component handling"
TMP_ICS="$(mktemp)"
cleanup_tmp_ics() { rm -f "$TMP_ICS"; }
trap cleanup_tmp_ics EXIT
cat > "$TMP_ICS" <<'ICS'
BEGIN:VCALENDAR
PRODID:-//Smoke//EN
VERSION:2.0
BEGIN:VEVENT
UID:smoke-cancelled-occurrence-001
SUMMARY:Smoke Cancelled Occurrence
DTSTART;TZID=Europe/London:20260408T123000
DTEND;TZID=Europe/London:20260408T133000
RRULE:FREQ=WEEKLY
END:VEVENT
BEGIN:VEVENT
UID:smoke-cancelled-occurrence-001
RECURRENCE-ID;TZID=Europe/London:20260506T123000
DTSTART;TZID=Europe/London:20260506T123000
DTEND;TZID=Europe/London:20260506T133000
STATUS:CANCELLED
END:VEVENT
END:VCALENDAR
ICS
curl -fsS -u rw_user@example.test:rwpass123456 -X PUT \
-H 'Content-Type: text/calendar; charset=utf-8' \
--data-binary @"$TMP_ICS" \
"$BASE_URL/caldav/calendars/public/smoke-cancelled-occurrence.ics" >/dev/null
CALDAV_ICS="$(curl -fsS -u rw_user@example.test:rwpass123456 \
"$BASE_URL/caldav/calendars/public/smoke-cancelled-occurrence.ics")"
echo "$CALDAV_ICS" | grep -q 'RRULE:FREQ=WEEKLY'
echo "$CALDAV_ICS" | grep -q 'EXDATE;TZID=Europe/London:20260506T123000'
EVENT_ID="$(curl -fsS -H 'X-WP-User: admin' "$BASE_URL/wp-json/calendar/v1/events" | python3 -c "import json,sys; data=json.load(sys.stdin)['data']; print([e for e in data if e.get('uid')=='smoke-cancelled-occurrence-001'][0]['id'])")"
OCC_RESP="$(curl -fsS -H 'X-WP-User: admin' \
"$BASE_URL/wp-json/calendar/v1/events/${EVENT_ID}/occurrences?from=2026-05-01&months=1")"
python3 - <<'PY' "$OCC_RESP"
import json,sys
days=[x["occurrence_start"][:10] for x in json.loads(sys.argv[1])["data"]]
assert "2026-05-06" not in days
assert "2026-05-13" in days
PY
trap - EXIT
cleanup_tmp_ics
echo "[smoke] checking future-only default"
curl -fsS "$BASE_URL/calendar" | grep -q 'id="futureOnly" type="checkbox" checked'
echo "[smoke] all checks passed"