Files
BattleForge/tests/Unit/Application/ImageUploadServiceTest.php
T
Keith Solomon 61abb93e59 fix: align upload-asset URL contract end-to-end
- ImageUploadService::store() now returns /assets/uploads/<token>/<file>
  so the URL the upload handler emits matches the new GET route.
- public/index.php registers a dedicated GET /assets/uploads/{userToken}/{filename}
  route; the legacy /assets/{kind}/{filename} is kept as a fallback.
- GetAssets reads the upload token from $request->server['__uploads_token']
  (the front controller threads it that way) instead of the cookies bag.
- Adds GetAssets unit tests for the upload-token happy path and a
  token-mismatch 404, plus a FullFlowTest step that uploads and re-fetches
  the asset through the front controller.
2026-07-06 23:21:33 -05:00

95 lines
3.0 KiB
PHP

<?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/uploads/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.');
}
}