name: Unit tests env: # enable colored output # https://github.com/pytest-dev/pytest/issues/7443 PY_COLORS: 1 on: pull_request: paths: - .github/workflows/python-tests.yaml - "src/prefect/**/*.py" - "tests/**/*.py" - pyproject.toml - Dockerfile - scripts/entrypoint.sh push: branches: - main paths: - .github/workflows/python-tests.yaml - "src/prefect/**/*.py" - "tests/**/*.py" - pyproject.toml - Dockerfile - scripts/entrypoint.sh permissions: contents: read actions: write # Limit concurrency by workflow/branch combination. # # For pull request builds, pushing additional changes to the # branch will cancel prior in-progress and pending builds. # # For builds triggered on a branch push, additional changes # will wait for prior builds to complete before starting. # # https://docs.github.com/en/actions/using-jobs/using-concurrency concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} cancel-in-progress: ${{ github.event_name == 'pull_request' }} jobs: run-tests: runs-on: ubuntu-latest name: ${{ matrix.test-type.name }} - python:${{ matrix.python-version }}, ${{ matrix.database }} strategy: fail-fast: false matrix: test-type: - name: Server Tests modules: tests/server/ tests/events/server --ignore=tests/server/database/ --ignore=tests/server/orchestration/ - name: Database and Orchestration Tests modules: tests/server/database/ tests/server/orchestration/ - name: Client Tests modules: >- tests/ --ignore=tests/typesafety --ignore=tests/server/ --ignore=tests/events/server --ignore=tests/test_task_runners.py --ignore=tests/runner --ignore=tests/workers --ignore=tests/cli/ --ignore=tests/test_settings.py --ignore=tests/input/ - name: Runner, Worker, Settings, Input, and CLI Tests modules: tests/test_task_runners.py tests/runner tests/workers tests/cli/ tests/test_settings.py tests/input/ database: - "postgres:14" - "sqlite" python-version: - "3.9" - "3.10" - "3.11" - "3.12" - "3.13" exclude: - database: "sqlite" test-type: name: Client Tests modules: >- tests/ --ignore=tests/typesafety --ignore=tests/server/ --ignore=tests/events/server --ignore=tests/test_task_runners.py --ignore=tests/runner --ignore=tests/workers --ignore=tests/cli/ --ignore=tests/test_settings.py --ignore=tests/input/ - database: "sqlite" test-type: name: Runner, Worker, Settings, Input, and CLI Tests modules: tests/test_task_runners.py tests/runner tests/workers tests/cli/ tests/test_settings.py tests/input/ timeout-minutes: 15 steps: - name: Display current test matrix run: echo '${{ toJSON(matrix) }}' - uses: actions/checkout@v4 with: persist-credentials: false fetch-depth: 0 - name: Set up uv and Python ${{ matrix.python-version }} uses: astral-sh/setup-uv@v6 with: enable-cache: true python-version: ${{ matrix.python-version }} cache-dependency-glob: "pyproject.toml" - name: Install packages run: uv sync --locked - name: Start database container if: ${{ startsWith(matrix.database, 'postgres') }} run: > docker run --name "postgres" --detach --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 --publish 5432:5432 --tmpfs /var/lib/postgresql/data --env POSTGRES_USER="prefect" --env POSTGRES_PASSWORD="prefect" --env POSTGRES_DB="prefect" --env LANG="C.UTF-8" --env LANGUAGE="C.UTF-8" --env LC_ALL="C.UTF-8" --env LC_COLLATE="C.UTF-8" --env LC_CTYPE="C.UTF-8" ${{ matrix.database }} -c max_connections=250 ./scripts/wait-for-healthy-container.sh postgres 30 echo "PREFECT_SERVER_DATABASE_CONNECTION_URL=postgresql+asyncpg://prefect:prefect@localhost/prefect" >> $GITHUB_ENV - name: Start redis run: > docker run --name "redis" --detach --publish 6379:6379 redis:latest - name: Run tests run: | uv run pytest ${{ matrix.test-type.modules }} \ --numprocesses auto \ --maxprocesses 6 \ --dist worksteal \ --disable-docker-image-builds \ --exclude-service kubernetes \ --exclude-service docker \ --durations 26 \ - name: Check database container # Only applicable for Postgres, but we want this to run even when tests fail if: always() run: | docker container inspect postgres \ && docker container logs postgres \ || echo "Ignoring bad exit code" run-docker-tests: runs-on: ubuntu-latest name: docker, python:${{ matrix.python-version }} strategy: matrix: database: - "postgres:14" python-version: - "3.9" - "3.10" - "3.11" - "3.12" fail-fast: false timeout-minutes: 45 steps: - name: Display current test matrix run: echo '${{ toJSON(matrix) }}' - uses: actions/checkout@v4 with: persist-credentials: false fetch-depth: 0 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 with: driver-opts: image=moby/buildkit:v0.12.5 - name: Set up uv and Python ${{ matrix.python-version }} uses: astral-sh/setup-uv@v6 with: enable-cache: true python-version: ${{ matrix.python-version }} cache-dependency-glob: "pyproject.toml" - name: Get image tag id: get_image_tag run: | SHORT_SHA=$(git rev-parse --short=7 HEAD) tmp="sha-$SHORT_SHA-python${{ matrix.python-version }}" echo "image_tag=${tmp}" >> $GITHUB_OUTPUT - name: Get node version id: get_node_version run: | echo "NODE_VERSION=$(cat .nvmrc)" >> $GITHUB_OUTPUT - name: Login to DockerHub uses: docker/login-action@v3 if: github.event.pull_request.head.repo.full_name == github.repository with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Build test image uses: docker/build-push-action@v6 env: DOCKER_BUILD_SUMMARY: false DOCKER_RECORD_UPLOAD: false with: context: . build-args: | PYTHON_VERSION=${{ matrix.python-version }} PREFECT_EXTRAS=[dev] NODE_VERSION=${{ steps.get_node_version.outputs.NODE_VERSION }} tags: prefecthq/prefect-dev:${{ steps.get_image_tag.outputs.image_tag }} outputs: type=docker,dest=/tmp/image.tar - name: Test Docker image run: | docker load --input /tmp/image.tar docker run --rm prefecthq/prefect-dev:${{ steps.get_image_tag.outputs.image_tag }} prefect version - name: Install packages run: uv sync --locked - name: Start database container if: ${{ startsWith(matrix.database, 'postgres') }} run: > docker run --name "postgres" --detach --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 --publish 5432:5432 --tmpfs /var/lib/postgresql/data --env POSTGRES_USER="prefect" --env POSTGRES_PASSWORD="prefect" --env POSTGRES_DB="prefect" --env LANG="C.UTF-8" --env LANGUAGE="C.UTF-8" --env LC_ALL="C.UTF-8" --env LC_COLLATE="C.UTF-8" --env LC_CTYPE="C.UTF-8" ${{ matrix.database }} -c max_connections=250 ./scripts/wait-for-healthy-container.sh postgres 30 echo "PREFECT_SERVER_DATABASE_CONNECTION_URL=postgresql+asyncpg://prefect:prefect@localhost/prefect" >> $GITHUB_ENV - name: Start docker registry run: > docker run --name "prefect-test-registry" --detach --publish 5555:5000 registry:2 - name: Start redis run: > docker run --name "redis" --detach --publish 6379:6379 redis:latest - name: Check git diff run: | git diff --exit-code - name: Run tests run: | uv run pytest tests --ignore=tests/typesafety \ --numprocesses auto \ --maxprocesses 6 \ --dist worksteal \ --disable-docker-image-builds \ --only-service docker \ --durations 26 - name: Check database container # Only applicable for Postgres, but we want this to run even when tests fail if: always() run: | docker container inspect postgres \ && docker container logs postgres \ || echo "Ignoring bad exit code" run-typesafety-test: name: typesafety runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: persist-credentials: false fetch-depth: 0 - name: Set up uv uses: astral-sh/setup-uv@v6 with: enable-cache: true python-version: "3.12" cache-dependency-glob: "pyproject.toml" - name: Install packages run: uv sync --locked - name: Run tests run: | uv run pytest tests/typesafety \ --disable-docker-image-builds