86 lines
2.6 KiB
Python
86 lines
2.6 KiB
Python
from dataclasses import dataclass
|
|
from datetime import date, timedelta
|
|
|
|
import pytest
|
|
|
|
from app.services.calculations import (
|
|
aggregate_week,
|
|
automatic_break_minutes,
|
|
compute_net_minutes,
|
|
cumulative_delta,
|
|
iso_week_bounds,
|
|
parse_time_to_minutes,
|
|
required_break_minutes_for_span,
|
|
)
|
|
|
|
|
|
@dataclass
|
|
class FakeEntry:
|
|
date: date
|
|
start_minutes: int
|
|
end_minutes: int
|
|
break_minutes: int
|
|
|
|
|
|
def test_compute_net_minutes_and_validation() -> None:
|
|
assert compute_net_minutes(8 * 60, 15 * 60, 30) == 390
|
|
|
|
with pytest.raises(ValueError):
|
|
compute_net_minutes(8 * 60, 8 * 60, 0)
|
|
|
|
with pytest.raises(ValueError):
|
|
compute_net_minutes(8 * 60, 12 * 60, 300)
|
|
|
|
|
|
def test_parse_time_to_minutes() -> None:
|
|
assert parse_time_to_minutes("08:30") == 510
|
|
|
|
with pytest.raises(ValueError):
|
|
parse_time_to_minutes("8:30")
|
|
|
|
|
|
def test_required_break_minutes_follow_german_thresholds() -> None:
|
|
assert required_break_minutes_for_span(6 * 60) == 0
|
|
assert required_break_minutes_for_span((6 * 60) + 1) == 30
|
|
assert required_break_minutes_for_span(9 * 60) == 30
|
|
assert required_break_minutes_for_span((9 * 60) + 1) == 45
|
|
|
|
|
|
def test_automatic_break_minutes_uses_work_span() -> None:
|
|
assert automatic_break_minutes(8 * 60, 14 * 60) == 0
|
|
assert automatic_break_minutes(8 * 60, (14 * 60) + 1) == 30
|
|
assert automatic_break_minutes(8 * 60, 17 * 60) == 30
|
|
assert automatic_break_minutes(8 * 60, (17 * 60) + 1) == 45
|
|
|
|
with pytest.raises(ValueError):
|
|
automatic_break_minutes(8 * 60, 8 * 60)
|
|
|
|
|
|
def test_week_aggregation_and_delta() -> None:
|
|
week_start, _ = iso_week_bounds(date(2026, 2, 25))
|
|
entries = [
|
|
FakeEntry(date=week_start, start_minutes=8 * 60, end_minutes=13 * 60, break_minutes=15),
|
|
FakeEntry(date=week_start + timedelta(days=1), start_minutes=9 * 60, end_minutes=14 * 60, break_minutes=30),
|
|
]
|
|
|
|
report = aggregate_week(entries, week_start, weekly_target_minutes=1500)
|
|
|
|
assert report["weekly_ist"] == (285 + 270)
|
|
assert report["weekly_soll"] == 1500
|
|
assert report["weekly_delta"] == -945
|
|
|
|
|
|
def test_cumulative_delta_multiple_weeks() -> None:
|
|
first_week_start, _ = iso_week_bounds(date(2026, 2, 2))
|
|
second_week_start = first_week_start + timedelta(days=7)
|
|
|
|
entries = [
|
|
FakeEntry(date=first_week_start, start_minutes=8 * 60, end_minutes=13 * 60, break_minutes=0),
|
|
FakeEntry(date=second_week_start, start_minutes=8 * 60, end_minutes=16 * 60, break_minutes=30),
|
|
]
|
|
|
|
result = cumulative_delta(entries, second_week_start, weekly_target_minutes=1500)
|
|
|
|
# Woche 1: 300 - 1500, Woche 2: 450 - 1500
|
|
assert result == -2250
|