Compare commits

...

11 Commits

  1. 27
      app/Enums/FileFormatEnum.php
  2. 33
      app/Http/Controllers/V1/ConvertController.php
  3. 8
      app/Interfaces/ChunkerInterface.php
  4. 48
      app/Jobs/ChunkFile.php
  5. 76
      app/Jobs/StoreChunk.php
  6. 17
      app/Models/User.php
  7. 24
      app/Pipes/StoreChunk/AgeFilterPipe.php
  8. 20
      app/Pipes/StoreChunk/CreditCardFilter.php
  9. 42
      app/Providers/HorizonServiceProvider.php
  10. 14
      app/Providers/RouteServiceProvider.php
  11. 46
      app/Services/Chunkers/JsonChunker.php
  12. 1
      composer.json
  13. 60
      composer.lock
  14. 1
      config/app.php
  15. 197
      config/horizon.php
  16. 15
      database/factories/UserFactory.php
  17. 13
      database/migrations/2014_10_12_000000_create_users_table.php
  18. 32
      database/migrations/2014_10_12_100000_create_password_resets_table.php
  19. 37
      database/migrations/2019_12_14_000001_create_personal_access_tokens_table.php
  20. 2
      docker-compose.yml
  21. 3
      docker/database/mysql/create-testing-database.sh/testing-database.sql
  22. 19
      routes/api.php
  23. 10
      routes/api/v1/routes.php
  24. 40
      tests/Feature/Upload/ConvertJsonTest.php
  25. 98
      tests/Storage/ConvertJsonTest.json
  26. 2
      tests/TestCase.php
  27. 39
      tests/Unit/Jobs/ChunkFileTest.php
  28. 38
      tests/Unit/Jobs/StoreChunkTest.php

@ -0,0 +1,27 @@
<?php
namespace App\Enums;
use App\Services\Chunkers\JsonChunker;
use App\Services\Converters\JsonToDatabaseConverter;
enum FileFormatEnum: string
{
case JSON = 'json';
public function chunker(string $uuid, string $path): \Generator
{
$chunker = match ($this) {
self::JSON => new JsonChunker
};
return $chunker->handle($uuid, $path);
}
public function converter()
{
return match($this){
self::JSON => new JsonToDatabaseConverter
};
}
}

@ -0,0 +1,33 @@
<?php
namespace App\Http\Controllers\V1;
use App\Enums\FileFormatEnum;
use App\Http\Controllers\Controller;
use App\Jobs\ChunkFile;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Redis;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
class ConvertController extends Controller
{
public function convert(FileFormatEnum $format, Request $request)
{
Storage::put(
$request->file('file')->getClientOriginalName(),
$request->file('file')->getContent(),
);
ChunkFile::dispatch(
Str::uuid(),
$request->file('file')->getClientOriginalName(),
$format
);
return response([
'msg' => 'done',
'success' => true
]);
}
}

@ -0,0 +1,8 @@
<?php
namespace App\Interfaces;
interface ChunkerInterface
{
public function handle(string $uuid, string $path): \Generator;
}

@ -0,0 +1,48 @@
<?php
namespace App\Jobs;
use App\Enums\FileFormatEnum;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Redis;
use Illuminate\Support\Facades\Storage;
class ChunkFile implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct(
public string $uuid,
public string $path,
public FileFormatEnum $format
)
{
//
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
$chunks = $this->format->chunker($this->uuid, $this->path);
foreach ($chunks as $key => $chunk) {
StoreChunk::dispatch($chunk);
Redis::set($this->uuid . ":latest", $key);
}
Storage::delete($this->path);
Redis::del($this->uuid . ":latest");
}
}

@ -0,0 +1,76 @@
<?php
namespace App\Jobs;
use App\Models\User;
use App\Pipes\StoreChunk\AgeFilterPipe;
use App\Pipes\StoreChunk\CreditCardFilter;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Pipeline\Pipeline;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Carbon;
use Illuminate\Support\Collection;
class StoreChunk implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public Collection $chunk;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct(
array $chunk
)
{
$this->chunk = collect($chunk);
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
$chunk = (new Pipeline(app()))
->through([
AgeFilterPipe::class,
CreditCardFilter::class,
])
->send($this->changeDateOfBirthToCarbon())
->thenReturn();
User::insert(
$this->prepare($chunk)->toArray()
);
}
protected function changeDateOfBirthToCarbon()
{
return $this->chunk->map(function ($item) {
if (!strpos($item['date_of_birth'], '/')) {
$item['date_of_birth'] = Carbon::create($item['date_of_birth']);
} else {
$item['date_of_birth'] = Carbon::createFromFormat('d/m/Y', $item['date_of_birth']);
}
return $item;
});
}
protected function prepare($chunk)
{
return $chunk->map(function ($item) {
$item['credit_card'] = json_encode($item['credit_card']);
return $item;
});
}
}

@ -19,8 +19,13 @@ class User extends Authenticatable
*/
protected $fillable = [
'name',
'address',
'checked',
'email',
'password',
'description',
'interest',
'date_of_birth',
'credit_card',
];
/**
@ -28,10 +33,7 @@ class User extends Authenticatable
*
* @var array<int, string>
*/
protected $hidden = [
'password',
'remember_token',
];
protected $hidden = [];
/**
* The attributes that should be cast.
@ -39,6 +41,9 @@ class User extends Authenticatable
* @var array<string, string>
*/
protected $casts = [
'email_verified_at' => 'datetime',
'date_of_birth' => 'datetime',
'credit_card' => 'json',
];
public $timestamps = false;
}

@ -0,0 +1,24 @@
<?php
namespace App\Pipes\StoreChunk;
use Illuminate\Support\Carbon;
use Illuminate\Support\Collection;
class AgeFilterPipe
{
/**
* @param Collection $chunk
* @param \Closure $next
* @return mixed
*/
public function handle($chunk, \Closure $next)
{
$chunk = $chunk->filter(function ($item) {
$age = $item['date_of_birth']->age;
return 18 <= $age && $age <= 65;
});
return $next($chunk);
}
}

@ -0,0 +1,20 @@
<?php
namespace App\Pipes\StoreChunk;
use Illuminate\Support\Collection;
class CreditCardFilter
{
/**
* @param Collection $chunk
* @param \Closure $next
* @return mixed
*/
public function handle($chunk, \Closure $next)
{
// filter based on credit card number
return $next($chunk);
}
}

@ -0,0 +1,42 @@
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Gate;
use Laravel\Horizon\Horizon;
use Laravel\Horizon\HorizonApplicationServiceProvider;
class HorizonServiceProvider extends HorizonApplicationServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
parent::boot();
// Horizon::routeSmsNotificationsTo('15556667777');
// Horizon::routeMailNotificationsTo('example@example.com');
// Horizon::routeSlackNotificationsTo('slack-webhook-url', '#channel');
// Horizon::night();
}
/**
* Register the Horizon gate.
*
* This gate determines who can access Horizon in non-local environments.
*
* @return void
*/
protected function gate()
{
Gate::define('viewHorizon', function ($user) {
return in_array($user->email, [
//
]);
});
}
}

@ -31,7 +31,7 @@ class RouteServiceProvider extends ServiceProvider
$this->routes(function () {
Route::middleware('api')
->prefix('api')
->group(base_path('routes/api.php'));
->group($this->apiRoutes());
Route::middleware('web')
->group(base_path('routes/web.php'));
@ -49,4 +49,16 @@ class RouteServiceProvider extends ServiceProvider
return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
});
}
/**
* Api version handler
*
* @return \Closure
*/
protected function apiRoutes()
{
return function(){
Route::prefix('v1')->group(base_path('routes/api/v1/routes.php'));
};
}
}

@ -0,0 +1,46 @@
<?php
namespace App\Services\Chunkers;
use App\Interfaces\ChunkerInterface;
use App\Services\Decoder\JsonDecoder;
use Illuminate\Support\Facades\Redis;
use Illuminate\Support\Facades\Storage;
use JsonMachine\Exception\InvalidArgumentException;
use JsonMachine\Items;
use JsonMachine\JsonDecoder\ExtJsonDecoder;
class JsonChunker implements ChunkerInterface
{
/**
* @throws InvalidArgumentException
*/
public function handle(string $uuid, string $path): \Generator
{
$chunk = [];
$latest = Redis::get($uuid . ":latest") ?? 0;
foreach (Items::fromFile(Storage::path($path), ['decoder' => new ExtJsonDecoder(true)]) as $key => $value) {
if ($key < $latest) {
continue;
}
// for cut off test you have to uncomment below codes
// if($key == 900){
// throw new \Exception('cut off');
// }
$chunk[] = $value;
if (count($chunk) == 500) {
yield $key => $chunk;
$chunk = [];
}
}
if (count($chunk) > 0) {
yield $key => $chunk;
}
}
}

@ -7,6 +7,7 @@
"require": {
"php": "^8.0.2",
"guzzlehttp/guzzle": "^7.2",
"halaxa/json-machine": "^1.1",
"laravel/framework": "^9.19",
"laravel/horizon": "^5.10",
"laravel/sanctum": "^3.0",

60
composer.lock generated

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "9dcd1c31751916ad055da24bc3ed4c71",
"content-hash": "7b1ad9d92186f7979d5ea9f3a6a9efa6",
"packages": [
{
"name": "brick/math",
@ -897,6 +897,64 @@
],
"time": "2022-10-26T14:07:24+00:00"
},
{
"name": "halaxa/json-machine",
"version": "1.1.3",
"source": {
"type": "git",
"url": "https://github.com/halaxa/json-machine.git",
"reference": "514025c5ebbdb8a058745b573b4a1e81d685802c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/halaxa/json-machine/zipball/514025c5ebbdb8a058745b573b4a1e81d685802c",
"reference": "514025c5ebbdb8a058745b573b4a1e81d685802c",
"shasum": ""
},
"require": {
"php": ">=7.0"
},
"require-dev": {
"ext-json": "*",
"friendsofphp/php-cs-fixer": "^3.0",
"phpunit/phpunit": "^8.0"
},
"suggest": {
"ext-json": "To run JSON Machine out of the box without custom decoders.",
"guzzlehttp/guzzle": "To run example with GuzzleHttp"
},
"type": "library",
"autoload": {
"psr-4": {
"JsonMachine\\": "src/"
},
"exclude-from-classmap": [
"src/autoloader.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Apache-2.0"
],
"authors": [
{
"name": "Filip Halaxa",
"email": "filip@halaxa.cz"
}
],
"description": "Efficient, easy-to-use and fast JSON pull parser",
"support": {
"issues": "https://github.com/halaxa/json-machine/issues",
"source": "https://github.com/halaxa/json-machine/tree/1.1.3"
},
"funding": [
{
"url": "https://ko-fi.com/G2G57KTE4",
"type": "other"
}
],
"time": "2022-10-12T11:40:33+00:00"
},
{
"name": "laravel/framework",
"version": "v9.40.1",

@ -193,6 +193,7 @@ return [
App\Providers\AuthServiceProvider::class,
// App\Providers\BroadcastServiceProvider::class,
App\Providers\EventServiceProvider::class,
App\Providers\HorizonServiceProvider::class,
App\Providers\RouteServiceProvider::class,
],

@ -0,0 +1,197 @@
<?php
use Illuminate\Support\Str;
return [
/*
|--------------------------------------------------------------------------
| Horizon Domain
|--------------------------------------------------------------------------
|
| This is the subdomain where Horizon will be accessible from. If this
| setting is null, Horizon will reside under the same domain as the
| application. Otherwise, this value will serve as the subdomain.
|
*/
'domain' => env('HORIZON_DOMAIN'),
/*
|--------------------------------------------------------------------------
| Horizon Path
|--------------------------------------------------------------------------
|
| This is the URI path where Horizon will be accessible from. Feel free
| to change this path to anything you like. Note that the URI will not
| affect the paths of its internal API that aren't exposed to users.
|
*/
'path' => env('HORIZON_PATH', 'horizon'),
/*
|--------------------------------------------------------------------------
| Horizon Redis Connection
|--------------------------------------------------------------------------
|
| This is the name of the Redis connection where Horizon will store the
| meta information required for it to function. It includes the list
| of supervisors, failed jobs, job metrics, and other information.
|
*/
'use' => 'default',
/*
|--------------------------------------------------------------------------
| Horizon Redis Prefix
|--------------------------------------------------------------------------
|
| This prefix will be used when storing all Horizon data in Redis. You
| may modify the prefix when you are running multiple installations
| of Horizon on the same server so that they don't have problems.
|
*/
'prefix' => env(
'HORIZON_PREFIX',
Str::slug(env('APP_NAME', 'laravel'), '_').'_horizon:'
),
/*
|--------------------------------------------------------------------------
| Horizon Route Middleware
|--------------------------------------------------------------------------
|
| These middleware will get attached onto each Horizon route, giving you
| the chance to add your own middleware to this list or change any of
| the existing middleware. Or, you can simply stick with this list.
|
*/
'middleware' => ['web'],
/*
|--------------------------------------------------------------------------
| Queue Wait Time Thresholds
|--------------------------------------------------------------------------
|
| This option allows you to configure when the LongWaitDetected event
| will be fired. Every connection / queue combination may have its
| own, unique threshold (in seconds) before this event is fired.
|
*/
'waits' => [
'redis:default' => 60,
],
/*
|--------------------------------------------------------------------------
| Job Trimming Times
|--------------------------------------------------------------------------
|
| Here you can configure for how long (in minutes) you desire Horizon to
| persist the recent and failed jobs. Typically, recent jobs are kept
| for one hour while all failed jobs are stored for an entire week.
|
*/
'trim' => [
'recent' => 60,
'pending' => 60,
'completed' => 60,
'recent_failed' => 10080,
'failed' => 10080,
'monitored' => 10080,
],
/*
|--------------------------------------------------------------------------
| Metrics
|--------------------------------------------------------------------------
|
| Here you can configure how many snapshots should be kept to display in
| the metrics graph. This will get used in combination with Horizon's
| `horizon:snapshot` schedule to define how long to retain metrics.
|
*/
'metrics' => [
'trim_snapshots' => [
'job' => 24,
'queue' => 24,
],
],
/*
|--------------------------------------------------------------------------
| Fast Termination
|--------------------------------------------------------------------------
|
| When this option is enabled, Horizon's "terminate" command will not
| wait on all of the workers to terminate unless the --wait option
| is provided. Fast termination can shorten deployment delay by
| allowing a new instance of Horizon to start while the last
| instance will continue to terminate each of its workers.
|
*/
'fast_termination' => false,
/*
|--------------------------------------------------------------------------
| Memory Limit (MB)
|--------------------------------------------------------------------------
|
| This value describes the maximum amount of memory the Horizon master
| supervisor may consume before it is terminated and restarted. For
| configuring these limits on your workers, see the next section.
|
*/
'memory_limit' => 64,
/*
|--------------------------------------------------------------------------
| Queue Worker Configuration
|--------------------------------------------------------------------------
|
| Here you may define the queue worker settings used by your application
| in all environments. These supervisors and settings handle all your
| queued jobs and will be provisioned by Horizon during deployment.
|
*/
'defaults' => [
'supervisor-1' => [
'connection' => 'redis',
'queue' => ['default'],
'balance' => 'auto',
'maxProcesses' => 1,
'maxTime' => 0,
'maxJobs' => 0,
'memory' => 128,
'tries' => 1,
'timeout' => 60,
'nice' => 0,
],
],
'environments' => [
'production' => [
'supervisor-1' => [
'maxProcesses' => 10,
'balanceMaxShift' => 1,
'balanceCooldown' => 3,
],
],
'local' => [
'supervisor-1' => [
'maxProcesses' => 3,
],
],
],
];

@ -19,10 +19,19 @@ class UserFactory extends Factory
{
return [
'name' => fake()->name(),
'account' => fake()->numberBetween(10000,99999),
'address' => fake()->address(),
'checked' => fake()->boolean(),
'email' => fake()->unique()->safeEmail(),
'email_verified_at' => now(),
'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
'remember_token' => Str::random(10),
'description' => fake()->realText(),
'interest' => fake()->jobTitle(),
'date_of_birth' => fake()->dateTimeBetween('-70 years', 'now'),
'credit_card' => json_encode([
"type" => fake()->creditCardType(),
"number" => fake()->creditCardNumber(),
"name" => fake()->name(),
"expirationDate" => fake()->creditCardExpirationDateString(),
]),
];
}

@ -15,12 +15,15 @@ return new class extends Migration
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->foreignId('account');
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->rememberToken();
$table->timestamps();
$table->text('address');
$table->boolean('checked');
$table->string('email');
$table->text('description')->nullable();
$table->text('interest')->nullable();
$table->dateTimeTz('date_of_birth');
$table->json('credit_card');
});
}

@ -1,32 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('password_resets', function (Blueprint $table) {
$table->string('email')->index();
$table->string('token');
$table->timestamp('created_at')->nullable();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('password_resets');
}
};

@ -1,37 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('personal_access_tokens', function (Blueprint $table) {
$table->id();
$table->morphs('tokenable');
$table->string('name');
$table->string('token', 64)->unique();
$table->text('abilities')->nullable();
$table->timestamp('last_used_at')->nullable();
$table->timestamp('expires_at')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('personal_access_tokens');
}
};

@ -38,7 +38,7 @@ services:
MYSQL_ALLOW_EMPTY_PASSWORD: 'yes'
volumes:
- 'sail-mariadb:/var/lib/mysql'
- './docker/database/mysql/create-testing-database.sh:/docker-entrypoint-initdb.d/10-create-testing-database.sh'
- './docker/database/mysql/create-testing-database.sh:/docker-entrypoint-initdb.d'
networks:
- sail
healthcheck:

@ -0,0 +1,3 @@
create database testing;
GRANT ALL ON `testing`.* TO 'sail'@'%';
FLUSH PRIVILEGES;

@ -1,19 +0,0 @@
<?php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/
Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
return $request->user();
});

@ -0,0 +1,10 @@
<?php
use Illuminate\Support\Facades\Route;
Route::prefix('convert')->group(function () {
Route::post('{format}', [
\App\Http\Controllers\V1\ConvertController::class,
'convert'
]);
});

@ -0,0 +1,40 @@
<?php
namespace Tests\Feature\Upload;
use App\Jobs\ChunkFile;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Http\Testing\File;
use Illuminate\Support\Facades\Bus;
use Illuminate\Support\Facades\Storage;
use Tests\TestCase;
class ConvertJsonTest extends TestCase
{
/**
* upload json file.
*
* @return void
* @group convert
*/
public function test_example()
{
$content = file_get_contents(base_path('/tests/Storage/ConvertJsonTest.json'));
$file = File::createWithContent(
'test.json',
$content
);
Bus::fake();
Storage::fake();
$this->post('/api/v1/convert/json', [
'file' => $file
])
->assertSuccessful();
Storage::assertExists($file->name);
Bus::assertDispatched(ChunkFile::class, 1);
}
}

@ -0,0 +1,98 @@
[
{
"name": "Mrs. Daphney Borer1",
"address": "57465 Kelton Trace Suite 676 49508-4359 Lake Iliana",
"checked": false,
"description": "Quae quia at quae aut quaerat dolore consectetur. Quaerat saepe perferendis incidunt sapiente optio reiciendis. Rerum inventore et adipisci dolores possimus est voluptas qui.<br>Quis harum reiciendis ipsum. Aliquam quam commodi repellat occaecati. Aut et quam vel cumque eaque est.",
"interest": "leverage cross-media communities",
"date_of_birth": "1974-06-30T15:41:42+00:00",
"email": "flatley.laurie@damore.com",
"account": "2454926431051",
"credit_card": {
"type": "Visa",
"number": "4532623545165",
"name": "Grace Kilback",
"expirationDate": "02\/18"
}
},
{
"name": "Mrs. Daphney Borer2",
"address": "57465 Kelton Trace Suite 676 49508-4359 Lake Iliana",
"checked": false,
"description": "Quae quia at quae aut quaerat dolore consectetur. Quaerat saepe perferendis incidunt sapiente optio reiciendis. Rerum inventore et adipisci dolores possimus est voluptas qui.<br>Quis harum reiciendis ipsum. Aliquam quam commodi repellat occaecati. Aut et quam vel cumque eaque est.",
"interest": "leverage cross-media communities",
"date_of_birth": "1974-06-30T15:41:42+00:00",
"email": "flatley.laurie@damore.com",
"account": "2454926431051",
"credit_card": {
"type": "Visa",
"number": "4532623545165",
"name": "Grace Kilback",
"expirationDate": "02\/18"
}
},
{
"name": "Mrs. Daphney Borer3",
"address": "57465 Kelton Trace Suite 676 49508-4359 Lake Iliana",
"checked": false,
"description": "Quae quia at quae aut quaerat dolore consectetur. Quaerat saepe perferendis incidunt sapiente optio reiciendis. Rerum inventore et adipisci dolores possimus est voluptas qui.<br>Quis harum reiciendis ipsum. Aliquam quam commodi repellat occaecati. Aut et quam vel cumque eaque est.",
"interest": "leverage cross-media communities",
"date_of_birth": "1974-06-30T15:41:42+00:00",
"email": "flatley.laurie@damore.com",
"account": "2454926431051",
"credit_card": {
"type": "Visa",
"number": "4532623545165",
"name": "Grace Kilback",
"expirationDate": "02\/18"
}
},
{
"name": "Mrs. Daphney Borer4",
"address": "57465 Kelton Trace Suite 676 49508-4359 Lake Iliana",
"checked": false,
"description": "Quae quia at quae aut quaerat dolore consectetur. Quaerat saepe perferendis incidunt sapiente optio reiciendis. Rerum inventore et adipisci dolores possimus est voluptas qui.<br>Quis harum reiciendis ipsum. Aliquam quam commodi repellat occaecati. Aut et quam vel cumque eaque est.",
"interest": "leverage cross-media communities",
"date_of_birth": "1974-06-30T15:41:42+00:00",
"email": "flatley.laurie@damore.com",
"account": "2454926431051",
"credit_card": {
"type": "Visa",
"number": "4532623545165",
"name": "Grace Kilback",
"expirationDate": "02\/18"
}
},
{
"name": "Mrs. Daphney Borer5",
"address": "57465 Kelton Trace Suite 676 49508-4359 Lake Iliana",
"checked": false,
"description": "Quae quia at quae aut quaerat dolore consectetur. Quaerat saepe perferendis incidunt sapiente optio reiciendis. Rerum inventore et adipisci dolores possimus est voluptas qui.<br>Quis harum reiciendis ipsum. Aliquam quam commodi repellat occaecati. Aut et quam vel cumque eaque est.",
"interest": "leverage cross-media communities",
"date_of_birth": "1974-06-30T15:41:42+00:00",
"email": "flatley.laurie@damore.com",
"account": "2454926431051",
"credit_card": {
"type": "Visa",
"number": "4532623545165",
"name": "Grace Kilback",
"expirationDate": "02\/18"
}
},
{
"name": "Mrs. Daphney Borer6",
"address": "57465 Kelton Trace Suite 676 49508-4359 Lake Iliana",
"checked": false,
"description": "Quae quia at quae aut quaerat dolore consectetur. Quaerat saepe perferendis incidunt sapiente optio reiciendis. Rerum inventore et adipisci dolores possimus est voluptas qui.<br>Quis harum reiciendis ipsum. Aliquam quam commodi repellat occaecati. Aut et quam vel cumque eaque est.",
"interest": "leverage cross-media communities",
"date_of_birth": "1974-06-30T15:41:42+00:00",
"email": "flatley.laurie@damore.com",
"account": "2454926431051",
"credit_card": {
"type": "Visa",
"number": "4532623545165",
"name": "Grace Kilback",
"expirationDate": "02\/18"
}
}
]

@ -2,9 +2,11 @@
namespace Tests;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
abstract class TestCase extends BaseTestCase
{
use CreatesApplication;
use RefreshDatabase;
}

@ -0,0 +1,39 @@
<?php
namespace Tests\Unit\Jobs;
use App\Enums\FileFormatEnum;
use App\Jobs\ChunkFile;
use App\Jobs\StoreChunk;
use Illuminate\Support\Facades\Queue;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Tests\TestCase;
use Illuminate\Support\Facades\Bus;
class ChunkFileTest extends TestCase
{
/**
* A basic unit test example.
*
* @return void
* @group convert
*/
public function test_chunk_json_file()
{
Bus::fake();
$path = base_path('/tests/Storage/ConvertJsonTest.json');
Storage::shouldReceive('path')->andReturn($path);
Storage::shouldReceive('delete')->andReturnTrue();
(new ChunkFile(
Str::uuid(),
$path,
FileFormatEnum::JSON
))->handle();
Bus::assertDispatched(StoreChunk::class, 1);
}
}

@ -0,0 +1,38 @@
<?php
namespace Tests\Unit\Jobs;
use App\Jobs\StoreChunk;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Sequence;
use Tests\TestCase;
class StoreChunkTest extends TestCase
{
/**
* A basic unit test example.
*
* @return void
* @group convert2
*/
public function test_store_chunk()
{
(new StoreChunk(
User::factory()->count(99)->state(
new Sequence(
[
'date_of_birth' => fake()->dateTimeBetween('-70 years', '-66 years'),
],
[
'date_of_birth' => fake()->dateTimeBetween('-65 years', '-18 years'),
],
[
'date_of_birth' => fake()->dateTimeBetween('-18 years', 'now'),
]
)
)->make()->toArray()
))->handle();
$this->assertDatabaseCount('users', 33);
}
}
Loading…
Cancel
Save