mirror of
https://github.com/Vateron-Media/XC_VM.git
synced 2026-09-25 12:01:55 +02:00
Move Core/Http into XcVm\Core\Http (ApiClient, CurlClient, RequestManager, Request, Response, Router) and Core/Http/Pipeline into XcVm\Core\Http\Pipeline (StreamContext, StreamMiddlewareInterface, StreamPipeline). RequestGuard.php is procedural (global functions) and stays global. - Namespace the 9 classes; qualify still-global deps with leading backslash (\ServerRepository in ApiClient; \Authorization, \ServiceContainer, \AdminHelpers in Router) and built-in \Throwable; same-namespace siblings unqualified. - Add 'use XcVm\Core\Http\...;' to referencing files — RequestManager 128, Router 19, ApiClient 15, Request 11, Response 6, CurlClient 5, plus Pipeline — across src/ and tests/. - Rewrite the modules' 'use Router;' → 'use XcVm\Core\Http\Router;' (plex, watch, tmdb). - Fix pre-existing leading-backslash global refs (\Router etc.) to the FQCN. - Tests: fully-qualify the Router type hint in generated module fixtures (registerRoutes(\XcVm\Core\Http\Router ...)) and update the InterfaceContractTest param-type expectation to the FQCN. Also repaired two fixtures where the use-inserter mis-placed a 'use' (files declare a method literally named namespace(), which the tokenizer reports as T_NAMESPACE). - phpstan-baseline.neon regenerated (292→292; class names in frozen messages gained the namespace, no new/unknown-class errors). Verified: php -l clean; PHPStan no errors; PHPUnit 295/295; Http classes resolve via Composer; leading-backslash re-sweep across migrated classes is clean.
157 lines
5.5 KiB
PHP
157 lines
5.5 KiB
PHP
<?php
|
|
|
|
use XcVm\Core\Http\Pipeline\StreamPipeline;
|
|
use XcVm\Core\Http\Pipeline\StreamMiddlewareInterface;
|
|
use XcVm\Core\Http\Pipeline\StreamContext;
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
final class StreamPipelineTest extends TestCase {
|
|
|
|
// ── Basic pipeline execution ───────────────────────────────
|
|
|
|
public function testRunPassesContextThroughMiddleware(): void {
|
|
$pipeline = new StreamPipeline();
|
|
$pipeline->pipe(new SetAttributeMiddleware('touched', true));
|
|
|
|
$ctx = new StreamContext(1, 'u1', 'hls', []);
|
|
$result = $pipeline->run($ctx);
|
|
|
|
$this->assertTrue($result->get('touched'));
|
|
}
|
|
|
|
public function testRunWithNoMiddlewareReturnsContextUnchanged(): void {
|
|
$pipeline = new StreamPipeline();
|
|
$ctx = new StreamContext(1, 'u1', 'hls', []);
|
|
$result = $pipeline->run($ctx);
|
|
|
|
$this->assertSame($ctx, $result);
|
|
}
|
|
|
|
// ── Priority ordering ──────────────────────────────────────
|
|
|
|
public function testMiddlewareRunsInPriorityDescendingOrder(): void {
|
|
$order = [];
|
|
$pipeline = new StreamPipeline();
|
|
$pipeline->pipe(new RecordOrderMiddleware($order, 'low', 10));
|
|
$pipeline->pipe(new RecordOrderMiddleware($order, 'high', 50));
|
|
$pipeline->pipe(new RecordOrderMiddleware($order, 'mid', 30));
|
|
|
|
$pipeline->run(new StreamContext(1, 'u', 'hls', []));
|
|
|
|
$this->assertSame(['high', 'mid', 'low'], $order);
|
|
}
|
|
|
|
// ── Abort behaviour ────────────────────────────────────────
|
|
|
|
public function testAbortedContextSkipsRemainingMiddleware(): void {
|
|
$afterAbort = false;
|
|
$pipeline = new StreamPipeline();
|
|
$pipeline->pipe(new AbortingMiddleware(100));
|
|
$pipeline->pipe(new CallbackMiddleware(function (StreamContext $ctx, callable $next) use (&$afterAbort) {
|
|
$afterAbort = true;
|
|
return $next($ctx);
|
|
}, 50));
|
|
|
|
$result = $pipeline->run(new StreamContext(1, 'u', 'hls', []));
|
|
|
|
$this->assertTrue($result->isAborted());
|
|
$this->assertFalse($afterAbort);
|
|
}
|
|
|
|
public function testAbortSetsReasonAndCode(): void {
|
|
$pipeline = new StreamPipeline();
|
|
$pipeline->pipe(new AbortingMiddleware(100, 'blocked', 403));
|
|
|
|
$result = $pipeline->run(new StreamContext(1, 'u', 'hls', []));
|
|
|
|
$this->assertSame('blocked', $result->getAbortReason());
|
|
$this->assertSame(403, $result->getAbortCode());
|
|
}
|
|
|
|
// ── StreamContext attribute bag ────────────────────────────
|
|
|
|
public function testContextAttributeBag(): void {
|
|
$ctx = new StreamContext(1, 'u', 'hls', []);
|
|
$ctx->set('key', 'value');
|
|
|
|
$this->assertTrue($ctx->has('key'));
|
|
$this->assertSame('value', $ctx->get('key'));
|
|
}
|
|
|
|
public function testContextGetReturnsNullForMissingAttribute(): void {
|
|
$ctx = new StreamContext(1, 'u', 'hls', []);
|
|
|
|
$this->assertNull($ctx->get('missing'));
|
|
}
|
|
|
|
// ── getMiddleware ──────────────────────────────────────────
|
|
|
|
public function testGetMiddlewareReturnsRegisteredMiddleware(): void {
|
|
$mw = new SetAttributeMiddleware('k', 'v');
|
|
$pipeline = new StreamPipeline();
|
|
$pipeline->pipe($mw);
|
|
|
|
$this->assertContains($mw, $pipeline->getMiddleware());
|
|
}
|
|
}
|
|
|
|
// ── Middleware fixtures ────────────────────────────────────────────
|
|
|
|
final class SetAttributeMiddleware implements StreamMiddlewareInterface {
|
|
public function __construct(
|
|
private readonly string $key,
|
|
private readonly mixed $value,
|
|
private readonly int $priority = 0,
|
|
) {}
|
|
|
|
public function handle(StreamContext $ctx, callable $next): StreamContext {
|
|
$ctx->set($this->key, $this->value);
|
|
return $next($ctx);
|
|
}
|
|
|
|
public function getPriority(): int { return $this->priority; }
|
|
}
|
|
|
|
final class RecordOrderMiddleware implements StreamMiddlewareInterface {
|
|
public function __construct(
|
|
private array &$order,
|
|
private string $label,
|
|
private int $priority,
|
|
) {}
|
|
|
|
public function handle(StreamContext $ctx, callable $next): StreamContext {
|
|
$this->order[] = $this->label;
|
|
return $next($ctx);
|
|
}
|
|
|
|
public function getPriority(): int { return $this->priority; }
|
|
}
|
|
|
|
final class AbortingMiddleware implements StreamMiddlewareInterface {
|
|
public function __construct(
|
|
private int $priority,
|
|
private string $reason = 'aborted',
|
|
private int $code = 0,
|
|
) {}
|
|
|
|
public function handle(StreamContext $ctx, callable $next): StreamContext {
|
|
$ctx->abort($this->reason, $this->code);
|
|
return $ctx;
|
|
}
|
|
|
|
public function getPriority(): int { return $this->priority; }
|
|
}
|
|
|
|
final class CallbackMiddleware implements StreamMiddlewareInterface {
|
|
public function __construct(
|
|
private readonly \Closure $callback,
|
|
private readonly int $priority = 0,
|
|
) {}
|
|
|
|
public function handle(StreamContext $ctx, callable $next): StreamContext {
|
|
return ($this->callback)($ctx, $next);
|
|
}
|
|
|
|
public function getPriority(): int { return $this->priority; }
|
|
}
|