Skip to content

V 3.0-dev - async support and misc #1240

V 3.0-dev - async support and misc

V 3.0-dev - async support and misc #1240

Workflow file for this run

---
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:
image: ghcr.io/cyrusimap/cyrus-docker-test-server:latest
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
bedework:
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('setup.py') }}|${{ 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"
# 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
# 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
docker exec ${{ job.services.nextcloud.id }} php -r "
\$db = new PDO('sqlite:/var/www/html/data/nextcloud.db');
\$db->exec('DELETE FROM oc_ratelimit_entries');
\$db->exec('DELETE FROM oc_bruteforce_attempts');
echo 'Cleared rate limit and bruteforce caches\n';
" || true
echo "Nextcloud is configured!"
- 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
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
- 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
BEDEWORK_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('setup.py') }}|${{ 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('setup.py') }}|${{ 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('setup.py') }}|${{ hashFiles('tox.ini') }}
- run: pip install tox
- run: tox -e deptry
async-niquests:
# Test that async code works with niquests when httpx is not installed
name: async (niquests fallback)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install dependencies without httpx
run: |
pip install --editable .[test]
pip uninstall -y httpx
- name: Verify niquests is used
run: |
python -c "
from caldav.async_davclient import _USE_HTTPX, _USE_NIQUESTS
assert not _USE_HTTPX, 'httpx 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
sync-requests:
# Test that sync code works with requests when niquests is not installed
name: sync (requests fallback)
runs-on: ubuntu-latest
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: Verify requests is used
run: |
python -c "
from caldav.davclient 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 "Radicale" --ignore=tests/test_async_integration.py