| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102 |
- <?php
- /*
- * Copyright 2026 Alessandro Chiapparini <sprint_planer_web@chiapparini.org>
- * SPDX-License-Identifier: Apache-2.0
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * See the LICENSE file in the project root for the full license text.
- */
- declare(strict_types=1);
- namespace App\Tests\Domain;
- use App\Domain\SprintWeek;
- use InvalidArgumentException;
- use PHPUnit\Framework\TestCase;
- /**
- * Phase 12: SprintWeek's weekday helpers. Pure logic — no DB.
- */
- final class SprintWeekTest extends TestCase
- {
- public function testPopcountCoversAllFiveBits(): void
- {
- $this->assertSame(0, SprintWeek::popcount(0));
- $this->assertSame(5, SprintWeek::popcount(31));
- $this->assertSame(1, SprintWeek::popcount(1));
- $this->assertSame(4, SprintWeek::popcount(15));
- // bits outside the 5-day range are clamped away
- $this->assertSame(5, SprintWeek::popcount(0xFFFF));
- }
- public function testMaskToDaysReturnsCanonicalOrder(): void
- {
- $this->assertSame(['Mo', 'Di', 'Mi', 'Do', 'Fr'], SprintWeek::maskToDays(31));
- $this->assertSame(['Mo', 'Fr'], SprintWeek::maskToDays(0b10001));
- $this->assertSame([], SprintWeek::maskToDays(0));
- }
- public function testDaysToMaskRoundTripsBothDirections(): void
- {
- foreach ([0, 1, 2, 7, 15, 17, 31] as $m) {
- $days = SprintWeek::maskToDays($m);
- $this->assertSame($m, SprintWeek::daysToMask($days), "round-trip for mask={$m}");
- }
- }
- public function testDaysToMaskRejectsUnknownLabel(): void
- {
- $this->expectException(InvalidArgumentException::class);
- SprintWeek::daysToMask(['Mo', 'Sa']); // Sa not in Mo–Fr set
- }
- public function testDaysToMaskRejectsDuplicates(): void
- {
- $this->expectException(InvalidArgumentException::class);
- SprintWeek::daysToMask(['Mo', 'Mo']);
- }
- public function testHasDayMatchesMaskBit(): void
- {
- $w = new SprintWeek(
- id: 1, sprintId: 1, sortOrder: 1, isoWeek: 1,
- startDate: '2026-01-05', maxWorkingDays: 3.0,
- activeDaysMask: 0b00111, // Mo Di Mi
- );
- $this->assertTrue($w->hasDay('Mo'));
- $this->assertTrue($w->hasDay('Mi'));
- $this->assertFalse($w->hasDay('Do'));
- $this->assertFalse($w->hasDay('Fr'));
- // Unknown label is a miss, not an error.
- $this->assertFalse($w->hasDay('Sa'));
- }
- public function testWithMaskRecomputesMaxWorkingDays(): void
- {
- $w = new SprintWeek(
- id: 1, sprintId: 1, sortOrder: 1, isoWeek: 1,
- startDate: '2026-01-05', maxWorkingDays: 5.0,
- activeDaysMask: 31,
- );
- $dropped = $w->withMask(0b01111); // Mo Di Mi Do
- $this->assertSame(15, $dropped->activeDaysMask);
- $this->assertSame(4.0, $dropped->maxWorkingDays);
- $this->assertSame(['Mo', 'Di', 'Mi', 'Do'], $dropped->activeDays());
- // Bits outside Mo–Fr are trimmed.
- $this->assertSame(31, $w->withMask(0xFF)->activeDaysMask);
- }
- public function testAuditSnapshotIncludesMask(): void
- {
- $w = new SprintWeek(
- id: 77, sprintId: 9, sortOrder: 2, isoWeek: 8,
- startDate: '2026-02-16', maxWorkingDays: 2.0,
- activeDaysMask: 0b00011,
- );
- $snap = $w->toAuditSnapshot();
- $this->assertSame(0b00011, $snap['active_days_mask']);
- $this->assertSame(2.0, $snap['max_working_days']);
- }
- }
|