Skip to content

Commit 07d09b8

Browse files
committed
test: raise master coverage above 90 percent
1 parent 5874f1e commit 07d09b8

23 files changed

Lines changed: 1957 additions & 31 deletions

src/base/types/helpers/metadata/MetadataEngine.php

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -453,7 +453,7 @@ private function buildEntryEnvelope(array $payload, string $signature, int $now)
453453
/**
454454
* @param array<string, mixed> $entry
455455
*/
456-
private function writeEntry(string $cacheKey, array $entry): void
456+
protected function writeEntry(string $cacheKey, array $entry): void
457457
{
458458
$this->storeLocal($cacheKey, $entry);
459459
$this->writeOpcacheArtifact($cacheKey, $entry);
@@ -489,25 +489,30 @@ protected function queueBackgroundRegeneration(string $cacheKey, string $classNa
489489
}
490490
$this->shutdownRegistered = true;
491491
register_shutdown_function(function (): void {
492-
while ($pair = array_shift(self::$regenQueue)) {
493-
[$key, $className] = $pair;
494-
try {
495-
if (!class_exists($className)) {
496-
continue;
497-
}
498-
$reflection = new ReflectionClass($className);
499-
$signature = $this->sourceSignature($reflection);
500-
$payload = $this->buildClassBundle($reflection);
501-
$entry = $this->buildEntryEnvelope($payload, $signature, time());
502-
$this->writeEntry($key, $entry);
503-
self::$stats['metadata.regen']++;
504-
} catch (\Throwable $exception) {
505-
Logger::log('[MetadataEngine][SWR] ' . $exception->getMessage(), LOG_WARNING);
506-
} finally {
507-
$this->releaseLock($key);
492+
$this->drainBackgroundRegeneration();
493+
});
494+
}
495+
496+
protected function drainBackgroundRegeneration(): void
497+
{
498+
while ($pair = array_shift(self::$regenQueue)) {
499+
[$key, $className] = $pair;
500+
try {
501+
if (!class_exists($className)) {
502+
continue;
508503
}
504+
$reflection = new ReflectionClass($className);
505+
$signature = $this->sourceSignature($reflection);
506+
$payload = $this->buildClassBundle($reflection);
507+
$entry = $this->buildEntryEnvelope($payload, $signature, time());
508+
$this->writeEntry($key, $entry);
509+
self::$stats['metadata.regen']++;
510+
} catch (\Throwable $exception) {
511+
Logger::log('[MetadataEngine][SWR] ' . $exception->getMessage(), LOG_WARNING);
512+
} finally {
513+
$this->releaseLock($key);
509514
}
510-
});
515+
}
511516
}
512517

513518
protected function readFromOpcacheArtifact(string $cacheKey): ?array

src/base/types/traits/Api/ApiTrait.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ protected function saveBulk()
127127
$this->bulkSavedCount = 0;
128128
$databaseName = $this->resolveTableMapDatabaseName($tablemap);
129129
foreach ($this->list as &$model) {
130-
$con = Propel::getWriteConnection($databaseName);
130+
$con = $this->getWriteConnection($databaseName);
131131
try {
132132
$model->save($con);
133133
$con->commit();
@@ -139,6 +139,11 @@ protected function saveBulk()
139139
}
140140
}
141141

142+
protected function getWriteConnection(string $databaseName)
143+
{
144+
return Propel::getWriteConnection($databaseName);
145+
}
146+
142147
/**
143148
* @return array
144149
*/

src/runtime/swoole/SwooleRequestHandler.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public function __construct(
1818
private readonly ?SwooleRequestExecutor $requestExecutor = null,
1919
private readonly ?SwooleRuntimeStateManager $stateManager = null,
2020
private readonly ?UiDevelopmentProxyResolver $uiDevelopmentProxyResolver = null,
21-
private readonly ?UiDevelopmentHttpProxy $uiDevelopmentHttpProxy = null
21+
private readonly ?UiDevelopmentHttpProxyInterface $uiDevelopmentHttpProxy = null
2222
) {
2323
}
2424

@@ -109,7 +109,7 @@ private function getUiDevelopmentProxyResolver(): UiDevelopmentProxyResolver
109109
return $this->uiDevelopmentProxyResolver ?? new UiDevelopmentProxyResolver();
110110
}
111111

112-
private function getUiDevelopmentHttpProxy(): UiDevelopmentHttpProxy
112+
private function getUiDevelopmentHttpProxy(): UiDevelopmentHttpProxyInterface
113113
{
114114
return $this->uiDevelopmentHttpProxy ?? new UiDevelopmentHttpProxy();
115115
}

src/runtime/swoole/UiDevelopmentHttpProxy.php

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,19 @@
22

33
namespace PSFS\runtime\swoole;
44

5-
final class UiDevelopmentHttpProxy
5+
use Closure;
6+
7+
final class UiDevelopmentHttpProxy implements UiDevelopmentHttpProxyInterface
68
{
79
private const HOP_BY_HOP_HEADERS = [
810
'connection', 'keep-alive', 'proxy-authenticate', 'proxy-authorization',
911
'te', 'trailer', 'transfer-encoding', 'upgrade',
1012
];
1113

14+
public function __construct(private readonly ?Closure $clientFactory = null)
15+
{
16+
}
17+
1218
public function forward(UiDevelopmentProxyTarget $target, string $requestUri): ?array
1319
{
1420
$parts = parse_url($target->upstream);
@@ -18,7 +24,7 @@ public function forward(UiDevelopmentProxyTarget $target, string $requestUri): ?
1824

1925
$ssl = ($parts['scheme'] ?? 'http') === 'https';
2026
$port = (int)($parts['port'] ?? ($ssl ? 443 : 80));
21-
$client = new \Swoole\Coroutine\Http\Client((string)$parts['host'], $port, $ssl);
27+
$client = $this->createClient((string)$parts['host'], $port, $ssl);
2228
$client->set(['timeout' => 10, 'keep_alive' => false]);
2329
$client->setHeaders($this->requestHeaders($parts));
2430
$client->setMethod((string)($_SERVER['REQUEST_METHOD'] ?? 'GET'));
@@ -43,6 +49,15 @@ public function forward(UiDevelopmentProxyTarget $target, string $requestUri): ?
4349
return $result;
4450
}
4551

52+
private function createClient(string $host, int $port, bool $ssl): object
53+
{
54+
if ($this->clientFactory !== null) {
55+
return ($this->clientFactory)($host, $port, $ssl);
56+
}
57+
58+
return new \Swoole\Coroutine\Http\Client($host, $port, $ssl);
59+
}
60+
4661
private function requestHeaders(array $upstream): array
4762
{
4863
$headers = [];
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
namespace PSFS\runtime\swoole;
4+
5+
interface UiDevelopmentHttpProxyInterface
6+
{
7+
/**
8+
* @return array{status:int,headers:array<string,mixed>,body:string}|null
9+
*/
10+
public function forward(UiDevelopmentProxyTarget $target, string $requestUri): ?array;
11+
}

src/runtime/swoole/UiDevelopmentWebSocketBridge.php

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,21 @@
22

33
namespace PSFS\runtime\swoole;
44

5+
use Closure;
56
use PSFS\base\config\Config;
67
use PSFS\base\runtime\RuntimeMode;
78
use PSFS\base\Security;
89

910
final class UiDevelopmentWebSocketBridge
1011
{
11-
/** @var array<int, \Swoole\Coroutine\Http\Client> */
12+
/** @var array<int, object> */
1213
private array $clients = [];
1314

1415
public function __construct(
1516
private readonly ?SwooleRequestHydrator $hydrator = null,
1617
private readonly ?SwooleRuntimeStateManager $stateManager = null,
17-
private readonly ?UiDevelopmentProxyResolver $resolver = null
18+
private readonly ?UiDevelopmentProxyResolver $resolver = null,
19+
private readonly ?Closure $clientFactory = null
1820
) {
1921
}
2022

@@ -77,15 +79,17 @@ public function close(object $server, int $fd): void
7779
$this->discard($fd);
7880
}
7981

80-
private function connect(UiDevelopmentProxyTarget $target): ?\Swoole\Coroutine\Http\Client
82+
private function connect(UiDevelopmentProxyTarget $target): ?object
8183
{
8284
$parts = parse_url($target->upstream);
8385
if (!is_array($parts) || empty($parts['host'])) {
8486
return null;
8587
}
8688
$ssl = ($parts['scheme'] ?? 'http') === 'https';
8789
$port = (int)($parts['port'] ?? ($ssl ? 443 : 80));
88-
$client = new \Swoole\Coroutine\Http\Client((string)$parts['host'], $port, $ssl);
90+
$client = $this->clientFactory !== null
91+
? ($this->clientFactory)((string)$parts['host'], $port, $ssl)
92+
: new \Swoole\Coroutine\Http\Client((string)$parts['host'], $port, $ssl);
8993
$client->set(['timeout' => 10, 'keep_alive' => true]);
9094
$client->setHeaders($this->upstreamHeaders($parts));
9195
if (!$client->upgrade($this->requestUri())) {

tests/base/controller/CoverageControllersTest.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use PSFS\base\exception\ApiException;
1717
use PSFS\base\exception\RouterException;
1818
use PSFS\base\types\Form;
19+
use PSFS\base\types\AuthAdminController;
1920
use PSFS\base\types\traits\Api\ManagerTrait;
2021
use PSFS\base\types\helpers\AuthHelper;
2122
use PSFS\controller\ConfigController;
@@ -390,6 +391,20 @@ public function testManagerTraitThrowsForbiddenForUserRoleAndExposesMenu(): void
390391
$probe->admin();
391392
}
392393

394+
public function testManagerTraitRendersBaseAndItemManagerContracts(): void
395+
{
396+
Security::setTest(true);
397+
Security::dropInstance();
398+
$renderer = new AuthAdminControllerCoverageStub();
399+
$this->injectSingleton(AuthAdminController::class, $renderer);
400+
401+
$probe = new ManagerTraitProbe();
402+
self::assertSame('manager-rendered', $probe->admin());
403+
self::assertSame('manager-rendered', $probe->adminItem('42'));
404+
self::assertSame('api.admin.html.twig', $renderer->template);
405+
self::assertSame('42', $renderer->vars['initialItemId']);
406+
}
407+
393408
private function seedAdmins(array $admins): void
394409
{
395410
Cache::getInstance()->storeData($this->adminsPath, $admins, Cache::JSONGZ, true);
@@ -625,3 +640,16 @@ public function exposeMenu(): array
625640
return $this->getMenu();
626641
}
627642
}
643+
644+
class AuthAdminControllerCoverageStub extends AuthAdminController
645+
{
646+
public string $template = '';
647+
public array $vars = [];
648+
649+
public function render($template, array $vars = [], $cookies = [], $domain = null)
650+
{
651+
$this->template = $template;
652+
$this->vars = $vars;
653+
return 'manager-rendered';
654+
}
655+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<?php
2+
3+
namespace PSFS\tests\base\dto;
4+
5+
use PHPUnit\Framework\TestCase;
6+
use PSFS\base\Request;
7+
use PSFS\base\dto\ValidationContext;
8+
9+
class ValidationContextTest extends TestCase
10+
{
11+
public function testFromRequestUsesRawPayloadAndExplicitValidationFlags(): void
12+
{
13+
$request = Request::getInstance();
14+
$raw = new \ReflectionProperty(Request::class, 'raw');
15+
$data = new \ReflectionProperty(Request::class, 'data');
16+
$raw->setValue($request, ['json' => 'payload']);
17+
$data->setValue($request, ['legacy' => 'form']);
18+
19+
$context = ValidationContext::fromRequest(false, false);
20+
21+
self::assertSame(['json' => 'payload'], $context->payload);
22+
self::assertFalse($context->strictUnknownFields);
23+
self::assertFalse($context->enforceCsrf);
24+
}
25+
26+
public function testFromRequestFallsBackToLegacyFormDataWhenRawPayloadIsEmpty(): void
27+
{
28+
$request = Request::getInstance();
29+
$raw = new \ReflectionProperty(Request::class, 'raw');
30+
$data = new \ReflectionProperty(Request::class, 'data');
31+
$raw->setValue($request, []);
32+
$data->setValue($request, ['legacy' => 'form']);
33+
34+
$context = ValidationContext::fromRequest();
35+
36+
self::assertSame(['legacy' => 'form'], $context->payload);
37+
self::assertTrue($context->strictUnknownFields);
38+
self::assertNull($context->enforceCsrf);
39+
}
40+
41+
public function testHeaderLookupSupportsExactCaseInsensitiveAndUnderscoreNames(): void
42+
{
43+
$context = new ValidationContext([], [
44+
'X-Exact' => 'exact',
45+
'X-Correlation' => 'correlation',
46+
'X_CORRELATION_ID' => 42,
47+
'X-Multi' => ['not-a-header'],
48+
]);
49+
50+
self::assertSame('exact', $context->header('X-Exact'));
51+
self::assertSame('correlation', $context->header('x-correlation'));
52+
self::assertSame('42', $context->header('x-correlation-id'));
53+
self::assertNull($context->header('X-Multi'));
54+
self::assertNull($context->header('missing-header'));
55+
}
56+
}

0 commit comments

Comments
 (0)