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",
|
||||
"phpstan/phpstan": "^2.1",
|
||||
"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",
|
||||
"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",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "1d9eef5574135e39ab7eaa6beae3fdad",
|
||||
"content-hash": "336ed1e898bc39b0e9990becc327415c",
|
||||
"packages": [
|
||||
{
|
||||
"name": "brick/math",
|
||||
|
|
@ -7992,6 +7992,80 @@
|
|||
],
|
||||
"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",
|
||||
"version": "v4.0.1",
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ trait DataObject
|
|||
/**
|
||||
* @param array<string,mixed> $input
|
||||
*/
|
||||
public static function fromArray(array $input): static
|
||||
public static function fromArray(array $input): ?static
|
||||
{
|
||||
$parameters = RuleFactory::getParametersMeta(static::class);
|
||||
foreach ($parameters as $parameter) {
|
||||
|
|
@ -72,8 +72,7 @@ trait DataObject
|
|||
$validator = static::withValidator($input, $rules);
|
||||
|
||||
if ($validator->fails()) {
|
||||
$exception = new ValidationException($validator);
|
||||
throw $exception;
|
||||
return static::fails($validator);
|
||||
}
|
||||
|
||||
$mappedInput = [];
|
||||
|
|
@ -103,6 +102,11 @@ trait DataObject
|
|||
return [];
|
||||
}
|
||||
|
||||
public static function fails(Validator $validator): ?static
|
||||
{
|
||||
throw new ValidationException($validator);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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