jonnybarnes.uk/tests/Feature/MicropubMediaTest.php
Jonny Barnes d7da42b626
Some checks failed
PHP Unit / PHPUnit test suite (pull_request) Has been cancelled
Laravel Pint / Laravel Pint (pull_request) Has been cancelled
Host images locally
We don’t need the complexity of S3. Sepcifically the complexity of
managing my own AWS account, flysystem made the Laravel side easy.

A command is added to copy the the S3 files over to local storage.
2024-10-25 20:40:52 +01:00

362 lines
12 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
declare(strict_types=1);
namespace Tests\Feature;
use App\Jobs\ProcessMedia;
use App\Models\Media;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Queue;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Tests\TestCase;
use Tests\TestToken;
class MicropubMediaTest extends TestCase
{
use RefreshDatabase;
use TestToken;
/** @test */
public function emptyResponseForLastUploadWhenNoneFound(): void
{
// Make sure theres no media
$this->assertCount(0, Media::all());
$response = $this->get(
'/api/media?q=last',
['HTTP_Authorization' => 'Bearer ' . $this->getToken()]
);
$response->assertStatus(200);
$response->assertJson(['url' => null]);
}
/** @test */
public function getRequestWithInvalidTokenReturnsErrorResponse(): void
{
$response = $this->get(
'/api/media?q=last',
['HTTP_Authorization' => 'Bearer abc123']
);
$response->assertStatus(400);
$response->assertJsonFragment(['error_description' => 'The provided token did not pass validation']);
}
/** @test */
public function getRequestWithTokenWithoutScopeReturnsErrorResponse(): void
{
$response = $this->get(
'/api/media?q=last',
['HTTP_Authorization' => 'Bearer ' . $this->getTokenWithNoScope()]
);
$response->assertStatus(400);
$response->assertJsonFragment(['error_description' => 'The provided token has no scopes']);
}
/** @test */
public function getRequestWithTokenWithInsufficientScopeReturnsErrorResponse(): void
{
$response = $this->get(
'/api/media?q=last',
['HTTP_Authorization' => 'Bearer ' . $this->getTokenWithIncorrectScope()]
);
$response->assertStatus(401);
$response->assertJsonFragment(['error_description' => 'The tokens scope does not have the necessary requirements.']);
}
/** @test */
public function emptyGetRequestWithTokenReceivesOkResponse(): void
{
$response = $this->get(
'/api/media',
['HTTP_Authorization' => 'Bearer ' . $this->getToken()]
);
$response->assertStatus(200);
$response->assertJson(['status' => 'OK']);
}
/** @test */
public function clientCanListLastUpload(): void
{
Queue::fake();
$file = __DIR__ . '/../aaron.png';
$token = $this->getToken();
$response = $this->post(
'/api/media',
[
'file' => new UploadedFile($file, 'aaron.png', 'image/png', null, true),
],
['HTTP_Authorization' => 'Bearer ' . $token]
);
$path = parse_url($response->headers->get('Location'), PHP_URL_PATH);
$filename = Str::chopStart($path, '/media/');
$lastUploadResponse = $this->get(
'/api/media?q=last',
['HTTP_Authorization' => 'Bearer ' . $token]
);
$lastUploadResponse->assertJson(['url' => $response->headers->get('Location')]);
// now remove file
unlink(storage_path('app/media/') . $filename);
$this->removeDirIfEmpty(storage_path('app/media'));
}
/** @test */
public function clientCanSourceUploads(): void
{
Queue::fake();
$file = __DIR__ . '/../aaron.png';
$token = $this->getToken();
$response = $this->post(
'/api/media',
[
'file' => new UploadedFile($file, 'aaron.png', 'image/png', null, true),
],
['HTTP_Authorization' => 'Bearer ' . $token]
);
$path = parse_url($response->headers->get('Location'), PHP_URL_PATH);
$filename = Str::chopStart($path, '/media/');
$sourceUploadResponse = $this->get(
'/api/media?q=source',
['HTTP_Authorization' => 'Bearer ' . $token]
);
$sourceUploadResponse->assertJson(['items' => [[
'url' => $response->headers->get('Location'),
'mime_type' => 'image/png',
]]]);
// now remove file
unlink(storage_path('app/media/') . $filename);
$this->removeDirIfEmpty(storage_path('app/media'));
}
/** @test */
public function clientCanSourceUploadsWithLimit(): void
{
Queue::fake();
$file = __DIR__ . '/../aaron.png';
$token = $this->getToken();
$response = $this->post(
'/api/media',
[
'file' => new UploadedFile($file, 'aaron.png', 'image/png', null, true),
],
['HTTP_Authorization' => 'Bearer ' . $token]
);
$path = parse_url($response->headers->get('Location'), PHP_URL_PATH);
$filename = Str::chopStart($path, '/media/');
$sourceUploadResponse = $this->get(
'/api/media?q=source&limit=1',
['HTTP_Authorization' => 'Bearer ' . $token]
);
$sourceUploadResponse->assertJson(['items' => [[
'url' => $response->headers->get('Location'),
'mime_type' => 'image/png',
]]]);
// And given our limit of 1 there should only be one result
$this->assertCount(1, json_decode($sourceUploadResponse->getContent(), true)['items']);
// now remove file
unlink(storage_path('app/media/') . $filename);
$this->removeDirIfEmpty(storage_path('app/media'));
}
/** @test */
public function mediaEndpointUploadRequiresFile(): void
{
$response = $this->post(
'/api/media',
[],
['HTTP_Authorization' => 'Bearer ' . $this->getToken()]
);
$response->assertStatus(400);
$response->assertJson([
'response' => 'error',
'error' => 'invalid_request',
'error_description' => 'No file was sent with the request',
]);
}
/** @test */
public function errorResponseForUnknownQValue(): void
{
$response = $this->get(
'/api/media?q=unknown',
['HTTP_Authorization' => 'Bearer ' . $this->getToken()]
);
$response->assertStatus(400);
$response->assertJson(['error' => 'invalid_request']);
}
/** @test */
public function optionsRequestReturnsCorsResponse(): void
{
$response = $this->options('/api/media');
$response->assertStatus(200);
$response->assertHeader('access-control-allow-origin', '*');
}
/** @test */
public function mediaEndpointRequestWithInvalidTokenReturns400Response(): void
{
$response = $this->post(
'/api/media',
[],
['HTTP_Authorization' => 'Bearer abc123']
);
$response->assertStatus(400);
$response->assertJsonFragment(['error_description' => 'The provided token did not pass validation']);
}
/** @test */
public function mediaEndpointRequestWithTokenWithNoScopeReturns400Response(): void
{
$response = $this->post(
'/api/media',
[],
['HTTP_Authorization' => 'Bearer ' . $this->getTokenWithNoScope()]
);
$response->assertStatus(400);
$response->assertJsonFragment(['error_description' => 'The provided token has no scopes']);
}
/** @test */
public function mediaEndpointRequestWithInsufficientTokenScopesReturns401Response(): void
{
$response = $this->post(
'/api/media',
[],
['HTTP_Authorization' => 'Bearer ' . $this->getTokenWithIncorrectScope()]
);
$response->assertStatus(401);
$response->assertJsonFragment([
'error_description' => 'The tokens scope does not have the necessary requirements.',
]);
}
/** @test */
public function mediaEndpointUploadFile(): void
{
Queue::fake();
$file = __DIR__ . '/../aaron.png';
$response = $this->post(
'/api/media',
[
'file' => new UploadedFile($file, 'aaron.png', 'image/png', null, true),
],
['HTTP_Authorization' => 'Bearer ' . $this->getToken()]
);
$path = parse_url($response->headers->get('Location'), PHP_URL_PATH);
$filename = Str::chopStart($path, '/media/');
Queue::assertPushed(ProcessMedia::class);
Storage::disk('local')->assertExists($path);
// now remove file
unlink(storage_path('app/media/') . $filename);
$this->removeDirIfEmpty(storage_path('app/media'));
}
/** @test */
public function mediaEndpointUploadAudioFile(): void
{
Queue::fake();
$file = __DIR__ . '/../audio.mp3';
$response = $this->post(
'/api/media',
[
'file' => new UploadedFile($file, 'audio.mp3', 'audio/mpeg', null, true),
],
['HTTP_Authorization' => 'Bearer ' . $this->getToken()]
);
$path = parse_url($response->headers->get('Location'), PHP_URL_PATH);
$filename = Str::chopStart($path, '/media/');
Queue::assertPushed(ProcessMedia::class);
Storage::disk('local')->assertExists($path);
// now remove file
unlink(storage_path('app/media/') . $filename);
$this->removeDirIfEmpty(storage_path('app/media'));
}
/** @test */
public function mediaEndpointUploadVideoFile(): void
{
Queue::fake();
$file = __DIR__ . '/../video.ogv';
$response = $this->post(
'/api/media',
[
'file' => new UploadedFile($file, 'video.ogv', 'video/ogg', null, true),
],
['HTTP_Authorization' => 'Bearer ' . $this->getToken()]
);
$path = parse_url($response->headers->get('Location'), PHP_URL_PATH);
$filename = Str::chopStart($path, '/media/');
Queue::assertPushed(ProcessMedia::class);
Storage::disk('local')->assertExists($path);
// now remove file
unlink(storage_path('app/media/') . $filename);
$this->removeDirIfEmpty(storage_path('app/media'));
}
/** @test */
public function mediaEndpointUploadDocumentFile(): void
{
Queue::fake();
$response = $this->post(
'/api/media',
[
'file' => UploadedFile::fake()->create('document.pdf', 100),
],
['HTTP_Authorization' => 'Bearer ' . $this->getToken()]
);
$path = parse_url($response->headers->get('Location'), PHP_URL_PATH);
$filename = Str::chopStart($path, '/media/');
Queue::assertPushed(ProcessMedia::class);
Storage::disk('local')->assertExists($path);
// now remove file
unlink(storage_path('app/media/') . $filename);
$this->removeDirIfEmpty(storage_path('app/media'));
}
/** @test */
public function mediaEndpointUploadInvalidFileReturnsError(): void
{
Queue::fake();
Storage::fake('local');
$response = $this->post(
'/api/media',
[
'file' => new UploadedFile(
__DIR__ . '/../aaron.png',
'aaron.png',
'image/png',
UPLOAD_ERR_INI_SIZE,
true
),
],
['HTTP_Authorization' => 'Bearer ' . $this->getToken()]
);
$response->assertStatus(400);
$response->assertJson(['error_description' => 'The uploaded file failed validation']);
}
}