clock = new SystemClock(); $this->assetService = Mockery::mock(AssetService::class); $this->storage = Mockery::mock(AssetStorageInterface::class); $this->jsonSerializer = new JsonSerializer(); $this->controller = new AssetsController( $this->assetService, $this->storage, $this->jsonSerializer ); }); it('uploads asset successfully', function () { $asset = AssetTestHelpers::createAsset($this->clock); $this->assetService->shouldReceive('upload') ->once() ->andReturn($asset); $request = new HttpRequest( method: Method::POST, path: '/api/v1/assets/upload', files: [ 'file' => [ 'tmp_name' => '/tmp/test.jpg', 'type' => 'image/jpeg', 'name' => 'test.jpg', ], ], id: new RequestId('test') ); // Mock is_uploaded_file $uploadRequest = new \App\Application\Asset\Api\Requests\UploadAssetRequest(); $response = $this->controller->upload($request, $uploadRequest); expect($response->status)->toBe(Status::CREATED); }); it('returns 400 when no file provided', function () { $request = new HttpRequest( method: Method::POST, path: '/api/v1/assets/upload', files: [], id: new RequestId('test') ); $uploadRequest = new \App\Application\Asset\Api\Requests\UploadAssetRequest(); $response = $this->controller->upload($request, $uploadRequest); expect($response->status)->toBe(Status::BAD_REQUEST); }); it('shows asset by id', function () { $assetId = AssetId::generate($this->clock); $asset = AssetTestHelpers::createAsset($this->clock, id: $assetId); $variants = []; $tags = []; $this->assetService->shouldReceive('findById') ->once() ->with($assetId) ->andReturn($asset); $this->assetService->shouldReceive('getVariants') ->once() ->with($assetId) ->andReturn($variants); $this->assetService->shouldReceive('getTags') ->once() ->with($assetId) ->andReturn($tags); $this->storage->shouldReceive('getUrl') ->once() ->andReturn('https://cdn.example.com/media/orig/test.jpg'); $response = $this->controller->show($assetId->toString()); expect($response->status)->toBe(Status::OK); }); it('returns 404 when asset not found', function () { $assetId = AssetId::generate($this->clock); $this->assetService->shouldReceive('findById') ->once() ->andThrow(\App\Domain\Asset\Exceptions\AssetNotFoundException::forId($assetId)); $response = $this->controller->show($assetId->toString()); expect($response->status)->toBe(Status::NOT_FOUND); }); it('lists variants for asset', function () { $assetId = AssetId::generate($this->clock); $asset = AssetTestHelpers::createAsset($this->clock, id: $assetId); $variants = [ AssetTestHelpers::createAssetVariant($assetId), ]; $this->assetService->shouldReceive('findById') ->once() ->with($assetId) ->andReturn($asset); $this->assetService->shouldReceive('getVariants') ->once() ->with($assetId) ->andReturn($variants); $this->storage->shouldReceive('getUrl') ->once() ->andReturn('https://cdn.example.com/variants/test.jpg'); $response = $this->controller->variants($assetId->toString()); expect($response->status)->toBe(Status::OK); }); it('shows specific variant', function () { $assetId = AssetId::generate($this->clock); $asset = AssetTestHelpers::createAsset($this->clock, id: $assetId); $variantName = VariantName::fromString('1200w.webp'); $variant = AssetTestHelpers::createAssetVariant($assetId, $variantName); $this->assetService->shouldReceive('findById') ->once() ->with($assetId) ->andReturn($asset); $this->assetService->shouldReceive('getVariant') ->once() ->with($assetId, $variantName) ->andReturn($variant); $this->storage->shouldReceive('getUrl') ->once() ->andReturn('https://cdn.example.com/variants/test/1200w.webp'); $response = $this->controller->variant($assetId->toString(), '1200w.webp'); expect($response->status)->toBe(Status::OK); }); it('returns 404 when variant not found', function () { $assetId = AssetId::generate($this->clock); $asset = AssetTestHelpers::createAsset($this->clock, id: $assetId); $variantName = VariantName::fromString('1200w.webp'); $this->assetService->shouldReceive('findById') ->once() ->with($assetId) ->andReturn($asset); $this->assetService->shouldReceive('getVariant') ->once() ->with($assetId, $variantName) ->andReturn(null); $response = $this->controller->variant($assetId->toString(), '1200w.webp'); expect($response->status)->toBe(Status::NOT_FOUND); }); it('gets asset URL', function () { $assetId = AssetId::generate($this->clock); $asset = AssetTestHelpers::createAsset($this->clock, id: $assetId); $url = 'https://cdn.example.com/media/orig/test.jpg'; $this->assetService->shouldReceive('findById') ->once() ->with($assetId) ->andReturn($asset); $this->storage->shouldReceive('getUrl') ->once() ->with($asset) ->andReturn($url); $response = $this->controller->url($assetId->toString()); expect($response->status)->toBe(Status::OK); }); it('gets signed URL for asset', function () { $assetId = AssetId::generate($this->clock); $asset = AssetTestHelpers::createAsset($this->clock, id: $assetId); $signedUrl = 'https://cdn.example.com/media/orig/test.jpg?signature=abc123'; $this->assetService->shouldReceive('findById') ->once() ->with($assetId) ->andReturn($asset); $this->storage->shouldReceive('getSignedUrl') ->once() ->with($asset, 3600) ->andReturn($signedUrl); $request = new HttpRequest( method: Method::GET, path: '/api/v1/assets/{id}/signed-url', query: ['expires' => '3600'], id: new RequestId('test') ); $response = $this->controller->signedUrl($request, $assetId->toString()); expect($response->status)->toBe(Status::OK); }); it('deletes asset', function () { $assetId = AssetId::generate($this->clock); $this->assetService->shouldReceive('delete') ->once() ->with($assetId) ->andReturnNull(); $response = $this->controller->delete($assetId->toString()); expect($response->status)->toBe(Status::NO_CONTENT); }); it('lists assets by tag', function () { $tag = 'hero'; $assets = [ AssetTestHelpers::createAsset($this->clock), ]; $this->assetService->shouldReceive('findByTag') ->once() ->with($tag) ->andReturn($assets); $this->storage->shouldReceive('getUrl') ->once() ->andReturn('https://cdn.example.com/media/orig/test.jpg'); $request = new HttpRequest( method: Method::GET, path: '/api/v1/assets', query: ['tag' => $tag], id: new RequestId('test') ); $response = $this->controller->index($request); expect($response->status)->toBe(Status::OK); }); it('lists assets by bucket', function () { $bucket = 'media'; $assets = [ AssetTestHelpers::createAsset($this->clock), ]; $this->assetService->shouldReceive('findByBucket') ->once() ->with(Mockery::type(\App\Framework\Storage\ValueObjects\BucketName::class)) ->andReturn($assets); $this->storage->shouldReceive('getUrl') ->once() ->andReturn('https://cdn.example.com/media/orig/test.jpg'); $request = new HttpRequest( method: Method::GET, path: '/api/v1/assets', query: ['bucket' => $bucket], id: new RequestId('test') ); $response = $this->controller->index($request); expect($response->status)->toBe(Status::OK); }); });