Initial commit: Pireon Admin — headless admin panel and public API

Laravel 13 + Filament 5 admin panel and REST API for a non-profit developer
collective, backed by the Pireon OAuth identity server. The public site is a
separate, server-rendered application consuming /api/public/v1.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-19 17:55:27 +02:00
commit 3897113313
534 changed files with 64050 additions and 0 deletions

View File

@@ -0,0 +1,186 @@
<?php
declare(strict_types=1);
namespace Tests\Unit;
use PHPUnit\Framework\Attributes\Test;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use SplFileInfo;
use Tests\TestCase;
/**
* The three languages have to say the same things.
*
* A key present in one file and absent from another does not fail anywhere: it
* renders as the raw key — `panel.widgets.stats.projects` — on a screen nobody
* is looking at in that language. It is the kind of fault that only ever gets
* found by a user, which is why it is worth a test.
*/
class TranslationCatalogueTest extends TestCase
{
/**
* @return array<int, string>
*/
protected function locales(): array
{
return array_keys((array) config('locales.supported'));
}
/**
* Every leaf of a translation file, as dotted keys.
*
* @param array<string, mixed> $translations
* @return array<int, string>
*/
protected function flatten(array $translations, string $prefix = ''): array
{
$keys = [];
foreach ($translations as $key => $value) {
$path = $prefix === '' ? (string) $key : "{$prefix}.{$key}";
if (is_array($value)) {
$keys = [...$keys, ...$this->flatten($value, $path)];
continue;
}
$keys[] = $path;
}
return $keys;
}
/**
* @return array<string, array<string, array<int, string>>>
*/
protected function catalogue(): array
{
$catalogue = [];
foreach ($this->locales() as $locale) {
foreach (glob(lang_path("{$locale}/*.php")) ?: [] as $file) {
$catalogue[basename($file)][$locale] = $this->flatten(require $file);
}
}
return $catalogue;
}
#[Test]
public function every_language_carries_the_same_keys(): void
{
$missing = [];
foreach ($this->catalogue() as $file => $byLocale) {
$everyKey = array_unique(array_merge(...array_values($byLocale)));
foreach ($this->locales() as $locale) {
foreach (array_diff($everyKey, $byLocale[$locale] ?? []) as $key) {
$missing[] = "[{$locale}] {$file} :: {$key}";
}
}
}
$this->assertSame([], $missing, "Untranslated keys:\n".implode("\n", $missing));
}
#[Test]
public function no_translation_is_left_empty(): void
{
$empty = [];
foreach ($this->locales() as $locale) {
foreach (glob(lang_path("{$locale}/*.php")) ?: [] as $file) {
foreach (require $file as $key => $value) {
foreach ($this->leaves($value, (string) $key) as $path => $text) {
if (trim((string) $text) === '') {
$empty[] = "[{$locale}] ".basename($file)." :: {$path}";
}
}
}
}
}
$this->assertSame([], $empty, "Empty translations:\n".implode("\n", $empty));
}
/**
* Every key the application actually asks for exists.
*
* The mirror image of the test above: that one catches a key translated in
* one language and forgotten in another, this one catches a key nobody
* translated at all.
*/
#[Test]
public function every_key_the_code_uses_has_a_translation(): void
{
$default = (string) config('locales.default');
$missing = [];
foreach ($this->sourceFiles() as $file) {
$source = (string) file_get_contents($file->getPathname());
preg_match_all(
'/(?:__|trans_choice)\(\s*[\'"]([a-z0-9_]+\.[a-z0-9_.\-]+)[\'"]/i',
$source,
$matches,
);
foreach ($matches[1] as $key) {
[$group] = explode('.', $key, 2);
// Keys belonging to a package (`filament-panels::…`) or built
// dynamically are not this test's business.
if (! file_exists(lang_path("{$default}/{$group}.php"))) {
continue;
}
if (! is_string(__($key)) || __($key) === $key) {
$missing[] = $key.' ('.str_replace(base_path().'/', '', $file->getPathname()).')';
}
}
}
$this->assertSame([], array_unique($missing), "Keys with no translation:\n".implode("\n", array_unique($missing)));
}
/**
* @return array<int, SplFileInfo>
*/
protected function sourceFiles(): array
{
$files = [];
foreach ([app_path(), resource_path('views')] as $directory) {
/** @var SplFileInfo $file */
foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($directory)) as $file) {
if ($file->isFile() && in_array($file->getExtension(), ['php'], true)) {
$files[] = $file;
}
}
}
return $files;
}
/**
* @return array<string, mixed>
*/
protected function leaves(mixed $value, string $prefix): array
{
if (! is_array($value)) {
return [$prefix => $value];
}
$leaves = [];
foreach ($value as $key => $child) {
$leaves += $this->leaves($child, "{$prefix}.{$key}");
}
return $leaves;
}
}