Skip to content

fix(web): close model selector before opening provider dialogs #38191

fix(web): close model selector before opening provider dialogs

fix(web): close model selector before opening provider dialogs #38191

name: Run Integration Tests v2
concurrency:
group: Run-Integration-Tests-${{ github.workflow }}-${{ github.head_ref || github.event.workflow_run.head_branch || github.run_id }}
cancel-in-progress: true
on:
merge_group:
pull_request:
branches:
- main
- "release/**"
# NOTE: Intentionally no `paths:` filter. We always trigger and let the
# `changes` job below decide whether the real test matrix runs. This
# avoids the dual-workflow skip pattern where a `paths-ignore`'d skip
# workflow can race the real workflow's same-named status check under
# branch protection.
push:
tags:
- "v*.*.*"
permissions:
contents: read
env:
# Test Environment Variables
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
SLACK_BOT_TOKEN_TEST_SPACE: ${{ secrets.SLACK_BOT_TOKEN_TEST_SPACE }}
CONFLUENCE_TEST_SPACE_URL: ${{ vars.CONFLUENCE_TEST_SPACE_URL }}
CONFLUENCE_USER_NAME: ${{ vars.CONFLUENCE_USER_NAME }}
CONFLUENCE_ACCESS_TOKEN: ${{ secrets.CONFLUENCE_ACCESS_TOKEN }}
CONFLUENCE_ACCESS_TOKEN_SCOPED: ${{ secrets.CONFLUENCE_ACCESS_TOKEN_SCOPED }}
JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }}
JIRA_ADMIN_USER_EMAIL: ${{ vars.JIRA_ADMIN_USER_EMAIL }}
JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }}
JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}
JIRA_API_TOKEN_SCOPED: ${{ secrets.JIRA_API_TOKEN_SCOPED }}
PERM_SYNC_SHAREPOINT_CLIENT_ID: ${{ secrets.PERM_SYNC_SHAREPOINT_CLIENT_ID }}
PERM_SYNC_SHAREPOINT_PRIVATE_KEY: ${{ secrets.PERM_SYNC_SHAREPOINT_PRIVATE_KEY }}
PERM_SYNC_SHAREPOINT_CERTIFICATE_PASSWORD: ${{ secrets.PERM_SYNC_SHAREPOINT_CERTIFICATE_PASSWORD }}
PERM_SYNC_SHAREPOINT_DIRECTORY_ID: ${{ secrets.PERM_SYNC_SHAREPOINT_DIRECTORY_ID }}
EXA_API_KEY: ${{ secrets.EXA_API_KEY }}
GITHUB_PERMISSION_SYNC_TEST_ACCESS_TOKEN: ${{ secrets.ONYX_GITHUB_PERMISSION_SYNC_TEST_ACCESS_TOKEN }}
GITHUB_PERMISSION_SYNC_TEST_ACCESS_TOKEN_CLASSIC: ${{ secrets.ONYX_GITHUB_PERMISSION_SYNC_TEST_ACCESS_TOKEN_CLASSIC }}
GITHUB_ADMIN_EMAIL: ${{ secrets.ONYX_GITHUB_ADMIN_EMAIL }}
GITHUB_TEST_USER_1_EMAIL: ${{ secrets.ONYX_GITHUB_TEST_USER_1_EMAIL }}
GITHUB_TEST_USER_2_EMAIL: ${{ secrets.ONYX_GITHUB_TEST_USER_2_EMAIL }}
jobs:
changes:
# Decides whether the integration test matrix runs. On pull_request /
# merge_group we use paths-filter; on push (tags) we default to `true` so
# everything runs.
runs-on: ubuntu-latest
timeout-minutes: 5
# paths-filter needs pull-requests:read to list PR files on private repos (no-op on public).
permissions:
contents: read
pull-requests: read
outputs:
integration: ${{ steps.filter.outputs.integration || 'true' }}
gateway: ${{ steps.filter.outputs.gateway || 'true' }}
steps:
- name: Checkout code
if: github.event_name == 'pull_request' || github.event_name == 'merge_group'
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # ratchet:actions/checkout@v6
with:
persist-credentials: false
- uses: dorny/paths-filter@7b450fff21473bca461d4b92ce414b9d0420d706
id: filter
if: github.event_name == 'pull_request' || github.event_name == 'merge_group'
with:
filters: |
integration:
- 'backend/**'
- 'cli/**'
- 'deployment/docker_compose/**'
- '.devcontainer/**'
- 'docker-bake.hcl'
- 'pyproject.toml'
- 'uv.lock'
- '.github/workflows/pr-integration-tests.yml'
- '.github/actions/setup-test-license/**'
- '.github/actions/install-python-deps/**'
- '.github/actions/login-ecr-pullthrough-cache/**'
gateway:
- 'backend/onyx/server/gateway/**'
- 'backend/tests/integration/tests/gateway_clients/**'
discover-test-dirs:
# NOTE: Github-hosted runners have about 20s faster queue times and are preferred here.
needs: changes
if: needs.changes.outputs.integration == 'true'
runs-on: ubuntu-slim
timeout-minutes: 5
outputs:
test-dirs: ${{ steps.set-matrix.outputs.test-dirs }}
editions: ${{ steps.set-editions.outputs.editions }}
steps:
- name: Checkout code
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # ratchet:actions/checkout@v6
with:
persist-credentials: false
- name: Discover test directories
id: set-matrix
env:
GATEWAY_CHANGED: ${{ needs.changes.outputs.gateway }}
run: |
# Find all leaf-level directories in both test directories.
# ``craft`` excluded: needs sandbox infra this lane doesn't set up;
tests_dirs=$(find backend/tests/integration/tests -mindepth 1 -maxdepth 1 -type d ! -name "__pycache__" ! -name "mcp" ! -name "no_vectordb" ! -name "migrations" ! -name "craft" ! -name "gateway_clients" -exec basename {} \; | sort)
if [[ "$GATEWAY_CHANGED" == "true" ]]; then
tests_dirs="$tests_dirs gateway_clients"
fi
connector_dirs=$(find backend/tests/integration/connector_job_tests -mindepth 1 -maxdepth 1 -type d ! -name "__pycache__" -exec basename {} \; | sort)
# Create JSON array with directory info
all_dirs=""
for dir in $tests_dirs; do
all_dirs="$all_dirs{\"path\":\"tests/$dir\",\"name\":\"tests-$dir\"},"
done
for dir in $connector_dirs; do
all_dirs="$all_dirs{\"path\":\"connector_job_tests/$dir\",\"name\":\"connector-$dir\"},"
done
# Remove trailing comma and wrap in array
all_dirs="[${all_dirs%,}]"
echo "test-dirs=$all_dirs" >> $GITHUB_OUTPUT
- name: Determine editions to test
id: set-editions
run: |
# On PRs, only run EE tests. On merge_group and tags, run both EE and MIT.
if [ "${{ github.event_name }}" = "pull_request" ]; then
echo 'editions=["ee"]' >> $GITHUB_OUTPUT
else
echo 'editions=["ee","mit"]' >> $GITHUB_OUTPUT
fi
build-model-server-image:
needs: changes
if: needs.changes.outputs.integration == 'true'
runs-on:
[
runs-on,
runner=1cpu-linux-arm64,
"run-id=${{ github.run_id }}-build-model-server-image",
"extras=ecr-cache",
]
timeout-minutes: 10
steps:
- uses: runs-on/action@4e5f72399b6b17f2e79c511c1b38a315a64d22dc
- name: Checkout code
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # ratchet:actions/checkout@v6
with:
persist-credentials: false
- name: Build model server image
uses: ./.github/actions/build-model-server-image
with:
runs-on-ecr-cache: ${{ env.RUNS_ON_ECR_CACHE }}
ref-name: ${{ github.ref_name }}
pr-number: ${{ github.event.pull_request.number }}
github-sha: ${{ github.event.pull_request.head.sha || github.sha }}
run-id: ${{ github.run_id }}
tag-prefix: integration-test-model-server-test
ecr-registry: ${{ vars.ECR_REGISTRY }}
docker-username: ${{ secrets.DOCKER_USERNAME }}
docker-token: ${{ secrets.DOCKER_TOKEN }}
build-devcontainer-image:
needs: changes
if: needs.changes.outputs.integration == 'true'
runs-on:
[
runs-on,
runner=2cpu-linux-arm64,
"run-id=${{ github.run_id }}-build-devcontainer-image",
"extras=ecr-cache",
]
timeout-minutes: 10
steps:
- uses: runs-on/action@4e5f72399b6b17f2e79c511c1b38a315a64d22dc
- name: Checkout code
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # ratchet:actions/checkout@v6
with:
persist-credentials: false
- name: Format branch name for cache
id: format-branch
env:
PR_NUMBER: ${{ github.event.pull_request.number }}
REF_NAME: ${{ github.ref_name }}
run: |
if [ -n "${PR_NUMBER}" ]; then
CACHE_SUFFIX="${PR_NUMBER}"
else
# shellcheck disable=SC2001
CACHE_SUFFIX=$(echo "${REF_NAME}" | sed 's/[^A-Za-z0-9._-]/-/g')
fi
echo "cache-suffix=${CACHE_SUFFIX}" >> $GITHUB_OUTPUT
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # ratchet:docker/setup-buildx-action@v4
- name: Log in to ECR pull-through cache
uses: ./.github/actions/login-ecr-pullthrough-cache
with:
ecr-registry: ${{ vars.ECR_REGISTRY }}
- name: Build and push Devcontainer Docker image
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a
with:
context: .devcontainer
file: .devcontainer/Dockerfile
push: true
build-args: |
BASE_IMAGE_REGISTRY=${{ env.BASE_IMAGE_REGISTRY }}
tags: ${{ env.RUNS_ON_ECR_CACHE }}:integration-test-devcontainer-${{ github.run_id }}
# Attestations attach as ECR referrers to the image digest, which is
# stable across runs and caps out at 100 per subject.
provenance: false
sbom: false
cache-from: |
type=registry,ref=${{ env.RUNS_ON_ECR_CACHE }}:devcontainer-cache-${{ github.event.pull_request.head.sha || github.sha }}
type=registry,ref=${{ env.RUNS_ON_ECR_CACHE }}:devcontainer-cache-${{ steps.format-branch.outputs.cache-suffix }}
type=registry,ref=${{ env.RUNS_ON_ECR_CACHE }}:devcontainer-cache
type=registry,ref=${{ env.BASE_IMAGE_REGISTRY }}/onyxdotapp/onyx-devcontainer:latest
cache-to: |
type=registry,ref=${{ env.RUNS_ON_ECR_CACHE }}:devcontainer-cache-${{ github.event.pull_request.head.sha || github.sha }},mode=max
type=registry,ref=${{ env.RUNS_ON_ECR_CACHE }}:devcontainer-cache-${{ steps.format-branch.outputs.cache-suffix }},mode=max
type=registry,ref=${{ env.RUNS_ON_ECR_CACHE }}:devcontainer-cache,mode=max
no-cache: ${{ vars.DOCKER_NO_CACHE == 'true' }}
integration-tests:
needs:
[
discover-test-dirs,
build-model-server-image,
build-devcontainer-image,
]
permissions:
id-token: write # Required for OIDC-based AWS credential exchange
contents: read
runs-on:
- runs-on
- runner=4cpu-linux-arm64
- ${{ format('run-id={0}-integration-tests-{1}-job-{2}', github.run_id, matrix.edition, strategy['job-index']) }}
- extras=ecr-cache
timeout-minutes: 25
strategy:
fail-fast: false
matrix:
test-dir: ${{ fromJson(needs.discover-test-dirs.outputs.test-dirs) }}
edition: ${{ fromJson(needs.discover-test-dirs.outputs.editions) }}
steps:
- uses: runs-on/action@4e5f72399b6b17f2e79c511c1b38a315a64d22dc
- name: Checkout code
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # ratchet:actions/checkout@v6
with:
persist-credentials: false
- name: Setup test license
uses: ./.github/actions/setup-test-license
with:
aws-oidc-role-arn: ${{ secrets.AWS_OIDC_ROLE_ARN }}
- name: Log in to ECR pull-through cache
uses: ./.github/actions/login-ecr-pullthrough-cache
with:
ecr-registry: ${{ vars.ECR_REGISTRY }}
# NOTE: Use pre-ping/null pool to reduce flakiness due to dropped connections
# NOTE: don't need web server for integration tests
- name: Create .env file for Docker Compose
env:
ECR_CACHE: ${{ env.RUNS_ON_ECR_CACHE }}
RUN_ID: ${{ github.run_id }}
EDITION: ${{ matrix.edition }}
run: |
# Base config shared by both editions
cat <<EOF > deployment/docker_compose/.env
COMPOSE_PROFILES=s3-filestore
AUTH_TYPE=basic
POSTGRES_POOL_PRE_PING=true
POSTGRES_USE_NULL_POOL=true
REQUIRE_EMAIL_VERIFICATION=false
DISABLE_TELEMETRY=true
ONYX_MODEL_SERVER_IMAGE=${ECR_CACHE}:integration-test-model-server-test-${RUN_ID}
INTEGRATION_TESTS_MODE=true
MCP_SERVER_ENABLED=true
# The MCP client tests register a mock MCP server on a private/loopback
# host (TEST_WEB_HOSTNAME); opt in so the SSRF guard allows it. Loopback
# needs its own gate on top of the private one; cloud-metadata stays
# blocked regardless.
MCP_SERVER_ALLOW_PRIVATE_NETWORK=true
MCP_SERVER_ALLOW_LOOPBACK=true
AUTO_LLM_UPDATE_INTERVAL_SECONDS=10
ENABLE_CRAFT=true
# Lowered upload/bundle caps so cap-enforcement tests can use
# small payloads instead of multi-MiB allocations.
BUILD_MAX_UPLOAD_FILE_SIZE_MB=2
BUILD_MAX_TOTAL_UPLOAD_SIZE_MB=4
BUILD_MAX_UPLOAD_FILES_PER_SESSION=5
MAX_EMBEDDED_IMAGES_PER_FILE=50
USER_LIBRARY_MAX_FILES_PER_UPLOAD=5
SKILL_BUNDLE_PER_FILE_MAX_BYTES=1048576
EOF
# EE-only config
if [ "$EDITION" = "ee" ]; then
cat <<EOF >> deployment/docker_compose/.env
ENABLE_PAID_ENTERPRISE_EDITION_FEATURES=true
CHECK_TTL_MANAGEMENT_TASK_FREQUENCY_IN_HOURS=0.001
EOF
fi
# NOTE: only `inference_model_server` runs. `indexing_model_server` is the
# same image with the same routes (INDEXING_ONLY only changes the log
# request-id prefix), so the tests point both hosts at the one container
# and skip a second load of the embedding model.
- name: Start Docker containers
run: |
cd deployment/docker_compose
docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d \
--wait --wait-timeout 300 \
relational_db \
opensearch \
cache \
minio \
inference_model_server
id: start_docker
- name: Start Mock Services
if: matrix.test-dir.path == 'tests/indexing'
run: |
cd backend/tests/integration/mock_services
docker compose -f docker-compose.mock-it-services.yml \
-p mock-it-services-stack up -d
- name: Build CLI binary (for CLI integration tests)
if: contains(matrix.test-dir.path, 'tests/cli')
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # zizmor: ignore[cache-poisoning]
with:
go-version: "1.26.5"
cache-dependency-path: "cli/go.sum"
- name: Compile CLI binary
if: contains(matrix.test-dir.path, 'tests/cli')
run: |
cd cli
GOARCH=arm64 GOOS=linux go build -o ${{ github.workspace }}/onyx-cli-test .
- name: Install Python deps
uses: ./.github/actions/install-python-deps
with:
devcontainer-image: ${{ env.RUNS_ON_ECR_CACHE }}:integration-test-devcontainer-${{ github.run_id }}
- name: Run Integration Tests (${{ matrix.edition }}) for ${{ matrix.test-dir.name }}
uses: nick-fields/retry@ad984534de44a9489a53aefd81eb77f87c70dc60 # ratchet:nick-fields/retry@v4.0.0
with:
timeout_minutes: 20
max_attempts: 3
retry_wait_seconds: 10
command: |
echo "Running ${{ matrix.edition }} integration tests for ${{ matrix.test-dir.path }}..."
# gateway_clients drives real CLI subprocesses, which need a real
# TCP api_server; run one inside the test container and make every
# missing prerequisite a hard failure instead of a skip.
GATEWAY_CLIENTS_STRICT=""
RUN_CMD="uv run --no-sync pytest -v --tb=short -rs tests/integration/${{ matrix.test-dir.path }}"
if [ "${{ matrix.test-dir.path }}" = "tests/gateway_clients" ]; then
GATEWAY_CLIENTS_STRICT="true"
RUN_CMD="bash tests/integration/tests/gateway_clients/run_with_server.sh"
fi
# Mount CLI binary into the container if it exists (CLI tests only)
CLI_VOLUME=""
if [ -f "${{ github.workspace }}/onyx-cli-test" ]; then
CLI_VOLUME="-v ${{ github.workspace }}/onyx-cli-test:/usr/local/bin/onyx-cli-test:ro"
CLI_VOLUME="$CLI_VOLUME -e ONYX_CLI_BINARY=/usr/local/bin/onyx-cli-test"
fi
docker run --rm --network onyx_default \
--name test-runner \
--env-file deployment/docker_compose/.env \
-v ${{ github.workspace }}:/workspace \
-w /workspace/backend \
$CLI_VOLUME \
-e INTEGRATION_TESTS_MODE=true \
-e POSTGRES_HOST=relational_db \
-e POSTGRES_USER=postgres \
-e POSTGRES_PASSWORD=password \
-e POSTGRES_DB=postgres \
-e DB_READONLY_USER=db_readonly_user \
-e DB_READONLY_PASSWORD=password \
-e POSTGRES_POOL_PRE_PING=true \
-e POSTGRES_USE_NULL_POOL=true \
-e OPENSEARCH_HOST=opensearch \
-e REDIS_HOST=cache \
-e MODEL_SERVER_HOST=inference_model_server \
-e INDEXING_MODEL_SERVER_HOST=inference_model_server \
-e S3_ENDPOINT_URL=http://minio:9000 \
-e S3_AWS_ACCESS_KEY_ID=minioadmin \
-e S3_AWS_SECRET_ACCESS_KEY=minioadmin \
-e OPENAI_API_KEY=${OPENAI_API_KEY} \
-e ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY} \
-e GATEWAY_CLIENT_TESTS_REQUIRED=${GATEWAY_CLIENTS_STRICT} \
-e EXA_API_KEY=${EXA_API_KEY} \
-e SLACK_BOT_TOKEN=${SLACK_BOT_TOKEN} \
-e SLACK_BOT_TOKEN_TEST_SPACE=${SLACK_BOT_TOKEN_TEST_SPACE} \
-e CONFLUENCE_TEST_SPACE_URL=${CONFLUENCE_TEST_SPACE_URL} \
-e CONFLUENCE_USER_NAME=${CONFLUENCE_USER_NAME} \
-e CONFLUENCE_ACCESS_TOKEN=${CONFLUENCE_ACCESS_TOKEN} \
-e CONFLUENCE_ACCESS_TOKEN_SCOPED=${CONFLUENCE_ACCESS_TOKEN_SCOPED} \
-e JIRA_BASE_URL=${JIRA_BASE_URL} \
-e JIRA_ADMIN_USER_EMAIL=${JIRA_ADMIN_USER_EMAIL} \
-e JIRA_USER_EMAIL=${JIRA_USER_EMAIL} \
-e JIRA_API_TOKEN=${JIRA_API_TOKEN} \
-e JIRA_API_TOKEN_SCOPED=${JIRA_API_TOKEN_SCOPED} \
-e PERM_SYNC_SHAREPOINT_CLIENT_ID=${PERM_SYNC_SHAREPOINT_CLIENT_ID} \
-e PERM_SYNC_SHAREPOINT_PRIVATE_KEY="${PERM_SYNC_SHAREPOINT_PRIVATE_KEY}" \
-e PERM_SYNC_SHAREPOINT_CERTIFICATE_PASSWORD=${PERM_SYNC_SHAREPOINT_CERTIFICATE_PASSWORD} \
-e PERM_SYNC_SHAREPOINT_DIRECTORY_ID=${PERM_SYNC_SHAREPOINT_DIRECTORY_ID} \
-e GITHUB_PERMISSION_SYNC_TEST_ACCESS_TOKEN=${GITHUB_PERMISSION_SYNC_TEST_ACCESS_TOKEN} \
-e GITHUB_PERMISSION_SYNC_TEST_ACCESS_TOKEN_CLASSIC=${GITHUB_PERMISSION_SYNC_TEST_ACCESS_TOKEN_CLASSIC} \
-e GITHUB_ADMIN_EMAIL=${GITHUB_ADMIN_EMAIL} \
-e GITHUB_TEST_USER_1_EMAIL=${GITHUB_TEST_USER_1_EMAIL} \
-e GITHUB_TEST_USER_2_EMAIL=${GITHUB_TEST_USER_2_EMAIL} \
-e TEST_WEB_HOSTNAME=test-runner \
-e MOCK_CONNECTOR_SERVER_HOST=mock_connector_server \
-e MOCK_CONNECTOR_SERVER_PORT=8001 \
-e ENABLE_PAID_ENTERPRISE_EDITION_FEATURES=${{ matrix.edition == 'ee' && 'true' || 'false' }} \
-e ENABLE_CRAFT=true \
-e ONYX_DEV_LICENSE \
${{ env.RUNS_ON_ECR_CACHE }}:integration-test-devcontainer-${{ github.run_id }} \
$RUN_CMD
# ------------------------------------------------------------
# Always gather logs BEFORE "down". The test-runner container's
# stdout (in-process api_server logs from pytest) is captured
# directly in the workflow log for the "Run Integration Tests" step.
- name: Dump all-container logs (optional)
if: always()
run: |
cd deployment/docker_compose
docker compose logs --no-color > $GITHUB_WORKSPACE/docker-compose.log || true
- name: Upload logs
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
with:
name: docker-all-logs-${{ matrix.edition }}-${{ matrix.test-dir.name }}
path: ${{ github.workspace }}/docker-compose.log
# ------------------------------------------------------------
onyx-lite-tests:
needs: [build-devcontainer-image]
permissions:
id-token: write # Required for OIDC-based AWS credential exchange
contents: read
runs-on:
[
runs-on,
runner=4cpu-linux-arm64,
"run-id=${{ github.run_id }}-onyx-lite-tests",
"extras=ecr-cache",
]
timeout-minutes: 10
steps:
- uses: runs-on/action@4e5f72399b6b17f2e79c511c1b38a315a64d22dc
- name: Checkout code
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # ratchet:actions/checkout@v6
with:
persist-credentials: false
- name: Setup test license
uses: ./.github/actions/setup-test-license
with:
aws-oidc-role-arn: ${{ secrets.AWS_OIDC_ROLE_ARN }}
- name: Log in to ECR pull-through cache
uses: ./.github/actions/login-ecr-pullthrough-cache
with:
ecr-registry: ${{ vars.ECR_REGISTRY }}
- name: Create .env file for Onyx Lite Docker Compose
run: |
cat <<EOF > deployment/docker_compose/.env
ENABLE_PAID_ENTERPRISE_EDITION_FEATURES=true
AUTH_TYPE=basic
POSTGRES_POOL_PRE_PING=true
POSTGRES_USE_NULL_POOL=true
REQUIRE_EMAIL_VERIFICATION=false
DISABLE_TELEMETRY=true
INTEGRATION_TESTS_MODE=true
EOF
# The api_server runs in-process inside pytest via FastAPI TestClient
# (see tests/integration/conftest.py); only Postgres is needed from
# compose for Onyx Lite.
- name: Start Docker containers (onyx-lite)
run: |
cd deployment/docker_compose
docker compose -f docker-compose.yml -f docker-compose.onyx-lite.yml -f docker-compose.dev.yml up -d \
--wait --wait-timeout 300 \
relational_db
id: start_docker_onyx_lite
- name: Install Python deps
uses: ./.github/actions/install-python-deps
with:
devcontainer-image: ${{ env.RUNS_ON_ECR_CACHE }}:integration-test-devcontainer-${{ github.run_id }}
- name: Run Onyx Lite Integration Tests
uses: nick-fields/retry@ad984534de44a9489a53aefd81eb77f87c70dc60 # ratchet:nick-fields/retry@v4.0.0
with:
timeout_minutes: 20
max_attempts: 3
retry_wait_seconds: 10
command: |
echo "Running onyx-lite integration tests..."
docker run --rm --network onyx_default \
--name test-runner \
-v ${{ github.workspace }}:/workspace \
-w /workspace/backend \
-e INTEGRATION_TESTS_MODE=true \
-e POSTGRES_HOST=relational_db \
-e POSTGRES_USER=postgres \
-e POSTGRES_PASSWORD=password \
-e POSTGRES_DB=postgres \
-e DB_READONLY_USER=db_readonly_user \
-e DB_READONLY_PASSWORD=password \
-e POSTGRES_POOL_PRE_PING=true \
-e POSTGRES_USE_NULL_POOL=true \
-e DISABLE_VECTOR_DB=true \
-e FILE_STORE_BACKEND=postgres \
-e CACHE_BACKEND=postgres \
-e AUTH_BACKEND=postgres \
-e OPENAI_API_KEY=${OPENAI_API_KEY} \
-e TEST_WEB_HOSTNAME=test-runner \
-e ONYX_DEV_LICENSE \
${{ env.RUNS_ON_ECR_CACHE }}:integration-test-devcontainer-${{ github.run_id }} \
uv run --no-sync pytest -v --tb=short -rs tests/integration/tests/no_vectordb
- name: Dump all-container logs (onyx-lite)
if: always()
run: |
cd deployment/docker_compose
docker compose -f docker-compose.yml -f docker-compose.onyx-lite.yml -f docker-compose.dev.yml \
logs --no-color > $GITHUB_WORKSPACE/docker-compose-onyx-lite.log || true
- name: Upload logs (onyx-lite)
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
with:
name: docker-all-logs-onyx-lite
path: ${{ github.workspace }}/docker-compose-onyx-lite.log
- name: Stop Docker containers (onyx-lite)
if: always()
run: |
cd deployment/docker_compose
docker compose -f docker-compose.yml -f docker-compose.onyx-lite.yml -f docker-compose.dev.yml down -v
multitenant-tests:
needs: [build-model-server-image, build-devcontainer-image]
runs-on:
[
runs-on,
runner=8cpu-linux-arm64,
"run-id=${{ github.run_id }}-multitenant-tests",
"extras=ecr-cache",
]
timeout-minutes: 15
steps:
- uses: runs-on/action@4e5f72399b6b17f2e79c511c1b38a315a64d22dc
- name: Checkout code
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # ratchet:actions/checkout@v6
with:
persist-credentials: false
- name: Log in to ECR pull-through cache
uses: ./.github/actions/login-ecr-pullthrough-cache
with:
ecr-registry: ${{ vars.ECR_REGISTRY }}
# NOTE: `indexing_model_server` is intentionally not started. See the
# integration-tests job above.
- name: Start Docker containers for multi-tenant tests
env:
ECR_CACHE: ${{ env.RUNS_ON_ECR_CACHE }}
RUN_ID: ${{ github.run_id }}
run: |
cd deployment/docker_compose
ENABLE_PAID_ENTERPRISE_EDITION_FEATURES=true \
LICENSE_ENFORCEMENT_ENABLED=false \
MULTI_TENANT=true \
AUTH_TYPE=cloud \
REQUIRE_EMAIL_VERIFICATION=false \
DISABLE_TELEMETRY=true \
OPENAI_DEFAULT_API_KEY=${OPENAI_API_KEY} \
ONYX_MODEL_SERVER_IMAGE=${ECR_CACHE}:integration-test-model-server-test-${RUN_ID} \
DEV_MODE=true \
docker compose -f docker-compose.multitenant-dev.yml up -d \
--wait --wait-timeout 300 \
relational_db \
opensearch \
cache \
minio \
inference_model_server
id: start_docker_multi_tenant
- name: Install Python deps
uses: ./.github/actions/install-python-deps
with:
devcontainer-image: ${{ env.RUNS_ON_ECR_CACHE }}:integration-test-devcontainer-${{ github.run_id }}
- name: Run Multi-Tenant Integration Tests
env:
ECR_CACHE: ${{ env.RUNS_ON_ECR_CACHE }}
RUN_ID: ${{ github.run_id }}
run: |
echo "Running multi-tenant integration tests..."
docker run --rm --network onyx_default \
--name test-runner \
-v ${{ github.workspace }}:/workspace \
-w /workspace/backend \
-e INTEGRATION_TESTS_MODE=true \
-e POSTGRES_HOST=relational_db \
-e POSTGRES_USER=postgres \
-e POSTGRES_PASSWORD=password \
-e DB_READONLY_USER=db_readonly_user \
-e DB_READONLY_PASSWORD=password \
-e POSTGRES_DB=postgres \
-e POSTGRES_USE_NULL_POOL=true \
-e OPENSEARCH_HOST=opensearch \
-e REDIS_HOST=cache \
-e MODEL_SERVER_HOST=inference_model_server \
-e INDEXING_MODEL_SERVER_HOST=inference_model_server \
-e S3_ENDPOINT_URL=http://minio:9000 \
-e S3_AWS_ACCESS_KEY_ID=minioadmin \
-e S3_AWS_SECRET_ACCESS_KEY=minioadmin \
-e S3_FILE_STORE_BUCKET_NAME=onyx-file-store-bucket \
-e OPENAI_API_KEY=${OPENAI_API_KEY} \
-e OPENAI_DEFAULT_API_KEY=${OPENAI_API_KEY} \
-e EXA_API_KEY=${EXA_API_KEY} \
-e SLACK_BOT_TOKEN=${SLACK_BOT_TOKEN} \
-e SLACK_BOT_TOKEN_TEST_SPACE=${SLACK_BOT_TOKEN_TEST_SPACE} \
-e TEST_WEB_HOSTNAME=test-runner \
-e AUTH_TYPE=cloud \
-e MULTI_TENANT=true \
-e SKIP_RESET=true \
-e REQUIRE_EMAIL_VERIFICATION=false \
-e DISABLE_TELEMETRY=true \
-e DEV_MODE=true \
-e ENABLE_PAID_ENTERPRISE_EDITION_FEATURES=true \
-e LICENSE_ENFORCEMENT_ENABLED=false \
${ECR_CACHE}:integration-test-devcontainer-${RUN_ID} \
uv run --no-sync pytest -v --tb=short -rs tests/integration/multitenant_tests
- name: Dump all-container logs (multi-tenant)
if: always()
run: |
cd deployment/docker_compose
docker compose -f docker-compose.multitenant-dev.yml logs --no-color > $GITHUB_WORKSPACE/docker-compose-multitenant.log || true
- name: Upload logs (multi-tenant)
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
with:
name: docker-all-logs-multitenant
path: ${{ github.workspace }}/docker-compose-multitenant.log
- name: Stop multi-tenant Docker containers
if: always()
run: |
cd deployment/docker_compose
docker compose -f docker-compose.multitenant-dev.yml down -v
required:
# NOTE: Github-hosted runners have about 20s faster queue times and are preferred here.
# This job is the single required status check for the integration suite. It
# always runs so branch protection has something to wait on, and it passes
# cleanly when `changes` reports no relevant paths changed (i.e. the test
# matrix was legitimately skipped).
runs-on: ubuntu-slim
timeout-minutes: 5
needs: [changes, integration-tests, onyx-lite-tests, multitenant-tests]
if: ${{ always() }}
steps:
- name: Check job status
env:
CHANGES_RESULT: ${{ needs.changes.result }}
RUN_TESTS: ${{ needs.changes.outputs.integration }}
INTEGRATION_RESULT: ${{ needs.integration-tests.result }}
LITE_RESULT: ${{ needs.onyx-lite-tests.result }}
MULTITENANT_RESULT: ${{ needs.multitenant-tests.result }}
run: |
# Fail closed if `changes` didn't succeed. Otherwise an empty
# RUN_TESTS (which is what we'd see when `changes` failed/cancelled)
# would be indistinguishable from "no relevant paths changed" and we
# would incorrectly pass the required check.
if [ "${CHANGES_RESULT}" != "success" ]; then
echo "changes job did not succeed (result: ${CHANGES_RESULT})"
exit 1
fi
if [ "${RUN_TESTS}" != "true" ]; then
echo "No relevant paths changed — required check passes."
exit 0
fi
if [ "${INTEGRATION_RESULT}" != "success" ] \
|| [ "${LITE_RESULT}" != "success" ] \
|| [ "${MULTITENANT_RESULT}" != "success" ]; then
echo "Test results: integration-tests=${INTEGRATION_RESULT}, onyx-lite-tests=${LITE_RESULT}, multitenant-tests=${MULTITENANT_RESULT}"
exit 1
fi
echo "All tests passed."