One path with an EditScope on the write verbs. v1 had a second parallel tree at /api/calendar/events/series/* -- 1,165 lines mostly duplicating the non-series handlers, dispatching on string literals in 53 places where a typo was a runtime fallthrough. What a scoped edit means to the stored .ics lives in runway-core::series, pure and tested without a server, because that is the subtle part and v1 shipped it with no coverage at all. Editing one occurrence writes an override and no EXDATE: an EXDATE says the occurrence does not happen, an override says it happens differently, and writing both is contradictory. Deleting one writes the EXDATE and removes any override that named it. Splitting a series divides its bound rather than dropping it. Six weekly occurrences split at the third become two plus four, not two plus forever -- the count is what the person asked for and it should survive being cut. Overrides after the split move to the new series; moving a whole series shifts its overrides' RECURRENCE-IDs by the same amount instead of leaving them pointing at occurrences that no longer exist. Every write states a precondition. There is no unconditional path: an update without an ETag is refused, and a stale one is a conflict rather than a silent overwrite. UIDs are minted server-side, because a client-supplied one could collide with and replace an unrelated event. Reads use time-range and fan out across calendars concurrently. Zones that cannot be resolved are reported in the response instead of being rendered as though they were fine. Two tests found real bugs: sub-second timestamps cannot survive iCalendar's one-second resolution, and splitting at the first occurrence was dropping the recurrence rule and quietly turning a series into a single event.
61 lines
2.0 KiB
Bash
Executable File
61 lines
2.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Run the CalDAV integration tests against a throwaway Baikal.
|
|
#
|
|
# Starts a container, walks it through the install wizard, runs the live test
|
|
# suite against it, and tears it down again. Nothing touches a real calendar.
|
|
#
|
|
# crates/runway-caldav/tests/baikal/run.sh # start, test, stop
|
|
# KEEP=1 crates/runway-caldav/tests/baikal/run.sh # leave it running
|
|
set -euo pipefail
|
|
|
|
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
NAME="${NAME:-runway-baikal}"
|
|
PORT="${PORT:-8800}"
|
|
IMAGE="${IMAGE:-docker.io/ckulka/baikal:nginx}"
|
|
USERNAME="testuser"
|
|
PASSWORD="testpassword"
|
|
|
|
runtime() {
|
|
if command -v podman >/dev/null 2>&1; then echo podman
|
|
elif command -v docker >/dev/null 2>&1; then echo docker
|
|
else echo "need podman or docker" >&2; exit 1
|
|
fi
|
|
}
|
|
RUNTIME="$(runtime)"
|
|
|
|
cleanup() {
|
|
if [ "${KEEP:-0}" != "1" ]; then
|
|
"$RUNTIME" rm -f "$NAME" >/dev/null 2>&1 || true
|
|
else
|
|
echo "container $NAME left running on port $PORT"
|
|
fi
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
"$RUNTIME" rm -f "$NAME" >/dev/null 2>&1 || true
|
|
"$RUNTIME" run -d --rm --name "$NAME" -p "$PORT:80" "$IMAGE" >/dev/null
|
|
echo "started $NAME ($IMAGE) on port $PORT"
|
|
|
|
# Baikal needs a moment before PHP answers.
|
|
for _ in $(seq 1 60); do
|
|
if [ "$(curl -sS -o /dev/null -w '%{http_code}' -L "http://localhost:$PORT/" 2>/dev/null)" = "200" ]; then
|
|
break
|
|
fi
|
|
sleep 1
|
|
done
|
|
|
|
python3 "$HERE/setup.py" "http://localhost:$PORT" "$USERNAME" "$PASSWORD"
|
|
|
|
export RUNWAY_CALDAV_URL="http://localhost:$PORT/dav.php/"
|
|
export RUNWAY_CALDAV_USER="$USERNAME"
|
|
export RUNWAY_CALDAV_PASSWORD="$PASSWORD"
|
|
# Turns a silent skip into a failure: see the note in live.rs.
|
|
export RUNWAY_REQUIRE_CALDAV=1
|
|
|
|
# Both suites: the CalDAV client against the server, and the backend's own
|
|
# login flow against it. Skipped tests report as "ok", so the only way to know
|
|
# they ran is to run them here.
|
|
cargo test -p runway-caldav --test live -- --test-threads=1 "$@"
|
|
cargo test -p runway-server --test auth -- --test-threads=1 "$@"
|
|
cargo test -p runway-server --test events -- --test-threads=1 "$@"
|