Compare commits

..

3 Commits

8 changed files with 191 additions and 165 deletions

View File

@ -10,6 +10,14 @@ jobs:
test: test:
runs-on: ubuntu-latest runs-on: ubuntu-latest
env:
DB_TYPE: mariadb
DB_HOST: 127.0.0.1
DB_PORT: 3306
DB_DATABASE: jilo_test
DB_USERNAME: test_jilo
DB_PASSWORD: test_password
services: services:
mariadb: mariadb:
image: mariadb:10.6 image: mariadb:10.6

View File

@ -17,7 +17,7 @@ CREATE TABLE `user` (
`password` varchar(100) NOT NULL, `password` varchar(100) NOT NULL,
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`) UNIQUE KEY `username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
INSERT INTO `user` (`id`, `username`, `password`) VALUES INSERT INTO `user` (`id`, `username`, `password`) VALUES
(1,'demo','$2y$12$AtIKs3eVxD4wTT1IWwJujuuHyGhhmfBJYqSfIrPFFPMDfKu3Rcsx6'), (1,'demo','$2y$12$AtIKs3eVxD4wTT1IWwJujuuHyGhhmfBJYqSfIrPFFPMDfKu3Rcsx6'),
@ -36,7 +36,7 @@ CREATE TABLE `user_meta` (
PRIMARY KEY (`id`,`user_id`) USING BTREE, PRIMARY KEY (`id`,`user_id`) USING BTREE,
KEY `user_id` (`user_id`), KEY `user_id` (`user_id`),
CONSTRAINT `user_meta_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) CONSTRAINT `user_meta_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
INSERT INTO `user_meta` (`id`, `user_id`, `name`, `email`, `timezone`, `avatar`, `bio`) VALUES INSERT INTO `user_meta` (`id`, `user_id`, `name`, `email`, `timezone`, `avatar`, `bio`) VALUES
(1,1,'demo admin user','admin@example.com',NULL,NULL,'This is a demo user of the demo install of Jilo Web'), (1,1,'demo admin user','admin@example.com',NULL,NULL,'This is a demo user of the demo install of Jilo Web'),
@ -48,7 +48,7 @@ CREATE TABLE `right` (
`name` varchar(255) NOT NULL, `name` varchar(255) NOT NULL,
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`) UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
INSERT INTO `right` (`id`, `name`) VALUES INSERT INTO `right` (`id`, `name`) VALUES
(1, 'superuser'), (1, 'superuser'),
@ -75,7 +75,7 @@ CREATE TABLE `user_right` (
KEY `fk_right_id` (`right_id`), KEY `fk_right_id` (`right_id`),
CONSTRAINT `fk_right_id` FOREIGN KEY (`right_id`) REFERENCES `right` (`id`), CONSTRAINT `fk_right_id` FOREIGN KEY (`right_id`) REFERENCES `right` (`id`),
CONSTRAINT `fk_user_id` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) CONSTRAINT `fk_user_id` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- -------------------------------------------------------- -- --------------------------------------------------------
CREATE TABLE `user_2fa` ( CREATE TABLE `user_2fa` (
@ -87,7 +87,7 @@ CREATE TABLE `user_2fa` (
`last_used` datetime DEFAULT NULL, `last_used` datetime DEFAULT NULL,
PRIMARY KEY (`user_id`), PRIMARY KEY (`user_id`),
CONSTRAINT `fk_user_2fa_id` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) CONSTRAINT `fk_user_2fa_id` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- -------------------------------------------------------- -- --------------------------------------------------------
CREATE TABLE `user_2fa_temp` ( CREATE TABLE `user_2fa_temp` (
@ -97,7 +97,7 @@ CREATE TABLE `user_2fa_temp` (
`expires_at` datetime NOT NULL, `expires_at` datetime NOT NULL,
PRIMARY KEY (`user_id`, `code`), PRIMARY KEY (`user_id`, `code`),
CONSTRAINT `fk_user_2fa_temp_id` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) CONSTRAINT `fk_user_2fa_temp_id` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- -------------------------------------------------------- -- --------------------------------------------------------
CREATE TABLE `user_password_reset` ( CREATE TABLE `user_password_reset` (
@ -109,7 +109,7 @@ CREATE TABLE `user_password_reset` (
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT `fk_user_password_reset` FOREIGN KEY (`user_id`) REFERENCES `user`(`id`), CONSTRAINT `fk_user_password_reset` FOREIGN KEY (`user_id`) REFERENCES `user`(`id`),
UNIQUE KEY `token_idx` (`token`) UNIQUE KEY `token_idx` (`token`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- --
-- Login security -- Login security
@ -123,7 +123,7 @@ CREATE TABLE `security_rate_auth` (
`attempted_at` datetime DEFAULT current_timestamp(), `attempted_at` datetime DEFAULT current_timestamp(),
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
KEY `idx_ip_username` (`ip_address`,`username`) KEY `idx_ip_username` (`ip_address`,`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- -------------------------------------------------------- -- --------------------------------------------------------
CREATE TABLE `security_rate_page` ( CREATE TABLE `security_rate_page` (
@ -134,7 +134,7 @@ CREATE TABLE `security_rate_page` (
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
KEY `idx_ip_endpoint` (`ip_address`,`endpoint`), KEY `idx_ip_endpoint` (`ip_address`,`endpoint`),
KEY `idx_request_time` (`request_time`) KEY `idx_request_time` (`request_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- -------------------------------------------------------- -- --------------------------------------------------------
CREATE TABLE `security_ip_blacklist` ( CREATE TABLE `security_ip_blacklist` (
@ -147,7 +147,7 @@ CREATE TABLE `security_ip_blacklist` (
`created_by` varchar(255) DEFAULT NULL, `created_by` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
UNIQUE KEY `unique_ip` (`ip_address`) UNIQUE KEY `unique_ip` (`ip_address`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
INSERT INTO `security_ip_blacklist` (`id`, `ip_address`, `is_network`, `reason`, `expiry_time`, `created_at`, `created_by`) VALUES INSERT INTO `security_ip_blacklist` (`id`, `ip_address`, `is_network`, `reason`, `expiry_time`, `created_at`, `created_by`) VALUES
(1, '0.0.0.0/8', 1, 'Reserved address space - RFC 1122', NULL, '2025-01-03 16:40:15', 'system'), (1, '0.0.0.0/8', 1, 'Reserved address space - RFC 1122', NULL, '2025-01-03 16:40:15', 'system'),
@ -166,7 +166,7 @@ CREATE TABLE `security_ip_whitelist` (
`created_by` varchar(255) DEFAULT NULL, `created_by` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
UNIQUE KEY `unique_ip` (`ip_address`) UNIQUE KEY `unique_ip` (`ip_address`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
INSERT INTO `security_ip_whitelist` (`id`, `ip_address`, `is_network`, `description`, `created_at`, `created_by`) VALUES INSERT INTO `security_ip_whitelist` (`id`, `ip_address`, `is_network`, `description`, `created_at`, `created_by`) VALUES
(1, '127.0.0.1', 0, 'localhost IPv4', '2025-01-03 16:40:15', 'system'), (1, '127.0.0.1', 0, 'localhost IPv4', '2025-01-03 16:40:15', 'system'),
@ -185,7 +185,7 @@ CREATE TABLE `jilo_agent_type` (
`description` varchar(255), `description` varchar(255),
`endpoint` varchar(255), `endpoint` varchar(255),
PRIMARY KEY (`id`) PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
INSERT INTO `jilo_agent_type` (`id`, `description`, `endpoint`) VALUES INSERT INTO `jilo_agent_type` (`id`, `description`, `endpoint`) VALUES
(1,'jvb','/jvb'), (1,'jvb','/jvb'),
(2,'jicofo','/jicofo'), (2,'jicofo','/jicofo'),
@ -201,7 +201,7 @@ CREATE TABLE `platform` (
`jilo_database` varchar(255) NOT NULL, `jilo_database` varchar(255) NOT NULL,
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`) UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
INSERT INTO `platform` (`id`, `name`, `jitsi_url`, `jilo_database`) VALUES INSERT INTO `platform` (`id`, `name`, `jitsi_url`, `jilo_database`) VALUES
(1,'example.com','https://meet.example.com','../../jilo/jilo.db'); (1,'example.com','https://meet.example.com','../../jilo/jilo.db');
@ -213,7 +213,7 @@ CREATE TABLE `host` (
`name` varchar(255), `name` varchar(255),
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
CONSTRAINT `host_ibfk_1` FOREIGN KEY (`platform_id`) REFERENCES `platform` (`id`) CONSTRAINT `host_ibfk_1` FOREIGN KEY (`platform_id`) REFERENCES `platform` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- -------------------------------------------------------- -- --------------------------------------------------------
CREATE TABLE `jilo_agent` ( CREATE TABLE `jilo_agent` (
@ -226,7 +226,7 @@ CREATE TABLE `jilo_agent` (
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
CONSTRAINT `jilo_agent_ibfk_1` FOREIGN KEY (`agent_type_id`) REFERENCES `jilo_agent_type` (`id`), CONSTRAINT `jilo_agent_ibfk_1` FOREIGN KEY (`agent_type_id`) REFERENCES `jilo_agent_type` (`id`),
CONSTRAINT `jilo_agent_ibfk_2` FOREIGN KEY (`host_id`) REFERENCES `host` (`id`) CONSTRAINT `jilo_agent_ibfk_2` FOREIGN KEY (`host_id`) REFERENCES `host` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- -------------------------------------------------------- -- --------------------------------------------------------
CREATE TABLE `jilo_agent_check` ( CREATE TABLE `jilo_agent_check` (
@ -238,7 +238,22 @@ CREATE TABLE `jilo_agent_check` (
`response_content` varchar(255), `response_content` varchar(255),
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
CONSTRAINT `jilo_agent_check_ibfk_1` FOREIGN KEY (`agent_id`) REFERENCES `jilo_agent` (`id`) CONSTRAINT `jilo_agent_check_ibfk_1` FOREIGN KEY (`agent_id`) REFERENCES `jilo_agent` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- --------------------------------------------------------
CREATE TABLE `settings` (
`key` VARCHAR(191) NOT NULL PRIMARY KEY,
`value` TEXT NULL,
`updated_at` DATETIME NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- --------------------------------------------------------
CREATE TABLE `migrations` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`migration` VARCHAR(255) NOT NULL UNIQUE,
`applied_at` DATETIME NOT NULL,
`batch` INT NOT NULL,
`content` TEXT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
COMMIT; COMMIT;

View File

@ -0,0 +1,17 @@
-- Migration: Creates tables that were missing in main.sql and were created by the code
-- This is needed for tests db auto-install
-- Created: 2026-01-21
CREATE TABLE IF NOT EXISTS `settings` (
`key` VARCHAR(191) NOT NULL PRIMARY KEY,
`value` TEXT NULL,
`updated_at` DATETIME NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS `migrations` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`migration` VARCHAR(255) NOT NULL UNIQUE,
`applied_at` DATETIME NOT NULL,
`batch` INT NOT NULL,
`content` TEXT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

View File

@ -11,4 +11,4 @@ CREATE TABLE IF NOT EXISTS `log` (
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
KEY `user_id` (`user_id`), KEY `user_id` (`user_id`),
CONSTRAINT `log_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) CONSTRAINT `log_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

View File

@ -121,44 +121,40 @@ class LogTest extends TestCase
]); ]);
$connection = $this->db->getConnection(); $connection = $this->db->getConnection();
// Ensure fresh log table schema (drop old schema if present)
$connection->exec("DROP TABLE IF EXISTS log"); $connection->exec("DROP TABLE IF EXISTS log");
$connection->exec("DROP TABLE IF EXISTS user");
// Create user table // Use centralized schema setup
$connection->exec(" setupTestDatabaseSchema($connection);
CREATE TABLE IF NOT EXISTS user (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(255) NOT NULL,
password VARCHAR(255) NOT NULL,
email VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
");
// Create log table with the expected schema from Log class // Load logs plugin migration to create log table
$connection->exec(" $logMigrationPath = __DIR__ . '/../../../plugins/logs/migrations/create_log_table.sql';
CREATE TABLE IF NOT EXISTS log ( if (file_exists($logMigrationPath)) {
id INT AUTO_INCREMENT PRIMARY KEY, $sql = file_get_contents($logMigrationPath);
user_id INT, $statements = array_filter(array_map('trim', explode(';', $sql)));
scope VARCHAR(50) NOT NULL DEFAULT 'user', foreach ($statements as $statement) {
message TEXT NOT NULL, if (!empty($statement)) {
time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, $connection->exec($statement);
FOREIGN KEY (user_id) REFERENCES user(id) }
) }
"); }
// Create test users with all required fields // Clean up any existing test data
$connection->exec("DELETE FROM log WHERE user_id >= 1000");
$connection->exec("DELETE FROM user WHERE id >= 1000");
// Create test users
$timestamp = time();
$connection->exec(" $connection->exec("
INSERT INTO user (username, password, email) INSERT INTO user (id, username, password)
VALUES VALUES
('testuser', 'password123', 'testuser@example.com'), (1000, 'testuser_log_{$timestamp}', 'password123'),
('testuser2', 'password123', 'testuser2@example.com') (1001, 'testuser2_log_{$timestamp}', 'password123')
"); ");
// Store test user ID for later use // Store test user ID for later use
$stmt = $this->db->getConnection()->query("SELECT id FROM user WHERE username = 'testuser' LIMIT 1"); $this->testUserId = 1000;
$user = $stmt->fetch(PDO::FETCH_ASSOC);
$this->testUserId = $user['id'];
// Create a TestLogger instance that will be used by the app's Log wrapper // Create a TestLogger instance that will be used by the app's Log wrapper
$this->log = new TestLogger($this->db); $this->log = new TestLogger($this->db);
@ -166,15 +162,15 @@ class LogTest extends TestCase
protected function tearDown(): void protected function tearDown(): void
{ {
// Drop tables in correct order (respect foreign key constraints) // Clean up test data
$this->db->getConnection()->exec("DROP TABLE IF EXISTS log"); $this->db->getConnection()->exec("DELETE FROM log WHERE user_id >= 1000");
$this->db->getConnection()->exec("DROP TABLE IF EXISTS user"); $this->db->getConnection()->exec("DELETE FROM user WHERE id >= 1000");
parent::tearDown(); parent::tearDown();
} }
public function testInsertLog() public function testInsertLog()
{ {
$result = $this->log->insertLog($this->testUserId, 'Test message', 'test'); $result = $this->log->insertLog($this->testUserId, 'Test message', 'user');
$this->assertTrue($result); $this->assertTrue($result);
// Verify the log was inserted // Verify the log was inserted
@ -182,7 +178,7 @@ class LogTest extends TestCase
$log = $stmt->fetch(PDO::FETCH_ASSOC); $log = $stmt->fetch(PDO::FETCH_ASSOC);
$this->assertEquals('Test message', $log['message']); $this->assertEquals('Test message', $log['message']);
$this->assertEquals('test', $log['scope']); $this->assertEquals('user', $log['scope']);
} }
public function testReadLog() public function testReadLog()

View File

@ -35,53 +35,14 @@ class UserRegisterTest extends TestCase
// Set up App::db() for Register class to use // Set up App::db() for Register class to use
App::set('db', $this->db->getConnection()); App::set('db', $this->db->getConnection());
// Create user table with MariaDB syntax // Use centralized schema setup
$this->db->getConnection()->exec(" setupTestDatabaseSchema($this->db->getConnection());
CREATE TABLE IF NOT EXISTS user (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(255) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
");
// Create user_meta table with MariaDB syntax // Clean up any test users from previous runs
$this->db->getConnection()->exec(" $this->db->getConnection()->exec("DELETE FROM user_2fa WHERE user_id >= 1000");
CREATE TABLE IF NOT EXISTS user_meta ( $this->db->getConnection()->exec("DELETE FROM security_rate_auth WHERE username LIKE 'testuser%'");
id INT PRIMARY KEY AUTO_INCREMENT, $this->db->getConnection()->exec("DELETE FROM user_meta WHERE user_id >= 1000");
user_id INT NOT NULL, $this->db->getConnection()->exec("DELETE FROM user WHERE id >= 1000");
name VARCHAR(255),
email VARCHAR(255),
timezone VARCHAR(100),
bio TEXT,
avatar VARCHAR(255),
FOREIGN KEY (user_id) REFERENCES user(id) ON DELETE CASCADE
)
");
// Create security_rate_auth table for rate limiting
$this->db->getConnection()->exec("
CREATE TABLE IF NOT EXISTS security_rate_auth (
id INT PRIMARY KEY AUTO_INCREMENT,
ip_address VARCHAR(45) NOT NULL,
username VARCHAR(255) NOT NULL,
attempted_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_ip_username (ip_address, username)
)
");
// Create user_2fa table for two-factor authentication
$this->db->getConnection()->exec("
CREATE TABLE IF NOT EXISTS user_2fa (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT NOT NULL,
secret_key VARCHAR(255) NOT NULL,
backup_codes TEXT,
enabled TINYINT(1) NOT NULL DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES user(id) ON DELETE CASCADE
)
");
$this->register = new Register(); $this->register = new Register();
$this->user = new User($this->db); $this->user = new User($this->db);
@ -92,18 +53,19 @@ class UserRegisterTest extends TestCase
// Clean up App state // Clean up App state
App::reset('db'); App::reset('db');
// Drop tables in correct order // Clean up test data
$this->db->getConnection()->exec("DROP TABLE IF EXISTS user_2fa"); $this->db->getConnection()->exec("DELETE FROM user_2fa WHERE user_id >= 1000");
$this->db->getConnection()->exec("DROP TABLE IF EXISTS security_rate_auth"); $this->db->getConnection()->exec("DELETE FROM security_rate_auth WHERE username LIKE 'testuser%'");
$this->db->getConnection()->exec("DROP TABLE IF EXISTS user_meta"); $this->db->getConnection()->exec("DELETE FROM user_meta WHERE user_id >= 1000");
$this->db->getConnection()->exec("DROP TABLE IF EXISTS user"); $this->db->getConnection()->exec("DELETE FROM user WHERE id >= 1000");
parent::tearDown(); parent::tearDown();
} }
public function testRegister() public function testRegister()
{ {
// Register a new user // Register a new user with unique username
$username = 'testuser'; $username = 'testuser_reg_' . time() . '_' . rand(1000, 9999);
$password = 'password123'; $password = 'password123';
$result = $this->register->register($username, $password); $result = $this->register->register($username, $password);
@ -129,8 +91,8 @@ class UserRegisterTest extends TestCase
public function testLogin() public function testLogin()
{ {
// First register a user // First register a user with unique username
$username = 'testuser'; $username = 'testuser_login_' . time() . '_' . rand(1000, 9999);
$password = 'password123'; $password = 'password123';
$this->register->register($username, $password); $this->register->register($username, $password);
@ -163,8 +125,8 @@ class UserRegisterTest extends TestCase
public function testGetUserDetails() public function testGetUserDetails()
{ {
// Register a test user first // First register a user with unique username
$username = 'testuser'; $username = 'testuser_details_' . time() . '_' . rand(1000, 9999);
$password = 'password123'; $password = 'password123';
$result = $this->register->register($username, $password); $result = $this->register->register($username, $password);
$this->assertTrue($result); $this->assertTrue($result);

View File

@ -35,53 +35,14 @@ class UserTest extends TestCase
// Set up App::db() for Register class to use // Set up App::db() for Register class to use
App::set('db', $this->db->getConnection()); App::set('db', $this->db->getConnection());
// Create user table with MariaDB syntax // Use centralized schema setup
$this->db->getConnection()->exec(" setupTestDatabaseSchema($this->db->getConnection());
CREATE TABLE IF NOT EXISTS user (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(255) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
");
// Create user_meta table with MariaDB syntax // Clean up any test users from previous runs
$this->db->getConnection()->exec(" $this->db->getConnection()->exec("DELETE FROM user_2fa WHERE user_id >= 1000");
CREATE TABLE IF NOT EXISTS user_meta ( $this->db->getConnection()->exec("DELETE FROM security_rate_auth WHERE username LIKE 'testuser%'");
id INT PRIMARY KEY AUTO_INCREMENT, $this->db->getConnection()->exec("DELETE FROM user_meta WHERE user_id >= 1000");
user_id INT NOT NULL, $this->db->getConnection()->exec("DELETE FROM user WHERE id >= 1000");
name VARCHAR(255),
email VARCHAR(255),
timezone VARCHAR(100),
bio TEXT,
avatar VARCHAR(255),
FOREIGN KEY (user_id) REFERENCES user(id) ON DELETE CASCADE
)
");
// Create security_rate_auth table for rate limiting
$this->db->getConnection()->exec("
CREATE TABLE IF NOT EXISTS security_rate_auth (
id INT PRIMARY KEY AUTO_INCREMENT,
ip_address VARCHAR(45) NOT NULL,
username VARCHAR(255) NOT NULL,
attempted_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_ip_username (ip_address, username)
)
");
// Create user_2fa table for two-factor authentication
$this->db->getConnection()->exec("
CREATE TABLE IF NOT EXISTS user_2fa (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT NOT NULL,
secret_key VARCHAR(255) NOT NULL,
backup_codes TEXT,
enabled TINYINT(1) NOT NULL DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES user(id) ON DELETE CASCADE
)
");
$this->user = new User($this->db); $this->user = new User($this->db);
$this->register = new Register(); $this->register = new Register();
@ -92,18 +53,19 @@ class UserTest extends TestCase
// Clean up App state // Clean up App state
App::reset('db'); App::reset('db');
// Drop tables in correct order // Clean up test data
$this->db->getConnection()->exec("DROP TABLE IF EXISTS user_2fa"); $this->db->getConnection()->exec("DELETE FROM user_2fa WHERE user_id >= 1000");
$this->db->getConnection()->exec("DROP TABLE IF EXISTS security_rate_auth"); $this->db->getConnection()->exec("DELETE FROM security_rate_auth WHERE username LIKE 'testuser%'");
$this->db->getConnection()->exec("DROP TABLE IF EXISTS user_meta"); $this->db->getConnection()->exec("DELETE FROM user_meta WHERE user_id >= 1000");
$this->db->getConnection()->exec("DROP TABLE IF EXISTS user"); $this->db->getConnection()->exec("DELETE FROM user WHERE id >= 1000");
parent::tearDown(); parent::tearDown();
} }
public function testLogin() public function testLogin()
{ {
// First register a user // First register a user with unique username
$username = 'testuser'; $username = 'testuser_login_' . time() . '_' . rand(1000, 9999);
$password = 'password123'; $password = 'password123';
$this->register->register($username, $password); $this->register->register($username, $password);
@ -136,8 +98,8 @@ class UserTest extends TestCase
public function testGetUserDetails() public function testGetUserDetails()
{ {
// Register a test user first // First register a user with unique username
$username = 'testuser'; $username = 'testuser_details_' . time() . '_' . rand(1000, 9999);
$password = 'password123'; $password = 'password123';
$result = $this->register->register($username, $password); $result = $this->register->register($username, $password);
$this->assertTrue($result); $this->assertTrue($result);

View File

@ -21,6 +21,13 @@ if (!defined('APP_PATH')) {
require_once __DIR__ . '/../app/core/App.php'; require_once __DIR__ . '/../app/core/App.php';
require_once __DIR__ . '/../app/core/PluginRouteRegistry.php'; require_once __DIR__ . '/../app/core/PluginRouteRegistry.php';
// 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 {
\App\Core\PluginRouteRegistry::registerPrefix($prefix, $definition);
}
}
// Load plugin Log model and IP helper early so fallback wrapper is bypassed // Load plugin Log model and IP helper early so fallback wrapper is bypassed
require_once __DIR__ . '/../app/helpers/ip_helper.php'; require_once __DIR__ . '/../app/helpers/ip_helper.php';
require_once __DIR__ . '/../app/helpers/logger_loader.php'; require_once __DIR__ . '/../app/helpers/logger_loader.php';
@ -72,3 +79,62 @@ $_SERVER['PHP_SELF'] = '/index.php';
$_SERVER['REMOTE_ADDR'] = '127.0.0.1'; $_SERVER['REMOTE_ADDR'] = '127.0.0.1';
$_SERVER['HTTP_USER_AGENT'] = 'PHPUnit Test Browser'; $_SERVER['HTTP_USER_AGENT'] = 'PHPUnit Test Browser';
$_SERVER['HTTPS'] = 'on'; $_SERVER['HTTPS'] = 'on';
/**
* Setup test database schema by applying main.sql and migrations
*
* @param PDO $pdo Database connection
* @return void
*/
function setupTestDatabaseSchema(PDO $pdo): void
{
// Apply main.sql schema
$mainSqlPath = __DIR__ . '/../doc/database/main.sql';
if (file_exists($mainSqlPath)) {
$sql = file_get_contents($mainSqlPath);
// Add IF NOT EXISTS to CREATE TABLE statements
$sql = preg_replace('/CREATE TABLE `/', 'CREATE TABLE IF NOT EXISTS `', $sql);
$statements = array_filter(array_map('trim', explode(';', $sql)));
foreach ($statements as $statement) {
if (!empty($statement)) {
try {
$pdo->exec($statement);
} catch (PDOException $e) {
// Skip errors for INSERT statements on existing data
if (strpos($statement, 'INSERT') === false) {
throw $e;
}
}
}
}
}
// Apply migrations from doc/database/migrations/ (excluding subfolders)
$migrationsDir = __DIR__ . '/../doc/database/migrations';
if (is_dir($migrationsDir)) {
$files = glob($migrationsDir . '/*.sql');
sort($files); // Apply in chronological order
foreach ($files as $file) {
$sql = file_get_contents($file);
// Add IF NOT EXISTS to CREATE TABLE statements
$sql = preg_replace('/CREATE TABLE `/', 'CREATE TABLE IF NOT EXISTS `', $sql);
$statements = array_filter(array_map('trim', explode(';', $sql)));
foreach ($statements as $statement) {
if (!empty($statement)) {
try {
$pdo->exec($statement);
} catch (PDOException $e) {
// Skip errors for:
// - Duplicate columns (already exists)
// - Table doesn't exist (plugin tables not yet created)
$errorMsg = $e->getMessage();
if (strpos($errorMsg, 'Duplicate column') === false &&
strpos($errorMsg, "doesn't exist") === false) {
throw $e;
}
}
}
}
}
}
}