name: Run Pytest

on:
  workflow_call:
    secrets:
      CODECOV_TOKEN:
        required: false

permissions:
  contents: read

concurrency:
  group: api-tests-${{ github.head_ref || github.run_id }}
  cancel-in-progress: true

jobs:
  api-unit:
    name: API Unit Tests
    runs-on: depot-ubuntu-24.04
    env:
      COVERAGE_FILE: coverage-unit
    defaults:
      run:
        shell: bash
    strategy:
      matrix:
        python-version:
          - "3.12"

    steps:
      - name: Checkout code
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          fetch-depth: 0
          persist-credentials: false

      - name: Setup UV and Python
        uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
        with:
          enable-cache: true
          python-version: ${{ matrix.python-version }}
          cache-dependency-glob: api/uv.lock

      - name: Check UV lockfile
        run: uv lock --project api --check

      - name: Install dependencies
        run: uv sync --project api --dev

      - name: Run dify config tests
        run: uv run --project api pytest api/tests/unit_tests/configs/test_env_consistency.py

      - name: Run Unit Tests
        run: |
          uv run --project api pytest \
            -p no:benchmark \
            --timeout "${PYTEST_TIMEOUT:-20}" \
            -n auto \
            api/tests/unit_tests \
            api/providers/vdb/*/tests/unit_tests \
            api/providers/trace/*/tests/unit_tests \
            --ignore=api/tests/unit_tests/controllers
          # Controller tests register Flask routes at import time, so keep them out of xdist.
          uv run --project api pytest \
            --timeout "${PYTEST_TIMEOUT:-20}" \
            --cov-append \
            api/tests/unit_tests/controllers

      - name: Upload unit coverage data
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: api-coverage-unit
          path: coverage-unit
          retention-days: 1

  api-integration:
    name: API Integration Tests
    runs-on: depot-ubuntu-24.04
    env:
      COVERAGE_FILE: coverage-integration
      STORAGE_TYPE: opendal
      OPENDAL_SCHEME: fs
      OPENDAL_FS_ROOT: /tmp/dify-storage
    defaults:
      run:
        shell: bash
    strategy:
      matrix:
        python-version:
          - "3.12"

    steps:
      - name: Checkout code
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          fetch-depth: 0
          persist-credentials: false

      - name: Setup UV and Python
        uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
        with:
          enable-cache: true
          python-version: ${{ matrix.python-version }}
          cache-dependency-glob: api/uv.lock

      - name: Check UV lockfile
        run: uv lock --project api --check

      - name: Install dependencies
        run: uv sync --project api --dev

      - name: Run Integration Tests
        run: |
          uv run --project api pytest \
            -p no:benchmark \
            --start-middleware \
            -n auto \
            --timeout "${PYTEST_TIMEOUT:-180}" \
            api/tests/integration_tests/workflow \
            api/tests/integration_tests/tools \
            api/tests/test_containers_integration_tests

      - name: Upload integration coverage data
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: api-coverage-integration
          path: coverage-integration
          retention-days: 1

  api-coverage:
    name: API Coverage
    runs-on: depot-ubuntu-24.04
    needs:
      - api-unit
      - api-integration
    env:
      CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
      COVERAGE_FILE: .coverage
    defaults:
      run:
        shell: bash

    steps:
      - name: Checkout code
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          fetch-depth: 0
          persist-credentials: false

      - name: Setup UV and Python
        uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
        with:
          enable-cache: true
          python-version: "3.12"
          cache-dependency-glob: api/uv.lock

      - name: Install dependencies
        run: uv sync --project api --dev

      - name: Download coverage data
        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        with:
          path: coverage-data
          pattern: api-coverage-*
          merge-multiple: true

      - name: Combine coverage
        run: |
          set -euo pipefail

          echo "### API Coverage" >> "$GITHUB_STEP_SUMMARY"
          echo "" >> "$GITHUB_STEP_SUMMARY"
          echo "Merged backend coverage report generated for Codecov project status." >> "$GITHUB_STEP_SUMMARY"
          echo "" >> "$GITHUB_STEP_SUMMARY"

          unit_coverage="$(find coverage-data -type f -name coverage-unit -print -quit)"
          integration_coverage="$(find coverage-data -type f -name coverage-integration -print -quit)"
          : "${unit_coverage:?coverage-unit artifact not found}"
          : "${integration_coverage:?coverage-integration artifact not found}"

          report_file="$(mktemp)"
          uv run --project api coverage combine "$unit_coverage" "$integration_coverage"
          uv run --project api coverage report --show-missing | tee "$report_file"
          echo "Summary: \`$(tail -n 1 "$report_file")\`" >> "$GITHUB_STEP_SUMMARY"
          {
            echo ""
            echo "<details><summary>Coverage report</summary>"
            echo ""
            echo '```'
            cat "$report_file"
            echo '```'
            echo "</details>"
          } >> "$GITHUB_STEP_SUMMARY"
          uv run --project api coverage xml -o coverage.xml

      - name: Report coverage
        if: ${{ env.CODECOV_TOKEN != '' }}
        uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1
        with:
          files: ./coverage.xml
          disable_search: true
          flags: api
        env:
          CODECOV_TOKEN: ${{ env.CODECOV_TOKEN }}
