Files
intotheeast-com-content/plugins/api/tests/Unit/TestHelper.php
T

272 lines
11 KiB
PHP

<?php
declare(strict_types=1);
namespace Grav\Plugin\Api\Tests\Unit;
use Grav\Common\Config\Config;
use Grav\Common\Grav;
use Grav\Common\User\Interfaces\UserInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\StreamInterface;
use Psr\Http\Message\UriInterface;
/**
* Static helper methods for building lightweight stubs used across the test suite.
*
* All request stubs are anonymous class implementations of PSR interfaces.
* Config/Grav/User stubs come from our test Stubs/GravStubs.php and are
* real instances of the Grav types (either the genuine classes or our
* minimal stubs depending on the environment).
*/
final class TestHelper
{
/**
* Create a stub PSR-7 ServerRequest.
*/
public static function createMockRequest(
string $method = 'GET',
string $path = '/',
array $headers = [],
string $body = '',
array $queryParams = [],
array $serverParams = [],
array $attributes = [],
): ServerRequestInterface {
return new class ($method, $path, $headers, $body, $queryParams, $serverParams, $attributes) implements ServerRequestInterface {
public function __construct(
private readonly string $method,
private readonly string $path,
private readonly array $headers,
private readonly string $body,
private readonly array $queryParams,
private readonly array $serverParams,
private array $attributes,
) {}
public function getMethod(): string { return $this->method; }
public function getUri(): UriInterface
{
$path = $this->path;
return new class ($path) implements UriInterface {
public function __construct(private readonly string $path) {}
public function getScheme(): string { return 'https'; }
public function getAuthority(): string { return ''; }
public function getUserInfo(): string { return ''; }
public function getHost(): string { return 'localhost'; }
public function getPort(): ?int { return null; }
public function getPath(): string { return $this->path; }
public function getQuery(): string { return ''; }
public function getFragment(): string { return ''; }
public function withScheme(string $scheme): static { return clone $this; }
public function withUserInfo(string $user, ?string $password = null): static { return clone $this; }
public function withHost(string $host): static { return clone $this; }
public function withPort(?int $port): static { return clone $this; }
public function withPath(string $path): static { return clone $this; }
public function withQuery(string $query): static { return clone $this; }
public function withFragment(string $fragment): static { return clone $this; }
public function __toString(): string { return $this->path; }
};
}
public function getBody(): StreamInterface
{
$body = $this->body;
return new class ($body) implements StreamInterface {
public function __construct(private readonly string $content) {}
public function __toString(): string { return $this->content; }
public function close(): void {}
public function detach() { return null; }
public function getSize(): ?int { return strlen($this->content); }
public function tell(): int { return 0; }
public function eof(): bool { return true; }
public function isSeekable(): bool { return false; }
public function seek(int $offset, int $whence = SEEK_SET): void {}
public function rewind(): void {}
public function isWritable(): bool { return false; }
public function write(string $string): int { return 0; }
public function isReadable(): bool { return true; }
public function read(int $length): string { return $this->content; }
public function getContents(): string { return $this->content; }
public function getMetadata(?string $key = null): mixed { return null; }
};
}
public function getQueryParams(): array { return $this->queryParams; }
public function getServerParams(): array { return $this->serverParams; }
public function getHeaderLine(string $name): string
{
foreach ($this->headers as $key => $value) {
if (strcasecmp($key, $name) === 0) {
return $value;
}
}
return '';
}
public function getHeader(string $name): array
{
foreach ($this->headers as $key => $value) {
if (strcasecmp($key, $name) === 0) {
return [$value];
}
}
return [];
}
public function hasHeader(string $name): bool
{
foreach ($this->headers as $key => $value) {
if (strcasecmp($key, $name) === 0) {
return true;
}
}
return false;
}
public function getHeaders(): array { return $this->headers; }
public function getAttribute(string $name, mixed $default = null): mixed
{
return $this->attributes[$name] ?? $default;
}
public function withAttribute(string $name, mixed $value): static
{
$clone = clone $this;
$clone->attributes[$name] = $value;
return $clone;
}
public function getRequestTarget(): string { return $this->path; }
public function withRequestTarget(string $requestTarget): static { return clone $this; }
public function withMethod(string $method): static { return clone $this; }
public function withUri(UriInterface $uri, bool $preserveHost = false): static { return clone $this; }
public function getProtocolVersion(): string { return '1.1'; }
public function withProtocolVersion(string $version): static { return clone $this; }
public function withHeader(string $name, $value): static { return clone $this; }
public function withAddedHeader(string $name, $value): static { return clone $this; }
public function withoutHeader(string $name): static { return clone $this; }
public function withBody(StreamInterface $body): static { return clone $this; }
public function getCookieParams(): array { return []; }
public function withCookieParams(array $cookies): static { return clone $this; }
public function withQueryParams(array $query): static { return clone $this; }
public function getUploadedFiles(): array { return []; }
public function withUploadedFiles(array $uploadedFiles): static { return clone $this; }
public function getParsedBody(): mixed { return null; }
public function withParsedBody($data): static { return clone $this; }
public function getAttributes(): array { return $this->attributes; }
public function withoutAttribute(string $name): static {
$clone = clone $this;
unset($clone->attributes[$name]);
return $clone;
}
};
}
/**
* Create a Config instance from a nested data array.
*
* Returns a real Grav\Common\Config\Config (or our stub equivalent).
*/
public static function createMockConfig(array $data = []): Config
{
return new Config($data);
}
/**
* Create a mock user that duck-types Grav\Common\User\Interfaces\UserInterface.
*
* The returned object has a public $username property and supports
* get(), set(), save(), and exists().
*/
public static function createMockUser(
string $username = 'testuser',
array $data = [],
bool $exists = true,
): UserInterface {
return new class ($username, $data, $exists) implements UserInterface {
public readonly string $username;
public function __construct(
string $username,
private array $data,
private readonly bool $existsFlag,
) {
$this->username = $username;
}
public function get(string $key, mixed $default = null): mixed
{
return $this->data[$key] ?? $default;
}
public function set(string $key, mixed $value): void
{
$this->data[$key] = $value;
}
public function save(): void
{
// no-op in tests
}
public function exists(): bool
{
return $this->existsFlag;
}
};
}
/**
* Create a Grav container instance with given services.
*
* Returns the Grav singleton (reset between calls).
*/
public static function createMockGrav(array $services = []): Grav
{
Grav::resetInstance();
$grav = Grav::instance();
foreach ($services as $key => $value) {
$grav[$key] = $value;
}
return $grav;
}
/**
* Create a mock accounts collection that is iterable and supports load().
*/
public static function createMockAccounts(array $users = []): object
{
return new class ($users) implements \IteratorAggregate {
/** @param array<string, object> $users keyed by username */
public function __construct(private readonly array $users) {}
public function getIterator(): \ArrayIterator
{
return new \ArrayIterator($this->users);
}
public function load(string $username): object
{
return $this->users[$username] ?? self::nonExistentUser($username);
}
private static function nonExistentUser(string $username): object
{
return new class ($username) {
public readonly string $username;
public function __construct(string $username)
{
$this->username = $username;
}
public function exists(): bool { return false; }
public function get(string $key, mixed $default = null): mixed { return $default; }
};
}
};
}
}