From 346c69386026c245df93a3ef284e4c525df69640 Mon Sep 17 00:00:00 2001 From: yuneng-jiang Date: Sat, 22 Aug 2026 22:45:30 -0700 Subject: [PATCH] ci: port the Postgres suites off CircleCI onto service containers (#37785) * ci: port the Postgres suites off CircleCI onto service containers proxy_behavior_tests, proxy_security_tests and schema_migration_check were near-identical CircleCI jobs: a Postgres sidecar, a schema seed, and one pytest tree each. They ran nowhere else, and CircleCI holds none of the branch ruleset's required checks, so the signal they produced gated nothing. test-postgres.yml runs the same three trees on a Postgres service container as one matrix, keeping each suite's own seeding rather than normalising it: the behavior and security trees keep `prisma db push`, and the migration tree keeps an empty database, which is what it needs to apply every committed migration itself. Their CircleCI definitions and workflow entries go with them, taking the config from 47 jobs to 44. assert_ci_coverage.py stays green: dropping the new workflow fails the census on exactly these trees, so the coverage moved rather than went missing. auth_ui_unit_tests is deliberately left behind. Ported, two of its tests fail because prepare_metadata_fields refuses enterprise-only keys without LITELLM_LICENSE, which exists as a CircleCI project variable and has no GitHub Actions secret. Creating that secret is a human action, so the job stays on CircleCI until it exists rather than shipping a red shard or quietly deselecting the two tests. * chore(ci): drop the narrative header from test-postgres.yml --- .circleci/config.yml | 126 ------------------------ .github/workflows/test-postgres.yml | 145 ++++++++++++++++++++++++++++ 2 files changed, 145 insertions(+), 126 deletions(-) create mode 100644 .github/workflows/test-postgres.yml diff --git a/.circleci/config.yml b/.circleci/config.yml index 5e77729df29..2586485e79c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -651,126 +651,6 @@ jobs: - auth_ui_unit_tests_coverage.xml - auth_ui_unit_tests_coverage - proxy_behavior_tests: - docker: - - *python312_image - - image: cimg/postgres:16.0@sha256:b125148bc76e8e8eee5eb3ad6020a3a14110a14e8192f1c645128afebe2e2f84 - environment: - POSTGRES_USER: postgres - POSTGRES_PASSWORD: postgres - POSTGRES_DB: litellm_test - working_directory: ~/project - environment: - DATABASE_URL: "postgresql://postgres:postgres@localhost:5432/litellm_test" - steps: - - checkout - - skip_if_unrelated_changes - - setup_google_dns - - install_uv - - install_rust - - run: - name: Install Dependencies - command: | - uv sync --frozen --all-groups --all-extras --python 3.12 - - wait_for_service: - url: tcp://localhost:5432 - timeout: "60" - - run: - name: Seed DB schema via prisma db push - command: | - uv run --no-sync prisma db push --schema litellm/proxy/schema.prisma --accept-data-loss - - run: - name: Generate Prisma Client - command: uv run --no-sync python -m prisma generate - - run: - name: Run proxy management behavior tests - command: | - mkdir -p test-results - uv run --no-sync python -m pytest tests/proxy_behavior \ - -v --junitxml=test-results/junit.xml --durations=10 - no_output_timeout: 15m - - store_test_results: - path: test-results - - proxy_security_tests: - docker: - - *python312_image - - image: cimg/postgres:16.0@sha256:b125148bc76e8e8eee5eb3ad6020a3a14110a14e8192f1c645128afebe2e2f84 - environment: - POSTGRES_USER: postgres - POSTGRES_PASSWORD: postgres - POSTGRES_DB: litellm_test - working_directory: ~/project - environment: - DATABASE_URL: "postgresql://postgres:postgres@localhost:5432/litellm_test" - steps: - - checkout - - skip_if_unrelated_changes - - setup_google_dns - - install_uv - - install_rust - - run: - name: Install Dependencies - command: | - uv sync --frozen --all-groups --all-extras --python 3.12 - - wait_for_service: - url: tcp://localhost:5432 - timeout: "60" - - run: - name: Seed DB schema via prisma db push - command: | - uv run --no-sync prisma db push --schema litellm/proxy/schema.prisma --accept-data-loss - - run: - name: Generate Prisma Client - command: uv run --no-sync python -m prisma generate - - run: - name: Run proxy security tests - command: | - mkdir -p test-results - uv run --no-sync python -m pytest tests/proxy_security_tests \ - -v --junitxml=test-results/junit.xml --durations=10 - no_output_timeout: 15m - - store_test_results: - path: test-results - - schema_migration_check: - docker: - - *python312_image - - image: cimg/postgres:16.0@sha256:b125148bc76e8e8eee5eb3ad6020a3a14110a14e8192f1c645128afebe2e2f84 - environment: - POSTGRES_USER: postgres - POSTGRES_PASSWORD: postgres - POSTGRES_DB: litellm_test - working_directory: ~/project - environment: - # An empty database; the test applies every committed migration itself. - DATABASE_URL: "postgresql://postgres:postgres@localhost:5432/litellm_test" - steps: - - checkout - - skip_if_unrelated_changes - - setup_google_dns - - install_uv - - install_rust - - run: - name: Install Dependencies - command: | - uv sync --frozen --all-groups --all-extras --python 3.12 - - wait_for_service: - url: tcp://localhost:5432 - timeout: "60" - - run: - name: Generate Prisma Client - command: uv run --no-sync python -m prisma generate - - run: - name: Check schema.prisma is in sync with committed migrations - command: | - mkdir -p test-results - uv run --no-sync python -m pytest tests/proxy_migration_tests \ - -v --junitxml=test-results/junit.xml --durations=10 - no_output_timeout: 15m - - store_test_results: - path: test-results - litellm_router_testing: # Runs all tests with the "router" keyword docker: - *python312_image @@ -3105,12 +2985,6 @@ workflows: filters: *main_branches - auth_ui_unit_tests: filters: *main_branches - - proxy_behavior_tests: - filters: *main_branches - - proxy_security_tests: - filters: *main_branches - - schema_migration_check: - filters: *main_branches - build_docker_database_image: filters: *main_branches - e2e_ui_testing: diff --git a/.github/workflows/test-postgres.yml b/.github/workflows/test-postgres.yml new file mode 100644 index 00000000000..96c514dff7c --- /dev/null +++ b/.github/workflows/test-postgres.yml @@ -0,0 +1,145 @@ +name: "Postgres Tests" + +on: + pull_request: + branches: + - main + - litellm_internal_staging + - litellm_oss_staging + - "litellm_**" + push: + branches: + - main + - litellm_internal_staging + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +jobs: + postgres: + name: ${{ matrix.shard }} + runs-on: ubuntu-latest + timeout-minutes: ${{ matrix.job-timeout-minutes }} + permissions: + contents: read + + services: + postgres: + image: postgres:16@sha256:e17e86066e5ef83e0952a9347f5c792b7ece00972e2aa787a6986f471b3dd3d5 + env: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DB: litellm_test + ports: + - 5432:5432 + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 10 + + strategy: + fail-fast: false + matrix: + include: + - shard: proxy-behavior + test-path: "tests/proxy_behavior" + seed: db-push + workers: 0 + timeout-minutes: 25 + job-timeout-minutes: 50 + + - shard: proxy-security + test-path: "tests/proxy_security_tests" + seed: db-push + workers: 0 + timeout-minutes: 15 + job-timeout-minutes: 40 + + - shard: schema-migration + test-path: "tests/proxy_migration_tests" + seed: none + workers: 0 + timeout-minutes: 20 + job-timeout-minutes: 45 + + env: + DATABASE_URL: "postgresql://postgres:postgres@localhost:5432/litellm_test" + + steps: + - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + timeout-minutes: 3 + with: + persist-credentials: false + + - name: Detect relevant changes + id: changes + timeout-minutes: 2 + uses: ./.github/actions/detect-changes + + - name: Set up Python + if: steps.changes.outputs.decision != 'skip' + timeout-minutes: 3 + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 + with: + python-version: "3.12" + + - name: Set up uv + if: steps.changes.outputs.decision != 'skip' + timeout-minutes: 3 + uses: ./.github/actions/setup-uv-with-retries + with: + version: "0.10.9" + + - name: Cache uv dependencies + if: steps.changes.outputs.decision != 'skip' + timeout-minutes: 5 + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + with: + path: | + ~/.cache/uv + .venv + key: ${{ runner.os }}-uv-postgres-${{ hashFiles('uv.lock') }} + restore-keys: | + ${{ runner.os }}-uv-postgres- + + - name: Install dependencies + if: steps.changes.outputs.decision != 'skip' + timeout-minutes: 12 + run: | + .github/scripts/uv_sync_with_retries.sh --frozen --all-groups --all-extras + + - name: Cache Prisma binaries + if: steps.changes.outputs.decision != 'skip' + timeout-minutes: 3 + uses: ./.github/actions/cache-prisma-binaries + + - name: Generate Prisma client + if: steps.changes.outputs.decision != 'skip' + timeout-minutes: 5 + run: | + uv run --no-sync prisma generate --schema litellm/proxy/schema.prisma + + - name: Seed database schema + if: steps.changes.outputs.decision != 'skip' && matrix.seed != 'none' + timeout-minutes: 10 + run: | + uv run --no-sync prisma db push --schema litellm/proxy/schema.prisma --accept-data-loss + + - name: Run tests + if: steps.changes.outputs.decision != 'skip' + timeout-minutes: ${{ matrix.timeout-minutes }} + env: + TEST_PATH: ${{ matrix.test-path }} + WORKERS: ${{ matrix.workers }} + run: | + if [ "${WORKERS}" = "0" ]; then + uv run --no-sync pytest ${TEST_PATH:?} -vv --tb=short --durations=10 + else + uv run --no-sync pytest ${TEST_PATH:?} -vv --tb=short --durations=10 -n "${WORKERS}" + fi