logger = $this->createStub(LoggerInterface::class); } #[Test] public function it_returns_empty_array_when_no_providers_are_given(): void { $aggregator = new ContributionAggregator([], $this->logger); $result = $aggregator->aggregate(); $this->assertSame([], $result); } #[Test] public function it_skips_unconfigured_providers(): void { $provider = $this->createStub(ProviderInterface::class); $provider->method('isConfigured')->willReturn(false); $aggregator = new ContributionAggregator([$provider], $this->logger); $result = $aggregator->aggregate(); $this->assertSame([], $result); } #[Test] public function it_returns_contributions_from_a_configured_provider(): void { $provider = $this->createStub(ProviderInterface::class); $provider->method('isConfigured')->willReturn(true); $provider->method('fetch')->willReturn(['2024-01-01' => 3]); $aggregator = new ContributionAggregator([$provider], $this->logger); $result = $aggregator->aggregate(); $this->assertSame(['2024-01-01' => 3], $result); } #[Test] public function it_sums_contributions_from_multiple_providers_on_the_same_date(): void { $providerA = $this->createStub(ProviderInterface::class); $providerA->method('isConfigured')->willReturn(true); $providerA->method('fetch')->willReturn(['2024-01-01' => 3, '2024-01-02' => 1]); $providerB = $this->createStub(ProviderInterface::class); $providerB->method('isConfigured')->willReturn(true); $providerB->method('fetch')->willReturn(['2024-01-01' => 2, '2024-01-03' => 5]); $aggregator = new ContributionAggregator([$providerA, $providerB], $this->logger); $result = $aggregator->aggregate(); $this->assertSame(['2024-01-01' => 5, '2024-01-02' => 1, '2024-01-03' => 5], $result); } #[Test] public function it_continues_fetching_remaining_providers_when_one_throws(): void { $failing = $this->createStub(ProviderInterface::class); $failing->method('isConfigured')->willReturn(true); $failing->method('fetch')->willThrowException(new \RuntimeException('Network error')); $healthy = $this->createStub(ProviderInterface::class); $healthy->method('isConfigured')->willReturn(true); $healthy->method('fetch')->willReturn(['2024-01-01' => 7]); $aggregator = new ContributionAggregator([$failing, $healthy], $this->logger); $result = $aggregator->aggregate(); $this->assertSame(['2024-01-01' => 7], $result); } #[Test] public function it_logs_a_warning_when_a_provider_fetch_throws(): void { $logger = $this->createMock(LoggerInterface::class); $logger->expects($this->once())->method('warning'); $provider = $this->createStub(ProviderInterface::class); $provider->method('isConfigured')->willReturn(true); $provider->method('fetch')->willThrowException(new \RuntimeException('fail')); (new ContributionAggregator([$provider], $logger))->aggregate(); } }