2026-09-17 18:11:39 +02:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
declare(strict_types=1);
|
|
|
|
|
|
|
|
|
|
namespace Tests\Feature\Api\V1\Middlewares;
|
|
|
|
|
|
|
|
|
|
use Illuminate\Http\Request;
|
|
|
|
|
use Presentation\Api\V1\Http\Middlewares\NormalizeQueryStringArrays;
|
|
|
|
|
use Symfony\Component\HttpFoundation\Response;
|
|
|
|
|
|
|
|
|
|
describe('NormalizeQueryStringArrays Middleware Feature Tests', function (): void {
|
|
|
|
|
it('normalizes repeated query parameters into array', function (): void {
|
|
|
|
|
// GIVEN
|
|
|
|
|
$middleware = new NormalizeQueryStringArrays();
|
|
|
|
|
$request = Request::create('/api/v1/employees?siteIds=LDCSER&siteIds=SLELA', 'GET');
|
|
|
|
|
|
|
|
|
|
// WHEN
|
|
|
|
|
$response = $middleware->handle($request, function (Request $req): Response {
|
|
|
|
|
expect($req->query('siteIds'))->toBe(['LDCSER', 'SLELA']);
|
|
|
|
|
|
|
|
|
|
return new Response();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// THEN
|
|
|
|
|
expect($response->getStatusCode())->toBe(200);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('normalizes bracket query parameters into array without trailing brackets in key', function (): void {
|
|
|
|
|
// GIVEN
|
|
|
|
|
$middleware = new NormalizeQueryStringArrays();
|
|
|
|
|
$request = Request::create('/api/v1/employees?siteIds[]=LDCSER&siteIds[]=SLELA', 'GET');
|
|
|
|
|
|
|
|
|
|
// WHEN
|
|
|
|
|
$response = $middleware->handle($request, function (Request $req): Response {
|
|
|
|
|
expect($req->query('siteIds'))->toBe(['LDCSER', 'SLELA']);
|
|
|
|
|
|
|
|
|
|
return new Response();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// THEN
|
|
|
|
|
expect($response->getStatusCode())->toBe(200);
|
|
|
|
|
});
|
|
|
|
|
|
2026-09-18 10:32:52 +02:00
|
|
|
it('preserves and normalizes nested bracket parameters into multidimensional array', function (): void {
|
2026-09-17 18:11:39 +02:00
|
|
|
// GIVEN
|
|
|
|
|
$middleware = new NormalizeQueryStringArrays();
|
2026-09-18 10:32:52 +02:00
|
|
|
$request = Request::create('/api/v1/employees?filter[date][from]=2024-01-01&filter[date][to]=2024-01-02', 'GET');
|
2026-09-17 18:11:39 +02:00
|
|
|
|
|
|
|
|
// WHEN
|
|
|
|
|
$response = $middleware->handle($request, function (Request $req): Response {
|
2026-09-18 10:32:52 +02:00
|
|
|
expect($req->query('filter'))->toBe([
|
|
|
|
|
'date' => [
|
|
|
|
|
'from' => '2024-01-01',
|
|
|
|
|
'to' => '2024-01-02',
|
|
|
|
|
],
|
|
|
|
|
]);
|
2026-09-17 18:11:39 +02:00
|
|
|
|
|
|
|
|
return new Response();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// THEN
|
|
|
|
|
expect($response->getStatusCode())->toBe(200);
|
|
|
|
|
});
|
|
|
|
|
|
2026-09-18 10:32:52 +02:00
|
|
|
it('preserves dots in query parameter keys without converting to underscores', function (): void {
|
2026-09-17 18:11:39 +02:00
|
|
|
// GIVEN
|
|
|
|
|
$middleware = new NormalizeQueryStringArrays();
|
2026-09-18 10:32:52 +02:00
|
|
|
$request = Request::create('/api/v1/employees?filter.status=active', 'GET');
|
2026-09-17 18:11:39 +02:00
|
|
|
|
|
|
|
|
// WHEN
|
|
|
|
|
$response = $middleware->handle($request, function (Request $req): Response {
|
2026-09-18 10:32:52 +02:00
|
|
|
expect($req->query('filter.status'))->toBe('active');
|
2026-09-17 18:11:39 +02:00
|
|
|
|
|
|
|
|
return new Response();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// THEN
|
|
|
|
|
expect($response->getStatusCode())->toBe(200);
|
|
|
|
|
});
|
|
|
|
|
|
2026-09-18 10:32:52 +02:00
|
|
|
it('preserves percent-encoded values without double decoding', function (): void {
|
|
|
|
|
// GIVEN
|
|
|
|
|
$middleware = new NormalizeQueryStringArrays();
|
|
|
|
|
$encodedVal = urlencode('%9999999%');
|
|
|
|
|
$request = Request::create("/api/v1/inco-sheets/search?incoIdLike={$encodedVal}", 'GET');
|
|
|
|
|
|
|
|
|
|
// WHEN
|
|
|
|
|
$response = $middleware->handle($request, function (Request $req): Response {
|
|
|
|
|
expect($req->query('incoIdLike'))->toBe('%9999999%');
|
|
|
|
|
|
|
|
|
|
return new Response();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// THEN
|
|
|
|
|
expect($response->getStatusCode())->toBe(200);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('handles empty query string gracefully', function (): void {
|
|
|
|
|
// GIVEN
|
|
|
|
|
$middleware = new NormalizeQueryStringArrays();
|
|
|
|
|
$request = Request::create('/api/v1/employees', 'GET');
|
|
|
|
|
|
|
|
|
|
// WHEN
|
|
|
|
|
$response = $middleware->handle($request, function (Request $req): Response {
|
|
|
|
|
expect($req->query->all())->toBeEmpty();
|
|
|
|
|
|
|
|
|
|
return new Response();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// THEN
|
|
|
|
|
expect($response->getStatusCode())->toBe(200);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('bypasses non-v1 routes and preserves native PHP query behavior', function (): void {
|
2026-09-17 18:11:39 +02:00
|
|
|
// GIVEN
|
|
|
|
|
$middleware = new NormalizeQueryStringArrays();
|
|
|
|
|
$request = Request::create('/ping?siteIds=LDCSER&siteIds=SLELA', 'GET');
|
|
|
|
|
|
|
|
|
|
// WHEN
|
|
|
|
|
$response = $middleware->handle($request, function (Request $req): Response {
|
2026-09-18 10:32:52 +02:00
|
|
|
// Native PHP behavior: last value wins if not normalized
|
2026-09-17 18:11:39 +02:00
|
|
|
expect($req->query('siteIds'))->toBe('SLELA');
|
|
|
|
|
|
|
|
|
|
return new Response();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// THEN
|
|
|
|
|
expect($response->getStatusCode())->toBe(200);
|
|
|
|
|
});
|
|
|
|
|
});
|