Add the CalDAV client and a CLI to drive it
Discovery is the three PROPFINDs RFC 4791 describes rather than a walk through likely URLs. Queries use time-range, which v1 never did -- it fetched whole calendars and filtered in the browser on every view change. Every write states a precondition, so a stale ETag produces a Conflict a caller can act on instead of silently destroying somebody's edit. XML goes through quick-xml with namespace resolution. v1 matched prefixes with six regexes tried in sequence and recompiled inside the loop; there is a fixture here that is the same document under different prefixes, and it parses identically. Protocol parsing is split from transport so it can be tested against responses recorded from a real Baikal -- including the second propstat carrying 404s, which is what makes "this calendar has no colour" different from "this calendar has an empty colour". Live tests run against a real server, never a mock. tests/baikal/run.sh starts a container, walks Baikal's install wizard, and runs them; each test builds and destroys its own collection, so pointing it at a real server touches nothing that was already there. They cover discovery, round-trip, stale-ETag conflict, duplicate create, delete, time-range filtering, a series returned whole with its override, and writing every synthetic golden fixture to the server and reading it back. libdav was evaluated first, as planned. Not adopted: its HttpClient trait is defined over hyper::body::Incoming, so using it means replacing reqwest everywhere, plus a DNS resolver for service discovery we do not do and a second XML parser. Its precondition design is where Precondition's shape comes from. Reasons are recorded in the crate docs.
This commit is contained in:
Executable
+53
@@ -0,0 +1,53 @@
|
||||
#!/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"
|
||||
|
||||
cargo test -p runway-caldav --test live -- --test-threads=1 "$@"
|
||||
Reference in New Issue
Block a user