95 lines
3.2 KiB
Python
95 lines
3.2 KiB
Python
"""Tests für Namensbereinigung und Plausibilitätsprüfung (ohne Flask)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import pytest
|
|
|
|
from app.config import GAME_RULE_DEFAULTS
|
|
from app.validation import (
|
|
NAME_MAX_LENGTH,
|
|
PlausibilityRules,
|
|
ScoreSubmission,
|
|
SubmissionError,
|
|
check_plausibility,
|
|
parse_submission,
|
|
sanitize_name,
|
|
)
|
|
|
|
RULES = PlausibilityRules.from_config(GAME_RULE_DEFAULTS)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("raw", "expected"),
|
|
[
|
|
("Ada", "Ada"),
|
|
(" Max Muster ", "Max Muster"),
|
|
("Jürg Größli", "Jürg Größli"),
|
|
("Fullwidth", "Fullwidth"),
|
|
("Null\x00Byte", "NullByte"),
|
|
("Zero\u200bWidth", "ZeroWidth"),
|
|
("Bidi\u202eTrick", "BidiTrick"),
|
|
("Tab\tNeu\nZeile", "TabNeuZeile"),
|
|
("<script>alert(1)</script>", "scriptalert1script"),
|
|
("O'Neil-Test!?", "O'Neil-Test!?"),
|
|
("Emoji 🚀 Held", "Emoji Held"),
|
|
("", ""),
|
|
],
|
|
)
|
|
def test_sanitize_name(raw: str, expected: str) -> None:
|
|
assert sanitize_name(raw) == expected
|
|
|
|
|
|
def _payload(**overrides: object) -> dict:
|
|
payload: dict = {"name": "Ada", "score": 100, "wave": 2, "duration_s": 60, "run_token": "t"}
|
|
payload.update(overrides)
|
|
return payload
|
|
|
|
|
|
def test_parse_submission_valid() -> None:
|
|
submission = parse_submission(_payload(name=" Ada "), RULES)
|
|
assert submission == ScoreSubmission("Ada", 100, 2, 60, "t")
|
|
|
|
|
|
def test_parse_submission_name_exactly_max_length() -> None:
|
|
name = "N" * NAME_MAX_LENGTH
|
|
assert parse_submission(_payload(name=name), RULES).name == name
|
|
|
|
|
|
def test_parse_submission_collects_errors() -> None:
|
|
with pytest.raises(SubmissionError) as info:
|
|
parse_submission(_payload(name="", score=-1, wave=0), RULES)
|
|
assert set(info.value.fields) == {"name", "score", "wave"}
|
|
|
|
|
|
def _submission(score: int, wave: int, duration_s: int) -> ScoreSubmission:
|
|
return ScoreSubmission("Ada", score, wave, duration_s, "t")
|
|
|
|
|
|
def test_plausible_submission() -> None:
|
|
assert check_plausibility(_submission(2_000, 3, 120), elapsed_s=150, rules=RULES) is None
|
|
|
|
|
|
def test_duration_may_be_shorter_than_elapsed_because_of_pauses() -> None:
|
|
assert check_plausibility(_submission(500, 2, 60), elapsed_s=3_000, rules=RULES) is None
|
|
|
|
|
|
def test_duration_longer_than_elapsed_is_implausible() -> None:
|
|
assert check_plausibility(_submission(500, 2, 600), elapsed_s=100, rules=RULES) is not None
|
|
|
|
|
|
def test_duration_within_tolerance_is_accepted() -> None:
|
|
tolerance = RULES.duration_tolerance_seconds
|
|
assert check_plausibility(_submission(500, 2, 100 + tolerance), 100, RULES) is None
|
|
|
|
|
|
def test_score_ceiling_depends_on_duration() -> None:
|
|
ceiling = RULES.score_flat_allowance + 100 * RULES.score_max_points_per_second
|
|
assert check_plausibility(_submission(ceiling, 5, 100), 200, RULES) is None
|
|
assert check_plausibility(_submission(ceiling + 1, 5, 100), 200, RULES) is not None
|
|
|
|
|
|
def test_high_wave_needs_minimum_time() -> None:
|
|
assert check_plausibility(_submission(10, 50, 10), 500, RULES) is not None
|
|
min_time = 49 * RULES.wave_min_seconds
|
|
assert check_plausibility(_submission(10, 50, min_time), min_time + 10, RULES) is None
|