fakeClient(repoPermissions: ['push' => true]); $checks = (new RepoHealthChecker($client))->check('fds2610', 'monitor'); self::assertTrue($checks['write_access']['ok']); } public function testWriteAccessFailsWhenPushPermissionFalse(): void { $client = $this->fakeClient(repoPermissions: ['push' => false]); $checks = (new RepoHealthChecker($client))->check('fds2610', 'monitor'); self::assertFalse($checks['write_access']['ok']); self::assertStringContainsString('keine Schreibrechte', $checks['write_access']['message']); } public function testWriteAccessFailsWithNotFoundMessageOn404(): void { $client = $this->fakeClient(repoPermissionsException: new CodebergApiException(404, 'nope')); $checks = (new RepoHealthChecker($client))->check('fds2610', 'unbekannt'); self::assertFalse($checks['write_access']['ok']); self::assertStringContainsString('nicht gefunden', $checks['write_access']['message']); } public function testWebhookOkWhenActiveJsonHookPointsToOwnEndpoint(): void { $client = $this->fakeClient(listWebhooks: [ ['active' => true, 'url' => 'https://monitor.example.org/api/v1/webhooks/codeberg', 'content_type' => 'json'], ]); $checks = (new RepoHealthChecker($client))->check('fds2610', 'monitor'); self::assertTrue($checks['webhook']['ok']); } public function testWebhookFailsWhenNoMatchingHookPresent(): void { $client = $this->fakeClient(listWebhooks: [ ['active' => true, 'url' => 'https://example.org/other', 'content_type' => 'json'], ['active' => false, 'url' => 'https://monitor.example.org/api/v1/webhooks/codeberg', 'content_type' => 'json'], ]); $checks = (new RepoHealthChecker($client))->check('fds2610', 'monitor'); self::assertFalse($checks['webhook']['ok']); } public function testWebhookFailsWithPermissionMessageOn403(): void { $client = $this->fakeClient(listWebhooksException: new CodebergApiException(403, 'forbidden')); $checks = (new RepoHealthChecker($client))->check('fds2610', 'monitor'); self::assertFalse($checks['webhook']['ok']); self::assertStringContainsString('keine Admin-Rechte', $checks['webhook']['message']); } public function testFailsGracefullyWhenCodebergUnreachable(): void { $client = $this->fakeClient(repoPermissionsException: new RuntimeException('Codeberg-API nicht erreichbar: timeout')); $checks = (new RepoHealthChecker($client))->check('fds2610', 'monitor'); self::assertFalse($checks['write_access']['ok']); self::assertStringContainsString('nicht erreichbar', $checks['write_access']['message']); } /** * @param array{push: bool}|null $repoPermissions * @param array|null $listWebhooks */ private function fakeClient( ?array $repoPermissions = null, ?RuntimeException $repoPermissionsException = null, ?array $listWebhooks = null, ?RuntimeException $listWebhooksException = null, ): CodebergClient { return new class ($repoPermissions, $repoPermissionsException, $listWebhooks, $listWebhooksException) implements CodebergClient { public function __construct( private readonly ?array $repoPermissions, private readonly ?RuntimeException $repoPermissionsException, private readonly ?array $listWebhooks, private readonly ?RuntimeException $listWebhooksException, ) { } public function listIssues(string $owner, string $repo, string $state): array { return []; } public function repoPermissions(string $owner, string $repo): array { if ($this->repoPermissionsException !== null) { throw $this->repoPermissionsException; } return $this->repoPermissions ?? ['push' => true]; } public function listWebhooks(string $owner, string $repo): array { if ($this->listWebhooksException !== null) { throw $this->listWebhooksException; } return $this->listWebhooks ?? []; } public function countOpenPullRequests(string $owner, string $repo): int { return 0; } }; } }