# CI/CD Pipeline & Test-System - Entwicklerdokumentation

> **Letzte Aktualisierung:** 2026-02-04
> **Status:** Produktiv
> **Maintainer:** DevOps Team

---

## Inhaltsverzeichnis

1. [Architektur-Übersicht](#1-architektur-übersicht)
2. [Woodpecker CI Pipeline](#2-woodpecker-ci-pipeline)
   - 2.5 [Integration Tests](#25-integration-tests)
3. [Test Registry Backend](#3-test-registry-backend)
4. [Datenbank-Schema](#4-datenbank-schema)
5. [Backlog-System](#5-backlog-system)
6. [Frontend Dashboard](#6-frontend-dashboard)
7. [Service-Übersicht](#7-service-übersicht)
8. [Fehlerbehebung](#8-fehlerbehebung)
9. [API-Referenz](#9-api-referenz)

---

## 1. Architektur-Übersicht

### Systemkomponenten

```
┌─────────────────────────────────────────────────────────────────────────────┐
│                           ENTWICKLER-WORKFLOW                                │
│                                                                              │
│   git push ──▶ Gitea (macmini:3003) ──▶ Webhook ──▶ Woodpecker CI           │
└─────────────────────────────────────────────────────────────────────────────┘
                                                            │
                                                            ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│                         WOODPECKER CI PIPELINE                               │
│                                                                              │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐  ┌──────────────────┐ │
│  │  Go Tests    │  │ Python Tests │  │  Node Tests  │  │ Report Results   │ │
│  │  (consent,   │  │  (backend,   │  │  (h5p)       │  │ (curl → Backend) │ │
│  │   billing,   │  │   voice,     │  │              │  │                  │ │
│  │   school)    │  │   klausur)   │  │              │  │                  │ │
│  └──────────────┘  └──────────────┘  └──────────────┘  └──────────────────┘ │
│                                                            │                 │
└────────────────────────────────────────────────────────────│─────────────────┘
                                                             │
                                                             ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│                           BACKEND (FastAPI)                                  │
│                                                                              │
│  POST /api/tests/ci-result                                                   │
│       │                                                                      │
│       ├──▶ TestRunDB (Test-Durchläufe)                                      │
│       ├──▶ TestResultDB (Einzelne Tests)                                    │
│       ├──▶ TestServiceStatsDB (Aggregierte Stats)                           │
│       └──▶ FailedTestBacklogDB (Backlog bei Fehlern)                        │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘
                                                             │
                                                             ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│                         FRONTEND (Next.js)                                   │
│                                                                              │
│  Test Dashboard: https://macmini:3002/infrastructure/tests                   │
│  CI/CD Dashboard: https://macmini:3002/infrastructure/ci-cd                  │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘
```

### Technologie-Stack

| Komponente | Technologie | Port | Beschreibung |
|------------|-------------|------|--------------|
| **CI/CD Server** | Woodpecker CI v3 | 4431 (HTTPS) | Pipeline-Orchestrierung |
| **Git Server** | Gitea | 3003 | Repository-Hosting |
| **Backend API** | FastAPI (Python) | 8000 | Test Registry, Backlog-Management |
| **Datenbank** | PostgreSQL | 5432 | Persistente Speicherung |
| **Frontend** | Next.js | 3002 | Admin Dashboard |
| **Plattform** | ARM64 (Apple Silicon) | - | Mac Mini M2 |

---

## 2. Woodpecker CI Pipeline

### Konfigurationsdatei

**Pfad:** `.woodpecker/main.yml`

### Pipeline-Stages

```yaml
# Stage 1: Lint (nur bei PRs)
go-lint          # golangci-lint für Go Services
python-lint      # ruff/black für Python

# Stage 2: Unit Tests
test-go-consent      # consent-service (Go)
test-go-billing      # billing-service (Go)
test-go-school       # school-service (Go)
test-python-backend  # backend (Python/pytest)
test-python-voice    # voice-service inkl. BQAS (Python/pytest)
test-python-klausur  # klausur-service (Python/pytest)
test-nodejs-h5p      # h5p-service (Node.js/Jest)

# Stage 3: Report
report-test-results  # Sendet Ergebnisse an Backend API

# Stage 4: Build (nur Tags/manuell)
build-consent-service
build-backend
build-voice-service

# Stage 5: Deploy (nur manuell)
deploy-production
```

### Test-Step Struktur (Beispiel Go)

```yaml
test-go-consent:
  image: golang:1.23-alpine
  environment:
    CGO_ENABLED: "0"
  commands:
    - |
      set -euo pipefail
      apk add --no-cache jq bash
      mkdir -p .ci-results

      # Directory-Check (falls Service nicht existiert)
      if [ ! -d "consent-service" ]; then
        echo '{"service":"consent-service","framework":"go","total":0,"passed":0,"failed":0,"skipped":0,"coverage":0}' > .ci-results/results-consent.json
        echo "WARNUNG: consent-service Verzeichnis nicht gefunden"
        exit 0
      fi

      cd consent-service
      set +e
      go test -v -json -coverprofile=coverage.out ./... 2>&1 | tee ../.ci-results/test-consent.json
      TEST_EXIT=$?
      set -e

      # jq für korrektes JSON-Parsing (nur Test-Zeilen zählen, nicht Package-Zeilen)
      TOTAL=$(jq -s '[.[] | select(.Action=="run" and .Test != null)] | length' ../.ci-results/test-consent.json || echo 0)
      PASSED=$(jq -s '[.[] | select(.Action=="pass" and .Test != null)] | length' ../.ci-results/test-consent.json || echo 0)
      FAILED=$(jq -s '[.[] | select(.Action=="fail" and .Test != null)] | length' ../.ci-results/test-consent.json || echo 0)
      SKIPPED=$(jq -s '[.[] | select(.Action=="skip" and .Test != null)] | length' ../.ci-results/test-consent.json || echo 0)

      COVERAGE=$(go tool cover -func=coverage.out 2>/dev/null | tail -1 | awk '{print $3}' | tr -d '%' || echo "0")
      [ -z "$COVERAGE" ] && COVERAGE=0

      echo "{\"service\":\"consent-service\",\"framework\":\"go\",\"total\":$TOTAL,\"passed\":$PASSED,\"failed\":$FAILED,\"skipped\":$SKIPPED,\"coverage\":$COVERAGE}" > ../.ci-results/results-consent.json
      cat ../.ci-results/results-consent.json

      if [ "$FAILED" -gt "0" ] || [ "$TEST_EXIT" -ne "0" ]; then exit 1; fi
```

**Wichtige Änderungen gegenüber früheren Versionen:**
- `set -euo pipefail` für strikte Fehlerbehandlung
- `jq` statt `grep -c` für korrektes JSON-Parsing (verhindert Überzählung bei mehreren Actions pro Test)
- Directory-Check vor dem `cd` Befehl
- Separate Prüfung von `TEST_EXIT` und `FAILED` Count

### Test-Step Struktur (Beispiel Python)

```yaml
test-python-backend:
  image: python:3.12-slim
  commands:
    - |
      mkdir -p .ci-results
      cd backend
      pip install --quiet -r requirements.txt
      pip install --quiet pytest pytest-cov pytest-asyncio pytest-json-report

      # Tests mit JSON-Report ausführen
      pytest tests/ -v --tb=short \
        --cov=. --cov-report=term-missing \
        --json-report --json-report-file=../.ci-results/test-backend.json || true

      # Statistiken aus JSON extrahieren
      if [ -f ../.ci-results/test-backend.json ]; then
        TOTAL=$(python3 -c "import json; d=json.load(open('../.ci-results/test-backend.json')); print(d.get('summary',{}).get('total',0))")
        PASSED=$(python3 -c "import json; d=json.load(open('../.ci-results/test-backend.json')); print(d.get('summary',{}).get('passed',0))")
        FAILED=$(python3 -c "import json; d=json.load(open('../.ci-results/test-backend.json')); print(d.get('summary',{}).get('failed',0))")
        SKIPPED=$(python3 -c "import json; d=json.load(open('../.ci-results/test-backend.json')); print(d.get('summary',{}).get('skipped',0))")
      else
        TOTAL=0; PASSED=0; FAILED=0; SKIPPED=0
      fi

      # JSON-Ergebnis speichern
      echo "{\"service\":\"backend\",\"framework\":\"pytest\",\"total\":$TOTAL,\"passed\":$PASSED,\"failed\":$FAILED,\"skipped\":$SKIPPED,\"coverage\":0}" \
        > ../.ci-results/results-backend.json
```

### Report-Step

```yaml
report-test-results:
  image: curlimages/curl:8.10.1
  commands:
    - |
      set -uo pipefail
      echo "=== Sende Test-Ergebnisse an Dashboard ==="
      echo "Pipeline Status: ${CI_PIPELINE_STATUS:-unknown}"
      ls -la .ci-results/ || echo "Verzeichnis nicht gefunden"

      PIPELINE_STATUS="${CI_PIPELINE_STATUS:-unknown}"

      # Schleife über alle Ergebnis-Dateien
      for f in .ci-results/results-*.json; do
        [ -f "$f" ] || continue
        echo "Sending: $f"
        curl -f -sS -X POST "http://backend:8000/api/tests/ci-result" \
          -H "Content-Type: application/json" \
          -d "{
            \"pipeline_id\": \"${CI_PIPELINE_NUMBER}\",
            \"commit\": \"${CI_COMMIT_SHA}\",
            \"branch\": \"${CI_COMMIT_BRANCH}\",
            \"status\": \"${PIPELINE_STATUS}\",
            \"test_results\": $(cat "$f")
          }" || echo "WARNUNG: Konnte $f nicht senden"
      done

      echo "=== Test-Ergebnisse gesendet ==="
  when:
    status: [success, failure]  # Läuft immer, auch bei Test-Fehlern
  depends_on:
    - test-go-consent
    - test-go-billing
    - test-go-school
    - test-python-backend
    - test-python-voice
    - test-python-klausur
    - test-nodejs-h5p
```

**Wichtige Verbesserungen:**
- **Vollständiges `depends_on`:** Alle Test-Steps inklusive integration-tests sind aufgelistet
- **Schleife statt if-Blöcke:** Reduziert Code-Duplikation
- **Dynamischer Status:** `${CI_PIPELINE_STATUS}` statt hardcodiertem `"success"`
- **curl mit `-f` Flag:** Zeigt HTTP-Fehler an
- **Pinned Image-Version:** `curl:8.10.1` statt `latest`

---

### 2.5 Integration Tests

Nach den Unit-Tests laeuft ein vollstaendiger Integration-Test-Schritt, der alle Services in einer Docker-Compose-Umgebung testet.

#### Docker Compose Services

| Service | Container-Name | Port (extern) | Port (intern) |
|---------|----------------|---------------|---------------|
| PostgreSQL | postgres-test | 55432 | 5432 |
| Valkey | valkey-test | 56379 | 6379 |
| Consent Service | consent-service-test | 58081 | 8081 |
| Backend | backend-test | 58000 | 8000 |
| Mailpit | mailpit-test | 58025/51025 | 8025/1025 |

#### Pipeline-Step Konfiguration

```yaml
integration-tests:
  image: docker:27-cli
  volumes:
    - /var/run/docker.sock:/var/run/docker.sock
  commands:
    - |
      # 1. Docker Compose Umgebung starten
      docker compose -f docker-compose.test.yml up -d

      # 2. Auf healthy Services warten (Timeout: 120s pro Service)
      for service in postgres-test valkey-test consent-service-test backend-test; do
        echo "Waiting for $service..."
        timeout=120
        while [ $elapsed -lt $timeout ]; do
          status=$(docker compose -f docker-compose.test.yml ps $service --format json | jq -r '.[0].Health')
          if [ "$status" = "healthy" ]; then break; fi
          sleep 5
        done
      done

      # 3. Integration Tests im Backend-Container ausfuehren
      docker compose -f docker-compose.test.yml exec -T backend-test \
        pytest tests/test_integration/ -v --tb=short \
        --json-report --json-report-file=/tmp/integration-results.json

      # 4. Cleanup
      docker compose -f docker-compose.test.yml down -v
  when:
    - event: [push, pull_request]
      branch: [main, develop]
  depends_on:
    - test-python-backend
```

#### Environment Variables im Integration-Modus

| Variable | Wert |
|----------|------|
| `SKIP_INTEGRATION_TESTS` | `false` |
| `DATABASE_URL` | `postgresql://breakpilot:breakpilot_test@postgres-test:5432/breakpilot_test` |
| `CONSENT_SERVICE_URL` | `http://consent-service-test:8081` |
| `VALKEY_URL` / `REDIS_URL` | `redis://valkey-test:6379` |
| `SMTP_HOST` / `SMTP_PORT` | `mailpit-test` / `1025` |

#### Lokales Testen der Integration-Tests

```bash
# 1. Test-Umgebung starten
docker compose -f docker-compose.test.yml up -d

# 2. Warten bis healthy
docker compose -f docker-compose.test.yml ps

# 3. Tests ausfuehren
cd backend
export SKIP_INTEGRATION_TESTS=false
pytest tests/test_integration/ -v

# 4. Aufraeumen
docker compose -f docker-compose.test.yml down -v
```

#### Troubleshooting Integration Tests

| Problem | Loesung |
|---------|---------|
| Service nicht healthy | `docker compose -f docker-compose.test.yml logs <service> --tail=100` |
| Port bereits belegt | `lsof -i :<port>` und bestehende Container stoppen |
| Tests finden keine Services | Sicherstellen dass `SKIP_INTEGRATION_TESTS=false` gesetzt ist |
| Timeout beim Warten | Health-Check-Intervalle in docker-compose.test.yml anpassen |

**Weitere Details:** Siehe [Integration Test Environment Dokumentation](../testing/integration-test-environment.md)

---

### CI-Result JSON-Format

```json
{
  "pipeline_id": "27",
  "commit": "abc12345",
  "branch": "main",
  "status": "success",
  "test_results": {
    "service": "consent-service",
    "framework": "go",
    "total": 57,
    "passed": 57,
    "failed": 0,
    "skipped": 0,
    "coverage": 75.5
  }
}
```

---

## 3. Test Registry Backend

### Dateien

| Datei | Beschreibung |
|-------|--------------|
| `backend/api/tests/registry.py` | Haupt-API Router (~2200 Zeilen) |
| `backend/api/tests/models.py` | Pydantic/Dataclass Models |
| `backend/api/tests/db_models.py` | SQLAlchemy DB Models |
| `backend/api/tests/repository.py` | Datenbank-Repository |
| `backend/api/tests/database.py` | DB-Session Management |

### Datenfluss bei CI-Result

```python
@router.post("/ci-result")
async def receive_ci_result(result: CIResultRequest, background_tasks: BackgroundTasks):
    """
    1. Extrahiere Service-Daten aus test_results
    2. Erstelle TestRunDB Eintrag
    3. Aktualisiere TestServiceStatsDB
    4. Aktualisiere In-Memory Cache (_persisted_results)
    5. Bei Fehlern: Erstelle FailedTestBacklogDB Eintrag (Background Task)
    6. Bei 0 Fehlern: Schließe offene Backlog-Einträge (Background Task)
    """
```

### In-Memory Cache

```python
# Wird beim Start aus PostgreSQL geladen
_persisted_results: Dict[str, Dict] = {}

# Struktur pro Service:
{
    "consent-service": {
        "total": 57,
        "passed": 57,
        "failed": 0,
        "last_run": "2026-02-02T18:46:50",
        "status": "passed",
        "failed_test_ids": []
    }
}

# Wird bei jedem CI-Result sofort aktualisiert für Echtzeit-Updates
```

---

## 4. Datenbank-Schema

### TestRunDB (test_runs)

Speichert jeden Test-Durchlauf.

```sql
CREATE TABLE test_runs (
    id SERIAL PRIMARY KEY,
    run_id VARCHAR(50) UNIQUE NOT NULL,      -- z.B. "ci-27-consent-service"
    service VARCHAR(100) NOT NULL,
    framework VARCHAR(50) NOT NULL,           -- go, pytest, jest
    started_at TIMESTAMP NOT NULL,
    completed_at TIMESTAMP,
    status VARCHAR(20) NOT NULL,              -- queued, running, completed, failed
    total_tests INTEGER DEFAULT 0,
    passed_tests INTEGER DEFAULT 0,
    failed_tests INTEGER DEFAULT 0,
    skipped_tests INTEGER DEFAULT 0,
    duration_seconds FLOAT DEFAULT 0,
    git_commit VARCHAR(40),
    git_branch VARCHAR(100),
    triggered_by VARCHAR(50),                 -- manual, ci, schedule
    output TEXT,
    created_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_test_runs_service ON test_runs(service);
CREATE INDEX idx_test_runs_started_at ON test_runs(started_at);
```

### TestResultDB (test_results)

Speichert einzelne Test-Ergebnisse.

```sql
CREATE TABLE test_results (
    id SERIAL PRIMARY KEY,
    run_id VARCHAR(50) REFERENCES test_runs(run_id) ON DELETE CASCADE,
    test_name VARCHAR(500) NOT NULL,
    test_file VARCHAR(500),
    line_number INTEGER,
    status VARCHAR(20) NOT NULL,              -- passed, failed, skipped, error
    duration_ms FLOAT,
    error_message TEXT,
    error_type VARCHAR(100),
    output TEXT,
    created_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_test_results_run_id ON test_results(run_id);
CREATE INDEX idx_test_results_status ON test_results(status);
```

### FailedTestBacklogDB (failed_tests_backlog)

Persistenter Backlog für fehlgeschlagene Tests.

```sql
CREATE TABLE failed_tests_backlog (
    id SERIAL PRIMARY KEY,
    test_name VARCHAR(500) NOT NULL,
    test_file VARCHAR(500),
    service VARCHAR(100) NOT NULL,
    framework VARCHAR(50),
    error_message TEXT,
    error_type VARCHAR(100),
    first_failed_at TIMESTAMP NOT NULL,
    last_failed_at TIMESTAMP NOT NULL,
    failure_count INTEGER DEFAULT 1,
    status VARCHAR(30) DEFAULT 'open',        -- open, in_progress, fixed, wont_fix, flaky
    priority VARCHAR(20) DEFAULT 'medium',    -- critical, high, medium, low
    assigned_to VARCHAR(100),
    fix_suggestion TEXT,
    notes TEXT,
    -- Auto-Close Felder
    resolved_at TIMESTAMP,
    resolution_commit VARCHAR(50),
    resolution_notes TEXT,
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW(),

    CONSTRAINT uq_backlog_test_service UNIQUE (test_name, service)
);

CREATE INDEX idx_backlog_service ON failed_tests_backlog(service);
CREATE INDEX idx_backlog_status ON failed_tests_backlog(status);
CREATE INDEX idx_backlog_priority ON failed_tests_backlog(priority);
```

### TestServiceStatsDB (test_service_stats)

Aggregierte Statistiken pro Service für schnelle Abfragen.

```sql
CREATE TABLE test_service_stats (
    id SERIAL PRIMARY KEY,
    service VARCHAR(100) UNIQUE NOT NULL,
    total_tests INTEGER DEFAULT 0,
    passed_tests INTEGER DEFAULT 0,
    failed_tests INTEGER DEFAULT 0,
    skipped_tests INTEGER DEFAULT 0,
    pass_rate FLOAT DEFAULT 0.0,
    last_run_id VARCHAR(50),
    last_run_at TIMESTAMP,
    last_status VARCHAR(20),
    updated_at TIMESTAMP DEFAULT NOW()
);
```

### TestFixHistoryDB (test_fixes_history)

Historie aller Fix-Versuche.

```sql
CREATE TABLE test_fixes_history (
    id SERIAL PRIMARY KEY,
    backlog_id INTEGER REFERENCES failed_tests_backlog(id) ON DELETE CASCADE,
    fix_type VARCHAR(50),                     -- manual, auto_claude, auto_script
    fix_description TEXT,
    commit_hash VARCHAR(40),
    success BOOLEAN,
    created_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_fixes_backlog_id ON test_fixes_history(backlog_id);
```

---

## 5. Backlog-System

### Status-Workflow

```
                    ┌─────────────┐
                    │             │
    Test schlägt ──▶│    open     │◀── Test schlägt erneut fehl
    fehl             │             │
                    └──────┬──────┘
                           │
                           │ Entwickler beginnt Fix
                           ▼
                    ┌─────────────┐
                    │             │
                    │ in_progress │
                    │             │
                    └──────┬──────┘
                           │
           ┌───────────────┼───────────────┐
           │               │               │
           ▼               ▼               ▼
    ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
    │             │ │             │ │             │
    │   fixed     │ │  wont_fix   │ │    flaky    │
    │             │ │             │ │             │
    └─────────────┘ └─────────────┘ └─────────────┘
           ▲
           │
           │ Automatisch wenn alle
           │ Tests bestehen
    ┌──────┴──────┐
    │  resolved   │
    │ (auto-close)│
    └─────────────┘
```

### Automatische Backlog-Erstellung

Bei `failed > 0` in CI-Result:

```python
async def _create_backlog_entry(service_name, framework, failed_count, pipeline_id, commit, branch):
    """
    1. Prüfe ob Eintrag für Service bereits existiert
    2. Falls ja: Erhöhe failure_count, aktualisiere last_failed_at
    3. Falls nein: Erstelle neuen Eintrag mit status='open'
    """
    with get_db_session() as db:
        existing = db.query(FailedTestBacklogDB).filter(
            FailedTestBacklogDB.service == service_name,
            FailedTestBacklogDB.status == "open"
        ).first()

        if existing:
            existing.failure_count += failed_count
            existing.last_failed_at = datetime.utcnow()
        else:
            entry = FailedTestBacklogDB(
                test_name=f"Pipeline {pipeline_id} - {failed_count} Tests",
                service=service_name,
                framework=framework,
                status="open",
                priority="medium",
                first_failed_at=datetime.utcnow(),
                last_failed_at=datetime.utcnow(),
                failure_count=failed_count,
                fix_suggestion="Analysiere den Stack-Trace für Details."
            )
            db.add(entry)
        db.commit()
```

### Automatisches Schließen (Auto-Close)

Bei `failed == 0` in CI-Result:

```python
async def _close_backlog_entry(service_name, pipeline_id, commit):
    """
    Schließt alle offenen Backlog-Einträge für einen Service,
    wenn alle Tests bestanden haben.
    """
    with get_db_session() as db:
        open_entries = db.query(FailedTestBacklogDB).filter(
            FailedTestBacklogDB.service == service_name,
            FailedTestBacklogDB.status == "open"
        ).all()

        for entry in open_entries:
            entry.status = "resolved"
            entry.resolved_at = datetime.utcnow()
            entry.resolution_commit = commit[:8] if commit else None
            entry.resolution_notes = f"Automatisch geschlossen - alle Tests in Pipeline {pipeline_id} bestanden"

        db.commit()
```

### Prioritäts-Regeln

| Priorität | Kriterium |
|-----------|-----------|
| `critical` | > 10 Fehler oder Service-kritisch |
| `high` | 5-10 Fehler oder häufige Regression |
| `medium` | 1-4 Fehler (Standard) |
| `low` | Flaky Tests oder Edge Cases |

---

## 6. Frontend Dashboard

### URLs

| Dashboard | URL | Beschreibung |
|-----------|-----|--------------|
| Test Dashboard | `https://macmini:3002/infrastructure/tests` | Übersicht aller Services |
| CI/CD Dashboard | `https://macmini:3002/infrastructure/ci-cd` | Pipeline-Status |
| Backlog | `https://macmini:3002/infrastructure/tests` (Tab) | Fehlgeschlagene Tests |

### Komponenten

**Pfad:** `admin-v2/app/(admin)/infrastructure/tests/page.tsx`

```typescript
// Haupt-Komponenten
<TestDashboard>
  <ServiceGrid />        // Kacheln für jeden Service
  <TestRunsTable />      // Letzte Test-Durchläufe
  <BacklogPanel />       // Offene Backlog-Einträge
  <CoverageChart />      // Coverage-Visualisierung
</TestDashboard>
```

### API-Aufrufe

```typescript
// Test Registry laden
const registry = await fetch('/api/tests/registry').then(r => r.json());

// Backlog laden
const backlog = await fetch('/api/tests/backlog').then(r => r.json());

// Test-Runs laden
const runs = await fetch('/api/tests/runs').then(r => r.json());

// Coverage laden
const coverage = await fetch('/api/tests/coverage').then(r => r.json());
```

---

## 7. Service-Übersicht

### Registrierte Services

| Service | Sprache | Framework | Port | Tests | Status |
|---------|---------|-----------|------|-------|--------|
| consent-service | Go | go_test | 8081 | ~60 | Aktiv |
| billing-service | Go | go_test | 8082 | ~20 | Aktiv |
| school-service | Go | go_test | 8084 | ~15 | Aktiv |
| backend | Python | pytest | 8000 | ~200 | Aktiv |
| voice-service | Python | pytest | 8091 | ~30 | Aktiv (inkl. BQAS) |
| klausur-service | Python | pytest | 8086 | ~150 | Aktiv |
| h5p-service | Node.js | jest | - | ~25 | Aktiv |
| edu-search-service | Go | go_test | 8088 | 0 | Keine Tests |
| ai-compliance-sdk | Go | go_test | - | ~150 | Nicht in Pipeline |
| website | TypeScript | jest | 3000 | ~0 | Nicht in Pipeline |
| bqas-golden | Python | pytest | 8091 | ~10 | In voice-service |
| bqas-rag | Python | pytest | 8091 | ~10 | In voice-service |

### Service-Definition (models.py)

```python
SERVICE_DEFINITIONS = [
    {
        "service": "consent-service",
        "display_name": "Consent Service",
        "port": 8081,
        "language": "go",
        "base_path": "/consent-service",
        "test_pattern": "*_test.go",
        "framework": TestFramework.GO_TEST,
    },
    # ... weitere Services
]
```

### Tests pro Service

#### consent-service (Go)

```
consent-service/
├── internal/
│   ├── handlers/
│   │   └── handlers_test.go      # HTTP Handler Tests
│   ├── services/
│   │   ├── auth_service_test.go  # Auth Tests
│   │   └── consent_service_test.go
│   └── middleware/
│       └── middleware_test.go
└── cmd/
    └── server_test.go
```

#### backend (Python)

```
backend/tests/
├── test_consent_client.py
├── test_gdpr_api.py
├── test_documents.py
├── test_worksheets_api.py
├── test_auth.py
└── ...
```

#### voice-service (Python)

```
voice-service/tests/
├── test_encryption.py
├── test_intent_router.py
├── test_sessions.py
├── test_tasks.py
└── bqas/
    ├── test_golden.py        # BQAS Golden Suite
    └── test_rag.py           # BQAS RAG Tests
```

#### klausur-service (Python)

```
klausur-service/backend/tests/
├── test_advanced_rag.py
├── test_byoeh.py
├── test_mail_service.py
├── test_ocr_labeling.py
├── test_rag_admin.py
├── test_rbac.py
├── test_vocab_worksheet.py
└── test_worksheet_editor.py
```

#### h5p-service (Node.js)

```
h5p-service/tests/
├── server.test.js
├── setup.js
└── README.md
```

---

## 8. Fehlerbehebung

### Problem: Tests zeigen 0/0 an

**Ursache:** Pipeline-Tests produzieren keine Ergebnisse

**Lösung:**
1. Prüfe Woodpecker Agent Logs:
   ```bash
   docker logs breakpilot-pwa-woodpecker-agent --tail=100
   ```
2. Prüfe ob Service kompiliert:
   ```bash
   cd billing-service && go build ./...
   ```
3. Prüfe ob Tests lokal laufen:
   ```bash
   cd billing-service && go test -v ./...
   ```

### Problem: Backlog zeigt 500 Error

**Ursache:** Fehlende DB-Spalten

**Lösung:**
```sql
ALTER TABLE failed_tests_backlog ADD COLUMN resolved_at TIMESTAMP;
ALTER TABLE failed_tests_backlog ADD COLUMN resolution_commit VARCHAR(50);
ALTER TABLE failed_tests_backlog ADD COLUMN resolution_notes TEXT;
```

### Problem: Frontend zeigt alte Daten

**Ursache:** In-Memory Cache nicht aktualisiert

**Lösung:**
1. Backend neustarten:
   ```bash
   docker compose restart backend
   ```
2. Oder: Manuell Cache-Refresh via API (wenn implementiert)

### Problem: Pipeline startet nicht

**Ursache:** Webhook von Gitea nicht empfangen

**Lösung:**
1. Prüfe Gitea Webhook-Konfiguration
2. Prüfe Woodpecker Server Logs:
   ```bash
   docker logs breakpilot-pwa-woodpecker-server --tail=50
   ```
3. Manuell Pipeline triggern via Woodpecker UI

### Problem: OOM Kill in Pipeline

**Ursache:** Zu viele parallele Tests

**Lösung:**
1. Tests sequentiell statt parallel ausführen
2. Memory-Limits in docker-compose erhöhen
3. Große Test-Suites aufteilen

---

## 9. API-Referenz

### Test Registry Endpoints

#### GET /api/tests/registry

Gibt alle registrierten Services mit Test-Statistiken zurück.

**Response:**
```json
{
  "services": [
    {
      "service": "consent-service",
      "display_name": "Consent Service",
      "port": 8081,
      "language": "go",
      "total_tests": 57,
      "passed_tests": 57,
      "failed_tests": 0,
      "skipped_tests": 0,
      "pass_rate": 100.0,
      "coverage_percent": 75.5,
      "last_run": "2026-02-02T18:46:50",
      "status": "passed"
    }
  ],
  "stats": {
    "total_tests": 500,
    "total_passed": 480,
    "total_failed": 20,
    "services_count": 12,
    "overall_pass_rate": 96.0
  }
}
```

#### POST /api/tests/ci-result

Empfängt Test-Ergebnisse von der CI/CD-Pipeline.

**Request:**
```json
{
  "pipeline_id": "27",
  "commit": "abc12345def67890",
  "branch": "main",
  "status": "success",
  "test_results": {
    "service": "consent-service",
    "framework": "go",
    "total": 57,
    "passed": 57,
    "failed": 0,
    "skipped": 0,
    "coverage": 75.5
  }
}
```

**Response:**
```json
{
  "received": true,
  "run_id": "ci-27-consent-service",
  "service": "consent-service",
  "pipeline_id": "27",
  "status": "passed",
  "tests": {"total": 57, "passed": 57, "failed": 0},
  "stored_in": "postgres"
}
```

#### GET /api/tests/backlog

Gibt alle Backlog-Einträge zurück.

**Query-Parameter:**
- `status`: Filter nach Status (open, in_progress, fixed, etc.)
- `service`: Filter nach Service
- `limit`: Anzahl der Ergebnisse (default: 50)

**Response:**
```json
{
  "total": 15,
  "items": [
    {
      "id": 1,
      "test_name": "Pipeline 27 - 3 Tests",
      "service": "backend",
      "status": "open",
      "priority": "medium",
      "failure_count": 3,
      "first_failed_at": "2026-02-02T10:00:00",
      "last_failed_at": "2026-02-02T18:00:00",
      "resolved_at": null
    }
  ]
}
```

#### GET /api/tests/runs

Gibt die letzten Test-Durchläufe zurück.

**Query-Parameter:**
- `service`: Filter nach Service
- `limit`: Anzahl (default: 20)

#### GET /api/tests/coverage

Gibt Coverage-Informationen zurück.

#### POST /api/tests/run/{suite}

Startet einen Test-Run manuell.

#### PUT /api/tests/backlog/{id}

Aktualisiert einen Backlog-Eintrag (Status, Priorität, Assignee).

---

## Anhang

### Umgebungsvariablen

```bash
# Woodpecker
WOODPECKER_URL=https://macmini:4431
WOODPECKER_TOKEN=<jwt-token>

# Backend
DATABASE_URL=postgresql://user:pass@postgres:5432/breakpilot
TEST_REGISTRY_ENABLED=true
```

### Lokale CI-Simulation

Für lokales Testen ohne Woodpecker CI stehen zwei Hilfsdateien zur Verfügung:

**Makefile** (Projektroot)
```bash
# Alle Tests lokal ausführen
make ci

# Nur Go-Tests
make test-go

# Nur Python-Tests
make test-python

# Woodpecker Agent Logs
make logs-agent

# Backend Logs (ci-result Filter)
make logs-backend

# Test-Ergebnisse löschen
make clean
```

**docker-compose.test.yml** (Projektroot)
```bash
# Test-Datenbanken starten
docker compose -f docker-compose.test.yml up -d

# Test-Datenbanken stoppen und Daten löschen
docker compose -f docker-compose.test.yml down -v
```

| Service | Port | Credentials |
|---------|------|-------------|
| PostgreSQL | 55432 | breakpilot_test/breakpilot/breakpilot |
| Redis | 56379 | (keine) |

---

### Nützliche Befehle

```bash
# Woodpecker Logs
docker logs breakpilot-pwa-woodpecker-agent --tail=100
docker logs breakpilot-pwa-woodpecker-server --tail=100

# Backend Logs
docker compose logs backend --tail=100 | grep -E "(CI-RESULT|test|error)"

# Datenbank prüfen
docker compose exec backend python3 -c "
from sqlalchemy.orm import Session
from classroom_engine.database import engine
from api.tests.db_models import TestRunDB, TestServiceStatsDB

with Session(engine) as db:
    runs = db.query(TestRunDB).order_by(TestRunDB.started_at.desc()).limit(5).all()
    for r in runs:
        print(f'{r.run_id}: {r.status} - {r.passed_tests}/{r.total_tests}')
"

# Pipeline manuell triggern
curl -X POST "https://macmini:4431/api/repos/pilotadmin/breakpilot-pwa/pipelines" \
  -H "Authorization: Bearer $WOODPECKER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"branch":"main"}'
```

### Weiterführende Dokumentation

- [Woodpecker CI Docs](https://woodpecker-ci.org/docs/intro)
- [BQAS Quality System](../architecture/bqas-quality-system.md)
- [Voice Service Guide](../guides/voice-service-developer-guide.md)
- [E2E Testing Guide](../guides/E2E-Testing-Playwright.md)

---

*Generiert am 2026-02-02 von Claude Code*
*Aktualisiert am 2026-02-02: jq-Parsing für Go-Tests, vollständiges depends_on, Makefile & docker-compose.test.yml*
*Aktualisiert am 2026-02-04: Integration Test Environment mit Docker Compose hinzugefuegt (Sektion 2.5)*
