014503e89b
The planner knew exactly how many semesterdagar a parent was about to forfeit and never placed a single day to prevent it. It also had no weekend-wrap logic at all: REASON_VACATION_WRAP_WEEKEND was stamped on every tier-3 interior vacation day, including a lone Tuesday in February. Two changes. Spend better. A workday adjacent to a free stretch, or in the middle of a gap of three or fewer fenced by free days, now takes vacation -- _vacation_bridge_reason, consulted for every parent type. This departs from spec 6.1's "wrapping suppressed for hourly": the FP-preservation argument is salary-type independent, only the pay argument was hourly-moot. In a both-parents window, both now take vacation on such a date rather than one paying with FP. After funding, reallocate_vacation_by_value swaps vacation onto higher-ranked dates 1-for-1 with FP days, so which dates hold vacation improves while how many never drops -- greedy first, re-timed after, never withheld. An invariant test asserts the count can only rise. Name the losses. A new pass proposes concrete dates for vacation that would otherwise expire, current year only, ranked red-day bridge then weekend bridge then interior, later dates first. They ride on PlanProposal.rescue_days, kept off `days` so nothing downstream commits them, and surface on the Dashboard with accept-one, accept-all (one batched /api/plan/apply, not N) and dismiss. Dismissal reuses the tip_dismissal table, keyed on (parent, year) identity rather than the group's dates -- those are recomputed from the whole plan's residual on every propose, so date-hashing made dismissal self-defeating. Dismiss does not bump the plan version; it changes no data and must not trigger a write. Measured on the seeded demo: 12 forfeiting days for one parent now yield 12 proposed dates, all Fridays or Mondays. Total vacation placed is unchanged at 203 days, confirming the reallocation only re-times. Three defects here were found by driving a browser, while 2273 unit tests passed -- all three lived in the seam between layers that unit tests mock away. Adds API-level tests over the real request sequence. Rate limiting is now off in tests (one file consumed the whole registration budget) with the suite's first real 429 assertion. PROPOSE_VS_SYNC_BUG.md documents a pre-existing, unfixed issue found along the way: Dashboard figures swing on the first sync with no user action. Backend 946 tests, frontend 1341, coverage above all gates. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YZaX6xbuMEMXuyKA1fq2Mm
536 lines
19 KiB
Python
536 lines
19 KiB
Python
"""Pytest fixtures.
|
|
|
|
Each test gets a fresh in-memory SQLite connection with the FULL schema applied
|
|
(all 9 tables), so all entities can be tested in isolation without Alembic.
|
|
Follow the `vacation_repo` / `vacation_service` pattern to add fixtures for
|
|
other entities.
|
|
"""
|
|
|
|
from collections.abc import AsyncIterator, Iterator
|
|
from dataclasses import dataclass
|
|
from datetime import date, datetime
|
|
from uuid import UUID, uuid4
|
|
|
|
import aiosqlite
|
|
import pytest
|
|
import pytest_asyncio
|
|
from httpx import ASGITransport, AsyncClient
|
|
|
|
from app.db.connection import ensure_schema
|
|
from app.dependencies import get_connection, get_current_user
|
|
from app.entities.child import Child
|
|
from app.entities.personal_data import PersonalData
|
|
from app.entities.user import User
|
|
from app.entities.workspace import Workspace
|
|
from app.main import app
|
|
from app.rate_limit import limiter
|
|
from app.repositories_impl.sqlite.child_repository import SQLiteChildRepository
|
|
from app.repositories_impl.sqlite.day_override_repository import SQLiteDayOverrideRepository
|
|
from app.repositories_impl.sqlite.leave_wish_repository import SQLiteLeaveWishRepository
|
|
from app.repositories_impl.sqlite.parental_leave_repository import SQLiteParentalLeaveRepository
|
|
from app.repositories_impl.sqlite.personal_data_repository import SQLitePersonalDataRepository
|
|
from app.repositories_impl.sqlite.planned_day_exclusion_repository import (
|
|
SQLitePlannedDayExclusionRepository,
|
|
)
|
|
from app.repositories_impl.sqlite.planned_leave_repository import SQLitePlannedLeaveRepository
|
|
from app.repositories_impl.sqlite.preschool_planning_days_repository import (
|
|
SQLitePreschoolPlanningDaysRepository,
|
|
)
|
|
from app.repositories_impl.sqlite.tip_dismissal_repository import SQLiteTipDismissalRepository
|
|
from app.repositories_impl.sqlite.unit_of_work import SQLiteUnitOfWork
|
|
from app.repositories_impl.sqlite.user_repository import SQLiteUserRepository
|
|
from app.repositories_impl.sqlite.vacation_days_repository import (
|
|
SQLiteVacationDaysRepository,
|
|
)
|
|
from app.repositories_impl.sqlite.workspace_repository import (
|
|
SQLiteWorkspaceInvitationRepository,
|
|
SQLiteWorkspaceRepository,
|
|
)
|
|
from app.services.allocation_service import AllocationInputLoader, PlanningEngineService
|
|
from app.services.analytics_service import AnalyticsService
|
|
from app.services.auth_service import AuthService
|
|
from app.services.child_service import ChildService
|
|
from app.services.day_override_service import DayOverrideService
|
|
from app.services.leave_wish_service import LeaveWishService
|
|
from app.services.planned_day_exclusion_service import PlannedDayExclusionService
|
|
from app.services.planning_service import PlanningService
|
|
from app.services.preschool_planning_days_service import PreschoolPlanningDaysService
|
|
from app.services.tips_service import TipsService
|
|
from app.services.vacation_days_service import VacationDaysService
|
|
from app.services.workspace_service import WorkspaceService
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Rate limiting — OFF by default for the whole test session.
|
|
#
|
|
# `limiter` (app/rate_limit.py) keys on remote address via slowapi's
|
|
# `get_remote_address`, which assumes one IP == one client. Every test in a
|
|
# pytest run shares the same `app.main.app` instance and drives it through
|
|
# `httpx.ASGITransport`, so the whole session looks like one client to the
|
|
# limiter — the same problem `frontend/playwright.config.ts` already works
|
|
# around for the E2E suite via `APP_RATE_LIMIT_ENABLED=false`. Left on here,
|
|
# `tests/integration/test_user_journey.py` alone makes exactly ten
|
|
# `/api/auth/register` calls, spending the entire 10/minute budget, so any
|
|
# other integration test that registers a user 429s if it happens to run
|
|
# afterwards in the same process — a suite that was only green by accident
|
|
# of file ordering.
|
|
#
|
|
# We flip the already-constructed `limiter` object directly rather than
|
|
# `settings.rate_limit_enabled`: `Limiter(..., enabled=...)` reads that
|
|
# setting once at import time in app/rate_limit.py, so mutating `settings`
|
|
# afterwards would no longer reach the singleton the app actually calls.
|
|
# Production is unaffected — `settings.rate_limit_enabled` still defaults to
|
|
# True, so a deployed process still builds its limiter enabled; only this
|
|
# pytest process ever runs the line below.
|
|
limiter.enabled = False
|
|
|
|
|
|
@pytest.fixture
|
|
def rate_limiting_enabled() -> Iterator[None]:
|
|
"""Opt-in fixture for a test that specifically exercises rate limiting.
|
|
|
|
Turns the shared limiter back on and clears its in-memory counters
|
|
first — in case some earlier test's traffic ever slipped through while
|
|
it was "enabled" — then restores both the disabled default and a clean
|
|
counter set afterwards. `limiter` is a process-global in-memory
|
|
limiter, so without this a test that enables it would leak state into
|
|
whichever test runs next.
|
|
"""
|
|
limiter.reset()
|
|
limiter.enabled = True
|
|
try:
|
|
yield
|
|
finally:
|
|
limiter.enabled = False
|
|
limiter.reset()
|
|
|
|
|
|
@dataclass
|
|
class SeededIds:
|
|
workspace_id: UUID
|
|
personal_data_uuid: UUID
|
|
child_id: UUID
|
|
|
|
|
|
@pytest_asyncio.fixture
|
|
async def connection() -> AsyncIterator[aiosqlite.Connection]:
|
|
conn = await aiosqlite.connect(":memory:")
|
|
conn.row_factory = aiosqlite.Row
|
|
await conn.execute("PRAGMA foreign_keys = ON;")
|
|
await ensure_schema(conn)
|
|
try:
|
|
yield conn
|
|
finally:
|
|
await conn.close()
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# VacationDays (reference slice — keep unchanged)
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@pytest_asyncio.fixture
|
|
async def vacation_repo(
|
|
connection: aiosqlite.Connection,
|
|
) -> SQLiteVacationDaysRepository:
|
|
return SQLiteVacationDaysRepository(connection)
|
|
|
|
|
|
@pytest_asyncio.fixture
|
|
async def vacation_service(
|
|
vacation_repo: SQLiteVacationDaysRepository,
|
|
) -> VacationDaysService:
|
|
return VacationDaysService(vacation_repo)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Auth
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@pytest_asyncio.fixture
|
|
async def user_repo(connection: aiosqlite.Connection) -> SQLiteUserRepository:
|
|
return SQLiteUserRepository(connection)
|
|
|
|
|
|
@pytest_asyncio.fixture
|
|
async def personal_data_repo(
|
|
connection: aiosqlite.Connection,
|
|
) -> SQLitePersonalDataRepository:
|
|
return SQLitePersonalDataRepository(connection)
|
|
|
|
|
|
@pytest_asyncio.fixture
|
|
async def auth_service(
|
|
user_repo: SQLiteUserRepository,
|
|
personal_data_repo: SQLitePersonalDataRepository,
|
|
workspace_repo: SQLiteWorkspaceRepository,
|
|
invitation_repo: SQLiteWorkspaceInvitationRepository,
|
|
) -> AuthService:
|
|
return AuthService(user_repo, personal_data_repo, workspace_repo, inv_repo=invitation_repo)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Workspace
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@pytest_asyncio.fixture
|
|
async def workspace_repo(
|
|
connection: aiosqlite.Connection,
|
|
) -> SQLiteWorkspaceRepository:
|
|
return SQLiteWorkspaceRepository(connection)
|
|
|
|
|
|
@pytest_asyncio.fixture
|
|
async def invitation_repo(
|
|
connection: aiosqlite.Connection,
|
|
) -> SQLiteWorkspaceInvitationRepository:
|
|
return SQLiteWorkspaceInvitationRepository(connection)
|
|
|
|
|
|
@pytest_asyncio.fixture
|
|
async def workspace_service(
|
|
workspace_repo: SQLiteWorkspaceRepository,
|
|
invitation_repo: SQLiteWorkspaceInvitationRepository,
|
|
personal_data_repo: SQLitePersonalDataRepository,
|
|
user_repo: SQLiteUserRepository,
|
|
leave_wish_repo: SQLiteLeaveWishRepository,
|
|
planned_leave_repo: SQLitePlannedLeaveRepository,
|
|
parental_leave_repo: SQLiteParentalLeaveRepository,
|
|
vacation_repo: SQLiteVacationDaysRepository,
|
|
) -> WorkspaceService:
|
|
return WorkspaceService(
|
|
workspace_repo,
|
|
invitation_repo,
|
|
personal_data_repo,
|
|
user_repo,
|
|
leave_wish_repo,
|
|
planned_leave_repo,
|
|
parental_leave_repo,
|
|
vacation_repo,
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Shared FK seed — used by any test that needs real workspace + personal_data rows
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@pytest_asyncio.fixture
|
|
async def seeded_ids(
|
|
workspace_repo: SQLiteWorkspaceRepository,
|
|
personal_data_repo: SQLitePersonalDataRepository,
|
|
child_repo: SQLiteChildRepository,
|
|
) -> SeededIds:
|
|
"""Pre-create a workspace + personal_data + child row so FK constraints pass."""
|
|
ws = await workspace_repo.create(
|
|
Workspace(id=uuid4(), name="Test WS", created_at=datetime.now())
|
|
)
|
|
pd = await personal_data_repo.create(
|
|
PersonalData(
|
|
uuid=uuid4(), workspace_id=ws.id, name="Test Parent", created_at=datetime.now()
|
|
)
|
|
)
|
|
child = await child_repo.create(
|
|
Child(
|
|
id=uuid4(),
|
|
workspace_id=ws.id,
|
|
name="Test Child",
|
|
birth_date=date(2024, 1, 1),
|
|
created_at=datetime.now(),
|
|
)
|
|
)
|
|
return SeededIds(workspace_id=ws.id, personal_data_uuid=pd.uuid, child_id=child.id)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Shared authenticated HTTP client — bound to `seeded_ids`'s workspace.
|
|
#
|
|
# T1 authz work: every router now rejects ids outside the caller's workspace,
|
|
# so a router test's `client` must be authenticated as a real member of
|
|
# whatever workspace the test operates on. This fixture is that identity for
|
|
# any router test built around `seeded_ids` (see `tests/unit/test_fetch_data_
|
|
# router.py`). Router tests that seed their own bespoke workspace per test
|
|
# (e.g. `test_plan_router.py`) still define their own local `client`/current-
|
|
# user fixture bound to THAT workspace instead — a single global identity
|
|
# can't satisfy both shapes.
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@pytest_asyncio.fixture
|
|
async def current_user(seeded_ids: SeededIds) -> User:
|
|
"""A real, logged-in user whose PersonalData is `seeded_ids.personal_data_uuid`."""
|
|
return User(
|
|
id=uuid4(),
|
|
personal_data_uuid=seeded_ids.personal_data_uuid,
|
|
email="seeded-user@example.com",
|
|
hashed_password="not-a-real-hash",
|
|
created_at=datetime.now(),
|
|
)
|
|
|
|
|
|
@pytest_asyncio.fixture
|
|
async def authed_client(
|
|
connection: aiosqlite.Connection, current_user: User
|
|
) -> AsyncIterator[AsyncClient]:
|
|
"""An httpx client against the real app, authenticated as `current_user`
|
|
and wired to the test's in-memory `connection` — the same technique
|
|
every router test file uses, consolidated here for tests keyed off
|
|
`seeded_ids`.
|
|
"""
|
|
app.dependency_overrides[get_connection] = lambda: connection
|
|
app.dependency_overrides[get_current_user] = lambda: current_user
|
|
try:
|
|
async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as ac:
|
|
yield ac
|
|
finally:
|
|
app.dependency_overrides.pop(get_connection, None)
|
|
app.dependency_overrides.pop(get_current_user, None)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Child & leave
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@pytest_asyncio.fixture
|
|
async def child_repo(connection: aiosqlite.Connection) -> SQLiteChildRepository:
|
|
return SQLiteChildRepository(connection)
|
|
|
|
|
|
@pytest_asyncio.fixture
|
|
async def parental_leave_repo(
|
|
connection: aiosqlite.Connection,
|
|
) -> SQLiteParentalLeaveRepository:
|
|
return SQLiteParentalLeaveRepository(connection)
|
|
|
|
|
|
@pytest_asyncio.fixture
|
|
async def planned_leave_repo(
|
|
connection: aiosqlite.Connection,
|
|
) -> SQLitePlannedLeaveRepository:
|
|
return SQLitePlannedLeaveRepository(connection)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# LeaveWish
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@pytest_asyncio.fixture
|
|
async def leave_wish_repo(
|
|
connection: aiosqlite.Connection,
|
|
) -> SQLiteLeaveWishRepository:
|
|
return SQLiteLeaveWishRepository(connection)
|
|
|
|
|
|
@pytest_asyncio.fixture
|
|
async def leave_wish_service(
|
|
leave_wish_repo: SQLiteLeaveWishRepository,
|
|
planned_leave_repo: SQLitePlannedLeaveRepository,
|
|
child_repo: SQLiteChildRepository,
|
|
) -> LeaveWishService:
|
|
# `child_repo` lets an open-ended ("until the child turns 4") series
|
|
# materialise against the child's real birth date; see LeaveWishService.
|
|
return LeaveWishService(leave_wish_repo, planned_leave_repo, child_repo)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# DayOverride
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@pytest_asyncio.fixture
|
|
async def day_override_repo(
|
|
connection: aiosqlite.Connection,
|
|
) -> SQLiteDayOverrideRepository:
|
|
return SQLiteDayOverrideRepository(connection)
|
|
|
|
|
|
@pytest_asyncio.fixture
|
|
async def day_override_service(
|
|
day_override_repo: SQLiteDayOverrideRepository,
|
|
) -> DayOverrideService:
|
|
return DayOverrideService(day_override_repo)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# PlannedDayExclusion (user-removed days — see app/entities/planned_day_exclusion.py)
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@pytest_asyncio.fixture
|
|
async def planned_day_exclusion_repo(
|
|
connection: aiosqlite.Connection,
|
|
) -> SQLitePlannedDayExclusionRepository:
|
|
return SQLitePlannedDayExclusionRepository(connection)
|
|
|
|
|
|
@pytest_asyncio.fixture
|
|
async def planned_day_exclusion_service(
|
|
planned_day_exclusion_repo: SQLitePlannedDayExclusionRepository,
|
|
planned_leave_repo: SQLitePlannedLeaveRepository,
|
|
) -> PlannedDayExclusionService:
|
|
return PlannedDayExclusionService(planned_day_exclusion_repo, planned_leave_repo)
|
|
|
|
|
|
@pytest_asyncio.fixture
|
|
async def child_service(
|
|
child_repo: SQLiteChildRepository,
|
|
) -> ChildService:
|
|
return ChildService(child_repo)
|
|
|
|
|
|
@pytest_asyncio.fixture
|
|
async def analytics_service(
|
|
parental_leave_repo: SQLiteParentalLeaveRepository,
|
|
planned_leave_repo: SQLitePlannedLeaveRepository,
|
|
child_repo: SQLiteChildRepository,
|
|
personal_data_repo: SQLitePersonalDataRepository,
|
|
workspace_repo: SQLiteWorkspaceRepository,
|
|
vacation_repo: SQLiteVacationDaysRepository,
|
|
) -> AnalyticsService:
|
|
return AnalyticsService(
|
|
parental_leave_repo,
|
|
planned_leave_repo,
|
|
child_repo,
|
|
personal_data_repo,
|
|
workspace_repo,
|
|
vacation_repo,
|
|
)
|
|
|
|
|
|
@pytest_asyncio.fixture
|
|
async def allocation_input_loader(
|
|
workspace_repo: SQLiteWorkspaceRepository,
|
|
child_repo: SQLiteChildRepository,
|
|
personal_data_repo: SQLitePersonalDataRepository,
|
|
parental_leave_repo: SQLiteParentalLeaveRepository,
|
|
vacation_repo: SQLiteVacationDaysRepository,
|
|
planned_leave_repo: SQLitePlannedLeaveRepository,
|
|
leave_wish_repo: SQLiteLeaveWishRepository,
|
|
day_override_repo: SQLiteDayOverrideRepository,
|
|
planned_day_exclusion_repo: SQLitePlannedDayExclusionRepository,
|
|
) -> AllocationInputLoader:
|
|
return AllocationInputLoader(
|
|
workspace_repo=workspace_repo,
|
|
child_repo=child_repo,
|
|
personal_data_repo=personal_data_repo,
|
|
parental_leave_repo=parental_leave_repo,
|
|
vacation_repo=vacation_repo,
|
|
planned_leave_repo=planned_leave_repo,
|
|
leave_wish_repo=leave_wish_repo,
|
|
day_override_repo=day_override_repo,
|
|
exclusion_repo=planned_day_exclusion_repo,
|
|
)
|
|
|
|
|
|
@pytest_asyncio.fixture
|
|
async def planning_engine_service(
|
|
allocation_input_loader: AllocationInputLoader,
|
|
) -> PlanningEngineService:
|
|
return PlanningEngineService(allocation_input_loader)
|
|
|
|
|
|
@pytest_asyncio.fixture
|
|
async def planning_service(
|
|
planned_leave_repo: SQLitePlannedLeaveRepository,
|
|
analytics_service: AnalyticsService,
|
|
parental_leave_repo: SQLiteParentalLeaveRepository,
|
|
vacation_repo: SQLiteVacationDaysRepository,
|
|
planning_engine_service: PlanningEngineService,
|
|
leave_wish_repo: SQLiteLeaveWishRepository,
|
|
) -> PlanningService:
|
|
return PlanningService(
|
|
planned_leave_repo,
|
|
analytics_service,
|
|
parental_leave_repo,
|
|
vacation_repo,
|
|
planning_engine_service,
|
|
leave_wish_repo=leave_wish_repo,
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Unit of work — a real `SQLiteUnitOfWork` bound to the test's shared
|
|
# `connection`, for tests that specifically exercise atomicity/locking (see
|
|
# `tests/unit/test_db_unit_of_work.py`). `planning_service` above deliberately
|
|
# keeps its default no-op unit of work so every other test's per-write-commit
|
|
# behavior stays exactly as it is; use `planning_service_with_uow` instead
|
|
# when a test needs the real thing.
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@pytest_asyncio.fixture
|
|
async def sqlite_uow(connection: aiosqlite.Connection) -> SQLiteUnitOfWork:
|
|
return SQLiteUnitOfWork(connection)
|
|
|
|
|
|
@pytest_asyncio.fixture
|
|
async def planning_service_with_uow(
|
|
planned_leave_repo: SQLitePlannedLeaveRepository,
|
|
analytics_service: AnalyticsService,
|
|
parental_leave_repo: SQLiteParentalLeaveRepository,
|
|
vacation_repo: SQLiteVacationDaysRepository,
|
|
planning_engine_service: PlanningEngineService,
|
|
leave_wish_repo: SQLiteLeaveWishRepository,
|
|
sqlite_uow: SQLiteUnitOfWork,
|
|
) -> PlanningService:
|
|
return PlanningService(
|
|
planned_leave_repo,
|
|
analytics_service,
|
|
parental_leave_repo,
|
|
vacation_repo,
|
|
planning_engine_service,
|
|
leave_wish_repo=leave_wish_repo,
|
|
uow=sqlite_uow,
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Tips
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@pytest_asyncio.fixture
|
|
async def tip_dismissal_repo(
|
|
connection: aiosqlite.Connection,
|
|
) -> SQLiteTipDismissalRepository:
|
|
return SQLiteTipDismissalRepository(connection)
|
|
|
|
|
|
@pytest_asyncio.fixture
|
|
async def preschool_planning_days_repo(
|
|
connection: aiosqlite.Connection,
|
|
) -> SQLitePreschoolPlanningDaysRepository:
|
|
return SQLitePreschoolPlanningDaysRepository(connection)
|
|
|
|
|
|
@pytest_asyncio.fixture
|
|
async def preschool_planning_days_service(
|
|
preschool_planning_days_repo: SQLitePreschoolPlanningDaysRepository,
|
|
) -> PreschoolPlanningDaysService:
|
|
return PreschoolPlanningDaysService(preschool_planning_days_repo)
|
|
|
|
|
|
@pytest_asyncio.fixture
|
|
async def tips_service(
|
|
parental_leave_repo: SQLiteParentalLeaveRepository,
|
|
planned_leave_repo: SQLitePlannedLeaveRepository,
|
|
child_repo: SQLiteChildRepository,
|
|
personal_data_repo: SQLitePersonalDataRepository,
|
|
vacation_repo: SQLiteVacationDaysRepository,
|
|
leave_wish_repo: SQLiteLeaveWishRepository,
|
|
tip_dismissal_repo: SQLiteTipDismissalRepository,
|
|
) -> TipsService:
|
|
return TipsService(
|
|
parental_leave_repo,
|
|
planned_leave_repo,
|
|
child_repo,
|
|
personal_data_repo,
|
|
vacation_repo,
|
|
leave_wish_repo,
|
|
tip_dismissal_repo,
|
|
)
|