- 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.
95 lines
3.0 KiB
PHP
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.');
|
|
}
|
|
}
|