Migrate all repos into monorepo context folders

Bahn: aisupport, Analyse-O2C-C2S, awesome-bahn-mcp-servers, beam-mcp,
      Confluence_Bot, db-planet-mcp-server, O2C-Harness, project-audit,
      Projekt-KIQ-HP, teamlandkarte-mcp
Dhive: Jury-Voting
Privat: CV, NoteGraph (NOTE: NoteGraph needs complete redo after consolidation)
Shared: AI-Orchestrator, OrgMyLife, power_skills_and_more
Shared/references: symphony (read-only)

Bahn repos remain available as independent remotes - this monorepo
pulls them in via subtree, the originals are untouched.
This commit is contained in:
2026-06-30 20:39:52 +02:00
parent 2f2b295531
commit a5f8fb49ab
1717 changed files with 447332 additions and 0 deletions
@@ -0,0 +1,236 @@
---
name: pytest-hypothesis
description: >
Erstellt und verbessert Unit Tests in Python mit pytest und hypothesis (Property-Based Testing).
Nutze diesen Skill wenn der User Tests für Python-Code schreiben, verbessern oder die
Test-Coverage erhöhen möchte. Umfasst pytest-Patterns, hypothesis-Strategien, Fixtures,
parametrisierte Tests und Coverage-Optimierung.
Keywords: pytest, hypothesis, unit test, property-based, test coverage, python test, fixture, parametrize.
compatibility: >
Python 3.12+ mit pytest (>=8.0), pytest-asyncio (>=1.0), hypothesis (>=6.100).
Optional: pytest-cov für Coverage-Reports.
metadata:
version: "1.0.0"
author: "#Einfachbahn Lab"
---
# Python Testing mit pytest + hypothesis
Skill für qualitativ hochwertige Python-Tests: klassische Unit Tests mit pytest und
Property-Based Tests mit hypothesis.
## Grundprinzipien
- **pytest** für determinstische Tests mit konkreten Ein-/Ausgaben
- **hypothesis** für Property-Based Tests: automatisch generierte Eingaben prüfen Invarianten
- Beide ergänzen sich: pytest für bekannte Szenarien, hypothesis für unerwartete Edge Cases
## Ablauf
### Schritt 1: Kontext erfassen
1. **Zu testende Komponente** — Welche Datei/Klasse/Funktion?
2. **Vorhandene Tests?** — Gibt es bereits Tests, die ergänzt werden sollen?
3. **Async?** — Nutzt der Code `async/await`?
4. **Externe Abhängigkeiten?** — Welche müssen gemockt werden?
### Schritt 2: Test-Typ wählen
| Situation | Test-Typ |
|-----------|----------|
| Konkrete Ein-/Ausgabe bekannt | pytest (klassisch) |
| Invariante/Eigenschaft prüfbar | hypothesis (property-based) |
| Async I/O | pytest-asyncio |
| Mehrere ähnliche Fälle | `@pytest.mark.parametrize` |
| Komplexes Setup | Fixtures (`@pytest.fixture`) |
### Schritt 3: Tests implementieren
## Conventions
### Dateistruktur
```
tests/
├── __init__.py
├── test_<modul>.py # Klassische Tests
├── test_<modul>_properties.py # Property-Based Tests (Suffix!)
└── conftest.py # Shared Fixtures
```
### Namenskonvention
```python
# Klassischer Test
def test_<funktion>_<szenario>_<erwartung>():
...
# Property-Based Test
@given(...)
def test_<funktion>_<eigenschaft>(value):
...
```
## pytest Patterns
### AAA-Pattern (Arrange-Act-Assert)
```python
def test_calculate_total_with_valid_items_returns_sum():
# Arrange
items = [Item(price=10.0), Item(price=20.0)]
calculator = PriceCalculator()
# Act
result = calculator.total(items)
# Assert
assert result == 30.0
```
### Parametrize
```python
@pytest.mark.parametrize("input_val,expected", [
("valid@email.de", True),
("invalid", False),
("", False),
("a@b.c", True),
])
def test_validate_email(input_val, expected):
assert validate_email(input_val) == expected
```
### Fixtures
```python
@pytest.fixture
def sample_config():
"""Liefert eine Test-Konfiguration."""
return Config(
endpoint="https://test.example.com",
api_key="test-key-123",
timeout_ms=5000,
)
def test_client_uses_config_endpoint(sample_config):
client = Client(sample_config)
assert client.base_url == "https://test.example.com"
```
### Async Tests
```python
import pytest
@pytest.mark.asyncio
async def test_fetch_data_returns_items():
client = AsyncClient(base_url="https://test.example.com")
result = await client.fetch_items()
assert len(result) > 0
```
### Mocking
```python
from unittest.mock import AsyncMock, patch
@pytest.mark.asyncio
async def test_orchestrator_dispatches_task():
mock_tracker = AsyncMock()
mock_tracker.get_ready_tasks.return_value = [task]
orchestrator = Orchestrator(tracker=mock_tracker)
await orchestrator.poll()
mock_tracker.move_task.assert_called_once_with(task.id, "in_progress")
```
## hypothesis Patterns
### Einfache Strategien
```python
from hypothesis import given, settings, assume
from hypothesis import strategies as st
@given(st.integers(), st.integers())
def test_addition_is_commutative(a, b):
assert add(a, b) == add(b, a)
@given(st.text(min_size=1))
def test_parse_never_crashes(text):
# Darf Exception werfen, aber nie crashen
try:
parse(text)
except ValidationError:
pass # Erwartetes Verhalten
```
### Composite Strategien (eigene Datentypen)
```python
@st.composite
def config_strategy(draw):
"""Generiert zufällige aber valide Config-Objekte."""
return Config(
endpoint=draw(st.from_regex(r"https://[a-z]+\.example\.com", fullmatch=True)),
api_key=draw(st.text(min_size=8, max_size=64, alphabet=st.characters(whitelist_categories=("L", "N")))),
timeout_ms=draw(st.integers(min_value=100, max_value=30000)),
)
@given(config_strategy())
def test_config_serialization_roundtrip(config):
serialized = config.to_dict()
restored = Config.from_dict(serialized)
assert restored == config
```
### Settings anpassen
```python
from hypothesis import settings, Phase
@settings(
max_examples=200, # Mehr Beispiele für kritische Funktionen
deadline=None, # Kein Timeout für langsame Tests
)
@given(st.lists(st.integers()))
def test_sort_produces_sorted_output(lst):
result = custom_sort(lst)
assert all(result[i] <= result[i+1] for i in range(len(result) - 1))
```
### Typische Properties (Invarianten)
| Property | Beispiel |
|----------|----------|
| Roundtrip | `deserialize(serialize(x)) == x` |
| Idempotenz | `f(f(x)) == f(x)` |
| Kommutativität | `f(a, b) == f(b, a)` |
| Monotonie | `a <= b → f(a) <= f(b)` |
| Invariante | `len(filter(lst)) <= len(lst)` |
| Keine Crashes | `f(random_input)` wirft keinen unerwarteten Error |
## Coverage
### Coverage messen
```bash
pytest --cov=src --cov-report=html --cov-report=term-missing
```
### Ziele
- **Gesamtprojekt:** ≥ 80% Line Coverage
- **Domain-Logik:** ≥ 90% Branch Coverage
- **Integrationspunkte:** Property-Based Tests für Serialisierung/Parsing
## Checkliste
- [ ] Alle öffentlichen Funktionen/Methoden haben mindestens einen Test
- [ ] Positive und negative Szenarien abgedeckt
- [ ] Edge Cases: leere Listen, None, Grenzwerte
- [ ] Property-Based Tests für Serialisierung/Roundtrips
- [ ] Async-Code mit `@pytest.mark.asyncio` getestet
- [ ] Externe Abhängigkeiten gemockt (kein Netzwerk/DB in Unit Tests)
- [ ] Testnamen beschreiben das erwartete Verhalten
- [ ] Coverage ≥ 80%
## Troubleshooting
| Problem | Lösung |
|---------|--------|
| `hypothesis.errors.DeadlineExceeded` | `@settings(deadline=None)` oder Code beschleunigen |
| `hypothesis.errors.Flaky` | Test ist nicht deterministisch → externe Abhängigkeit mocken |
| `pytest.mark.asyncio` nicht erkannt | `pytest-asyncio` installieren, `asyncio_mode = "auto"` in pyproject.toml |
| Fixtures nicht gefunden | In `conftest.py` verschieben (gleiche oder übergeordnete Ebene) |
| Coverage zu niedrig | Branch-Report prüfen: `--cov-branch`, fehlende Error-Pfade testen |