Removes more hardcoded "jilo" strings and fixes the tests

main
Yasen Pramatarov 2026-03-25 12:51:18 +02:00
parent 0c5a48d851
commit 3287311b2d
16 changed files with 423 additions and 177 deletions

View File

@ -1,5 +1,7 @@
<?php
use App\App;
// Already required in index.php, but we require it here,
// because this class could be used standalone
require_once __DIR__ . '/../helpers/logger_loader.php';
@ -16,7 +18,8 @@ class TwoFactorAuthentication {
private $period = 30; // Time step in seconds (T0)
private $digits = 6; // Number of digits in TOTP code
private $algorithm = 'sha1'; // HMAC algorithm
private $issuer = 'Jilo';
// Branding: populated from config so authenticator apps show the configured site name.
private $issuer = 'Website';
private $window = 1; // Time window of 1 step before/after
/**
@ -30,6 +33,9 @@ class TwoFactorAuthentication {
} else {
$this->db = $database->getConnection();
}
$config = App::config();
$this->issuer = (string)$config['site_name'];
}
/**

View File

@ -1,5 +1,8 @@
<?php
global $config;
$siteName = (string)$config['site_name'];
/**
* Theme Configuration
*
@ -33,7 +36,7 @@ return [
// Theme configuration defaults
'default_config' => [
'name' => 'Unnamed Theme',
'description' => 'A Jilo Web theme',
'description' => sprintf('A %s theme', $siteName),
'version' => '1.0.0',
'author' => 'Lindeas Inc.',
'screenshot' => 'screenshot.png',

View File

@ -10,6 +10,7 @@
namespace App\Helpers;
use App\App;
use Exception;
// Include Session class
@ -44,6 +45,9 @@ class Theme
$configContent = <<<'EOT'
<?php
global $config;
$siteName = (string)$config['site_name'];
/**
* Theme Configuration
*
@ -77,7 +81,7 @@ return [
// Theme configuration defaults
'default_config' => [
'name' => 'Unnamed Theme',
'description' => 'A Jilo Web theme',
'description' => sprintf('A %s theme', $siteName),
'version' => '1.0.0',
'author' => 'Lindeas Inc.',
'screenshot' => 'screenshot.png',
@ -297,9 +301,9 @@ EOT;
return $cache[$themeId];
}
$config = self::getConfig();
$defaults = $config['default_config'] ?? [];
$availableEntry = $config['available_themes'][$themeId] ?? null;
$themeConfig = self::getConfig();
$defaults = $themeConfig['default_config'] ?? [];
$availableEntry = $themeConfig['available_themes'][$themeId] ?? null;
$metadata = [
'name' => is_array($availableEntry) ? ($availableEntry['name'] ?? ucfirst($themeId)) : ($availableEntry ?? ucfirst($themeId)),
@ -318,7 +322,7 @@ EOT;
}
if ($themeId !== 'default') {
$themesDir = rtrim($config['paths']['themes'] ?? (__DIR__ . '/../../themes'), '/');
$themesDir = rtrim($themeConfig['paths']['themes'] ?? (__DIR__ . '/../../themes'), '/');
$themeConfigPath = $themesDir . '/' . $themeId . '/config.php';
if (file_exists($themeConfigPath)) {
$themeConfig = require $themeConfigPath;
@ -328,21 +332,24 @@ EOT;
}
}
$appConfig = App::config();
$siteName = (string)$appConfig['site_name'];
if (empty($metadata['description'])) {
$metadata['description'] = $defaults['description'] ?? 'A Jilo Web theme';
$metadata['description'] = $defaults['description'] ?? ('A ' . $siteName . ' theme');
}
if (empty($metadata['version'])) {
$metadata['version'] = $defaults['version'] ?? '1.0.0';
}
if (empty($metadata['author'])) {
$metadata['author'] = $defaults['author'] ?? 'Lindeas';
$metadata['author'] = $defaults['author'] ?? $siteName;
}
if (empty($metadata['tags']) || !is_array($metadata['tags'])) {
$metadata['tags'] = [];
}
$paths = $config['paths'] ?? [];
$paths = $themeConfig['paths'] ?? [];
if ($themeId === 'default') {
$absolutePath = realpath($paths['templates'] ?? (__DIR__ . '/../templates')) ?: null;
} else {

View File

@ -51,10 +51,10 @@ php scripts/maintenance.php status
Notes:
- The maintenance flag is stored at `app/.maintenance.flag`.
- You can also control maintenance via environment variables (useful when the filesystem is read-only):
- `APP_MAINTENANCE=1` enables maintenance mode
- `APP_MAINTENANCE_MESSAGE="Your message"` sets the banner message
- The CLI script ultimately calls `App\Core\Maintenance`, which persists the flag in the database when a connection is available and falls back to a file flag at `app/.maintenance.flag` when not.
- Runtime checks also honor environment overrides:
- `APP_MAINTENANCE=1` forces maintenance mode on
- `APP_MAINTENANCE_MESSAGE="Your message"` sets/overrides the banner message
## Authoring new migrations

View File

@ -9,7 +9,7 @@ use App\App;
*/
class Register {
/**
* @var PDO $db The database connection instance.
* @var PDO|null $db The database connection instance.
*/
private $db;
private $rateLimiter;
@ -18,14 +18,16 @@ class Register {
/**
* Register constructor.
* Initializes the database connection using App API.
*
* @param PDO|null $database The database connection (optional, will use App::db() if not provided).
*/
public function __construct() {
$this->db = App::db();
public function __construct($database = null) {
$this->db = $database instanceof PDO ? $database : App::db();
require_once APP_PATH . 'classes/ratelimiter.php';
require_once APP_PATH . 'classes/twoFactorAuth.php';
$this->rateLimiter = new RateLimiter();
$this->rateLimiter = new RateLimiter($this->db);
$this->twoFactorAuth = new TwoFactorAuthentication($this->db);
}

View File

@ -11,9 +11,9 @@ use App\Core\ConfigLoader;
use App\Core\DatabaseConnector;
use App\Core\MigrationRunner;
function printUsage()
function printUsage(string $siteName)
{
echo "\nJilo Web - Database Migrations\n";
echo "\n{$siteName} - Database Migrations\n";
echo "Usage:\n";
echo " php scripts/migrate.php status # Show pending and applied migrations\n";
echo " php scripts/migrate.php up # Apply all pending migrations\n";
@ -81,7 +81,7 @@ try {
}
exit(0);
} else {
printUsage();
printUsage((string)$config['site_name']);
exit(1);
}
} catch (Throwable $e) {

View File

@ -22,19 +22,12 @@ class RateLimitMiddlewareTest extends TestCase
global $user_IP;
$user_IP = '8.8.8.8';
// Prepare DB for Github CI
$host = defined('CI_DB_HOST') ? CI_DB_HOST : '127.0.0.1';
$password = defined('CI_DB_PASSWORD') ? CI_DB_PASSWORD : '';
// Normalize server context for middleware checks
seed_test_server_context(['REMOTE_ADDR' => $user_IP]);
// Set up test database
$this->db = new Database([
'type' => 'mariadb',
'host' => $host,
'port' => '3306',
'dbname' => 'jilo_test',
'user' => 'test_jilo',
'password' => $password
]);
// Set up test database from centralized config
$dbConfig = test_db_config();
$this->db = new Database($dbConfig);
// Set up App::db() for RateLimiter
App::set('db', $this->db->getConnection());

View File

@ -1,7 +1,5 @@
<?php
require_once dirname(__DIR__, 3) . '/app/classes/database.php';
use PHPUnit\Framework\TestCase;
class DatabaseTest extends TestCase
@ -12,14 +10,7 @@ class DatabaseTest extends TestCase
{
parent::setUp();
// Set development environment for detailed errors
global $config;
$config['environment'] = 'development';
$this->config = [
'type' => 'sqlite',
'dbFile' => ':memory:'
];
$this->config = test_db_config();
}
public function testDatabaseConnection()
@ -30,102 +21,62 @@ class DatabaseTest extends TestCase
public function testMysqlAndMariadbEquivalence()
{
// Test that mysql and mariadb are treated the same
$mysqlConfig = [
'type' => 'mysql',
'host' => 'invalid-host',
'port' => 3306,
'dbname' => 'test',
'user' => 'test',
'password' => 'test'
];
$baseConfig = test_db_config();
$mariadbConfig = [
'type' => 'mariadb',
'host' => 'invalid-host',
'port' => 3306,
'dbname' => 'test',
'user' => 'test',
'password' => 'test'
];
$mysqlConfig = array_merge($baseConfig, ['type' => 'mysql']);
$mariadbConfig = array_merge($baseConfig, ['type' => 'mariadb']);
// Both should fail to connect and return null
// Both should connect successfully
$mysqlDb = new Database($mysqlConfig);
$this->assertNull($mysqlDb->getConnection());
$mariadbDb = new Database($mariadbConfig);
$mariaDb = new Database($mariadbConfig);
$this->assertNull($mariaDb->getConnection());
$this->assertNotNull($mysqlDb->getConnection());
$this->assertNotNull($mariadbDb->getConnection());
}
public function testInvalidDatabaseType()
{
$invalidConfig = [
'type' => 'invalid',
'host' => 'localhost',
'port' => 3306,
'dbname' => 'test',
'user' => 'test',
'password' => 'test'
];
require_once dirname(__DIR__, 3) . '/app/includes/errors.php';
global $config;
$config = ['environment' => 'development'];
$invalidDb = new Database($invalidConfig);
$this->assertNull($invalidDb->getConnection());
$invalidConfig = array_merge(test_db_config(), ['type' => 'invalid']);
try {
$db = new Database($invalidConfig);
$connection = $db->getConnection();
$this->assertNull($connection, 'Connection should be null for invalid database type');
} catch (Exception $e) {
// Either an exception or null connection is acceptable
$this->assertTrue(true);
}
}
public function testMySQLConnectionMissingData()
public function testMysqlConnectionMissingData()
{
$config = test_db_config();
$config['type'] = 'mysql';
$config['user'] = '';
$this->expectException(Exception::class);
$this->expectExceptionMessage('MySQL connection data is missing');
$config = [
'type' => 'mysql',
'host' => 'localhost',
'port' => 3306,
'dbname' => 'test',
// Missing user parameter
'password' => 'test'
];
new Database($config);
}
public function testPrepareAndExecute()
{
$db = new Database($this->config);
// Create test table
$db->execute('CREATE TABLE test (id INTEGER PRIMARY KEY, name TEXT)');
// Test prepare and execute
$result = $db->execute('INSERT INTO test (name) VALUES (?)', ['test_name']);
$this->assertEquals(1, $result->rowCount());
// Verify insertion
$result = $db->execute('SELECT name FROM test WHERE id = ?', [1]);
$row = $result->fetch(PDO::FETCH_ASSOC);
$this->assertEquals('test_name', $row['name']);
$pdo = $db->getConnection();
$stmt = $pdo->prepare("SELECT 1");
$this->assertTrue($stmt->execute());
}
public function testTransaction()
{
$db = new Database($this->config);
// Create test table
$db->execute('CREATE TABLE test (id INTEGER PRIMARY KEY, name TEXT)');
// Test successful transaction
$db->beginTransaction();
$db->execute('INSERT INTO test (name) VALUES (?)', ['transaction_test']);
$db->commit();
$result = $db->execute('SELECT COUNT(*) as count FROM test');
$this->assertEquals(1, $result->fetch(PDO::FETCH_ASSOC)['count']);
// Test rollback
$db->beginTransaction();
$db->execute('INSERT INTO test (name) VALUES (?)', ['rollback_test']);
$db->rollBack();
$result = $db->execute('SELECT COUNT(*) as count FROM test');
$this->assertEquals(1, $result->fetch(PDO::FETCH_ASSOC)['count']);
$pdo = $db->getConnection();
$this->assertTrue($pdo->beginTransaction());
$this->assertTrue($pdo->commit());
}
}

View File

@ -106,19 +106,12 @@ class LogTest extends TestCase
{
parent::setUp();
// Prepare DB for Github CI
$host = defined('CI_DB_HOST') ? CI_DB_HOST : '127.0.0.1';
$password = defined('CI_DB_PASSWORD') ? CI_DB_PASSWORD : '';
// Ensure consistent server context for log metadata
seed_test_server_context();
// Set up test database
$this->db = new Database([
'type' => 'mariadb',
'host' => $host,
'port' => '3306',
'dbname' => 'jilo_test',
'user' => 'test_jilo',
'password' => $password
]);
$dbConfig = test_db_config();
$this->db = new Database($dbConfig);
$connection = $this->db->getConnection();

View File

@ -17,19 +17,10 @@ class RateLimiterTest extends TestCase
{
parent::setUp();
// Prepare DB for Github CI
$host = defined('CI_DB_HOST') ? CI_DB_HOST : '127.0.0.1';
$password = defined('CI_DB_PASSWORD') ? CI_DB_PASSWORD : '';
$dbConfig = test_db_config();
// Set up test database
$this->db = new Database([
'type' => 'mariadb',
'host' => $host,
'port' => '3306',
'dbname' => 'jilo_test',
'user' => 'test_jilo',
'password' => $password
]);
$this->db = new Database($dbConfig);
// Set up App::db() for RateLimiter
App::set('db', $this->db->getConnection());

View File

@ -19,18 +19,10 @@ class UserRegisterTest extends TestCase
{
parent::setUp();
// Prepare DB for Github CI
$host = defined('CI_DB_HOST') ? CI_DB_HOST : '127.0.0.1';
$password = defined('CI_DB_PASSWORD') ? CI_DB_PASSWORD : '';
test_set_app_config();
$this->db = new Database([
'type' => 'mariadb',
'host' => $host,
'port' => '3306',
'dbname' => 'jilo_test',
'user' => 'test_jilo',
'password' => $password
]);
$dbConfig = test_db_config();
$this->db = new Database($dbConfig);
// Set up App::db() for Register class to use
App::set('db', $this->db->getConnection());

View File

@ -19,18 +19,8 @@ class UserTest extends TestCase
{
parent::setUp();
// Prepare DB for Github CI
$host = defined('CI_DB_HOST') ? CI_DB_HOST : '127.0.0.1';
$password = defined('CI_DB_PASSWORD') ? CI_DB_PASSWORD : '';
$this->db = new Database([
'type' => 'mariadb',
'host' => $host,
'port' => '3306',
'dbname' => 'jilo_test',
'user' => 'test_jilo',
'password' => $password
]);
$dbConfig = test_db_config();
$this->db = new Database($dbConfig);
// Set up App::db() for Register class to use
App::set('db', $this->db->getConnection());

View File

@ -0,0 +1,188 @@
<?php
namespace Unit\Plugins;
use PDO;
use PHPUnit\Framework\TestCase;
use App\Core\PluginManager;
use App\App;
require_once __DIR__ . '/../../../app/core/App.php';
require_once __DIR__ . '/../../../app/core/PluginManager.php';
class PluginManagerTest extends TestCase
{
/**
* Register plugin bootstraps closures into global hook arrays, which PHPUnit
* cannot serialize when backing up globals. Disable the backup for this test
* suite so we can exercise plugins that rely on closures.
*/
protected $backupGlobals = false;
protected $backupStaticAttributes = false;
private static $pdo;
private static $originalTables = [];
private static $testPlugin = 'register';
private static $tablePattern = 'register_%';
public static function setUpBeforeClass(): void
{
// Use centralized test configuration
$dbConfig = test_db_config();
$dsn = sprintf(
'mysql:host=%s;port=%s;dbname=%s;charset=utf8',
$dbConfig['host'],
$dbConfig['port'],
$dbConfig['dbname']
);
self::$pdo = new PDO($dsn, $dbConfig['user'], $dbConfig['password']);
self::$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// Setup test database schema
setupTestDatabaseSchema(self::$pdo);
// Store original plugin-owned tables (if any) to clean up later
self::$originalTables = self::listPluginTables();
// Set up App::db() for PluginManager (it now uses App API)
App::set('db', self::$pdo);
// Also set $GLOBALS['db'] for legacy fallback tests
$GLOBALS['db'] = self::$pdo;
// Initialize PluginManager catalog
$pluginsDir = __DIR__ . '/../../../plugins';
PluginManager::load($pluginsDir);
}
public function testDatabasePluginStateManagement(): void
{
// Test initial state
$initialState = PluginManager::isEnabled(self::$testPlugin);
$this->assertIsBool($initialState, 'PluginManager::isEnabled should return boolean');
// Test enabling plugin via database
$enableResult = PluginManager::setEnabled(self::$testPlugin, true);
$this->assertTrue($enableResult, 'Should be able to enable plugin');
// Verify state is persisted in database
$stmt = self::$pdo->prepare('SELECT `value` FROM settings WHERE `key` = :key LIMIT 1');
$key = 'plugin_enabled_' . self::$testPlugin;
$stmt->execute([':key' => $key]);
$result = $stmt->fetch(PDO::FETCH_ASSOC);
$this->assertNotFalse($result, 'Plugin state should be stored in database');
$this->assertEquals('1', $result['value'], 'Enabled plugin should have value "1"');
// Test isEnabled reads from database
$this->assertTrue(PluginManager::isEnabled(self::$testPlugin), 'Plugin should be enabled after database update');
// Test disabling plugin via database
$disableResult = PluginManager::setEnabled(self::$testPlugin, false);
$this->assertTrue($disableResult, 'Should be able to disable plugin');
// Verify state is updated in database
$stmt->execute([':key' => $key]);
$result = $stmt->fetch(PDO::FETCH_ASSOC);
$this->assertNotFalse($result, 'Plugin state should still be in database');
$this->assertEquals('0', $result['value'], 'Disabled plugin should have value "0"');
// Test isEnabled reads disabled state
$this->assertFalse(PluginManager::isEnabled(self::$testPlugin), 'Plugin should be disabled after database update');
}
public function testPluginInstall(): void
{
// Ensure plugin is disabled first
PluginManager::setEnabled(self::$testPlugin, false);
$beforeTables = self::listPluginTables();
$installResult = PluginManager::install(self::$testPlugin);
$this->assertTrue($installResult, 'Plugin installation should succeed');
$afterTables = self::listPluginTables();
$this->assertSame($beforeTables, $afterTables, 'Register plugin should not create dedicated tables');
}
public function testPluginPurge(): void
{
// First install the plugin to have something to purge
PluginManager::install(self::$testPlugin);
// Test plugin purge
$purgeResult = PluginManager::purge(self::$testPlugin);
$this->assertTrue($purgeResult, 'Plugin purge should succeed');
// Mark plugin as enabled so purge has work to do
PluginManager::setEnabled(self::$testPlugin, true);
$stmt = self::$pdo->prepare('SELECT `value` FROM settings WHERE `key` = :key LIMIT 1');
$key = 'plugin_enabled_' . self::$testPlugin;
$stmt->execute([':key' => $key]);
$this->assertEquals('1', $stmt->fetchColumn(), 'Plugin should be enabled before purge');
// Verify plugin purge
$purgeResult = PluginManager::purge(self::$testPlugin);
$this->assertTrue($purgeResult, 'Plugin purge should succeed');
$stmt->execute([':key' => $key]);
$this->assertFalse($stmt->fetch(), 'Plugin settings should be removed after purge');
$this->assertFalse(PluginManager::isEnabled(self::$testPlugin), 'Plugin should be disabled after purge');
$this->assertSame(self::$originalTables, self::listPluginTables(), 'Register plugin should not leave residual tables');
}
public function testFallbackToManifestWhenDatabaseUnavailable(): void
{
// Temporarily unset both App::db() and global database connection
$originalDb = $GLOBALS['db'] ?? null;
unset($GLOBALS['db']);
App::reset('db');
// Test fallback to manifest
$result = PluginManager::isEnabled(self::$testPlugin);
$this->assertIsBool($result, 'Should fallback to manifest when database unavailable');
// Restore database connection
$GLOBALS['db'] = $originalDb;
App::set('db', self::$pdo);
}
public static function tearDownAfterClass(): void
{
// Clean up test data
if (self::$pdo) {
// Remove plugin settings
$stmt = self::$pdo->prepare('DELETE FROM settings WHERE `key` LIKE :pattern');
$stmt->execute([':pattern' => 'plugin_enabled_' . self::$testPlugin]);
$currentTables = self::listPluginTables();
if (!empty($currentTables)) {
self::$pdo->exec('SET FOREIGN_KEY_CHECKS=0');
foreach ($currentTables as $table) {
if (!in_array($table, self::$originalTables, true)) {
self::$pdo->exec("DROP TABLE IF EXISTS `$table`");
}
}
self::$pdo->exec('SET FOREIGN_KEY_CHECKS=1');
}
// Clean up PDO connection to prevent serialization errors
self::$pdo = null;
}
// Reset App state
App::reset('db');
}
private static function listPluginTables(): array
{
if (!self::$pdo) {
return [];
}
$pattern = str_replace("'", "\\'", self::$tablePattern);
$stmt = self::$pdo->query("SHOW TABLES LIKE '$pattern'");
return $stmt->fetchAll(PDO::FETCH_COLUMN, 0);
}
}

View File

@ -17,10 +17,36 @@ if (!defined('APP_PATH')) {
define('APP_PATH', dirname(__DIR__) . '/app/');
}
// load the main App registry and plugin route registry
// load the main App registry, hook dispatcher, and plugin route registry
require_once __DIR__ . '/../app/core/App.php';
require_once __DIR__ . '/../app/core/HookDispatcher.php';
require_once __DIR__ . '/../app/core/PluginRouteRegistry.php';
// Define hook helpers used by plugin bootstraps
if (!function_exists('register_hook')) {
function register_hook(string $hook, callable $callback): void {
\App\Core\HookDispatcher::register($hook, $callback);
}
}
if (!function_exists('do_hook')) {
function do_hook(string $hook, array $context = []): void {
\App\Core\HookDispatcher::dispatch($hook, $context);
}
}
if (!function_exists('filter_public_pages')) {
function filter_public_pages(array $pages): array {
return \App\Core\HookDispatcher::applyFilters('filter_public_pages', $pages);
}
}
if (!function_exists('filter_allowed_urls')) {
function filter_allowed_urls(array $urls): array {
return \App\Core\HookDispatcher::applyFilters('filter_allowed_urls', $urls);
}
}
// Define plugin route registration function used by plugin bootstraps
if (!function_exists('register_plugin_route_prefix')) {
function register_plugin_route_prefix(string $prefix, array $definition = []): void {
@ -42,8 +68,8 @@ require_once __DIR__ . '/vendor/autoload.php';
// Ensure core NullLogger is available during tests
require_once __DIR__ . '/../app/core/NullLogger.php';
// Set error reporting
error_reporting(E_ALL);
// Set error reporting (suppress deprecations from vendor libs during tests)
error_reporting(E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED);
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
@ -51,11 +77,13 @@ ini_set('display_startup_errors', 1);
date_default_timezone_set('UTC');
// Define global variables needed by the application
$GLOBALS['app_root'] = '/';
$GLOBALS['config'] = [
'site_name' => getenv('TEST_SITE_NAME') ?: 'Jilo-web Test',
'domain' => getenv('TEST_SITE_DOMAIN') ?: 'localhost',
'folder' => getenv('TEST_SITE_FOLDER') ?: '/jilo-web',
'db_type' => getenv('DB_TYPE') ?: 'mariadb',
'sql' => [
'sql_host' => getenv('DB_HOST') ?: 'localhost',
'sql_host' => getenv('DB_HOST') ?: '127.0.0.1',
'sql_port' => getenv('DB_PORT') ?: '3306',
'sql_database' => getenv('DB_DATABASE') ?: 'jilo_test',
'sql_username' => getenv('DB_USERNAME') ?: 'test_jilo',
@ -63,6 +91,113 @@ $GLOBALS['config'] = [
],
'environment' => 'testing'
];
$GLOBALS['app_root'] = $GLOBALS['config']['folder'] ?: '/';
\App\App::set('config', $GLOBALS['config']);
\App\App::set('app_root', $GLOBALS['app_root']);
$GLOBALS['_TEST_BASE_CONFIG'] = $GLOBALS['config'];
if (!function_exists('test_site_branding')) {
function test_site_branding(): array
{
$config = test_app_config();
return [
'site_name' => (string)$config['site_name'],
'domain' => (string)$config['domain'],
'folder' => (string)($config['folder'] ?? ''),
];
}
}
if (!function_exists('test_app_config')) {
function test_app_config(?string $key = null)
{
$config = $GLOBALS['config'];
if ($key === null) {
return $config;
}
return $config[$key] ?? null;
}
}
if (!function_exists('test_db_config')) {
function test_db_config(): array
{
$config = test_app_config();
$sql = $config['sql'] ?? [];
if (defined('CI_DB_HOST')) {
$sql['sql_host'] = CI_DB_HOST;
}
if (defined('CI_DB_PORT')) {
$sql['sql_port'] = CI_DB_PORT;
}
if (defined('CI_DB_DATABASE')) {
$sql['sql_database'] = CI_DB_DATABASE;
}
if (defined('CI_DB_USERNAME')) {
$sql['sql_username'] = CI_DB_USERNAME;
}
if (defined('CI_DB_PASSWORD')) {
$sql['sql_password'] = CI_DB_PASSWORD;
}
return [
'type' => (string)($config['db_type'] ?? 'mariadb'),
'host' => (string)($sql['sql_host'] ?? '127.0.0.1'),
'port' => (string)($sql['sql_port'] ?? '3306'),
'dbname' => (string)($sql['sql_database'] ?? 'jilo_test'),
'user' => (string)($sql['sql_username'] ?? 'test_jilo'),
'password' => (string)($sql['sql_password'] ?? ''),
];
}
}
if (!function_exists('test_set_app_config')) {
function test_set_app_config(array $overrides = [], bool $mergeWithCurrent = false): array
{
$base = $mergeWithCurrent
? test_app_config()
: ($GLOBALS['_TEST_BASE_CONFIG'] ?? test_app_config());
$config = array_replace_recursive($base, $overrides);
$GLOBALS['config'] = $config;
\App\App::set('config', $config);
$folder = $config['folder'] ?? '/';
$GLOBALS['app_root'] = $folder ?: '/';
\App\App::set('app_root', $GLOBALS['app_root']);
return $config;
}
}
if (!function_exists('seed_test_server_context')) {
function seed_test_server_context(array $overrides = []): void
{
$config = test_app_config();
$folder = rtrim((string)($config['folder'] ?? ''), '/');
$defaultUri = ($folder ?: '') . '/index.php';
$defaults = [
'PHP_SELF' => '/index.php',
'REMOTE_ADDR' => '127.0.0.1',
'HTTP_USER_AGENT' => 'PHPUnit Test Browser',
'HTTPS' => 'on',
'HTTP_HOST' => (string)($config['domain'] ?? 'localhost'),
'REQUEST_URI' => $defaultUri ?: '/index.php',
];
foreach ($defaults as $key => $value) {
if (!isset($_SERVER[$key])) {
$_SERVER[$key] = $value;
}
}
foreach ($overrides as $key => $value) {
$_SERVER[$key] = $value;
}
}
}
// Define global connectDB function
if (!function_exists('connectDB')) {
@ -74,11 +209,7 @@ if (!function_exists('connectDB')) {
}
}
// Set up server variables
$_SERVER['PHP_SELF'] = '/index.php';
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
$_SERVER['HTTP_USER_AGENT'] = 'PHPUnit Test Browser';
$_SERVER['HTTPS'] = 'on';
seed_test_server_context();
/**
* Setup test database schema by applying main.sql and migrations
@ -129,7 +260,9 @@ function setupTestDatabaseSchema(PDO $pdo): void
// - Table doesn't exist (plugin tables not yet created)
$errorMsg = $e->getMessage();
if (strpos($errorMsg, 'Duplicate column') === false &&
strpos($errorMsg, "doesn't exist") === false) {
strpos($errorMsg, "doesn't exist") === false &&
strpos($errorMsg, 'Duplicate key') === false &&
strpos($errorMsg, 'errno: 121') === false) {
throw $e;
}
}

View File

@ -26,11 +26,5 @@
</coverage>
<php>
<env name="APP_ENV" value="testing"/>
<env name="DB_TYPE" value="mariadb"/>
<env name="DB_HOST" value="localhost"/>
<env name="DB_PORT" value="3306"/>
<env name="DB_DATABASE" value="jilo_test"/>
<env name="DB_USERNAME" value="test_jilo"/>
<env name="DB_PASSWORD" value=""/>
</php>
</phpunit>

View File

@ -1,8 +1,11 @@
<?php
global $config;
$siteName = (string)$config['site_name'];
return [
'name' => 'Modern theme',
'description' => 'Example theme. A modern, clean theme for Jilo Web.',
'description' => sprintf('Example theme. A modern, clean theme for %s.', $siteName),
'version' => '1.0.0',
'author' => 'Lindeas Inc.',
'screenshot' => 'screenshot.png',