masoud
2 years ago
59 changed files with 1761 additions and 242 deletions
-
1.gitignore
-
13Dockerfile
-
1app/Console/Commands/TestGenerator.php
-
9app/Http/Controllers/CollectionController.php
-
3app/Http/Controllers/Controller.php
-
87app/Http/Controllers/FileController.php
-
8app/Http/Controllers/Traits/FileTrait.php
-
6app/Http/Kernel.php
-
14app/Http/Middleware/BindCollectionModelMiddleware.php
-
75app/Http/Middleware/BindFileModelMiddleware.php
-
72app/Http/Requests/FileStoreRequest.php
-
34app/Http/Requests/FileUpdateRequest.php
-
2app/Http/Resources/CollectionResource.php
-
1app/Http/Resources/FileResource.php
-
271app/Image/ImageProcessor.php
-
8app/Image/Traits/ModulateTrait.php
-
41app/Models/Collection.php
-
31app/Models/File.php
-
3app/Models/Traits/Validatable.php
-
2app/Providers/AppServiceProvider.php
-
3app/Providers/EventServiceProvider.php
-
1app/Providers/RouteServiceProvider.php
-
31app/Providers/UuidServiceProvider.php
-
193app/Utilities/Helpers/Image.php
-
5composer.json
-
47composer.lock
-
3config/app.php
-
8config/filesystems.php
-
9database/factories/BaseFactory.php
-
61database/factories/CollectionFactory.php
-
9database/factories/Documents/PolicyDocumentFactory.php
-
39database/factories/FileFactory.php
-
2database/factories/ILaravelFactory.php
-
2database/factories/UserFactory.php
-
1database/migrations/2022_07_27_073858_create_files_table.php
-
5database/migrations/2022_07_27_073906_create_collections_table.php
-
50database/seeders/DatabaseSeeder.php
-
1docker-compose.yml
-
BINpublic/image-modified.webp
-
18resources/views/welcome.blade.php
-
19routes/api.php
-
96routes/web.php
-
1storage/framework/.gitignore
-
0storage/stub/image.png
-
BINstorage/stub/image2.jpeg
-
BINstorage/stub/image3.png
-
8tests/Base/FactoryMethodsTrait.php
-
8tests/Bootstrap.php
-
56tests/Feature/Collection/CollectionDeleteTest.php
-
34tests/Feature/Collection/CollectionShowTest.php
-
51tests/Feature/Collection/CollectionStoreTest.php
-
67tests/Feature/Collection/CollectionUpdateTest.php
-
47tests/Feature/FileDeleteTest.php
-
28tests/Feature/FileShowTest.php
-
206tests/Feature/FileStoreTest.php
-
47tests/Feature/FileUpdateTest.php
-
22tests/Feature/Traits/FileImageTrait.php
-
52tests/Feature/Traits/FileShowTrait.php
-
89tests/Feature/Traits/FileTraits.php
@ -0,0 +1,8 @@ |
|||
<?php |
|||
|
|||
namespace App\Http\Controllers\Traits; |
|||
|
|||
trait FileTrait |
|||
{ |
|||
public $availableParams = ['q','r','w','h','canv','brightness','saturation','hue','rotation','flip']; |
|||
} |
@ -0,0 +1,75 @@ |
|||
<?php |
|||
|
|||
namespace App\Http\Middleware; |
|||
|
|||
use App\Models\Collection; |
|||
use App\Models\File; |
|||
use Closure; |
|||
use Illuminate\Support\Str; |
|||
use Illuminate\Http\Request; |
|||
use Illuminate\Support\Facades\Storage; |
|||
|
|||
class BindFileModelMiddleware |
|||
{ |
|||
/** |
|||
* Handle an incoming request. |
|||
* |
|||
* @param \Illuminate\Http\Request $request |
|||
* @param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next |
|||
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse |
|||
*/ |
|||
public function handle(Request $request, Closure $next) |
|||
{ |
|||
|
|||
if (($request->route()->action['as'] == 'api.files.store') && is_null($request->file('file'))) { |
|||
$file = File::find($request->file); |
|||
|
|||
if (!is_null($file)) { |
|||
$Collection = Collection::where('name', $request->route('collection_name'))->firstOrFail(); |
|||
if (Storage::disk($Collection->disk)->exists($file->server_path . $file->uuid . '.' . $Collection->ext)) { |
|||
app()->bind('file', function () use ($file) { |
|||
return $file; |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
|
|||
if ($request->route()->action['as'] == 'api.files.show') { |
|||
$file = File::findOrFail($request->route('uuid')); |
|||
$Collection = Collection::where('name', $request->route('collection_name'))->firstOrFail(); |
|||
if (Storage::disk($Collection->disk)->exists($file->server_path . $file->uuid . '.' . $Collection->ext)) { |
|||
app()->bind('file', function () use ($file) { |
|||
return $file; |
|||
}); |
|||
}else{ |
|||
abort(404); |
|||
} |
|||
} |
|||
|
|||
if ($request->route()->action['as'] == 'api.files.update') { |
|||
$file = File::findOrFail($request->route('uuid')); |
|||
$Collection = Collection::where('name', $request->route('collection_name'))->firstOrFail(); |
|||
if (Storage::disk($Collection->disk)->exists($file->server_path . $file->uuid . '.' . $Collection->ext)) { |
|||
app()->bind('file', function () use ($file) { |
|||
return $file; |
|||
}); |
|||
}else{ |
|||
abort(404); |
|||
} |
|||
} |
|||
|
|||
if ($request->route()->action['as'] == 'api.files.destroy') { |
|||
$file = File::withTrashed()->findOrFail($request->route('uuid')); |
|||
$Collection = Collection::withTrashed()->where('name', $request->route('collection_name'))->firstOrFail(); |
|||
if (Storage::disk($Collection->disk)->exists($file->server_path . $file->uuid . '.' . $Collection->ext)) { |
|||
app()->bind('file', function () use ($file) { |
|||
return $file; |
|||
}); |
|||
}else{ |
|||
abort(404); |
|||
} |
|||
} |
|||
|
|||
return $next($request); |
|||
} |
|||
} |
@ -0,0 +1,72 @@ |
|||
<?php |
|||
|
|||
namespace App\Http\Requests; |
|||
|
|||
use App\Models\File; |
|||
use App\Utilities\Polices\BasePolicy; |
|||
use Illuminate\Foundation\Http\FormRequest; |
|||
use Illuminate\Support\Facades\Storage; |
|||
|
|||
class FileStoreRequest extends FormRequest |
|||
{ |
|||
public function isImage($file) |
|||
{ |
|||
if (@is_array(getimagesize($file))) { |
|||
return true; |
|||
} else { |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Determine if the user is authorized to make this request. |
|||
* |
|||
* @return bool |
|||
*/ |
|||
public function authorize() |
|||
{ |
|||
|
|||
|
|||
if (!app()->collection->tmp_support && !$this->model_id) { |
|||
return false; |
|||
} |
|||
if (app()->collection->count !== 1 && (app()->collection->count <= File::where('user_id', auth()->id())->where('collection_id',app()->collection->id)->count()) && !app()->collection->tmp_support) { |
|||
return false; |
|||
} |
|||
if (!app()->bound('file') && is_null($this->file('file'))) { |
|||
return false; |
|||
} |
|||
|
|||
if (!$this->hasFile('file')) { |
|||
$this->replace([ |
|||
'file' => new \Illuminate\Http\File(app()->file->getPath(), app()->file->uuid . '.' . app()->collection->ext) |
|||
]); |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
/** |
|||
* Get the validation rules that apply to the request. |
|||
* |
|||
* @return array<string, mixed> |
|||
*/ |
|||
public function rules() |
|||
{ |
|||
|
|||
return [ |
|||
"file" => [ |
|||
"mimes:" . app()->collection->getExts(), |
|||
"mimetypes:" . app()->collection->getMimeTypes(), |
|||
!$this->isImage(request()->file->path()) ?: "dimensions:min_width=" . app()->collection->min_width . ",min_height=" . app()->collection->min_height . ',max_width=' . app()->collection->max_width . ',max_height=' . app()->collection->max_height, |
|||
"max:" . app()->collection->max_file_size, |
|||
"min:" . app()->collection->min_file_size, |
|||
], |
|||
"alts" => [app()->collection->alt_required ? "required" : "nullable", 'array'], |
|||
"alts.*" => [app()->collection->alt_required ? "required" : "nullable", 'max:1000'], |
|||
"description" => [app()->collection->description_required ? "required" : "nullable", 'max:300'], |
|||
'original_name' => ["string", "nullable", 'max:300'], |
|||
'published_at' => ['date_format:Y-m-d H:i:s', 'nullable'], |
|||
]; |
|||
} |
|||
} |
@ -0,0 +1,34 @@ |
|||
<?php |
|||
|
|||
namespace App\Http\Requests; |
|||
|
|||
use Illuminate\Foundation\Http\FormRequest; |
|||
|
|||
class FileUpdateRequest extends FormRequest |
|||
{ |
|||
/** |
|||
* Determine if the user is authorized to make this request. |
|||
* |
|||
* @return bool |
|||
*/ |
|||
public function authorize() |
|||
{ |
|||
return true; |
|||
} |
|||
|
|||
/** |
|||
* Get the validation rules that apply to the request. |
|||
* |
|||
* @return array<string, mixed> |
|||
*/ |
|||
public function rules() |
|||
{ |
|||
return [ |
|||
"alts" => [app()->collection->alt_required ? "required" : "nullable", 'array'], |
|||
"alts.*" => [app()->collection->alt_required ? "required" : "nullable", 'max:1000'], |
|||
"description" => [app()->collection->description_required ? "required" : "nullable", 'max:300'], |
|||
'original_name' => ["string", "nullable", 'max:300'], |
|||
'published_at' => ['date_format:Y-m-d H:i:s', 'nullable'], |
|||
]; |
|||
} |
|||
} |
@ -0,0 +1,271 @@ |
|||
<?php |
|||
|
|||
namespace App\Image; |
|||
|
|||
use App\Http\Controllers\Traits\FileTrait; |
|||
use App\Image\Traits\ModulateTrait; |
|||
use Illuminate\Contracts\Validation\Rule; |
|||
use Illuminate\Support\Facades\Validator; |
|||
use Illuminate\Validation\Rule as ValidationRule; |
|||
use Jcupitt\Vips\Image; |
|||
use Jcupitt\Vips\Interpretation; |
|||
|
|||
class ImageProcessor |
|||
{ |
|||
use ModulateTrait, FileTrait; |
|||
|
|||
protected $defaultW = 2000; |
|||
protected $defaultH = 2000; |
|||
|
|||
public function processToBuffer(string $filename, array $options = [], $saveOptions = ['Q' => 100]) |
|||
{ |
|||
if (array_key_exists('w', $options) && array_key_exists('h', $options) && array_key_exists('r', $options)) { |
|||
unset($options['r']); |
|||
} |
|||
|
|||
if (array_key_exists('r', $options)) { |
|||
$options['r'] = explode(':', $options['r']); |
|||
if (count($options['r']) != 2) { |
|||
unset($options['r']); |
|||
} |
|||
|
|||
$validator = Validator::make($options, [ |
|||
'r' => ['nullable', 'array'], |
|||
'r.*' => ['nullable', 'numeric'], |
|||
]); |
|||
if ($validator->fails()) { |
|||
foreach ($validator->messages()->getMessages() as $field_name => $messages) { |
|||
unset($options[explode('.', $field_name)[0]]); |
|||
} |
|||
} |
|||
} |
|||
|
|||
if (array_key_exists('r', $options)) { |
|||
|
|||
if (array_key_exists('w', $options) && !array_key_exists('h', $options)) { |
|||
$options['w'] = $options['w'] * $options['r'][0]; |
|||
$options['h'] = $options['w'] * $options['r'][1]; |
|||
} |
|||
|
|||
if (!array_key_exists('w', $options) && array_key_exists('h', $options)) { |
|||
$options['w'] = $options['h'] * $options['r'][0]; |
|||
$options['h'] = $options['h'] * $options['r'][1]; |
|||
} |
|||
if (!array_key_exists('w', $options) && !array_key_exists('h', $options)) { |
|||
$options['w'] = getimagesize($filename)[0] * $options['r'][0]; |
|||
$options['h'] = getimagesize($filename)[0] * $options['r'][1]; |
|||
} |
|||
|
|||
unset($options['r']); |
|||
} |
|||
|
|||
$validator = Validator::make($options, [ |
|||
'w' => ['numeric', 'between:1,2000'], |
|||
'h' => ['numeric', 'between:1,2000'], |
|||
'canv' => ['boolean'], |
|||
'brightness' => ['numeric', 'between:0,100'], |
|||
'saturation' => ['numeric', 'between:0,100'], |
|||
'hue' => ['numeric', 'between:0,100'], |
|||
'rotation' => ['numeric'], |
|||
'flip' => ['string', ValidationRule::in(['h', 'v', 'hv'])] |
|||
]); |
|||
|
|||
if ($validator->fails()) { |
|||
foreach ($validator->messages()->getMessages() as $field_name => $messages) { |
|||
unset($options[$field_name]); |
|||
} |
|||
} |
|||
|
|||
|
|||
if (!array_key_exists('w', $options) && !array_key_exists('h', $options)) { |
|||
$image = Image::newFromFile($filename); |
|||
} |
|||
|
|||
if (array_key_exists('w', $options) && !array_key_exists('h', $options)) { |
|||
$image = Image::thumbnail($filename, $options['w'], ['height' => $this->defaultH]); |
|||
} |
|||
|
|||
if (!array_key_exists('w', $options) && array_key_exists('h', $options)) { |
|||
$image = Image::thumbnail($filename, $this->defaultW, ['height' => $options['h']]); |
|||
} |
|||
|
|||
if (array_key_exists('w', $options) && array_key_exists('h', $options)) { |
|||
if (array_key_exists('canv', $options) && ($options['canv'] == true)) { |
|||
$image = Image::thumbnail($filename, $options['w'], ['height' => $options['h']]); |
|||
$widthH = ($options['h'] - $image->height) / 2; |
|||
$widthW = ($options['w'] - $image->width) / 2; |
|||
$image = $image->embed( |
|||
$widthW, |
|||
$widthH, |
|||
$options['w'], |
|||
$options['h'], |
|||
['extend' => 'background', 'background' => [255,255,255]] |
|||
|
|||
); |
|||
} else { |
|||
$image = Image::thumbnail($filename, $options['w'], ['height' => $options['h'], 'crop' => 'centre']); |
|||
} |
|||
} |
|||
|
|||
if (array_key_exists('brightness', $options) || array_key_exists('saturation', $options) || array_key_exists('hue', $options)) { |
|||
$image = $this->brightness($image, array_key_exists('brightness', $options) ? $options['brightness'] : 1.0, array_key_exists('saturation', $options) ? $options['saturation'] : 1.0, array_key_exists('hue', $options) ? $options['hue'] : 0.0); |
|||
} |
|||
|
|||
if (array_key_exists('rotation', $options)) { |
|||
$image = $image->rotate($options['rotation']); |
|||
} |
|||
|
|||
if (array_key_exists('flip', $options)) { |
|||
if ($options['flip'] == "h") { |
|||
$image = $image->fliphor(); |
|||
} |
|||
|
|||
if ($options['flip'] == "v") { |
|||
$image = $image->flipver(); |
|||
} |
|||
|
|||
if ($options['flip'] == "hv") { |
|||
$image = $image->fliphor(); |
|||
$image = $image->flipver(); |
|||
} |
|||
} |
|||
if (array_key_exists('q', $options)) { |
|||
$saveOptions['Q'] = $options['q']; |
|||
} |
|||
if (app()->file->ext == 'png') { |
|||
$saveOptions = array_merge(['palette'=>true],$saveOptions); |
|||
} |
|||
|
|||
return $image->writeToBuffer("." . app()->file->ext, $saveOptions); |
|||
} |
|||
|
|||
public function process(string $filename, string $target, array $options = [], $saveOptions = ['Q' => 100]) |
|||
{ |
|||
|
|||
|
|||
if (array_key_exists('w', $options) && array_key_exists('h', $options) && array_key_exists('r', $options)) { |
|||
unset($options['r']); |
|||
} |
|||
|
|||
if (array_key_exists('r', $options)) { |
|||
$options['r'] = explode(':', $options['r']); |
|||
if (count($options['r']) != 2) { |
|||
unset($options['r']); |
|||
} |
|||
|
|||
$validator = Validator::make($options, [ |
|||
'r' => ['nullable', 'array'], |
|||
'r.*' => ['nullable', 'numeric'], |
|||
]); |
|||
if ($validator->fails()) { |
|||
foreach ($validator->messages()->getMessages() as $field_name => $messages) { |
|||
unset($options[explode('.', $field_name)[0]]); |
|||
} |
|||
} |
|||
} |
|||
|
|||
if (array_key_exists('r', $options)) { |
|||
|
|||
if (array_key_exists('w', $options) && !array_key_exists('h', $options)) { |
|||
$options['w'] = $options['w'] * $options['r'][0]; |
|||
$options['h'] = $options['w'] * $options['r'][1]; |
|||
} |
|||
|
|||
if (!array_key_exists('w', $options) && array_key_exists('h', $options)) { |
|||
$options['w'] = $options['h'] * $options['r'][0]; |
|||
$options['h'] = $options['h'] * $options['r'][1]; |
|||
} |
|||
if (!array_key_exists('w', $options) && !array_key_exists('h', $options)) { |
|||
$options['w'] = getimagesize($filename)[0] * $options['r'][0]; |
|||
$options['h'] = getimagesize($filename)[0] * $options['r'][1]; |
|||
} |
|||
|
|||
unset($options['r']); |
|||
} |
|||
|
|||
$validator = Validator::make($options, [ |
|||
'w' => ['numeric', 'between:1,2000'], |
|||
'h' => ['numeric', 'between:1,2000'], |
|||
'canv' => ['boolean'], |
|||
'brightness' => ['numeric', 'between:0,100'], |
|||
'saturation' => ['numeric', 'between:0,100'], |
|||
'hue' => ['numeric', 'between:0,100'], |
|||
'rotation' => ['numeric'], |
|||
'flip' => ['string', ValidationRule::in(['h', 'v', 'hv'])] |
|||
]); |
|||
|
|||
if ($validator->fails()) { |
|||
foreach ($validator->messages()->getMessages() as $field_name => $messages) { |
|||
unset($options[$field_name]); |
|||
} |
|||
} |
|||
|
|||
|
|||
if (!array_key_exists('w', $options) && !array_key_exists('h', $options)) { |
|||
$image = Image::newFromFile($filename); |
|||
} |
|||
|
|||
if (array_key_exists('w', $options) && !array_key_exists('h', $options)) { |
|||
$image = Image::thumbnail($filename, $options['w'], ['height' => $this->defaultH]); |
|||
} |
|||
|
|||
if (!array_key_exists('w', $options) && array_key_exists('h', $options)) { |
|||
$image = Image::thumbnail($filename, $this->defaultW, ['height' => $options['h']]); |
|||
} |
|||
|
|||
if (array_key_exists('w', $options) && array_key_exists('h', $options)) { |
|||
if (array_key_exists('canv', $options) && ($options['canv'] == true)) { |
|||
$image = Image::thumbnail($filename, $options['w'], ['height' => $options['h']]); |
|||
$widthH = ($options['h'] - $image->height) / 2; |
|||
$widthW = ($options['w'] - $image->width) / 2; |
|||
$image = $image->embed( |
|||
$widthW, |
|||
$widthH, |
|||
$options['w'], |
|||
$options['h'], |
|||
['extend' => 'background', 'background' => 0] |
|||
); |
|||
} else { |
|||
$image = Image::thumbnail($filename, $options['w'], ['height' => $options['h'], 'crop' => 'centre']); |
|||
} |
|||
} |
|||
|
|||
if (array_key_exists('brightness', $options) || array_key_exists('saturation', $options) || array_key_exists('hue', $options)) { |
|||
$image = $this->brightness($image, array_key_exists('brightness', $options) ? $options['brightness'] : 1.0, array_key_exists('saturation', $options) ? $options['saturation'] : 1.0, array_key_exists('hue', $options) ? $options['hue'] : 0.0); |
|||
} |
|||
|
|||
if (array_key_exists('rotation', $options)) { |
|||
$image = $image->rotate($options['rotation']); |
|||
} |
|||
|
|||
if (array_key_exists('flip', $options)) { |
|||
if ($options['flip'] == "h") { |
|||
$image = $image->fliphor(); |
|||
} |
|||
|
|||
if ($options['flip'] == "v") { |
|||
$image = $image->flipver(); |
|||
} |
|||
|
|||
if ($options['flip'] == "hv") { |
|||
$image = $image->fliphor(); |
|||
$image = $image->flipver(); |
|||
} |
|||
} |
|||
$image->writeToFile($target, $saveOptions); |
|||
|
|||
return new \Illuminate\Http\File($target); |
|||
} |
|||
|
|||
public function createFakeImage($stub, $path, $saveOptions = ['Q' => 100], $options = ["w" => null, "h" => null, "r" => null, "flip" => null, "canv" => null, "rotation" => null]) |
|||
{ |
|||
return $this->process($stub, $path, $options, $saveOptions); |
|||
} |
|||
|
|||
public function convertImage(string $filePath, string $target, array $options = ['Q' => 100]): \Illuminate\Http\File |
|||
{ |
|||
$tmpFile = \Jcupitt\Vips\Image::newFromFile($filePath); |
|||
$tmpFile->writeToFile($target, $options); |
|||
return new \Illuminate\Http\File($target); |
|||
} |
|||
} |
@ -0,0 +1,31 @@ |
|||
<?php |
|||
|
|||
namespace App\Providers; |
|||
|
|||
use Illuminate\Support\ServiceProvider; |
|||
use Illuminate\Support\Str; |
|||
|
|||
class UuidServiceProvider extends ServiceProvider |
|||
{ |
|||
/** |
|||
* Register services. |
|||
* |
|||
* @return void |
|||
*/ |
|||
public function register() |
|||
{ |
|||
//
|
|||
} |
|||
|
|||
/** |
|||
* Bootstrap services. |
|||
* |
|||
* @return void |
|||
*/ |
|||
public function boot() |
|||
{ |
|||
$this->app->bind('uuid', function () { |
|||
return Str::uuid(); |
|||
}); |
|||
} |
|||
} |
@ -0,0 +1,193 @@ |
|||
<?php |
|||
|
|||
namespace App\Utilities\Helpers; |
|||
|
|||
use Faker\Provider\Base; |
|||
|
|||
class Image extends Base { |
|||
/** |
|||
* @var string |
|||
*/ |
|||
public const BASE_URL = 'https://via.placeholder.com'; |
|||
|
|||
public const FORMAT_JPG = 'jpg'; |
|||
public const FORMAT_JPEG = 'jpeg'; |
|||
public const FORMAT_PNG = 'png'; |
|||
|
|||
/** |
|||
* @var array |
|||
* |
|||
* @deprecated Categories are no longer used as a list in the placeholder API but referenced as string instead |
|||
*/ |
|||
protected static $categories = [ |
|||
'abstract', 'animals', 'business', 'cats', 'city', 'food', 'nightlife', |
|||
'fashion', 'people', 'nature', 'sports', 'technics', 'transport', |
|||
]; |
|||
|
|||
/** |
|||
* Generate the URL that will return a random image |
|||
* |
|||
* Set randomize to false to remove the random GET parameter at the end of the url. |
|||
* |
|||
* @example 'http://via.placeholder.com/640x480.png/CCCCCC?text=well+hi+there' |
|||
* |
|||
* @param int $width |
|||
* @param int $height |
|||
* @param string|null $category |
|||
* @param bool $randomize |
|||
* @param string|null $word |
|||
* @param bool $gray |
|||
* @param string $format |
|||
* |
|||
* @return string |
|||
*/ |
|||
public static function imageUrl( |
|||
$width = 640, |
|||
$height = 480, |
|||
$category = null, |
|||
$randomize = true, |
|||
$word = null, |
|||
$gray = false, |
|||
$format = 'png' |
|||
) { |
|||
trigger_deprecation( |
|||
'fakerphp/faker', |
|||
'1.20', |
|||
'Provider is deprecated and will no longer be available in Faker 2. Please use a custom provider instead' |
|||
); |
|||
|
|||
// Validate image format
|
|||
$imageFormats = static::getFormats(); |
|||
|
|||
if (!in_array(strtolower($format), $imageFormats, true)) { |
|||
throw new \InvalidArgumentException(sprintf( |
|||
'Invalid image format "%s". Allowable formats are: %s', |
|||
$format, |
|||
implode(', ', $imageFormats) |
|||
)); |
|||
} |
|||
|
|||
$size = sprintf('%dx%d.%s', $width, $height, $format); |
|||
|
|||
$imageParts = []; |
|||
|
|||
if ($category !== null) { |
|||
$imageParts[] = $category; |
|||
} |
|||
|
|||
if ($word !== null) { |
|||
$imageParts[] = $word; |
|||
} |
|||
|
|||
if ($randomize === true) { |
|||
$imageParts[] = Lorem::word(); |
|||
} |
|||
|
|||
$backgroundColor = $gray === true ? 'CCCCCC' : str_replace('#', '', Color::safeHexColor()); |
|||
|
|||
return sprintf( |
|||
'%s/%s/%s%s', |
|||
self::BASE_URL, |
|||
$size, |
|||
$backgroundColor, |
|||
count($imageParts) > 0 ? '?text=' . urlencode(implode(' ', $imageParts)) : '' |
|||
); |
|||
} |
|||
|
|||
/** |
|||
* Download a remote random image to disk and return its location |
|||
* |
|||
* Requires curl, or allow_url_fopen to be on in php.ini. |
|||
* |
|||
* @example '/path/to/dir/13b73edae8443990be1aa8f1a483bc27.png' |
|||
* |
|||
* @return bool|string |
|||
*/ |
|||
public static function image( |
|||
$dir = null, |
|||
$width = 640, |
|||
$height = 480, |
|||
$category = null, |
|||
$fullPath = true, |
|||
$randomize = true, |
|||
$word = null, |
|||
$gray = false, |
|||
$format = 'png' |
|||
) { |
|||
trigger_deprecation( |
|||
'fakerphp/faker', |
|||
'1.20', |
|||
'Provider is deprecated and will no longer be available in Faker 2. Please use a custom provider instead' |
|||
); |
|||
|
|||
$dir = null === $dir ? sys_get_temp_dir() : $dir; // GNU/Linux / OS X / Windows compatible
|
|||
// Validate directory path
|
|||
if (!is_dir($dir) || !is_writable($dir)) { |
|||
throw new \InvalidArgumentException(sprintf('Cannot write to directory "%s"', $dir)); |
|||
} |
|||
|
|||
// Generate a random filename. Use the server address so that a file
|
|||
// generated at the same time on a different server won't have a collision.
|
|||
$name = md5(uniqid(empty($_SERVER['SERVER_ADDR']) ? '' : $_SERVER['SERVER_ADDR'], true)); |
|||
$filename = sprintf('%s.%s', $name, $format); |
|||
$filepath = $dir . DIRECTORY_SEPARATOR . $filename; |
|||
|
|||
$url = static::imageUrl($width, $height, $category, $randomize, $word, $gray, $format); |
|||
|
|||
// save file
|
|||
if (function_exists('curl_exec')) { |
|||
// use cURL
|
|||
$fp = fopen($filepath, 'w'); |
|||
$ch = curl_init($url); |
|||
curl_setopt($ch, CURLOPT_FILE, $fp); |
|||
$success = curl_exec($ch) && curl_getinfo($ch, CURLINFO_HTTP_CODE) === 200; |
|||
fclose($fp); |
|||
curl_close($ch); |
|||
|
|||
if (!$success) { |
|||
unlink($filepath); |
|||
|
|||
// could not contact the distant URL or HTTP error - fail silently.
|
|||
return false; |
|||
} |
|||
} elseif (ini_get('allow_url_fopen')) { |
|||
// use remote fopen() via copy()
|
|||
$success = copy($url, $filepath); |
|||
|
|||
if (!$success) { |
|||
// could not contact the distant URL or HTTP error - fail silently.
|
|||
return false; |
|||
} |
|||
} else { |
|||
return new \RuntimeException('The image formatter downloads an image from a remote HTTP server. Therefore, it requires that PHP can request remote hosts, either via cURL or fopen()'); |
|||
} |
|||
|
|||
return $fullPath ? $filepath : $filename; |
|||
} |
|||
|
|||
public static function getFormats(): array |
|||
{ |
|||
trigger_deprecation( |
|||
'fakerphp/faker', |
|||
'1.20', |
|||
'Provider is deprecated and will no longer be available in Faker 2. Please use a custom provider instead' |
|||
); |
|||
|
|||
return array_keys(static::getFormatConstants()); |
|||
} |
|||
|
|||
public static function getFormatConstants(): array |
|||
{ |
|||
trigger_deprecation( |
|||
'fakerphp/faker', |
|||
'1.20', |
|||
'Provider is deprecated and will no longer be available in Faker 2. Please use a custom provider instead' |
|||
); |
|||
|
|||
return [ |
|||
static::FORMAT_JPG => constant('IMAGETYPE_JPEG'), |
|||
static::FORMAT_JPEG => constant('IMAGETYPE_JPEG'), |
|||
static::FORMAT_PNG => constant('IMAGETYPE_PNG'), |
|||
]; |
|||
} |
|||
} |
Binary file not shown.
Before Width: 423 | Height: 750 | Size: 468 KiB After Width: 423 | Height: 750 | Size: 468 KiB |
Binary file not shown.
After Width: 720 | Height: 1204 | Size: 124 KiB |
Binary file not shown.
After Width: 423 | Height: 750 | Size: 42 KiB |
@ -0,0 +1,56 @@ |
|||
<?php |
|||
|
|||
namespace Tests\Feature\Collection; |
|||
|
|||
use App\Models\Collection; |
|||
use Tests\Bootstrap; |
|||
|
|||
class CollectionDeleteTest extends Bootstrap |
|||
{ |
|||
public function test_collection_delete_success() |
|||
{ |
|||
$this->modelWithPolicy('collections', ['permission:collections.delete']) |
|||
->loginAs(['collections.delete']) |
|||
->deleteJson(route("api.collections.destroy", $collection = $this->one(Collection::class))) |
|||
->assertOk(); |
|||
|
|||
$this->loginAsAdmin() |
|||
->getJson(route("api.collections.show", $collection)) |
|||
->assertNotFound(); |
|||
} |
|||
|
|||
public function test_collection_restore_success() |
|||
{ |
|||
$this->modelWithPolicy('collections', ['permission:collections.restore']) |
|||
->loginAsUser(['collections.restore']) |
|||
->deleteJson(route("api.collections.destroy", $collection = $this->trashed(Collection::class))) |
|||
->assertOk(); |
|||
|
|||
$this->loginAsAdmin() |
|||
->getJson(route("api.collections.show", $collection)) |
|||
->assertOk(); |
|||
} |
|||
|
|||
public function test_collection_delete_forbidden() |
|||
{ |
|||
$this->modelWithPolicy('collections', ['permission:collections.delete']) |
|||
->loginAs(['wrong.permission']) |
|||
->deleteJson(route("api.collections.destroy", $collection = $this->one(Collection::class))) |
|||
->assertForbidden(); |
|||
} |
|||
|
|||
public function test_collection_restore_forbidden() |
|||
{ |
|||
$this->modelWithPolicy('collections', ['permission:collections.restore']) |
|||
->loginAs(['wrong.permission']) |
|||
->deleteJson(route("api.collections.destroy", $collection = $this->trashed(Collection::class))) |
|||
->assertForbidden(); |
|||
} |
|||
|
|||
public function test_collection_delete_notFound() |
|||
{ |
|||
$this->loginAsAdmin() |
|||
->deleteJson(route("api.collections.destroy", 0)) |
|||
->assertNotFound(); |
|||
} |
|||
} |
@ -0,0 +1,34 @@ |
|||
<?php |
|||
|
|||
namespace Tests\Feature\Collection; |
|||
|
|||
use App\Models\Collection; |
|||
use Tests\Bootstrap; |
|||
use App\Models\User; |
|||
|
|||
class CollectionShowTest extends Bootstrap |
|||
{ |
|||
public function test_collection_show_success() |
|||
{ |
|||
$this->modelWithPolicy('collections', ['permission:collections.show']) |
|||
->loginAs(['collections.show']) |
|||
->getJson(route("api.collections.show", $collection = $this->one(Collection::class))) |
|||
->assertOk(); |
|||
} |
|||
|
|||
public function test_collection_show_not_found() |
|||
{ |
|||
$this->modelWithPolicy('collections', ['permission:collections.show']) |
|||
->loginAs(['collections.show']) |
|||
->getJson(route("api.collections.show", 0)) |
|||
->assertNotFound(); |
|||
} |
|||
|
|||
public function test_collection_show_forbidden() |
|||
{ |
|||
$this->modelWithPolicy('collections', ['permission:collections.show']) |
|||
->loginAs(['wrong.permission']) |
|||
->getJson(route("api.collections.show", $collection = $this->one(Collection::class)), []) |
|||
->assertForbidden(); |
|||
} |
|||
} |
@ -0,0 +1,51 @@ |
|||
<?php |
|||
|
|||
namespace Tests\Feature\Collection; |
|||
|
|||
use App\Models\Collection; |
|||
use App\Documents\UserDocument; |
|||
use Tests\Bootstrap; |
|||
use Illuminate\Support\Arr; |
|||
|
|||
class CollectionStoreTest extends Bootstrap |
|||
{ |
|||
public function test_collection_store_success() |
|||
{ |
|||
$this->modelWithPolicy('collections', ['permission:collections.store']) |
|||
->loginAs(['collections.store']) |
|||
->postJson(route('api.collections.store'), $collection = $this->make(Collection::class)) |
|||
->assertCreated(); |
|||
} |
|||
|
|||
/** |
|||
* @testWith |
|||
* ["name:gtString"] |
|||
* ["name:numeric"] |
|||
* ["name:null"] |
|||
* ["path:gtString"] |
|||
* ["path:numeric"] |
|||
* ["public:string"] |
|||
* ["disk:numeric"] |
|||
* ["disk:null"] |
|||
* ["disk:gtString"] |
|||
* ["count:null"] |
|||
* ["count:string"] |
|||
* ["count:gt:100000"] |
|||
* |
|||
*/ |
|||
public function test_collection_store_unprocessable($field) |
|||
{ |
|||
$this->modelWithPolicy('collections', ['permission:collections.store']) |
|||
->loginAsAdmin() |
|||
->postJson(route("api.collections.store"), $collection = $this->make(collection::class, smash: $field, withDependency: true)) |
|||
->assertUnprocessable(); |
|||
} |
|||
|
|||
public function test_collection_store_forbidden() |
|||
{ |
|||
$this->modelWithPolicy('collections', ['permission:collections.store']) |
|||
->loginAs(['wrong.permission']) |
|||
->postJson(route("api.collections.store"), []) |
|||
->assertForbidden(); |
|||
} |
|||
} |
@ -0,0 +1,67 @@ |
|||
<?php |
|||
|
|||
namespace Tests\Feature\Collection; |
|||
|
|||
use App\Models\Collection; |
|||
use Tests\Bootstrap; |
|||
use App\Models\User; |
|||
use Illuminate\Support\Arr; |
|||
|
|||
|
|||
class CollectionUpdateTest extends Bootstrap |
|||
{ |
|||
public function test_collection_update_success() |
|||
{ |
|||
$this->modelWithPolicy('collections', ['permission:collections.update']) |
|||
->loginAs(['collections.update']) |
|||
->putJson( |
|||
route("api.collections.update", $collection = $this->one(Collection::class)), |
|||
$update = $this->make(Collection::class, withDependency: true) |
|||
) |
|||
->assertOk(); |
|||
} |
|||
|
|||
/** |
|||
* @testWith |
|||
* ["name:gtString"] |
|||
* ["name:numeric"] |
|||
* ["name:null"] |
|||
* ["path:gtString"] |
|||
* ["path:numeric"] |
|||
* ["public:string"] |
|||
* ["disk:numeric"] |
|||
* ["disk:null"] |
|||
* ["disk:gtString"] |
|||
* ["count:null"] |
|||
* ["count:string"] |
|||
* ["count:gt:100000"] |
|||
* |
|||
*/ |
|||
public function test_collection_update_unprocessable($field) |
|||
{ |
|||
$this->modelWithPolicy('collections', ['permission:collections.update']) |
|||
->loginAs(['collections.update'])->putJson( |
|||
route("api.collections.update", $collection = $this->one(Collection::class)), |
|||
$update = $this->make(collection::class, smash: $field, withDependency: true) |
|||
) |
|||
->assertUnprocessable(); |
|||
} |
|||
|
|||
public function test_collection_update_forbidden() |
|||
{ |
|||
$this->modelWithPolicy('collections', ['permission:collections.update']) |
|||
->loginAs(['wrong.permission']) |
|||
->putJson( |
|||
route("api.collections.update", $collection = $this->one(Collection::class)), |
|||
[] |
|||
) |
|||
->assertForbidden(); |
|||
} |
|||
|
|||
public function test_collection_update_not_found() |
|||
{ |
|||
$this->loginAsUser(['collections.update']) |
|||
->putJson(route("api.collections.update", 0), []) |
|||
->assertNotFound(); |
|||
} |
|||
} |
@ -0,0 +1,47 @@ |
|||
<?php |
|||
|
|||
namespace Tests\Feature; |
|||
|
|||
use App\Image\ImageProcessor; |
|||
use App\Models\Collection; |
|||
use App\Models\File; |
|||
use Illuminate\Foundation\Testing\RefreshDatabase; |
|||
use Illuminate\Foundation\Testing\WithFaker; |
|||
use Illuminate\Support\Facades\Storage; |
|||
use Tests\Bootstrap; |
|||
use Tests\TestCase; |
|||
|
|||
class FileDeleteTest extends Bootstrap |
|||
{ |
|||
// public function test_user_with_permission_can_not_delete_file()
|
|||
// {
|
|||
// $this->assertFalse("it's not mohammad's fault, I'm waiting for dynamic policy");
|
|||
// }
|
|||
|
|||
public function test_user_with_permission_can_delete_file() |
|||
{ |
|||
$file = $this->one(File::class, dependencyAttributes: ['withImage' => true]); |
|||
$collection = Collection::find($file->collection_id); |
|||
$response = $this->loginAs()->deleteJson(route('api.files.destroy', ['collection_name' => $collection->name, 'uuid' => $file->uuid, 'extention' => $collection->ext])); |
|||
$response->assertok(); |
|||
} |
|||
|
|||
public function test_file_restore_success() |
|||
{ |
|||
$this->modelWithPolicy('collections', ['permission:collections.restore']) |
|||
->loginAsUser(['collections.restore']) |
|||
->deleteJson(route("api.collections.destroy", $collection = $this->trashed(Collection::class, dependencyAttributes: ['withImage' => true]))) |
|||
->assertOk(); |
|||
|
|||
$this->loginAsAdmin() |
|||
->getJson(route("api.collections.show", $collection)) |
|||
->assertOk(); |
|||
} |
|||
|
|||
public function test_file_delete_notFound() |
|||
{ |
|||
$this->loginAsAdmin() |
|||
->deleteJson(route("api.files.destroy", ['collection_name' => 'not found', 'uuid' => 'wrong uuid', 'extention' => 'wrong ext'])) |
|||
->assertNotFound(); |
|||
} |
|||
} |
@ -0,0 +1,28 @@ |
|||
<?php |
|||
|
|||
namespace Tests\Feature; |
|||
|
|||
use App\Image\ImageProcessor; |
|||
use App\Models\Collection; |
|||
use App\Models\File; |
|||
use Illuminate\Foundation\Testing\RefreshDatabase; |
|||
use Illuminate\Foundation\Testing\WithFaker; |
|||
use Illuminate\Support\Facades\Storage; |
|||
use Tests\Feature\Traits\FileImageTrait; |
|||
use Tests\Feature\Traits\FileShowTrait; |
|||
use Tests\TestCase; |
|||
|
|||
class FileShowTest extends TestCase |
|||
{ |
|||
use FileShowTrait, FileImageTrait; |
|||
public function test_user_can_resize_image_with_width_and_height_fields_ok() |
|||
{ |
|||
$file = $this->one(File::class, dependencyAttributes: ['withImage' => true]); |
|||
$collection = Collection::find($file->collection_id); |
|||
$w = rand(10, 2000); |
|||
$h = rand(10, 2000); |
|||
$canv = rand(0, 1); |
|||
$response = $this->loginAs()->getJson(route('api.files.show', ['collection_name' => $collection->name, 'uuid' => $file->uuid, 'extention' => $collection->ext, 'w' => $w, 'h' => $h, 'canv' => $canv])); |
|||
$response->assertOk()->assertHeader('content-type', $response->headers->get('content-type')); |
|||
} |
|||
} |
@ -0,0 +1,206 @@ |
|||
<?php |
|||
|
|||
namespace Tests\Feature; |
|||
|
|||
use App\Http\Controllers\Traits\FileTrait; |
|||
use App\Image\ImageProcessor; |
|||
use App\Models\Collection; |
|||
use App\Models\File; |
|||
use Illuminate\Foundation\Testing\RefreshDatabase; |
|||
use Illuminate\Foundation\Testing\WithFaker; |
|||
use Illuminate\Http\UploadedFile; |
|||
use Illuminate\Support\Facades\Storage; |
|||
use Tests\Bootstrap; |
|||
use Tests\TestCase; |
|||
use Illuminate\Support\Str; |
|||
use Tests\Feature\Traits\FileTraits; |
|||
|
|||
class FileStoreTest extends Bootstrap |
|||
{ |
|||
use FileTraits; |
|||
|
|||
public function test_tmp_and_model_id_forbidden() |
|||
{ |
|||
$data = [ |
|||
"file" => UploadedFile::fake()->image('test.png'), |
|||
"alts" => ['1', '2', '3'], |
|||
"description" => 'lfjdsklfslfsdlfasdfsfhgsfgsdf', |
|||
"public" => 1 |
|||
]; |
|||
$collection = Collection::factory()->createQuietly([ |
|||
'tmp_support' => false |
|||
]); |
|||
|
|||
$response = $this->loginAs()->postJson(route('api.files.store', ['collection_name' => $collection->name]), $data); |
|||
$response->assertForbidden(); |
|||
} |
|||
|
|||
public function test_tmp_false_and_collection_is_full_forbidden() |
|||
{ |
|||
|
|||
$randomCount = rand(2, 10); |
|||
$collection = Collection::factory()->createQuietly([ |
|||
'tmp_support' => false, |
|||
'count' => $randomCount |
|||
]); |
|||
|
|||
for ($i = $randomCount; $i > 0; $i--) { |
|||
$uuid = app()->uuid; |
|||
$this->one(File::class, [ |
|||
'uuid' => $uuid, |
|||
'user_id' => 1, |
|||
'collection_id' => $collection->id, |
|||
'server_path' => '/' . date('y') . '/' . date('m') . '/' . $uuid . '.' . $collection->ext, |
|||
]); |
|||
} |
|||
|
|||
$data = [ |
|||
"file" => UploadedFile::fake()->image('test.png'), |
|||
"alts" => ['1', '2', '3'], |
|||
"description" => 'lfjdsklfslfsdlfasdfsfhgsfgsdf', |
|||
"public" => 1 |
|||
]; |
|||
$response = $this->loginAs()->postJson(route('api.files.store', ['collection_name' => $collection->name]), $data); |
|||
$response->assertForbidden(); |
|||
} |
|||
|
|||
public function test_file_is_not_isset_forbidden() |
|||
{ |
|||
$collection = Collection::factory()->createQuietly([ |
|||
'tmp_support' => true |
|||
]); |
|||
$data = [ |
|||
"file" => app()->uuid, |
|||
"alts" => ['1', '2', '3'], |
|||
"description" => 'lfjdsklfslfsdlfasdfsfhgsfgsdf', |
|||
"public" => 1 |
|||
]; |
|||
|
|||
$response = $this->loginAs()->postJson(route('api.files.store', ['collection_name' => $collection->name]), $data); |
|||
$response->assertForbidden(); |
|||
} |
|||
|
|||
public function test_user_can_not_access_not_ownered_files() |
|||
{ |
|||
$collection = Collection::factory()->createQuietly(); |
|||
$file = File::factory()->createQuietly([ |
|||
'user_id' => auth()->id() + 1234, |
|||
'collection_id' => $collection->id |
|||
]); |
|||
|
|||
$data = [ |
|||
"file" => $file->uuid, |
|||
"public" => 1 |
|||
]; |
|||
|
|||
$response = $this->loginAs()->postJson(route('api.files.store', ['collection_name' => $collection->name]), $data); |
|||
$response->assertForbidden(); |
|||
} |
|||
|
|||
/** |
|||
* @dataProvider storeValidationTestProvider |
|||
*/ |
|||
public function test_store_dynamic_validation_unprocessable($collectionFields, $dataFields) |
|||
{ |
|||
$collection = Collection::factory()->createQuietly($collectionFields); |
|||
$response = $this->loginAs()->postJson(route('api.files.store', ['collection_name' => $collection->name]), $dataFields); |
|||
$response->assertUnprocessable(); |
|||
} |
|||
|
|||
public function test_store_static_validation_unprocessable() |
|||
{ |
|||
$collection = Collection::factory()->createQuietly([ |
|||
'min_file_size' => 0 |
|||
]); |
|||
|
|||
|
|||
$data = [ |
|||
"file" => UploadedFile::fake()->image('lol.png'), |
|||
"description" => Str::random(1000) |
|||
]; |
|||
|
|||
$this->modelWithPolicy('collections', ['permission:collections.store']) |
|||
->loginAsAdmin() |
|||
->postJson(route("api.files.store", ['collection_name' => $collection->name]), $data) |
|||
->assertUnprocessable(); |
|||
} |
|||
|
|||
|
|||
/** |
|||
* @dataProvider storeValidationTestProvider |
|||
*/ |
|||
public function test_store_dynamic_validation_stored_file_unprocessable($collectionFields) |
|||
{ |
|||
$collectionFields['withImage'] = true; |
|||
$file = $this->one(File::class, dependencyAttributes: $collectionFields); |
|||
$collection = Collection::find($file->collection_id); |
|||
$data = [ |
|||
"file" => $file->uuid, |
|||
]; |
|||
|
|||
$response = $this->loginAs()->postJson(route('api.files.store', ['collection_name' => $collection->name]), $data); |
|||
$response->assertUnprocessable(); |
|||
} |
|||
|
|||
public function test_user_can_store_sent_file_count_multiple_without_image_processor_ok() |
|||
{ |
|||
$collectionFields['withImage'] = true; |
|||
$file = $this->one(File::class, dependencyAttributes: $collectionFields); |
|||
$collection = Collection::find($file->collection_id); |
|||
|
|||
|
|||
$data = [ |
|||
"file" => UploadedFile::fake()->image('test.png'), |
|||
]; |
|||
$response = $this->loginAs()->postJson(route('api.files.store', ['collection_name' => $collection->name]), $data); |
|||
$response->assertCreated(); |
|||
} |
|||
|
|||
public function test_user_can_store_sent_file_uuid_count_multiple_without_image_processor_ok() |
|||
{ |
|||
$collectionFields['withImage'] = true; |
|||
$file = $this->one(File::class, dependencyAttributes: $collectionFields); |
|||
$collection = Collection::find($file->collection_id); |
|||
|
|||
$data = [ |
|||
"file" => $file->uuid, |
|||
"public" => 1 |
|||
]; |
|||
|
|||
$response = $this->loginAs()->postJson(route('api.files.store', ['collection_name' => $collection->name]), $data); |
|||
$response->assertCreated(); |
|||
} |
|||
|
|||
public function test_user_can_store_sent_file_count_one_without_image_processor_ok() |
|||
{ |
|||
$collection = Collection::factory()->createQuietly([ |
|||
'count' => 1 |
|||
]); |
|||
|
|||
|
|||
$data = [ |
|||
"file" => UploadedFile::fake()->image('lol.png'), |
|||
"public" => 1 |
|||
]; |
|||
|
|||
$response = $this->loginAs()->postJson(route('api.files.store', ['collection_name' => $collection->name, 'model_id' => 100]), $data); |
|||
$response->assertCreated(); |
|||
} |
|||
|
|||
public function test_user_can_store_sent_file_uuid_count_one_without_image_processor_ok() |
|||
{ |
|||
$collectionFields['withImage'] = true; |
|||
$collectionFields['count'] = 1; |
|||
$file = $this->one(File::class, dependencyAttributes: $collectionFields); |
|||
$collection = Collection::find($file->collection_id); |
|||
|
|||
|
|||
$data = [ |
|||
"file" => $file->uuid, |
|||
"public" => 1 |
|||
]; |
|||
|
|||
$response = $this->loginAs()->postJson(route('api.files.store', ['collection_name' => $collection->name, 'model_id' => 100]), $data); |
|||
$response->assertCreated(); |
|||
} |
|||
} |
@ -0,0 +1,47 @@ |
|||
<?php |
|||
|
|||
namespace Tests\Feature; |
|||
|
|||
use App\Image\ImageProcessor; |
|||
use App\Models\Collection; |
|||
use App\Models\File; |
|||
use Illuminate\Foundation\Testing\RefreshDatabase; |
|||
use Illuminate\Foundation\Testing\WithFaker; |
|||
use Illuminate\Support\Facades\Storage; |
|||
use Tests\Feature\Traits\FileTraits; |
|||
use Tests\TestCase; |
|||
|
|||
class FileUpdateTest extends TestCase |
|||
{ |
|||
use FileTraits; |
|||
|
|||
// public function test_user_can_not_access_update_without_permission()
|
|||
// {
|
|||
// $this->assertFalse("it's not mohammad's fault, I'm waiting for dynamic policy");
|
|||
// }
|
|||
|
|||
public function test_user_update_not_exits_files_not_found() |
|||
{ |
|||
$data = [ |
|||
"original_name" => 'original name is very bad', |
|||
"description" => 'this is a description for file and has beed updated', |
|||
]; |
|||
|
|||
$response = $this->loginAs()->putJson(route('api.files.update', ['collection_name' => 'not_exits', 'uuid' => app()->uuid, 'extention' => 'jpg']), $data); |
|||
$response->assertNotFound(); |
|||
} |
|||
|
|||
public function test_user_can_update_file() |
|||
{ |
|||
$file = $this->one(File::class, dependencyAttributes: ['withImage' => true]); |
|||
$collection = Collection::find($file->collection_id); |
|||
|
|||
$data = [ |
|||
"original_name" => 'original name is very bad', |
|||
"description" => 'this is a description for file and has beed updated', |
|||
]; |
|||
|
|||
$response = $this->loginAs()->putJson(route('api.files.update', ['collection_name' => $collection->name, 'uuid' => $file->uuid, 'extention' => $collection->ext]), $data); |
|||
$response->assertok()->assertJson(['data' => $data]); |
|||
} |
|||
} |
@ -0,0 +1,22 @@ |
|||
<?php |
|||
|
|||
namespace Tests\Feature\Traits; |
|||
|
|||
use App\Image\ImageProcessor; |
|||
use Illuminate\Support\Facades\Storage; |
|||
|
|||
trait FileImageTrait |
|||
{ |
|||
|
|||
public function createImageForModel(\App\Models\Collection $collection, \App\Models\File $file): void |
|||
{ |
|||
$imageProcessor = new ImageProcessor; |
|||
$imageProcessor->createFakeImage(storage_path('stub') . '/image.png', Storage::disk($collection->disk)->path($file->server_path . $file->uuid . '.' . $collection->ext)); |
|||
} |
|||
|
|||
public function deleteFakeImageForModel(\App\Models\File $file): void |
|||
{ |
|||
unlink($file->getPath()); |
|||
unlink($file->getModifiedPath()); |
|||
} |
|||
} |
@ -0,0 +1,52 @@ |
|||
<?php |
|||
|
|||
namespace Tests\Feature\Traits; |
|||
|
|||
use App\Models\Collection; |
|||
use App\Models\File; |
|||
use Illuminate\Support\Facades\Validator; |
|||
use Illuminate\Validation\Rule; |
|||
|
|||
trait FileShowTrait |
|||
{ |
|||
use FileImageTrait; |
|||
public function dimensions(\Illuminate\Http\File $image, $minWidth, $minHeight, $maxWidth, $maxHeight, $maxSize, $minSize) : bool |
|||
{ |
|||
$data = [ |
|||
'image' => $image |
|||
]; |
|||
$validator = Validator::make($data, [ |
|||
'avatar' => [ |
|||
Rule::dimensions()->minWidth($minWidth)->minHeight($minHeight)->maxWidth($maxWidth)->maxHeight($maxHeight), |
|||
'max:' . $maxSize, |
|||
'min:' . $minSize |
|||
], |
|||
]); |
|||
|
|||
return !$validator->fails(); |
|||
} |
|||
|
|||
public function createCollectionAndFileModelWithImage() |
|||
{ |
|||
$collection = Collection::factory()->createQuietly([ |
|||
'alt_required' => false, |
|||
'description_required' => false, |
|||
'tmp_support' => true, |
|||
'max_width' => 2000, |
|||
'max_height' => 2000, |
|||
'min_width' => 1, |
|||
'min_height' => 1, |
|||
'min_file_size' => 0 |
|||
]); |
|||
$uuid = app()->uuid; |
|||
$file = File::factory()->createQuietly([ |
|||
'uuid' => $uuid, |
|||
'server_path' => '/' . date('y') . '/' . date('m') . '/', |
|||
'user_id' => auth()->id(), |
|||
'collection_id' => $collection->id |
|||
]); |
|||
$this->createImageForModel($collection,$file); |
|||
|
|||
return ['file' => $file ,'collection' => $collection]; |
|||
} |
|||
} |
@ -0,0 +1,89 @@ |
|||
<?php |
|||
|
|||
namespace Tests\Feature\Traits; |
|||
|
|||
use Illuminate\Http\UploadedFile; |
|||
|
|||
trait FileTraits |
|||
{ |
|||
|
|||
private function defaultArray() |
|||
{ |
|||
return [ |
|||
[ |
|||
'alt_required' => false, |
|||
'description_required' => false, |
|||
'tmp_support' => true, |
|||
'max_width' => 2000, |
|||
'max_height' => 2000, |
|||
'min_width' => 1, |
|||
'min_height' => 1, |
|||
'min_file_size' => 0 |
|||
], |
|||
[ |
|||
"file" => UploadedFile::fake()->image('test.png', 400, 400), |
|||
"public" => 1 |
|||
] |
|||
]; |
|||
} |
|||
|
|||
private function defaultArrayForUpdate() |
|||
{ |
|||
return [ |
|||
[ |
|||
'alt_required' => false, |
|||
'description_required' => false, |
|||
'tmp_support' => true, |
|||
'max_width' => 2000, |
|||
'max_height' => 2000, |
|||
'min_width' => 1, |
|||
'min_height' => 1, |
|||
'min_file_size' => 0 |
|||
] |
|||
]; |
|||
} |
|||
|
|||
private function changeDefaultArrayAndReturn($defaultArray, $key, $value): array |
|||
{ |
|||
$defaultArray[$key[0]][$key[1]] = $value; |
|||
return $defaultArray; |
|||
} |
|||
|
|||
public function storeValidationTestProvider() |
|||
{ |
|||
|
|||
$data = [ |
|||
$this->changeDefaultArrayAndReturn($this->defaultArray(), [0, 'alt_required'], true), |
|||
$this->changeDefaultArrayAndReturn($this->defaultArray(), [0, 'description_required'], true), |
|||
$this->changeDefaultArrayAndReturn($this->defaultArray(), [0, 'max_width'], 2), |
|||
$this->changeDefaultArrayAndReturn($this->defaultArray(), [0, 'max_height'], 2), |
|||
$this->changeDefaultArrayAndReturn($this->defaultArray(), [0, 'min_width'], 1500), |
|||
$this->changeDefaultArrayAndReturn($this->defaultArray(), [0, 'min_height'], 1500), |
|||
$this->changeDefaultArrayAndReturn($this->defaultArray(), [0, 'min_file_size'], 1000), |
|||
$this->changeDefaultArrayAndReturn($this->defaultArray(), [0, 'max_file_size'], 0), |
|||
$this->changeDefaultArrayAndReturn($this->defaultArray(), [0, 'exts'], ['jpg']), |
|||
$this->changeDefaultArrayAndReturn($this->defaultArray(), [0, 'mimetypes'], ['image/jpg']), |
|||
]; |
|||
return $data; |
|||
} |
|||
|
|||
|
|||
public function storeValidationWithUuidTestProvider() |
|||
{ |
|||
|
|||
$data = [ |
|||
$this->changeDefaultArrayAndReturn($this->defaultArray(), [0, 'alt_required'], true), |
|||
]; |
|||
return $data; |
|||
} |
|||
|
|||
public function updateValidationTestProvider() |
|||
{ |
|||
|
|||
$data = [ |
|||
$this->changeDefaultArrayAndReturn($this->defaultArrayForUpdate(), [0, 'alt_required'], true), |
|||
$this->changeDefaultArrayAndReturn($this->defaultArrayForUpdate(), [0, 'description_required'], true), |
|||
]; |
|||
return $data; |
|||
} |
|||
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue