Files
Mitgliederverwaltung/tests/Unit/MapperTest.php
T
shahondin1624 ccda3ee570
Database Portability Tests / Unit Tests (PlatformHelper) (pull_request) Failing after 42s
Database Portability Tests / Integration (mysql) (pull_request) Has been skipped
Database Portability Tests / Integration (postgres) (pull_request) Has been skipped
Database Portability Tests / Integration (sqlite) (pull_request) Has been skipped
Database Portability Tests / Verify no MySQL-specific SQL (pull_request) Successful in 5s
Fix findArchived() filtering all members in memory (Closes #202)
Replace in-memory array_filter with a dedicated SQL query
WHERE deleted_at IS NOT NULL in MemberMapper::findArchived().

This fixes broken pagination (limit/offset now apply to archived
members only), eliminates unnecessary data transfer, and follows
the same pattern as the countArchived() fix from #201.
2026-04-29 21:13:32 +02:00

1279 lines
53 KiB
PHP

<?php
declare(strict_types=1);
namespace OCA\Mitgliederverwaltung\Tests\Unit;
use OCA\Mitgliederverwaltung\Db\AddressMapper;
use OCA\Mitgliederverwaltung\Db\AuditLogMapper;
use OCA\Mitgliederverwaltung\Db\EmailMapper;
use OCA\Mitgliederverwaltung\Db\FamilyMapper;
use OCA\Mitgliederverwaltung\Db\FeeRecordMapper;
use OCA\Mitgliederverwaltung\Db\FeeRuleMapper;
use OCA\Mitgliederverwaltung\Db\InjuryMapper;
use OCA\Mitgliederverwaltung\Db\LagerMapper;
use OCA\Mitgliederverwaltung\Db\LagerTeilnehmerMapper;
use OCA\Mitgliederverwaltung\Db\MemberMapper;
use OCA\Mitgliederverwaltung\Db\PermissionMapper;
use OCA\Mitgliederverwaltung\Db\PhoneMapper;
use OCA\Mitgliederverwaltung\Db\SavedQueryMapper;
use OCA\Mitgliederverwaltung\Db\StufeHistoryMapper;
use OCA\Mitgliederverwaltung\Db\StufeMapper;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\DB\IResult;
use OCP\DB\QueryBuilder\ICompositeExpression;
use OCP\DB\QueryBuilder\IExpressionBuilder;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
/**
* Comprehensive tests for all mapper classes.
*
* These tests exercise the actual query-building methods by mocking
* the IDBConnection/IQueryBuilder/IResult chain and verifying that
* mapper methods execute without errors and return expected entity types.
*/
class MapperTest extends TestCase {
private IDBConnection&MockObject $db;
private IQueryBuilder&MockObject $qb;
private IExpressionBuilder&MockObject $expr;
private IResult&MockObject $result;
protected function setUp(): void {
parent::setUp();
$this->db = $this->createMock(IDBConnection::class);
$this->qb = $this->createMock(IQueryBuilder::class);
$this->expr = $this->createMock(IExpressionBuilder::class);
$this->result = $this->createMock(IResult::class);
// Configure the query builder to chain fluently
$this->qb->method('expr')->willReturn($this->expr);
$this->qb->method('select')->willReturnSelf();
$this->qb->method('selectDistinct')->willReturnSelf();
$this->qb->method('from')->willReturnSelf();
$this->qb->method('where')->willReturnSelf();
$this->qb->method('andWhere')->willReturnSelf();
$this->qb->method('orWhere')->willReturnSelf();
$this->qb->method('orderBy')->willReturnSelf();
$this->qb->method('addOrderBy')->willReturnSelf();
$this->qb->method('setMaxResults')->willReturnSelf();
$this->qb->method('setFirstResult')->willReturnSelf();
$this->qb->method('innerJoin')->willReturnSelf();
$this->qb->method('leftJoin')->willReturnSelf();
$this->qb->method('insert')->willReturnSelf();
$this->qb->method('delete')->willReturnSelf();
$this->qb->method('values')->willReturnSelf();
$this->qb->method('createNamedParameter')->willReturn(':param');
$this->qb->method('createFunction')->willReturnCallback(fn($call) => $call);
$this->qb->method('getSQL')->willReturn('SELECT * FROM test');
// Expression builder returns string predicates
$compositeExpr = $this->createMock(ICompositeExpression::class);
$this->expr->method('eq')->willReturn('col = :param');
$this->expr->method('neq')->willReturn('col != :param');
$this->expr->method('like')->willReturn('col LIKE :param');
$this->expr->method('iLike')->willReturn('col ILIKE :param');
$this->expr->method('isNull')->willReturn('col IS NULL');
$this->expr->method('gte')->willReturn('col >= :param');
$this->expr->method('lte')->willReturn('col <= :param');
$this->expr->method('orX')->willReturn($compositeExpr);
// DB connection returns query builder and provides escapeLikeParameter
$this->db->method('getQueryBuilder')->willReturn($this->qb);
$this->db->method('escapeLikeParameter')->willReturnCallback(fn($s) => $s);
}
/**
* Configure the IResult mock to return rows and then false (end-of-results).
*
* @param array[] $rows Rows to return from fetch()
*/
private function configureResultRows(array $rows): void {
$fetchCalls = array_merge($rows, [false]);
$this->result->method('fetch')
->willReturnOnConsecutiveCalls(...$fetchCalls);
$this->result->method('closeCursor')->willReturn(true);
$this->qb->method('executeQuery')->willReturn($this->result);
}
/**
* Configure the IResult mock to return a single row (for findEntity).
* findEntity calls fetch() twice: once for the row, once to verify no second row.
*/
private function configureResultSingleRow(array $row): void {
$this->result->method('fetch')
->willReturnOnConsecutiveCalls($row, false);
$this->result->method('closeCursor')->willReturn(true);
$this->qb->method('executeQuery')->willReturn($this->result);
}
/**
* Configure the IResult mock to return no rows (for DoesNotExistException).
*/
private function configureResultEmpty(): void {
$this->result->method('fetch')->willReturn(false);
$this->result->method('closeCursor')->willReturn(true);
$this->qb->method('executeQuery')->willReturn($this->result);
}
/**
* Configure the IResult mock for count queries (fetchOne returns a number).
*/
private function configureResultCount(int $count): void {
$this->result->method('fetchOne')->willReturn((string)$count);
$this->result->method('closeCursor')->willReturn(true);
$this->qb->method('executeQuery')->willReturn($this->result);
}
/**
* Configure the query builder for executeStatement (insert/delete/update).
*/
private function configureExecuteStatement(int $affected = 1): void {
$this->qb->method('executeStatement')->willReturn($affected);
}
// ── Helper: sample row data ─────────────────────────────────────
private function memberRow(int $id = 1): array {
return [
'id' => $id,
'vorname' => 'Max',
'nachname' => 'Mustermann',
'geburtsdatum' => '1990-06-15',
'geschlecht' => 'Maennlich',
'rolle' => 'mitglied',
'stufe_id' => 1,
'eintritt' => '2020-01-01',
'austritt' => null,
'status' => 'aktiv',
'allergien_encrypted' => null,
'notizen' => null,
'zusatz_notizen' => null,
'kv_typ' => null,
'kv_name' => null,
'family_id' => 1,
'frozen_fee_rate' => null,
'calendar_event_uri' => null,
'contact_vcard_uri' => null,
'created_at' => '2025-01-01 00:00:00',
'updated_at' => '2025-01-01 00:00:00',
'deleted_at' => null,
'einwilligung_datum' => null,
];
}
private function familyRow(int $id = 1): array {
return [
'id' => $id,
'name' => 'Familie Mustermann',
'kontoinhaber_encrypted' => null,
'iban_encrypted' => null,
'bic' => null,
'kreditinstitut' => null,
'created_at' => '2025-01-01',
'updated_at' => '2025-01-01',
];
}
private function addressRow(int $id = 1): array {
return [
'id' => $id,
'member_id' => 1,
'label' => null,
'strasse' => 'Hauptstr. 1',
'plz' => '12345',
'ort' => 'Berlin',
'land' => 'Deutschland',
'is_primary' => 1,
];
}
private function phoneRow(int $id = 1): array {
return [
'id' => $id,
'member_id' => 1,
'label' => 'Mobil',
'number_e164' => '+491701234567',
];
}
private function emailRow(int $id = 1): array {
return [
'id' => $id,
'member_id' => 1,
'label' => 'Privat',
'email' => 'max@example.com',
];
}
private function feeRecordRow(int $id = 1): array {
return [
'id' => $id,
'member_id' => 1,
'year' => 2026,
'amount' => '120.00',
'rule_snapshot_json' => null,
'manuell_angepasst' => 0,
'paid' => 0,
'payment_date' => null,
'notes' => null,
'created_at' => '2025-01-01',
'updated_at' => '2025-01-01',
];
}
private function feeRuleRow(int $id = 1): array {
return [
'id' => $id,
'year_from' => 2025,
'base_rate' => '120.00',
'family_rules_json' => '{}',
'inactive_rule' => 'freeze_rate_at_exit',
'created_at' => '2025-01-01',
'created_by' => 'admin',
];
}
private function injuryRow(int $id = 1): array {
return [
'id' => $id,
'member_id' => 1,
'lager_id' => null,
'activity_name' => null,
'datum' => '2026-03-15',
'beschreibung' => 'Sturz beim Wandern',
'created_by' => 'admin',
'created_at' => '2025-01-01',
];
}
private function lagerRow(int $id = 1): array {
return [
'id' => $id,
'name' => 'Sommerlager 2026',
'startdatum' => '2026-07-15',
'enddatum' => '2026-07-25',
'ort' => 'Allgaeu',
'beschreibung' => null,
'files_folder_path' => null,
'created_at' => '2025-01-01',
'updated_at' => '2025-01-01',
];
}
private function lagerTeilnehmerRow(int $id = 1): array {
return [
'id' => $id,
'lager_id' => 1,
'member_id' => 1,
'rolle' => 'Teilnehmer',
];
}
private function auditLogRow(int $id = 1): array {
return [
'id' => $id,
'zeitpunkt' => '2025-01-01 12:00:00',
'nc_user_id' => 'admin',
'aktion' => 'create',
'entitaet' => 'member',
'entitaet_id' => 1,
'feld' => null,
'alter_wert' => null,
'neuer_wert' => null,
];
}
private function permissionRow(int $id = 1): array {
return [
'id' => $id,
'nc_user_id' => 'admin',
'level' => 'admin',
'allowed_stufen_json' => null,
'can_see_banking' => 0,
];
}
private function savedQueryRow(int $id = 1): array {
return [
'id' => $id,
'name' => 'Test Query',
'query_json' => '{"filters":[]}',
'sort_json' => null,
'created_by' => 'admin',
'created_at' => '2025-01-01',
];
}
private function stufeRow(int $id = 1): array {
return [
'id' => $id,
'name' => 'Woelflinge',
'sort_order' => 1,
'age_range_min' => 7,
'age_range_max' => 10,
'color' => '#FF9900',
];
}
private function stufeHistoryRow(int $id = 1): array {
return [
'id' => $id,
'member_id' => 1,
'stufe_id' => 1,
'changed_at' => '2025-01-01',
'changed_by' => 'admin',
];
}
// ══════════════════════════════════════════════════════════════════
// ── MemberMapper ─────────────────────────────────────────────────
// ══════════════════════════════════════════════════════════════════
public function testMemberMapperInstantiation(): void {
$mapper = new MemberMapper($this->db);
$this->assertInstanceOf(MemberMapper::class, $mapper);
}
public function testMemberMapperFindById(): void {
$this->configureResultSingleRow($this->memberRow(42));
$mapper = new MemberMapper($this->db);
$member = $mapper->findById(42);
$this->assertSame(42, $member->getId());
$this->assertSame('Max', $member->getVorname());
$this->assertSame('Mustermann', $member->getNachname());
}
public function testMemberMapperFindByIdThrowsWhenNotFound(): void {
$this->configureResultEmpty();
$mapper = new MemberMapper($this->db);
$this->expectException(DoesNotExistException::class);
$mapper->findById(999);
}
public function testMemberMapperFindAllNoParams(): void {
$this->configureResultRows([$this->memberRow(1), $this->memberRow(2)]);
$mapper = new MemberMapper($this->db);
$members = $mapper->findAll();
$this->assertCount(2, $members);
}
public function testMemberMapperFindAllWithPagination(): void {
$this->configureResultRows([$this->memberRow(1)]);
$mapper = new MemberMapper($this->db);
$members = $mapper->findAll(10, 0);
$this->assertCount(1, $members);
}
public function testMemberMapperFindAllIncludeDeleted(): void {
$this->configureResultRows([$this->memberRow(1)]);
$mapper = new MemberMapper($this->db);
$members = $mapper->findAll(null, null, true);
$this->assertCount(1, $members);
}
public function testMemberMapperFindByFamily(): void {
$this->configureResultRows([$this->memberRow(1), $this->memberRow(2)]);
$mapper = new MemberMapper($this->db);
$members = $mapper->findByFamily(1);
$this->assertCount(2, $members);
}
public function testMemberMapperFindByStatus(): void {
$this->configureResultRows([$this->memberRow(1)]);
$mapper = new MemberMapper($this->db);
$members = $mapper->findByStatus('aktiv');
$this->assertCount(1, $members);
}
public function testMemberMapperFindByStufe(): void {
$this->configureResultRows([$this->memberRow(1)]);
$mapper = new MemberMapper($this->db);
$members = $mapper->findByStufe(1);
$this->assertCount(1, $members);
}
public function testMemberMapperSearch(): void {
$this->configureResultRows([$this->memberRow(1)]);
$mapper = new MemberMapper($this->db);
$members = $mapper->search('Max');
$this->assertCount(1, $members);
}
public function testMemberMapperSearchEmptyResult(): void {
$this->configureResultRows([]);
$mapper = new MemberMapper($this->db);
$members = $mapper->search('Nonexistent');
$this->assertCount(0, $members);
}
public function testMemberMapperFindByBirthdayMonth(): void {
$this->configureResultRows([$this->memberRow(1)]);
$mapper = new MemberMapper($this->db);
$members = $mapper->findByBirthdayMonth(6);
$this->assertCount(1, $members);
}
public function testMemberMapperFindByBirthdayMonthPadsSingleDigit(): void {
$this->configureResultRows([$this->memberRow(1)]);
$mapper = new MemberMapper($this->db);
$members = $mapper->findByBirthdayMonth(1);
$this->assertCount(1, $members);
}
public function testMemberMapperFindWithUnpaidFees(): void {
$this->configureResultRows([$this->memberRow(1)]);
$mapper = new MemberMapper($this->db);
$members = $mapper->findWithUnpaidFees(2026);
$this->assertCount(1, $members);
}
public function testMemberMapperFullTextSearch(): void {
$this->configureResultRows([$this->memberRow(1)]);
$mapper = new MemberMapper($this->db);
$members = $mapper->fullTextSearch('Max');
$this->assertCount(1, $members);
}
public function testMemberMapperFullTextSearchWithCustomLimit(): void {
$this->configureResultRows([$this->memberRow(1)]);
$mapper = new MemberMapper($this->db);
$members = $mapper->fullTextSearch('Max', 5);
$this->assertCount(1, $members);
}
public function testMemberMapperFindByNameAndBirthdate(): void {
$this->configureResultSingleRow($this->memberRow(1));
$mapper = new MemberMapper($this->db);
$member = $mapper->findByNameAndBirthdate('Max', 'Mustermann', '1990-06-15');
$this->assertNotNull($member);
$this->assertSame(1, $member->getId());
}
public function testMemberMapperFindByNameAndBirthdateReturnsNullWhenNotFound(): void {
$this->configureResultEmpty();
$mapper = new MemberMapper($this->db);
$member = $mapper->findByNameAndBirthdate('Nonexistent', 'Person', '2000-01-01');
$this->assertNull($member);
}
public function testMemberMapperFindFilteredNoFilters(): void {
$this->configureResultRows([$this->memberRow(1), $this->memberRow(2)]);
$mapper = new MemberMapper($this->db);
$members = $mapper->findFiltered();
$this->assertCount(2, $members);
}
public function testMemberMapperFindFilteredByStatus(): void {
$this->configureResultRows([$this->memberRow(1)]);
$mapper = new MemberMapper($this->db);
$members = $mapper->findFiltered(status: 'aktiv');
$this->assertCount(1, $members);
}
public function testMemberMapperFindFilteredByRolle(): void {
$this->configureResultRows([$this->memberRow(1)]);
$mapper = new MemberMapper($this->db);
$members = $mapper->findFiltered(rolle: 'mitglied');
$this->assertCount(1, $members);
}
public function testMemberMapperFindFilteredByBirthdayThisMonth(): void {
$this->configureResultRows([$this->memberRow(1)]);
$mapper = new MemberMapper($this->db);
$members = $mapper->findFiltered(birthdayThisMonth: true);
$this->assertCount(1, $members);
}
public function testMemberMapperFindFilteredByUnpaidFees(): void {
$this->configureResultRows([$this->memberRow(1)]);
$mapper = new MemberMapper($this->db);
$members = $mapper->findFiltered(unpaidFees: true);
$this->assertCount(1, $members);
}
public function testMemberMapperFindFilteredCombinedStatusAndRolle(): void {
$this->configureResultRows([$this->memberRow(1)]);
$mapper = new MemberMapper($this->db);
$members = $mapper->findFiltered(status: 'aktiv', rolle: 'mitglied');
$this->assertCount(1, $members);
}
public function testMemberMapperFindFilteredCombinedAll(): void {
$this->configureResultRows([]);
$mapper = new MemberMapper($this->db);
$members = $mapper->findFiltered(
status: 'aktiv',
rolle: 'erziehungsberechtigter',
birthdayThisMonth: true,
unpaidFees: true
);
$this->assertCount(0, $members);
}
public function testMemberMapperFindFilteredCombinedStatusAndBirthday(): void {
$this->configureResultRows([$this->memberRow(1)]);
$mapper = new MemberMapper($this->db);
$members = $mapper->findFiltered(status: 'inaktiv', birthdayThisMonth: true);
$this->assertCount(1, $members);
}
public function testMemberMapperFindFilteredCombinedRolleAndUnpaid(): void {
$this->configureResultRows([$this->memberRow(1), $this->memberRow(2)]);
$mapper = new MemberMapper($this->db);
$members = $mapper->findFiltered(rolle: 'mitglied', unpaidFees: true);
$this->assertCount(2, $members);
}
public function testMemberMapperCountAll(): void {
$this->configureResultCount(42);
$mapper = new MemberMapper($this->db);
$count = $mapper->countAll();
$this->assertSame(42, $count);
}
public function testMemberMapperCountAllZero(): void {
$this->configureResultCount(0);
$mapper = new MemberMapper($this->db);
$count = $mapper->countAll();
$this->assertSame(0, $count);
}
public function testMemberMapperCountArchived(): void {
$this->configureResultCount(7);
$mapper = new MemberMapper($this->db);
$count = $mapper->countArchived();
$this->assertSame(7, $count);
}
public function testMemberMapperCountArchivedZero(): void {
$this->configureResultCount(0);
$mapper = new MemberMapper($this->db);
$count = $mapper->countArchived();
$this->assertSame(0, $count);
}
public function testMemberMapperFindArchivedNoParams(): void {
$row = $this->memberRow(1);
$row['deleted_at'] = '2026-01-01 00:00:00';
$this->configureResultRows([$row]);
$mapper = new MemberMapper($this->db);
$members = $mapper->findArchived();
$this->assertCount(1, $members);
}
public function testMemberMapperFindArchivedWithPagination(): void {
$row = $this->memberRow(1);
$row['deleted_at'] = '2026-01-01 00:00:00';
$this->configureResultRows([$row]);
$mapper = new MemberMapper($this->db);
$members = $mapper->findArchived(10, 0);
$this->assertCount(1, $members);
}
public function testMemberMapperFindArchivedEmpty(): void {
$this->configureResultRows([]);
$mapper = new MemberMapper($this->db);
$members = $mapper->findArchived();
$this->assertCount(0, $members);
}
// ══════════════════════════════════════════════════════════════════
// ── FamilyMapper ─────────────────────────────────────────────────
// ══════════════════════════════════════════════════════════════════
public function testFamilyMapperInstantiation(): void {
$mapper = new FamilyMapper($this->db);
$this->assertInstanceOf(FamilyMapper::class, $mapper);
}
public function testFamilyMapperFindById(): void {
$this->configureResultSingleRow($this->familyRow(1));
$mapper = new FamilyMapper($this->db);
$family = $mapper->findById(1);
$this->assertSame(1, $family->getId());
}
public function testFamilyMapperFindByIdThrowsWhenNotFound(): void {
$this->configureResultEmpty();
$mapper = new FamilyMapper($this->db);
$this->expectException(DoesNotExistException::class);
$mapper->findById(999);
}
public function testFamilyMapperFindAll(): void {
$this->configureResultRows([$this->familyRow(1), $this->familyRow(2)]);
$mapper = new FamilyMapper($this->db);
$families = $mapper->findAll();
$this->assertCount(2, $families);
}
public function testFamilyMapperFindAllWithPagination(): void {
$this->configureResultRows([$this->familyRow(1)]);
$mapper = new FamilyMapper($this->db);
$families = $mapper->findAll(10, 0);
$this->assertCount(1, $families);
}
public function testFamilyMapperFindByName(): void {
$this->configureResultRows([$this->familyRow(1)]);
$mapper = new FamilyMapper($this->db);
$families = $mapper->findByName('Mustermann');
$this->assertCount(1, $families);
}
public function testFamilyMapperFindByNameEmpty(): void {
$this->configureResultRows([]);
$mapper = new FamilyMapper($this->db);
$families = $mapper->findByName('Nonexistent');
$this->assertCount(0, $families);
}
public function testFamilyMapperCountAll(): void {
$this->configureResultCount(5);
$mapper = new FamilyMapper($this->db);
$count = $mapper->countAll();
$this->assertSame(5, $count);
}
// ══════════════════════════════════════════════════════════════════
// ── AddressMapper ────────────────────────────────────────────────
// ══════════════════════════════════════════════════════════════════
public function testAddressMapperInstantiation(): void {
$mapper = new AddressMapper($this->db);
$this->assertInstanceOf(AddressMapper::class, $mapper);
}
public function testAddressMapperFindAll(): void {
$this->configureResultRows([$this->addressRow(1), $this->addressRow(2)]);
$mapper = new AddressMapper($this->db);
$addresses = $mapper->findAll();
$this->assertCount(2, $addresses);
}
public function testAddressMapperFindByMemberId(): void {
$this->configureResultRows([$this->addressRow(1)]);
$mapper = new AddressMapper($this->db);
$addresses = $mapper->findByMemberId(1);
$this->assertCount(1, $addresses);
}
public function testAddressMapperFindByMemberIdEmpty(): void {
$this->configureResultRows([]);
$mapper = new AddressMapper($this->db);
$addresses = $mapper->findByMemberId(999);
$this->assertCount(0, $addresses);
}
public function testAddressMapperDeleteByMemberId(): void {
$this->configureExecuteStatement(2);
$mapper = new AddressMapper($this->db);
$mapper->deleteByMemberId(1);
// No exception means success
$this->assertTrue(true);
}
// ══════════════════════════════════════════════════════════════════
// ── PhoneMapper ──────────────────────────────────────────────────
// ══════════════════════════════════════════════════════════════════
public function testPhoneMapperInstantiation(): void {
$mapper = new PhoneMapper($this->db);
$this->assertInstanceOf(PhoneMapper::class, $mapper);
}
public function testPhoneMapperFindAll(): void {
$this->configureResultRows([$this->phoneRow(1), $this->phoneRow(2)]);
$mapper = new PhoneMapper($this->db);
$phones = $mapper->findAll();
$this->assertCount(2, $phones);
}
public function testPhoneMapperFindByMemberId(): void {
$this->configureResultRows([$this->phoneRow(1)]);
$mapper = new PhoneMapper($this->db);
$phones = $mapper->findByMemberId(1);
$this->assertCount(1, $phones);
}
public function testPhoneMapperFindByMemberIdEmpty(): void {
$this->configureResultRows([]);
$mapper = new PhoneMapper($this->db);
$phones = $mapper->findByMemberId(999);
$this->assertCount(0, $phones);
}
public function testPhoneMapperDeleteByMemberId(): void {
$this->configureExecuteStatement(1);
$mapper = new PhoneMapper($this->db);
$mapper->deleteByMemberId(1);
$this->assertTrue(true);
}
// ══════════════════════════════════════════════════════════════════
// ── EmailMapper ──────────────────────────────────────────────────
// ══════════════════════════════════════════════════════════════════
public function testEmailMapperInstantiation(): void {
$mapper = new EmailMapper($this->db);
$this->assertInstanceOf(EmailMapper::class, $mapper);
}
public function testEmailMapperFindAll(): void {
$this->configureResultRows([$this->emailRow(1), $this->emailRow(2)]);
$mapper = new EmailMapper($this->db);
$emails = $mapper->findAll();
$this->assertCount(2, $emails);
}
public function testEmailMapperFindByMemberId(): void {
$this->configureResultRows([$this->emailRow(1)]);
$mapper = new EmailMapper($this->db);
$emails = $mapper->findByMemberId(1);
$this->assertCount(1, $emails);
}
public function testEmailMapperFindByMemberIdEmpty(): void {
$this->configureResultRows([]);
$mapper = new EmailMapper($this->db);
$emails = $mapper->findByMemberId(999);
$this->assertCount(0, $emails);
}
public function testEmailMapperDeleteByMemberId(): void {
$this->configureExecuteStatement(1);
$mapper = new EmailMapper($this->db);
$mapper->deleteByMemberId(1);
$this->assertTrue(true);
}
// ══════════════════════════════════════════════════════════════════
// ── FeeRecordMapper ──────────────────────────────────────────────
// ══════════════════════════════════════════════════════════════════
public function testFeeRecordMapperInstantiation(): void {
$mapper = new FeeRecordMapper($this->db);
$this->assertInstanceOf(FeeRecordMapper::class, $mapper);
}
public function testFeeRecordMapperFindById(): void {
$this->configureResultSingleRow($this->feeRecordRow(1));
$mapper = new FeeRecordMapper($this->db);
$record = $mapper->findById(1);
$this->assertSame(1, $record->getId());
}
public function testFeeRecordMapperFindByIdThrowsWhenNotFound(): void {
$this->configureResultEmpty();
$mapper = new FeeRecordMapper($this->db);
$this->expectException(DoesNotExistException::class);
$mapper->findById(999);
}
public function testFeeRecordMapperFindAll(): void {
$this->configureResultRows([$this->feeRecordRow(1), $this->feeRecordRow(2)]);
$mapper = new FeeRecordMapper($this->db);
$records = $mapper->findAll();
$this->assertCount(2, $records);
}
public function testFeeRecordMapperFindByYear(): void {
$this->configureResultRows([$this->feeRecordRow(1)]);
$mapper = new FeeRecordMapper($this->db);
$records = $mapper->findByYear(2026);
$this->assertCount(1, $records);
}
public function testFeeRecordMapperFindByMemberId(): void {
$this->configureResultRows([$this->feeRecordRow(1)]);
$mapper = new FeeRecordMapper($this->db);
$records = $mapper->findByMemberId(1);
$this->assertCount(1, $records);
}
public function testFeeRecordMapperFindByMemberAndYear(): void {
$this->configureResultSingleRow($this->feeRecordRow(1));
$mapper = new FeeRecordMapper($this->db);
$record = $mapper->findByMemberAndYear(1, 2026);
$this->assertSame(1, $record->getId());
}
public function testFeeRecordMapperFindByMemberAndYearThrowsWhenNotFound(): void {
$this->configureResultEmpty();
$mapper = new FeeRecordMapper($this->db);
$this->expectException(DoesNotExistException::class);
$mapper->findByMemberAndYear(999, 2026);
}
public function testFeeRecordMapperCountByYear(): void {
$this->configureResultCount(15);
$mapper = new FeeRecordMapper($this->db);
$count = $mapper->countByYear(2026);
$this->assertSame(15, $count);
}
public function testFeeRecordMapperCountByYearZero(): void {
$this->configureResultCount(0);
$mapper = new FeeRecordMapper($this->db);
$count = $mapper->countByYear(2099);
$this->assertSame(0, $count);
}
// ══════════════════════════════════════════════════════════════════
// ── FeeRuleMapper ────────────────────────────────────────────────
// ══════════════════════════════════════════════════════════════════
public function testFeeRuleMapperInstantiation(): void {
$mapper = new FeeRuleMapper($this->db);
$this->assertInstanceOf(FeeRuleMapper::class, $mapper);
}
public function testFeeRuleMapperFindById(): void {
$this->configureResultSingleRow($this->feeRuleRow(1));
$mapper = new FeeRuleMapper($this->db);
$rule = $mapper->findById(1);
$this->assertSame(1, $rule->getId());
}
public function testFeeRuleMapperFindByIdThrowsWhenNotFound(): void {
$this->configureResultEmpty();
$mapper = new FeeRuleMapper($this->db);
$this->expectException(DoesNotExistException::class);
$mapper->findById(999);
}
public function testFeeRuleMapperFindByYear(): void {
$this->configureResultSingleRow($this->feeRuleRow(1));
$mapper = new FeeRuleMapper($this->db);
$rule = $mapper->findByYear(2026);
$this->assertSame(1, $rule->getId());
}
public function testFeeRuleMapperFindByYearThrowsWhenNoneApplies(): void {
$this->configureResultEmpty();
$mapper = new FeeRuleMapper($this->db);
$this->expectException(DoesNotExistException::class);
$mapper->findByYear(1900);
}
public function testFeeRuleMapperFindAll(): void {
$this->configureResultRows([$this->feeRuleRow(1), $this->feeRuleRow(2)]);
$mapper = new FeeRuleMapper($this->db);
$rules = $mapper->findAll();
$this->assertCount(2, $rules);
}
// ══════════════════════════════════════════════════════════════════
// ── InjuryMapper ─────────────────────────────────────────────────
// ══════════════════════════════════════════════════════════════════
public function testInjuryMapperInstantiation(): void {
$mapper = new InjuryMapper($this->db);
$this->assertInstanceOf(InjuryMapper::class, $mapper);
}
public function testInjuryMapperFindById(): void {
$this->configureResultSingleRow($this->injuryRow(1));
$mapper = new InjuryMapper($this->db);
$injury = $mapper->findById(1);
$this->assertSame(1, $injury->getId());
}
public function testInjuryMapperFindByIdThrowsWhenNotFound(): void {
$this->configureResultEmpty();
$mapper = new InjuryMapper($this->db);
$this->expectException(DoesNotExistException::class);
$mapper->findById(999);
}
public function testInjuryMapperFindAllNoParams(): void {
$this->configureResultRows([$this->injuryRow(1), $this->injuryRow(2)]);
$mapper = new InjuryMapper($this->db);
$injuries = $mapper->findAll();
$this->assertCount(2, $injuries);
}
public function testInjuryMapperFindAllWithPagination(): void {
$this->configureResultRows([$this->injuryRow(1)]);
$mapper = new InjuryMapper($this->db);
$injuries = $mapper->findAll(10, 0);
$this->assertCount(1, $injuries);
}
public function testInjuryMapperFindByMemberId(): void {
$this->configureResultRows([$this->injuryRow(1)]);
$mapper = new InjuryMapper($this->db);
$injuries = $mapper->findByMemberId(1);
$this->assertCount(1, $injuries);
}
public function testInjuryMapperFindByLagerId(): void {
$this->configureResultRows([$this->injuryRow(1)]);
$mapper = new InjuryMapper($this->db);
$injuries = $mapper->findByLagerId(1);
$this->assertCount(1, $injuries);
}
public function testInjuryMapperFindByDateRange(): void {
$this->configureResultRows([$this->injuryRow(1)]);
$mapper = new InjuryMapper($this->db);
$injuries = $mapper->findByDateRange('2026-01-01', '2026-12-31');
$this->assertCount(1, $injuries);
}
public function testInjuryMapperFindByDateRangeEmpty(): void {
$this->configureResultRows([]);
$mapper = new InjuryMapper($this->db);
$injuries = $mapper->findByDateRange('2000-01-01', '2000-12-31');
$this->assertCount(0, $injuries);
}
// ══════════════════════════════════════════════════════════════════
// ── LagerMapper ──────────────────────────────────────────────────
// ══════════════════════════════════════════════════════════════════
public function testLagerMapperInstantiation(): void {
$mapper = new LagerMapper($this->db);
$this->assertInstanceOf(LagerMapper::class, $mapper);
}
public function testLagerMapperFindById(): void {
$this->configureResultSingleRow($this->lagerRow(1));
$mapper = new LagerMapper($this->db);
$lager = $mapper->findById(1);
$this->assertSame(1, $lager->getId());
}
public function testLagerMapperFindByIdThrowsWhenNotFound(): void {
$this->configureResultEmpty();
$mapper = new LagerMapper($this->db);
$this->expectException(DoesNotExistException::class);
$mapper->findById(999);
}
public function testLagerMapperFindAllNoParams(): void {
$this->configureResultRows([$this->lagerRow(1), $this->lagerRow(2)]);
$mapper = new LagerMapper($this->db);
$camps = $mapper->findAll();
$this->assertCount(2, $camps);
}
public function testLagerMapperFindAllWithPagination(): void {
$this->configureResultRows([$this->lagerRow(1)]);
$mapper = new LagerMapper($this->db);
$camps = $mapper->findAll(10, 0);
$this->assertCount(1, $camps);
}
public function testLagerMapperFindByYear(): void {
$this->configureResultRows([$this->lagerRow(1)]);
$mapper = new LagerMapper($this->db);
$camps = $mapper->findByYear(2026);
$this->assertCount(1, $camps);
}
public function testLagerMapperFindByYearEmpty(): void {
$this->configureResultRows([]);
$mapper = new LagerMapper($this->db);
$camps = $mapper->findByYear(1999);
$this->assertCount(0, $camps);
}
public function testLagerMapperFindByStufe(): void {
$this->configureResultRows([$this->lagerRow(1)]);
$mapper = new LagerMapper($this->db);
$camps = $mapper->findByStufe(1);
$this->assertCount(1, $camps);
}
// ══════════════════════════════════════════════════════════════════
// ── LagerTeilnehmerMapper ────────────────────────────────────────
// ══════════════════════════════════════════════════════════════════
public function testLagerTeilnehmerMapperInstantiation(): void {
$mapper = new LagerTeilnehmerMapper($this->db);
$this->assertInstanceOf(LagerTeilnehmerMapper::class, $mapper);
}
public function testLagerTeilnehmerMapperFindAll(): void {
$this->configureResultRows([$this->lagerTeilnehmerRow(1), $this->lagerTeilnehmerRow(2)]);
$mapper = new LagerTeilnehmerMapper($this->db);
$participants = $mapper->findAll();
$this->assertCount(2, $participants);
}
public function testLagerTeilnehmerMapperFindByLagerId(): void {
$this->configureResultRows([$this->lagerTeilnehmerRow(1)]);
$mapper = new LagerTeilnehmerMapper($this->db);
$participants = $mapper->findByLagerId(1);
$this->assertCount(1, $participants);
}
public function testLagerTeilnehmerMapperFindByMemberId(): void {
$this->configureResultRows([$this->lagerTeilnehmerRow(1)]);
$mapper = new LagerTeilnehmerMapper($this->db);
$participants = $mapper->findByMemberId(1);
$this->assertCount(1, $participants);
}
public function testLagerTeilnehmerMapperAddParticipant(): void {
$this->configureExecuteStatement(1);
$mapper = new LagerTeilnehmerMapper($this->db);
$mapper->addParticipant(1, 1);
$this->assertTrue(true);
}
public function testLagerTeilnehmerMapperAddParticipantWithCustomRole(): void {
$this->configureExecuteStatement(1);
$mapper = new LagerTeilnehmerMapper($this->db);
$mapper->addParticipant(1, 1, 'Leiter');
$this->assertTrue(true);
}
public function testLagerTeilnehmerMapperRemoveParticipant(): void {
$this->configureExecuteStatement(1);
$mapper = new LagerTeilnehmerMapper($this->db);
$mapper->removeParticipant(1, 1);
$this->assertTrue(true);
}
public function testLagerTeilnehmerMapperDeleteByLagerId(): void {
$this->configureExecuteStatement(5);
$mapper = new LagerTeilnehmerMapper($this->db);
$mapper->deleteByLagerId(1);
$this->assertTrue(true);
}
// ══════════════════════════════════════════════════════════════════
// ── AuditLogMapper ───────────────────────────────────────────────
// ══════════════════════════════════════════════════════════════════
public function testAuditLogMapperInstantiation(): void {
$mapper = new AuditLogMapper($this->db);
$this->assertInstanceOf(AuditLogMapper::class, $mapper);
}
public function testAuditLogMapperFindFilteredNoParams(): void {
$this->configureResultRows([$this->auditLogRow(1), $this->auditLogRow(2)]);
$mapper = new AuditLogMapper($this->db);
$logs = $mapper->findFiltered();
$this->assertCount(2, $logs);
}
public function testAuditLogMapperFindFilteredByEntitaet(): void {
$this->configureResultRows([$this->auditLogRow(1)]);
$mapper = new AuditLogMapper($this->db);
$logs = $mapper->findFiltered(entitaet: 'member');
$this->assertCount(1, $logs);
}
public function testAuditLogMapperFindFilteredByEntitaetId(): void {
$this->configureResultRows([$this->auditLogRow(1)]);
$mapper = new AuditLogMapper($this->db);
$logs = $mapper->findFiltered(entitaetId: 1);
$this->assertCount(1, $logs);
}
public function testAuditLogMapperFindFilteredByNcUserId(): void {
$this->configureResultRows([$this->auditLogRow(1)]);
$mapper = new AuditLogMapper($this->db);
$logs = $mapper->findFiltered(ncUserId: 'admin');
$this->assertCount(1, $logs);
}
public function testAuditLogMapperFindFilteredByDateRange(): void {
$this->configureResultRows([$this->auditLogRow(1)]);
$mapper = new AuditLogMapper($this->db);
$logs = $mapper->findFiltered(fromDate: '2025-01-01', toDate: '2025-12-31');
$this->assertCount(1, $logs);
}
public function testAuditLogMapperFindFilteredWithPagination(): void {
$this->configureResultRows([$this->auditLogRow(1)]);
$mapper = new AuditLogMapper($this->db);
$logs = $mapper->findFiltered(limit: 10, offset: 0);
$this->assertCount(1, $logs);
}
public function testAuditLogMapperFindFilteredAllParams(): void {
$this->configureResultRows([$this->auditLogRow(1)]);
$mapper = new AuditLogMapper($this->db);
$logs = $mapper->findFiltered('member', 1, 'admin', '2025-01-01', '2025-12-31', 10, 0);
$this->assertCount(1, $logs);
}
public function testAuditLogMapperCountFilteredNoParams(): void {
$this->configureResultCount(100);
$mapper = new AuditLogMapper($this->db);
$count = $mapper->countFiltered();
$this->assertSame(100, $count);
}
public function testAuditLogMapperCountFilteredByEntitaet(): void {
$this->configureResultCount(10);
$mapper = new AuditLogMapper($this->db);
$count = $mapper->countFiltered(entitaet: 'member');
$this->assertSame(10, $count);
}
public function testAuditLogMapperCountFilteredByEntitaetId(): void {
$this->configureResultCount(3);
$mapper = new AuditLogMapper($this->db);
$count = $mapper->countFiltered(entitaetId: 1);
$this->assertSame(3, $count);
}
public function testAuditLogMapperCountFilteredByNcUserId(): void {
$this->configureResultCount(5);
$mapper = new AuditLogMapper($this->db);
$count = $mapper->countFiltered(ncUserId: 'admin');
$this->assertSame(5, $count);
}
public function testAuditLogMapperCountFilteredAllParams(): void {
$this->configureResultCount(1);
$mapper = new AuditLogMapper($this->db);
$count = $mapper->countFiltered('member', 1, 'admin');
$this->assertSame(1, $count);
}
// ══════════════════════════════════════════════════════════════════
// ── PermissionMapper ─────────────────────────────────────────────
// ══════════════════════════════════════════════════════════════════
public function testPermissionMapperInstantiation(): void {
$mapper = new PermissionMapper($this->db);
$this->assertInstanceOf(PermissionMapper::class, $mapper);
}
public function testPermissionMapperFindByUserId(): void {
$this->configureResultSingleRow($this->permissionRow(1));
$mapper = new PermissionMapper($this->db);
$permission = $mapper->findByUserId('admin');
$this->assertSame(1, $permission->getId());
}
public function testPermissionMapperFindByUserIdThrowsWhenNotFound(): void {
$this->configureResultEmpty();
$mapper = new PermissionMapper($this->db);
$this->expectException(DoesNotExistException::class);
$mapper->findByUserId('nonexistent');
}
public function testPermissionMapperFindAll(): void {
$this->configureResultRows([$this->permissionRow(1), $this->permissionRow(2)]);
$mapper = new PermissionMapper($this->db);
$permissions = $mapper->findAll();
$this->assertCount(2, $permissions);
}
// ══════════════════════════════════════════════════════════════════
// ── StufeMapper ──────────────────────────────────────────────────
// ══════════════════════════════════════════════════════════════════
public function testStufeMapperInstantiation(): void {
$mapper = new StufeMapper($this->db);
$this->assertInstanceOf(StufeMapper::class, $mapper);
}
public function testStufeMapperFindById(): void {
$this->configureResultSingleRow($this->stufeRow(1));
$mapper = new StufeMapper($this->db);
$stufe = $mapper->findById(1);
$this->assertSame(1, $stufe->getId());
}
public function testStufeMapperFindByIdThrowsWhenNotFound(): void {
$this->configureResultEmpty();
$mapper = new StufeMapper($this->db);
$this->expectException(DoesNotExistException::class);
$mapper->findById(999);
}
public function testStufeMapperFindAll(): void {
$this->configureResultRows([$this->stufeRow(1), $this->stufeRow(2)]);
$mapper = new StufeMapper($this->db);
$stufen = $mapper->findAll();
$this->assertCount(2, $stufen);
}
// ══════════════════════════════════════════════════════════════════
// ── SavedQueryMapper ─────────────────────────────────────────────
// ══════════════════════════════════════════════════════════════════
public function testSavedQueryMapperInstantiation(): void {
$mapper = new SavedQueryMapper($this->db);
$this->assertInstanceOf(SavedQueryMapper::class, $mapper);
}
public function testSavedQueryMapperFindById(): void {
$this->configureResultSingleRow($this->savedQueryRow(1));
$mapper = new SavedQueryMapper($this->db);
$query = $mapper->findById(1);
$this->assertSame(1, $query->getId());
}
public function testSavedQueryMapperFindByIdThrowsWhenNotFound(): void {
$this->configureResultEmpty();
$mapper = new SavedQueryMapper($this->db);
$this->expectException(DoesNotExistException::class);
$mapper->findById(999);
}
public function testSavedQueryMapperFindAll(): void {
$this->configureResultRows([$this->savedQueryRow(1), $this->savedQueryRow(2)]);
$mapper = new SavedQueryMapper($this->db);
$queries = $mapper->findAll();
$this->assertCount(2, $queries);
}
// ══════════════════════════════════════════════════════════════════
// ── StufeHistoryMapper ───────────────────────────────────────────
// ══════════════════════════════════════════════════════════════════
public function testStufeHistoryMapperInstantiation(): void {
$mapper = new StufeHistoryMapper($this->db);
$this->assertInstanceOf(StufeHistoryMapper::class, $mapper);
}
public function testStufeHistoryMapperFindByMemberId(): void {
$this->configureResultRows([$this->stufeHistoryRow(1)]);
$mapper = new StufeHistoryMapper($this->db);
$history = $mapper->findByMemberId(1);
$this->assertCount(1, $history);
}
public function testStufeHistoryMapperFindByMemberIdEmpty(): void {
$this->configureResultRows([]);
$mapper = new StufeHistoryMapper($this->db);
$history = $mapper->findByMemberId(999);
$this->assertCount(0, $history);
}
public function testStufeHistoryMapperFindAll(): void {
$this->configureResultRows([$this->stufeHistoryRow(1), $this->stufeHistoryRow(2)]);
$mapper = new StufeHistoryMapper($this->db);
$history = $mapper->findAll();
$this->assertCount(2, $history);
}
public function testStufeHistoryMapperFindByStufeId(): void {
$this->configureResultRows([$this->stufeHistoryRow(1)]);
$mapper = new StufeHistoryMapper($this->db);
$history = $mapper->findByStufeId(1);
$this->assertCount(1, $history);
}
public function testStufeHistoryMapperFindByStufeIdEmpty(): void {
$this->configureResultRows([]);
$mapper = new StufeHistoryMapper($this->db);
$history = $mapper->findByStufeId(999);
$this->assertCount(0, $history);
}
}