diff --git a/app/Services/Packs/PackDeletionService.php b/app/Services/Packs/PackDeletionService.php index f38a2df71..590bdb4db 100644 --- a/app/Services/Packs/PackDeletionService.php +++ b/app/Services/Packs/PackDeletionService.php @@ -87,7 +87,7 @@ class PackDeletionService $pack = $this->repository->withColumns(['id', 'uuid'])->find($pack); } - $count = $this->serverRepository->findCountWhere([['pack_id', '=', $pack]]); + $count = $this->serverRepository->findCountWhere([['pack_id', '=', $pack->id]]); if ($count !== 0) { throw new HasActiveServersException(trans('admin/exceptions.packs.delete_has_servers')); } diff --git a/tests/Unit/Services/Packs/PackCreationServiceTest.php b/tests/Unit/Services/Packs/PackCreationServiceTest.php new file mode 100644 index 000000000..7a21b7a99 --- /dev/null +++ b/tests/Unit/Services/Packs/PackCreationServiceTest.php @@ -0,0 +1,204 @@ +. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +namespace Tests\Unit\Services\Packs; + +use Exception; +use Illuminate\Contracts\Filesystem\Factory; +use Illuminate\Http\UploadedFile; +use Mockery as m; +use Illuminate\Database\ConnectionInterface; +use Pterodactyl\Contracts\Repository\PackRepositoryInterface; +use Pterodactyl\Exceptions\Service\Pack\InvalidFileMimeTypeException; +use Pterodactyl\Exceptions\Service\Pack\InvalidFileUploadException; +use Pterodactyl\Models\Pack; +use Pterodactyl\Services\Packs\PackCreationService; +use Tests\TestCase; + +class PackCreationServiceTest extends TestCase +{ + /** + * @var \Illuminate\Database\ConnectionInterface + */ + protected $connection; + + /** + * @var \Illuminate\Http\UploadedFile + */ + protected $file; + + /** + * @var \Pterodactyl\Contracts\Repository\PackRepositoryInterface + */ + protected $repository; + + /** + * @var \Pterodactyl\Services\Packs\PackCreationService + */ + protected $service; + + /** + * @var \Illuminate\Contracts\Filesystem\Factory + */ + protected $storage; + + /** + * @var \Ramsey\Uuid\Uuid + */ + protected $uuid; + + /** + * Setup tests. + */ + public function setUp() + { + parent::setUp(); + + $this->connection = m::mock(ConnectionInterface::class); + $this->file = m::mock(UploadedFile::class); + $this->repository = m::mock(PackRepositoryInterface::class); + $this->storage = m::mock(Factory::class); + $this->uuid = m::mock('overload:\Ramsey\Uuid\Uuid'); + + $this->service = new PackCreationService($this->connection, $this->storage, $this->repository); + } + + /** + * Test that a pack is created when no file upload is provided. + */ + public function testPackIsCreatedWhenNoUploadedFileIsPassed() + { + $model = factory(Pack::class)->make(); + + $this->connection->shouldReceive('beginTransaction')->withNoArgs()->once()->andReturnNull(); + $this->uuid->shouldReceive('uuid4')->withNoArgs()->once()->andReturn($model->uuid); + $this->repository->shouldReceive('create')->with([ + 'uuid' => $model->uuid, + 'selectable' => false, + 'visible' => false, + 'locked' => false, + 'test-data' => 'value', + ])->once()->andReturn($model); + + $this->storage->shouldReceive('disk')->withNoArgs()->once()->andReturnSelf() + ->shouldReceive('makeDirectory')->with('packs/' . $model->uuid)->once()->andReturnNull(); + $this->connection->shouldReceive('commit')->withNoArgs()->once()->andReturnNull(); + + $response = $this->service->handle(['test-data' => 'value']); + $this->assertInstanceOf(Pack::class, $response); + $this->assertEquals($model, $response); + } + + /** + * Test that a pack can be created when an uploaded file is provided. + * + * @dataProvider mimetypeProvider + */ + public function testPackIsCreatedWhenUploadedFileIsProvided($mime) + { + $model = factory(Pack::class)->make(); + + $this->file->shouldReceive('isValid')->withNoArgs()->once()->andReturn(true); + $this->file->shouldReceive('getMimeType')->withNoArgs()->once()->andReturn($mime); + $this->connection->shouldReceive('beginTransaction')->withNoArgs()->once()->andReturnNull(); + $this->uuid->shouldReceive('uuid4')->withNoArgs()->once()->andReturn($model->uuid); + $this->repository->shouldReceive('create')->with([ + 'uuid' => $model->uuid, + 'selectable' => false, + 'visible' => false, + 'locked' => false, + 'test-data' => 'value', + ])->once()->andReturn($model); + + $this->storage->shouldReceive('disk')->withNoArgs()->once()->andReturnSelf() + ->shouldReceive('makeDirectory')->with('packs/' . $model->uuid)->once()->andReturnNull(); + $this->file->shouldReceive('storeAs')->with('packs/' . $model->uuid, 'archive.tar.gz')->once()->andReturnNull(); + $this->connection->shouldReceive('commit')->withNoArgs()->once()->andReturnNull(); + + $response = $this->service->handle(['test-data' => 'value'], $this->file); + $this->assertInstanceOf(Pack::class, $response); + $this->assertEquals($model, $response); + } + + /** + * Test that an exception is thrown if the file upload is not valid. + */ + public function testExceptionIsThrownIfInvalidUploadIsProvided() + { + $this->file->shouldReceive('isValid')->withNoArgs()->once()->andReturn(false); + + try { + $this->service->handle([], $this->file); + } catch (Exception $exception) { + $this->assertInstanceOf(InvalidFileUploadException::class, $exception); + $this->assertEquals(trans('admin/exceptions.packs.invalid_upload'), $exception->getMessage()); + } + } + + /** + * Test that an exception is thrown when an invalid mimetype is provided. + * + * @dataProvider invalidMimetypeProvider + */ + public function testExceptionIsThrownIfInvalidMimetypeIsFound($mime) + { + $this->file->shouldReceive('isValid')->withNoArgs()->once()->andReturn(true); + $this->file->shouldReceive('getMimeType')->withNoArgs()->once()->andReturn($mime); + + try { + $this->service->handle([], $this->file); + } catch (InvalidFileMimeTypeException $exception) { + $this->assertEquals(trans('admin/exceptions.packs.invalid_mime', [ + 'type' => implode(', ', PackCreationService::VALID_UPLOAD_TYPES), + ]), $exception->getMessage()); + } + } + + /** + * Return an array of valid mimetypes to test aganist. + * + * @return array + */ + public function mimetypeProvider() + { + return [ + ['application/gzip'], + ['application/x-gzip'], + ]; + } + + /** + * Provide invalid mimetypes to test exceptions aganist. + * + * @return array + */ + public function invalidMimetypeProvider() + { + return [ + ['application/zip'], + ['text/plain'], + ['image/jpeg'], + ]; + } +} diff --git a/tests/Unit/Services/Packs/PackDeletionServiceTest.php b/tests/Unit/Services/Packs/PackDeletionServiceTest.php new file mode 100644 index 000000000..74ec01d55 --- /dev/null +++ b/tests/Unit/Services/Packs/PackDeletionServiceTest.php @@ -0,0 +1,136 @@ +. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +namespace Tests\Unit\Services\Packs; + +use Exception; +use Illuminate\Contracts\Filesystem\Factory; +use Illuminate\Database\ConnectionInterface; +use Mockery as m; +use Pterodactyl\Contracts\Repository\PackRepositoryInterface; +use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; +use Pterodactyl\Exceptions\Service\HasActiveServersException; +use Pterodactyl\Models\Pack; +use Pterodactyl\Services\Packs\PackDeletionService; +use Tests\TestCase; + +class PackDeletionServiceTest extends TestCase +{ + /** + * @var \Illuminate\Database\ConnectionInterface + */ + protected $connection; + + /** + * @var \Pterodactyl\Contracts\Repository\PackRepositoryInterface + */ + protected $repository; + + /** + * @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface + */ + protected $serverRepository; + + /** + * @var \Pterodactyl\Services\Packs\PackDeletionService + */ + protected $service; + + /** + * @var \Illuminate\Contracts\Filesystem\Factory + */ + protected $storage; + + /** + * Setup tests. + */ + public function setUp() + { + parent::setUp(); + + $this->connection = m::mock(ConnectionInterface::class); + $this->repository = m::mock(PackRepositoryInterface::class); + $this->serverRepository = m::mock(ServerRepositoryInterface::class); + $this->storage = m::mock(Factory::class); + + $this->service = new PackDeletionService( + $this->connection, + $this->storage, + $this->repository, + $this->serverRepository + ); + } + + /** + * Test that a pack is deleted. + */ + public function testPackIsDeleted() + { + $model = factory(Pack::class)->make(); + + $this->serverRepository->shouldReceive('findCountWhere')->with([['pack_id', '=', $model->id]])->once()->andReturn(0); + $this->connection->shouldReceive('beginTransaction')->withNoArgs()->once()->andReturnNull(); + $this->repository->shouldReceive('delete')->with($model->id)->once()->andReturnNull(); + $this->storage->shouldReceive('disk')->withNoArgs()->once()->andReturnSelf() + ->shouldReceive('deleteDirectory')->with('packs/' . $model->uuid)->once()->andReturnNull(); + $this->connection->shouldReceive('commit')->withNoArgs()->once()->andReturnNull(); + + $this->service->handle($model); + } + + /** + * Test that a pack ID can be passed in place of the model. + */ + public function testPackIdCanBePassedInPlaceOfModel() + { + $model = factory(Pack::class)->make(); + + $this->repository->shouldReceive('withColumns')->with(['id', 'uuid'])->once()->andReturnSelf() + ->shouldReceive('find')->with($model->id)->once()->andReturn($model); + $this->serverRepository->shouldReceive('findCountWhere')->with([['pack_id', '=', $model->id]])->once()->andReturn(0); + $this->connection->shouldReceive('beginTransaction')->withNoArgs()->once()->andReturnNull(); + $this->repository->shouldReceive('delete')->with($model->id)->once()->andReturnNull(); + $this->storage->shouldReceive('disk')->withNoArgs()->once()->andReturnSelf() + ->shouldReceive('deleteDirectory')->with('packs/' . $model->uuid)->once()->andReturnNull(); + $this->connection->shouldReceive('commit')->withNoArgs()->once()->andReturnNull(); + + $this->service->handle($model->id); + } + + /** + * Test that an exception gets thrown if a server is attached to a pack. + */ + public function testExceptionIsThrownIfServerIsAttachedToPack() + { + $model = factory(Pack::class)->make(); + + $this->serverRepository->shouldReceive('findCountWhere')->with([['pack_id', '=', $model->id]])->once()->andReturn(1); + + try { + $this->service->handle($model); + } catch (HasActiveServersException $exception) { + $this->assertEquals(trans('admin/exceptions.packs.delete_has_servers'), $exception->getMessage()); + } + } +} diff --git a/tests/Unit/Services/Packs/PackUpdateServiceTest.php b/tests/Unit/Services/Packs/PackUpdateServiceTest.php new file mode 100644 index 000000000..628979bd0 --- /dev/null +++ b/tests/Unit/Services/Packs/PackUpdateServiceTest.php @@ -0,0 +1,116 @@ +. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +namespace Tests\Unit\Services\Packs; + +use Mockery as m; +use Pterodactyl\Contracts\Repository\PackRepositoryInterface; +use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; +use Pterodactyl\Exceptions\Service\HasActiveServersException; +use Pterodactyl\Models\Pack; +use Pterodactyl\Services\Packs\PackUpdateService; +use Tests\TestCase; + +class PackUpdateServiceTest extends TestCase +{ + /** + * @var \Pterodactyl\Contracts\Repository\PackRepositoryInterface + */ + protected $repository; + + /** + * @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface + */ + protected $serverRepository; + + /** + * @var \Pterodactyl\Services\Packs\PackUpdateService + */ + protected $service; + + /** + * Setup tests. + */ + public function setUp() + { + parent::setUp(); + + $this->repository = m::mock(PackRepositoryInterface::class); + $this->serverRepository = m::mock(ServerRepositoryInterface::class); + + $this->service = new PackUpdateService($this->repository, $this->serverRepository); + } + + /** + * Test that a pack is updated. + */ + public function testPackIsUpdated() + { + $model = factory(Pack::class)->make(); + $this->repository->shouldReceive('withoutFresh')->withNoArgs()->once()->andReturnSelf() + ->shouldReceive('update')->with($model->id, [ + 'locked' => false, + 'visible' => false, + 'selectable' => false, + 'test-data' => 'value' + ])->once()->andReturn(1); + + $this->assertEquals(1, $this->service->handle($model, ['test-data' => 'value'])); + } + + /** + * Test that an exception is thrown if the pack option ID is changed while servers are using the pack. + */ + public function testExceptionIsThrownIfModifyingOptionIdWhenServersAreAttached() + { + $model = factory(Pack::class)->make(); + $this->serverRepository->shouldReceive('findCountWhere')->with([['pack_id', '=', $model->id]])->once()->andReturn(1); + + try { + $this->service->handle($model, ['option_id' => 0]); + } catch (HasActiveServersException $exception) { + $this->assertEquals(trans('admin/exceptions.packs.update_has_servers'), $exception->getMessage()); + } + } + + /** + * Test that an ID for a pack can be passed in place of the model. + */ + public function testPackIdCanBePassedInPlaceOfModel() + { + $model = factory(Pack::class)->make(); + + $this->repository->shouldReceive('withColumns')->with(['id', 'option_id'])->once()->andReturnSelf() + ->shouldReceive('find')->with($model->id)->once()->andReturn($model); + $this->repository->shouldReceive('withoutFresh')->withNoArgs()->once()->andReturnSelf() + ->shouldReceive('update')->with($model->id, [ + 'locked' => false, + 'visible' => false, + 'selectable' => false, + 'test-data' => 'value' + ])->once()->andReturn(1); + + $this->assertEquals(1, $this->service->handle($model->id, ['test-data' => 'value'])); + } +} diff --git a/tests/Unit/Services/Packs/TemplateUploadServiceTest.php b/tests/Unit/Services/Packs/TemplateUploadServiceTest.php new file mode 100644 index 000000000..4f8a81d24 --- /dev/null +++ b/tests/Unit/Services/Packs/TemplateUploadServiceTest.php @@ -0,0 +1,261 @@ +. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +namespace Tests\Unit\Services\Packs; + +use Illuminate\Http\UploadedFile; +use Mockery as m; +use Pterodactyl\Exceptions\Service\Pack\InvalidFileMimeTypeException; +use Pterodactyl\Exceptions\Service\Pack\InvalidFileUploadException; +use Pterodactyl\Exceptions\Service\Pack\InvalidPackArchiveFormatException; +use Pterodactyl\Exceptions\Service\Pack\UnreadableZipArchiveException; +use Pterodactyl\Exceptions\Service\Pack\ZipExtractionException; +use Pterodactyl\Models\Pack; +use Pterodactyl\Services\Packs\PackCreationService; +use Pterodactyl\Services\Packs\TemplateUploadService; +use Tests\TestCase; +use ZipArchive; + +class TemplateUploadServiceTest extends TestCase +{ + const JSON_FILE_CONTENTS = '{"test_content": "value"}'; + + /** + * @var \ZipArchive + */ + protected $archive; + + /** + * @var \Pterodactyl\Services\Packs\PackCreationService + */ + protected $creationService; + + /** + * @var \Illuminate\Http\UploadedFile + */ + protected $file; + + /** + * @var \Pterodactyl\Services\Packs\TemplateUploadService + */ + protected $service; + + /** + * Setup tests. + */ + public function setUp() + { + parent::setUp(); + + $this->archive = m::mock(ZipArchive::class); + $this->creationService = m::mock(PackCreationService::class); + $this->file = m::mock(UploadedFile::class); + + $this->service = new TemplateUploadService($this->creationService, $this->archive); + } + + /** + * Test that a JSON file can be processed and turned into a pack. + * + * @dataProvider jsonMimetypeProvider + */ + public function testJsonFileIsProcessed($mime) + { + $this->file->shouldReceive('isValid')->withNoArgs()->once()->andReturn(true); + $this->file->shouldReceive('getMimeType')->withNoArgs()->twice()->andReturn($mime); + $this->file->shouldReceive('getSize')->withNoArgs()->once()->andReturn(128); + $this->file->shouldReceive('openFile')->withNoArgs()->once()->andReturnSelf() + ->shouldReceive('fread')->with(128)->once()->andReturn(self::JSON_FILE_CONTENTS); + + $this->creationService->shouldReceive('handle')->with(['test_content' => 'value', 'option_id' => 1]) + ->once()->andReturn(factory(Pack::class)->make()); + + $this->assertInstanceOf(Pack::class, $this->service->handle(1, $this->file)); + } + + /** + * Test that a zip file can be processed. + */ + public function testZipfileIsProcessed() + { + $model = factory(Pack::class)->make(); + + $this->file->shouldReceive('isValid')->withNoArgs()->once()->andReturn(true); + $this->file->shouldReceive('getMimeType')->withNoArgs()->twice()->andReturn('application/zip'); + + $this->file->shouldReceive('getRealPath')->withNoArgs()->once()->andReturn('/test/real'); + $this->archive->shouldReceive('open')->with('/test/real')->once()->andReturn(true); + $this->archive->shouldReceive('locateName')->with('import.json')->once()->andReturn(true); + $this->archive->shouldReceive('locateName')->with('archive.tar.gz')->once()->andReturn(true); + $this->archive->shouldReceive('getFromName')->with('import.json')->once()->andReturn(self::JSON_FILE_CONTENTS); + $this->creationService->shouldReceive('handle')->with(['test_content' => 'value', 'option_id' => 1]) + ->once()->andReturn($model); + $this->archive->shouldReceive('extractTo')->with(storage_path('app/packs/' . $model->uuid), 'archive.tar.gz') + ->once()->andReturn(true); + $this->archive->shouldReceive('close')->withNoArgs()->once()->andReturnNull(); + + $this->assertInstanceOf(Pack::class, $this->service->handle(1, $this->file)); + } + + /** + * Test that an exception is thrown if the file upload is invalid. + */ + public function testExceptionIsThrownIfFileUploadIsInvalid() + { + $this->file->shouldReceive('isValid')->withNoArgs()->once()->andReturn(false); + + try { + $this->service->handle(1, $this->file); + } catch (InvalidFileUploadException $exception) { + $this->assertEquals(trans('admin/exceptions.packs.invalid_upload'), $exception->getMessage()); + } + } + + /** + * Test that an invalid mimetype throws an exception. + * + * @dataProvider invalidMimetypeProvider + */ + public function testExceptionIsThrownIfMimetypeIsInvalid($mime) + { + $this->file->shouldReceive('isValid')->withNoArgs()->once()->andReturn(true); + $this->file->shouldReceive('getMimeType')->withNoArgs()->once()->andReturn($mime); + + try { + $this->service->handle(1, $this->file); + } catch (InvalidFileMimeTypeException $exception) { + $this->assertEquals(trans('admin/exceptions.packs.invalid_mime', [ + 'type' => implode(', ', TemplateUploadService::VALID_UPLOAD_TYPES), + ]), $exception->getMessage()); + } + } + + /** + * Test that an exception is thrown if the zip is unreadable. + */ + public function testExceptionIsThrownIfZipArchiveIsUnreadable() + { + $this->file->shouldReceive('isValid')->withNoArgs()->once()->andReturn(true); + $this->file->shouldReceive('getMimeType')->withNoArgs()->twice()->andReturn('application/zip'); + + $this->file->shouldReceive('getRealPath')->withNoArgs()->once()->andReturn('/test/path'); + $this->archive->shouldReceive('open')->with('/test/path')->once()->andReturn(false); + + try { + $this->service->handle(1, $this->file); + } catch (UnreadableZipArchiveException $exception) { + $this->assertEquals(trans('admin/exceptions.packs.unreadable'), $exception->getMessage()); + } + } + + /** + * Test that a zip missing the required files throws an exception. + * + * @dataProvider filenameProvider + */ + public function testExceptionIsThrownIfZipDoesNotContainProperFiles($a, $b) + { + $this->file->shouldReceive('isValid')->withNoArgs()->once()->andReturn(true); + $this->file->shouldReceive('getMimeType')->withNoArgs()->twice()->andReturn('application/zip'); + + $this->file->shouldReceive('getRealPath')->withNoArgs()->once()->andReturn('/test/path'); + $this->archive->shouldReceive('open')->with('/test/path')->once()->andReturn(true); + $this->archive->shouldReceive('locateName')->with('import.json')->once()->andReturn($a); + + if ($a) { + $this->archive->shouldReceive('locateName')->with('archive.tar.gz')->once()->andReturn($b); + } + + try { + $this->service->handle(1, $this->file); + } catch (InvalidPackArchiveFormatException $exception) { + $this->assertEquals(trans('admin/exceptions.packs.invalid_archive_exception'), $exception->getMessage()); + } + } + + /** + * Test that an exception is thrown if an archive cannot be extracted from the zip file. + */ + public function testExceptionIsThrownIfArchiveCannotBeExtractedFromZip() + { + $model = factory(Pack::class)->make(); + + $this->file->shouldReceive('isValid')->withNoArgs()->once()->andReturn(true); + $this->file->shouldReceive('getMimeType')->withNoArgs()->twice()->andReturn('application/zip'); + + $this->file->shouldReceive('getRealPath')->withNoArgs()->once()->andReturn('/test/real'); + $this->archive->shouldReceive('open')->once()->andReturn(true); + $this->archive->shouldReceive('locateName')->twice()->andReturn(true); + $this->archive->shouldReceive('getFromName')->once()->andReturn(self::JSON_FILE_CONTENTS); + $this->creationService->shouldReceive('handle')->once()->andReturn($model); + $this->archive->shouldReceive('extractTo')->once()->andReturn(false); + + try { + $this->service->handle(1, $this->file); + } catch (ZipExtractionException $exception) { + $this->assertEquals(trans('admin/exceptions.packs.zip_extraction'), $exception->getMessage()); + } + } + + /** + * Provide valid JSON mimetypes to use in tests. + * + * @return array + */ + public function jsonMimetypeProvider() + { + return [ + ['text/plain'], + ['application/json'], + ]; + } + + /** + * Return invalid mimetypes for testing. + * + * @return array + */ + public function invalidMimetypeProvider() + { + return [ + ['application/gzip'], + ['application/x-gzip'], + ['image/jpeg'], + ]; + } + + /** + * Return values for archive->locateName function, import.json and archive.tar.gz respectively + * + * @return array + */ + public function filenameProvider() + { + return [ + [true, false], + [false, true], + [false, false], + ]; + } +}