name: System Tests

on:
  push:
    branches:
      - main
      - 'mq-working-branch**'
  pull_request:
  workflow_dispatch: {}
  schedule:
    - cron: '00 04 * * 2-6'

jobs:
  build-wheels:
    uses: ./.github/workflows/build_python_3.yml
    with:
      cibw_build: 'cp311-manylinux_x86_64 cp312-manylinux_x86_64 cp313-manylinux_x86_64'

  system-tests-build-weblog:
    needs:
      - build-wheels
    runs-on: ubuntu-latest
    strategy:
      matrix:
        include:
          - weblog-variant: flask-poc
          - weblog-variant: uwsgi-poc
          - weblog-variant: django-poc
          - weblog-variant: fastapi
          # runs django-poc for 3.12
          - weblog-variant: python3.12
          - weblog-variant: django-py3.13
      fail-fast: false
    env:
      TEST_LIBRARY: python
      WEBLOG_VARIANT: ${{ matrix.weblog-variant }}
      CMAKE_BUILD_PARALLEL_LEVEL: 12
    steps:
      - name: Install Dependencies
        run: sudo apt-get install -y patchelf

      - name: Checkout system tests
        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false
          repository: 'DataDog/system-tests'
          # Automatically managed, use scripts/update-system-tests-version to update
          ref: '2eb26336ed43c2db1b7f90fff17ce4f5fc36a12b'

      - name: Download wheels to binaries directory
        uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
        with:
          pattern: wheels-*
          path: binaries/
          merge-multiple: true

      - name: Build
        run: ./build.sh -i weblog

      - name: Save
        id: save
        run: |
          docker image save system_tests/weblog:latest | gzip > ${{ matrix.weblog-variant}}_weblog_${{ github.sha }}.tar.gz

      - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
        with:
          name: ${{ matrix.weblog-variant }}_${{ github.sha }}
          path: |
            ${{ matrix.weblog-variant}}_weblog_${{ github.sha }}.tar.gz
          retention-days: 2

  system-tests:
    runs-on: ubuntu-latest
    needs: [system-tests-build-weblog]
    strategy:
      matrix:
        weblog-variant: [flask-poc, uwsgi-poc , django-poc, fastapi, python3.12, django-py3.13]
        scenario: [remote-config, appsec, appsec-1, other, debugger-1, debugger-2]

      fail-fast: false
    env:
      TEST_LIBRARY: python
      WEBLOG_VARIANT: ${{ matrix.weblog-variant }}
      CMAKE_BUILD_PARALLEL_LEVEL: 12
    steps:

      - name: Checkout system tests
        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false
          repository: 'DataDog/system-tests'
          # Automatically managed, use scripts/update-system-tests-version to update
          ref: '2eb26336ed43c2db1b7f90fff17ce4f5fc36a12b'

      - name: Build runner
        uses: ./.github/actions/install_runner

      - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
        with:
          name: ${{ matrix.weblog-variant }}_${{ github.sha }}
          path: images_artifacts/

      - name: docker load
        id: docker_load
        run: |
          docker load < images_artifacts/${{ matrix.weblog-variant}}_weblog_${{ github.sha }}.tar.gz

      - name: Run DEFAULT
        if: always() && steps.docker_load.outcome == 'success' && matrix.scenario == 'other'
        run: ./run.sh DEFAULT

      - name: Run SAMPLING
        if: always() && steps.docker_load.outcome == 'success' && matrix.scenario == 'other'
        run: ./run.sh SAMPLING

      - name: Run INTEGRATIONS
        if: always() && steps.docker_load.outcome == 'success' && matrix.scenario == 'other'
        run: ./run.sh INTEGRATIONS

      - name: Run CROSSED_TRACING_LIBRARIES
        if: always() && steps.docker_load.outcome == 'success' && matrix.scenario == 'other'
        run: ./run.sh CROSSED_TRACING_LIBRARIES

      - name: Run PROFILING
        if: always() && steps.docker_load.outcome == 'success' && matrix.scenario == 'other'
        run: |
          cat /proc/sys/kernel/perf_event_paranoid
          sudo sysctl kernel.perf_event_paranoid=1
          sudo sysctl -p
          ./run.sh PROFILING

      - name: Run AGENT_SUPPORTING_SPAN_EVENTS
        if: always() && steps.docker_load.outcome == 'success' && matrix.scenario == 'other'
        run: ./run.sh AGENT_SUPPORTING_SPAN_EVENTS

      - name: Run REMOTE_CONFIG_MOCKED_BACKEND_ASM_FEATURES
        if: always() && steps.docker_load.outcome == 'success' && matrix.scenario == 'remote-config'
        run: ./run.sh REMOTE_CONFIG_MOCKED_BACKEND_ASM_FEATURES

      - name: Run REMOTE_CONFIG_MOCKED_BACKEND_LIVE_DEBUGGING
        if: always() && steps.docker_load.outcome == 'success' && matrix.scenario == 'remote-config'
        run: ./run.sh REMOTE_CONFIG_MOCKED_BACKEND_LIVE_DEBUGGING

      - name: Run REMOTE_CONFIG_MOCKED_BACKEND_ASM_DD
        if: always() && steps.docker_load.outcome == 'success' && matrix.scenario == 'remote-config'
        run: ./run.sh REMOTE_CONFIG_MOCKED_BACKEND_ASM_DD

      - name: Run APPSEC_MISSING_RULES
        if: always() && steps.docker_load.outcome == 'success' && matrix.scenario == 'appsec'
        run: ./run.sh APPSEC_MISSING_RULES

      - name: Run APPSEC_AUTO_EVENTS_EXTENDED
        if: always() && steps.docker_load.outcome == 'success' && matrix.scenario == 'appsec'
        run: ./run.sh APPSEC_AUTO_EVENTS_EXTENDED

      - name: Run APPSEC_CUSTOM_RULES
        if: always() && steps.docker_load.outcome == 'success' && matrix.scenario == 'appsec'
        run: ./run.sh APPSEC_CUSTOM_RULES

      - name: Run APPSEC_CORRUPTED_RULES
        if: always() && steps.docker_load.outcome == 'success' && matrix.scenario == 'appsec'
        run: ./run.sh APPSEC_CORRUPTED_RULES

      - name: Run APPSEC_RULES_MONITORING_WITH_ERRORS
        if: always() && steps.docker_load.outcome == 'success' && matrix.scenario == 'appsec'
        run: ./run.sh APPSEC_RULES_MONITORING_WITH_ERRORS

      - name: Run APPSEC_LOW_WAF_TIMEOUT
        if: always() && steps.docker_load.outcome == 'success' && matrix.scenario == 'appsec'
        run: ./run.sh APPSEC_LOW_WAF_TIMEOUT

      - name: Run APPSEC_CUSTOM_OBFUSCATION
        if: always() && steps.docker_load.outcome == 'success' && matrix.scenario == 'appsec'
        run: ./run.sh APPSEC_CUSTOM_OBFUSCATION

      - name: Run APPSEC_RATE_LIMITER
        if: always() && steps.docker_load.outcome == 'success' && matrix.scenario == 'appsec'
        run: ./run.sh APPSEC_RATE_LIMITER

      - name: Run APPSEC_STANDALONE
        if: always() && steps.docker_load.outcome == 'success' && matrix.scenario == 'appsec-1'
        run: ./run.sh APPSEC_STANDALONE

      - name: Run APPSEC_STANDALONE_API_SECURITY
        if: always() && steps.docker_load.outcome == 'success' && matrix.scenario == 'appsec-1'
        run: ./run.sh APPSEC_STANDALONE_API_SECURITY

      - name: Run IAST_STANDALONE
        if: always() && steps.docker_load.outcome == 'success' && matrix.scenario == 'appsec-1'
        run: ./run.sh IAST_STANDALONE

      - name: Run SCA_STANDALONE
        if: always() && steps.docker_load.outcome == 'success' && matrix.scenario == 'appsec-1'
        run: ./run.sh SCA_STANDALONE

      - name: Run APPSEC_RUNTIME_ACTIVATION
        if: always() && steps.docker_load.outcome == 'success' && matrix.scenario == 'appsec-1'
        run: ./run.sh APPSEC_RUNTIME_ACTIVATION

      - name: Run APPSEC_WAF_TELEMETRY
        if: always() && steps.docker_load.outcome == 'success' && matrix.scenario == 'appsec-1'
        run: ./run.sh APPSEC_WAF_TELEMETRY

      - name: Run APPSEC_DISABLED
        if: always() && steps.docker_load.outcome == 'success' && matrix.scenario == 'appsec-1'
        run: ./run.sh APPSEC_DISABLED

      - name: Run APPSEC_BLOCKING
        if: always() && steps.docker_load.outcome == 'success' && matrix.scenario == 'appsec-1'
        run: ./run.sh APPSEC_BLOCKING

      - name: Run APPSEC_BLOCKING_FULL_DENYLIST
        if: always() && steps.docker_load.outcome == 'success' && matrix.scenario == 'appsec-1'
        run: ./run.sh APPSEC_BLOCKING_FULL_DENYLIST

      - name: Run APPSEC_RASP
        if: always() && steps.docker_load.outcome == 'success' && matrix.scenario == 'appsec-1'
        run: ./run.sh APPSEC_RASP

      - name: Run APPSEC_STANDALONE_RASP
        if: always() && steps.docker_load.outcome == 'success' && matrix.scenario == 'appsec-1'
        run: ./run.sh APPSEC_STANDALONE_RASP

      - name: Run DEBUGGER_PROBES_STATUS
        if: always() && steps.docker_load.outcome == 'success' && matrix.scenario == 'debugger-1'
        run: ./run.sh DEBUGGER_PROBES_STATUS

      - name: Run DEBUGGER_PROBES_SNAPSHOT
        if: always() && steps.docker_load.outcome == 'success' && matrix.scenario == 'debugger-1'
        run: ./run.sh DEBUGGER_PROBES_SNAPSHOT

      - name: Run DEBUGGER_PII_REDACTION
        if: always() && steps.docker_load.outcome == 'success' && matrix.scenario == 'debugger-1'
        run: ./run.sh DEBUGGER_PII_REDACTION

      - name: Run DEBUGGER_EXPRESSION_LANGUAGE
        if: always() && steps.docker_load.outcome == 'success' && matrix.scenario == 'debugger-1'
        run: ./run.sh DEBUGGER_EXPRESSION_LANGUAGE

      - name: Run DEBUGGER_EXCEPTION_REPLAY
        if: always() && steps.docker_load.outcome == 'success' && matrix.scenario == 'debugger-2'
        run: ./run.sh DEBUGGER_EXCEPTION_REPLAY

      - name: Run DEBUGGER_SYMDB
        if: always() && steps.docker_load.outcome == 'success' && matrix.scenario == 'debugger-2'
        run: ./run.sh DEBUGGER_SYMDB

      # The compress step speed up a lot the upload artifact process
      - name: Compress artifact
        if: always() && steps.docker_load.outcome == 'success'
        id: compress-artifact
        run: tar -czvf artifact.tar.gz $(ls | grep logs)

      - name: Upload artifact
        uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
        if: always() && steps.docker_load.outcome == 'success'
        with:
          name: logs_${{ matrix.weblog-variant }}_${{ matrix.scenario }}
          path: artifact.tar.gz


  parametric:
    needs:
      - build-wheels
    runs-on: ubuntu-latest
    env:
      TEST_LIBRARY: python
    steps:
      - name: Install Dependencies
        run: sudo apt-get install -y patchelf

      - name: Checkout system tests
        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false
          repository: 'DataDog/system-tests'
          # Automatically managed, use scripts/update-system-tests-version to update
          ref: '2eb26336ed43c2db1b7f90fff17ce4f5fc36a12b'
      - name: Download wheels to binaries directory
        uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
        with:
          pattern: wheels-*
          path: binaries/
          merge-multiple: true

      - name: Build runner
        id: build_runner
        uses: ./.github/actions/install_runner

      - name: Run
        if: always() && steps.build_runner.outcome == 'success'
        run: ./run.sh PARAMETRIC

      - name: Compress artifact
        if: always() && steps.build_runner.outcome == 'success'
        run: tar -czvf artifact.tar.gz $(ls | grep logs)

      - name: Upload artifact
        uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
        if: always() && steps.build_runner.outcome == 'success'
        with:
          name: logs_parametric
          path: artifact.tar.gz

  finished:
    name: system-tests finished
    runs-on: ubuntu-latest
    needs: [parametric, system-tests]
    if: success() || failure()
    steps:
      - name: True when everything else succeeded
        if: needs.parametric.result == 'success' && needs.system-tests.result == 'success'
        run: exit 0
      - name: Fails if anything else failed
        if: needs.parametric.result != 'success' || needs.system-tests.result != 'success'
        run: exit 1
