# GitHub Actions CI/CD Pipeline for Test Execution
# Generated by BMad TEA Agent - Test Architect Module
# Optimized for: Parallel Sharding, Burn-In Loop
# Stack: {test_stack_type} | Framework: {test_framework}
#
# Variables to customize per project:
#   INSTALL_CMD       - dependency install command (e.g., npm ci, pnpm install --frozen-lockfile, yarn --frozen-lockfile)
#   TEST_CMD          - main test command (e.g., npm run test:e2e, npm test, npx vitest)
#   LINT_CMD          - lint command (e.g., npm run lint)
#   BROWSER_INSTALL   - browser install command (frontend/fullstack only; omit for backend)
#   BROWSER_CACHE_PATH - browser cache path (frontend/fullstack only; omit for backend)

name: Test Pipeline

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main, develop]
  schedule:
    # Weekly burn-in on Sundays at 2 AM UTC
    - cron: "0 2 * * 0"

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

jobs:
  # Lint stage - Code quality checks
  lint:
    name: Lint
    runs-on: ubuntu-latest
    timeout-minutes: 5

    steps:
      - uses: actions/checkout@v4

      - name: Determine Node version
        id: node-version
        run: |
          if [ -f .nvmrc ]; then
            echo "value=$(cat .nvmrc)" >> "$GITHUB_OUTPUT"
            echo "Using Node from .nvmrc"
          else
            echo "value=24" >> "$GITHUB_OUTPUT"
            echo "Using default Node 24 (current LTS)"
          fi

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ steps.node-version.outputs.value }}
          cache: "npm"

      - name: Install dependencies
        run: npm ci # Replace with INSTALL_CMD

      - name: Run linter
        run: npm run lint # Replace with LINT_CMD

  # Test stage - Parallel execution with sharding
  test:
    name: Test (Shard ${{ matrix.shard }})
    runs-on: ubuntu-latest
    timeout-minutes: 30
    needs: lint

    strategy:
      fail-fast: false
      matrix:
        shard: [1, 2, 3, 4]

    steps:
      - uses: actions/checkout@v4

      - name: Determine Node version
        id: node-version
        run: |
          if [ -f .nvmrc ]; then
            echo "value=$(cat .nvmrc)" >> "$GITHUB_OUTPUT"
            echo "Using Node from .nvmrc"
          else
            echo "value=22" >> "$GITHUB_OUTPUT"
            echo "Using default Node 22 (current LTS)"
          fi

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ steps.node-version.outputs.value }}
          cache: "npm"

      - name: Cache Playwright browsers
        uses: actions/cache@v4
        with:
          path: ~/.cache/ms-playwright
          key: ${{ runner.os }}-playwright-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-playwright-

      - name: Install dependencies
        run: npm ci # Replace with INSTALL_CMD

      # Frontend/Fullstack only — remove this step for backend-only stacks
      - name: Install Playwright browsers
        run: npx playwright install --with-deps chromium # Replace with BROWSER_INSTALL

      - name: Run tests (shard ${{ matrix.shard }}/4)
        run: npm run test:e2e -- --shard=${{ matrix.shard }}/4 # Replace with TEST_CMD + shard args

      - name: Upload test results
        if: failure()
        uses: actions/upload-artifact@v4
        with:
          name: test-results-${{ matrix.shard }}
          path: |
            test-results/
            playwright-report/
          retention-days: 30

  # Burn-in stage - Flaky test detection
  burn-in:
    name: Burn-In (Flaky Detection)
    runs-on: ubuntu-latest
    timeout-minutes: 60
    needs: test
    # Only run burn-in on PRs to main/develop or on schedule
    if: github.event_name == 'pull_request' || github.event_name == 'schedule'

    steps:
      - uses: actions/checkout@v4

      - name: Determine Node version
        id: node-version
        run: |
          if [ -f .nvmrc ]; then
            echo "value=$(cat .nvmrc)" >> "$GITHUB_OUTPUT"
            echo "Using Node from .nvmrc"
          else
            echo "value=22" >> "$GITHUB_OUTPUT"
            echo "Using default Node 22 (current LTS)"
          fi

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ steps.node-version.outputs.value }}
          cache: "npm"

      # Frontend/Fullstack only — remove this step for backend-only stacks
      - name: Cache Playwright browsers
        uses: actions/cache@v4
        with:
          path: ~/.cache/ms-playwright # Replace with BROWSER_CACHE_PATH
          key: ${{ runner.os }}-playwright-${{ hashFiles('**/package-lock.json') }}

      - name: Install dependencies
        run: npm ci # Replace with INSTALL_CMD

      # Frontend/Fullstack only — remove this step for backend-only stacks
      - name: Install Playwright browsers
        run: npx playwright install --with-deps chromium # Replace with BROWSER_INSTALL

      # Note: Burn-in targets UI flakiness. For backend-only stacks, remove this job entirely.
      - name: Run burn-in loop (10 iterations)
        run: |
          echo "🔥 Starting burn-in loop - detecting flaky tests"
          for i in {1..10}; do
            echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
            echo "🔥 Burn-in iteration $i/10"
            echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
            npm run test:e2e || exit 1  # Replace with TEST_CMD
          done
          echo "✅ Burn-in complete - no flaky tests detected"

      - name: Upload burn-in failure artifacts
        if: failure()
        uses: actions/upload-artifact@v4
        with:
          name: burn-in-failures
          path: |
            test-results/
            playwright-report/
          retention-days: 30

  # Report stage - Aggregate and publish results
  report:
    name: Test Report
    runs-on: ubuntu-latest
    needs: [test, burn-in]
    if: always()

    steps:
      - name: Download all artifacts
        uses: actions/download-artifact@v4
        with:
          path: artifacts

      - name: Generate summary
        run: |
          echo "## Test Execution Summary" >> $GITHUB_STEP_SUMMARY
          echo "" >> $GITHUB_STEP_SUMMARY
          echo "- **Status**: ${{ needs.test.result }}" >> $GITHUB_STEP_SUMMARY
          echo "- **Burn-in**: ${{ needs.burn-in.result }}" >> $GITHUB_STEP_SUMMARY
          echo "- **Shards**: 4" >> $GITHUB_STEP_SUMMARY
          echo "" >> $GITHUB_STEP_SUMMARY

          if [ "${{ needs.burn-in.result }}" == "failure" ]; then
            echo "⚠️ **Flaky tests detected** - Review burn-in artifacts" >> $GITHUB_STEP_SUMMARY
          fi

# ============================================================================
# EXTENSION PATTERNS — Script Injection Prevention
# ============================================================================
# When extending this template into reusable workflows, manual dispatch
# workflows, or composite actions, NEVER use ${{ inputs.* }} directly in
# run: blocks. Always pass through env: intermediaries.
#
# KEY PRINCIPLE: Inputs must be DATA, not COMMANDS.
# Pass inputs through env: and interpolate as quoted arguments into fixed
# commands. NEVER accept command-shaped inputs (e.g., install-command,
# test-command) that get executed as shell code — even through env:.
#
# --- Reusable Workflow (workflow_call) ---
#
# on:
#   workflow_call:
#     inputs:
#       test-grep:
#         description: 'Test grep filter (data only — not a command)'
#         type: string
#         required: false
#         default: ''
#       base-ref:
#         description: 'Base branch for diff'
#         type: string
#         required: false
#         default: 'main'
#       burn-in-count:
#         description: 'Number of burn-in iterations'
#         type: string
#         required: false
#         default: '10'
#
# jobs:
#   test:
#     runs-on: ubuntu-latest
#     steps:
#       - uses: actions/checkout@v4
#       # Fixed command — not derived from inputs
#       - name: Install dependencies
#         run: npm ci
#       # ✅ SAFE — input is DATA passed as an argument to a fixed command
#       - name: Run tests
#         env:
#           TEST_GREP: ${{ inputs.test-grep }}
#         run: |
#           # Security: inputs passed through env: to prevent script injection
#           if [ -n "$TEST_GREP" ]; then
#             npx playwright test --grep "$TEST_GREP"
#           else
#             npx playwright test
#           fi
#
# --- Manual Dispatch (workflow_dispatch) ---
#
# on:
#   workflow_dispatch:
#     inputs:
#       test-grep:
#         description: 'Test grep filter (data only — not a command)'
#         type: string
#         required: false
#       environment:
#         description: 'Target environment'
#         type: choice
#         options: [staging, production]
#
# jobs:
#   run-tests:
#     runs-on: ubuntu-latest
#     steps:
#       - uses: actions/checkout@v4
#       # ✅ SAFE — input is DATA interpolated into a fixed command
#       - name: Run selected tests
#         env:
#           TEST_GREP: ${{ inputs.test-grep }}
#         run: |
#           # Security: inputs passed through env: to prevent script injection
#           npx playwright test --grep "$TEST_GREP"
#
# --- Composite Action (action.yml) ---
#
# inputs:
#   test-grep:
#     description: 'Test grep filter (data only — not a command)'
#     required: false
#     default: ''
#   burn-in-count:
#     description: 'Number of burn-in iterations'
#     required: false
#     default: '10'
#
# runs:
#   using: composite
#   steps:
#     # ✅ SAFE — inputs are DATA arguments to fixed commands
#     - name: Run burn-in
#       shell: bash
#       env:
#         TEST_GREP: ${{ inputs.test-grep }}
#         BURN_IN_COUNT: ${{ inputs.burn-in-count }}
#       run: |
#         # Security: inputs passed through env: to prevent script injection
#         for i in $(seq 1 "$BURN_IN_COUNT"); do
#           echo "Burn-in iteration $i/$BURN_IN_COUNT"
#           npx playwright test --grep "$TEST_GREP" || exit 1
#         done
#
# ❌ NEVER DO THIS:
#   # Direct ${{ inputs.* }} in run: — GitHub expression injection
#   - run: npx playwright test --grep "${{ inputs.test-grep }}"
#
#   # Executing input-derived env var as a command — still command injection
#   - env:
#       CMD: ${{ inputs.test-command }}
#     run: $CMD
# ============================================================================
