ValidationFailure tests
This commit is contained in:
parent
709201547c
commit
f1d46dacb6
7 changed files with 264 additions and 5 deletions
|
|
@ -11,7 +11,8 @@
|
||||||
"pestphp/pest": "^4.4",
|
"pestphp/pest": "^4.4",
|
||||||
"phpstan/phpstan": "^2.1",
|
"phpstan/phpstan": "^2.1",
|
||||||
"friendsofphp/php-cs-fixer": "^3.94",
|
"friendsofphp/php-cs-fixer": "^3.94",
|
||||||
"orchestra/testbench": "^9.16"
|
"orchestra/testbench": "^9.16",
|
||||||
|
"pestphp/pest-plugin-laravel": "^4.0"
|
||||||
},
|
},
|
||||||
"license": "GPL-2.0-only",
|
"license": "GPL-2.0-only",
|
||||||
"autoload": {
|
"autoload": {
|
||||||
|
|
|
||||||
76
composer.lock
generated
76
composer.lock
generated
|
|
@ -4,7 +4,7 @@
|
||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "1d9eef5574135e39ab7eaa6beae3fdad",
|
"content-hash": "336ed1e898bc39b0e9990becc327415c",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "brick/math",
|
"name": "brick/math",
|
||||||
|
|
@ -7992,6 +7992,80 @@
|
||||||
],
|
],
|
||||||
"time": "2025-08-20T13:10:51+00:00"
|
"time": "2025-08-20T13:10:51+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "pestphp/pest-plugin-laravel",
|
||||||
|
"version": "v4.0.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/pestphp/pest-plugin-laravel.git",
|
||||||
|
"reference": "e12a07046b826a40b1c8632fd7b80d6b8d7b628e"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/pestphp/pest-plugin-laravel/zipball/e12a07046b826a40b1c8632fd7b80d6b8d7b628e",
|
||||||
|
"reference": "e12a07046b826a40b1c8632fd7b80d6b8d7b628e",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"laravel/framework": "^11.45.2|^12.25.0",
|
||||||
|
"pestphp/pest": "^4.0.0",
|
||||||
|
"php": "^8.3.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"laravel/dusk": "^8.3.3",
|
||||||
|
"orchestra/testbench": "^9.13.0|^10.5.0",
|
||||||
|
"pestphp/pest-dev-tools": "^4.0.0"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"pest": {
|
||||||
|
"plugins": [
|
||||||
|
"Pest\\Laravel\\Plugin"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"laravel": {
|
||||||
|
"providers": [
|
||||||
|
"Pest\\Laravel\\PestServiceProvider"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"files": [
|
||||||
|
"src/Autoload.php"
|
||||||
|
],
|
||||||
|
"psr-4": {
|
||||||
|
"Pest\\Laravel\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"description": "The Pest Laravel Plugin",
|
||||||
|
"keywords": [
|
||||||
|
"framework",
|
||||||
|
"laravel",
|
||||||
|
"pest",
|
||||||
|
"php",
|
||||||
|
"test",
|
||||||
|
"testing",
|
||||||
|
"unit"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"source": "https://github.com/pestphp/pest-plugin-laravel/tree/v4.0.0"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://www.paypal.com/paypalme/enunomaduro",
|
||||||
|
"type": "custom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/nunomaduro",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2025-08-20T12:46:37+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "pestphp/pest-plugin-mutate",
|
"name": "pestphp/pest-plugin-mutate",
|
||||||
"version": "v4.0.1",
|
"version": "v4.0.1",
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ trait DataObject
|
||||||
/**
|
/**
|
||||||
* @param array<string,mixed> $input
|
* @param array<string,mixed> $input
|
||||||
*/
|
*/
|
||||||
public static function fromArray(array $input): static
|
public static function fromArray(array $input): ?static
|
||||||
{
|
{
|
||||||
$parameters = RuleFactory::getParametersMeta(static::class);
|
$parameters = RuleFactory::getParametersMeta(static::class);
|
||||||
foreach ($parameters as $parameter) {
|
foreach ($parameters as $parameter) {
|
||||||
|
|
@ -72,8 +72,7 @@ trait DataObject
|
||||||
$validator = static::withValidator($input, $rules);
|
$validator = static::withValidator($input, $rules);
|
||||||
|
|
||||||
if ($validator->fails()) {
|
if ($validator->fails()) {
|
||||||
$exception = new ValidationException($validator);
|
return static::fails($validator);
|
||||||
throw $exception;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$mappedInput = [];
|
$mappedInput = [];
|
||||||
|
|
@ -103,6 +102,11 @@ trait DataObject
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function fails(Validator $validator): ?static
|
||||||
|
{
|
||||||
|
throw new ValidationException($validator);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array<string,array<int, string|Rule>>
|
* @return array<string,array<int, string|Rule>>
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
23
tests/Classes/FailsReturnsDefault.php
Normal file
23
tests/Classes/FailsReturnsDefault.php
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Tests\Classes;
|
||||||
|
|
||||||
|
use Icefox\DTO\DataObject;
|
||||||
|
use Illuminate\Validation\Validator;
|
||||||
|
|
||||||
|
readonly class FailsReturnsDefault
|
||||||
|
{
|
||||||
|
use DataObject;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
public string $string,
|
||||||
|
public int $int = 42,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public static function fails(Validator $validator): ?static
|
||||||
|
{
|
||||||
|
return new self(string: 'default_value');
|
||||||
|
}
|
||||||
|
}
|
||||||
23
tests/Classes/FailsReturnsNull.php
Normal file
23
tests/Classes/FailsReturnsNull.php
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Tests\Classes;
|
||||||
|
|
||||||
|
use Icefox\DTO\DataObject;
|
||||||
|
use Illuminate\Validation\Validator;
|
||||||
|
|
||||||
|
readonly class FailsReturnsNull
|
||||||
|
{
|
||||||
|
use DataObject;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
public string $string,
|
||||||
|
public int $int,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public static function fails(Validator $validator): ?static
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
26
tests/Classes/FailsWithHttpResponse.php
Normal file
26
tests/Classes/FailsWithHttpResponse.php
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Tests\Classes;
|
||||||
|
|
||||||
|
use Icefox\DTO\DataObject;
|
||||||
|
use Illuminate\Http\Exceptions\HttpResponseException;
|
||||||
|
use Illuminate\Validation\Validator;
|
||||||
|
|
||||||
|
readonly class FailsWithHttpResponse
|
||||||
|
{
|
||||||
|
use DataObject;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
public string $string,
|
||||||
|
public int $int,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public static function fails(Validator $validator): ?static
|
||||||
|
{
|
||||||
|
throw new HttpResponseException(
|
||||||
|
response()->json(['errors' => $validator->errors()], 422)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
108
tests/FailedValidation/FailsMethodTest.php
Normal file
108
tests/FailedValidation/FailsMethodTest.php
Normal file
|
|
@ -0,0 +1,108 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\FailedValidation;
|
||||||
|
|
||||||
|
use Illuminate\Validation\ValidationException;
|
||||||
|
use Tests\Classes\FailsReturnsDefault;
|
||||||
|
use Tests\Classes\FailsReturnsNull;
|
||||||
|
use Tests\Classes\FailsWithHttpResponse;
|
||||||
|
use Tests\Classes\PrimitiveData;
|
||||||
|
|
||||||
|
describe('fails method behavior', function () {
|
||||||
|
|
||||||
|
it('throws ValidationException when class does not implement fails()', function () {
|
||||||
|
expect(function () {
|
||||||
|
PrimitiveData::fromArray([
|
||||||
|
'int' => 0,
|
||||||
|
'float' => 3.14,
|
||||||
|
'bool' => true,
|
||||||
|
]);
|
||||||
|
})->toThrow(ValidationException::class);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns null when fails() returns null', function () {
|
||||||
|
$result = FailsReturnsNull::fromArray([
|
||||||
|
'int' => 0,
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect($result)->toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns static instance when fails() returns an object', function () {
|
||||||
|
$result = FailsReturnsDefault::fromArray([
|
||||||
|
'int' => 0,
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect($result)->toBeInstanceOf(FailsReturnsDefault::class);
|
||||||
|
expect($result->string)->toBe('default_value');
|
||||||
|
expect($result->int)->toBe(42);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('HTTP request handling', function () {
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
\Illuminate\Support\Facades\Route::post('/test-validation-exception', function () {
|
||||||
|
PrimitiveData::fromArray([
|
||||||
|
'int' => 0,
|
||||||
|
'float' => 3.14,
|
||||||
|
'bool' => true,
|
||||||
|
]);
|
||||||
|
return response()->json(['success' => true]);
|
||||||
|
});
|
||||||
|
|
||||||
|
\Illuminate\Support\Facades\Route::post('/test-http-response-exception', function () {
|
||||||
|
FailsWithHttpResponse::fromArray([
|
||||||
|
'int' => 0,
|
||||||
|
]);
|
||||||
|
return response()->json(['success' => true]);
|
||||||
|
});
|
||||||
|
|
||||||
|
\Illuminate\Support\Facades\Route::post('/test-validation-exception-html', function () {
|
||||||
|
PrimitiveData::fromArray([
|
||||||
|
'int' => 0,
|
||||||
|
'float' => 3.14,
|
||||||
|
'bool' => true,
|
||||||
|
]);
|
||||||
|
return response('success');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns 422 with errors when ValidationException is thrown in JSON request', function () {
|
||||||
|
$response = $this->postJson('/test-validation-exception', [
|
||||||
|
'int' => 0,
|
||||||
|
'float' => 3.14,
|
||||||
|
'bool' => true,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$response->assertStatus(422);
|
||||||
|
$response->assertJsonValidationErrors(['string']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns custom JSON response when HttpResponseException is thrown', function () {
|
||||||
|
$response = $this->postJson('/test-http-response-exception', [
|
||||||
|
'int' => 0,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$response->assertStatus(422);
|
||||||
|
$response->assertJsonStructure(['errors']);
|
||||||
|
$response->assertJsonFragment([
|
||||||
|
'errors' => [
|
||||||
|
'string' => ['The string field is required.'],
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('redirects back with session errors when ValidationException is thrown in text/html request', function () {
|
||||||
|
$response = $this->post('/test-validation-exception-html', [
|
||||||
|
'int' => 0,
|
||||||
|
'float' => 3.14,
|
||||||
|
'bool' => true,
|
||||||
|
], [
|
||||||
|
'Accept' => 'text/html',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$response->assertRedirect();
|
||||||
|
$response->assertSessionHasErrors(['string']);
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
Add table
Add a link
Reference in a new issue