monitor/api/tests/Unit/Repository/RepoConfigRepositoryTest.php
fds2610-bot d72c945a61 Codeberg-Projekte des Sync ueber ein Modal am PO konfigurierbar machen
Klick auf den PO oeffnet ein Modal, in dem sich die Projekte (Owner/Repo +
Farbe), aus denen der Codeberg-Sync liest, hinzufuegen, bearbeiten und
loeschen lassen - Icon-Schaltflaechen mit Tooltip statt Textbuttons. Die
Projektliste war bisher eine Umgebungsvariable (CODEBERG_REPOS, Issue #26)
und wandert dafuer in eine neue Tabelle repo_configs (Migration
003_repo_configs.sql, geseedet mit den bisherigen drei Default-Projekten);
CodebergSyncService liest sie ueber RepoConfigRepository bei jedem Lauf
frisch ein, damit eine Aenderung im laufenden --watch-Prozess ohne Neustart
wirkt. Neue Endpunkte GET/POST/PATCH/DELETE /api/v1/repo-configs, Schreiben
hinter X-Admin-Key wie die Agenten-Registrierung; das Frontend fragt den Key
bei der ersten Aenderung per Prompt ab und merkt ihn sich im lokalen
Speicher. office-modal.js bekommt dafuer einen Oeffnen-Token
(isCurrentModal()), damit ein verspaetet aufloesender Request nach dem
Oeffnen eines anderen Modals nicht dessen Inhalt ueberschreibt.

Schliesst #27
2026-07-30 17:53:39 +02:00

99 lines
3.1 KiB
PHP

<?php
declare(strict_types=1);
namespace Monitor\Api\Tests\Repository;
use InvalidArgumentException;
use Monitor\Api\Db\Database;
use Monitor\Api\Http\NotFoundException;
use Monitor\Api\Repository\RepoConfigRepository;
use PHPUnit\Framework\TestCase;
final class RepoConfigRepositoryTest extends TestCase
{
private RepoConfigRepository $repository;
protected function setUp(): void
{
$db = Database::inMemory();
$db->pdo()->exec('DELETE FROM repo_configs');
$this->repository = new RepoConfigRepository($db);
}
public function testAllIsEmptyWithoutConfiguredProjects(): void
{
self::assertSame([], $this->repository->all());
}
public function testCreateNormalizesColorAndAppendsAtEndOfSortOrder(): void
{
$first = $this->repository->create(['owner' => 'fds2610', 'repo' => 'monitor', 'color' => 'fbca04']);
$second = $this->repository->create(['owner' => 'fds2610', 'repo' => 'Steuerkram', 'color' => '#28A745']);
self::assertSame('#fbca04', $first['color']);
self::assertSame('#28A745', $second['color']);
self::assertSame([$first['id'], $second['id']], array_map(
static fn (array $r): int => (int) $r['id'],
$this->repository->all()
));
}
public function testCreateRejectsMissingOwnerOrRepo(): void
{
$this->expectException(InvalidArgumentException::class);
$this->repository->create(['owner' => '', 'repo' => 'monitor', 'color' => '#fbca04']);
}
public function testCreateRejectsInvalidColor(): void
{
$this->expectException(InvalidArgumentException::class);
$this->repository->create(['owner' => 'fds2610', 'repo' => 'monitor', 'color' => 'nicht-hex']);
}
public function testCreateRejectsDuplicateOwnerRepo(): void
{
$this->repository->create(['owner' => 'fds2610', 'repo' => 'monitor', 'color' => '#fbca04']);
$this->expectException(InvalidArgumentException::class);
$this->repository->create(['owner' => 'fds2610', 'repo' => 'monitor', 'color' => '#000000']);
}
public function testUpdateChangesFields(): void
{
$repo = $this->repository->create(['owner' => 'fds2610', 'repo' => 'monitor', 'color' => '#fbca04']);
$updated = $this->repository->update((int) $repo['id'], [
'owner' => 'fds2610',
'repo' => 'monitor',
'color' => '#000000',
]);
self::assertSame('#000000', $updated['color']);
}
public function testUpdateUnknownIdThrowsNotFound(): void
{
$this->expectException(NotFoundException::class);
$this->repository->update(999, ['owner' => 'a', 'repo' => 'b', 'color' => '#000000']);
}
public function testDeleteRemovesEntry(): void
{
$repo = $this->repository->create(['owner' => 'fds2610', 'repo' => 'monitor', 'color' => '#fbca04']);
$this->repository->delete((int) $repo['id']);
self::assertSame([], $this->repository->all());
}
public function testDeleteUnknownIdThrowsNotFound(): void
{
$this->expectException(NotFoundException::class);
$this->repository->delete(999);
}
}