Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cleanup download tests #1457

Merged
merged 2 commits into from
Aug 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions tests/Feature/Lib/AlbumsUnitTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
namespace Tests\Feature\Lib;

use Illuminate\Testing\TestResponse;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Tests\TestCase;

class AlbumsUnitTest
Expand Down Expand Up @@ -345,6 +346,12 @@ public function download(string $id): TestResponse
]
);
$response->assertOk();
if ($response->baseResponse instanceof StreamedResponse) {
// The content of a streamed response is not generated unless
// the content is fetched.
// This ensures that the generator of SUT is actually executed.
$response->streamedContent();
}

return $response;
}
Expand Down
84 changes: 84 additions & 0 deletions tests/Feature/Lib/AssertableZipArchive.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<?php

/**
* We don't care for unhandled exceptions in tests.
* It is the nature of a test to throw an exception.
* Without this suppression we had 100+ Linter warning in this file which
* don't help anything.
*
* @noinspection PhpDocMissingThrowsInspection
* @noinspection PhpUnhandledExceptionInspection
*/

namespace Tests\Feature\Lib;

use App\Image\InMemoryBuffer;
use App\Image\TemporaryLocalFile;
use Illuminate\Testing\Assert as PHPUnit;
use Illuminate\Testing\TestResponse;
use function Safe\fwrite;
use Symfony\Component\HttpFoundation\StreamedResponse;

class AssertableZipArchive extends \ZipArchive
{
public const ZIP_STAT_SIZE = 'size';

/**
* Creates an assertable ZIP archive from the given response.
*
* @param TestResponse $response
*
* @return static
*/
public static function createFromResponse(TestResponse $response): self
{
$memoryBlob = new InMemoryBuffer();
fwrite(
$memoryBlob->stream(),
$response->baseResponse instanceof StreamedResponse ? $response->streamedContent() : $response->content()
);
$tmpZipFile = new TemporaryLocalFile('.zip', 'archive');
$tmpZipFile->write($memoryBlob->read());
$memoryBlob->close();

$zipArchive = new self();
$zipArchive->open($tmpZipFile->getRealPath());

return $zipArchive;
}

/**
* Asserts that the ZIP archive contains a file of the given name and size.
*
* @param string $fileName the name of the expected file
* @param int|null $expectedFileSize (optional) the expected file size of the uncompressed file
*
* @return void
*/
public function assertContainsFile(string $fileName, ?int $expectedFileSize): void
{
$stat = $this->statName($fileName);
PHPUnit::assertNotFalse($stat, 'Could not assert that ZIP archive contains ' . $fileName);
if ($expectedFileSize !== null) {
PHPUnit::assertEquals($expectedFileSize, $stat[self::ZIP_STAT_SIZE]);
}
}

/**
* Asserts that the ZIP archive contains exactly the given files and no more.
*
* @param array<string, array{size?: ?int}> $expectedFiles a list of expected file names together with optional file attributes
*
* @return void
*/
public function assertContainsFilesExactly(array $expectedFiles): void
{
PHPUnit::assertCount(count($expectedFiles), $this);
foreach ($expectedFiles as $fileName => $fileStat) {
$this->assertContainsFile(
$fileName,
key_exists(self::ZIP_STAT_SIZE, $fileStat) ? $fileStat[self::ZIP_STAT_SIZE] : null
);
}
}
}
7 changes: 7 additions & 0 deletions tests/Feature/Lib/PhotosUnitTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use App\Actions\Photo\Archive;
use Illuminate\Http\UploadedFile;
use Illuminate\Testing\TestResponse;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Tests\TestCase;

class PhotosUnitTest
Expand Down Expand Up @@ -349,6 +350,12 @@ public function download(
]
);
$response->assertOk();
if ($response->baseResponse instanceof StreamedResponse) {
// The content of a streamed response is not generated unless
// the content is fetched.
// This ensures that the generator of SUT is actually executed.
$response->streamedContent();
}

return $response;
}
Expand Down
180 changes: 129 additions & 51 deletions tests/Feature/PhotosDownloadTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,131 @@

namespace Tests\Feature;

use App\Actions\Photo\Archive;
use App\Image\ImagickHandler;
use App\Image\InMemoryBuffer;
use App\Image\TemporaryLocalFile;
use App\Image\VideoHandler;
use function Safe\file_get_contents;
use function Safe\filesize;
use Symfony\Component\HttpFoundation\HeaderUtils;
use Tests\Feature\Lib\AssertableZipArchive;
use Tests\TestCase;
use ZipArchive;

class PhotosDownloadTest extends Base\PhotoTestBase
{
public const MULTI_BYTE_ALBUM_TITLE = 'Lychee supporte les caractères multi-octets';

/**
* Downloads a single photo.
*
* @return void
*/
public function testSinglePhotoDownload(): void
{
$photoUploadResponse = $this->photos_tests->upload(
TestCase::createUploadedFile(TestCase::SAMPLE_FILE_NIGHT_IMAGE)
);
$photoArchiveResponse = $this->photos_tests->download([$photoUploadResponse->offsetGet('id')]);

// Stream the response in a temporary file
$memoryBlob = new InMemoryBuffer();
fwrite($memoryBlob->stream(), $photoArchiveResponse->streamedContent());
$imageFile = new TemporaryLocalFile('.jpg', 'night');
$imageFile->write($memoryBlob->read());
$memoryBlob->close();

// Just do a simple read test
$image = new ImagickHandler();
$image->load($imageFile);
$imageDim = $image->getDimensions();
static::assertEquals(6720, $imageDim->width);
static::assertEquals(4480, $imageDim->height);
}

/**
* Downloads an archive of two different photos.
*
* @return void
*/
public function testMultiplePhotoDownload(): void
{
$photoID1 = $this->photos_tests->upload(
TestCase::createUploadedFile(TestCase::SAMPLE_FILE_NIGHT_IMAGE)
)->offsetGet('id');
$photoID2 = $this->photos_tests->upload(
TestCase::createUploadedFile(TestCase::SAMPLE_FILE_MONGOLIA_IMAGE)
)->offsetGet('id');

$photoArchiveResponse = $this->photos_tests->download([$photoID1, $photoID2]);

$zipArchive = AssertableZipArchive::createFromResponse($photoArchiveResponse);
$zipArchive->assertContainsFilesExactly([
'night.jpg' => ['size' => filesize(base_path(self::SAMPLE_FILE_NIGHT_IMAGE))],
'mongolia.jpeg' => ['size' => filesize(base_path(self::SAMPLE_FILE_MONGOLIA_IMAGE))],
]);
}

/**
* Downloads the video part of a Google Motion Photo.
*
* @return void
*/
public function testGoogleMotionPhotoDownload(): void
{
static::assertHasExifToolOrSkip();
static::assertHasFFMpegOrSkip();

$photoUploadResponse = $this->photos_tests->upload(
TestCase::createUploadedFile(TestCase::SAMPLE_FILE_GMP_IMAGE)
);
$photoArchiveResponse = $this->photos_tests->download(
[$photoUploadResponse->offsetGet('id')],
Archive::LIVEPHOTOVIDEO
);

// Stream the response in a temporary file
$memoryBlob = new InMemoryBuffer();
fwrite($memoryBlob->stream(), $photoArchiveResponse->streamedContent());
$videoFile = new TemporaryLocalFile('.mov', 'gmp');
$videoFile->write($memoryBlob->read());
$memoryBlob->close();

// Just do a simple read test
$video = new VideoHandler();
$video->load($videoFile);
}

/**
* Downloads an archive of three photos with one photo being included twice.
*
* This tests the capability of the archive function to generate unique
* file names for duplicates.
*
* @return void
*/
public function testAmbiguousPhotoDownload(): void
{
$photoID1 = $this->photos_tests->upload(
TestCase::createUploadedFile(TestCase::SAMPLE_FILE_TRAIN_IMAGE)
)->offsetGet('id');
$photoID2a = $this->photos_tests->upload(
TestCase::createUploadedFile(TestCase::SAMPLE_FILE_MONGOLIA_IMAGE)
)->offsetGet('id');
$photoID2b = $this->photos_tests->duplicate(
[$photoID2a], null
)->offsetGet('id');

$photoArchiveResponse = $this->photos_tests->download([$photoID1, $photoID2a, $photoID2b]);

$zipArchive = AssertableZipArchive::createFromResponse($photoArchiveResponse);
$zipArchive->assertContainsFilesExactly([
'train.jpg' => ['size' => filesize(base_path(self::SAMPLE_FILE_TRAIN_IMAGE))],
'mongolia-1.jpeg' => ['size' => filesize(base_path(self::SAMPLE_FILE_MONGOLIA_IMAGE))],
'mongolia-2.jpeg' => ['size' => filesize(base_path(self::SAMPLE_FILE_MONGOLIA_IMAGE))],
]);
}

public function testPhotoDownloadWithMultiByteFilename(): void
{
$id = $this->photos_tests->upload(
Expand All @@ -50,39 +163,20 @@ public function testPhotoDownloadWithMultiByteFilename(): void
*/
public function testMultiplePhotoDownloadWithMultiByteFilename(): void
{
$photoUploadResponse1 = $this->photos_tests->upload(
$photoID1 = $this->photos_tests->upload(
TestCase::createUploadedFile(TestCase::SAMPLE_FILE_SUNSET_IMAGE)
);
$photoUploadResponse2 = $this->photos_tests->upload(
)->offsetGet('id');
$photoID2 = $this->photos_tests->upload(
TestCase::createUploadedFile(TestCase::SAMPLE_FILE_MONGOLIA_IMAGE)
);

$photoArchiveResponse = $this->photos_tests->download([
$photoUploadResponse1->offsetGet('id'),
$photoUploadResponse2->offsetGet('id'),
]);

$memoryBlob = new InMemoryBuffer();
fwrite($memoryBlob->stream(), $photoArchiveResponse->streamedContent());
$tmpZipFile = new TemporaryLocalFile('.zip', 'archive');
$tmpZipFile->write($memoryBlob->read());
$memoryBlob->close();

$zipArchive = new ZipArchive();
$zipArchive->open($tmpZipFile->getRealPath());

static::assertCount(2, $zipArchive);
$fileStat1 = $zipArchive->statIndex(0);
$fileStat2 = $zipArchive->statIndex(1);

static::assertContains($fileStat1['name'], ['fin de journée.jpg', 'mongolia.jpeg']);
static::assertContains($fileStat2['name'], ['fin de journée.jpg', 'mongolia.jpeg']);
)->offsetGet('id');

$expectedSize1 = $fileStat1['name'] === 'fin de journée.jpg' ? filesize(base_path(TestCase::SAMPLE_FILE_SUNSET_IMAGE)) : filesize(base_path(TestCase::SAMPLE_FILE_MONGOLIA_IMAGE));
$expectedSize2 = $fileStat2['name'] === 'fin de journée.jpg' ? filesize(base_path(TestCase::SAMPLE_FILE_SUNSET_IMAGE)) : filesize(base_path(TestCase::SAMPLE_FILE_MONGOLIA_IMAGE));
$photoArchiveResponse = $this->photos_tests->download([$photoID1, $photoID2]);

static::assertEquals($expectedSize1, $fileStat1['size']);
static::assertEquals($expectedSize2, $fileStat2['size']);
$zipArchive = AssertableZipArchive::createFromResponse($photoArchiveResponse);
$zipArchive->assertContainsFilesExactly([
'fin de journée.jpg' => ['size' => filesize(base_path(TestCase::SAMPLE_FILE_SUNSET_IMAGE))],
'mongolia.jpeg' => ['size' => filesize(base_path(TestCase::SAMPLE_FILE_MONGOLIA_IMAGE))],
]);
}

public function testAlbumDownloadWithMultibyteTitle(): void
Expand All @@ -103,27 +197,11 @@ public function testAlbumDownloadWithMultibyteTitle(): void
'Album.zip'
));

$memoryBlob = new InMemoryBuffer();
fwrite($memoryBlob->stream(), $albumArchiveResponse->streamedContent());
$tmpZipFile = new TemporaryLocalFile('.zip', 'archive');
$tmpZipFile->write($memoryBlob->read());
$memoryBlob->close();

$zipArchive = new ZipArchive();
$zipArchive->open($tmpZipFile->getRealPath());

static::assertCount(2, $zipArchive);
$fileStat1 = $zipArchive->statIndex(0);
$fileStat2 = $zipArchive->statIndex(1);

static::assertContains($fileStat1['name'], [self::MULTI_BYTE_ALBUM_TITLE . '/fin de journée.jpg', self::MULTI_BYTE_ALBUM_TITLE . '/mongolia.jpeg']);
static::assertContains($fileStat2['name'], [self::MULTI_BYTE_ALBUM_TITLE . '/fin de journée.jpg', self::MULTI_BYTE_ALBUM_TITLE . '/mongolia.jpeg']);

$expectedSize1 = $fileStat1['name'] === self::MULTI_BYTE_ALBUM_TITLE . '/fin de journée.jpg' ? filesize(base_path(TestCase::SAMPLE_FILE_SUNSET_IMAGE)) : filesize(base_path(TestCase::SAMPLE_FILE_MONGOLIA_IMAGE));
$expectedSize2 = $fileStat2['name'] === self::MULTI_BYTE_ALBUM_TITLE . '/fin de journée.jpg' ? filesize(base_path(TestCase::SAMPLE_FILE_SUNSET_IMAGE)) : filesize(base_path(TestCase::SAMPLE_FILE_MONGOLIA_IMAGE));

static::assertEquals($expectedSize1, $fileStat1['size']);
static::assertEquals($expectedSize2, $fileStat2['size']);
$zipArchive = AssertableZipArchive::createFromResponse($albumArchiveResponse);
$zipArchive->assertContainsFilesExactly([
self::MULTI_BYTE_ALBUM_TITLE . '/fin de journée.jpg' => ['size' => filesize(base_path(TestCase::SAMPLE_FILE_SUNSET_IMAGE))],
self::MULTI_BYTE_ALBUM_TITLE . '/mongolia.jpeg' => ['size' => filesize(base_path(TestCase::SAMPLE_FILE_MONGOLIA_IMAGE))],
]);

$this->albums_tests->delete([$albumID]);
}
Expand Down
Loading