# CI/CD para QA: Automação do Pipeline de Testes

**Meta Description:** Aprenda a configurar CI/CD para QA: GitHub Actions, GitLab CI, Jenkins. Automação de testes, quality gates e deploy contínuo.

---

## O Que é CI/CD?

### Continuous Integration (CI)

Desenvolvedores commitam código frequentemente (diariamente ou mais). Cada commit triggers build automático e suite de testes.

### Continuous Delivery (CD)

Código que passa em todos os testes é automaticamente deployado para staging/produção.

---

## GitHub Actions

### Estrutura Básica

```yaml
# .github/workflows/ci.yml
name: CI Pipeline

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

env:
  NODE_VERSION: '18'

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup Node
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'npm'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Run linter
        run: npm run lint
      
      - name: Run type check
        run: npm run type-check

  test:
    needs: lint
    runs-on: ubuntu-latest
    services:
      postgres:
        image: postgres:15
        env:
          POSTGRES_DB: test
          POSTGRES_USER: test
          POSTGRES_PASSWORD: test
        ports:
          - 5432:5432
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
      
      redis:
        image: redis:7
        ports:
          - 6379:6379
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup Node
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'npm'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Run unit tests
        run: npm run test:unit -- --coverage
      
      - name: Run integration tests
        env:
          DATABASE_URL: postgresql://test:test@localhost:5432/test
          REDIS_URL: redis://localhost:6379
        run: npm run test:integration
      
      - name: Upload coverage
        uses: codecov/codecov-action@v3
        with:
          files: ./coverage/lcov.info
          fail_ci_if_error: true

  e2e:
    needs: test
    runs-on: ubuntu-latest
    if: github.event_name == 'push'
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup Node
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
      
      - name: Install dependencies
        run: npm ci
      
      - name: Install Playwright
        run: npx playwright install --with-deps
      
      - name: Run E2E tests
        run: npm run test:e2e
      
      - name: Upload test results
        uses: actions/upload-artifact@v4
        if: always()
        with:
          name: playwright-report
          path: playwright-report/

  security:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Run Snyk
        uses: snyk/actions/node@master
        env:
          SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
      
      - name: Run SonarCloud
        uses: SonarSource/sonarcloud-github-action@master
        env:
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}

  deploy-staging:
    needs: [test, security, e2e]
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/develop'
    environment: staging
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Deploy to Staging
        run: |
          echo "Deploying to staging..."
          kubectl apply -f k8s/staging/
      
      - name: Smoke tests
        run: |
          sleep 30
          curl -f https://staging.exemplo.com/health

  deploy-production:
    needs: [deploy-staging]
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    environment: production
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Deploy to Production
        run: |
          echo "Deploying to production..."
          kubectl apply -f k8s/production/
```

---

## GitLab CI

```yaml
# .gitlab-ci.yml
stages:
  - lint
  - test
  - security
  - deploy

variables:
  NODE_VERSION: '18'
  POSTGRES_DB: test
  POSTGRES_USER: test
  POSTGRES_PASSWORD: test

lint:
  stage: lint
  image: node:$NODE_VERSION
  before_script:
    - npm ci
  script:
    - npm run lint
    - npm run type-check
  allow_failure: false

test:unit:
  stage: test
  image: node:$NODE_VERSION
  services:
    - postgres:15
  before_script:
    - npm ci
  script:
    - npm run test:unit -- --coverage
  coverage: '/Lines\s*:\s*(\d+\.\d+)%/'
  artifacts:
    reports:
      junit: junit.xml
      coverage_report:
        coverage_format: cobertura
        path: coverage/cobertura-coverage.xml

test:integration:
  stage: test
  image: node:$NODE_VERSION
  services:
    - postgres:15
    - redis:7
  before_script:
    - npm ci
  script:
    - npm run test:integration
  dependencies:
    - test:unit

test:e2e:
  stage: test
  image: node:$NODE_VERSION
  before_script:
    - npm ci
    - npx playwright install --with-deps
  script:
    - npm run test:e2e
  dependencies:
    - test:integration
  allow_failure: true

security:dependency-scanning:
  stage: security
  image: alpine:latest
  before_script:
    - apk add --no-cache curl
  script:
    - curl -sL https://getsentry.com/snippets/snyk-gitlab.sh | bash

deploy:staging:
  stage: deploy
  script:
    - echo "Deploying to staging"
    - kubectl config use-context staging
    - kubectl apply -f k8s/staging/
  environment:
    name: staging
  only:
    - develop

deploy:production:
  stage: deploy
  script:
    - echo "Deploying to production"
    - kubectl config use-context production
    - kubectl apply -f k8s/production/
  environment:
    name: production
  when: manual
  only:
    - main
```

---

## Quality Gates

### Implementação

```yaml
quality-gates:
  stage: quality
  image: node:$NODE_VERSION
  before_script:
    - npm ci
  script:
    - |
      # Check coverage
      COVERAGE=$(cat coverage/coverage.json | jq '.total.lines.pct')
      echo "Coverage: $COVERAGE%"
      
      if (( $(echo "$COVERAGE < 80" | bc -l) )); then
        echo "Coverage below 80% threshold"
        exit 1
      fi
      
      # Check test pass rate
      PASS_RATE=$(cat test-results.json | jq '.passed / .total * 100')
      echo "Pass rate: $PASS_RATE%"
      
      if (( $(echo "$PASS_RATE < 95" | bc -l) )); then
        echo "Pass rate below 95% threshold"
        exit 1
      fi
      
      # Check security
      if grep -q "high\|critical" security-report.json; then
        echo "Security vulnerabilities found"
        exit 1
      fi
      
      echo "All quality gates passed"
  needs:
    - test
  allow_failure: false
```

---

## Test Reports

### JUnit XML Format

```xml
<?xml version="1.0" encoding="UTF-8"?>
<testsuite name="pytest" tests="10" failures="1" errors="0" skipped="0">
  <testcase classname="tests.test_login" name="test_login_success" time="0.123"/>
  <testcase classname="tests.test_login" name="test_login_failure" time="0.234">
    <failure message="AssertionError">assert 'token' not in response</failure>
  </testcase>
</testsuite>
```

### GitHub Actions Upload

```yaml
- name: Upload JUnit test results
  uses: actions/upload-artifact@v4
  with:
    name: test-results
    path: test-results/junit.xml

- name: Publish Test Results
  uses: dorny/test-reporter@v1
  with:
    name: Test Results
    path: test-results/*.xml
    reporter: java-junit
    fail-on: test failures
```

---

## Conclusão

CI/CD robusto é essencial para QA moderno. As chaves são:

1. **Automação completa** - Build, test, deploy
2. **Quality gates** - Critérios objetivos para avançar
3. **Feedback rápido** - Resultados em minutos
4. **Parallel execution** - Speed up pipelines
5. **Reliable reporting** - JUnit XML, cobertura, etc.
