docs(config): document auth_type and other connection keys #1557
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --- | |
| name: tests | |
| on: | |
| push: | |
| branches: | |
| - master | |
| pull_request: | |
| branches: | |
| - master | |
| jobs: | |
| tests: | |
| name: ${{ matrix.python }} | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| python: ${{ github.event_name == 'pull_request' && fromJSON('["3.10", "3.14"]') || fromJSON('["3.10", "3.11", "3.12", "3.13", "3.14"]') }} | |
| services: | |
| baikal: | |
| image: ckulka/baikal:nginx | |
| ports: | |
| - 8800:80 | |
| options: >- | |
| --health-cmd "curl -f http://localhost/ || exit 1" | |
| --health-interval 10s | |
| --health-timeout 5s | |
| --health-retries 5 | |
| --health-start-period 30s | |
| nextcloud: | |
| image: nextcloud:latest | |
| ports: | |
| - 8801:80 | |
| env: | |
| NEXTCLOUD_ADMIN_USER: admin | |
| NEXTCLOUD_ADMIN_PASSWORD: admin | |
| options: >- | |
| --health-cmd "curl -f http://localhost/status.php || exit 1" | |
| --health-interval 10s | |
| --health-timeout 5s | |
| --health-retries 5 | |
| --health-start-period 60s | |
| cyrus: | |
| # Pinned to last known-good build (pre-April-2026 multi-stage rebuild | |
| # that broke CalDAV startup). Unpin once upstream is fixed. | |
| # Working digest confirmed in CI run 23748898521 (2026-03-30). | |
| image: ghcr.io/cyrusimap/cyrus-docker-test-server@sha256:d639a9116691a7a1c875073486c419d60843e5ef8e32e65c5ef56283874dbf2c | |
| ports: | |
| - 8802:8080 | |
| - 8001:8001 | |
| env: | |
| DEFAULTDOMAIN: example.com | |
| SERVERNAME: cyrus-test | |
| options: >- | |
| --health-cmd "curl -s http://localhost:8080/ || exit 1" | |
| --health-interval 10s | |
| --health-timeout 5s | |
| --health-retries 5 | |
| --health-start-period 60s | |
| sogo-db: | |
| image: mariadb:11 | |
| env: | |
| MYSQL_DATABASE: sogo | |
| MYSQL_USER: sogo | |
| MYSQL_PASSWORD: sogo | |
| MYSQL_ROOT_PASSWORD: sogo | |
| options: >- | |
| --health-cmd "healthcheck.sh --connect --innodb_initialized" | |
| --health-interval 5s | |
| --health-timeout 5s | |
| --health-retries 20 | |
| --health-start-period 10s | |
| --network-alias db | |
| sogo: | |
| image: japoch/sogo:latest | |
| ports: | |
| - 8803:80 | |
| env: | |
| sogo_user: testuser | |
| sogo_pass: testpass | |
| sogo_name: Test User | |
| sogo_fqhn: example.com | |
| bedework3: | |
| image: ioggstream/bedework:latest | |
| ports: | |
| - 8804:8080 | |
| options: >- | |
| --health-cmd "curl -f http://localhost:8080/bedework/ || exit 1" | |
| --health-interval 10s | |
| --health-timeout 5s | |
| --health-retries 15 | |
| --health-start-period 120s | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: ${{ matrix.python }} | |
| - uses: actions/cache@v4 | |
| with: | |
| path: ~/.cache/pip | |
| key: pip|${{ hashFiles('pyproject.toml') }}|${{ hashFiles('tox.ini') }} | |
| - run: pip install tox | |
| - name: Configure Baikal with pre-seeded database | |
| run: | | |
| # Copy pre-configured database and config to Baikal container | |
| docker cp tests/docker-test-servers/baikal/Specific/. ${{ job.services.baikal.id }}:/var/www/baikal/Specific/ | |
| docker cp tests/docker-test-servers/baikal/config/. ${{ job.services.baikal.id }}:/var/www/baikal/config/ | |
| # Fix permissions for SQLite | |
| docker exec ${{ job.services.baikal.id }} chown -R nginx:nginx /var/www/baikal/Specific /var/www/baikal/config | |
| docker exec ${{ job.services.baikal.id }} chmod -R 770 /var/www/baikal/Specific | |
| # Restart to pick up configuration | |
| docker restart ${{ job.services.baikal.id }} | |
| - name: Wait for Baikal to be ready | |
| run: | | |
| sleep 5 | |
| if timeout 60 bash -c 'until curl -f http://localhost:8800/ 2>/dev/null; do echo "Waiting..."; sleep 2; done'; then | |
| echo "✓ Baikal is ready!" | |
| else | |
| echo "✗ Error: Baikal did not become ready within 60 seconds" | |
| exit 1 | |
| fi | |
| - name: Configure Nextcloud | |
| run: | | |
| # Wait for Nextcloud web server to be up | |
| echo "Waiting for Nextcloud web server..." | |
| if timeout 60 bash -c 'until curl -f http://localhost:8801/status.php 2>/dev/null; do echo -n "."; sleep 2; done'; then | |
| echo "" | |
| echo "✓ Web server is up" | |
| else | |
| echo "" | |
| echo "✗ Error: Nextcloud web server did not become ready within 60 seconds" | |
| exit 1 | |
| fi | |
| # Install Nextcloud if not already installed | |
| if ! docker exec ${{ job.services.nextcloud.id }} php occ status 2>/dev/null | grep -q "installed: true"; then | |
| echo "Installing Nextcloud..." | |
| docker exec ${{ job.services.nextcloud.id }} php occ maintenance:install \ | |
| --database=sqlite \ | |
| --admin-user=admin \ | |
| --admin-pass=admin | |
| echo "✓ Nextcloud installed" | |
| else | |
| echo "✓ Nextcloud is already installed" | |
| fi | |
| # Disable password policy | |
| docker exec ${{ job.services.nextcloud.id }} php occ app:disable password_policy || true | |
| # Create test user | |
| docker exec -e OC_PASS="testpass" ${{ job.services.nextcloud.id }} php occ user:add --password-from-env --display-name="Test User" testuser || echo "User may already exist" | |
| # Create scheduling test users (user1-user3) | |
| for i in 1 2 3; do | |
| docker exec -e OC_PASS="testpass${i}" ${{ job.services.nextcloud.id }} php occ user:add --password-from-env --display-name="User ${i}" "user${i}" || echo "user${i} may already exist" | |
| done | |
| # Set email addresses for scheduling users (required for calendar-user-address-set) | |
| for i in 1 2 3; do | |
| docker exec ${{ job.services.nextcloud.id }} php occ user:setting "user${i}" settings email "user${i}@localhost" || true | |
| done | |
| # Enable calendar and contacts apps | |
| docker exec ${{ job.services.nextcloud.id }} php occ app:enable calendar || true | |
| docker exec ${{ job.services.nextcloud.id }} php occ app:enable contacts || true | |
| # Disable rate limiting and bruteforce protection | |
| docker exec ${{ job.services.nextcloud.id }} php occ config:system:set ratelimit.enabled --value=false --type=boolean || true | |
| docker exec ${{ job.services.nextcloud.id }} php occ app:disable bruteforcesettings || true | |
| docker exec ${{ job.services.nextcloud.id }} php occ config:system:set auth.bruteforce.protection.enabled --value=false --type=boolean || true | |
| # Disable CalDAV trashbin: setting calendarRetentionObligation=0 makes | |
| # CalDavBackend hard-delete objects instead of soft-deleting them. | |
| # Without this, deleted events remain in oc_calendarobjects with a deleted_at | |
| # timestamp, causing UNIQUE constraint violations when tests recreate the same UID. | |
| docker exec ${{ job.services.nextcloud.id }} php occ config:app:set dav calendarRetentionObligation --value=0 || true | |
| docker exec ${{ job.services.nextcloud.id }} php occ dav:retention:clean-up || true | |
| # Configure CalDAV rate limits | |
| docker exec ${{ job.services.nextcloud.id }} php occ config:app:set dav rateLimitCalendarCreation --value=99999 || true | |
| docker exec ${{ job.services.nextcloud.id }} php occ config:app:set dav maximumCalendarsSubscriptions --value=-1 || true | |
| # Add IP whitelist for rate limiting | |
| docker exec ${{ job.services.nextcloud.id }} php occ config:system:set ratelimit.whitelist.0 --value='172.17.0.0/16' || true | |
| docker exec ${{ job.services.nextcloud.id }} php occ config:system:set ratelimit.whitelist.1 --value='127.0.0.1' || true | |
| # Clear rate limit cache. The SQLite file is named after the `dbname` | |
| # config value, which defaults to `owncloud` — look it up rather than | |
| # guessing, and check it exists first: PDO creates a missing SQLite | |
| # file, so a wrong path silently yields "no such table" for every | |
| # DELETE below. | |
| DB_NAME=$(docker exec ${{ job.services.nextcloud.id }} php occ config:system:get dbname 2>/dev/null | tr -d '\r\n') | |
| DB_PATH="/var/www/html/data/${DB_NAME:-owncloud}.db" | |
| if docker exec ${{ job.services.nextcloud.id }} test -f "$DB_PATH"; then | |
| docker exec ${{ job.services.nextcloud.id }} php -r " | |
| \$db = new PDO('sqlite:$DB_PATH'); | |
| foreach (['oc_ratelimit_entries', 'oc_bruteforce_attempts'] as \$table) { | |
| try { | |
| \$db->exec(\"DELETE FROM \$table\"); | |
| } catch (PDOException \$e) { | |
| fwrite(STDERR, \"skipping \$table: \" . \$e->getMessage() . \"\n\"); | |
| } | |
| } | |
| echo \"Cleared rate limit and bruteforce caches\n\"; | |
| " || true | |
| else | |
| echo "No database found at $DB_PATH — skipping cache cleanup" | |
| fi | |
| echo "Nextcloud is configured!" | |
| - name: Configure Cyrus | |
| run: | | |
| # Copy imapd.conf with virtdomains: off (required for iTIP scheduling delivery). | |
| # The default virtdomains: userid setting causes caladdress_lookup() to preserve | |
| # the full email form (user2@example.com) while mailbox ACLs use the short form | |
| # (user2), resulting in 403 errors when delivering iTIP invites. | |
| sed 's/{{DEFAULTDOMAIN}}/example.com/g; s/{{SERVERNAME}}/cyrus-test/g' \ | |
| tests/docker-test-servers/cyrus/imapd.conf > /tmp/imapd_expanded.conf | |
| docker cp /tmp/imapd_expanded.conf ${{ job.services.cyrus.id }}:/srv/cyrus-docker-test-server.git/imapd.conf | |
| docker restart ${{ job.services.cyrus.id }} | |
| echo "✓ Cyrus reconfigured with virtdomains: off" | |
| - name: Wait for Cyrus to be ready | |
| run: | | |
| echo "Waiting for Cyrus server..." | |
| # Cyrus takes a bit longer to initialize | |
| sleep 10 | |
| if timeout 60 bash -c 'until curl -s http://localhost:8802/ 2>/dev/null | grep -q .; do echo -n "."; sleep 2; done'; then | |
| echo "" | |
| echo "✓ Cyrus HTTP server is ready" | |
| else | |
| echo "" | |
| echo "✗ Error: Cyrus HTTP server did not become ready within 60 seconds" | |
| exit 1 | |
| fi | |
| # Verify CalDAV access with pre-created user | |
| # Cyrus CalDAV can take significant time to initialize, especially in CI | |
| echo "Waiting for CalDAV access..." | |
| if timeout 120 bash -c 'until curl -s -X PROPFIND -H "Depth: 0" -u user1:x http://localhost:8802/dav/calendars/user/user1/ 2>/dev/null | grep -qi "multistatus\|collection" ; do echo -n "."; sleep 2; done'; then | |
| echo "" | |
| echo "✓ Cyrus CalDAV with pre-created user ready" | |
| else | |
| echo "" | |
| echo "✗ Error: Cyrus CalDAV did not become accessible within 120 seconds" | |
| echo "Attempting to debug..." | |
| echo "" | |
| echo "=== Container logs ===" | |
| docker logs ${{ job.services.cyrus.id }} 2>&1 | tail -100 || true | |
| echo "" | |
| echo "=== Listening ports inside container ===" | |
| docker exec ${{ job.services.cyrus.id }} netstat -tlnp 2>&1 || true | |
| echo "" | |
| echo "=== Running processes ===" | |
| docker exec ${{ job.services.cyrus.id }} ps aux 2>&1 || true | |
| echo "" | |
| echo "=== HTTP response from host ===" | |
| curl -v http://localhost:8802/ 2>&1 || true | |
| echo "" | |
| echo "=== CalDAV PROPFIND response from host ===" | |
| curl -v -X PROPFIND -H "Depth: 0" -u user1:x http://localhost:8802/dav/calendars/user/user1/ 2>&1 || true | |
| echo "" | |
| echo "=== HTTP response from inside container ===" | |
| docker exec ${{ job.services.cyrus.id }} curl -v http://localhost:8080/ 2>&1 || true | |
| exit 1 | |
| fi | |
| - name: Configure SOGo | |
| run: | | |
| echo "Configuring SOGo..." | |
| # Wait for database to be ready | |
| echo "Waiting for database..." | |
| sleep 5 | |
| # Initialize database with test user | |
| docker cp tests/docker-test-servers/sogo/init-sogo-users.sql ${{ job.services.sogo-db.id }}:/tmp/init-sogo-users.sql | |
| docker exec -i ${{ job.services.sogo-db.id }} mariadb -usogo -psogo sogo < tests/docker-test-servers/sogo/init-sogo-users.sql | |
| echo "✓ Database initialized with test user" | |
| # Copy SOGo configuration (database is now reachable via network alias 'db') | |
| docker cp tests/docker-test-servers/sogo/sogo.conf ${{ job.services.sogo.id }}:/etc/sogo/sogo.conf | |
| echo "✓ SOGo configuration copied" | |
| # Restart SOGo to pick up configuration | |
| docker restart ${{ job.services.sogo.id }} | |
| # Wait for SOGo to be ready | |
| echo "Waiting for SOGo to start..." | |
| if timeout 60 bash -c 'until curl -f http://localhost:8803/SOGo/ 2>/dev/null; do echo -n "."; sleep 2; done'; then | |
| echo "" | |
| echo "✓ SOGo is ready" | |
| else | |
| echo "" | |
| echo "✗ Error: SOGo did not become ready within 60 seconds" | |
| exit 1 | |
| fi | |
| # Verify CalDAV access | |
| echo "Verifying CalDAV access..." | |
| if curl -s -X PROPFIND -H "Depth: 0" -u testuser:testpass http://localhost:8803/SOGo/dav/testuser/Calendar/ 2>/dev/null | grep -qi "multistatus"; then | |
| echo "✓ SOGo CalDAV access verified" | |
| else | |
| echo "✗ Error: SOGo CalDAV access failed" | |
| exit 1 | |
| fi | |
| - name: Configure Bedework 3.10.3 | |
| run: | | |
| echo "Waiting for Bedework..." | |
| # Bedework/JBoss takes longer to start up | |
| if timeout 180 bash -c 'until curl -f http://localhost:8804/bedework/ 2>/dev/null; do echo -n "."; sleep 5; done'; then | |
| echo "" | |
| echo "✓ Bedework web interface is ready" | |
| else | |
| echo "" | |
| echo "✗ Error: Bedework did not become ready within 180 seconds" | |
| exit 1 | |
| fi | |
| # Verify CalDAV access with default user (vbede:bedework) | |
| echo "Verifying CalDAV access..." | |
| if curl -s -X PROPFIND -H "Depth: 0" -u vbede:bedework http://localhost:8804/ucaldav/user/vbede/ 2>/dev/null | grep -qi "multistatus"; then | |
| echo "✓ Bedework CalDAV access verified" | |
| else | |
| echo "✗ Error: Bedework CalDAV access failed" | |
| exit 1 | |
| fi | |
| # Runs the full test suite (sync + async) against all servers above. | |
| # Async tests run with niquests (the default install). | |
| - run: tox -e py | |
| env: | |
| NEXTCLOUD_URL: http://localhost:8801 | |
| BAIKAL_URL: http://localhost:8800 | |
| CYRUS_URL: http://localhost:8802 | |
| SOGO_URL: http://localhost:8803 | |
| BEDEWORK3_URL: http://localhost:8804 | |
| docs: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.14" | |
| - uses: actions/cache@v4 | |
| with: | |
| path: ~/.cache/pip | |
| key: pip|${{ hashFiles('pyproject.toml') }}|${{ hashFiles('tox.ini') }} | |
| - run: pip install tox | |
| - run: tox -e docs | |
| style: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.14" | |
| - uses: actions/cache@v4 | |
| with: | |
| path: ~/.cache/pip | |
| key: pip|${{ hashFiles('pyproject.toml') }}|${{ hashFiles('tox.ini') }} | |
| - uses: actions/cache@v4 | |
| with: | |
| path: ~/.cache/pre-commit | |
| key: pre-commit|${{ hashFiles('.pre-commit-config.yaml') }} | |
| - run: pip install tox | |
| - run: tox -e style | |
| deptry: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.13" | |
| - uses: actions/cache@v4 | |
| with: | |
| path: ~/.cache/pip | |
| key: pip|${{ hashFiles('pyproject.toml') }}|${{ hashFiles('tox.ini') }} | |
| - run: pip install tox | |
| - run: tox -e deptry | |
| # The three async-* jobs below exist to test the async backend *selection* logic, | |
| # not to re-run the async integration tests. The async HTTP library | |
| # (_USE_HTTPX / _USE_NIQUESTS / _HTTPX_FLAVOUR) is chosen at import time based on | |
| # what is installed, so the only way to exercise each fallback path is to | |
| # manipulate the installed packages before running pytest. The main `tests` | |
| # job above already covers async tests with niquests (the default install); | |
| # these jobs cover the httpxyz and plain-httpx fallback paths and explicitly | |
| # assert the right _USE_* flag before running tests. | |
| async-niquests: | |
| # Explicit labelled check that the niquests path (default) works in isolation. | |
| # Complements the main `tests` job; uninstalls httpx/httpxyz to ensure niquests | |
| # is selected, then runs unit tests only (no server required). | |
| name: async (niquests) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.12" | |
| - name: Install dependencies without httpx/httpxyz | |
| run: | | |
| pip install --editable .[test] | |
| pip uninstall -y httpx httpxyz | |
| - name: Verify niquests is used | |
| run: | | |
| python -c " | |
| from caldav.async_davclient import _USE_HTTPX, _USE_NIQUESTS | |
| assert not _USE_HTTPX, 'httpx/httpxyz should not be available' | |
| assert _USE_NIQUESTS, 'niquests should be used' | |
| print('✓ Using niquests for async HTTP') | |
| " | |
| - name: Run async tests with niquests | |
| run: pytest tests/test_async_davclient.py -v | |
| async-httpxyz: | |
| # Uninstalls niquests and installs httpxyz to force the httpxyz fallback path. | |
| # Runs unit tests only (no server required). | |
| name: async (httpxyz fallback) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.12" | |
| - name: Install dependencies with httpxyz, without niquests | |
| run: | | |
| pip install --editable .[test] | |
| pip uninstall -y niquests | |
| pip install httpxyz | |
| - name: Verify httpxyz is used | |
| run: | | |
| python -c " | |
| from caldav.async_davclient import _HTTPX_FLAVOUR, _USE_HTTPX, _USE_NIQUESTS | |
| assert not _USE_NIQUESTS, 'niquests should not be available' | |
| assert _HTTPX_FLAVOUR == 'httpxyz', f'expected httpxyz, got {_HTTPX_FLAVOUR}' | |
| assert _USE_HTTPX, '_USE_HTTPX should be set when httpxyz is used' | |
| print('✓ Using httpxyz for async HTTP') | |
| " | |
| - name: Run async tests with httpxyz | |
| run: pytest tests/test_async_davclient.py -v | |
| async-httpx2: | |
| # Uninstalls niquests and httpxyz and installs httpx2 - Pydantic's | |
| # continuation of httpx, a separate package rather than a new httpx release. | |
| # Unlike httpxyz it does not register itself in sys.modules as "httpx", so | |
| # this job is what catches code that reaches for httpx by name. | |
| # Runs unit tests plus the Xandikos async integration tests (embedded, no | |
| # service container needed), so the backend is exercised over real HTTP. | |
| name: async (httpx2 fallback) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.12" | |
| - name: Install dependencies with httpx2, without niquests or httpxyz | |
| run: | | |
| pip install --editable .[test] | |
| pip uninstall -y niquests httpxyz httpx | |
| pip install httpx2 | |
| - name: Verify httpx2 is used | |
| run: | | |
| python -c " | |
| from caldav.async_davclient import _HTTPX_FLAVOUR, _USE_HTTPX, _USE_NIQUESTS | |
| assert not _USE_NIQUESTS, 'niquests should not be available' | |
| assert _USE_HTTPX, '_USE_HTTPX should be set when httpx2 is used' | |
| assert _HTTPX_FLAVOUR == 'httpx2', f'expected httpx2, got {_HTTPX_FLAVOUR}' | |
| print('✓ Using httpx2 for async HTTP') | |
| " | |
| - name: Run async tests with httpx2 | |
| # Xandikos runs embedded, so no service container is needed; the | |
| # selection is deliberately narrow rather than "everything not baikal", | |
| # since the runner has docker and would otherwise auto-discover the | |
| # docker test servers. | |
| run: pytest tests/test_async_davclient.py tests/test_async_integration.py -v -k "xandikos or Xandikos" | |
| async-httpx: | |
| # Uninstalls both niquests and httpxyz to force the plain-httpx fallback path. | |
| # Runs unit tests + a real integration test against Baikal (the lightest server) | |
| # to verify end-to-end async HTTP with this backend. | |
| name: async (httpx fallback) | |
| runs-on: ubuntu-latest | |
| services: | |
| baikal: | |
| image: ckulka/baikal:nginx | |
| ports: | |
| - 8800:80 | |
| options: >- | |
| --health-cmd "curl -f http://localhost/ || exit 1" | |
| --health-interval 10s | |
| --health-timeout 5s | |
| --health-retries 5 | |
| --health-start-period 30s | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.12" | |
| - name: Install dependencies with httpx only | |
| run: | | |
| pip install --editable .[test] | |
| pip uninstall -y niquests httpxyz | |
| - name: Configure Baikal with pre-seeded database | |
| run: | | |
| docker cp tests/docker-test-servers/baikal/Specific/. ${{ job.services.baikal.id }}:/var/www/baikal/Specific/ | |
| docker cp tests/docker-test-servers/baikal/config/. ${{ job.services.baikal.id }}:/var/www/baikal/config/ | |
| docker exec ${{ job.services.baikal.id }} chown -R nginx:nginx /var/www/baikal/Specific /var/www/baikal/config | |
| docker exec ${{ job.services.baikal.id }} chmod -R 770 /var/www/baikal/Specific | |
| docker restart ${{ job.services.baikal.id }} | |
| - name: Wait for Baikal to be ready | |
| run: | | |
| if timeout 60 bash -c 'until curl -f http://localhost:8800/ 2>/dev/null; do echo "Waiting..."; sleep 2; done'; then | |
| echo "✓ Baikal is ready!" | |
| else | |
| echo "✗ Error: Baikal did not become ready within 60 seconds" | |
| exit 1 | |
| fi | |
| - name: Verify httpx is used | |
| run: | | |
| python -c " | |
| from caldav.async_davclient import _HTTPX_FLAVOUR, _USE_HTTPX, _USE_NIQUESTS | |
| assert not _USE_NIQUESTS, 'niquests should not be available' | |
| assert _HTTPX_FLAVOUR == 'httpx', f'expected httpx, got {_HTTPX_FLAVOUR}' | |
| assert _USE_HTTPX, 'httpx should be used' | |
| print('✓ Using httpx for async HTTP') | |
| " | |
| - name: Run async tests with httpx | |
| run: pytest tests/test_async_davclient.py tests/test_async_integration.py -v -k baikal | |
| env: | |
| BAIKAL_URL: http://localhost:8800 | |
| sync-requests: | |
| # Test that sync code works with requests when niquests is not installed | |
| name: sync (requests fallback) | |
| runs-on: ubuntu-latest | |
| services: | |
| baikal: | |
| image: ckulka/baikal:nginx | |
| ports: | |
| - 8800:80 | |
| options: >- | |
| --health-cmd "curl -f http://localhost/ || exit 1" | |
| --health-interval 10s | |
| --health-timeout 5s | |
| --health-retries 5 | |
| --health-start-period 30s | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.12" | |
| - name: Install dependencies with requests instead of niquests | |
| run: | | |
| pip install --editable .[test] | |
| pip uninstall -y niquests | |
| pip install requests | |
| - name: Configure Baikal with pre-seeded database | |
| run: | | |
| docker cp tests/docker-test-servers/baikal/Specific/. ${{ job.services.baikal.id }}:/var/www/baikal/Specific/ | |
| docker cp tests/docker-test-servers/baikal/config/. ${{ job.services.baikal.id }}:/var/www/baikal/config/ | |
| docker exec ${{ job.services.baikal.id }} chown -R nginx:nginx /var/www/baikal/Specific /var/www/baikal/config | |
| docker exec ${{ job.services.baikal.id }} chmod -R 770 /var/www/baikal/Specific | |
| docker restart ${{ job.services.baikal.id }} | |
| - name: Wait for Baikal to be ready | |
| run: | | |
| if timeout 60 bash -c 'until curl -f http://localhost:8800/ 2>/dev/null; do echo "Waiting..."; sleep 2; done'; then | |
| echo "✓ Baikal is ready!" | |
| else | |
| echo "✗ Error: Baikal did not become ready within 60 seconds" | |
| exit 1 | |
| fi | |
| - name: Verify requests is used | |
| run: | | |
| python -c " | |
| from caldav.lib.http_sync import USE_REQUESTS, USE_NIQUESTS | |
| assert USE_REQUESTS, 'requests should be available' | |
| assert not USE_NIQUESTS, 'niquests should not be available' | |
| print('✓ Using requests for sync HTTP') | |
| " | |
| - name: Run sync tests with requests | |
| run: pytest tests/test_caldav.py -v -k "Baikal or Radicale" --ignore=tests/test_async_integration.py | |
| env: | |
| BAIKAL_URL: http://localhost:8800 |