summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--config/config.default.php9
-rw-r--r--db/migrations/2018_08_30_000000_create_log_entries_table.php12
-rw-r--r--db/migrations/2018_09_11_000000_create_sessions_table.php27
-rw-r--r--src/Database/Database.php98
-rw-r--r--src/Database/DatabaseServiceProvider.php16
-rw-r--r--src/Database/Db.php1
-rw-r--r--src/Database/Migration/Migrate.php40
-rw-r--r--src/Database/Migration/MigrationServiceProvider.php11
-rw-r--r--src/Http/SessionHandlers/AbstractHandler.php75
-rw-r--r--src/Http/SessionHandlers/DatabaseHandler.php108
-rw-r--r--src/Http/SessionServiceProvider.php21
-rw-r--r--src/Models/BaseModel.php16
-rw-r--r--tests/Unit/Database/DatabaseServiceProviderTest.php54
-rw-r--r--tests/Unit/Database/DatabaseTest.php146
-rw-r--r--tests/Unit/Database/Migration/MigrateTest.php14
-rw-r--r--tests/Unit/Database/Migration/MigrationServiceProviderTest.php17
-rw-r--r--tests/Unit/HasDatabase.php47
-rw-r--r--tests/Unit/Http/SessionHandlers/AbstractHandlerTest.php44
-rw-r--r--tests/Unit/Http/SessionHandlers/DatabaseHandlerTest.php95
-rw-r--r--tests/Unit/Http/SessionHandlers/Stub/ArrayHandler.php59
-rw-r--r--tests/Unit/Http/SessionServiceProviderTest.php56
-rw-r--r--tests/Unit/Models/BaseModelTest.php24
-rw-r--r--tests/Unit/Models/Stub/BaseModelImplementation.php12
23 files changed, 943 insertions, 59 deletions
diff --git a/config/config.default.php b/config/config.default.php
index 659a26b7..079b7e63 100644
--- a/config/config.default.php
+++ b/config/config.default.php
@@ -132,6 +132,15 @@ return [
'4XL' => '4XL'
],
+ // Session config
+ 'session' => [
+ // Supported: pdo or native
+ 'driver' => env('SESSION_DRIVER', 'pdo'),
+
+ // Cookie name
+ 'name' => 'session',
+ ],
+
// IP addresses of reverse proxies that are trusted, can be an array or a comma separated list
'trusted_proxies' => env('TRUSTED_PROXIES', ['127.0.0.0/8', '::ffff:127.0.0.0/8', '::1/128']),
];
diff --git a/db/migrations/2018_08_30_000000_create_log_entries_table.php b/db/migrations/2018_08_30_000000_create_log_entries_table.php
index 68815434..bef78712 100644
--- a/db/migrations/2018_08_30_000000_create_log_entries_table.php
+++ b/db/migrations/2018_08_30_000000_create_log_entries_table.php
@@ -17,12 +17,14 @@ class CreateLogEntriesTable extends Migration
$table->timestamp('created_at')->nullable();
});
- $this->schema->getConnection()->unprepared('
- INSERT INTO log_entries (`id`, `level`, `message`, `created_at`)
- SELECT `id`, `level`, `message`, FROM_UNIXTIME(`timestamp`) FROM LogEntries
- ');
+ if ($this->schema->hasTable('LogEntries')) {
+ $this->schema->getConnection()->unprepared('
+ INSERT INTO log_entries (`id`, `level`, `message`, `created_at`)
+ SELECT `id`, `level`, `message`, FROM_UNIXTIME(`timestamp`) FROM LogEntries
+ ');
- $this->schema->dropIfExists('LogEntries');
+ $this->schema->drop('LogEntries');
+ }
}
/**
diff --git a/db/migrations/2018_09_11_000000_create_sessions_table.php b/db/migrations/2018_09_11_000000_create_sessions_table.php
new file mode 100644
index 00000000..33a9f569
--- /dev/null
+++ b/db/migrations/2018_09_11_000000_create_sessions_table.php
@@ -0,0 +1,27 @@
+<?php
+
+use Engelsystem\Database\Migration\Migration;
+use Illuminate\Database\Schema\Blueprint;
+
+class CreateSessionsTable extends Migration
+{
+ /**
+ * Run the migration
+ */
+ public function up()
+ {
+ $this->schema->create('sessions', function (Blueprint $table) {
+ $table->string('id')->unique();
+ $table->text('payload');
+ $table->dateTime('last_activity')->useCurrent();
+ });
+ }
+
+ /**
+ * Reverse the migration
+ */
+ public function down()
+ {
+ $this->schema->dropIfExists('sessions');
+ }
+}
diff --git a/src/Database/Database.php b/src/Database/Database.php
new file mode 100644
index 00000000..407a8bf9
--- /dev/null
+++ b/src/Database/Database.php
@@ -0,0 +1,98 @@
+<?php
+
+namespace Engelsystem\Database;
+
+use Illuminate\Database\Connection as DatabaseConnection;
+use PDO;
+
+class Database
+{
+ /** @var DatabaseConnection */
+ protected $connection;
+
+ /**
+ * @param DatabaseConnection $connection
+ */
+ public function __construct(DatabaseConnection $connection)
+ {
+ $this->connection = $connection;
+ }
+
+ /**
+ * Run a select query
+ *
+ * @param string $query
+ * @param array $bindings
+ * @return object[]
+ */
+ public function select($query, array $bindings = [])
+ {
+ return $this->connection->select($query, $bindings);
+ }
+
+ /**
+ * Run a select query and return only the first result or null if no result is found.
+ *
+ * @param string $query
+ * @param array $bindings
+ * @return object|null
+ */
+ public function selectOne($query, array $bindings = [])
+ {
+ return $this->connection->selectOne($query, $bindings);
+ }
+
+ /**
+ * Run an insert query
+ *
+ * @param string $query
+ * @param array $bindings
+ * @return bool
+ */
+ public function insert($query, array $bindings = [])
+ {
+ return $this->connection->insert($query, $bindings);
+ }
+
+ /**
+ * Run an update query
+ *
+ * @param string $query
+ * @param array $bindings
+ * @return int
+ */
+ public function update($query, array $bindings = [])
+ {
+ return $this->connection->update($query, $bindings);
+ }
+
+ /**
+ * Run a delete query
+ *
+ * @param string $query
+ * @param array $bindings
+ * @return int
+ */
+ public function delete($query, array $bindings = [])
+ {
+ return $this->connection->delete($query, $bindings);
+ }
+
+ /**
+ * Get the PDO instance
+ *
+ * @return PDO
+ */
+ public function getPdo()
+ {
+ return $this->connection->getPdo();
+ }
+
+ /**
+ * @return DatabaseConnection
+ */
+ public function getConnection()
+ {
+ return $this->connection;
+ }
+}
diff --git a/src/Database/DatabaseServiceProvider.php b/src/Database/DatabaseServiceProvider.php
index 7328bc4e..b3c33588 100644
--- a/src/Database/DatabaseServiceProvider.php
+++ b/src/Database/DatabaseServiceProvider.php
@@ -5,6 +5,7 @@ namespace Engelsystem\Database;
use Engelsystem\Container\ServiceProvider;
use Exception;
use Illuminate\Database\Capsule\Manager as CapsuleManager;
+use Illuminate\Database\Connection as DatabaseConnection;
use PDOException;
class DatabaseServiceProvider extends ServiceProvider
@@ -30,14 +31,25 @@ class DatabaseServiceProvider extends ServiceProvider
$capsule->bootEloquent();
$capsule->getConnection()->useDefaultSchemaGrammar();
+ $pdo = null;
try {
- $capsule->getConnection()->getPdo();
+ $pdo = $capsule->getConnection()->getPdo();
} catch (PDOException $e) {
$this->exitOnError();
}
- $this->app->instance('db', $capsule);
+ $this->app->instance(CapsuleManager::class, $capsule);
+ $this->app->instance(Db::class, $capsule);
Db::setDbManager($capsule);
+
+ $connection = $capsule->getConnection();
+ $this->app->instance(DatabaseConnection::class, $connection);
+
+ $database = $this->app->make(Database::class);
+ $this->app->instance(Database::class, $database);
+ $this->app->instance('db', $database);
+ $this->app->instance('db.pdo', $pdo);
+ $this->app->instance('db.connection', $connection);
}
/**
diff --git a/src/Database/Db.php b/src/Database/Db.php
index c0871e68..d007f1e5 100644
--- a/src/Database/Db.php
+++ b/src/Database/Db.php
@@ -6,6 +6,7 @@ use Illuminate\Database\Capsule\Manager as CapsuleManager;
use Illuminate\Database\Connection as DatabaseConnection;
use PDO;
+/** @deprecated */
class Db
{
/** @var CapsuleManager */
diff --git a/src/Database/Migration/Migrate.php b/src/Database/Migration/Migrate.php
index 3a08bb6e..9c6d3e43 100644
--- a/src/Database/Migration/Migrate.php
+++ b/src/Database/Migration/Migrate.php
@@ -18,7 +18,7 @@ class Migrate
protected $app;
/** @var SchemaBuilder */
- protected $scheme;
+ protected $schema;
/** @var callable */
protected $output;
@@ -29,13 +29,13 @@ class Migrate
/**
* Migrate constructor
*
- * @param SchemaBuilder $scheme
+ * @param SchemaBuilder $schema
* @param Application $app
*/
- public function __construct(SchemaBuilder $scheme, Application $app)
+ public function __construct(SchemaBuilder $schema, Application $app)
{
$this->app = $app;
- $this->scheme = $scheme;
+ $this->schema = $schema;
$this->output = function () { };
}
@@ -77,6 +77,21 @@ class Migrate
}
/**
+ * Setup migration tables
+ */
+ public function initMigration()
+ {
+ if ($this->schema->hasTable($this->table)) {
+ return;
+ }
+
+ $this->schema->create($this->table, function (Blueprint $table) {
+ $table->increments('id');
+ $table->string('migration');
+ });
+ }
+
+ /**
* Get all migrated migrations
*
* @return Collection
@@ -156,28 +171,13 @@ class Migrate
}
/**
- * Setup migration tables
- */
- protected function initMigration()
- {
- if ($this->scheme->hasTable($this->table)) {
- return;
- }
-
- $this->scheme->create($this->table, function (Blueprint $table) {
- $table->increments('id');
- $table->string('migration');
- });
- }
-
- /**
* Init a table query
*
* @return Builder
*/
protected function getTableQuery()
{
- return $this->scheme->getConnection()->table($this->table);
+ return $this->schema->getConnection()->table($this->table);
}
/**
diff --git a/src/Database/Migration/MigrationServiceProvider.php b/src/Database/Migration/MigrationServiceProvider.php
index 15d06eaf..310b2114 100644
--- a/src/Database/Migration/MigrationServiceProvider.php
+++ b/src/Database/Migration/MigrationServiceProvider.php
@@ -3,16 +3,19 @@
namespace Engelsystem\Database\Migration;
use Engelsystem\Container\ServiceProvider;
-use Engelsystem\Database\Db;
+use Engelsystem\Database\Database;
use Illuminate\Database\Schema\Builder as SchemaBuilder;
class MigrationServiceProvider extends ServiceProvider
{
public function register()
{
- $schema = Db::connection()->getSchemaBuilder();
- $this->app->instance('db.scheme', $schema);
- $this->app->bind(SchemaBuilder::class, 'db.scheme');
+ /** @var Database $database */
+ $database = $this->app->get(Database::class);
+ $schema = $database->getConnection()->getSchemaBuilder();
+
+ $this->app->instance('db.schema', $schema);
+ $this->app->bind(SchemaBuilder::class, 'db.schema');
$migration = $this->app->make(Migrate::class);
$this->app->instance('db.migration', $migration);
diff --git a/src/Http/SessionHandlers/AbstractHandler.php b/src/Http/SessionHandlers/AbstractHandler.php
new file mode 100644
index 00000000..135d0d43
--- /dev/null
+++ b/src/Http/SessionHandlers/AbstractHandler.php
@@ -0,0 +1,75 @@
+<?php
+
+namespace Engelsystem\Http\SessionHandlers;
+
+use SessionHandlerInterface;
+
+abstract class AbstractHandler implements SessionHandlerInterface
+{
+ /** @var string */
+ protected $name;
+
+ /** @var string */
+ protected $sessionPath;
+
+ /**
+ * Bootstrap the session handler
+ *
+ * @param string $sessionPath
+ * @param string $name
+ * @return bool
+ */
+ public function open($sessionPath, $name): bool
+ {
+ $this->name = $name;
+ $this->sessionPath = $sessionPath;
+
+ return true;
+ }
+
+ /**
+ * Shutdown the session handler
+ *
+ * @return bool
+ */
+ public function close(): bool
+ {
+ return true;
+ }
+
+ /**
+ * Remove old sessions
+ *
+ * @param int $maxLifetime
+ * @return bool
+ */
+ public function gc($maxLifetime): bool
+ {
+ return true;
+ }
+
+ /**
+ * Read session data
+ *
+ * @param string $id
+ * @return string
+ */
+ abstract public function read($id): string;
+
+ /**
+ * Write session data
+ *
+ * @param string $id
+ * @param string $data
+ * @return bool
+ */
+ abstract public function write($id, $data): bool;
+
+ /**
+ * Delete a session
+ *
+ * @param string $id
+ * @return bool
+ */
+ abstract public function destroy($id): bool;
+}
diff --git a/src/Http/SessionHandlers/DatabaseHandler.php b/src/Http/SessionHandlers/DatabaseHandler.php
new file mode 100644
index 00000000..8df70287
--- /dev/null
+++ b/src/Http/SessionHandlers/DatabaseHandler.php
@@ -0,0 +1,108 @@
+<?php
+
+namespace Engelsystem\Http\SessionHandlers;
+
+use Engelsystem\Database\Database;
+use Illuminate\Database\Query\Builder as QueryBuilder;
+
+class DatabaseHandler extends AbstractHandler
+{
+ /** @var Database */
+ protected $database;
+
+ /**
+ * @param Database $database
+ */
+ public function __construct(Database $database)
+ {
+ $this->database = $database;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function read($id): string
+ {
+ $session = $this->getQuery()
+ ->where('id', '=', $id)
+ ->first();
+
+ return $session ? $session->payload : '';
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function write($id, $data): bool
+ {
+ $values = [
+ 'payload' => $data,
+ 'last_activity' => $this->getCurrentTimestamp(),
+ ];
+
+ $session = $this->getQuery()
+ ->where('id', '=', $id)
+ ->first();
+
+ if (!$session) {
+ return $this->getQuery()
+ ->insert($values + [
+ 'id' => $id,
+ ]);
+ }
+
+ $this->getQuery()
+ ->where('id', '=', $id)
+ ->update($values);
+
+ // The update return can't be used directly because it won't change if the second call is in the same second
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function destroy($id): bool
+ {
+ $this->getQuery()
+ ->where('id', '=', $id)
+ ->delete();
+
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function gc($maxLifetime): bool
+ {
+ $timestamp = $this->getCurrentTimestamp(-$maxLifetime);
+
+ $this->getQuery()
+ ->where('last_activity', '<', $timestamp)
+ ->delete();
+
+ return true;
+ }
+
+ /**
+ * @return QueryBuilder
+ */
+ protected function getQuery(): QueryBuilder
+ {
+ return $this->database
+ ->getConnection()
+ ->table('sessions');
+ }
+
+ /**
+ * Format the SQL timestamp
+ *
+ * @param int $diff
+ * @return string
+ */
+ protected function getCurrentTimestamp(int $diff = 0): string
+ {
+ return date('Y-m-d H:i:s', strtotime(sprintf('%+d seconds', $diff)));
+ }
+}
diff --git a/src/Http/SessionServiceProvider.php b/src/Http/SessionServiceProvider.php
index 59121a3b..c2e09624 100644
--- a/src/Http/SessionServiceProvider.php
+++ b/src/Http/SessionServiceProvider.php
@@ -2,7 +2,9 @@
namespace Engelsystem\Http;
+use Engelsystem\Config\Config;
use Engelsystem\Container\ServiceProvider;
+use Engelsystem\Http\SessionHandlers\DatabaseHandler;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;
@@ -38,7 +40,24 @@ class SessionServiceProvider extends ServiceProvider
return $this->app->make(MockArraySessionStorage::class);
}
- return $this->app->make(NativeSessionStorage::class, ['options' => ['cookie_httponly' => true]]);
+ /** @var Config $config */
+ $config = $this->app->get('config');
+ $sessionConfig = $config->get('session');
+
+ $handler = null;
+ switch ($sessionConfig['driver']) {
+ case 'pdo':
+ $handler = $this->app->make(DatabaseHandler::class);
+ break;
+ }
+
+ return $this->app->make(NativeSessionStorage::class, [
+ 'options' => [
+ 'cookie_httponly' => true,
+ 'name' => $sessionConfig['name'],
+ ],
+ 'handler' => $handler,
+ ]);
}
/**
diff --git a/src/Models/BaseModel.php b/src/Models/BaseModel.php
index cf718e4f..d5ded428 100644
--- a/src/Models/BaseModel.php
+++ b/src/Models/BaseModel.php
@@ -2,6 +2,8 @@
namespace Engelsystem\Models;
+use Illuminate\Database\Eloquent\Builder;
+use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
abstract class BaseModel extends Model
@@ -10,6 +12,8 @@ abstract class BaseModel extends Model
public $timestamps = false;
/**
+ * Create a new model
+ *
* @param array $attributes
* @return BaseModel
*/
@@ -20,4 +24,16 @@ abstract class BaseModel extends Model
return $instance;
}
+
+ /**
+ * Find a model by its primary key
+ *
+ * @param mixed $id
+ * @param array $columns
+ * @return Builder|Builder[]|Collection|Model|null
+ */
+ public static function find($id, $columns = ['*'])
+ {
+ return static::query()->find($id, $columns);
+ }
}
diff --git a/tests/Unit/Database/DatabaseServiceProviderTest.php b/tests/Unit/Database/DatabaseServiceProviderTest.php
index 8f7898cd..7dae065f 100644
--- a/tests/Unit/Database/DatabaseServiceProviderTest.php
+++ b/tests/Unit/Database/DatabaseServiceProviderTest.php
@@ -2,12 +2,16 @@
namespace Engelsystem\Test\Unit\Database;
+use Engelsystem\Application;
use Engelsystem\Config\Config;
+use Engelsystem\Database\Database;
use Engelsystem\Database\DatabaseServiceProvider;
+use Engelsystem\Database\Db;
use Engelsystem\Test\Unit\ServiceProviderTest;
use Exception;
use Illuminate\Database\Capsule\Manager as CapsuleManager;
use Illuminate\Database\Connection;
+use PDO;
use PDOException;
use PHPUnit_Framework_MockObject_MockObject as MockObject;
@@ -18,9 +22,29 @@ class DatabaseServiceProviderTest extends ServiceProviderTest
*/
public function testRegister()
{
- list($app, $dbManager) = $this->prepare(['driver' => 'sqlite', 'database' => ':memory:']);
+ /** @var Application|MockObject $app */
+ /** @var CapsuleManager|MockObject $dbManager */
+ /** @var PDO|MockObject $pdo */
+ /** @var Database|MockObject $database */
+ /** @var Connection|MockObject $connection */
+ list($app, $dbManager, $pdo, $database, $connection) = $this->prepare(
+ [
+ 'driver' => 'sqlite',
+ 'database' => ':memory:'
+ ]
+ );
- $this->setExpects($app, 'instance', ['db', $dbManager]);
+ $app->expects($this->exactly(7))
+ ->method('instance')
+ ->withConsecutive(
+ [CapsuleManager::class, $dbManager],
+ [Db::class, $dbManager],
+ [Connection::class, $connection],
+ [Database::class, $database],
+ ['db', $database],
+ ['db.pdo', $pdo],
+ ['db.connection', $connection]
+ );
$serviceProvider = new DatabaseServiceProvider($app);
$serviceProvider->register();
@@ -64,28 +88,46 @@ class DatabaseServiceProviderTest extends ServiceProviderTest
$connection = $this->getMockBuilder(Connection::class)
->disableOriginalConstructor()
->getMock();
+ /** @var PDO|MockObject $pdo */
+ $pdo = $this->getMockBuilder(PDO::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ /** @var Database|MockObject $database */
+ $database = $this->getMockBuilder(Database::class)
+ ->disableOriginalConstructor()
+ ->getMock();
$app = $this->getApp(['get', 'make', 'instance']);
$this->setExpects($app, 'get', ['config'], $config);
- $this->setExpects($app, 'make', [CapsuleManager::class], $dbManager);
$this->setExpects($config, 'get', ['database'], $dbConfigData, $this->atLeastOnce());
+ $app->expects($this->atLeastOnce())
+ ->method('make')
+ ->withConsecutive(
+ [CapsuleManager::class],
+ [Database::class]
+ )
+ ->willReturn(
+ $dbManager,
+ $database
+ );
+
$this->setExpects($dbManager, 'setAsGlobal');
$this->setExpects($dbManager, 'bootEloquent');
$this->setExpects($connection, 'useDefaultSchemaGrammar');
$connection->expects($this->once())
->method('getPdo')
- ->willReturnCallback(function () use ($getPdoThrowException) {
+ ->willReturnCallback(function () use ($getPdoThrowException, $pdo) {
if ($getPdoThrowException) {
throw new PDOException();
}
- return '';
+ return $pdo;
});
$this->setExpects($dbManager, 'getConnection', [], $connection, $this->atLeastOnce());
- return [$app, $dbManager];
+ return [$app, $dbManager, $pdo, $database, $connection];
}
}
diff --git a/tests/Unit/Database/DatabaseTest.php b/tests/Unit/Database/DatabaseTest.php
new file mode 100644
index 00000000..91d6a6c7
--- /dev/null
+++ b/tests/Unit/Database/DatabaseTest.php
@@ -0,0 +1,146 @@
+<?php
+
+namespace Engelsystem\Test\Unit\Database;
+
+use Engelsystem\Database\Database;
+use Illuminate\Database\Capsule\Manager as CapsuleManager;
+use Illuminate\Database\Connection as DatabaseConnection;
+use PDO;
+use PHPUnit\Framework\TestCase;
+use PHPUnit_Framework_MockObject_MockObject as MockObject;
+
+class DatabaseTest extends TestCase
+{
+ /** @var DatabaseConnection */
+ protected $connection;
+
+ /**
+ * @covers \Engelsystem\Database\Database::__construct()
+ * @covers \Engelsystem\Database\Database::getPdo()
+ * @covers \Engelsystem\Database\Database::getConnection()
+ */
+ public function testInit()
+ {
+ /** @var Pdo|MockObject $pdo */
+ $pdo = $this->getMockBuilder(Pdo::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ /** @var DatabaseConnection|MockObject $databaseConnection */
+ $databaseConnection = $this->getMockBuilder(DatabaseConnection::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $databaseConnection->expects($this->atLeastOnce())
+ ->method('getPdo')
+ ->willReturn($pdo);
+
+ $db = new Database($databaseConnection);
+
+ $this->assertEquals($databaseConnection, $db->getConnection());
+ $this->assertEquals($pdo, $db->getPdo());
+ $this->assertInstanceOf(PDO::class, $db->getPdo());
+ }
+
+ /**
+ * @covers \Engelsystem\Database\Database::select()
+ */
+ public function testSelect()
+ {
+ $db = new Database($this->connection);
+
+ $return = $db->select('SELECT * FROM test_data');
+ $this->assertTrue(count($return) > 3);
+
+ $return = $db->select('SELECT * FROM test_data WHERE id = ?', [2]);
+ $this->assertCount(1, $return);
+ }
+
+ /**
+ * @covers \Engelsystem\Database\Database::selectOne()
+ */
+ public function testSelectOne()
+ {
+ $db = new Database($this->connection);
+
+ $return = $db->selectOne('SELECT * FROM test_data');
+ $this->assertEquals('Foo', $return->data);
+
+ $return = $db->selectOne('SELECT * FROM test_data WHERE id = -1');
+ $this->assertEmpty($return);
+
+ $return = $db->selectOne('SELECT * FROM test_data WHERE id = ?', [3]);
+ $this->assertTrue(!is_array($return));
+ }
+
+ /**
+ * @covers \Engelsystem\Database\Database::insert()
+ */
+ public function testInsert()
+ {
+ $db = new Database($this->connection);
+
+ $result = $db->insert("INSERT INTO test_data (id, data) VALUES (5, 'Some random text'), (6, 'another text')");
+ $this->assertTrue($result);
+ }
+
+ /**
+ * @covers \Engelsystem\Database\Database::update()
+ */
+ public function testUpdate()
+ {
+ $db = new Database($this->connection);
+
+ $count = $db->update("UPDATE test_data SET data='NOPE' WHERE data LIKE '%Replaceme%'");
+ $this->assertEquals(3, $count);
+
+ $count = $db->update("UPDATE test_data SET data=? WHERE data LIKE '%NOPE%'", ['Some random text!']);
+ $this->assertEquals(3, $count);
+ }
+
+ /**
+ * @covers \Engelsystem\Database\Database::delete()
+ */
+ public function testDelete()
+ {
+ $db = new Database($this->connection);
+
+ $count = $db->delete('DELETE FROM test_data WHERE id=1');
+ $this->assertEquals(1, $count);
+
+ $count = $db->delete('DELETE FROM test_data WHERE data LIKE ?', ['%Replaceme%']);
+ $this->assertEquals(3, $count);
+ }
+
+ /**
+ * Setup in memory database
+ */
+ protected function setUp()
+ {
+ $dbManager = new CapsuleManager();
+ $dbManager->addConnection(['driver' => 'sqlite', 'database' => ':memory:']);
+
+ $connection = $dbManager->getConnection();
+ $this->connection = $connection;
+
+ $connection->getPdo()->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+ $connection->statement(
+ '
+ CREATE TABLE test_data(
+ id INT PRIMARY KEY NOT NULL,
+ data TEXT NOT NULL
+ );
+ ');
+ $connection->statement('CREATE UNIQUE INDEX test_data_id_uindex ON test_data (id);');
+ $connection->insert("
+ INSERT INTO test_data (id, data)
+ VALUES
+ (1, 'Foo'),
+ (2, 'Bar'),
+ (3, 'Batz'),
+ (4, 'Lorem ipsum dolor sit'),
+ (10, 'Replaceme ipsum dolor sit amet'),
+ (11, 'Lorem Replaceme dolor sit amet'),
+ (12, 'Lorem ipsum Replaceme sit amet')
+ ;");
+ }
+}
diff --git a/tests/Unit/Database/Migration/MigrateTest.php b/tests/Unit/Database/Migration/MigrateTest.php
index c88ad777..2adbed41 100644
--- a/tests/Unit/Database/Migration/MigrateTest.php
+++ b/tests/Unit/Database/Migration/MigrateTest.php
@@ -120,12 +120,12 @@ class MigrateTest extends TestCase
$dbManager->bootEloquent();
$db = $dbManager->getConnection();
$db->useDefaultSchemaGrammar();
- $scheme = $db->getSchemaBuilder();
+ $schema = $db->getSchemaBuilder();
- $app->instance('scheme', $scheme);
- $app->bind(SchemaBuilder::class, 'scheme');
+ $app->instance('schema', $schema);
+ $app->bind(SchemaBuilder::class, 'schema');
- $migration = new Migrate($scheme, $app);
+ $migration = new Migrate($schema, $app);
$messages = [];
$migration->setOutput(function ($msg) use (&$messages) {
@@ -134,7 +134,7 @@ class MigrateTest extends TestCase
$migration->run(__DIR__ . '/Stub', Migrate::UP);
- $this->assertTrue($scheme->hasTable('migrations'));
+ $this->assertTrue($schema->hasTable('migrations'));
$migrations = $db->table('migrations')->get();
$this->assertCount(3, $migrations);
@@ -143,7 +143,7 @@ class MigrateTest extends TestCase
$this->assertTrue($migrations->contains('migration', '2017_12_24_053300_another_stuff'));
$this->assertTrue($migrations->contains('migration', '2022_12_22_221222_add_some_feature'));
- $this->assertTrue($scheme->hasTable('lorem_ipsum'));
+ $this->assertTrue($schema->hasTable('lorem_ipsum'));
$migration->run(__DIR__ . '/Stub', Migrate::DOWN, true);
@@ -155,6 +155,6 @@ class MigrateTest extends TestCase
$migrations = $db->table('migrations')->get();
$this->assertCount(0, $migrations);
- $this->assertFalse($scheme->hasTable('lorem_ipsum'));
+ $this->assertFalse($schema->hasTable('lorem_ipsum'));
}
}
diff --git a/tests/Unit/Database/Migration/MigrationServiceProviderTest.php b/tests/Unit/Database/Migration/MigrationServiceProviderTest.php
index a99cdebe..593da5c5 100644
--- a/tests/Unit/Database/Migration/MigrationServiceProviderTest.php
+++ b/tests/Unit/Database/Migration/MigrationServiceProviderTest.php
@@ -2,11 +2,10 @@
namespace Engelsystem\Test\Unit\Database\Migration;
-use Engelsystem\Database\Db;
+use Engelsystem\Database\Database;
use Engelsystem\Database\Migration\Migrate;
use Engelsystem\Database\Migration\MigrationServiceProvider;
use Engelsystem\Test\Unit\ServiceProviderTest;
-use Illuminate\Database\Capsule\Manager as CapsuleManager;
use Illuminate\Database\Connection;
use Illuminate\Database\Schema\Builder as SchemaBuilder;
use PHPUnit_Framework_MockObject_MockObject as MockObject;
@@ -22,8 +21,8 @@ class MigrationServiceProviderTest extends ServiceProviderTest
$migration = $this->getMockBuilder(Migrate::class)
->disableOriginalConstructor()
->getMock();
- /** @var MockObject|CapsuleManager $dbManager */
- $dbManager = $this->getMockBuilder(CapsuleManager::class)
+ /** @var Database|MockObject $database */
+ $database = $this->getMockBuilder(Database::class)
->disableOriginalConstructor()
->getMock();
/** @var MockObject|Connection $dbConnection */
@@ -35,19 +34,19 @@ class MigrationServiceProviderTest extends ServiceProviderTest
->disableOriginalConstructor()
->getMock();
- $app = $this->getApp(['make', 'instance', 'bind']);
+ $app = $this->getApp(['make', 'instance', 'bind', 'get']);
$app->expects($this->atLeastOnce())
->method('instance')
- ->withConsecutive(['db.scheme'], ['db.migration'])
+ ->withConsecutive(['db.schema'], ['db.migration'])
->willReturnOnConsecutiveCalls($schemaBuilder, $migration);
- $this->setExpects($app, 'bind', [SchemaBuilder::class, 'db.scheme']);
+ $this->setExpects($app, 'bind', [SchemaBuilder::class, 'db.schema']);
$this->setExpects($app, 'make', [Migrate::class], $migration);
+ $this->setExpects($app, 'get', [Database::class], $database);
$this->setExpects($dbConnection, 'getSchemaBuilder', null, $schemaBuilder);
- $this->setExpects($dbManager, 'getConnection', null, $dbConnection);
- Db::setDbManager($dbManager);
+ $this->setExpects($database, 'getConnection', null, $dbConnection);
$serviceProvider = new MigrationServiceProvider($app);
$serviceProvider->register();
diff --git a/tests/Unit/HasDatabase.php b/tests/Unit/HasDatabase.php
new file mode 100644
index 00000000..d69f0a3a
--- /dev/null
+++ b/tests/Unit/HasDatabase.php
@@ -0,0 +1,47 @@
+<?php
+
+namespace Engelsystem\Test\Unit;
+
+use Engelsystem\Application;
+use Engelsystem\Database\Database;
+use Engelsystem\Database\Migration\Migrate;
+use Engelsystem\Database\Migration\MigrationServiceProvider;
+use Illuminate\Database\Capsule\Manager as CapsuleManager;
+use PDO;
+
+trait HasDatabase
+{
+ /** @var Database */
+ protected $database;
+
+ /**
+ * Setup in memory database
+ */
+ protected function initDatabase()
+ {
+ $dbManager = new CapsuleManager();
+ $dbManager->addConnection(['driver' => 'sqlite', 'database' => ':memory:']);
+
+ $connection = $dbManager->getConnection();
+ $connection->getPdo()->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+ $this->database = new Database($connection);
+
+ $app = new Application();
+ $app->instance(Database::class, $this->database);
+ $app->register(MigrationServiceProvider::class);
+
+ /** @var Migrate $migration */
+ $migration = $app->get('db.migration');
+ $migration->initMigration();
+
+ $this->database
+ ->getConnection()
+ ->table('migrations')
+ ->insert([
+ ['migration' => '2018_01_01_000001_import_install_sql'],
+ ['migration' => '2018_01_01_000002_import_update_sql'],
+ ]);
+
+ $migration->run(__DIR__ . '/../../db/migrations');
+ }
+}
diff --git a/tests/Unit/Http/SessionHandlers/AbstractHandlerTest.php b/tests/Unit/Http/SessionHandlers/AbstractHandlerTest.php
new file mode 100644
index 00000000..bfd2e883
--- /dev/null
+++ b/tests/Unit/Http/SessionHandlers/AbstractHandlerTest.php
@@ -0,0 +1,44 @@
+<?php
+
+namespace Engelsystem\Test\Unit\Http\SessionHandlers;
+
+use Engelsystem\Test\Unit\Http\SessionHandlers\Stub\ArrayHandler;
+use PHPUnit\Framework\TestCase;
+
+class AbstractHandlerTest extends TestCase
+{
+ /**
+ * @covers \Engelsystem\Http\SessionHandlers\AbstractHandler::open
+ */
+ public function testOpen()
+ {
+ $handler = new ArrayHandler();
+ $return = $handler->open('/foo/bar', '1337asd098hkl7654');
+
+ $this->assertTrue($return);
+ $this->assertEquals('1337asd098hkl7654', $handler->getName());
+ $this->assertEquals('/foo/bar', $handler->getSessionPath());
+ }
+
+ /**
+ * @covers \Engelsystem\Http\SessionHandlers\AbstractHandler::close
+ */
+ public function testClose()
+ {
+ $handler = new ArrayHandler();
+ $return = $handler->close();
+
+ $this->assertTrue($return);
+ }
+
+ /**
+ * @covers \Engelsystem\Http\SessionHandlers\AbstractHandler::gc
+ */
+ public function testGc()
+ {
+ $handler = new ArrayHandler();
+ $return = $handler->gc(60 * 60 * 24);
+
+ $this->assertTrue($return);
+ }
+}
diff --git a/tests/Unit/Http/SessionHandlers/DatabaseHandlerTest.php b/tests/Unit/Http/SessionHandlers/DatabaseHandlerTest.php
new file mode 100644
index 00000000..ea4f3701
--- /dev/null
+++ b/tests/Unit/Http/SessionHandlers/DatabaseHandlerTest.php
@@ -0,0 +1,95 @@
+<?php
+
+namespace Engelsystem\Test\Unit\Http\SessionHandlers;
+
+use Engelsystem\Http\SessionHandlers\DatabaseHandler;
+use Engelsystem\Test\Unit\HasDatabase;
+use PHPUnit\Framework\TestCase;
+
+class DatabaseHandlerTest extends TestCase
+{
+ use HasDatabase;
+
+ /**
+ * @covers \Engelsystem\Http\SessionHandlers\DatabaseHandler::__construct
+ * @covers \Engelsystem\Http\SessionHandlers\DatabaseHandler::read
+ * @covers \Engelsystem\Http\SessionHandlers\DatabaseHandler::getQuery
+ */
+ public function testRead()
+ {
+ $handler = new DatabaseHandler($this->database);
+ $this->assertEquals('', $handler->read('foo'));
+
+ $this->database->insert("INSERT INTO sessions VALUES ('foo', 'Lorem Ipsum', CURRENT_TIMESTAMP)");
+ $this->assertEquals('Lorem Ipsum', $handler->read('foo'));
+ }
+
+ /**
+ * @covers \Engelsystem\Http\SessionHandlers\DatabaseHandler::write
+ * @covers \Engelsystem\Http\SessionHandlers\DatabaseHandler::getCurrentTimestamp
+ */
+ public function testWrite()
+ {
+ $handler = new DatabaseHandler($this->database);
+
+ foreach (['Lorem Ipsum', 'Dolor Sit!'] as $data) {
+ $this->assertTrue($handler->write('foo', $data));
+
+ $return = $this->database->select('SELECT * FROM sessions WHERE id = :id', ['id' => 'foo']);
+ $this->assertCount(1, $return);
+
+ $return = array_shift($return);
+ $this->assertEquals($data, $return->payload);
+ }
+ }
+
+ /**
+ * @covers \Engelsystem\Http\SessionHandlers\DatabaseHandler::destroy
+ */
+ public function testDestroy()
+ {
+ $this->database->insert("INSERT INTO sessions VALUES ('foo', 'Lorem Ipsum', CURRENT_TIMESTAMP)");
+ $this->database->insert("INSERT INTO sessions VALUES ('bar', 'Dolor Sit', CURRENT_TIMESTAMP)");
+
+ $handler = new DatabaseHandler($this->database);
+ $this->assertTrue($handler->destroy('batz'));
+
+ $return = $this->database->select('SELECT * FROM sessions');
+ $this->assertCount(2, $return);
+
+ $this->assertTrue($handler->destroy('bar'));
+
+ $return = $this->database->select('SELECT * FROM sessions');
+ $this->assertCount(1, $return);
+
+ $return = array_shift($return);
+ $this->assertEquals('foo', $return->id);
+ }
+
+ /**
+ * @covers \Engelsystem\Http\SessionHandlers\DatabaseHandler::gc
+ */
+ public function testGc()
+ {
+ $this->database->insert("INSERT INTO sessions VALUES ('foo', 'Lorem Ipsum', '2000-01-01 01:00')");
+ $this->database->insert("INSERT INTO sessions VALUES ('bar', 'Dolor Sit', '3000-01-01 01:00')");
+
+ $handler = new DatabaseHandler($this->database);
+
+ $this->assertTrue($handler->gc(60 * 60));
+
+ $return = $this->database->select('SELECT * FROM sessions');
+ $this->assertCount(1, $return);
+
+ $return = array_shift($return);
+ $this->assertEquals('bar', $return->id);
+ }
+
+ /**
+ * Prepare tests
+ */
+ protected function setUp()
+ {
+ $this->initDatabase();
+ }
+}
diff --git a/tests/Unit/Http/SessionHandlers/Stub/ArrayHandler.php b/tests/Unit/Http/SessionHandlers/Stub/ArrayHandler.php
new file mode 100644
index 00000000..4d37da48
--- /dev/null
+++ b/tests/Unit/Http/SessionHandlers/Stub/ArrayHandler.php
@@ -0,0 +1,59 @@
+<?php
+
+namespace Engelsystem\Test\Unit\Http\SessionHandlers\Stub;
+
+use Engelsystem\Http\SessionHandlers\AbstractHandler;
+
+class ArrayHandler extends AbstractHandler
+{
+ /** @var string[] */
+ protected $content = [];
+
+ /**
+ * {@inheritdoc}
+ */
+ public function read($id): string
+ {
+ if (isset($this->content[$id])) {
+ return $this->content[$id];
+ }
+
+ return '';
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function write($id, $data): bool
+ {
+ $this->content[$id] = $data;
+
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function destroy($id): bool
+ {
+ unset($this->content[$id]);
+
+ return true;
+ }
+
+ /**
+ * @return string
+ */
+ public function getName(): string
+ {
+ return $this->name;
+ }
+
+ /**
+ * @return string
+ */
+ public function getSessionPath(): string
+ {
+ return $this->sessionPath;
+ }
+}
diff --git a/tests/Unit/Http/SessionServiceProviderTest.php b/tests/Unit/Http/SessionServiceProviderTest.php
index d0125bc2..dd0e538f 100644
--- a/tests/Unit/Http/SessionServiceProviderTest.php
+++ b/tests/Unit/Http/SessionServiceProviderTest.php
@@ -2,7 +2,9 @@
namespace Engelsystem\Test\Unit\Http;
+use Engelsystem\Config\Config;
use Engelsystem\Http\Request;
+use Engelsystem\Http\SessionHandlers\DatabaseHandler;
use Engelsystem\Http\SessionServiceProvider;
use Engelsystem\Test\Unit\ServiceProviderTest;
use PHPUnit_Framework_MockObject_MockObject as MockObject;
@@ -23,6 +25,9 @@ class SessionServiceProviderTest extends ServiceProviderTest
$sessionStorage = $this->getMockForAbstractClass(StorageInterface::class);
$sessionStorage2 = $this->getMockForAbstractClass(StorageInterface::class);
+ $databaseHandler = $this->getMockBuilder(DatabaseHandler::class)
+ ->disableOriginalConstructor()
+ ->getMock();
$session = $this->getSessionMock();
$request = $this->getRequestMock();
@@ -32,22 +37,38 @@ class SessionServiceProviderTest extends ServiceProviderTest
->setConstructorArgs([$app])
->setMethods(['isCli'])
->getMock();
- $serviceProvider->expects($this->exactly(2))
+
+ /** @var Config|MockObject $config */
+ $config = $this->createMock(Config::class);
+
+ $serviceProvider->expects($this->exactly(3))
->method('isCli')
- ->willReturnOnConsecutiveCalls(true, false);
+ ->willReturnOnConsecutiveCalls(true, false, false);
- $app->expects($this->exactly(4))
+ $app->expects($this->exactly(7))
->method('make')
->withConsecutive(
[MockArraySessionStorage::class],
[Session::class],
- [NativeSessionStorage::class, ['options' => ['cookie_httponly' => true]]],
+ [
+ NativeSessionStorage::class,
+ ['options' => ['cookie_httponly' => true, 'name' => 'session'], 'handler' => null]
+ ],
+ [Session::class],
+ [DatabaseHandler::class],
+ [
+ NativeSessionStorage::class,
+ ['options' => ['cookie_httponly' => true, 'name' => 'foobar'], 'handler' => $databaseHandler]
+ ],
[Session::class]
)
->willReturnOnConsecutiveCalls(
$sessionStorage,
$session,
$sessionStorage2,
+ $session,
+ $databaseHandler,
+ $sessionStorage2,
$session
);
$app->expects($this->atLeastOnce())
@@ -58,13 +79,38 @@ class SessionServiceProviderTest extends ServiceProviderTest
['session', $session]
);
+ $app->expects($this->exactly(5))
+ ->method('get')
+ ->withConsecutive(
+ ['request'],
+ ['config'],
+ ['request'],
+ ['config'],
+ ['request']
+ )
+ ->willReturnOnConsecutiveCalls(
+ $request,
+ $config,
+ $request,
+ $config,
+ $request
+ );
+
+ $config->expects($this->exactly(2))
+ ->method('get')
+ ->with('session')
+ ->willReturnOnConsecutiveCalls(
+ ['driver' => 'native', 'name' => 'session'],
+ ['driver' => 'pdo', 'name' => 'foobar']
+ );
+
$this->setExpects($app, 'bind', [StorageInterface::class, 'session.storage'], null, $this->atLeastOnce());
- $this->setExpects($app, 'get', ['request'], $request, $this->atLeastOnce());
$this->setExpects($request, 'setSession', [$session], null, $this->atLeastOnce());
$this->setExpects($session, 'start', null, null, $this->atLeastOnce());
$serviceProvider->register();
$serviceProvider->register();
+ $serviceProvider->register();
}
/**
diff --git a/tests/Unit/Models/BaseModelTest.php b/tests/Unit/Models/BaseModelTest.php
index 52cb8c7b..9af55fa1 100644
--- a/tests/Unit/Models/BaseModelTest.php
+++ b/tests/Unit/Models/BaseModelTest.php
@@ -3,6 +3,8 @@
namespace Engelsystem\Test\Unit\Models;
use Engelsystem\Test\Unit\Models\Stub\BaseModelImplementation;
+use Illuminate\Database\Eloquent\Builder as QueryBuilder;
+use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
class BaseModelTest extends TestCase
@@ -19,4 +21,26 @@ class BaseModelTest extends TestCase
$this->assertEquals('bar', $newModel->foo);
$this->assertEquals(1, $newModel->saveCount);
}
+
+ /**
+ * @covers \Engelsystem\Models\BaseModel::find
+ */
+ public function testFind()
+ {
+ /** @var QueryBuilder|MockObject $queryBuilder */
+ $queryBuilder = $this->createMock(QueryBuilder::class);
+ BaseModelImplementation::$queryBuilder = $queryBuilder;
+
+ $anotherModel = new BaseModelImplementation();
+
+ $queryBuilder->expects($this->once())
+ ->method('find')
+ ->with(1337, ['foo', 'bar'])
+ ->willReturn($anotherModel);
+
+ $model = new BaseModelImplementation();
+ $newModel = $model->find(1337, ['foo', 'bar']);
+
+ $this->assertEquals($anotherModel, $newModel);
+ }
}
diff --git a/tests/Unit/Models/Stub/BaseModelImplementation.php b/tests/Unit/Models/Stub/BaseModelImplementation.php
index 4aa1ef0b..70643b03 100644
--- a/tests/Unit/Models/Stub/BaseModelImplementation.php
+++ b/tests/Unit/Models/Stub/BaseModelImplementation.php
@@ -3,6 +3,7 @@
namespace Engelsystem\Test\Unit\Models\Stub;
use Engelsystem\Models\BaseModel;
+use Illuminate\Database\Eloquent\Builder as QueryBuilder;
/**
* @property string foo
@@ -15,6 +16,9 @@ class BaseModelImplementation extends BaseModel
/** @var int */
public $saveCount = 0;
+ /** @var QueryBuilder */
+ public static $queryBuilder = null;
+
/**
* @param array $options
* @return bool
@@ -24,4 +28,12 @@ class BaseModelImplementation extends BaseModel
$this->saveCount++;
return true;
}
+
+ /**
+ * @return QueryBuilder
+ */
+ public static function query()
+ {
+ return self::$queryBuilder;
+ }
}