# BSN Coding Standards — Agenten-Testaufgabe > **Ziel:** Prüfen, ob ein lokaler Agent (z. B. Qwen 3.6) die BSN Coding Standards > selbstständig einhält. Die Aufgabe ist bewusst klein — 15–20 Minuten für einen Agenten. --- ## Aufgabe: Spiel-Bewertungs-Utility Schreibe ein Python-Modul `game_rating.py`, das Brettspiel-Bewertungen validiert und einen Durchschnitt berechnet. Das Modul wird später in einer Flask-API verwendet. ### Anforderungen 1. **Funktion `validate_rating(wert: int) -> bool`:** - Prüft, ob eine Bewertung zwischen 1 und 10 liegt - Wirft `ValueError` bei ungültigen Werten - **Deutscher Docstring (Google-Style) mit Args, Returns, Raises** 2. **Funktion `berechne_durchschnitt(bewertungen: list[int]) -> float | None`:** - Berechnet den Durchschnitt einer Bewertungsliste - Gibt `None` zurück, wenn die Liste leer ist - Jede Bewertung wird vorher mit `validate_rating()` geprüft - **Vollständige Typ-Annotationen** (auch `-> None` wo nötig) 3. **Konstante `MAX_BEWERTUNG`** = 10 (UPPER_SNAKE_CASE) 4. **Tests in `test_game_rating.py`:** - Test für gültige Bewertung - Test für ungültige Bewertung (ValueError) - Test für Durchschnitt mit mehreren Werten - Test für leere Liste (None) - Test für ungültigen Wert in der Liste (ValueError) ### Was der Agent BEACHTEN muss (BSN Standards) | Regel | Woran erkennbar | |---|---| | Typ-Annotationen | Jede Funktion hat `->` mit korrektem Typ | | `X \| None` | `-> float \| None`, NICHT `Optional[float]` | | Deutsche Docstrings | Google-Style, auf Deutsch | | `snake_case` | Funktionen und Variablen | | `UPPER_SNAKE_CASE` | Konstante | | Keine Secrets | Kein API-Key, kein Passwort im Code | | Test-Namen | `test__` | ### Erfolgskriterien (Automatisch prüfbar) ```bash # 1. Linting + Formatierung ruff check game_rating.py test_game_rating.py --fix ruff format game_rating.py test_game_rating.py # 2. Typ-Prüfung mypy game_rating.py test_game_rating.py # 3. Tests pytest test_game_rating.py -v # Alle drei müssen GRÜN sein. ``` ### Abgabe Der Agent soll zwei Dateien erstellen: - `game_rating.py` — die Implementierung - `test_game_rating.py` — die Tests --- ## Erwartete Lösung (Referenz — NICHT dem Agenten zeigen!)
⚠️ Nur zum Nachschlagen — nicht vorab zeigen ```python # game_rating.py """Brettspiel-Bewertungs-Utility. Validiert Bewertungen und berechnet Durchschnittswerte. """ MAX_BEWERTUNG = 10 def validate_rating(wert: int) -> bool: """Prüft, ob eine Bewertung im gültigen Bereich liegt. Args: wert: Die zu prüfende Bewertung (1–10). Returns: True, wenn die Bewertung gültig ist. Raises: ValueError: Wenn die Bewertung außerhalb 1–10 liegt. """ if not isinstance(wert, int): raise ValueError(f"Bewertung muss eine Ganzzahl sein, nicht {type(wert).__name__}") if wert < 1 or wert > MAX_BEWERTUNG: raise ValueError( f"Bewertung {wert} ist ungültig. Erlaubt: 1–{MAX_BEWERTUNG}" ) return True def berechne_durchschnitt(bewertungen: list[int]) -> float | None: """Berechnet den Durchschnitt einer Bewertungsliste. Args: bewertungen: Liste von Bewertungen (je 1–10). Returns: Der Durchschnittswert oder None, wenn die Liste leer ist. Raises: ValueError: Wenn eine Bewertung ungültig ist. """ if not bewertungen: return None for wert in bewertungen: validate_rating(wert) return sum(bewertungen) / len(bewertungen) ``` ```python # test_game_rating.py """Tests für das Spiel-Bewertungs-Utility.""" import pytest from game_rating import MAX_BEWERTUNG, validate_rating, berechne_durchschnitt class TestValidateRating: """Tests für validate_rating().""" def test_valid_rating_returns_true(self) -> None: """Gibt True für eine gültige Bewertung zurück.""" assert validate_rating(5) is True def test_min_rating_returns_true(self) -> None: """Gibt True für die minimale Bewertung (1) zurück.""" assert validate_rating(1) is True def test_max_rating_returns_true(self) -> None: """Gibt True für die maximale Bewertung (10) zurück.""" assert validate_rating(MAX_BEWERTUNG) is True def test_zero_raises_valueerror(self) -> None: """Wirft ValueError bei Bewertung 0.""" with pytest.raises(ValueError, match="ungültig"): validate_rating(0) def test_above_max_raises_valueerror(self) -> None: """Wirft ValueError bei Bewertung > 10.""" with pytest.raises(ValueError, match="ungültig"): validate_rating(11) def test_negative_raises_valueerror(self) -> None: """Wirft ValueError bei negativer Bewertung.""" with pytest.raises(ValueError, match="ungültig"): validate_rating(-1) def test_non_int_raises_valueerror(self) -> None: """Wirft ValueError bei nicht-Integer-Wert.""" with pytest.raises(ValueError, match="Ganzzahl"): validate_rating(3.5) # type: ignore[arg-type] class TestBerechneDurchschnitt: """Tests für berechne_durchschnitt().""" def test_single_rating_returns_same(self) -> None: """Gibt den Wert selbst bei einer einzelnen Bewertung zurück.""" assert berechne_durchschnitt([7]) == 7.0 def test_multiple_ratings_returns_average(self) -> None: """Berechnet den korrekten Durchschnitt mehrerer Bewertungen.""" assert berechne_durchschnitt([6, 8, 10]) == 8.0 def test_empty_list_returns_none(self) -> None: """Gibt None bei leerer Liste zurück.""" assert berechne_durchschnitt([]) is None def test_invalid_rating_in_list_raises_valueerror(self) -> None: """Wirft ValueError, wenn die Liste eine ungültige Bewertung enthält.""" with pytest.raises(ValueError, match="ungültig"): berechne_durchschnitt([5, 0, 7]) def test_float_result(self) -> None: """Gibt Float zurück, auch wenn der Durchschnitt keine ganze Zahl ist.""" result = berechne_durchschnitt([5, 8]) assert result == 6.5 assert isinstance(result, float) ```