feat: add image upload service

This commit is contained in:
Keith Solomon
2026-07-06 18:36:34 -05:00
parent 6399e2d86c
commit c24964ff96
2 changed files with 132 additions and 0 deletions
@@ -0,0 +1,94 @@
<?php
declare(strict_types=1);
namespace BattleForge\Tests\Unit\Application;
use BattleForge\Application\ImageUploadService;
use PHPUnit\Framework\TestCase;
final class ImageUploadServiceTest extends TestCase
{
private string $tmpDir;
protected function setUp(): void
{
$this->tmpDir = sys_get_temp_dir() . '/bf-uploads-' . bin2hex(random_bytes(4));
mkdir($this->tmpDir, 0700, true);
}
protected function tearDown(): void
{
if (is_dir($this->tmpDir)) {
foreach (glob($this->tmpDir . '/*/*') as $file) {
unlink($file);
}
foreach (glob($this->tmpDir . '/*') as $dir) {
rmdir($dir);
}
rmdir($this->tmpDir);
}
}
public function testItStoresAValidImageAndReturnsAStableUrl(): void
{
$service = new ImageUploadService('user-token-1', $this->tmpDir);
$tmp = tempnam(sys_get_temp_dir(), 'bf-up');
file_put_contents($tmp, self::validPng(8, 8));
$url = $service->store($tmp, 'image/png');
self::assertStringStartsWith('/assets/user-token-1/', $url);
self::assertStringEndsWith('.png', $url);
$stored = $this->tmpDir . '/user-token-1/' . basename($url);
self::assertFileExists($stored);
}
public function testItCreatesTheUserNamespaceDirectory(): void
{
$service = new ImageUploadService('fresh-user', $this->tmpDir);
$tmp = tempnam(sys_get_temp_dir(), 'bf-up');
file_put_contents($tmp, self::validPng(8, 8));
$service->store($tmp, 'image/png');
self::assertDirectoryExists($this->tmpDir . '/fresh-user');
}
public function testItProducesUniqueFilenamesForRepeatedUploads(): void
{
$service = new ImageUploadService('user-token-2', $this->tmpDir);
$tmp1 = tempnam(sys_get_temp_dir(), 'bf-up');
$tmp2 = tempnam(sys_get_temp_dir(), 'bf-up');
file_put_contents($tmp1, self::validPng(8, 8));
file_put_contents($tmp2, self::validPng(8, 8));
$url1 = $service->store($tmp1, 'image/png');
$url2 = $service->store($tmp2, 'image/png');
self::assertNotSame($url1, $url2);
}
public function testItRejectsAnInvalidImage(): void
{
$service = new ImageUploadService('user-token-3', $this->tmpDir);
$tmp = tempnam(sys_get_temp_dir(), 'bf-up');
file_put_contents($tmp, 'not an image');
$this->expectException(\BattleForge\Application\InvalidImageException::class);
$service->store($tmp, 'image/png');
}
private static function validPng(int $width, int $height): string
{
if ($width === 8 && $height === 8) {
return base64_decode(
'iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAFElEQVR4nGNgYGD4z0AswK' .
'EWBgYGRgYGBkYGRgAAB4nCH2AAAAAElFTkSuQmCC',
true,
);
}
throw new \InvalidArgumentException('Only 8x8 base PNG supported in tests.');
}
}