Remove the oauth Docker Compose profile (mcp-oauth service, port 8001) which used OAuth bearer tokens for direct NC API access, requiring upstream OIDC patches. All NC access should use app passwords via Login Flow v2 or BasicAuth. Changes: - Remove mcp-oauth service from docker-compose.yml - Remove oauth mode from CI test matrix - Delete oauth pass-through tests (core, permissions, token exchange) - Delete oauth-specific tests (elicitation, NC PHP app, astrolabe) - Migrate MCP/OAuth integration tests to login-flow profile: - DCR lifecycle, deletion, token type tests - Scope authorization (tool filtering) tests - Token introspection tests - Fix flaky consent screen automation: replace JS btn.click() with Playwright native click + retry (handles Vue.js event binding race) - Add scope-filtered OAuth client fixtures to login-flow conftest - Keep keycloak profile for external IdP testing Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
215 lines
8.0 KiB
YAML
215 lines
8.0 KiB
YAML
name: Tests
|
|
|
|
on:
|
|
pull_request:
|
|
branches:
|
|
- master
|
|
|
|
jobs:
|
|
linting:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
- name: Install the latest version of uv
|
|
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
|
|
- name: Check format
|
|
run: uv run --frozen ruff format --diff
|
|
- name: Linting
|
|
run: uv run --frozen ruff check
|
|
- name: Type check
|
|
run: uv run --frozen ty check -- nextcloud_mcp_server
|
|
|
|
unit-test:
|
|
runs-on: ubuntu-latest
|
|
needs: [linting]
|
|
steps:
|
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
- name: Install the latest version of uv
|
|
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
|
|
- name: Run unit tests
|
|
run: uv run pytest -v -m unit -o "addopts=-p no:asyncio"
|
|
|
|
integration-test:
|
|
runs-on: ubuntu-latest
|
|
needs: [linting]
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
nextcloud_version:
|
|
- "31"
|
|
- "32"
|
|
# - "33" # Disabled until all upstream apps support NC 33
|
|
mode:
|
|
- "single-user"
|
|
- "multi-user-basic"
|
|
- "login-flow"
|
|
include:
|
|
# Version-specific image pins — Renovate updates these via customManagers in renovate.json
|
|
# Each entry is pinned to its major version (e.g., NC 31 only gets 31.x updates)
|
|
- nextcloud_version: "31"
|
|
nextcloud_image: "docker.io/library/nextcloud:31.0.14@sha256:9bf3fae91aad4dca3eff02c1f71df8d5c6705a349065fb537aa5c5ef578f1013"
|
|
- nextcloud_version: "32"
|
|
nextcloud_image: "docker.io/library/nextcloud:32.0.6@sha256:5c4e09f72f096cd68379a8ae69f71e61d13da5a07430fc4a17c702a14e6a4267"
|
|
# Disabled until all upstream apps support NC 33
|
|
# - nextcloud_version: "33"
|
|
# nextcloud_image: "docker.io/library/nextcloud:33.0.1@sha256:0b14848e05d19aa25e8a189eb9cfef4215489e0cbf5eb999e8784bf03027c226"
|
|
|
|
# Mode-specific properties
|
|
- mode: single-user
|
|
profile: single-user
|
|
markers: "(smoke and not keycloak and not login_flow and not multi_user_basic) or (integration and not keycloak and not login_flow and not multi_user_basic)"
|
|
wait-port: 8000
|
|
mcp-internal-url: "http://mcp:8000"
|
|
needs-playwright: false
|
|
extra-args: >-
|
|
--ignore=tests/integration/test_qdrant_collection_creation.py
|
|
--ignore=tests/rag_evaluation/
|
|
|
|
- mode: multi-user-basic
|
|
profile: multi-user-basic
|
|
markers: "multi_user_basic"
|
|
wait-port: 8003
|
|
mcp-internal-url: "http://mcp-multi-user-basic:8000"
|
|
needs-playwright: true
|
|
extra-args: ""
|
|
|
|
- mode: login-flow
|
|
profile: login-flow
|
|
markers: "login_flow"
|
|
wait-port: 8004
|
|
mcp-internal-url: "http://mcp-login-flow:8004"
|
|
needs-playwright: true
|
|
extra-args: ""
|
|
|
|
name: integration (${{ matrix.mode }} / nc${{ matrix.nextcloud_version }})
|
|
|
|
steps:
|
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
with:
|
|
submodules: 'true'
|
|
|
|
- name: Set up PHP 8.4
|
|
if: matrix.mode != 'single-user'
|
|
uses: shivammathur/setup-php@44454db4f0199b8b9685a5d763dc37cbf79108e1 # 2.36.0
|
|
with:
|
|
php-version: 8.4
|
|
coverage: none
|
|
|
|
# OIDC app installed from app store (dev mount removed from docker-compose.yml)
|
|
|
|
- name: Set up Node.js
|
|
if: matrix.mode != 'single-user'
|
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
|
with:
|
|
node-version: 24
|
|
|
|
- name: Build Astrolabe app
|
|
if: matrix.mode != 'single-user'
|
|
run: |
|
|
cd third_party/astrolabe
|
|
composer install --no-dev --optimize-autoloader
|
|
npm ci
|
|
npm run build
|
|
|
|
# OIDC app is now installed from the Nextcloud app store via app-hook
|
|
# (third_party/oidc fork no longer mounted — upstream v1.16.3 fixes PR #631)
|
|
# - name: Build OIDC app
|
|
# run: |
|
|
# cd third_party/oidc
|
|
# composer install --no-dev --optimize-autoloader
|
|
# npm ci
|
|
# npm run build
|
|
|
|
# Start services with the appropriate profile
|
|
- name: Run docker compose
|
|
uses: hoverkraft-tech/compose-action@4894d2492015c1774ee5a13a95b1072093087ec3 # v2.5.0
|
|
with:
|
|
compose-file: "./docker-compose.yml"
|
|
compose-flags: "--profile ${{ matrix.profile }}"
|
|
up-flags: "--build"
|
|
env:
|
|
MCP_SERVER_URL: ${{ matrix.mcp-internal-url }}
|
|
NEXTCLOUD_IMAGE: ${{ matrix.nextcloud_image }}
|
|
|
|
- name: Install the latest version of uv
|
|
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
|
|
|
|
- name: Install Playwright
|
|
if: matrix.needs-playwright
|
|
run: uv run playwright install chromium --with-deps
|
|
|
|
# Wait for Nextcloud to be healthy
|
|
- name: Wait for Nextcloud
|
|
run: |
|
|
echo "Waiting for Nextcloud at http://localhost:8080..."
|
|
max_attempts=60
|
|
attempt=0
|
|
until curl -sSf http://localhost:8080/status.php 2>/dev/null | grep -q '"installed":true'; do
|
|
attempt=$((attempt + 1))
|
|
if [ $attempt -ge $max_attempts ]; then
|
|
echo "Nextcloud did not become ready in time."
|
|
docker compose logs app
|
|
exit 1
|
|
fi
|
|
echo "Attempt $attempt/$max_attempts: Not ready, sleeping 5s..."
|
|
sleep 5
|
|
done
|
|
echo "Nextcloud is ready."
|
|
|
|
# Wait for the MCP service to be healthy
|
|
- name: Wait for MCP service (${{ matrix.mode }})
|
|
run: |
|
|
echo "Waiting for MCP service on port ${{ matrix.wait-port }}..."
|
|
max_attempts=30
|
|
attempt=0
|
|
until curl -o /dev/null -s -w "%{http_code}\n" http://localhost:${{ matrix.wait-port }}/health 2>/dev/null | grep -qE "200|404|405"; do
|
|
attempt=$((attempt + 1))
|
|
if [ $attempt -ge $max_attempts ]; then
|
|
echo "MCP service did not become ready in time."
|
|
docker compose --profile ${{ matrix.profile }} logs
|
|
exit 1
|
|
fi
|
|
echo "Attempt $attempt/$max_attempts: Not ready, sleeping 5s..."
|
|
sleep 5
|
|
done
|
|
echo "MCP service is ready on port ${{ matrix.wait-port }}."
|
|
|
|
- name: Verify OIDC configuration
|
|
if: matrix.mode == 'login-flow'
|
|
run: |
|
|
echo "=== OIDC Discovery ==="
|
|
curl -s http://localhost:8080/.well-known/openid-configuration | jq .
|
|
echo "=== OIDC App Status ==="
|
|
docker compose exec -T app php occ app:list --output=json 2>/dev/null | jq '.enabled.oidc // "NOT INSTALLED"'
|
|
|
|
- name: Run tests (${{ matrix.mode }})
|
|
env:
|
|
NEXTCLOUD_HOST: "http://localhost:8080"
|
|
NEXTCLOUD_USERNAME: "admin"
|
|
NEXTCLOUD_PASSWORD: "admin"
|
|
run: |
|
|
uv run pytest -v \
|
|
--log-cli-level=WARN \
|
|
-m '${{ matrix.markers }}' \
|
|
-o "addopts=-p no:asyncio" \
|
|
--timeout=300 \
|
|
${{ matrix.extra-args }}
|
|
|
|
- name: Collect service logs on failure
|
|
if: failure()
|
|
run: |
|
|
docker compose --profile ${{ matrix.profile }} logs --tail=500 > /tmp/docker-compose-logs.txt 2>&1
|
|
docker compose exec -T app cat /var/www/html/data/nextcloud.log 2>/dev/null | tail -100 > /tmp/nextcloud-app.log 2>&1 || true
|
|
|
|
- name: Upload debug artifacts
|
|
if: failure()
|
|
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
|
with:
|
|
name: debug-${{ matrix.mode }}-nc${{ matrix.nextcloud_version }}
|
|
path: |
|
|
/tmp/*.png
|
|
/tmp/docker-compose-logs.txt
|
|
/tmp/nextcloud-app.log
|
|
retention-days: 7
|
|
if-no-files-found: ignore
|