From 8a0e5de4a886e5ece14d85dded5f3bb343dce865 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20M=C3=BChl?= Date: Thu, 9 Apr 2026 21:20:07 +0200 Subject: [PATCH] test(08-01): add failing tests for aggregation methods and filters --- Tests/Service/HeatmapServiceTest.php | 187 +++++++++++++++++++++++++++ 1 file changed, 187 insertions(+) diff --git a/Tests/Service/HeatmapServiceTest.php b/Tests/Service/HeatmapServiceTest.php index ab8f670..d325b88 100644 --- a/Tests/Service/HeatmapServiceTest.php +++ b/Tests/Service/HeatmapServiceTest.php @@ -63,6 +63,172 @@ class HeatmapServiceTest extends TestCase $this->assertEquals(1.51, $result[0]['hours']); } + public function testGetHourlyAggregationReturnsFormattedResults(): void + { + $service = $this->createServiceWithNativeResults([ + ['hour_slot' => 9, 'duration' => 7200, 'count' => 5], + ]); + + $user = $this->createMock(User::class); + $user->method('getTimezone')->willReturn('Europe/Berlin'); + $user->method('getId')->willReturn(1); + + $result = $service->getHourlyAggregation( + $user, + new \DateTimeImmutable('2026-04-01'), + new \DateTimeImmutable('2026-04-30') + ); + + $this->assertCount(1, $result); + $this->assertSame(9, $result[0]['hour']); + $this->assertSame(2.0, $result[0]['hours']); + $this->assertSame(5, $result[0]['count']); + } + + public function testGetHourlyAggregationReturnsEmptyForNoData(): void + { + $service = $this->createServiceWithNativeResults([]); + + $user = $this->createMock(User::class); + $user->method('getTimezone')->willReturn('UTC'); + $user->method('getId')->willReturn(1); + + $result = $service->getHourlyAggregation( + $user, + new \DateTimeImmutable('2026-04-01'), + new \DateTimeImmutable('2026-04-30') + ); + + $this->assertCount(0, $result); + } + + public function testGetDayHourAggregationReturnsFormattedResults(): void + { + // MySQL DAYOFWEEK: 2 = Monday + // weekStart=monday -> Monday = day 0 + $service = $this->createServiceWithNativeResults([ + ['dow' => 2, 'hour_slot' => 14, 'duration' => 3600, 'count' => 1], + ]); + + $user = $this->createMock(User::class); + $user->method('getTimezone')->willReturn('Europe/Berlin'); + $user->method('getId')->willReturn(1); + + $result = $service->getDayHourAggregation( + $user, + new \DateTimeImmutable('2026-04-01'), + new \DateTimeImmutable('2026-04-30'), + null, null, null, + 'monday' + ); + + $this->assertCount(1, $result); + $this->assertSame(0, $result[0]['day']); + $this->assertSame(14, $result[0]['hour']); + $this->assertSame(1.0, $result[0]['hours']); + $this->assertSame(1, $result[0]['count']); + } + + public function testGetDayHourAggregationSundayStart(): void + { + // MySQL DAYOFWEEK: 2 = Monday + // weekStart=sunday -> Monday = day 1 + $service = $this->createServiceWithNativeResults([ + ['dow' => 2, 'hour_slot' => 14, 'duration' => 3600, 'count' => 1], + ]); + + $user = $this->createMock(User::class); + $user->method('getTimezone')->willReturn('Europe/Berlin'); + $user->method('getId')->willReturn(1); + + $result = $service->getDayHourAggregation( + $user, + new \DateTimeImmutable('2026-04-01'), + new \DateTimeImmutable('2026-04-30'), + null, null, null, + 'sunday' + ); + + $this->assertCount(1, $result); + $this->assertSame(1, $result[0]['day']); + $this->assertSame(14, $result[0]['hour']); + $this->assertSame(1.0, $result[0]['hours']); + $this->assertSame(1, $result[0]['count']); + } + + public function testGetDailyAggregationWithActivityFilter(): void + { + $service = $this->createServiceWithResults([ + ['day' => '2026-04-01', 'duration' => 3600, 'count' => 1], + ]); + + $result = $service->getDailyAggregation( + $this->createMock(User::class), + new \DateTimeImmutable('2026-04-01'), + new \DateTimeImmutable('2026-04-30'), + null, + null, + 42 + ); + + $this->assertCount(1, $result); + } + + public function testGetDailyAggregationWithCustomerFilter(): void + { + $service = $this->createServiceWithResults([ + ['day' => '2026-04-01', 'duration' => 3600, 'count' => 1], + ]); + + $result = $service->getDailyAggregation( + $this->createMock(User::class), + new \DateTimeImmutable('2026-04-01'), + new \DateTimeImmutable('2026-04-30'), + null, + 99 + ); + + $this->assertCount(1, $result); + } + + public function testGetUserCustomersReturnsFormattedResults(): void + { + $service = $this->createServiceWithResults([ + ['customerId' => 1, 'name' => 'Acme'], + ]); + + $result = $service->getUserCustomers($this->createMock(User::class)); + + $this->assertCount(1, $result); + $this->assertSame(1, $result[0]['id']); + $this->assertSame('Acme', $result[0]['name']); + } + + public function testGetUserActivitiesReturnsFormattedResults(): void + { + $service = $this->createServiceWithResults([ + ['activityId' => 5, 'name' => 'Dev'], + ]); + + $result = $service->getUserActivities($this->createMock(User::class)); + + $this->assertCount(1, $result); + $this->assertSame(5, $result[0]['id']); + $this->assertSame('Dev', $result[0]['name']); + } + + public function testGetUserActivitiesWithProjectScope(): void + { + $service = $this->createServiceWithResults([ + ['activityId' => 5, 'name' => 'Dev'], + ]); + + $result = $service->getUserActivities($this->createMock(User::class), 10); + + $this->assertCount(1, $result); + $this->assertSame(5, $result[0]['id']); + } + private function createServiceWithResults(array $results): HeatmapService { $query = $this->createMock(AbstractQuery::class); @@ -75,6 +241,7 @@ class HeatmapServiceTest extends TestCase $qb->method('setParameter')->willReturnSelf(); $qb->method('groupBy')->willReturnSelf(); $qb->method('orderBy')->willReturnSelf(); + $qb->method('join')->willReturnSelf(); $qb->method('expr')->willReturn(new Expr()); $qb->method('getQuery')->willReturn($query); @@ -83,4 +250,24 @@ class HeatmapServiceTest extends TestCase return new HeatmapService($repo); } + + private function createServiceWithNativeResults(array $results): HeatmapService + { + $statement = $this->createMock(\Doctrine\DBAL\Result::class); + $statement->method('fetchAllAssociative')->willReturn($results); + + $connection = $this->createMock(\Doctrine\DBAL\Connection::class); + $connection->method('executeQuery')->willReturn($statement); + + $em = $this->createMock(\Doctrine\ORM\EntityManagerInterface::class); + $em->method('getConnection')->willReturn($connection); + + $repo = $this->getMockBuilder(TimesheetRepository::class) + ->disableOriginalConstructor() + ->onlyMethods(['getEntityManager']) + ->getMock(); + $repo->method('getEntityManager')->willReturn($em); + + return new HeatmapService($repo); + } }