Expanded test coverage.
This commit is contained in:
@@ -24,6 +24,7 @@ use Psr\EventDispatcher\EventDispatcherInterface;
|
||||
use Psr\Http\Message\UriInterface;
|
||||
use Psr\Log\LoggerInterface as iLogger;
|
||||
use Psr\SimpleCache\CacheInterface;
|
||||
use Symfony\Component\Cache\Adapter\ArrayAdapter;
|
||||
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
|
||||
use Symfony\Component\Cache\Adapter\NullAdapter;
|
||||
use Symfony\Component\Cache\Adapter\RedisAdapter;
|
||||
@@ -121,6 +122,10 @@ return (function (): array {
|
||||
return new Psr16Cache(new NullAdapter());
|
||||
}
|
||||
|
||||
if (true === (defined('IN_TEST_MODE') && true === IN_TEST_MODE)) {
|
||||
return new Psr16Cache(new ArrayAdapter());
|
||||
}
|
||||
|
||||
$ns = getAppVersion();
|
||||
|
||||
if (null !== ($prefix = Config::get('cache.prefix')) && true === isValidName($prefix)) {
|
||||
|
||||
@@ -103,7 +103,7 @@ final class ReportCommand extends Command
|
||||
$output->writeln(
|
||||
r('Is the tasks runner working? <flag>{answer}</flag>', [
|
||||
'answer' => (function () {
|
||||
$info = isTaskWorkerRunning(true);
|
||||
$info = isTaskWorkerRunning(ignoreContainer: true);
|
||||
return r("{status} '{container}' - {message}", [
|
||||
'status' => $info['status'] ? 'Yes' : 'No',
|
||||
'message' => $info['message'],
|
||||
|
||||
@@ -1539,10 +1539,6 @@ if (!function_exists('parseEnvFile')) {
|
||||
}
|
||||
|
||||
foreach (file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) as $line) {
|
||||
if (empty($line)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (true === str_starts_with($line, '#') || false === str_contains($line, '=')) {
|
||||
continue;
|
||||
}
|
||||
@@ -1611,11 +1607,12 @@ if (!function_exists('isTaskWorkerRunning')) {
|
||||
/**
|
||||
* Check if the task worker is running. This function is only available when running in a container.
|
||||
*
|
||||
* @param string $pidFile (Optional) The PID file to check.
|
||||
* @param bool $ignoreContainer (Optional) Whether to ignore the container check.
|
||||
*
|
||||
* @return array{ status: bool, message: string }
|
||||
*/
|
||||
function isTaskWorkerRunning(bool $ignoreContainer = false): array
|
||||
function isTaskWorkerRunning(string $pidFile = '/tmp/ws-job-runner.pid', bool $ignoreContainer = false): array
|
||||
{
|
||||
if (false === $ignoreContainer && !inContainer()) {
|
||||
return [
|
||||
@@ -1633,8 +1630,6 @@ if (!function_exists('isTaskWorkerRunning')) {
|
||||
];
|
||||
}
|
||||
|
||||
$pidFile = '/tmp/ws-job-runner.pid';
|
||||
|
||||
if (!file_exists($pidFile)) {
|
||||
return [
|
||||
'status' => false,
|
||||
@@ -1649,7 +1644,33 @@ if (!function_exists('isTaskWorkerRunning')) {
|
||||
return ['status' => false, 'message' => $e->getMessage()];
|
||||
}
|
||||
|
||||
if (file_exists(r('/proc/{pid}/status', ['pid' => $pid]))) {
|
||||
switch (PHP_OS) {
|
||||
case 'Linux':
|
||||
{
|
||||
$status = file_exists(r('/proc/{pid}/status', ['pid' => $pid]));
|
||||
}
|
||||
break;
|
||||
case 'WINNT':
|
||||
{
|
||||
// -- Windows does not have a /proc directory so we need different way to get the status.
|
||||
@exec("tasklist /FI \"PID eq {$pid}\" 2>NUL", $output);
|
||||
// -- windows doesn't return 0 if the process is not found. we need to parse the output.
|
||||
$status = false;
|
||||
foreach ($output as $line) {
|
||||
if (false === str_contains($line, $pid)) {
|
||||
continue;
|
||||
}
|
||||
$status = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
$status = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (true === $status) {
|
||||
return ['status' => true, 'restartable' => true, 'message' => 'Task worker is running.'];
|
||||
}
|
||||
|
||||
@@ -1863,6 +1884,7 @@ if (!function_exists('cacheableItem')) {
|
||||
* @param Closure $function
|
||||
* @param DateInterval|int|null $ttl
|
||||
* @param bool $ignoreCache
|
||||
* @param array $opts
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
@@ -1870,15 +1892,16 @@ if (!function_exists('cacheableItem')) {
|
||||
string $key,
|
||||
Closure $function,
|
||||
DateInterval|int|null $ttl = null,
|
||||
bool $ignoreCache = false
|
||||
bool $ignoreCache = false,
|
||||
array $opts = [],
|
||||
): mixed {
|
||||
$cache = Container::get(iCache::class);
|
||||
$cache = $opts[iCache::class] ?? Container::get(iCache::class);
|
||||
|
||||
if (!$ignoreCache && $cache->has($key)) {
|
||||
return $cache->get($key);
|
||||
}
|
||||
|
||||
$reflectContainer = Container::get(ReflectionContainer::class);
|
||||
$reflectContainer = $opts[ReflectionContainer::class] ?? Container::get(ReflectionContainer::class);
|
||||
$item = $reflectContainer->call($function);
|
||||
|
||||
if (null === $ttl) {
|
||||
@@ -2048,8 +2071,9 @@ if (!function_exists('getBackend')) {
|
||||
|
||||
$default = $configFile->get($name);
|
||||
$default['name'] = $name;
|
||||
$data = array_replace_recursive($default, $config);
|
||||
|
||||
return makeBackend(array_replace_recursive($default, $config), $name);
|
||||
return makeBackend($data, $name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2094,3 +2118,26 @@ if (!function_exists('lw')) {
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('timeIt')) {
|
||||
/**
|
||||
* Time the execution of a function.
|
||||
*
|
||||
* @param Closure $function The function to time.
|
||||
* @param string $name The name of the function.
|
||||
* @param int $round (Optional) The number of decimal places to round to.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function timeIt(Closure $function, string $name, int $round = 6): string
|
||||
{
|
||||
$start = microtime(true);
|
||||
$function();
|
||||
$end = microtime(true);
|
||||
|
||||
return r("Execution time is '{time}' for '{name}'", [
|
||||
'name' => $name,
|
||||
'time' => round($end - $start, $round),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
0
tests/Fixtures/local_data/fanart.png
Normal file
0
tests/Fixtures/local_data/fanart.png
Normal file
0
tests/Fixtures/local_data/poster.jpg
Normal file
0
tests/Fixtures/local_data/poster.jpg
Normal file
1
tests/Fixtures/local_data/test.mkv
Normal file
1
tests/Fixtures/local_data/test.mkv
Normal file
@@ -0,0 +1 @@
|
||||
0
|
||||
0
tests/Fixtures/local_data/test.png
Normal file
0
tests/Fixtures/local_data/test.png
Normal file
1
tests/Fixtures/local_data/test.srt
Normal file
1
tests/Fixtures/local_data/test.srt
Normal file
@@ -0,0 +1 @@
|
||||
0
|
||||
62
tests/Fixtures/test_servers.yaml
Normal file
62
tests/Fixtures/test_servers.yaml
Normal file
@@ -0,0 +1,62 @@
|
||||
test_plex:
|
||||
type: plex
|
||||
url: 'https://plex.example.invalid'
|
||||
token: t000000000000000000p
|
||||
user: 11111111
|
||||
uuid: s00000000000000000000000000000000000000p
|
||||
export:
|
||||
enabled: true
|
||||
lastSync: null
|
||||
import:
|
||||
enabled: true
|
||||
lastSync: 1724173445
|
||||
webhook:
|
||||
match:
|
||||
user: true
|
||||
uuid: true
|
||||
options:
|
||||
ignore: '22,1,2,3'
|
||||
LIBRARY_SEGMENT: 1000
|
||||
ADMIN_TOKEN: plex_admin_token
|
||||
use_old_progress_endpoint: true
|
||||
plex_user_uuid: r00000000000000p
|
||||
|
||||
test_jellyfin:
|
||||
type: jellyfin
|
||||
url: 'https://jellyfin.example.invalid'
|
||||
token: t000000000000000000000000000000j
|
||||
user: u000000000000000000000000000000j
|
||||
uuid: s000000000000000000000000000000j
|
||||
export:
|
||||
enabled: true
|
||||
lastSync: null
|
||||
import:
|
||||
enabled: true
|
||||
lastSync: 1724173445
|
||||
webhook:
|
||||
match:
|
||||
user: false
|
||||
uuid: true
|
||||
options:
|
||||
ignore: 'i000000000000000000000000000000j,i100000000000000000000000000000j'
|
||||
MAX_EPISODE_RANGE: 6
|
||||
|
||||
test_emby:
|
||||
type: emby
|
||||
url: 'https://emby.example.invalid'
|
||||
token: t000000000000000000000000000000e
|
||||
user: u000000000000000000000000000000e
|
||||
uuid: s000000000000000000000000000000e
|
||||
import:
|
||||
enabled: true
|
||||
lastSync: 1724173445
|
||||
export:
|
||||
enabled: true
|
||||
lastSync: null
|
||||
webhook:
|
||||
match:
|
||||
user: false
|
||||
uuid: true
|
||||
options:
|
||||
MAX_EPISODE_RANGE: 6
|
||||
IMPORT_METADATA_ONLY: true
|
||||
@@ -6,11 +6,16 @@ namespace Tests\Libs;
|
||||
|
||||
use App\Backends\Plex\PlexClient;
|
||||
use App\Libs\Config;
|
||||
use App\Libs\Container;
|
||||
use App\Libs\Database\DBLayer;
|
||||
use App\Libs\Entity\StateEntity;
|
||||
use App\Libs\Enums\Http\Method;
|
||||
use App\Libs\Enums\Http\Status;
|
||||
use App\Libs\Exceptions\AppExceptionInterface;
|
||||
use App\Libs\Exceptions\DBLayerException;
|
||||
use App\Libs\Exceptions\InvalidArgumentException;
|
||||
use App\Libs\Exceptions\RuntimeException;
|
||||
use App\Libs\Extends\ReflectionContainer;
|
||||
use App\Libs\TestCase;
|
||||
use JsonMachine\Items;
|
||||
use JsonMachine\JsonDecoder\ErrorWrappingDecoder;
|
||||
@@ -23,10 +28,84 @@ use Psr\SimpleCache\CacheInterface;
|
||||
use Stringable;
|
||||
use Symfony\Component\HttpClient\MockHttpClient;
|
||||
use Symfony\Component\HttpClient\Response\MockResponse;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
use TypeError;
|
||||
|
||||
class HelpersTest extends TestCase
|
||||
{
|
||||
protected CacheInterface|null $cache = null;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->cache = new class implements CacheInterface {
|
||||
public array $cache = [];
|
||||
public bool $throw = false;
|
||||
|
||||
public function get(string $key, mixed $default = null): mixed
|
||||
{
|
||||
return $this->cache[$key] ?? $default;
|
||||
}
|
||||
|
||||
public function set(string $key, mixed $value, \DateInterval|int|null $ttl = null): bool
|
||||
{
|
||||
if ($this->throw) {
|
||||
throw new class() extends \InvalidArgumentException implements
|
||||
\Psr\SimpleCache\InvalidArgumentException {
|
||||
};
|
||||
}
|
||||
|
||||
$this->cache[$key] = $value;
|
||||
return true;
|
||||
}
|
||||
|
||||
public function delete(string $key): bool
|
||||
{
|
||||
unset($this->cache[$key]);
|
||||
return true;
|
||||
}
|
||||
|
||||
public function clear(): bool
|
||||
{
|
||||
$this->cache = [];
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getMultiple(iterable $keys, mixed $default = null): iterable
|
||||
{
|
||||
foreach ($keys as $key) {
|
||||
yield $key => $this->get($key, $default);
|
||||
}
|
||||
}
|
||||
|
||||
public function setMultiple(iterable $values, \DateInterval|int|null $ttl = null): bool
|
||||
{
|
||||
foreach ($values as $key => $value) {
|
||||
$this->set($key, $value, $ttl);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function deleteMultiple(iterable $keys): bool
|
||||
{
|
||||
foreach ($keys as $key) {
|
||||
$this->delete($key);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function has(string $key): bool
|
||||
{
|
||||
return isset($this->cache[$key]);
|
||||
}
|
||||
|
||||
public function reset(): void
|
||||
{
|
||||
$this->cache = [];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public function test_env_conditions(): void
|
||||
{
|
||||
$values = [
|
||||
@@ -925,111 +1004,48 @@ class HelpersTest extends TestCase
|
||||
|
||||
public function test_generateRoutes()
|
||||
{
|
||||
$class = new class implements CacheInterface {
|
||||
public array $cache = [];
|
||||
public bool $throw = false;
|
||||
$routes = generateRoutes('cli', [CacheInterface::class => $this->cache]);
|
||||
|
||||
public function get(string $key, mixed $default = null): mixed
|
||||
{
|
||||
return $this->cache[$key] ?? $default;
|
||||
}
|
||||
|
||||
public function set(string $key, mixed $value, \DateInterval|int|null $ttl = null): bool
|
||||
{
|
||||
if ($this->throw) {
|
||||
throw new class() extends \InvalidArgumentException implements
|
||||
\Psr\SimpleCache\InvalidArgumentException {
|
||||
};
|
||||
}
|
||||
|
||||
$this->cache[$key] = $value;
|
||||
return true;
|
||||
}
|
||||
|
||||
public function delete(string $key): bool
|
||||
{
|
||||
unset($this->cache[$key]);
|
||||
return true;
|
||||
}
|
||||
|
||||
public function clear(): bool
|
||||
{
|
||||
$this->cache = [];
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getMultiple(iterable $keys, mixed $default = null): iterable
|
||||
{
|
||||
foreach ($keys as $key) {
|
||||
yield $key => $this->get($key, $default);
|
||||
}
|
||||
}
|
||||
|
||||
public function setMultiple(iterable $values, \DateInterval|int|null $ttl = null): bool
|
||||
{
|
||||
foreach ($values as $key => $value) {
|
||||
$this->set($key, $value, $ttl);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function deleteMultiple(iterable $keys): bool
|
||||
{
|
||||
foreach ($keys as $key) {
|
||||
$this->delete($key);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function has(string $key): bool
|
||||
{
|
||||
return isset($this->cache[$key]);
|
||||
}
|
||||
|
||||
public function reset(): void
|
||||
{
|
||||
$this->cache = [];
|
||||
}
|
||||
};
|
||||
|
||||
$routes = generateRoutes('cli', [CacheInterface::class => $class]);
|
||||
|
||||
$this->assertCount(2, $class->cache, 'It should have generated two cache buckets for http and cli routes.');
|
||||
$this->assertCount(
|
||||
2,
|
||||
$this->cache->cache,
|
||||
'It should have generated two cache buckets for http and cli routes.'
|
||||
);
|
||||
$this->assertGreaterThanOrEqual(
|
||||
1,
|
||||
count($class->cache['routes_cli']),
|
||||
count($this->cache->cache['routes_cli']),
|
||||
'It should have more than 1 route for cli routes.'
|
||||
);
|
||||
$this->assertGreaterThanOrEqual(
|
||||
1,
|
||||
count($class->cache['routes_http']),
|
||||
count($this->cache->cache['routes_http']),
|
||||
'It should have more than 1 route for cli routes.'
|
||||
);
|
||||
|
||||
$this->assertSame(
|
||||
$routes,
|
||||
$class->cache['routes_cli'],
|
||||
$this->cache->cache['routes_cli'],
|
||||
'It should return cli routes when called with cli type.'
|
||||
);
|
||||
|
||||
$class->reset();
|
||||
$this->cache->reset();
|
||||
|
||||
$this->assertSame(
|
||||
generateRoutes('http', [CacheInterface::class => $class]),
|
||||
$class->cache['routes_http'],
|
||||
generateRoutes('http', [CacheInterface::class => $this->cache]),
|
||||
$this->cache->cache['routes_http'],
|
||||
'It should return http routes. when called with http type.'
|
||||
);
|
||||
|
||||
$class->reset();
|
||||
$class->throw = true;
|
||||
$routes = generateRoutes('http', [CacheInterface::class => $class]);
|
||||
$this->assertCount(0, $class->cache, 'When cache throws exception, it should not save anything.');
|
||||
$this->cache->reset();
|
||||
$this->cache->throw = true;
|
||||
$routes = generateRoutes('http', [CacheInterface::class => $this->cache]);
|
||||
$this->assertCount(0, $this->cache->cache, 'When cache throws exception, it should not save anything.');
|
||||
$this->assertNotSame([], $routes, 'Routes should be generated even if cache throws exception.');
|
||||
|
||||
// --
|
||||
$save = Config::get('supported', []);
|
||||
Config::save('supported', ['not_set' => 'not_set_client', 'plex' => PlexClient::class,]);
|
||||
$routes = generateRoutes('http', [CacheInterface::class => $class]);
|
||||
$routes = generateRoutes('http', [CacheInterface::class => $this->cache]);
|
||||
Config::save('supported', $save);
|
||||
}
|
||||
|
||||
@@ -1280,4 +1296,335 @@ class HelpersTest extends TestCase
|
||||
);
|
||||
$this->assertSame('finally_was_called', $f, 'finally block should be executed.');
|
||||
}
|
||||
|
||||
public function test_getServerColumnSpec()
|
||||
{
|
||||
$this->assertSame(
|
||||
[
|
||||
'key' => 'user',
|
||||
'type' => 'string',
|
||||
'visible' => true,
|
||||
'description' => 'The user ID of the backend.',
|
||||
],
|
||||
getServerColumnSpec('user'),
|
||||
'It should return correct column spec.'
|
||||
);
|
||||
|
||||
$this->assertSame([], getServerColumnSpec('not_set'), 'It should return empty array when column is not set.');
|
||||
}
|
||||
|
||||
public function test_getEnvSpec()
|
||||
{
|
||||
$this->assertSame(
|
||||
[
|
||||
'key' => 'WS_DATA_PATH',
|
||||
'description' => 'Where to store main data. (config, db).',
|
||||
'type' => 'string',
|
||||
],
|
||||
getEnvSpec('WS_DATA_PATH'),
|
||||
'It should return correct env spec.'
|
||||
);
|
||||
|
||||
$this->assertSame([], getEnvSpec('not_set'), 'It should return empty array when env is not set.');
|
||||
}
|
||||
|
||||
public function test_isTaskWorkerRunning()
|
||||
{
|
||||
$_ENV['IN_CONTAINER'] = false;
|
||||
$d = isTaskWorkerRunning();
|
||||
$this->assertTrue($d['status'], 'When not in container, and $ignoreContainer is false, it should return true.');
|
||||
unset($_ENV['IN_CONTAINER']);
|
||||
|
||||
$_ENV['DISABLE_CRON'] = true;
|
||||
$d = isTaskWorkerRunning(ignoreContainer: true);
|
||||
$this->assertFalse($d['status'], 'When DISABLE_CRON is set, it should return false.');
|
||||
unset($_ENV['DISABLE_CRON']);
|
||||
|
||||
$d = isTaskWorkerRunning(pidFile: __DIR__ . '/../Fixtures/worker.pid', ignoreContainer: true);
|
||||
$this->assertFalse($d['status'], 'When pid file is not found, it should return false.');
|
||||
|
||||
$tmpFile = tempnam(sys_get_temp_dir(), 'worker');
|
||||
try {
|
||||
file_put_contents($tmpFile, getmypid());
|
||||
$d = isTaskWorkerRunning(pidFile: $tmpFile, ignoreContainer: true);
|
||||
$this->assertTrue($d['status'], 'When pid file is found, and process exists it should return true.');
|
||||
} finally {
|
||||
if (file_exists($tmpFile)) {
|
||||
unlink($tmpFile);
|
||||
}
|
||||
}
|
||||
|
||||
$tmpFile = tempnam(sys_get_temp_dir(), 'worker');
|
||||
try {
|
||||
/** @noinspection PhpUnhandledExceptionInspection */
|
||||
file_put_contents($tmpFile, random_int(1, 9999) . getmypid());
|
||||
$d = isTaskWorkerRunning(pidFile: $tmpFile, ignoreContainer: true);
|
||||
$this->assertFalse(
|
||||
$d['status'],
|
||||
'When pid file is found, and process does not exists it should return false.'
|
||||
);
|
||||
} finally {
|
||||
if (file_exists($tmpFile)) {
|
||||
unlink($tmpFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function test_findSideCarFiles()
|
||||
{
|
||||
$n = new \SplFileInfo(__DIR__ . '/../Fixtures/local_data/test.mkv');
|
||||
$this->assertCount(
|
||||
4,
|
||||
findSideCarFiles($n),
|
||||
'It should return side car files for given file.'
|
||||
);
|
||||
}
|
||||
|
||||
public function test_array_change_key_case_recursive()
|
||||
{
|
||||
$array = [
|
||||
'foo' => 'bar',
|
||||
'baz' => 'taz',
|
||||
'kaz' => [
|
||||
'raz' => 'maz',
|
||||
'naz' => 'laz',
|
||||
],
|
||||
];
|
||||
|
||||
$expected = [
|
||||
'FOO' => 'bar',
|
||||
'BAZ' => 'taz',
|
||||
'KAZ' => [
|
||||
'RAZ' => 'maz',
|
||||
'NAZ' => 'laz',
|
||||
],
|
||||
];
|
||||
|
||||
$this->assertSame(
|
||||
$expected,
|
||||
array_change_key_case_recursive($array, CASE_UPPER),
|
||||
'It should change keys case.'
|
||||
);
|
||||
|
||||
$this->assertSame(
|
||||
$array,
|
||||
array_change_key_case_recursive($expected, CASE_LOWER),
|
||||
'It should change keys case.'
|
||||
);
|
||||
|
||||
$this->expectException(RuntimeException::class);
|
||||
array_change_key_case_recursive($array, 999);
|
||||
}
|
||||
|
||||
public function test_getMimeType()
|
||||
{
|
||||
$this->assertSame(
|
||||
'application/json',
|
||||
getMimeType(__DIR__ . '/../Fixtures/plex_data.json'),
|
||||
'It should return correct mime type.'
|
||||
);
|
||||
}
|
||||
|
||||
public function test_getExtension()
|
||||
{
|
||||
$this->assertSame(
|
||||
'json',
|
||||
getExtension(__DIR__ . '/../Fixtures/plex_data.json'),
|
||||
'It should return correct extension.'
|
||||
);
|
||||
}
|
||||
|
||||
public function test_generateUUID()
|
||||
{
|
||||
#1ef6d04c-23c3-6442-9fd5-c87f54c3d8d1
|
||||
$this->assertMatchesRegularExpression(
|
||||
'/^[0-9a-f]{8}-[0-9a-f]{4}-6[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/',
|
||||
generateUUID(),
|
||||
'It should match valid UUID6 pattern.'
|
||||
);
|
||||
|
||||
$this->assertMatchesRegularExpression(
|
||||
'/^test\-[0-9a-f]{8}-[0-9a-f]{4}-6[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/',
|
||||
generateUUID('test'),
|
||||
'It should match valid UUID6 pattern.'
|
||||
);
|
||||
}
|
||||
|
||||
public function test_cacheableItem()
|
||||
{
|
||||
$reflectContainer = new class() {
|
||||
public function call(callable $callable, array $args = []): mixed
|
||||
{
|
||||
return $callable(...$args);
|
||||
}
|
||||
};
|
||||
|
||||
$item = fn() => cacheableItem(
|
||||
key: 'test',
|
||||
function: fn() => 'foo',
|
||||
ignoreCache: false,
|
||||
opts: [
|
||||
CacheInterface::class => $this->cache,
|
||||
ReflectionContainer::class => $reflectContainer,
|
||||
]
|
||||
);
|
||||
|
||||
$this->assertSame('foo', $item(), 'It should return correct value.');
|
||||
$this->assertSame('foo', $item(), 'It should return correct value.');
|
||||
}
|
||||
|
||||
public function test_getPagination()
|
||||
{
|
||||
$factory = new Psr17Factory();
|
||||
$creator = new ServerRequestCreator($factory, $factory, $factory, $factory);
|
||||
|
||||
$request = $creator->fromArrays([
|
||||
'REQUEST_METHOD' => 'GET',
|
||||
'QUERY_STRING' => 'page=2&perpage=10'
|
||||
], get: ['page' => 2, 'perpage' => 10]);
|
||||
|
||||
[$page, $perpage, $start] = getPagination($request, 1);
|
||||
|
||||
$this->assertSame(2, $page, 'It should return correct page number.');
|
||||
$this->assertSame(10, $perpage, 'It should return correct perpage number.');
|
||||
$this->assertSame(10, $start, 'It should return correct start number.');
|
||||
}
|
||||
|
||||
public function test_getBackend()
|
||||
{
|
||||
Container::init();
|
||||
Config::init(require __DIR__ . '/../../config/config.php');
|
||||
foreach ((array)require __DIR__ . '/../../config/services.php' as $name => $definition) {
|
||||
Container::add($name, $definition);
|
||||
}
|
||||
Config::save('backends_file', __DIR__ . '/../Fixtures/test_servers.yaml');
|
||||
|
||||
$this->assertInstanceOf(
|
||||
PlexClient::class,
|
||||
getBackend('test_plex'),
|
||||
'It should return correct backend client.'
|
||||
);
|
||||
|
||||
$this->expectException(RuntimeException::class);
|
||||
getBackend('not_set');
|
||||
}
|
||||
|
||||
public function test_makeBackend()
|
||||
{
|
||||
Container::init();
|
||||
Config::init(require __DIR__ . '/../../config/config.php');
|
||||
foreach ((array)require __DIR__ . '/../../config/services.php' as $name => $definition) {
|
||||
Container::add($name, $definition);
|
||||
}
|
||||
Config::save('backends_file', __DIR__ . '/../Fixtures/test_servers.yaml');
|
||||
|
||||
$exception = null;
|
||||
try {
|
||||
makeBackend([], 'foo');
|
||||
} catch (\Throwable $e) {
|
||||
$exception = $e;
|
||||
} finally {
|
||||
$this->assertInstanceOf(
|
||||
InvalidArgumentException::class,
|
||||
$exception,
|
||||
'Should throw exception when no type is given.'
|
||||
);
|
||||
$this->assertStringContainsString('No backend type was set.', $exception?->getMessage());
|
||||
}
|
||||
|
||||
$exception = null;
|
||||
try {
|
||||
makeBackend(['type' => 'plex'], 'foo');
|
||||
} catch (\Throwable $e) {
|
||||
$exception = $e;
|
||||
} finally {
|
||||
$this->assertInstanceOf(
|
||||
InvalidArgumentException::class,
|
||||
$exception,
|
||||
'Should throw exception when no url is given.'
|
||||
);
|
||||
$this->assertStringContainsString('No backend url was set.', $exception?->getMessage());
|
||||
}
|
||||
|
||||
$exception = null;
|
||||
try {
|
||||
makeBackend(['type' => 'far', 'url' => 'http://test.example.invalid'], 'foo');
|
||||
} catch (\Throwable $e) {
|
||||
$exception = $e;
|
||||
} finally {
|
||||
$this->assertInstanceOf(
|
||||
InvalidArgumentException::class,
|
||||
$exception,
|
||||
'Should throw exception when no type is not supported.'
|
||||
);
|
||||
$this->assertStringContainsString('Unexpected client type', $exception?->getMessage());
|
||||
}
|
||||
|
||||
$data = Yaml::parseFile(__DIR__ . '/../Fixtures/test_servers.yaml');
|
||||
|
||||
$this->assertInstanceOf(
|
||||
PlexClient::class,
|
||||
makeBackend($data['test_plex'], 'test_plex'),
|
||||
'It should return correct backend client.'
|
||||
);
|
||||
}
|
||||
|
||||
public function test_lw()
|
||||
{
|
||||
$exception = new RuntimeException();
|
||||
$exception->addContext('foo', 'bar');
|
||||
|
||||
$this->assertSame(
|
||||
[AppExceptionInterface::class => ['foo' => 'bar']],
|
||||
lw('test', [], $exception)['context'],
|
||||
'it should return the added AppContext'
|
||||
);
|
||||
$this->assertSame(
|
||||
['bar' => 'foo'],
|
||||
lw('test', ['bar' => 'foo'], new \RuntimeException())['context'],
|
||||
'If exception is not AppExceptionInterface, it should return same data.'
|
||||
);
|
||||
|
||||
$exception = new DBLayerException();
|
||||
/** @noinspection SqlResolve */
|
||||
$exception->setInfo('SELECT * FROM foo WHERE id = :id', ['id' => 1], [], 122);
|
||||
/** @noinspection SqlResolve */
|
||||
$this->assertSame(
|
||||
[
|
||||
'bar' => 'foo',
|
||||
DBLayer::class => [
|
||||
'query' => 'SELECT * FROM foo WHERE id = :id',
|
||||
'bind' => ['id' => 1],
|
||||
'error' => [],
|
||||
],
|
||||
],
|
||||
lw('test', ['bar' => 'foo'], $exception)['context'],
|
||||
'If exception is not AppExceptionInterface, it should return same data.'
|
||||
);
|
||||
|
||||
$this->assertSame(
|
||||
['bar' => 'foo'],
|
||||
lw('test', ['bar' => 'foo'])['context'],
|
||||
'If no exception is given, it should return same data.'
|
||||
);
|
||||
}
|
||||
|
||||
public function test_commandContext()
|
||||
{
|
||||
$_ENV['IN_CONTAINER'] = true;
|
||||
$this->assertSame(
|
||||
'docker exec -ti watchstate console',
|
||||
trim(commandContext()),
|
||||
'It should return correct command context. When in container.'
|
||||
);
|
||||
unset($_ENV['IN_CONTAINER']);
|
||||
|
||||
$_ENV['IN_CONTAINER'] = false;
|
||||
$this->assertSame(
|
||||
$_SERVER['argv'][0] ?? 'php bin/console',
|
||||
trim(commandContext()),
|
||||
'If not in container, it should return argv[0] or defaults to php bin/console.'
|
||||
);
|
||||
unset($_ENV['IN_CONTAINER']);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
if (false === defined('IN_TEST_MODE')) {
|
||||
define('IN_TEST_MODE', true);
|
||||
}
|
||||
|
||||
require __DIR__ . '/../pre_init.php';
|
||||
|
||||
if (!file_exists(__DIR__ . '/../vendor/autoload.php')) {
|
||||
|
||||
Reference in New Issue
Block a user