"""The 2026-08-26 formula change, verified over the wire. ``test_annual_leave_calculator.py`` pins the pure function. This file pins the part the pure function cannot reach: that ``basis_cap_applied`` survives the schema, the INSERT, the new migration-550 columns and the history replay — i.e. the exact JSON the calculator page reads to decide whether to render the 「已按本人年假基数封顶」 tag. That round trip is the failure mode worth a test. A flag that is correct in ``calculate()`` and lost in ``_record_to_response`` looks like a working calculator that silently stops explaining itself. """ from __future__ import annotations from uuid import uuid4 import pytest import pytest_asyncio from app.auth.models import AuthUserRole from app.core.security import create_access_token from app.identity.person_models import IdentityPerson # Leap fiscal year 2027 (2027-04-01 → 2028-03-31, 366 days). # N=17 → legal basis 10, S=12 → welfare basis 5. No N or S anniversary # lands inside the window, so this is a single clean segment. _LEAP_INPUTS = { "work_start_date": "2010-01-01", "hire_date": "2015-01-01", "termination_date": "2028-03-31", "carry_legal_days": "0", "carry_welfare_days": "0", "used_legal_days": "0", "used_welfare_days": "0", } def _bearer(token: str) -> dict[str, str]: return {"Authorization": f"Bearer {token}"} @pytest_asyncio.fixture async def hr_caller(db_session): """HR-role user — ``POST /calculate`` is ``require_hr_admin()``.""" user_id = uuid4() db_session.add( IdentityPerson( id=user_id, work_email="hr-calc-contract@test.com", display_name="HR Calc Contract", employment_status="active", source_system="successfactors", hris_person_id="sf-hr-calc-contract", ) ) await db_session.flush() from sqlalchemy import text as _text role_id = ( await db_session.execute( _text("SELECT id FROM auth_roles WHERE name = 'hr'") ) ).scalar_one() db_session.add(AuthUserRole(user_id=user_id, role_id=role_id)) await db_session.flush() await db_session.commit() return user_id, create_access_token(str(user_id), ["hr"]) @pytest.mark.asyncio async def test_basis_cap_flag_reaches_the_client_and_survives_replay( client, hr_caller, ): _, token = hr_caller r = await client.post( "/api/v1/hr-tools/annual-leave/calculate", json=_LEAP_INPUTS, headers=_bearer(token), ) assert r.status_code == 200, r.text body = r.json() assert body["success"] is True data = body["data"] entitlement = data["entitlement"] # The 365.25 denominator is visible to HR in the formula string they # read back to the employee, so it is part of the contract. assert entitlement["segments"][0]["legal_formula"] == "366/365.25×10" # Pre-cap and post-cap are BOTH exposed — the page shows 取整后 and # 封顶后最终值 side by side, and collapsing them hides the step HR # has to explain. assert entitlement["legal_rounded"] == "10.5" assert entitlement["legal_capped"] == "10.0" assert entitlement["basis_cap_applied"] is True # Distinct flags: 10 + 5 = 15 exactly, so the 15-day cap is NOT what # happened here and must not claim it was. assert entitlement["cap_applied"] is False assert data["settlement"]["basis_cap_applied"] is True # Replay the persisted row: the flag must come back from the # migration-550 columns, not be recomputed. record_id = data["id"] r2 = await client.get( f"/api/v1/hr-tools/annual-leave/history/{record_id}", headers=_bearer(token), ) assert r2.status_code == 200, r2.text replayed = r2.json()["data"] assert replayed["entitlement"]["basis_cap_applied"] is True assert replayed["settlement"]["basis_cap_applied"] is True assert replayed["entitlement"]["legal_capped"] == "10.0" assert replayed["entitlement"]["cap_applied"] is False @pytest.mark.asyncio async def test_ordinary_year_reports_no_cap_at_all(client, hr_caller): """The cap is inert outside the leap case — the tag must not appear on the overwhelming majority of settlements.""" _, token = hr_caller r = await client.post( "/api/v1/hr-tools/annual-leave/calculate", json={**_LEAP_INPUTS, "termination_date": "2027-03-31"}, headers=_bearer(token), ) assert r.status_code == 200, r.text entitlement = r.json()["data"]["entitlement"] assert entitlement["segments"][0]["legal_formula"] == "365/365.25×10" # A full non-leap year still yields the whole basis, not 9.5. assert entitlement["legal_capped"] == "10.0" assert entitlement["basis_cap_applied"] is False assert entitlement["cap_applied"] is False