first commit
This commit is contained in:
19
laravel/tests/Feature/ExampleTest.php
Normal file
19
laravel/tests/Feature/ExampleTest.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
// use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class ExampleTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* A basic test example.
|
||||
*/
|
||||
public function test_the_application_returns_a_successful_response(): void
|
||||
{
|
||||
$response = $this->get('/');
|
||||
|
||||
$response->assertStatus(200);
|
||||
}
|
||||
}
|
||||
122
laravel/tests/Feature/TaskControllerTest.php
Normal file
122
laravel/tests/Feature/TaskControllerTest.php
Normal file
@@ -0,0 +1,122 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Models\Tag;
|
||||
use App\Models\Task;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class TaskControllerTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_index_returns_tasks(): void
|
||||
{
|
||||
Task::factory(3)->create();
|
||||
|
||||
$response = $this->getJson(route('tasks.index'));
|
||||
|
||||
$response->assertOk()
|
||||
->assertJsonCount(3, 'data');
|
||||
}
|
||||
|
||||
public function test_store_creates_task(): void
|
||||
{
|
||||
$payload = [
|
||||
'title' => 'Новая задача',
|
||||
'is_done' => false,
|
||||
'due_at' => now()->addDay()->toIso8601ZuluString(),
|
||||
'tags' => ['home', 'shopping'],
|
||||
];
|
||||
|
||||
$response = $this->postJson(route('tasks.store'), $payload);
|
||||
|
||||
$response->assertCreated()
|
||||
->assertJsonFragment([
|
||||
'title' => 'Новая задача',
|
||||
'is_done' => false,
|
||||
])
|
||||
->assertJsonCount(2, 'data.tags');
|
||||
|
||||
$this->assertDatabaseHas('tasks', [
|
||||
'title' => 'Новая задача',
|
||||
]);
|
||||
|
||||
$this->assertDatabaseHas('tags', ['name' => 'home']);
|
||||
$this->assertDatabaseHas('tags', ['name' => 'shopping']);
|
||||
}
|
||||
|
||||
public function test_show_returns_task(): void
|
||||
{
|
||||
$task = Task::factory()->create();
|
||||
|
||||
$response = $this->getJson(route('tasks.show', $task));
|
||||
|
||||
$response->assertOk()
|
||||
->assertJsonPath('data.id', $task->id);
|
||||
}
|
||||
|
||||
public function test_update_modifies_task(): void
|
||||
{
|
||||
$task = Task::factory()->create();
|
||||
|
||||
$payload = ['title' => 'Обновлённая задача', 'is_done' => true];
|
||||
|
||||
$response = $this->putJson(route('tasks.update', $task), $payload);
|
||||
|
||||
$response->assertOk()
|
||||
->assertJsonFragment([
|
||||
'title' => 'Обновлённая задача',
|
||||
'is_done' => true,
|
||||
]);
|
||||
|
||||
$this->assertDatabaseHas('tasks', [
|
||||
'id' => $task->id,
|
||||
'title' => 'Обновлённая задача',
|
||||
'is_done' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_destroy_deletes_task(): void
|
||||
{
|
||||
$task = Task::factory()->create();
|
||||
|
||||
$response = $this->deleteJson(route('tasks.destroy', $task));
|
||||
|
||||
$response->assertNoContent();
|
||||
|
||||
$this->assertDatabaseMissing('tasks', [
|
||||
'id' => $task->id,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_can_filter_tasks_by_tag_and_is_done(): void
|
||||
{
|
||||
$tagWork = Tag::create(['name' => 'work']);
|
||||
$tagHome = Tag::create(['name' => 'home']);
|
||||
|
||||
$task1 = Task::factory()->create(['is_done' => false]);
|
||||
$task2 = Task::factory()->create(['is_done' => true]);
|
||||
$task3 = Task::factory()->create(['is_done' => false]);
|
||||
|
||||
$task1->tags()->attach($tagWork->id);
|
||||
$task2->tags()->attach($tagWork->id);
|
||||
$task3->tags()->attach($tagHome->id);
|
||||
|
||||
$response = $this->getJson(route('tasks.index', ['tag' => 'work']));
|
||||
$response->assertOk()
|
||||
->assertJsonCount(2, 'data');
|
||||
|
||||
$response = $this->getJson(route('tasks.index', ['is_done' => 0]));
|
||||
$response->assertOk()
|
||||
->assertJsonCount(2, 'data');
|
||||
|
||||
$response = $this->getJson(route('tasks.index', ['is_done' => 1, 'tag' => 'work']));
|
||||
$response->assertOk()
|
||||
->assertJsonCount(1, 'data')
|
||||
->assertJsonPath('data.0.id', $task2->id);
|
||||
}
|
||||
}
|
||||
10
laravel/tests/TestCase.php
Normal file
10
laravel/tests/TestCase.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Tests;
|
||||
|
||||
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
|
||||
|
||||
abstract class TestCase extends BaseTestCase
|
||||
{
|
||||
//
|
||||
}
|
||||
16
laravel/tests/Unit/ExampleTest.php
Normal file
16
laravel/tests/Unit/ExampleTest.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Unit;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class ExampleTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* A basic test example.
|
||||
*/
|
||||
public function test_that_true_is_true(): void
|
||||
{
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
}
|
||||
121
laravel/tests/Unit/QueryFilterPipelineTest.php
Normal file
121
laravel/tests/Unit/QueryFilterPipelineTest.php
Normal file
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Unit;
|
||||
|
||||
use App\Contracts\FilterFactoryInterface;
|
||||
use App\Contracts\FilterInterface;
|
||||
use App\Contracts\RequestFilterInterface;
|
||||
use App\Pipelines\QueryFilterPipeline;
|
||||
use Illuminate\Contracts\Database\Eloquent\Builder;
|
||||
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Contracts\Pipeline\Pipeline as PipelineContract;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use InvalidArgumentException;
|
||||
use Mockery;
|
||||
use Tests\TestCase;
|
||||
use Throwable;
|
||||
|
||||
final class QueryFilterPipelineTest extends TestCase
|
||||
{
|
||||
protected function tearDown(): void
|
||||
{
|
||||
Mockery::close();
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function testApplyFiltersWithValidPerPage(): void
|
||||
{
|
||||
$builder = Mockery::mock(Builder::class);
|
||||
$pipeline = Mockery::mock(PipelineContract::class);
|
||||
$factory = Mockery::mock(FilterFactoryInterface::class);
|
||||
$filterRequest = Mockery::mock(RequestFilterInterface::class);
|
||||
$paginator = Mockery::mock(LengthAwarePaginator::class);
|
||||
|
||||
Config::shouldReceive('get')->with('filters.pagination.per_page', 15)->andReturn(15);
|
||||
Config::shouldReceive('get')->with('filters.pagination.max_per_page', 50)->andReturn(50);
|
||||
|
||||
Log::spy();
|
||||
|
||||
$values = ['is_done' => true];
|
||||
$filter = Mockery::mock(FilterInterface::class);
|
||||
|
||||
$filterRequest->shouldReceive('values')->once()->andReturn($values);
|
||||
$filterRequest->shouldReceive('filters')->once()->andReturn(array_keys($values));
|
||||
$filterRequest->shouldReceive('perPage')->once()->andReturn(10);
|
||||
|
||||
$factory->shouldReceive('make')->with('is_done', $values)->andReturn($filter);
|
||||
|
||||
$pipeline->shouldReceive('send')->with($builder)->andReturnSelf();
|
||||
$pipeline->shouldReceive('through')->with([$filter])->andReturnSelf();
|
||||
$pipeline->shouldReceive('thenReturn')->andReturn($builder);
|
||||
|
||||
$builder->shouldReceive('paginate')->with(10)->andReturn($paginator);
|
||||
|
||||
$service = new QueryFilterPipeline($pipeline, $factory);
|
||||
|
||||
$result = $service->applyFilters($builder, $filterRequest);
|
||||
|
||||
$this->assertSame($paginator, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function testApplyFiltersWithPerPageExceedingMaxThrowsException(): void
|
||||
{
|
||||
$builder = Mockery::mock(Builder::class);
|
||||
$pipeline = Mockery::mock(PipelineContract::class);
|
||||
$factory = Mockery::mock(FilterFactoryInterface::class);
|
||||
$filterRequest = Mockery::mock(RequestFilterInterface::class);
|
||||
|
||||
Config::shouldReceive('get')->with('filters.pagination.per_page', 15)->andReturn(15);
|
||||
Config::shouldReceive('get')->with('filters.pagination.max_per_page', 50)->andReturn(50);
|
||||
|
||||
$filterRequest->shouldReceive('perPage')->once()->andReturn(100);
|
||||
|
||||
$service = new QueryFilterPipeline($pipeline, $factory);
|
||||
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('per_page cannot exceed 50');
|
||||
|
||||
$service->applyFilters($builder, $filterRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function testApplyFiltersWithNoFilters(): void
|
||||
{
|
||||
$builder = Mockery::mock(Builder::class);
|
||||
$pipeline = Mockery::mock(PipelineContract::class);
|
||||
$factory = Mockery::mock(FilterFactoryInterface::class);
|
||||
$filterRequest = Mockery::mock(RequestFilterInterface::class);
|
||||
$paginator = Mockery::mock(LengthAwarePaginator::class);
|
||||
|
||||
Config::shouldReceive('get')->with('filters.pagination.per_page', 15)->andReturn(15);
|
||||
Config::shouldReceive('get')->with('filters.pagination.max_per_page', 50)->andReturn(50);
|
||||
Log::spy();
|
||||
|
||||
$filterRequest->shouldReceive('values')->once()->andReturn([]);
|
||||
$filterRequest->shouldReceive('filters')->once()->andReturn([]);
|
||||
$filterRequest->shouldReceive('perPage')->once()->andReturn(null);
|
||||
|
||||
$pipeline->shouldReceive('send')->with($builder)->andReturnSelf();
|
||||
$pipeline->shouldReceive('through')->with([])->andReturnSelf();
|
||||
$pipeline->shouldReceive('thenReturn')->andReturn($builder);
|
||||
|
||||
$builder->shouldReceive('paginate')->with(15)->andReturn($paginator);
|
||||
|
||||
$service = new QueryFilterPipeline($pipeline, $factory);
|
||||
|
||||
$result = $service->applyFilters($builder, $filterRequest);
|
||||
|
||||
$this->assertSame($paginator, $result);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user