create(); $pressRelease = PressRelease::factory()->create([ 'user_id' => $user->id, 'status' => PressReleaseStatus::Draft->value, ]); Sanctum::actingAs($user, ['press-releases:read', 'press-release-images:write']); $uploadResponse = $this->postJson("/api/v1/press-releases/{$pressRelease->id}/images", [ 'image' => UploadedFile::fake()->image('press-release.jpg', 1200, 800)->size(5120), 'title' => 'Pressefoto', 'is_preview' => true, ]); // Pressebilder werden auf das Hero-/Cover-Format normalisiert (harte // Obergrenze 1280x580, siehe ImageService::PRESS_RELEASE_IMAGE_VARIANTS). // Gespeichert werden daher die Cover-Maße, nicht die Originalgröße. $uploadResponse ->assertCreated() ->assertJsonPath('data.title', 'Pressefoto') ->assertJsonPath('data.is_preview', true) ->assertJsonPath('data.width', 1280) ->assertJsonPath('data.height', 580) ->assertJsonStructure(['data' => ['variants', 'urls']]); $image = PressReleaseImage::query()->firstOrFail(); expect(Storage::disk('public')->exists($image->path))->toBeTrue(); expect($image->variants)->toBeArray(); expect($image->variants)->toHaveKeys(['thumb', 'medium', 'large']); foreach ($image->variants as $variantPath) { expect(Storage::disk('public')->exists($variantPath))->toBeTrue(); } $this->getJson("/api/v1/press-releases/{$pressRelease->id}/images") ->assertOk() ->assertJsonPath('data.0.id', $image->id) ->assertJsonPath('data.0.url', asset('storage/'.ltrim($image->path, '/'))); $this->getJson("/api/v1/press-releases/{$pressRelease->id}") ->assertOk() ->assertJsonPath('data.images.0.id', $image->id); $variantPaths = $image->variants ?? []; $this->deleteJson("/api/v1/press-release-images/{$image->id}") ->assertNoContent(); expect(Storage::disk('public')->exists($image->path))->toBeFalse(); foreach ($variantPaths as $variantPath) { expect(Storage::disk('public')->exists($variantPath))->toBeFalse(); } expect(PressReleaseImage::withTrashed()->find($image->id)?->trashed())->toBeTrue(); }); test('image upload is limited to eight megabytes', function () { /** @var TestCase $this */ Storage::fake('public'); $user = User::factory()->create(); $pressRelease = PressRelease::factory()->create([ 'user_id' => $user->id, 'status' => PressReleaseStatus::Draft->value, ]); Sanctum::actingAs($user, ['press-release-images:write']); $this->postJson("/api/v1/press-releases/{$pressRelease->id}/images", [ 'image' => UploadedFile::fake()->image('too-large.jpg')->size(8193), ])->assertUnprocessable() ->assertInvalid(['image']); }); test('api user cannot manage another users press release images', function () { /** @var TestCase $this */ Storage::fake('public'); $owner = User::factory()->create(); $otherUser = User::factory()->create(); $pressRelease = PressRelease::factory()->create([ 'user_id' => $owner->id, 'status' => PressReleaseStatus::Draft->value, ]); Sanctum::actingAs($otherUser, ['press-releases:read', 'press-release-images:write']); $this->postJson("/api/v1/press-releases/{$pressRelease->id}/images", [ 'image' => UploadedFile::fake()->image('foreign.jpg'), ])->assertForbidden(); }); test('published press release images cannot be changed via api', function () { /** @var TestCase $this */ Storage::fake('public'); $user = User::factory()->create(); $pressRelease = PressRelease::factory()->published()->create([ 'user_id' => $user->id, ]); Sanctum::actingAs($user, ['press-release-images:write']); $this->postJson("/api/v1/press-releases/{$pressRelease->id}/images", [ 'image' => UploadedFile::fake()->image('published.jpg'), ])->assertConflict() ->assertJsonPath('message', 'Only draft or rejected press releases may be edited.'); });