$fresh */ private function makeProvider( string $name, bool $configured = true, ?array $fresh = null, ?\Throwable $throws = null, ): ProviderInterface { $provider = $this->createStub(ProviderInterface::class); $provider->method('getName')->willReturn($name); $provider->method('isConfigured')->willReturn($configured); if ($throws !== null) { $provider->method('startFetch')->willThrowException($throws); } elseif ($fresh !== null) { $provider->method('resolveFetch')->willReturn($fresh); } return $provider; } /** @param iterable $providers */ private function makeTester(iterable $providers, ContributionStore $store): CommandTester { $command = new RefetchContributionsCommand($providers, $store); $application = new Application(); $application->add($command); return new CommandTester($application->find('graph:contributions:refetch')); } #[Test] public function it_refetches_the_default_365_day_window_for_all_configured_providers(): void { $store = new ContributionStore(':memory:'); $provider = $this->makeProvider('github', fresh: ['2024-06-10' => 3]); $tester = $this->makeTester([$provider], $store); $exitCode = $tester->execute([]); $this->assertSame(0, $exitCode); $this->assertCount(1, $store->all('github')); } #[Test] public function it_skips_unconfigured_providers_by_default(): void { $store = new ContributionStore(':memory:'); $provider = $this->makeProvider('github', configured: false); $tester = $this->makeTester([$provider], $store); $tester->execute([]); $this->assertCount(0, $store->all('github')); } #[Test] public function it_fails_cleanly_on_an_unknown_provider_name(): void { $store = new ContributionStore(':memory:'); $provider = $this->makeProvider('github'); $tester = $this->makeTester([$provider], $store); $exitCode = $tester->execute(['--provider' => 'bogus']); $this->assertSame(1, $exitCode); $this->assertStringContainsString('Unknown provider: bogus', $tester->getDisplay()); } #[Test] public function it_restricts_to_the_named_provider(): void { $store = new ContributionStore(':memory:'); $github = $this->makeProvider('github', fresh: ['2024-06-10' => 1]); $gitlab = $this->makeProvider('gitlab', fresh: ['2024-06-10' => 1]); $tester = $this->makeTester([$github, $gitlab], $store); $tester->execute(['--provider' => 'github']); $this->assertCount(1, $store->all('github')); $this->assertCount(0, $store->all('gitlab')); } #[Test] public function it_continues_with_remaining_providers_when_one_throws(): void { $store = new ContributionStore(':memory:'); $failing = $this->makeProvider('gitlab', throws: new \RuntimeException('boom')); $healthy = $this->makeProvider('github', fresh: ['2024-06-10' => 2]); $tester = $this->makeTester([$failing, $healthy], $store); $exitCode = $tester->execute([]); $this->assertSame(0, $exitCode); $this->assertCount(1, $store->all('github')); $this->assertCount(0, $store->all('gitlab')); } #[Test] public function it_fails_when_every_provider_errors(): void { $store = new ContributionStore(':memory:'); $failing = $this->makeProvider('github', throws: new \RuntimeException('boom')); $tester = $this->makeTester([$failing], $store); $exitCode = $tester->execute([]); $this->assertSame(1, $exitCode); } #[Test] public function it_splits_a_multi_year_range_into_365_day_chunks(): void { $store = new ContributionStore(':memory:'); $calls = 0; $provider = $this->createStub(ProviderInterface::class); $provider->method('getName')->willReturn('github'); $provider->method('isConfigured')->willReturn(true); $provider->method('resolveFetch')->willReturnCallback(function () use (&$calls): array { $calls++; return []; }); $tester = $this->makeTester([$provider], $store); $tester->execute(['--from' => '2020-01-01', '--to' => '2023-01-01']); $this->assertGreaterThan(1, $calls); } }