Die im Issue beschriebene Event-Anzeige (neuestes Event oben, aeltere schieben nach, Ueberschuss wird abgeschnitten) ist bereits seit Phase 4 umgesetzt (buildEventLines()/MAX_EVENT_LINES, F5) und braucht keine Aenderung. Neu ist die vierte, immer sichtbare KPI-Kachel ganz rechts im KPI-Monitor: "Hooks: n" zaehlt die seit dem letzten Start des api-Containers empfangenen Webhooks. Der Zaehler ist dateibasiert (Metrics\HookCounter), weil der eingebaute PHP-Server jede Anfrage als eigene Skriptausfuehrung behandelt; docker/entrypoint.sh setzt die Datei beim Start des api-Containers (nicht des sync-Containers) zurueck. Neuer oeffentlicher Endpunkt GET /api/v1/webhooks/stats liefert den Stand, das Frontend haengt ihn als eigene Kachel an (buildHooksKpi()) und reserviert ihr dafuer eine feste Kachel statt sie der MAX_KPI_TILES- Kappung der uebrigen KPIs zu unterwerfen. Der Wert fliesst zusaetzlich in den SSE-Fingerabdruck (ChangeFeed) ein. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
126 lines
4 KiB
PHP
126 lines
4 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Monitor\Api\Tests\Controller;
|
|
|
|
use Monitor\Api\Controller\WebhookController;
|
|
use Monitor\Api\Http\Request;
|
|
use Monitor\Api\Metrics\HookCounter;
|
|
use Monitor\Api\Sync\SyncTrigger;
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
final class WebhookControllerTest extends TestCase
|
|
{
|
|
private string $path;
|
|
private string $hooksPath;
|
|
private SyncTrigger $trigger;
|
|
private HookCounter $hooks;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
$this->path = sys_get_temp_dir() . '/monitor-webhook-' . uniqid() . '/sync-requested';
|
|
$this->hooksPath = sys_get_temp_dir() . '/monitor-webhook-hooks-' . uniqid() . '/hooks-count';
|
|
$this->trigger = new SyncTrigger($this->path);
|
|
$this->hooks = new HookCounter($this->hooksPath);
|
|
}
|
|
|
|
protected function tearDown(): void
|
|
{
|
|
if (file_exists($this->path)) {
|
|
unlink($this->path);
|
|
}
|
|
if (is_dir(dirname($this->path))) {
|
|
rmdir(dirname($this->path));
|
|
}
|
|
if (file_exists($this->hooksPath)) {
|
|
unlink($this->hooksPath);
|
|
}
|
|
if (is_dir(dirname($this->hooksPath))) {
|
|
rmdir(dirname($this->hooksPath));
|
|
}
|
|
}
|
|
|
|
public function testIssueEventRequestsASync(): void
|
|
{
|
|
$controller = new WebhookController($this->trigger, $this->hooks);
|
|
|
|
$response = $controller->codeberg($this->request('issues'));
|
|
|
|
self::assertSame(202, $response->status);
|
|
self::assertTrue($this->trigger->consume());
|
|
}
|
|
|
|
/**
|
|
* Codeberg schickt beim Anlegen des Webhooks ein "ping" zum Testen der Zustellung - das
|
|
* ist keine Issue-Aenderung und soll keinen Codeberg-Request ausloesen.
|
|
*/
|
|
public function testPingIsAnsweredWithoutRequestingASync(): void
|
|
{
|
|
$controller = new WebhookController($this->trigger, $this->hooks);
|
|
|
|
$response = $controller->codeberg($this->request('ping'));
|
|
|
|
self::assertSame(200, $response->status);
|
|
self::assertFalse($this->trigger->consume());
|
|
}
|
|
|
|
/** Unbekannte Events loesen einen Sync aus - lieber einmal zu viel als einen verpassten. */
|
|
public function testUnknownEventStillRequestsASync(): void
|
|
{
|
|
$controller = new WebhookController($this->trigger, $this->hooks);
|
|
|
|
$response = $controller->codeberg($this->request('issue_label'));
|
|
|
|
self::assertSame(202, $response->status);
|
|
self::assertTrue($this->trigger->consume());
|
|
}
|
|
|
|
/**
|
|
* Hooks-KPI (Issue #38): jede angenommene Zustellung zaehlt, auch "ping" - der Endpunkt
|
|
* meldet nur, dass etwas empfangen wurde, nicht ob es fachlich relevant war.
|
|
*/
|
|
public function testCodebergIncrementsHookCounterRegardlessOfEventType(): void
|
|
{
|
|
$controller = new WebhookController($this->trigger, $this->hooks);
|
|
|
|
$controller->codeberg($this->request('issues'));
|
|
$controller->codeberg($this->request('ping'));
|
|
$controller->codeberg($this->request('issue_label'));
|
|
|
|
self::assertSame(3, $this->hooks->count());
|
|
}
|
|
|
|
public function testStatsReturnsCurrentHookCount(): void
|
|
{
|
|
$controller = new WebhookController($this->trigger, $this->hooks);
|
|
$controller->codeberg($this->request('issues'));
|
|
$controller->codeberg($this->request('issues'));
|
|
|
|
$response = $controller->stats(Request::create('GET', '/api/v1/webhooks/stats'));
|
|
|
|
self::assertSame(200, $response->status);
|
|
self::assertSame(2, $response->body()['count']);
|
|
}
|
|
|
|
public function testStatsReturnsZeroWithoutAnyReceivedWebhook(): void
|
|
{
|
|
$controller = new WebhookController($this->trigger, $this->hooks);
|
|
|
|
$response = $controller->stats(Request::create('GET', '/api/v1/webhooks/stats'));
|
|
|
|
self::assertSame(0, $response->body()['count']);
|
|
}
|
|
|
|
private function request(string $event): Request
|
|
{
|
|
return Request::create(
|
|
'POST',
|
|
'/api/v1/webhooks/codeberg',
|
|
[],
|
|
[],
|
|
['X-Gitea-Event' => $event],
|
|
'{}'
|
|
);
|
|
}
|
|
}
|