Skip to content

Commit

Permalink
Cleanup download tests
Browse files Browse the repository at this point in the history
  • Loading branch information
nagmat84 committed Aug 10, 2022
1 parent 420beb4 commit c0e79ac
Show file tree
Hide file tree
Showing 5 changed files with 227 additions and 207 deletions.
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 exactly contains 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

0 comments on commit c0e79ac

Please sign in to comment.