monitor/api/tests/Unit/Sync/RepoHealthCheckerTest.php
fds2610-bot ec8c74c0f8 Neue KPI: offene PRs ueber alle Projekte (Issue #59)
CodebergSyncService berechnet am Ende jedes Sync-Laufs (periodisch und
webhook-getriggert, auch bei PR-Aenderungen) die Summe der offenen Pull
Requests aller konfigurierten Projekte und speichert sie als KPI
"open_prs" im bestehenden generischen KPI-Repository - sie erscheint
damit automatisch im KPI-Monitor der Wand und im KPI-Modal.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-21 11:57:08 +02:00

135 lines
4.9 KiB
PHP

<?php
declare(strict_types=1);
namespace Monitor\Api\Tests\Sync;
use Monitor\Api\Sync\CodebergApiException;
use Monitor\Api\Sync\CodebergClient;
use Monitor\Api\Sync\RepoHealthChecker;
use PHPUnit\Framework\TestCase;
use RuntimeException;
final class RepoHealthCheckerTest extends TestCase
{
public function testWriteAccessOkWhenPushPermissionTrue(): void
{
$client = $this->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<int, array{active: bool, url: string, content_type: string}>|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;
}
};
}
}