Skip to content

Commit 933cd39

Browse files
committed
feat(tests): adicionar testes para validar os limites de pooling de JSON
1 parent ca005ba commit 933cd39

File tree

3 files changed

+247
-21
lines changed

3 files changed

+247
-21
lines changed

src/Http/Response.php

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -11,68 +11,68 @@
1111
use InvalidArgumentException;
1212

1313
/**
14-
* Classe Response híbrida que implementa PSR-7 mantendo compatibilidade Express.js
14+
* Hybrid Response class that implements PSR-7 while maintaining Express.js compatibility
1515
*
16-
* Esta classe oferece suporte completo a PSR-7 (ResponseInterface)
17-
* enquanto mantém todos os métodos de conveniência do estilo Express.js
18-
* para total compatibilidade com código existente.
16+
* This class offers complete PSR-7 (ResponseInterface) support
17+
* while maintaining all Express.js style convenience methods
18+
* for full backward compatibility with existing code.
1919
*/
2020
class Response implements ResponseInterface
2121
{
2222
/**
23-
* Flags para encoding JSON consistente
23+
* Flags for consistent JSON encoding
2424
*/
2525
private const JSON_ENCODE_FLAGS = JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE;
2626

2727
/**
28-
* Instância PSR-7 interna (lazy loaded)
28+
* Internal PSR-7 instance (lazy loaded)
2929
*/
3030
private ?ResponseInterface $psr7Response = null;
3131

3232
/**
33-
* Código de status HTTP.
33+
* HTTP status code.
3434
*/
3535
private int $statusCode = 200;
3636

3737
/**
38-
* Cabeçalhos da resposta.
38+
* Response headers.
3939
*
4040
* @var array<string, mixed>
4141
*/
4242
private array $headers = [];
4343

4444
/**
45-
* Corpo da resposta.
45+
* Response body.
4646
*/
4747
private string $body = '';
4848

4949
/**
50-
* Indica se a resposta está sendo enviada como stream.
50+
* Indicates if the response is being sent as stream.
5151
*/
5252
private bool $isStreaming = false;
5353

5454
/**
55-
* Buffer size para streaming (em bytes).
55+
* Buffer size for streaming (in bytes).
5656
*/
5757
private int $streamBufferSize = 8192;
5858

5959
/**
60-
* Indica se está em modo teste (não faz echo direto).
60+
* Indicates if in test mode (does not echo directly).
6161
*/
6262
private bool $testMode = false;
6363

6464
/**
65-
* Indica se a resposta já foi enviada.
65+
* Indicates if the response has already been sent.
6666
*/
6767
private bool $sent = false;
6868

6969
/**
70-
* Indica se o controle de emissão automática está desabilitado.
70+
* Indicates if automatic emission control is disabled.
7171
*/
7272
private bool $disableAutoEmit = false;
7373

7474
/**
75-
* Construtor da classe Response.
75+
* Response class constructor.
7676
*/
7777
public function __construct()
7878
{
@@ -774,24 +774,24 @@ public function resetSentState(): self
774774
}
775775

776776
/**
777-
* Determina se deve usar pooling para JSON
777+
* Determines if JSON pooling should be used for the given data
778778
*/
779779
private function shouldUseJsonPooling(mixed $data): bool
780780
{
781-
// Usar pooling para arrays/objetos médios e grandes
781+
// Use pooling for medium and large arrays/objects
782782
if (is_array($data)) {
783783
$count = count($data);
784-
return $count >= 10; // Arrays com 10+ elementos
784+
return $count >= JsonBufferPool::POOLING_ARRAY_THRESHOLD;
785785
}
786786

787787
if (is_object($data)) {
788788
$vars = get_object_vars($data);
789-
return $vars && count($vars) >= 5; // Objetos com 5+ propriedades
789+
return $vars && count($vars) >= JsonBufferPool::POOLING_OBJECT_THRESHOLD;
790790
}
791791

792-
// Usar pooling para strings longas
792+
// Use pooling for long strings
793793
if (is_string($data)) {
794-
return strlen($data) > 1024; // Strings > 1KB
794+
return strlen($data) > JsonBufferPool::POOLING_STRING_THRESHOLD;
795795
}
796796

797797
return false;

src/Json/Pool/JsonBufferPool.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@ class JsonBufferPool
4141
private const MIN_LARGE_BUFFER_SIZE = 65536; // Minimum size for very large buffers (64KB)
4242
private const BUFFER_SIZE_MULTIPLIER = 2; // Multiplier for buffer size calculation
4343

44+
// Pooling decision thresholds (for determining when to use pooled encoding)
45+
public const POOLING_ARRAY_THRESHOLD = 10; // Arrays with 10+ elements use pooling
46+
public const POOLING_OBJECT_THRESHOLD = 5; // Objects with 5+ properties use pooling
47+
public const POOLING_STRING_THRESHOLD = 1024; // Strings longer than 1KB use pooling
48+
4449
/**
4550
* Buffer pools organized by capacity
4651
*/
Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PivotPHP\Core\Tests\Json;
6+
7+
use PHPUnit\Framework\TestCase;
8+
use PivotPHP\Core\Http\Response;
9+
use PivotPHP\Core\Json\Pool\JsonBufferPool;
10+
11+
/**
12+
* Test that pooling thresholds are centralized and consistent
13+
*/
14+
class JsonPoolingThresholdsTest extends TestCase
15+
{
16+
protected function setUp(): void
17+
{
18+
JsonBufferPool::clearPools();
19+
JsonBufferPool::resetConfiguration();
20+
}
21+
22+
protected function tearDown(): void
23+
{
24+
JsonBufferPool::clearPools();
25+
JsonBufferPool::resetConfiguration();
26+
}
27+
28+
/**
29+
* Test that pooling constants are properly defined
30+
*/
31+
public function testPoolingConstantsExist(): void
32+
{
33+
$this->assertTrue(defined('PivotPHP\Core\Json\Pool\JsonBufferPool::POOLING_ARRAY_THRESHOLD'));
34+
$this->assertTrue(defined('PivotPHP\Core\Json\Pool\JsonBufferPool::POOLING_OBJECT_THRESHOLD'));
35+
$this->assertTrue(defined('PivotPHP\Core\Json\Pool\JsonBufferPool::POOLING_STRING_THRESHOLD'));
36+
37+
// Verify values are reasonable
38+
$this->assertEquals(10, JsonBufferPool::POOLING_ARRAY_THRESHOLD);
39+
$this->assertEquals(5, JsonBufferPool::POOLING_OBJECT_THRESHOLD);
40+
$this->assertEquals(1024, JsonBufferPool::POOLING_STRING_THRESHOLD);
41+
}
42+
43+
/**
44+
* Test that Response uses centralized thresholds
45+
*/
46+
public function testResponseUsesPoolingThresholds(): void
47+
{
48+
$response = new Response();
49+
$response->setTestMode(true);
50+
51+
// Test array threshold - just below threshold should not pool
52+
$smallArray = array_fill(0, JsonBufferPool::POOLING_ARRAY_THRESHOLD - 1, 'item');
53+
$response->json($smallArray);
54+
55+
$stats = JsonBufferPool::getStatistics();
56+
$this->assertEquals(0, $stats['total_operations'], 'Small arrays should not use pooling');
57+
58+
// Reset
59+
JsonBufferPool::clearPools();
60+
$response = new Response();
61+
$response->setTestMode(true);
62+
63+
// Test array threshold - at threshold should pool
64+
$mediumArray = array_fill(0, JsonBufferPool::POOLING_ARRAY_THRESHOLD, 'item');
65+
$response->json($mediumArray);
66+
67+
$stats = JsonBufferPool::getStatistics();
68+
$this->assertEquals(1, $stats['total_operations'], 'Arrays at threshold should use pooling');
69+
}
70+
71+
/**
72+
* Test object pooling threshold consistency
73+
*/
74+
public function testObjectPoolingThreshold(): void
75+
{
76+
$response = new Response();
77+
$response->setTestMode(true);
78+
79+
// Create object just below threshold
80+
$smallObject = new \stdClass();
81+
for ($i = 0; $i < JsonBufferPool::POOLING_OBJECT_THRESHOLD - 1; $i++) {
82+
$smallObject->{"prop{$i}"} = "value{$i}";
83+
}
84+
85+
$response->json($smallObject);
86+
$stats = JsonBufferPool::getStatistics();
87+
$this->assertEquals(0, $stats['total_operations'], 'Small objects should not use pooling');
88+
89+
// Reset
90+
JsonBufferPool::clearPools();
91+
$response = new Response();
92+
$response->setTestMode(true);
93+
94+
// Create object at threshold
95+
$mediumObject = new \stdClass();
96+
for ($i = 0; $i < JsonBufferPool::POOLING_OBJECT_THRESHOLD; $i++) {
97+
$mediumObject->{"prop{$i}"} = "value{$i}";
98+
}
99+
100+
$response->json($mediumObject);
101+
$stats = JsonBufferPool::getStatistics();
102+
$this->assertEquals(1, $stats['total_operations'], 'Objects at threshold should use pooling');
103+
}
104+
105+
/**
106+
* Test string pooling threshold consistency
107+
*/
108+
public function testStringPoolingThreshold(): void
109+
{
110+
$response = new Response();
111+
$response->setTestMode(true);
112+
113+
// String just under threshold
114+
$shortString = str_repeat('x', JsonBufferPool::POOLING_STRING_THRESHOLD);
115+
$response->json($shortString);
116+
117+
$stats = JsonBufferPool::getStatistics();
118+
$this->assertEquals(0, $stats['total_operations'], 'Short strings should not use pooling');
119+
120+
// Reset
121+
JsonBufferPool::clearPools();
122+
$response = new Response();
123+
$response->setTestMode(true);
124+
125+
// String over threshold
126+
$longString = str_repeat('x', JsonBufferPool::POOLING_STRING_THRESHOLD + 1);
127+
$response->json($longString);
128+
129+
$stats = JsonBufferPool::getStatistics();
130+
$this->assertEquals(1, $stats['total_operations'], 'Long strings should use pooling');
131+
}
132+
133+
/**
134+
* Test that thresholds are reasonable for performance
135+
*/
136+
public function testThresholdsAreReasonable(): void
137+
{
138+
// Array threshold should be high enough to avoid pooling small arrays
139+
$this->assertGreaterThanOrEqual(5, JsonBufferPool::POOLING_ARRAY_THRESHOLD);
140+
$this->assertLessThanOrEqual(50, JsonBufferPool::POOLING_ARRAY_THRESHOLD);
141+
142+
// Object threshold should be reasonable for common objects
143+
$this->assertGreaterThanOrEqual(3, JsonBufferPool::POOLING_OBJECT_THRESHOLD);
144+
$this->assertLessThanOrEqual(20, JsonBufferPool::POOLING_OBJECT_THRESHOLD);
145+
146+
// String threshold should be reasonable (around 1KB)
147+
$this->assertGreaterThanOrEqual(512, JsonBufferPool::POOLING_STRING_THRESHOLD);
148+
$this->assertLessThanOrEqual(4096, JsonBufferPool::POOLING_STRING_THRESHOLD);
149+
}
150+
151+
/**
152+
* Test consistency between direct pooling and Response pooling
153+
*/
154+
public function testConsistencyBetweenDirectAndResponsePooling(): void
155+
{
156+
$testData = array_fill(0, JsonBufferPool::POOLING_ARRAY_THRESHOLD, 'test');
157+
158+
// Direct pooling
159+
JsonBufferPool::clearPools();
160+
$directResult = JsonBufferPool::encodeWithPool($testData);
161+
$directStats = JsonBufferPool::getStatistics();
162+
163+
// Response pooling
164+
JsonBufferPool::clearPools();
165+
$response = new Response();
166+
$response->setTestMode(true);
167+
$response->json($testData);
168+
$responseResult = $response->getBodyAsString();
169+
$responseStats = JsonBufferPool::getStatistics();
170+
171+
// Results should be identical
172+
$this->assertEquals($directResult, $responseResult);
173+
174+
// Both should have used pooling
175+
$this->assertEquals(1, $directStats['total_operations']);
176+
$this->assertEquals(1, $responseStats['total_operations']);
177+
}
178+
179+
/**
180+
* Test that updating centralized constants affects both components
181+
*/
182+
public function testCentralizedConstantsAffectBothComponents(): void
183+
{
184+
// This test verifies that the constants are truly centralized
185+
// by checking that Response uses the same values as JsonBufferPool
186+
187+
$reflection = new \ReflectionClass('PivotPHP\Core\Http\Response');
188+
$shouldUsePoolingMethod = $reflection->getMethod('shouldUseJsonPooling');
189+
$shouldUsePoolingMethod->setAccessible(true);
190+
191+
$response = new Response();
192+
193+
// Test array threshold boundary
194+
$arrayAtThreshold = array_fill(0, JsonBufferPool::POOLING_ARRAY_THRESHOLD, 'item');
195+
$arrayBelowThreshold = array_fill(0, JsonBufferPool::POOLING_ARRAY_THRESHOLD - 1, 'item');
196+
197+
$this->assertTrue($shouldUsePoolingMethod->invoke($response, $arrayAtThreshold));
198+
$this->assertFalse($shouldUsePoolingMethod->invoke($response, $arrayBelowThreshold));
199+
200+
// Test object threshold boundary
201+
$objectAtThreshold = new \stdClass();
202+
for ($i = 0; $i < JsonBufferPool::POOLING_OBJECT_THRESHOLD; $i++) {
203+
$objectAtThreshold->{"prop{$i}"} = "value{$i}";
204+
}
205+
206+
$objectBelowThreshold = new \stdClass();
207+
for ($i = 0; $i < JsonBufferPool::POOLING_OBJECT_THRESHOLD - 1; $i++) {
208+
$objectBelowThreshold->{"prop{$i}"} = "value{$i}";
209+
}
210+
211+
$this->assertTrue($shouldUsePoolingMethod->invoke($response, $objectAtThreshold));
212+
$this->assertFalse($shouldUsePoolingMethod->invoke($response, $objectBelowThreshold));
213+
214+
// Test string threshold boundary
215+
$stringAtThreshold = str_repeat('x', JsonBufferPool::POOLING_STRING_THRESHOLD + 1);
216+
$stringBelowThreshold = str_repeat('x', JsonBufferPool::POOLING_STRING_THRESHOLD);
217+
218+
$this->assertTrue($shouldUsePoolingMethod->invoke($response, $stringAtThreshold));
219+
$this->assertFalse($shouldUsePoolingMethod->invoke($response, $stringBelowThreshold));
220+
}
221+
}

0 commit comments

Comments
 (0)