Toolvana: production-ready media tools platform (CodeIgniter 4)

- 134-tool registry with programmatic SEO (unique titles/H1/descriptions,
  JSON-LD graphs, sitemap index, canonical 301 enforcement via required filter)
- DB-backed job queue (SKIP LOCKED) with drivers: Ffmpeg, Images (GD),
  Pdf (qpdf/gs/poppler), Youtube (thumbnails), Qr (server-side PNG)
- Security: SSRF guard, MIME validation, rate limits, API-key auth,
  bcrypt admin login, security headers
- Admin panel: dashboard, tools/categories/guides CRUD, SEO audit,
  analytics, job inspector with retry, system health, feature flags
- Docker deployment (nginx + web/api FPM pools + scalable workers),
  PHPUnit suite (19 tests / 1139 assertions), PWA manifest + service worker
This commit is contained in:
deepseek
2026-08-23 07:10:30 +00:00
commit beaf0e1f37
217 changed files with 19619 additions and 0 deletions
+97
View File
@@ -0,0 +1,97 @@
<?php
declare(strict_types=1);
namespace Tests\Unit;
use App\Libraries\CatalogBuilder;
use App\Libraries\FormatCatalog;
use App\Libraries\Pipeline\Youtube as YoutubeLib;
use App\Libraries\UrlGuard;
use CodeIgniter\Test\CIUnitTestCase;
/**
* Pure unit tests — no database required.
*/
final class CatalogTest extends CIUnitTestCase
{
public function testCatalogBuildsUniqueSlugs(): void
{
$slugs = array_column(CatalogBuilder::build(), 'slug');
$this->assertSame($slugs, array_values(array_unique($slugs)), 'Tool slugs must be unique');
$this->assertGreaterThan(100, count($slugs));
}
public function testEveryToolHasCompleteSeoFields(): void
{
foreach (CatalogBuilder::build() as $tool) {
$this->assertNotSame('', trim((string) $tool['seo_title']), "seo_title empty for {$tool['slug']}");
$this->assertNotSame('', trim((string) $tool['h1']), "h1 empty for {$tool['slug']}");
$this->assertGreaterThanOrEqual(20, mb_strlen((string) $tool['seo_description']), "short description for {$tool['slug']}");
$this->assertNotEmpty($tool['how_to'], "how_to missing for {$tool['slug']}");
$this->assertNotEmpty($tool['faqs'], "faqs missing for {$tool['slug']}");
$this->assertLessThanOrEqual(65, mb_strlen((string) $tool['seo_title']) + 0, "title too long for {$tool['slug']}");
}
}
public function testConverterTitlesAreFormatSpecific(): void
{
$bySlug = [];
foreach (CatalogBuilder::build() as $tool) {
$bySlug[$tool['slug']] = $tool;
}
// each converter page must mention its actual formats
foreach (['jpg-to-png', 'mp4-to-webm', 'flac-to-mp3'] as $slug) {
$this->assertArrayHasKey($slug, $bySlug);
$title = (string) $bySlug[$slug]['seo_title'];
[$from, $to] = explode('-to-', $slug);
$this->assertStringContainsString(FormatCatalog::name($from), $title);
$this->assertStringContainsString(FormatCatalog::name($to), $title);
}
}
public function testYoutubeIdExtraction(): void
{
$cases = [
'https://www.youtube.com/watch?v=dQw4w9WgXcQ' => 'dQw4w9WgXcQ',
'https://youtu.be/dQw4w9WgXcQ?t=42' => 'dQw4w9WgXcQ',
'https://www.youtube.com/shorts/abcdefghijk' => 'abcdefghijk',
'https://www.youtube.com/embed/abcdefghijk' => 'abcdefghijk',
'dQw4w9WgXcQ' => 'dQw4w9WgXcQ',
'https://evil.com/watch?v=dQw4w9WgXcQ' => null,
'not a url' => null,
];
foreach ($cases as $input => $expected) {
$this->assertSame($expected, YoutubeLib::extractId($input), "Failed extracting from: {$input}");
}
}
public function testUrlGuardBlocksPrivateNetworks(): void
{
$guard = new UrlGuard();
$this->expectException(\InvalidArgumentException::class);
$guard->check('http://127.0.0.1/secret');
}
public function testUrlGuardBlocksFileScheme(): void
{
$guard = new UrlGuard();
$this->expectException(\InvalidArgumentException::class);
$guard->check('file:///etc/passwd');
}
public function testIsPublicIp(): void
{
$guard = new UrlGuard();
$this->assertFalse($guard->isPublicIp('10.0.0.5'));
$this->assertFalse($guard->isPublicIp('192.168.1.1'));
$this->assertFalse($guard->isPublicIp('169.254.169.254')); // cloud metadata
$this->assertTrue($guard->isPublicIp('142.250.185.78'));
}
}
+49
View File
@@ -0,0 +1,49 @@
<?php
use CodeIgniter\Test\CIUnitTestCase;
use Config\App;
use Tests\Support\Libraries\ConfigReader;
/**
* @internal
*/
final class HealthTest extends CIUnitTestCase
{
public function testIsDefinedAppPath(): void
{
$this->assertTrue(defined('APPPATH'));
}
public function testBaseUrlHasBeenSet(): void
{
$validation = service('validation');
$env = false;
// Check the baseURL in .env
if (is_file(HOMEPATH . '.env')) {
$env = preg_grep('/^app\.baseURL = ./', file(HOMEPATH . '.env')) !== false;
}
if ($env) {
// BaseURL in .env is a valid URL?
// phpunit.dist.xml sets app.baseURL in $_SERVER
// So if you set app.baseURL in .env, it takes precedence
$config = new App();
$this->assertTrue(
$validation->check($config->baseURL, 'valid_url'),
'baseURL "' . $config->baseURL . '" in .env is not valid URL',
);
}
// Get the baseURL in app/Config/App.php
// You can't use Config\App, because phpunit.dist.xml sets app.baseURL
$reader = new ConfigReader();
// BaseURL in app/Config/App.php is a valid URL?
$this->assertTrue(
$validation->check($reader->baseURL, 'valid_url'),
'baseURL "' . $reader->baseURL . '" in app/Config/App.php is not valid URL',
);
}
}