test(aggregator): cover concurrent fetch failure modes

Mocks startFetch()/resolveFetch() instead of fetch(), and splits the
old single failure test into separate start-throws and
resolve-throws cases since those are now distinct call sites.
This commit is contained in:
2026-07-07 13:32:11 +02:00
parent 1e2f022579
commit 8fb7781227
@@ -49,7 +49,7 @@ final class ContributionAggregatorTest extends TestCase
{ {
$provider = $this->createStub(ProviderInterface::class); $provider = $this->createStub(ProviderInterface::class);
$provider->method('isConfigured')->willReturn(true); $provider->method('isConfigured')->willReturn(true);
$provider->method('fetch')->willReturn(['2024-01-01' => 3]); $provider->method('resolveFetch')->willReturn(['2024-01-01' => 3]);
$aggregator = new ContributionAggregator([$provider], $this->logger); $aggregator = new ContributionAggregator([$provider], $this->logger);
@@ -63,11 +63,11 @@ final class ContributionAggregatorTest extends TestCase
{ {
$providerA = $this->createStub(ProviderInterface::class); $providerA = $this->createStub(ProviderInterface::class);
$providerA->method('isConfigured')->willReturn(true); $providerA->method('isConfigured')->willReturn(true);
$providerA->method('fetch')->willReturn(['2024-01-01' => 3, '2024-01-02' => 1]); $providerA->method('resolveFetch')->willReturn(['2024-01-01' => 3, '2024-01-02' => 1]);
$providerB = $this->createStub(ProviderInterface::class); $providerB = $this->createStub(ProviderInterface::class);
$providerB->method('isConfigured')->willReturn(true); $providerB->method('isConfigured')->willReturn(true);
$providerB->method('fetch')->willReturn(['2024-01-01' => 2, '2024-01-03' => 5]); $providerB->method('resolveFetch')->willReturn(['2024-01-01' => 2, '2024-01-03' => 5]);
$aggregator = new ContributionAggregator([$providerA, $providerB], $this->logger); $aggregator = new ContributionAggregator([$providerA, $providerB], $this->logger);
@@ -77,15 +77,15 @@ final class ContributionAggregatorTest extends TestCase
} }
#[Test] #[Test]
public function it_continues_fetching_remaining_providers_when_one_throws(): void public function it_continues_fetching_remaining_providers_when_one_throws_on_start(): void
{ {
$failing = $this->createStub(ProviderInterface::class); $failing = $this->createStub(ProviderInterface::class);
$failing->method('isConfigured')->willReturn(true); $failing->method('isConfigured')->willReturn(true);
$failing->method('fetch')->willThrowException(new \RuntimeException('Network error')); $failing->method('startFetch')->willThrowException(new \RuntimeException('Network error'));
$healthy = $this->createStub(ProviderInterface::class); $healthy = $this->createStub(ProviderInterface::class);
$healthy->method('isConfigured')->willReturn(true); $healthy->method('isConfigured')->willReturn(true);
$healthy->method('fetch')->willReturn(['2024-01-01' => 7]); $healthy->method('resolveFetch')->willReturn(['2024-01-01' => 7]);
$aggregator = new ContributionAggregator([$failing, $healthy], $this->logger); $aggregator = new ContributionAggregator([$failing, $healthy], $this->logger);
@@ -95,14 +95,45 @@ final class ContributionAggregatorTest extends TestCase
} }
#[Test] #[Test]
public function it_logs_a_warning_when_a_provider_fetch_throws(): void public function it_continues_fetching_remaining_providers_when_one_throws_on_resolve(): void
{
$failing = $this->createStub(ProviderInterface::class);
$failing->method('isConfigured')->willReturn(true);
$failing->method('resolveFetch')->willThrowException(new \RuntimeException('Network error'));
$healthy = $this->createStub(ProviderInterface::class);
$healthy->method('isConfigured')->willReturn(true);
$healthy->method('resolveFetch')->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_start_fetch_throws(): void
{ {
$logger = $this->createMock(LoggerInterface::class); $logger = $this->createMock(LoggerInterface::class);
$logger->expects($this->once())->method('warning'); $logger->expects($this->once())->method('warning');
$provider = $this->createStub(ProviderInterface::class); $provider = $this->createStub(ProviderInterface::class);
$provider->method('isConfigured')->willReturn(true); $provider->method('isConfigured')->willReturn(true);
$provider->method('fetch')->willThrowException(new \RuntimeException('fail')); $provider->method('startFetch')->willThrowException(new \RuntimeException('fail'));
(new ContributionAggregator([$provider], $logger))->aggregate();
}
#[Test]
public function it_logs_a_warning_when_a_provider_resolve_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('resolveFetch')->willThrowException(new \RuntimeException('fail'));
(new ContributionAggregator([$provider], $logger))->aggregate(); (new ContributionAggregator([$provider], $logger))->aggregate();
} }