feat(claude): Add testing, documentation rules and project hooks
- rules/testing.md: TDD workflow for Go and Python - rules/documentation.md: Auto-documentation guidelines - plans/embedding-service-separation.md: Migration plan - settings.json: Post-edit hooks for docs/tests validation - .gitignore: Exclude settings.local.json (contains API keys) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
263
.claude/plans/embedding-service-separation.md
Normal file
263
.claude/plans/embedding-service-separation.md
Normal file
@@ -0,0 +1,263 @@
|
|||||||
|
# Plan: Embedding-Service Separation
|
||||||
|
|
||||||
|
## Ziel
|
||||||
|
Trennung der ML/Embedding-Komponenten vom klausur-service in einen eigenständigen `embedding-service`, um die Build-Zeit von ~20 Minuten auf ~30 Sekunden zu reduzieren.
|
||||||
|
|
||||||
|
## Aktuelle Situation
|
||||||
|
|
||||||
|
| Service | Build-Zeit | Image-Größe | Problem |
|
||||||
|
|---------|------------|-------------|---------|
|
||||||
|
| klausur-service | ~20 min | ~2.5 GB | PyTorch + sentence-transformers werden bei jedem Build installiert |
|
||||||
|
|
||||||
|
## Ziel-Architektur
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────┐ HTTP ┌──────────────────┐
|
||||||
|
│ klausur-service │ ───────────→ │ embedding-service │
|
||||||
|
│ (FastAPI) │ │ (FastAPI) │
|
||||||
|
│ Port 8086 │ │ Port 8087 │
|
||||||
|
│ ~200 MB │ │ ~2.5 GB │
|
||||||
|
│ Build: 30s │ │ Build: 15 min │
|
||||||
|
└─────────────────┘ └──────────────────┘
|
||||||
|
│ │
|
||||||
|
└────────────┬───────────────────┘
|
||||||
|
▼
|
||||||
|
┌───────────────┐
|
||||||
|
│ Qdrant │
|
||||||
|
│ Port 6333 │
|
||||||
|
└───────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Phase 1: Neuen Embedding-Service erstellen
|
||||||
|
|
||||||
|
### 1.1 Verzeichnisstruktur anlegen
|
||||||
|
```
|
||||||
|
klausur-service/
|
||||||
|
├── embedding-service/ # NEU
|
||||||
|
│ ├── Dockerfile
|
||||||
|
│ ├── requirements.txt
|
||||||
|
│ ├── main.py # FastAPI App
|
||||||
|
│ ├── eh_pipeline.py # Kopie
|
||||||
|
│ ├── reranker.py # Kopie
|
||||||
|
│ ├── hyde.py # Kopie
|
||||||
|
│ ├── hybrid_search.py # Kopie
|
||||||
|
│ ├── pdf_extraction.py # Kopie
|
||||||
|
│ └── config.py # Embedding-Konfiguration
|
||||||
|
├── backend/ # Bestehend (wird angepasst)
|
||||||
|
└── frontend/ # Bestehend
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.2 Dateien in embedding-service erstellen
|
||||||
|
|
||||||
|
**requirements.txt** (ML-spezifisch):
|
||||||
|
```
|
||||||
|
fastapi>=0.109.0
|
||||||
|
uvicorn[standard]>=0.27.0
|
||||||
|
torch>=2.0.0
|
||||||
|
sentence-transformers>=2.2.0
|
||||||
|
qdrant-client>=1.7.0
|
||||||
|
unstructured>=0.12.0
|
||||||
|
pypdf>=4.0.0
|
||||||
|
httpx>=0.26.0
|
||||||
|
pydantic>=2.0.0
|
||||||
|
python-dotenv>=1.0.0
|
||||||
|
```
|
||||||
|
|
||||||
|
**main.py** - API-Endpoints:
|
||||||
|
- `POST /embed` - Generiert Embeddings für Text/Liste von Texten
|
||||||
|
- `POST /embed-single` - Einzelnes Embedding
|
||||||
|
- `POST /rerank` - Re-Ranking von Suchergebnissen
|
||||||
|
- `POST /extract-pdf` - PDF-Text-Extraktion
|
||||||
|
- `GET /health` - Health-Check
|
||||||
|
- `GET /models` - Verfügbare Modelle
|
||||||
|
|
||||||
|
### 1.3 Dockerfile (embedding-service)
|
||||||
|
```dockerfile
|
||||||
|
FROM python:3.11-slim
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# PyTorch CPU-only für kleinere Images
|
||||||
|
RUN pip install --no-cache-dir torch --index-url https://download.pytorch.org/whl/cpu
|
||||||
|
|
||||||
|
COPY requirements.txt .
|
||||||
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
|
# Modelle vorab laden (Layer-Cache)
|
||||||
|
RUN python -c "from sentence_transformers import SentenceTransformer; SentenceTransformer('BAAI/bge-m3')"
|
||||||
|
RUN python -c "from sentence_transformers import CrossEncoder; CrossEncoder('BAAI/bge-reranker-v2-m3')"
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8087"]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Phase 2: Klausur-Service anpassen
|
||||||
|
|
||||||
|
### 2.1 ML-Dependencies aus requirements.txt entfernen
|
||||||
|
Entfernen:
|
||||||
|
- `torch`
|
||||||
|
- `sentence-transformers`
|
||||||
|
- `unstructured`
|
||||||
|
- `pypdf`
|
||||||
|
|
||||||
|
Behalten:
|
||||||
|
- `fastapi`, `uvicorn`, `httpx`
|
||||||
|
- `qdrant-client` (für Suche)
|
||||||
|
- `cryptography` (BYOEH)
|
||||||
|
- Alle Business-Logic-Dependencies
|
||||||
|
|
||||||
|
### 2.2 Embedding-Client erstellen
|
||||||
|
Neue Datei `backend/embedding_client.py`:
|
||||||
|
```python
|
||||||
|
class EmbeddingClient:
|
||||||
|
def __init__(self, base_url: str = "http://embedding-service:8087"):
|
||||||
|
self.base_url = base_url
|
||||||
|
|
||||||
|
async def generate_embeddings(self, texts: list[str]) -> list[list[float]]:
|
||||||
|
async with httpx.AsyncClient() as client:
|
||||||
|
response = await client.post(f"{self.base_url}/embed", json={"texts": texts})
|
||||||
|
return response.json()["embeddings"]
|
||||||
|
|
||||||
|
async def rerank(self, query: str, documents: list[str]) -> list[dict]:
|
||||||
|
async with httpx.AsyncClient() as client:
|
||||||
|
response = await client.post(f"{self.base_url}/rerank",
|
||||||
|
json={"query": query, "documents": documents})
|
||||||
|
return response.json()["results"]
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.3 Bestehende Aufrufe umleiten
|
||||||
|
Dateien anpassen:
|
||||||
|
- `backend/main.py`: `generate_single_embedding()` → `embedding_client.generate_embeddings()`
|
||||||
|
- `backend/admin_api.py`: Embedding-Aufrufe über Client
|
||||||
|
- `backend/qdrant_service.py`: Bleibt für Suche, Indexierung nutzt Client
|
||||||
|
|
||||||
|
## Phase 3: Docker-Compose Integration
|
||||||
|
|
||||||
|
### 3.1 docker-compose.dev.yml erweitern
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
klausur-service:
|
||||||
|
build:
|
||||||
|
context: ./klausur-service
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
ports:
|
||||||
|
- "8086:8086"
|
||||||
|
environment:
|
||||||
|
- EMBEDDING_SERVICE_URL=http://embedding-service:8087
|
||||||
|
depends_on:
|
||||||
|
- embedding-service
|
||||||
|
- qdrant
|
||||||
|
|
||||||
|
embedding-service:
|
||||||
|
build:
|
||||||
|
context: ./klausur-service/embedding-service
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
ports:
|
||||||
|
- "8087:8087"
|
||||||
|
environment:
|
||||||
|
- EMBEDDING_BACKEND=local
|
||||||
|
- LOCAL_EMBEDDING_MODEL=BAAI/bge-m3
|
||||||
|
- LOCAL_RERANKER_MODEL=BAAI/bge-reranker-v2-m3
|
||||||
|
volumes:
|
||||||
|
- embedding-models:/root/.cache/huggingface # Model-Cache
|
||||||
|
deploy:
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
memory: 4G
|
||||||
|
|
||||||
|
qdrant:
|
||||||
|
image: qdrant/qdrant:latest
|
||||||
|
ports:
|
||||||
|
- "6333:6333"
|
||||||
|
volumes:
|
||||||
|
- qdrant-data:/qdrant/storage
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
embedding-models:
|
||||||
|
qdrant-data:
|
||||||
|
```
|
||||||
|
|
||||||
|
## Phase 4: Tests und Validierung
|
||||||
|
|
||||||
|
### 4.1 Unit Tests für Embedding-Service
|
||||||
|
- Test Embedding-Generierung
|
||||||
|
- Test Re-Ranking
|
||||||
|
- Test PDF-Extraktion
|
||||||
|
- Test Health-Endpoint
|
||||||
|
|
||||||
|
### 4.2 Integration Tests
|
||||||
|
- Test klausur-service → embedding-service Kommunikation
|
||||||
|
- Test RAG-Query End-to-End
|
||||||
|
- Test EH-Upload mit Embedding
|
||||||
|
|
||||||
|
### 4.3 Performance-Validierung
|
||||||
|
- Build-Zeit klausur-service messen (Ziel: <1 min)
|
||||||
|
- Embedding-Latenz messen (Ziel: <500ms für einzelnes Embedding)
|
||||||
|
- Re-Ranking-Latenz messen (Ziel: <1s für 10 Dokumente)
|
||||||
|
|
||||||
|
## Implementierungsreihenfolge
|
||||||
|
|
||||||
|
1. **embedding-service/main.py** - FastAPI App mit Endpoints
|
||||||
|
2. **embedding-service/config.py** - Konfiguration
|
||||||
|
3. **embedding-service/requirements.txt** - Dependencies
|
||||||
|
4. **embedding-service/Dockerfile** - Container-Build
|
||||||
|
5. **backend/embedding_client.py** - HTTP-Client
|
||||||
|
6. **backend/requirements.txt** - ML-Deps entfernen
|
||||||
|
7. **backend/main.py** - Aufrufe umleiten
|
||||||
|
8. **backend/admin_api.py** - Aufrufe umleiten
|
||||||
|
9. **docker-compose.dev.yml** - Service hinzufügen
|
||||||
|
10. **Tests** - Validierung
|
||||||
|
|
||||||
|
## Zu bewegende Dateien (Referenz)
|
||||||
|
|
||||||
|
| Datei | Zeilen | Aktion |
|
||||||
|
|-------|--------|--------|
|
||||||
|
| eh_pipeline.py | 777 | Kopieren → embedding-service |
|
||||||
|
| reranker.py | 253 | Kopieren → embedding-service |
|
||||||
|
| hyde.py | 209 | Kopieren → embedding-service |
|
||||||
|
| hybrid_search.py | 285 | Kopieren → embedding-service |
|
||||||
|
| pdf_extraction.py | 479 | Kopieren → embedding-service |
|
||||||
|
|
||||||
|
## Umgebungsvariablen
|
||||||
|
|
||||||
|
### embedding-service
|
||||||
|
```
|
||||||
|
EMBEDDING_BACKEND=local
|
||||||
|
LOCAL_EMBEDDING_MODEL=BAAI/bge-m3
|
||||||
|
LOCAL_RERANKER_MODEL=BAAI/bge-reranker-v2-m3
|
||||||
|
OPENAI_API_KEY= # Optional für OpenAI-Backend
|
||||||
|
PDF_EXTRACTION_BACKEND=auto
|
||||||
|
LOG_LEVEL=INFO
|
||||||
|
```
|
||||||
|
|
||||||
|
### klausur-service (neu)
|
||||||
|
```
|
||||||
|
EMBEDDING_SERVICE_URL=http://embedding-service:8087
|
||||||
|
```
|
||||||
|
|
||||||
|
## Risiken und Mitigationen
|
||||||
|
|
||||||
|
| Risiko | Mitigation |
|
||||||
|
|--------|------------|
|
||||||
|
| Netzwerk-Latenz zwischen Services | Model-Caching, Connection-Pooling |
|
||||||
|
| embedding-service nicht erreichbar | Health-Checks, Retry-Logik, Graceful Degradation |
|
||||||
|
| Inkonsistente Embedding-Modelle | Versionierung, Model-Hash-Prüfung |
|
||||||
|
| Erhöhter RAM-Bedarf (2 Container) | Memory-Limits in Docker, Model-Offloading |
|
||||||
|
|
||||||
|
## Erwartete Ergebnisse
|
||||||
|
|
||||||
|
| Metrik | Vorher | Nachher |
|
||||||
|
|--------|--------|---------|
|
||||||
|
| Build-Zeit klausur-service | ~20 min | ~30 sec |
|
||||||
|
| Build-Zeit embedding-service | - | ~15 min |
|
||||||
|
| Image-Größe klausur-service | ~2.5 GB | ~200 MB |
|
||||||
|
| Image-Größe embedding-service | - | ~2.5 GB |
|
||||||
|
| Entwickler-Iteration | Langsam | Schnell |
|
||||||
|
|
||||||
|
## Nicht vergessen (Task A)
|
||||||
|
|
||||||
|
Nach Abschluss der Service-Trennung:
|
||||||
|
- [ ] EH-Upload-Wizard mit Test-Klausur testen
|
||||||
|
- [ ] Security-Infobox im Wizard verifizieren
|
||||||
|
- [ ] End-to-End RAG-Query testen
|
||||||
91
.claude/rules/documentation.md
Normal file
91
.claude/rules/documentation.md
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
# Dokumentations-Regeln
|
||||||
|
|
||||||
|
## Automatische Dokumentations-Aktualisierung
|
||||||
|
|
||||||
|
**WICHTIG:** Bei JEDER Code-Änderung muss die entsprechende Dokumentation aktualisiert werden!
|
||||||
|
|
||||||
|
## Wann Dokumentation aktualisieren?
|
||||||
|
|
||||||
|
### API-Änderungen
|
||||||
|
Wenn du einen Endpoint änderst, hinzufügst oder entfernst:
|
||||||
|
- Aktualisiere `/docs/api/consent-service-api.md` (Go Endpoints)
|
||||||
|
- Aktualisiere `/docs/api/backend-api.md` (Python Endpoints)
|
||||||
|
|
||||||
|
### Neue Funktionen/Klassen
|
||||||
|
Wenn du neue Funktionen, Klassen oder Module erstellst:
|
||||||
|
- Aktualisiere `/docs/consent-service/README.md` (für Go)
|
||||||
|
- Aktualisiere `/docs/backend/README.md` (für Python)
|
||||||
|
|
||||||
|
### Architektur-Änderungen
|
||||||
|
Wenn du die Systemarchitektur änderst:
|
||||||
|
- Aktualisiere `/docs/architecture/system-architecture.md`
|
||||||
|
- Aktualisiere `/docs/architecture/data-model.md` (bei DB-Änderungen)
|
||||||
|
|
||||||
|
### Neue Konfigurationsoptionen
|
||||||
|
Wenn du neue Umgebungsvariablen oder Konfigurationen hinzufügst:
|
||||||
|
- Aktualisiere die entsprechende README
|
||||||
|
- Füge zur `guides/local-development.md` hinzu
|
||||||
|
|
||||||
|
## Dokumentations-Format
|
||||||
|
|
||||||
|
### API-Endpoints dokumentieren
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
### METHOD /path/to/endpoint
|
||||||
|
|
||||||
|
Kurze Beschreibung.
|
||||||
|
|
||||||
|
**Request Body:**
|
||||||
|
\`\`\`json
|
||||||
|
{
|
||||||
|
"field": "value"
|
||||||
|
}
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
**Response (200):**
|
||||||
|
\`\`\`json
|
||||||
|
{
|
||||||
|
"result": "value"
|
||||||
|
}
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
**Errors:**
|
||||||
|
- `400`: Beschreibung
|
||||||
|
- `401`: Beschreibung
|
||||||
|
```
|
||||||
|
|
||||||
|
### Funktionen dokumentieren
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
### FunctionName (file.go:123)
|
||||||
|
|
||||||
|
\`\`\`go
|
||||||
|
func FunctionName(param Type) ReturnType
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
**Beschreibung:** Was macht die Funktion?
|
||||||
|
|
||||||
|
**Parameter:**
|
||||||
|
- `param`: Beschreibung
|
||||||
|
|
||||||
|
**Rückgabe:** Beschreibung
|
||||||
|
```
|
||||||
|
|
||||||
|
## Checkliste nach Code-Änderungen
|
||||||
|
|
||||||
|
Vor dem Abschluss einer Aufgabe prüfe:
|
||||||
|
|
||||||
|
- [ ] Wurden neue API-Endpoints hinzugefügt? → API-Docs aktualisieren
|
||||||
|
- [ ] Wurden Datenmodelle geändert? → data-model.md aktualisieren
|
||||||
|
- [ ] Wurden neue Konfigurationen hinzugefügt? → README aktualisieren
|
||||||
|
- [ ] Wurden neue Abhängigkeiten hinzugefügt? → requirements.txt/go.mod UND Docs
|
||||||
|
- [ ] Wurde die Architektur geändert? → architecture/ aktualisieren
|
||||||
|
|
||||||
|
## Beispiel: Vollständige Dokumentation einer neuen Funktion
|
||||||
|
|
||||||
|
Wenn du z.B. `GetUserStats()` im Go Service hinzufügst:
|
||||||
|
|
||||||
|
1. **Code schreiben** in `internal/services/stats_service.go`
|
||||||
|
2. **API-Doc aktualisieren** in `docs/api/consent-service-api.md`
|
||||||
|
3. **Service-Doc aktualisieren** in `docs/consent-service/README.md`
|
||||||
|
4. **Test schreiben** (siehe testing.md)
|
||||||
202
.claude/rules/testing.md
Normal file
202
.claude/rules/testing.md
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
# Test-Regeln
|
||||||
|
|
||||||
|
## Automatische Test-Erweiterung
|
||||||
|
|
||||||
|
**WICHTIG:** Bei JEDER Code-Änderung müssen entsprechende Tests erstellt oder aktualisiert werden!
|
||||||
|
|
||||||
|
## Wann Tests schreiben?
|
||||||
|
|
||||||
|
### IMMER wenn du:
|
||||||
|
1. **Neue Funktionen** erstellst → Unit Test
|
||||||
|
2. **Neue API-Endpoints** hinzufügst → Handler Test
|
||||||
|
3. **Bugs fixst** → Regression Test (der Bug sollte nie wieder auftreten)
|
||||||
|
4. **Bestehenden Code änderst** → Bestehende Tests anpassen
|
||||||
|
|
||||||
|
## Test-Struktur
|
||||||
|
|
||||||
|
### Go Tests (Consent Service)
|
||||||
|
|
||||||
|
**Speicherort:** Im gleichen Verzeichnis wie der Code
|
||||||
|
|
||||||
|
```
|
||||||
|
internal/
|
||||||
|
├── services/
|
||||||
|
│ ├── auth_service.go
|
||||||
|
│ └── auth_service_test.go ← Test hier
|
||||||
|
├── handlers/
|
||||||
|
│ ├── handlers.go
|
||||||
|
│ └── handlers_test.go ← Test hier
|
||||||
|
└── middleware/
|
||||||
|
├── auth.go
|
||||||
|
└── middleware_test.go ← Test hier
|
||||||
|
```
|
||||||
|
|
||||||
|
**Test-Namenskonvention:**
|
||||||
|
```go
|
||||||
|
func TestFunctionName_Scenario_ExpectedResult(t *testing.T)
|
||||||
|
|
||||||
|
// Beispiele:
|
||||||
|
func TestHashPassword_ValidPassword_ReturnsHash(t *testing.T)
|
||||||
|
func TestLogin_InvalidCredentials_Returns401(t *testing.T)
|
||||||
|
func TestCreateDocument_MissingTitle_ReturnsError(t *testing.T)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Test-Template:**
|
||||||
|
```go
|
||||||
|
func TestFunctionName(t *testing.T) {
|
||||||
|
// Arrange
|
||||||
|
service := &MyService{}
|
||||||
|
input := "test-input"
|
||||||
|
|
||||||
|
// Act
|
||||||
|
result, err := service.DoSomething(input)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Expected no error, got %v", err)
|
||||||
|
}
|
||||||
|
if result != expected {
|
||||||
|
t.Errorf("Expected %v, got %v", expected, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Table-Driven Tests bevorzugen:**
|
||||||
|
```go
|
||||||
|
func TestValidateEmail(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
email string
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{"valid email", "test@example.com", true},
|
||||||
|
{"missing @", "testexample.com", false},
|
||||||
|
{"empty", "", false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
result := ValidateEmail(tt.email)
|
||||||
|
if result != tt.expected {
|
||||||
|
t.Errorf("Expected %v, got %v", tt.expected, result)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Python Tests (Backend)
|
||||||
|
|
||||||
|
**Speicherort:** `/backend/tests/`
|
||||||
|
|
||||||
|
```
|
||||||
|
backend/
|
||||||
|
├── consent_client.py
|
||||||
|
├── gdpr_api.py
|
||||||
|
└── tests/
|
||||||
|
├── __init__.py
|
||||||
|
├── test_consent_client.py ← Tests für consent_client.py
|
||||||
|
└── test_gdpr_api.py ← Tests für gdpr_api.py
|
||||||
|
```
|
||||||
|
|
||||||
|
**Test-Namenskonvention:**
|
||||||
|
```python
|
||||||
|
class TestClassName:
|
||||||
|
def test_method_scenario_expected_result(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Beispiele:
|
||||||
|
class TestConsentClient:
|
||||||
|
def test_check_consent_valid_token_returns_status(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_check_consent_expired_token_raises_error(self):
|
||||||
|
pass
|
||||||
|
```
|
||||||
|
|
||||||
|
**Test-Template:**
|
||||||
|
```python
|
||||||
|
import pytest
|
||||||
|
from unittest.mock import AsyncMock, patch, MagicMock
|
||||||
|
|
||||||
|
class TestMyFeature:
|
||||||
|
def test_sync_function(self):
|
||||||
|
# Arrange
|
||||||
|
input_data = "test"
|
||||||
|
|
||||||
|
# Act
|
||||||
|
result = my_function(input_data)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
assert result == expected
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_async_function(self):
|
||||||
|
# Arrange
|
||||||
|
client = MyClient()
|
||||||
|
|
||||||
|
# Act
|
||||||
|
with patch("httpx.AsyncClient") as mock:
|
||||||
|
mock_instance = AsyncMock()
|
||||||
|
mock.return_value = mock_instance
|
||||||
|
result = await client.fetch_data()
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
assert result is not None
|
||||||
|
```
|
||||||
|
|
||||||
|
## Test-Kategorien
|
||||||
|
|
||||||
|
### 1. Unit Tests (Höchste Priorität)
|
||||||
|
- Testen einzelne Funktionen/Methoden
|
||||||
|
- Keine externen Abhängigkeiten (Mocks verwenden)
|
||||||
|
- Schnell ausführbar
|
||||||
|
|
||||||
|
### 2. Integration Tests
|
||||||
|
- Testen Zusammenspiel mehrerer Komponenten
|
||||||
|
- Können echte DB verwenden (Test-DB)
|
||||||
|
|
||||||
|
### 3. Security Tests
|
||||||
|
- Auth/JWT Validierung
|
||||||
|
- Passwort-Hashing
|
||||||
|
- Berechtigungsprüfung
|
||||||
|
|
||||||
|
## Checkliste vor Abschluss
|
||||||
|
|
||||||
|
Vor dem Abschluss einer Aufgabe:
|
||||||
|
|
||||||
|
- [ ] Gibt es Tests für alle neuen Funktionen?
|
||||||
|
- [ ] Gibt es Tests für alle Edge Cases?
|
||||||
|
- [ ] Gibt es Tests für Fehlerfälle?
|
||||||
|
- [ ] Laufen alle bestehenden Tests noch? (`go test ./...` / `pytest`)
|
||||||
|
- [ ] Ist die Test-Coverage angemessen?
|
||||||
|
|
||||||
|
## Tests ausführen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Go - Alle Tests
|
||||||
|
cd consent-service && go test -v ./...
|
||||||
|
|
||||||
|
# Go - Mit Coverage
|
||||||
|
cd consent-service && go test -cover ./...
|
||||||
|
|
||||||
|
# Python - Alle Tests
|
||||||
|
cd backend && source venv/bin/activate && pytest -v
|
||||||
|
|
||||||
|
# Python - Mit Coverage
|
||||||
|
cd backend && pytest --cov=. --cov-report=html
|
||||||
|
```
|
||||||
|
|
||||||
|
## Beispiel: Vollständiger Test-Workflow
|
||||||
|
|
||||||
|
Wenn du z.B. eine neue `GetUserStats()` Funktion im Go Service hinzufügst:
|
||||||
|
|
||||||
|
1. **Funktion schreiben** in `internal/services/stats_service.go`
|
||||||
|
2. **Test erstellen** in `internal/services/stats_service_test.go`:
|
||||||
|
```go
|
||||||
|
func TestGetUserStats_ValidUser_ReturnsStats(t *testing.T) {...}
|
||||||
|
func TestGetUserStats_InvalidUser_ReturnsError(t *testing.T) {...}
|
||||||
|
func TestGetUserStats_NoConsents_ReturnsEmptyStats(t *testing.T) {...}
|
||||||
|
```
|
||||||
|
3. **Tests ausführen**: `go test -v ./internal/services/...`
|
||||||
|
4. **Dokumentation aktualisieren** (siehe documentation.md)
|
||||||
36
.claude/settings.json
Normal file
36
.claude/settings.json
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
{
|
||||||
|
"hooks": {
|
||||||
|
"PostToolUse": [
|
||||||
|
{
|
||||||
|
"matcher": "Edit|Write",
|
||||||
|
"hooks": [
|
||||||
|
{
|
||||||
|
"type": "command",
|
||||||
|
"command": "python3 ~/.claude/hooks/breakpilot-post-edit.py",
|
||||||
|
"timeout": 15000
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Stop": [
|
||||||
|
{
|
||||||
|
"hooks": [
|
||||||
|
{
|
||||||
|
"type": "prompt",
|
||||||
|
"prompt": "Überprüfe ob bei dieser Aufgabe:\n1. Dokumentation aktualisiert werden muss (neue API, neue Funktion, Architektur-Änderung)\n2. Tests geschrieben/aktualisiert werden müssen (neue Funktion, Bug-Fix, Code-Änderung)\n3. Ein ADR (Architecture Decision Record) erstellt werden sollte (neues Modul, Technologiewechsel, signifikante Architektur-Entscheidung)\n\nWenn etwas fehlt, antworte mit {\"decision\": \"block\", \"reason\": \"Fehlend: [Details]\"}\nWenn alles erledigt ist, antworte mit {\"decision\": \"approve\", \"reason\": \"Alle Dokumentation, Tests und ADRs sind aktuell\"}",
|
||||||
|
"timeout": 30000
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"permissions": {
|
||||||
|
"allow": [
|
||||||
|
"Read(docs/**)",
|
||||||
|
"Read(.claude/**)",
|
||||||
|
"Read(backend/tests/**)",
|
||||||
|
"Read(consent-service/**/*_test.go)",
|
||||||
|
"Write(docs/adr/**)"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
145
.gitignore
vendored
Normal file
145
.gitignore
vendored
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
# ============================================
|
||||||
|
# BreakPilot PWA - Git Ignore
|
||||||
|
# ============================================
|
||||||
|
|
||||||
|
# Environment files (keep examples only)
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
*.env.local
|
||||||
|
|
||||||
|
# Keep examples and environment templates
|
||||||
|
!.env.example
|
||||||
|
!.env.dev
|
||||||
|
!.env.staging
|
||||||
|
# .env.prod should NOT be in repo (contains production secrets)
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# Python
|
||||||
|
# ============================================
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
*.so
|
||||||
|
.Python
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
.venv/
|
||||||
|
*.egg-info/
|
||||||
|
.eggs/
|
||||||
|
*.egg
|
||||||
|
.pytest_cache/
|
||||||
|
htmlcov/
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
coverage.xml
|
||||||
|
*.cover
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# Node.js
|
||||||
|
# ============================================
|
||||||
|
node_modules/
|
||||||
|
.next/
|
||||||
|
out/
|
||||||
|
dist/
|
||||||
|
build/
|
||||||
|
.npm
|
||||||
|
.yarn-integrity
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# Go
|
||||||
|
# ============================================
|
||||||
|
*.exe
|
||||||
|
*.exe~
|
||||||
|
*.dll
|
||||||
|
*.dylib
|
||||||
|
*.test
|
||||||
|
*.out
|
||||||
|
vendor/
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# Docker
|
||||||
|
# ============================================
|
||||||
|
# Don't ignore docker-compose files
|
||||||
|
# Ignore volume data if mounted locally
|
||||||
|
backups/
|
||||||
|
*.sql.gz
|
||||||
|
*.sql
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# IDE & Editors
|
||||||
|
# ============================================
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
.project
|
||||||
|
.classpath
|
||||||
|
.settings/
|
||||||
|
*.sublime-workspace
|
||||||
|
*.sublime-project
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# OS Files
|
||||||
|
# ============================================
|
||||||
|
.DS_Store
|
||||||
|
.DS_Store?
|
||||||
|
._*
|
||||||
|
.Spotlight-V100
|
||||||
|
.Trashes
|
||||||
|
ehthumbs.db
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# Secrets & Credentials
|
||||||
|
# ============================================
|
||||||
|
secrets/
|
||||||
|
*.pem
|
||||||
|
*.key
|
||||||
|
*.crt
|
||||||
|
*.p12
|
||||||
|
*.pfx
|
||||||
|
credentials.json
|
||||||
|
service-account.json
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# Logs
|
||||||
|
# ============================================
|
||||||
|
*.log
|
||||||
|
logs/
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# Build Artifacts
|
||||||
|
# ============================================
|
||||||
|
*.zip
|
||||||
|
*.tar.gz
|
||||||
|
*.rar
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# Temporary Files
|
||||||
|
# ============================================
|
||||||
|
tmp/
|
||||||
|
temp/
|
||||||
|
*.tmp
|
||||||
|
*.temp
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# Test Results
|
||||||
|
# ============================================
|
||||||
|
test-results/
|
||||||
|
playwright-report/
|
||||||
|
coverage/
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# ML Models (large files)
|
||||||
|
# ============================================
|
||||||
|
*.pt
|
||||||
|
*.pth
|
||||||
|
*.onnx
|
||||||
|
*.safetensors
|
||||||
|
models/
|
||||||
|
.claude/settings.local.json
|
||||||
Reference in New Issue
Block a user