# Plan: Issue #45 — Unit tests for FeeCalculationService ## Summary Create comprehensive PHPUnit tests for `FeeCalculationService` covering all 13 test cases specified in the issue: single member fees, family discount tiers, frozen rates, Erziehungsberechtigter exclusion, manual overrides during batch, mixed family states, error handling for missing rules, and cross-year rule changes. ## Implementation Steps ### Step 1: Create phpunit.xml configuration - Add `phpunit.xml` at project root with standard Nextcloud app test configuration - Configure autoloading to use composer PSR-4 paths - Set test suite directory to `tests/Unit/` ### Step 2: Create test helper for building mock Members/Rules - Create a helper trait or base class that provides factory methods: - `createMember(id, rolle, status, familyId, geburtsdatum, frozenFeeRate)` - `createFeeRule(yearFrom, baseRate, familyRulesJson, inactiveRule)` - `createFeeRecord(memberId, year, amount, manuellAngepasst)` ### Step 3: Create FeeCalculationServiceTest.php Write isolated test methods for each case in the issue: 1. `testSingleMemberNoFamily` — member without family_id → base_rate 2. `testSingleChildInFamily` — 1 active child → family rule "1" rate 3. `testTwoChildrenInFamily` — 2 active children → correct per-child amounts per position 4. `testThreeOrMoreChildrenInFamily` — 3+ children → "3+" catch-all rule applied, additional children at that rate 5. `testInactiveMemberWithFrozenRate` — inactive member with frozen_fee_rate → frozen rate returned 6. `testErziehungsberechtigterExcluded` — rolle=erziehungsberechtigter → fee = 0.00 7. `testManualOverrideSkippedDuringBatch` — manuell_angepasst records not overwritten in batchCalculate 8. `testMixedFamilyActiveInactiveChildren` — only active children counted for position-based discount 9. `testMemberBecomesInactiveMidYear` — rate frozen at current value 10. `testNoFeeRuleForYear` — DoesNotExistException thrown 11. `testRuleChangeBetweenYears` — correct rule applied per year 12. `testEmptyFamilyNoChildren` — no fees generated (base rate for adults in empty family) 13. `testMemberWithoutFamilyWithOverrideBanking` — base rate ### Step 4: Ensure all tests use mocked mappers - Mock `FeeRuleMapper`, `FeeRecordMapper`, `MemberMapper` - Mock `IUserSession` and `LoggerInterface` - Each test creates its own data set, no shared state ## Acceptance Criteria Checklist - [ ] AC1: `FeeCalculationServiceTest.php` exists in `tests/Unit/` - [ ] AC2: All 13 test cases from the issue are implemented - [ ] AC3: Tests use mock data (mocked mappers), isolated per test - [ ] AC4: Tests assert exact amounts and rule snapshots - [ ] AC5: Tests cover both `calculateFee()` and `batchCalculate()` methods - [ ] AC6: phpunit.xml configuration is present