deps: earned_time 0.8.0 (tutor credit per active minute); tutor rung tests for minute rows and legacy blocks
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
pre-commit / pre-commit (${{ matrix.group }}) (flutter, flutter-analyze, ) (push) Waiting to run
pre-commit / pre-commit (${{ matrix.group }}) (mypy, mypy, ) (push) Waiting to run
pre-commit / pre-commit (${{ matrix.group }}) (pylint, pylint, ) (push) Waiting to run
pre-commit / pre-commit (${{ matrix.group }}) (rest, , mypy,pylint,flutter-analyze) (push) Waiting to run
Tests / test (push) Waiting to run
Tests / lock-popup-safety (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (0) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (1) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (2) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (3) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (4) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (5) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (6) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (7) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (8) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (9) (push) Waiting to run
Tests / flutter-coverage (push) Blocked by required conditions
Tests / web-coverage (push) Waiting to run
repo-contract / repo contract (push) Waiting to run

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Krzysztof Rudnicki 2026-10-10 18:32:43 +02:00
parent 384ffcda7d
commit 5eb18b92ca
5 changed files with 79 additions and 33 deletions

View File

@ -134,7 +134,7 @@ repos:
# than merely an old one.
- gatelock @ git+https://github.com/kuhyx/utils@gatelock-v0.9.0#subdirectory=gatelock
- crdt-sync @ git+https://github.com/kuhyx/utils@crdt-sync-v0.9.2#subdirectory=crdt-sync
- earned-time @ git+https://github.com/kuhyx/utils@earned-time-v0.7.0#subdirectory=earned_time
- earned-time @ git+https://github.com/kuhyx/utils@earned-time-v0.8.0#subdirectory=earned_time
- freedays @ git+https://github.com/kuhyx/utils@freedays-v0.3.1#subdirectory=freedays
- mcp>=2,<3
- pydantic
@ -155,7 +155,7 @@ repos:
# than merely an old one.
- gatelock @ git+https://github.com/kuhyx/utils@gatelock-v0.9.0#subdirectory=gatelock
- crdt-sync @ git+https://github.com/kuhyx/utils@crdt-sync-v0.9.2#subdirectory=crdt-sync
- earned-time @ git+https://github.com/kuhyx/utils@earned-time-v0.7.0#subdirectory=earned_time
- earned-time @ git+https://github.com/kuhyx/utils@earned-time-v0.8.0#subdirectory=earned_time
- freedays @ git+https://github.com/kuhyx/utils@freedays-v0.3.1#subdirectory=freedays
- mcp>=2,<3
- pydantic

View File

@ -10,7 +10,7 @@ dependencies = [
"gatelock @ git+https://github.com/kuhyx/utils@gatelock-v0.9.0#subdirectory=gatelock",
"crdt-sync @ git+https://github.com/kuhyx/utils@crdt-sync-v0.9.2#subdirectory=crdt-sync",
"freedays @ git+https://github.com/kuhyx/utils@freedays-v0.3.1#subdirectory=freedays",
"earned-time @ git+https://github.com/kuhyx/utils@earned-time-v0.7.0#subdirectory=earned_time",
"earned-time @ git+https://github.com/kuhyx/utils@earned-time-v0.8.0#subdirectory=earned_time",
]
[project.scripts]

View File

@ -4,7 +4,7 @@ bandit==1.9.4
codespell==2.4.3
coverage==7.16.2
crdt-sync @ git+https://github.com/kuhyx/utils@crdt-sync-v0.9.2#subdirectory=crdt-sync
earned-time @ git+https://github.com/kuhyx/utils@earned-time-v0.7.0#subdirectory=earned_time
earned-time @ git+https://github.com/kuhyx/utils@earned-time-v0.8.0#subdirectory=earned_time
freedays @ git+https://github.com/kuhyx/utils@freedays-v0.3.1#subdirectory=freedays
gatelock @ git+https://github.com/kuhyx/utils@gatelock-v0.9.0#subdirectory=gatelock
mcp==2.3.0

View File

@ -7,7 +7,7 @@ the tutor-cutover one (``earners_for``) read, so these hold on either.
from __future__ import annotations
from datetime import date, datetime, time
from datetime import date
import json
import logging
from typing import TYPE_CHECKING, Any
@ -207,31 +207,3 @@ class TestThroughTheShutdownBase:
apply_flat_bonuses_if_new(state, mixin)
mixin._adjust_shutdown_time_by.assert_not_called()
assert not state.exists()
@pytest.mark.usefixtures("key")
def test_the_real_tutor_rung_pays_15_per_block_then_nothing() -> None:
"""The registry's per-block rung (earned_time 0.7.0), through this pass."""
tutor = getattr(earned_time, "AUTOMATION_TUTOR", None)
if tutor is None:
pytest.skip("installed earned_time predates the tutor (< 0.5)")
cutover = earned_time.TUTOR_FROM.isoformat()
state: dict[str, Any] = {}
adjust = MagicMock(return_value=True)
rows: list[dict[str, Any]] = []
for number in range(1, 6):
ended = datetime.combine(earned_time.TUTOR_FROM, time(18, number)).astimezone()
rows.append(
signed(
{
"kind": "credit",
"entry_id": f"s-b{number}",
"day": cutover,
"detail": {"block": number, "ended_at": ended.timestamp()},
}
)
)
write_ledger(tutor, rows)
apply_counted_bonus(state, adjust, tutor, cutover)
assert [c.args[0] for c in adjust.call_args_list] == [15, 15, 15, 15]
assert state["automation_bonus_units"] == {"date": cutover, "units": 5}

View File

@ -0,0 +1,74 @@
"""The real registry's tutor rung through the live pass (earned_time 0.8.0).
Units are credited minutes: a ``detail.minutes`` row pays its minutes, a legacy
block row (no ``detail.minutes``) pays 15, and the rung stops paying at 60.
"""
from __future__ import annotations
from datetime import datetime, time
from typing import TYPE_CHECKING, Any
from unittest.mock import MagicMock
import earned_time
import pytest
from screen_locker._gate_bonus import apply_counted_bonus
from screen_locker.tests._earned_fixtures import signed, signing_key, write_ledger
if TYPE_CHECKING:
from collections.abc import Iterator
from pathlib import Path
@pytest.fixture
def key(tmp_path: Path) -> Iterator[Path]:
with signing_key(tmp_path) as path:
yield path
@pytest.mark.usefixtures("key")
def test_the_real_tutor_rung_pays_legacy_blocks_as_15_minutes_up_to_60() -> None:
"""Legacy block rows (no ``detail.minutes``) pay 15 minutes each (0.8.0)."""
tutor = getattr(earned_time, "AUTOMATION_TUTOR", None)
if tutor is None:
pytest.skip("installed earned_time predates the tutor (< 0.5)")
cutover = earned_time.TUTOR_FROM.isoformat()
state: dict[str, Any] = {}
adjust = MagicMock(return_value=True)
rows: list[dict[str, Any]] = []
for number in range(1, 6):
ended = datetime.combine(earned_time.TUTOR_FROM, time(18, number)).astimezone()
rows.append(
signed(
{
"kind": "credit",
"entry_id": f"s-b{number}",
"day": cutover,
"detail": {"block": number, "ended_at": ended.timestamp()},
}
)
)
write_ledger(tutor, rows)
apply_counted_bonus(state, adjust, tutor, cutover)
assert [c.args[0] for c in adjust.call_args_list] == [15, 15, 15, 15]
# Units are minutes now: five block rows count 75; the cap stops the pay.
assert state["automation_bonus_units"] == {"date": cutover, "units": 75}
@pytest.mark.usefixtures("key")
def test_the_real_tutor_rung_pays_each_credited_minute() -> None:
"""A per-minute row (``detail.minutes``) pays exactly its minutes."""
tutor = getattr(earned_time, "AUTOMATION_TUTOR", None)
if tutor is None:
pytest.skip("installed earned_time predates the tutor (< 0.5)")
cutover = earned_time.TUTOR_FROM.isoformat()
state: dict[str, Any] = {}
adjust = MagicMock(return_value=True)
ended = datetime.combine(earned_time.TUTOR_FROM, time(18, 7)).astimezone()
detail = {"block": 7, "minutes": 7, "ended_at": ended.timestamp()}
row = {"kind": "credit", "entry_id": "s-m7", "day": cutover, "detail": detail}
write_ledger(tutor, [signed(row)])
apply_counted_bonus(state, adjust, tutor, cutover)
adjust.assert_called_once_with(7)
assert state["automation_bonus_units"] == {"date": cutover, "units": 7}