Added more tests.
This commit is contained in:
@@ -14,7 +14,7 @@
|
||||
"test": "vendor/bin/phpunit"
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.1",
|
||||
"php": ">=8.2",
|
||||
"ext-pdo": "*",
|
||||
"ext-pdo_sqlite": "*",
|
||||
"ext-mbstring": "*",
|
||||
|
||||
@@ -8,5 +8,13 @@
|
||||
<exclude>./tests/Mappers/Import.php</exclude>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<coverage>
|
||||
<include>
|
||||
<directory suffix=".php">src</directory>
|
||||
</include>
|
||||
<exclude>
|
||||
<directory>tests</directory>
|
||||
</exclude>
|
||||
</coverage>
|
||||
<logging/>
|
||||
</phpunit>
|
||||
|
||||
@@ -65,7 +65,12 @@ final class Cache implements Countable
|
||||
|
||||
public function remove(string $key): bool
|
||||
{
|
||||
return ag_exists($this->data, $key);
|
||||
if (false === ag_exists($this->data, $key)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->data = ag_delete($this->data, $key);
|
||||
return true;
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
|
||||
295
tests/Backends/Common/CacheTest.php
Normal file
295
tests/Backends/Common/CacheTest.php
Normal file
@@ -0,0 +1,295 @@
|
||||
<?php
|
||||
/** @noinspection PhpMultipleClassDeclarationsInspection */
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Backends\Common;
|
||||
|
||||
use App\Backends\Common\Cache;
|
||||
use App\Libs\TestCase;
|
||||
use Monolog\Handler\TestHandler;
|
||||
use Monolog\Logger;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Psr\SimpleCache\CacheInterface;
|
||||
use stdClass;
|
||||
use Symfony\Component\Cache\Adapter\ArrayAdapter;
|
||||
use Symfony\Component\Cache\CacheItem;
|
||||
use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Cache\Psr16Cache;
|
||||
|
||||
class CacheTest extends TestCase
|
||||
{
|
||||
private Cache|null $cache = null;
|
||||
private PSRCache|null $psrCache = null;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->psrCache = new PSRCache();
|
||||
|
||||
$this->cache = new Cache(
|
||||
logger: new Logger('test'),
|
||||
cache: $this->psrCache,
|
||||
);
|
||||
$this->cache = $this->cache->withData('namespace', []);
|
||||
$this->handler = new TestHandler();
|
||||
$this->logger = new Logger('logger');
|
||||
$this->logger->pushHandler($this->handler);
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
public function test_backend_cache_get(): void
|
||||
{
|
||||
$obj = new StdClass();
|
||||
$obj->foo = 'bar';
|
||||
$obj->bar = 'baz';
|
||||
|
||||
$types = [
|
||||
'foo' => 'bar',
|
||||
'bar.baz' => 1,
|
||||
'bar.bar.kaz' => 1.1,
|
||||
'taz' => true,
|
||||
'faz' => false,
|
||||
'nay.foo' => null,
|
||||
'arr.a.y' => ['foo' => 'bar'],
|
||||
'o.b.j.e.c.t' => $obj,
|
||||
];
|
||||
|
||||
foreach ($types as $key => $value) {
|
||||
$this->cache->set($key, $value);
|
||||
$this->assertEquals(
|
||||
$value,
|
||||
$this->cache->get($key),
|
||||
'Assert value type is preserved'
|
||||
);
|
||||
}
|
||||
|
||||
$this->assertEquals(
|
||||
'default_value',
|
||||
$this->cache->get('non_set', 'default_value'),
|
||||
'Assert default value is returned when key is not found'
|
||||
);
|
||||
}
|
||||
|
||||
public function test_backend_cache_has(): void
|
||||
{
|
||||
$this->cache->set('key_name', 'value');
|
||||
$this->assertTrue(
|
||||
$this->cache->has('key_name'),
|
||||
'Assert key exists'
|
||||
);
|
||||
|
||||
$this->assertFalse(
|
||||
$this->cache->has('non_set'),
|
||||
'Assert key does not exist'
|
||||
);
|
||||
}
|
||||
|
||||
public function test_backend_cache_set(): void
|
||||
{
|
||||
$this->cache->set('foo.bar', 'value');
|
||||
$this->assertTrue(
|
||||
$this->cache->has('foo.bar'),
|
||||
'Assert key exists'
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
'value',
|
||||
$this->cache->get('foo.bar'),
|
||||
'assert returned value is correct'
|
||||
);
|
||||
}
|
||||
|
||||
public function test_backend_cache_remove(): void
|
||||
{
|
||||
$this->cache->set('foo.bar', 'value');
|
||||
$this->assertTrue(
|
||||
$this->cache->has('foo.bar'),
|
||||
'Assert key exists'
|
||||
);
|
||||
|
||||
$this->assertTrue(
|
||||
$this->cache->remove('foo.bar'),
|
||||
'Assert remove() true is returned when key exists and is removed'
|
||||
);
|
||||
|
||||
$this->assertFalse(
|
||||
$this->cache->remove('foo.taz'),
|
||||
'Assert remove() false is returned when key does not exist'
|
||||
);
|
||||
|
||||
$this->assertFalse(
|
||||
$this->cache->has('foo.bar'),
|
||||
'Assert key does not exist'
|
||||
);
|
||||
}
|
||||
|
||||
public function test_backend_cache_total(): void
|
||||
{
|
||||
$this->assertCount(
|
||||
0,
|
||||
$this->cache,
|
||||
'Assert cache is empty'
|
||||
);
|
||||
|
||||
$this->cache->set('foo.bar', 'value');
|
||||
$this->assertCount(
|
||||
1,
|
||||
$this->cache,
|
||||
'Assert cache has 1 item'
|
||||
);
|
||||
|
||||
$this->cache->set('foo.baz', 'value');
|
||||
$this->assertCount(
|
||||
1,
|
||||
$this->cache,
|
||||
'Assert count still at 1 when adding another item with same parent key'
|
||||
);
|
||||
|
||||
$this->cache->set('bar', 'value');
|
||||
$this->assertCount(
|
||||
2,
|
||||
$this->cache,
|
||||
'There should be 2 items in the cache'
|
||||
);
|
||||
|
||||
$this->cache->remove('foo.bar');
|
||||
$this->assertCount(
|
||||
2,
|
||||
$this->cache,
|
||||
'Assert count still at 2 when removing a single item from a parent key'
|
||||
);
|
||||
|
||||
$this->cache->remove('foo');
|
||||
$this->cache->remove('bar');
|
||||
$this->assertCount(
|
||||
0,
|
||||
$this->cache,
|
||||
'Assert cache is empty after removing all items'
|
||||
);
|
||||
}
|
||||
|
||||
public function test_backend_cache_withData_exception_handling(): void
|
||||
{
|
||||
$c = new Cache(
|
||||
logger: $this->logger,
|
||||
cache: new Psr16Cache(
|
||||
new class() extends ArrayAdapter {
|
||||
public function getItem(mixed $key): CacheItem
|
||||
{
|
||||
throw new InvalidArgumentException('foo');
|
||||
}
|
||||
}
|
||||
),
|
||||
);
|
||||
|
||||
$c->withData('namespace', []);
|
||||
|
||||
$this->assertStringContainsString(
|
||||
'Failed to load cache data for key',
|
||||
$this->handler->getRecords()[0]['message'],
|
||||
'Assert exception is caught and logged'
|
||||
);
|
||||
}
|
||||
|
||||
public function test_backend_cache__destruct(): void
|
||||
{
|
||||
$c = new Cache(
|
||||
logger: $this->logger,
|
||||
cache: new class($this->logger) extends PSRCache {
|
||||
public function __construct(private LoggerInterface $logger)
|
||||
{
|
||||
}
|
||||
|
||||
public function set(string $key, mixed $value, \DateInterval|int|null $ttl = null): bool
|
||||
{
|
||||
$this->logger->info('set() called');
|
||||
throw new InvalidArgumentException('foo');
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
$c = $c->withData('namespaced');
|
||||
|
||||
$c->set('foo', 'bar');
|
||||
$c->__destruct();
|
||||
|
||||
$this->assertStringContainsString(
|
||||
'set() called',
|
||||
$this->handler->getRecords()[0]['message'],
|
||||
'assert set() is called and exception is caught'
|
||||
);
|
||||
}
|
||||
|
||||
public function test_backend_cache__destruct_saved(): void
|
||||
{
|
||||
$c = $this->cache->withData('namespaced');
|
||||
|
||||
$c->set('foo', 'bar');
|
||||
$c->__destruct();
|
||||
|
||||
$this->assertCount(
|
||||
1,
|
||||
$this->psrCache->getData(),
|
||||
'Assert data is saved into backend.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class PSRCache implements CacheInterface
|
||||
{
|
||||
private array $data = [];
|
||||
|
||||
public function get(string $key, mixed $default = null): mixed
|
||||
{
|
||||
return $this->data[$key] ?? $default;
|
||||
}
|
||||
|
||||
public function set(string $key, mixed $value, \DateInterval|int|null $ttl = null): bool
|
||||
{
|
||||
$this->data[$key] = $value;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function delete(string $key): bool
|
||||
{
|
||||
unset($this->data[$key]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function clear(): bool
|
||||
{
|
||||
$this->data = [];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getMultiple(iterable $keys, mixed $default = null): iterable
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function setMultiple(iterable $values, \DateInterval|int|null $ttl = null): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function deleteMultiple(iterable $keys): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function has(string $key): bool
|
||||
{
|
||||
return isset($this->data[$key]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getData(): array
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
}
|
||||
93
tests/Backends/Common/ErrorTest.php
Normal file
93
tests/Backends/Common/ErrorTest.php
Normal file
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Backends\Common;
|
||||
|
||||
use App\Backends\Common\Error;
|
||||
use App\Backends\Common\Levels;
|
||||
use App\Libs\TestCase;
|
||||
|
||||
class ErrorTest extends TestCase
|
||||
{
|
||||
public function test_backend_error_object(): void
|
||||
{
|
||||
$context = [
|
||||
'not' => 'used',
|
||||
'foo' => 'bar',
|
||||
'arr' => [
|
||||
'foo' => 'bar'
|
||||
],
|
||||
'obj' => (object)[
|
||||
'foo' => 'bar'
|
||||
],
|
||||
'res' => fopen('php://memory', 'r'),
|
||||
];
|
||||
|
||||
fclose($context['res']);
|
||||
|
||||
$message = 'Hello World %(foo)! %(arr) %(obj) %(res) %(taz)';
|
||||
|
||||
$error = new Error(
|
||||
message: $message,
|
||||
context: $context,
|
||||
level: Levels::ERROR,
|
||||
previous: null,
|
||||
);
|
||||
|
||||
$this->assertTrue(
|
||||
$error->hasTags(),
|
||||
'hasTags() should return true of message contains tags.'
|
||||
);
|
||||
|
||||
$this->assertFalse(
|
||||
$error->hasException(),
|
||||
'hasException() should return false if no previous exception is found.'
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
$message,
|
||||
$error->message,
|
||||
'Assert message is returned as it is with no formatting.'
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
'Hello World bar! array{"foo":"bar"} [object stdClass] [resource (closed)] %(taz)',
|
||||
$error->format(),
|
||||
'Assert message is formatted correctly if tags are found.'
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
$context,
|
||||
$error->context,
|
||||
'Error object should have the same context as the one passed in the constructor.'
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
Levels::ERROR->value,
|
||||
$error->level(),
|
||||
'level() should return the string value of the enum level.'
|
||||
);
|
||||
|
||||
try {
|
||||
throw new \RuntimeException('test exception');
|
||||
} catch (\RuntimeException $e) {
|
||||
}
|
||||
|
||||
$error = new Error('message with no tags', previous: $e);
|
||||
$this->assertFalse(
|
||||
$error->hasTags(),
|
||||
'hasTags() should return false if no tags are found.'
|
||||
);
|
||||
$this->assertSame(
|
||||
'message with no tags',
|
||||
$error->format(),
|
||||
'format() should return the message as it is if no tags are found.'
|
||||
);
|
||||
|
||||
$this->assertTrue(
|
||||
$error->hasException(),
|
||||
'hasException() should return true if previous exception is set.'
|
||||
);
|
||||
}
|
||||
}
|
||||
52
tests/Backends/Common/ResponseTest.php
Normal file
52
tests/Backends/Common/ResponseTest.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Backends\Common;
|
||||
|
||||
use App\Backends\Common\Error;
|
||||
use App\Libs\TestCase;
|
||||
|
||||
class ResponseTest extends TestCase
|
||||
{
|
||||
public function test_backend_response_object(): void
|
||||
{
|
||||
$response = new \App\Backends\Common\Response(
|
||||
status: true,
|
||||
response: 'Hello World!',
|
||||
error: null,
|
||||
extra: [
|
||||
'foo' => 'bar'
|
||||
],
|
||||
);
|
||||
|
||||
$this->assertTrue(
|
||||
$response->isSuccessful(),
|
||||
'Response object should be successful if status is true.'
|
||||
);
|
||||
|
||||
$this->assertFalse(
|
||||
$response->hasError(),
|
||||
'Response object should not have an error if error is null.'
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
'Hello World!',
|
||||
$response->response,
|
||||
'Response object should have the same response as the one passed in the constructor.'
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
['foo' => 'bar'],
|
||||
$response->extra,
|
||||
'Response object should have the same extra as the one passed in the constructor.'
|
||||
);
|
||||
|
||||
$this->assertInstanceOf(
|
||||
Error::class,
|
||||
$response->getError(),
|
||||
'getError() should return an Error object in all cases even if error is null.'
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user