  test-{{ lowercase_group }}:
    name: {{ group }}
    timeout-minutes: 30
    runs-on: {% raw %}${{ matrix.os }}{% endraw %}
    strategy:
      fail-fast: false
      matrix:
        python-version: [{{ py_versions|join(",") }}]
        os: [ubuntu-22.04]

    {% if needs_docker or needs_postgres or needs_redis or needs_mysql %}
    services:
      {% if needs_docker %}
      docker:
        image: docker:dind  # Required for Docker network management
        options: --privileged  # Required for Docker-in-Docker operations
      {% endif %}
      {% if needs_postgres %}
      postgres:
        image: ghcr.io/getsentry/image-mirror-library-postgres:17-alpine
        env:
          POSTGRES_PASSWORD: sentry
        # Set health checks to wait until postgres has started
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
        # Maps tcp port 5432 on service container to the host
        ports:
          - 5432:5432
      {% endif %}
      {% if needs_redis %}
      redis:
        image: ghcr.io/getsentry/image-mirror-library-redis:7.0.8-bullseye
        options: >-
          --health-cmd "redis-cli ping"
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
        ports:
          - 6379:6379
      {% endif %}
      {% if needs_mysql %}
      mysql:
        image: mysql
        env:
          MYSQL_ROOT_PASSWORD: sentry
        options: >-
          --health-cmd "mysqladmin ping -h localhost"
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
        ports:
          - 3306:3306
      {% endif %}
    {% endif %}
    env:
      # 3.6/3.7 run in the python:X.Y container; tell uv to use that system Python.
      TOX_UV_PYTHON_PREFERENCE: {% raw %}${{ (matrix.python-version == '3.6' || matrix.python-version == '3.7') && 'only-system' || 'managed' }}{% endraw %}
      {% if needs_postgres %}
      SENTRY_PYTHON_TEST_POSTGRES_HOST: {% raw %}${{ (matrix.python-version == '3.6' || matrix.python-version == '3.7') && 'postgres' || 'localhost' }}{% endraw %}
      SENTRY_PYTHON_TEST_POSTGRES_USER: postgres
      SENTRY_PYTHON_TEST_POSTGRES_PASSWORD: sentry
      {% endif %}
      {% if needs_redis %}
      SENTRY_PYTHON_TEST_REDIS_HOST: {% raw %}${{ (matrix.python-version == '3.6' || matrix.python-version == '3.7') && 'redis' || 'localhost' }}{% endraw %}
      {% endif %}
      {% if needs_mysql %}
      SENTRY_PYTHON_TEST_MYSQL_USER: root
      SENTRY_PYTHON_TEST_MYSQL_PASSWORD: sentry
      {% endif %}
    container: {% raw %}${{ (matrix.python-version == '3.6' || matrix.python-version == '3.7') && format('python:{0}', matrix.python-version) || null }}{% endraw %}
    steps:
      - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
      - name: Install uv
        uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
        with:
          enable-cache: false

      {% if needs_clickhouse %}
      - name: "Setup ClickHouse Server"
        uses: getsentry/action-clickhouse-in-ci@5dc8a6a50d689bd6051db0241f34849e5a36490b # v1.7
      {% endif %}

      {% if needs_java %}
      - name: Install Java
        uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
        with:
          distribution: 'temurin'
          java-version: '21'
      {% endif %}

      - name: Mark workspace safe for git (3.6/3.7 container)
        # needed to make git rev-parse work in the containers
        # subprocesses (e.g. sentry_sdk.utils.get_git_revision) can run git.
        if: {% raw %}${{ matrix.python-version == '3.6' || matrix.python-version == '3.7' }}{% endraw %}
        run: git config --global --add safe.directory "$GITHUB_WORKSPACE"

      - name: Setup Test Env
        run: |
          uv sync

      {% for framework in frameworks %}
      - name: Test {{ framework }}
        run: |
          set -x # print commands that are executed
          ./scripts/runtox.sh "{% raw %}py${{ matrix.python-version }}{% endraw %}-{{ framework }}"
      {% endfor %}

      - name: Upload coverage data
        if: {% raw %}${{ !cancelled() }}{% endraw %}
        continue-on-error: true
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: coverage-{{ lowercase_group }}-{% raw %}${{ matrix.python-version }}{% endraw %}
          # .coverage-* / .junitxml-* are dotfiles, excluded by default
          include-hidden-files: true
          path: |
            .coverage-sentry-*
            .junitxml-*
          if-no-files-found: 'ignore'
          retention-days: 1
          overwrite: true
