Adds initial unit tests
parent
2da13af04c
commit
6fc3629014
|
@ -0,0 +1,40 @@
|
||||||
|
# Jilo Web Test Suite
|
||||||
|
|
||||||
|
This directory contains the test suite for the Jilo Web application. All testing-related files are isolated here to keep the main application clean.
|
||||||
|
|
||||||
|
## Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
tests/
|
||||||
|
├── framework/ # Test framework files
|
||||||
|
│ ├── composer.json # Composer configuration for tests
|
||||||
|
│ ├── phpunit.xml # PHPUnit configuration
|
||||||
|
│ ├── Unit/ # Unit tests
|
||||||
|
│ ├── Integration/ # Integration tests
|
||||||
|
│ └── TestCase.php # Base test case class
|
||||||
|
└── README.md # This file
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running Tests
|
||||||
|
|
||||||
|
1. Change to the framework directory:
|
||||||
|
```bash
|
||||||
|
cd tests/framework
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Install dependencies (first time only):
|
||||||
|
```bash
|
||||||
|
composer install
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Run all tests:
|
||||||
|
```bash
|
||||||
|
composer test
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Generate coverage report:
|
||||||
|
```bash
|
||||||
|
composer test-coverage
|
||||||
|
```
|
||||||
|
|
||||||
|
The coverage report will be generated in `tests/framework/coverage/`.
|
|
@ -0,0 +1,4 @@
|
||||||
|
/vendor/
|
||||||
|
/coverage/
|
||||||
|
.phpunit.result.cache
|
||||||
|
composer.lock
|
|
@ -0,0 +1,68 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
require_once __DIR__ . '/vendor/autoload.php';
|
||||||
|
|
||||||
|
class TestCase extends PHPUnit\Framework\TestCase
|
||||||
|
{
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
// Set up test environment
|
||||||
|
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
|
||||||
|
$_SERVER['HTTP_USER_AGENT'] = 'PHPUnit Test Browser';
|
||||||
|
|
||||||
|
// Include common app files
|
||||||
|
require_once dirname(__DIR__, 2) . '/app/includes/config.php';
|
||||||
|
require_once dirname(__DIR__, 2) . '/app/includes/functions.php';
|
||||||
|
|
||||||
|
// Clean up any existing session
|
||||||
|
if (session_status() === PHP_SESSION_ACTIVE) {
|
||||||
|
session_destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset session data
|
||||||
|
$_SESSION = [];
|
||||||
|
|
||||||
|
// Only start session if headers haven't been sent
|
||||||
|
if (!headers_sent() && session_status() === PHP_SESSION_NONE) {
|
||||||
|
session_start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function tearDown(): void
|
||||||
|
{
|
||||||
|
// Clean up session after each test
|
||||||
|
if (session_status() === PHP_SESSION_ACTIVE) {
|
||||||
|
$_SESSION = [];
|
||||||
|
session_destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
parent::tearDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method to start a new session if needed
|
||||||
|
*/
|
||||||
|
protected function ensureSession(): void
|
||||||
|
{
|
||||||
|
if (session_status() === PHP_SESSION_NONE && !headers_sent()) {
|
||||||
|
session_start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function captureHeaders(): array
|
||||||
|
{
|
||||||
|
$headers = [];
|
||||||
|
$callback = function($header) use (&$headers) {
|
||||||
|
$headers[] = $header;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Mock header function
|
||||||
|
if (!function_exists('header')) {
|
||||||
|
eval('function header($header) use ($callback) { $callback($header); }');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $headers;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,233 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
require_once dirname(__DIR__, 4) . '/app/classes/database.php';
|
||||||
|
require_once dirname(__DIR__, 4) . '/app/classes/agent.php';
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class AgentTest extends TestCase
|
||||||
|
{
|
||||||
|
private $db;
|
||||||
|
private $agent;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
// Set development environment for detailed errors
|
||||||
|
global $config;
|
||||||
|
$config['environment'] = 'development';
|
||||||
|
|
||||||
|
// Set up test database
|
||||||
|
$this->db = new Database([
|
||||||
|
'type' => 'sqlite',
|
||||||
|
'dbFile' => ':memory:'
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Create jilo_agents table
|
||||||
|
$this->db->getConnection()->exec("
|
||||||
|
CREATE TABLE jilo_agents (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
host_id INTEGER NOT NULL,
|
||||||
|
agent_type_id INTEGER NOT NULL,
|
||||||
|
url TEXT NOT NULL,
|
||||||
|
secret_key TEXT,
|
||||||
|
check_period INTEGER DEFAULT 60,
|
||||||
|
created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))
|
||||||
|
)
|
||||||
|
");
|
||||||
|
|
||||||
|
// Create jilo_agent_types table
|
||||||
|
$this->db->getConnection()->exec("
|
||||||
|
CREATE TABLE jilo_agent_types (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
description TEXT NOT NULL,
|
||||||
|
endpoint TEXT NOT NULL
|
||||||
|
)
|
||||||
|
");
|
||||||
|
|
||||||
|
// Create hosts table
|
||||||
|
$this->db->getConnection()->exec("
|
||||||
|
CREATE TABLE hosts (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
platform_id INTEGER NOT NULL,
|
||||||
|
name TEXT NOT NULL
|
||||||
|
)
|
||||||
|
");
|
||||||
|
|
||||||
|
// Insert test host
|
||||||
|
$this->db->getConnection()->exec("
|
||||||
|
INSERT INTO hosts (id, platform_id, name) VALUES (1, 1, 'Test Host')
|
||||||
|
");
|
||||||
|
|
||||||
|
// Insert test agent type
|
||||||
|
$this->db->getConnection()->exec("
|
||||||
|
INSERT INTO jilo_agent_types (id, description, endpoint)
|
||||||
|
VALUES (1, 'Test Agent Type', '/api/test')
|
||||||
|
");
|
||||||
|
|
||||||
|
$this->agent = new Agent($this->db);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAddAgent()
|
||||||
|
{
|
||||||
|
$hostId = 1;
|
||||||
|
$data = [
|
||||||
|
'type_id' => 1,
|
||||||
|
'url' => 'http://test.agent:8080',
|
||||||
|
'secret_key' => 'test_secret',
|
||||||
|
'check_period' => 60
|
||||||
|
];
|
||||||
|
|
||||||
|
try {
|
||||||
|
$result = $this->agent->addAgent($hostId, $data);
|
||||||
|
$this->assertTrue($result);
|
||||||
|
|
||||||
|
// Verify agent was created
|
||||||
|
$stmt = $this->db->getConnection()->prepare('SELECT * FROM jilo_agents WHERE host_id = ?');
|
||||||
|
$stmt->execute([$hostId]);
|
||||||
|
$agent = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
$this->assertEquals($data['url'], $agent['url']);
|
||||||
|
$this->assertEquals($data['secret_key'], $agent['secret_key']);
|
||||||
|
$this->assertEquals($data['check_period'], $agent['check_period']);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$this->fail('An error occurred while adding agent: ' . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetAgentDetails()
|
||||||
|
{
|
||||||
|
// Add test agent
|
||||||
|
$hostId = 1;
|
||||||
|
$data = [
|
||||||
|
'type_id' => 1,
|
||||||
|
'url' => 'http://test.agent:8080',
|
||||||
|
'secret_key' => 'test_secret',
|
||||||
|
'check_period' => 60
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->agent->addAgent($hostId, $data);
|
||||||
|
|
||||||
|
// Test getting agent details
|
||||||
|
$agents = $this->agent->getAgentDetails($hostId);
|
||||||
|
$this->assertIsArray($agents);
|
||||||
|
$this->assertCount(1, $agents);
|
||||||
|
$this->assertEquals($data['url'], $agents[0]['url']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testEditAgent()
|
||||||
|
{
|
||||||
|
// Add test agent
|
||||||
|
$hostId = 1;
|
||||||
|
$data = [
|
||||||
|
'type_id' => 1,
|
||||||
|
'url' => 'http://test.agent:8080',
|
||||||
|
'secret_key' => 'test_secret',
|
||||||
|
'check_period' => 60
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->agent->addAgent($hostId, $data);
|
||||||
|
|
||||||
|
// Get agent ID
|
||||||
|
$stmt = $this->db->getConnection()->prepare('SELECT id FROM jilo_agents WHERE host_id = ? LIMIT 1');
|
||||||
|
$stmt->execute([$hostId]);
|
||||||
|
$agentId = $stmt->fetch(PDO::FETCH_COLUMN);
|
||||||
|
|
||||||
|
// Update agent
|
||||||
|
$updateData = [
|
||||||
|
'type_id' => 1,
|
||||||
|
'url' => 'http://updated.agent:8080',
|
||||||
|
'secret_key' => 'updated_secret',
|
||||||
|
'check_period' => 120,
|
||||||
|
'agent_type_id' => 1 // Add this field for the update
|
||||||
|
];
|
||||||
|
|
||||||
|
$result = $this->agent->editAgent($agentId, $updateData);
|
||||||
|
$this->assertTrue($result);
|
||||||
|
|
||||||
|
// Verify update
|
||||||
|
$stmt = $this->db->getConnection()->prepare('SELECT * FROM jilo_agents WHERE id = ?');
|
||||||
|
$stmt->execute([$agentId]);
|
||||||
|
$agent = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
$this->assertEquals($updateData['url'], $agent['url']);
|
||||||
|
$this->assertEquals($updateData['secret_key'], $agent['secret_key']);
|
||||||
|
$this->assertEquals($updateData['check_period'], $agent['check_period']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDeleteAgent()
|
||||||
|
{
|
||||||
|
// Add test agent
|
||||||
|
$hostId = 1;
|
||||||
|
$data = [
|
||||||
|
'type_id' => 1,
|
||||||
|
'url' => 'http://test.agent:8080',
|
||||||
|
'secret_key' => 'test_secret',
|
||||||
|
'check_period' => 60
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->agent->addAgent($hostId, $data);
|
||||||
|
|
||||||
|
// Get agent ID
|
||||||
|
$stmt = $this->db->getConnection()->prepare('SELECT id FROM jilo_agents WHERE host_id = ? LIMIT 1');
|
||||||
|
$stmt->execute([$hostId]);
|
||||||
|
$agentId = $stmt->fetch(PDO::FETCH_COLUMN);
|
||||||
|
|
||||||
|
// Delete agent
|
||||||
|
$result = $this->agent->deleteAgent($agentId);
|
||||||
|
$this->assertTrue($result);
|
||||||
|
|
||||||
|
// Verify deletion
|
||||||
|
$stmt = $this->db->getConnection()->prepare('SELECT COUNT(*) FROM jilo_agents WHERE id = ?');
|
||||||
|
$stmt->execute([$agentId]);
|
||||||
|
$count = $stmt->fetch(PDO::FETCH_COLUMN);
|
||||||
|
|
||||||
|
$this->assertEquals(0, $count);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFetchAgent()
|
||||||
|
{
|
||||||
|
// Add test agent
|
||||||
|
$hostId = 1;
|
||||||
|
$data = [
|
||||||
|
'type_id' => 1,
|
||||||
|
'url' => 'http://test.agent:8080',
|
||||||
|
'secret_key' => 'test_secret',
|
||||||
|
'check_period' => 60
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->agent->addAgent($hostId, $data);
|
||||||
|
|
||||||
|
// Get agent ID
|
||||||
|
$stmt = $this->db->getConnection()->prepare('SELECT id FROM jilo_agents WHERE host_id = ? LIMIT 1');
|
||||||
|
$stmt->execute([$hostId]);
|
||||||
|
$agentId = $stmt->fetch(PDO::FETCH_COLUMN);
|
||||||
|
|
||||||
|
// Mock fetch response
|
||||||
|
$mockAgent = $this->getMockBuilder(Agent::class)
|
||||||
|
->setConstructorArgs([$this->db])
|
||||||
|
->onlyMethods(['fetchAgent'])
|
||||||
|
->getMock();
|
||||||
|
|
||||||
|
$mockResponse = json_encode([
|
||||||
|
'status' => 'ok',
|
||||||
|
'metrics' => [
|
||||||
|
'cpu_usage' => 25.5,
|
||||||
|
'memory_usage' => 1024,
|
||||||
|
'uptime' => 3600
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
|
||||||
|
$mockAgent->expects($this->once())
|
||||||
|
->method('fetchAgent')
|
||||||
|
->willReturn($mockResponse);
|
||||||
|
|
||||||
|
$response = $mockAgent->fetchAgent($agentId);
|
||||||
|
$this->assertJson($response);
|
||||||
|
|
||||||
|
$data = json_decode($response, true);
|
||||||
|
$this->assertEquals('ok', $data['status']);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,131 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
require_once dirname(__DIR__, 4) . '/app/classes/database.php';
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class DatabaseTest extends TestCase
|
||||||
|
{
|
||||||
|
private $config;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
// Set development environment for detailed errors
|
||||||
|
global $config;
|
||||||
|
$config['environment'] = 'development';
|
||||||
|
|
||||||
|
$this->config = [
|
||||||
|
'type' => 'sqlite',
|
||||||
|
'dbFile' => ':memory:'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDatabaseConnection()
|
||||||
|
{
|
||||||
|
$db = new Database($this->config);
|
||||||
|
$this->assertNotNull($db->getConnection());
|
||||||
|
}
|
||||||
|
|
||||||
|
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'
|
||||||
|
];
|
||||||
|
|
||||||
|
$mariadbConfig = [
|
||||||
|
'type' => 'mariadb',
|
||||||
|
'host' => 'invalid-host',
|
||||||
|
'port' => 3306,
|
||||||
|
'dbname' => 'test',
|
||||||
|
'user' => 'test',
|
||||||
|
'password' => 'test'
|
||||||
|
];
|
||||||
|
|
||||||
|
// Both should fail to connect and return null
|
||||||
|
$mysqlDb = new Database($mysqlConfig);
|
||||||
|
$this->assertNull($mysqlDb->getConnection());
|
||||||
|
|
||||||
|
$mariaDb = new Database($mariadbConfig);
|
||||||
|
$this->assertNull($mariaDb->getConnection());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testInvalidDatabaseType()
|
||||||
|
{
|
||||||
|
$invalidConfig = [
|
||||||
|
'type' => 'invalid',
|
||||||
|
'host' => 'localhost',
|
||||||
|
'port' => 3306,
|
||||||
|
'dbname' => 'test',
|
||||||
|
'user' => 'test',
|
||||||
|
'password' => 'test'
|
||||||
|
];
|
||||||
|
|
||||||
|
$invalidDb = new Database($invalidConfig);
|
||||||
|
$this->assertNull($invalidDb->getConnection());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testMySQLConnectionMissingData()
|
||||||
|
{
|
||||||
|
$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']);
|
||||||
|
}
|
||||||
|
|
||||||
|
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']);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,112 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
require_once dirname(__DIR__, 4) . '/app/classes/feedback.php';
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class FeedbackTest extends TestCase
|
||||||
|
{
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
// Start session for flash messages
|
||||||
|
if (session_status() === PHP_SESSION_NONE && !headers_sent()) {
|
||||||
|
session_start();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize session variables
|
||||||
|
$_SESSION = [];
|
||||||
|
$_SESSION['flash_messages'] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function tearDown(): void
|
||||||
|
{
|
||||||
|
// Clean up session
|
||||||
|
if (session_status() === PHP_SESSION_ACTIVE) {
|
||||||
|
session_destroy();
|
||||||
|
}
|
||||||
|
parent::tearDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetFlash()
|
||||||
|
{
|
||||||
|
// Add a test message
|
||||||
|
Feedback::flash('LOGIN', 'LOGIN_SUCCESS', 'Test message');
|
||||||
|
$messages = $_SESSION['flash_messages'];
|
||||||
|
|
||||||
|
$this->assertIsArray($messages);
|
||||||
|
$this->assertCount(1, $messages);
|
||||||
|
|
||||||
|
$message = $messages[0];
|
||||||
|
$this->assertEquals('LOGIN', $message['category']);
|
||||||
|
$this->assertEquals('LOGIN_SUCCESS', $message['key']);
|
||||||
|
$this->assertEquals('Test message', $message['custom_message']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRender()
|
||||||
|
{
|
||||||
|
// Test success message with custom text
|
||||||
|
$output = Feedback::render('LOGIN', 'LOGIN_SUCCESS', 'Success message');
|
||||||
|
$this->assertStringContainsString('alert-success', $output);
|
||||||
|
$this->assertStringContainsString('Success message', $output);
|
||||||
|
$this->assertStringContainsString('alert-dismissible', $output);
|
||||||
|
|
||||||
|
// Test error message (non-dismissible)
|
||||||
|
$output = Feedback::render('LOGIN', 'LOGIN_FAILED', 'Error message');
|
||||||
|
$this->assertStringContainsString('alert-danger', $output);
|
||||||
|
$this->assertStringContainsString('Error message', $output);
|
||||||
|
$this->assertStringNotContainsString('alert-dismissible', $output);
|
||||||
|
|
||||||
|
// Test small message
|
||||||
|
$output = Feedback::render('LOGIN', 'LOGIN_SUCCESS', 'Small message', true, true);
|
||||||
|
$this->assertStringContainsString('alert-sm', $output);
|
||||||
|
$this->assertStringContainsString('btn-close-sm', $output);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetMessageData()
|
||||||
|
{
|
||||||
|
$data = Feedback::getMessageData('LOGIN', 'LOGIN_SUCCESS', 'Test message');
|
||||||
|
|
||||||
|
$this->assertIsArray($data);
|
||||||
|
$this->assertEquals(Feedback::TYPE_SUCCESS, $data['type']);
|
||||||
|
$this->assertEquals('Test message', $data['message']);
|
||||||
|
$this->assertTrue($data['dismissible']);
|
||||||
|
$this->assertFalse($data['small']);
|
||||||
|
|
||||||
|
// Test with default message
|
||||||
|
$data = Feedback::getMessageData('LOGIN', 'LOGIN_SUCCESS');
|
||||||
|
$this->assertNotNull($data['message']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFlash()
|
||||||
|
{
|
||||||
|
Feedback::flash('LOGIN', 'LOGIN_SUCCESS', 'Test message');
|
||||||
|
$this->assertArrayHasKey('flash_messages', $_SESSION);
|
||||||
|
$this->assertCount(1, $_SESSION['flash_messages']);
|
||||||
|
|
||||||
|
$message = $_SESSION['flash_messages'][0];
|
||||||
|
$this->assertEquals('LOGIN', $message['category']);
|
||||||
|
$this->assertEquals('LOGIN_SUCCESS', $message['key']);
|
||||||
|
$this->assertEquals('Test message', $message['custom_message']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPredefinedMessageTypes()
|
||||||
|
{
|
||||||
|
$this->assertEquals('success', Feedback::TYPE_SUCCESS);
|
||||||
|
$this->assertEquals('danger', Feedback::TYPE_ERROR);
|
||||||
|
$this->assertEquals('info', Feedback::TYPE_INFO);
|
||||||
|
$this->assertEquals('warning', Feedback::TYPE_WARNING);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testMessageConfigurations()
|
||||||
|
{
|
||||||
|
$config = Feedback::get('LOGIN', 'LOGIN_SUCCESS');
|
||||||
|
$this->assertEquals(Feedback::TYPE_SUCCESS, $config['type']);
|
||||||
|
$this->assertTrue($config['dismissible']);
|
||||||
|
|
||||||
|
$config = Feedback::get('LOGIN', 'LOGIN_FAILED');
|
||||||
|
$this->assertEquals(Feedback::TYPE_ERROR, $config['type']);
|
||||||
|
$this->assertFalse($config['dismissible']);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,183 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
require_once dirname(__DIR__, 4) . '/app/classes/host.php';
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class HostTest extends TestCase
|
||||||
|
{
|
||||||
|
private $db;
|
||||||
|
private $host;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
// Set development environment for detailed errors
|
||||||
|
global $config;
|
||||||
|
$config['environment'] = 'development';
|
||||||
|
|
||||||
|
// Set up test database
|
||||||
|
$this->db = new \Database([
|
||||||
|
'type' => 'sqlite',
|
||||||
|
'dbFile' => ':memory:'
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Create hosts table
|
||||||
|
$this->db->getConnection()->exec("
|
||||||
|
CREATE TABLE hosts (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
platform_id INTEGER NOT NULL,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
address TEXT NOT NULL
|
||||||
|
)
|
||||||
|
");
|
||||||
|
|
||||||
|
// Create jilo_agents table for relationship testing
|
||||||
|
$this->db->getConnection()->exec("
|
||||||
|
CREATE TABLE jilo_agents (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
host_id INTEGER NOT NULL,
|
||||||
|
agent_type_id INTEGER NOT NULL,
|
||||||
|
url TEXT NOT NULL,
|
||||||
|
secret_key TEXT,
|
||||||
|
check_period INTEGER DEFAULT 60
|
||||||
|
)
|
||||||
|
");
|
||||||
|
|
||||||
|
$this->host = new \Host($this->db);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAddHost()
|
||||||
|
{
|
||||||
|
$data = [
|
||||||
|
'platform_id' => 1,
|
||||||
|
'name' => 'Test host',
|
||||||
|
'address' => '192.168.1.100'
|
||||||
|
];
|
||||||
|
|
||||||
|
$result = $this->host->addHost($data);
|
||||||
|
$this->assertTrue($result);
|
||||||
|
|
||||||
|
// Verify host was created
|
||||||
|
$stmt = $this->db->getConnection()->prepare('SELECT * FROM hosts WHERE platform_id = ? AND name = ?');
|
||||||
|
$stmt->execute([$data['platform_id'], $data['name']]);
|
||||||
|
$host = $stmt->fetch(\PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
$this->assertEquals($data['name'], $host['name']);
|
||||||
|
$this->assertEquals($data['address'], $host['address']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetHostDetails()
|
||||||
|
{
|
||||||
|
// Add test host
|
||||||
|
$data = [
|
||||||
|
'platform_id' => 1,
|
||||||
|
'name' => 'Test host',
|
||||||
|
'address' => '192.168.1.100'
|
||||||
|
];
|
||||||
|
$this->host->addHost($data);
|
||||||
|
|
||||||
|
// Test getting all hosts
|
||||||
|
$hosts = $this->host->getHostDetails();
|
||||||
|
$this->assertIsArray($hosts);
|
||||||
|
$this->assertCount(1, $hosts);
|
||||||
|
|
||||||
|
// Test getting hosts by platform
|
||||||
|
$hosts = $this->host->getHostDetails(1);
|
||||||
|
$this->assertIsArray($hosts);
|
||||||
|
$this->assertCount(1, $hosts);
|
||||||
|
$this->assertEquals($data['name'], $hosts[0]['name']);
|
||||||
|
|
||||||
|
// Test getting specific host
|
||||||
|
$hosts = $this->host->getHostDetails(1, $hosts[0]['id']);
|
||||||
|
$this->assertIsArray($hosts);
|
||||||
|
$this->assertCount(1, $hosts);
|
||||||
|
$this->assertEquals($data['name'], $hosts[0]['name']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testEditHost()
|
||||||
|
{
|
||||||
|
// Add test host
|
||||||
|
$data = [
|
||||||
|
'platform_id' => 1,
|
||||||
|
'name' => 'Test host',
|
||||||
|
'address' => '192.168.1.100'
|
||||||
|
];
|
||||||
|
$this->host->addHost($data);
|
||||||
|
|
||||||
|
// Get host ID
|
||||||
|
$stmt = $this->db->getConnection()->prepare('SELECT id FROM hosts WHERE platform_id = ? AND name = ?');
|
||||||
|
$stmt->execute([$data['platform_id'], $data['name']]);
|
||||||
|
$hostId = $stmt->fetch(\PDO::FETCH_COLUMN);
|
||||||
|
|
||||||
|
// Update host
|
||||||
|
$updateData = [
|
||||||
|
'id' => $hostId,
|
||||||
|
'name' => 'Updated host',
|
||||||
|
'address' => '192.168.1.200'
|
||||||
|
];
|
||||||
|
|
||||||
|
$result = $this->host->editHost($data['platform_id'], $updateData);
|
||||||
|
$this->assertTrue($result);
|
||||||
|
|
||||||
|
// Verify update
|
||||||
|
$stmt = $this->db->getConnection()->prepare('SELECT * FROM hosts WHERE id = ?');
|
||||||
|
$stmt->execute([$hostId]);
|
||||||
|
$host = $stmt->fetch(\PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
$this->assertEquals($updateData['name'], $host['name']);
|
||||||
|
$this->assertEquals($updateData['address'], $host['address']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDeleteHost()
|
||||||
|
{
|
||||||
|
// Add test host
|
||||||
|
$data = [
|
||||||
|
'platform_id' => 1,
|
||||||
|
'name' => 'Test host',
|
||||||
|
'address' => '192.168.1.100'
|
||||||
|
];
|
||||||
|
$this->host->addHost($data);
|
||||||
|
|
||||||
|
// Get host ID
|
||||||
|
$stmt = $this->db->getConnection()->prepare('SELECT id FROM hosts WHERE platform_id = ? AND name = ?');
|
||||||
|
$stmt->execute([$data['platform_id'], $data['name']]);
|
||||||
|
$hostId = $stmt->fetch(\PDO::FETCH_COLUMN);
|
||||||
|
|
||||||
|
// Add test agent to the host
|
||||||
|
$this->db->getConnection()->exec("
|
||||||
|
INSERT INTO jilo_agents (host_id, agent_type_id, url, secret_key)
|
||||||
|
VALUES ($hostId, 1, 'http://test:8080', 'secret')
|
||||||
|
");
|
||||||
|
|
||||||
|
// Delete host
|
||||||
|
$result = $this->host->deleteHost($hostId);
|
||||||
|
$this->assertTrue($result);
|
||||||
|
|
||||||
|
// Verify host deletion
|
||||||
|
$stmt = $this->db->getConnection()->prepare('SELECT COUNT(*) FROM hosts WHERE id = ?');
|
||||||
|
$stmt->execute([$hostId]);
|
||||||
|
$hostCount = $stmt->fetch(\PDO::FETCH_COLUMN);
|
||||||
|
$this->assertEquals(0, $hostCount);
|
||||||
|
|
||||||
|
// Verify agent deletion
|
||||||
|
$stmt = $this->db->getConnection()->prepare('SELECT COUNT(*) FROM jilo_agents WHERE host_id = ?');
|
||||||
|
$stmt->execute([$hostId]);
|
||||||
|
$agentCount = $stmt->fetch(\PDO::FETCH_COLUMN);
|
||||||
|
$this->assertEquals(0, $agentCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testEditNonexistentHost()
|
||||||
|
{
|
||||||
|
$updateData = [
|
||||||
|
'id' => 999,
|
||||||
|
'name' => 'Nonexistent host',
|
||||||
|
'address' => '192.168.1.200'
|
||||||
|
];
|
||||||
|
|
||||||
|
$result = $this->host->editHost(1, $updateData);
|
||||||
|
$this->assertIsString($result);
|
||||||
|
$this->assertStringContainsString('No host found', $result);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
require_once dirname(__DIR__, 4) . '/app/classes/database.php';
|
||||||
|
require_once dirname(__DIR__, 4) . '/app/classes/server.php';
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class JiloServerTest extends TestCase
|
||||||
|
{
|
||||||
|
private $db;
|
||||||
|
private $server;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
// Set up test database
|
||||||
|
$this->db = new Database([
|
||||||
|
'type' => 'sqlite',
|
||||||
|
'dbFile' => ':memory:'
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Create servers table
|
||||||
|
$this->db->getConnection()->exec("
|
||||||
|
CREATE TABLE servers (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
host_id INTEGER NOT NULL,
|
||||||
|
port INTEGER NOT NULL,
|
||||||
|
status TEXT DEFAULT 'offline',
|
||||||
|
last_seen INTEGER,
|
||||||
|
version TEXT,
|
||||||
|
created_at INTEGER NOT NULL,
|
||||||
|
updated_at INTEGER NOT NULL
|
||||||
|
)
|
||||||
|
");
|
||||||
|
|
||||||
|
$this->server = new Server($this->db);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetServerStatus()
|
||||||
|
{
|
||||||
|
// Create mock server that overrides file_get_contents
|
||||||
|
$mockServer = $this->getMockBuilder(Server::class)
|
||||||
|
->setConstructorArgs([$this->db])
|
||||||
|
->onlyMethods(['getServerStatus'])
|
||||||
|
->getMock();
|
||||||
|
|
||||||
|
// Test successful response
|
||||||
|
$mockServer->expects($this->exactly(2))
|
||||||
|
->method('getServerStatus')
|
||||||
|
->willReturnMap([
|
||||||
|
['localhost', 8080, '/health', true],
|
||||||
|
['localhost', 8081, '/health', false]
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertTrue($mockServer->getServerStatus('localhost', 8080));
|
||||||
|
$this->assertFalse($mockServer->getServerStatus('localhost', 8081));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetServerStatusWithCustomEndpoint()
|
||||||
|
{
|
||||||
|
$mockServer = $this->getMockBuilder(Server::class)
|
||||||
|
->setConstructorArgs([$this->db])
|
||||||
|
->onlyMethods(['getServerStatus'])
|
||||||
|
->getMock();
|
||||||
|
|
||||||
|
$mockServer->expects($this->once())
|
||||||
|
->method('getServerStatus')
|
||||||
|
->with('localhost', 8080, '/custom/health')
|
||||||
|
->willReturn(true);
|
||||||
|
|
||||||
|
$this->assertTrue($mockServer->getServerStatus('localhost', 8080, '/custom/health'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetServerStatusWithDefaults()
|
||||||
|
{
|
||||||
|
$mockServer = $this->getMockBuilder(Server::class)
|
||||||
|
->setConstructorArgs([$this->db])
|
||||||
|
->onlyMethods(['getServerStatus'])
|
||||||
|
->getMock();
|
||||||
|
|
||||||
|
$mockServer->expects($this->once())
|
||||||
|
->method('getServerStatus')
|
||||||
|
->with('127.0.0.1', 8080, '/health')
|
||||||
|
->willReturn(true);
|
||||||
|
|
||||||
|
$this->assertTrue($mockServer->getServerStatus());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,138 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
require_once dirname(__DIR__, 4) . '/app/classes/database.php';
|
||||||
|
require_once dirname(__DIR__, 4) . '/app/classes/log.php';
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class LogTest extends TestCase
|
||||||
|
{
|
||||||
|
private $db;
|
||||||
|
private $log;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
// Set up test database
|
||||||
|
$this->db = new Database([
|
||||||
|
'type' => 'sqlite',
|
||||||
|
'dbFile' => ':memory:'
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Create users table
|
||||||
|
$this->db->getConnection()->exec("
|
||||||
|
CREATE TABLE users (
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
username TEXT NOT NULL
|
||||||
|
)
|
||||||
|
");
|
||||||
|
|
||||||
|
// Create test user
|
||||||
|
$this->db->getConnection()->exec("
|
||||||
|
INSERT INTO users (id, username) VALUES (1, 'testuser'), (2, 'testuser2')
|
||||||
|
");
|
||||||
|
|
||||||
|
// Create logs table
|
||||||
|
$this->db->getConnection()->exec("
|
||||||
|
CREATE TABLE logs (
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
user_id INTEGER,
|
||||||
|
scope TEXT NOT NULL,
|
||||||
|
message TEXT NOT NULL,
|
||||||
|
time DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY (user_id) REFERENCES users(id)
|
||||||
|
)
|
||||||
|
");
|
||||||
|
|
||||||
|
$this->log = new Log($this->db);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testInsertLog()
|
||||||
|
{
|
||||||
|
$result = $this->log->insertLog(1, 'Test message', 'test');
|
||||||
|
$this->assertTrue($result);
|
||||||
|
|
||||||
|
$stmt = $this->db->getConnection()->prepare("SELECT * FROM logs WHERE scope = ?");
|
||||||
|
$stmt->execute(['test']);
|
||||||
|
$log = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
$this->assertEquals(1, $log['user_id']);
|
||||||
|
$this->assertEquals('Test message', $log['message']);
|
||||||
|
$this->assertEquals('test', $log['scope']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testReadLog()
|
||||||
|
{
|
||||||
|
// Insert test logs
|
||||||
|
$this->log->insertLog(1, 'Test message 1', 'user');
|
||||||
|
$this->log->insertLog(1, 'Test message 2', 'user');
|
||||||
|
|
||||||
|
$logs = $this->log->readLog(1, 'user');
|
||||||
|
$this->assertCount(2, $logs);
|
||||||
|
$this->assertEquals('Test message 1', $logs[0]['message']);
|
||||||
|
$this->assertEquals('Test message 2', $logs[1]['message']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testReadLogWithTimeFilter()
|
||||||
|
{
|
||||||
|
// Insert test logs with different times
|
||||||
|
$this->log->insertLog(1, 'Old message', 'user');
|
||||||
|
sleep(1); // Ensure different timestamps
|
||||||
|
$this->log->insertLog(1, 'New message', 'user');
|
||||||
|
|
||||||
|
$now = date('Y-m-d H:i:s');
|
||||||
|
$oneHourAgo = date('Y-m-d H:i:s', strtotime('-1 hour'));
|
||||||
|
|
||||||
|
$logs = $this->log->readLog(1, 'user', 0, '', [
|
||||||
|
'from_time' => $oneHourAgo,
|
||||||
|
'until_time' => $now
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertCount(2, $logs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testReadLogWithPagination()
|
||||||
|
{
|
||||||
|
// Insert test logs
|
||||||
|
$this->log->insertLog(1, 'Message 1', 'user');
|
||||||
|
$this->log->insertLog(1, 'Message 2', 'user');
|
||||||
|
$this->log->insertLog(1, 'Message 3', 'user');
|
||||||
|
|
||||||
|
// Test with limit
|
||||||
|
$logs = $this->log->readLog(1, 'user', 0, 2);
|
||||||
|
$this->assertCount(2, $logs);
|
||||||
|
|
||||||
|
// Test with offset
|
||||||
|
$logs = $this->log->readLog(1, 'user', 2, 2);
|
||||||
|
$this->assertCount(1, $logs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testReadLogWithMessageFilter()
|
||||||
|
{
|
||||||
|
// Insert test logs
|
||||||
|
$this->log->insertLog(1, 'Test message', 'user');
|
||||||
|
$this->log->insertLog(1, 'Another message', 'user');
|
||||||
|
|
||||||
|
$logs = $this->log->readLog(1, 'user', 0, '', [
|
||||||
|
'message' => 'Test'
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertCount(1, $logs);
|
||||||
|
$this->assertEquals('Test message', $logs[0]['message']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testReadLogWithUserFilter()
|
||||||
|
{
|
||||||
|
// Insert test logs for different users
|
||||||
|
$this->log->insertLog(1, 'User 1 message', 'user');
|
||||||
|
$this->log->insertLog(2, 'User 2 message', 'user');
|
||||||
|
|
||||||
|
$logs = $this->log->readLog(1, 'user', 0, '', [
|
||||||
|
'id' => 1
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertCount(1, $logs);
|
||||||
|
$this->assertEquals('User 1 message', $logs[0]['message']);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,222 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
require_once dirname(__DIR__, 4) . '/app/classes/database.php';
|
||||||
|
require_once dirname(__DIR__, 4) . '/app/classes/platform.php';
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class PlatformTest extends TestCase
|
||||||
|
{
|
||||||
|
private $db;
|
||||||
|
private $platform;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
// Set up test database
|
||||||
|
$this->db = new Database([
|
||||||
|
'type' => 'sqlite',
|
||||||
|
'dbFile' => ':memory:'
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Create hosts table
|
||||||
|
$this->db->getConnection()->exec("
|
||||||
|
CREATE TABLE hosts (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
platform_id INTEGER NOT NULL,
|
||||||
|
name TEXT NOT NULL
|
||||||
|
)
|
||||||
|
");
|
||||||
|
|
||||||
|
// Create jilo_agents table
|
||||||
|
$this->db->getConnection()->exec("
|
||||||
|
CREATE TABLE jilo_agents (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
host_id INTEGER NOT NULL
|
||||||
|
)
|
||||||
|
");
|
||||||
|
|
||||||
|
// Create platforms table
|
||||||
|
$this->db->getConnection()->exec("
|
||||||
|
CREATE TABLE platforms (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
jitsi_url TEXT NOT NULL,
|
||||||
|
jilo_database TEXT NOT NULL,
|
||||||
|
created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))
|
||||||
|
)
|
||||||
|
");
|
||||||
|
|
||||||
|
$this->platform = new Platform($this->db);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAddPlatform()
|
||||||
|
{
|
||||||
|
$data = [
|
||||||
|
'name' => 'Test platform',
|
||||||
|
'jitsi_url' => 'https://jitsi.example.com',
|
||||||
|
'jilo_database' => '/path/to/jilo.db'
|
||||||
|
];
|
||||||
|
|
||||||
|
$result = $this->platform->addPlatform($data);
|
||||||
|
$this->assertTrue($result);
|
||||||
|
|
||||||
|
// Verify platform was created
|
||||||
|
$stmt = $this->db->getConnection()->prepare('SELECT * FROM platforms WHERE name = ?');
|
||||||
|
$stmt->execute([$data['name']]);
|
||||||
|
$platform = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
$this->assertNotNull($platform);
|
||||||
|
$this->assertEquals($data['name'], $platform['name']);
|
||||||
|
$this->assertEquals($data['jitsi_url'], $platform['jitsi_url']);
|
||||||
|
$this->assertEquals($data['jilo_database'], $platform['jilo_database']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetPlatformDetails()
|
||||||
|
{
|
||||||
|
// Create test platform
|
||||||
|
$stmt = $this->db->getConnection()->prepare('INSERT INTO platforms (name, jitsi_url, jilo_database) VALUES (?, ?, ?)');
|
||||||
|
$stmt->execute(['Test platform', 'https://jitsi.example.com', '/path/to/jilo.db']);
|
||||||
|
$platformId = $this->db->getConnection()->lastInsertId();
|
||||||
|
|
||||||
|
// Test getting specific platform
|
||||||
|
$platform = $this->platform->getPlatformDetails($platformId);
|
||||||
|
$this->assertIsArray($platform);
|
||||||
|
$this->assertEquals('Test platform', $platform[0]['name']);
|
||||||
|
|
||||||
|
// Test getting all platforms
|
||||||
|
$platforms = $this->platform->getPlatformDetails();
|
||||||
|
$this->assertIsArray($platforms);
|
||||||
|
$this->assertCount(1, $platforms);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testEditPlatform()
|
||||||
|
{
|
||||||
|
// Create test platform
|
||||||
|
$stmt = $this->db->getConnection()->prepare('INSERT INTO platforms (name, jitsi_url, jilo_database) VALUES (?, ?, ?)');
|
||||||
|
$stmt->execute(['Test platform', 'https://jitsi.example.com', '/path/to/jilo.db']);
|
||||||
|
$platformId = $this->db->getConnection()->lastInsertId();
|
||||||
|
|
||||||
|
$updateData = [
|
||||||
|
'name' => 'Updated platform',
|
||||||
|
'jitsi_url' => 'https://new.example.com',
|
||||||
|
'jilo_database' => '/path/to/jilo.db'
|
||||||
|
];
|
||||||
|
|
||||||
|
$result = $this->platform->editPlatform($platformId, $updateData);
|
||||||
|
$this->assertTrue($result);
|
||||||
|
|
||||||
|
// Verify update
|
||||||
|
$stmt = $this->db->getConnection()->prepare('SELECT * FROM platforms WHERE id = ?');
|
||||||
|
$stmt->execute([$platformId]);
|
||||||
|
$platform = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
$this->assertEquals($updateData['name'], $platform['name']);
|
||||||
|
$this->assertEquals($updateData['jitsi_url'], $platform['jitsi_url']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDeletePlatform()
|
||||||
|
{
|
||||||
|
// Create test platform
|
||||||
|
$stmt = $this->db->getConnection()->prepare('INSERT INTO platforms (name, jitsi_url, jilo_database) VALUES (?, ?, ?)');
|
||||||
|
$stmt->execute(['Test platform', 'https://jitsi.example.com', '/path/to/jilo.db']);
|
||||||
|
$platformId = $this->db->getConnection()->lastInsertId();
|
||||||
|
|
||||||
|
// Create test host
|
||||||
|
$stmt = $this->db->getConnection()->prepare('INSERT INTO hosts (platform_id, name) VALUES (?, ?)');
|
||||||
|
$stmt->execute([$platformId, 'Test host']);
|
||||||
|
$hostId = $this->db->getConnection()->lastInsertId();
|
||||||
|
|
||||||
|
// Create test agent
|
||||||
|
$stmt = $this->db->getConnection()->prepare('INSERT INTO jilo_agents (host_id) VALUES (?)');
|
||||||
|
$stmt->execute([$hostId]);
|
||||||
|
|
||||||
|
$result = $this->platform->deletePlatform($platformId);
|
||||||
|
$this->assertTrue($result);
|
||||||
|
|
||||||
|
// Verify platform deletion
|
||||||
|
$stmt = $this->db->getConnection()->prepare('SELECT COUNT(*) as count FROM platforms WHERE id = ?');
|
||||||
|
$stmt->execute([$platformId]);
|
||||||
|
$result = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
$this->assertEquals(0, $result['count']);
|
||||||
|
|
||||||
|
// Verify host deletion
|
||||||
|
$stmt = $this->db->getConnection()->prepare('SELECT COUNT(*) as count FROM hosts WHERE platform_id = ?');
|
||||||
|
$stmt->execute([$platformId]);
|
||||||
|
$result = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
$this->assertEquals(0, $result['count']);
|
||||||
|
|
||||||
|
// Verify agent deletion
|
||||||
|
$stmt = $this->db->getConnection()->prepare('SELECT COUNT(*) as count FROM jilo_agents WHERE host_id = ?');
|
||||||
|
$stmt->execute([$hostId]);
|
||||||
|
$result = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
$this->assertEquals(0, $result['count']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testValidatePlatformData()
|
||||||
|
{
|
||||||
|
$validData = [
|
||||||
|
'name' => 'Test platform',
|
||||||
|
'jitsi_url' => 'https://jitsi.example.com',
|
||||||
|
'jilo_database' => '/path/to/jilo.db'
|
||||||
|
];
|
||||||
|
|
||||||
|
$result = $this->platform->addPlatform($validData);
|
||||||
|
$this->assertTrue($result);
|
||||||
|
|
||||||
|
// Verify platform was created
|
||||||
|
$stmt = $this->db->getConnection()->prepare('SELECT COUNT(*) as count FROM platforms WHERE name = ?');
|
||||||
|
$stmt->execute([$validData['name']]);
|
||||||
|
$result = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
$this->assertEquals(1, $result['count']);
|
||||||
|
|
||||||
|
// Test invalid data (missing required fields)
|
||||||
|
$invalidData = [
|
||||||
|
'name' => 'Test platform 2'
|
||||||
|
// Missing jitsi_url and jilo_database
|
||||||
|
];
|
||||||
|
|
||||||
|
$result = $this->platform->addPlatform($invalidData);
|
||||||
|
$this->assertIsString($result); // Should return error message
|
||||||
|
|
||||||
|
// Verify platform was not created
|
||||||
|
$stmt = $this->db->getConnection()->prepare('SELECT COUNT(*) as count FROM platforms WHERE name = ?');
|
||||||
|
$stmt->execute([$invalidData['name']]);
|
||||||
|
$result = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
$this->assertEquals(0, $result['count']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCheckJiloDatabaseAccess()
|
||||||
|
{
|
||||||
|
// Create a temporary SQLite database for testing
|
||||||
|
$tempDb = tempnam(sys_get_temp_dir(), 'jilo_test_');
|
||||||
|
$testDb = new \SQLite3($tempDb);
|
||||||
|
$testDb->close();
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'name' => 'Test platform',
|
||||||
|
'jitsi_url' => 'https://jitsi.example.com',
|
||||||
|
'jilo_database' => $tempDb
|
||||||
|
];
|
||||||
|
|
||||||
|
$result = $this->platform->addPlatform($data);
|
||||||
|
$this->assertTrue($result);
|
||||||
|
|
||||||
|
// Verify platform was created
|
||||||
|
$stmt = $this->db->getConnection()->prepare('SELECT COUNT(*) as count FROM platforms WHERE jilo_database = ?');
|
||||||
|
$stmt->execute([$tempDb]);
|
||||||
|
$result = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
$this->assertEquals(1, $result['count']);
|
||||||
|
|
||||||
|
// Test with non-existent database
|
||||||
|
$data['name'] = 'Another platform';
|
||||||
|
$data['jilo_database'] = '/nonexistent/path/db.sqlite';
|
||||||
|
$result = $this->platform->addPlatform($data);
|
||||||
|
$this->assertTrue($result); // No database validation in Platform class
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
unlink($tempDb);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,110 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
require_once dirname(__DIR__, 4) . '/app/classes/database.php';
|
||||||
|
require_once dirname(__DIR__, 4) . '/app/classes/ratelimiter.php';
|
||||||
|
require_once dirname(__DIR__, 4) . '/app/classes/log.php';
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class RateLimiterTest extends TestCase
|
||||||
|
{
|
||||||
|
private $rateLimiter;
|
||||||
|
private $db;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
// Set up in-memory SQLite database
|
||||||
|
$this->db = new Database([
|
||||||
|
'type' => 'sqlite',
|
||||||
|
'dbFile' => ':memory:'
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->rateLimiter = new RateLimiter($this->db);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetRecentAttempts()
|
||||||
|
{
|
||||||
|
$ip = '127.0.0.1';
|
||||||
|
$username = 'testuser';
|
||||||
|
|
||||||
|
// Clean up any existing attempts first
|
||||||
|
$stmt = $this->db->getConnection()->prepare("DELETE FROM {$this->rateLimiter->authRatelimitTable} WHERE ip_address = ?");
|
||||||
|
$stmt->execute([$ip]);
|
||||||
|
|
||||||
|
// Initially should have no attempts
|
||||||
|
$attempts = $this->rateLimiter->getRecentAttempts($ip);
|
||||||
|
$this->assertEquals(0, $attempts);
|
||||||
|
|
||||||
|
// Add a login attempt
|
||||||
|
$stmt = $this->db->getConnection()->prepare("INSERT INTO {$this->rateLimiter->authRatelimitTable} (ip_address, username) VALUES (?, ?)");
|
||||||
|
$stmt->execute([$ip, $username]);
|
||||||
|
|
||||||
|
// Should now have 1 attempt
|
||||||
|
$attempts = $this->rateLimiter->getRecentAttempts($ip);
|
||||||
|
$this->assertEquals(1, $attempts);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testIpBlacklisting()
|
||||||
|
{
|
||||||
|
$ip = '192.0.2.1'; // Using TEST-NET-1 range
|
||||||
|
|
||||||
|
// Should be blacklisted by default (TEST-NET-1 range)
|
||||||
|
$this->assertTrue($this->rateLimiter->isIpBlacklisted($ip));
|
||||||
|
|
||||||
|
// Test with non-blacklisted IP
|
||||||
|
$nonBlacklistedIp = '8.8.8.8'; // Google DNS
|
||||||
|
$this->assertFalse($this->rateLimiter->isIpBlacklisted($nonBlacklistedIp));
|
||||||
|
|
||||||
|
// Add IP to blacklist
|
||||||
|
$stmt = $this->db->getConnection()->prepare("INSERT INTO {$this->rateLimiter->blacklistTable} (ip_address, reason) VALUES (?, ?)");
|
||||||
|
$stmt->execute([$nonBlacklistedIp, 'Test blacklist']);
|
||||||
|
|
||||||
|
// IP should now be blacklisted
|
||||||
|
$this->assertTrue($this->rateLimiter->isIpBlacklisted($nonBlacklistedIp));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testIpWhitelisting()
|
||||||
|
{
|
||||||
|
$ip = '127.0.0.1'; // Localhost
|
||||||
|
|
||||||
|
// Clean up any existing whitelist entries
|
||||||
|
$stmt = $this->db->getConnection()->prepare("DELETE FROM {$this->rateLimiter->whitelistTable} WHERE ip_address = ?");
|
||||||
|
$stmt->execute([$ip]);
|
||||||
|
|
||||||
|
// Add to whitelist
|
||||||
|
$stmt = $this->db->getConnection()->prepare("INSERT INTO {$this->rateLimiter->whitelistTable} (ip_address, description) VALUES (?, ?)");
|
||||||
|
$stmt->execute([$ip, 'Test whitelist']);
|
||||||
|
|
||||||
|
// Should be whitelisted
|
||||||
|
$this->assertTrue($this->rateLimiter->isIpWhitelisted($ip));
|
||||||
|
|
||||||
|
// Test with non-whitelisted IP
|
||||||
|
$nonWhitelistedIp = '8.8.8.8'; // Google DNS
|
||||||
|
$this->assertFalse($this->rateLimiter->isIpWhitelisted($nonWhitelistedIp));
|
||||||
|
|
||||||
|
// Add to whitelist
|
||||||
|
$stmt = $this->db->getConnection()->prepare("INSERT INTO {$this->rateLimiter->whitelistTable} (ip_address, description) VALUES (?, ?)");
|
||||||
|
$stmt->execute([$nonWhitelistedIp, 'Test whitelist']);
|
||||||
|
|
||||||
|
// Should now be whitelisted
|
||||||
|
$this->assertTrue($this->rateLimiter->isIpWhitelisted($nonWhitelistedIp));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testIpRangeBlacklisting()
|
||||||
|
{
|
||||||
|
$ip = '8.8.8.8'; // Google DNS
|
||||||
|
$networkIp = '8.8.8.0/24'; // Network containing Google DNS
|
||||||
|
|
||||||
|
// Initially IP should not be blacklisted
|
||||||
|
$this->assertFalse($this->rateLimiter->isIpBlacklisted($ip));
|
||||||
|
|
||||||
|
// Add network to blacklist
|
||||||
|
$stmt = $this->db->getConnection()->prepare("INSERT INTO {$this->rateLimiter->blacklistTable} (ip_address, is_network, reason) VALUES (?, 1, ?)");
|
||||||
|
$stmt->execute([$networkIp, 'Test network blacklist']);
|
||||||
|
|
||||||
|
// IP in range should now be blacklisted
|
||||||
|
$this->assertTrue($this->rateLimiter->isIpBlacklisted($ip));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,86 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
require_once dirname(__DIR__, 4) . '/app/classes/router.php';
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class RouterTest extends TestCase
|
||||||
|
{
|
||||||
|
private $router;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
$this->router = new Router('', true); // Empty controller path and dry-run mode
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAddRoute()
|
||||||
|
{
|
||||||
|
$this->router->add('/test', 'TestController@index');
|
||||||
|
$this->assertTrue(true); // If we get here, no exception was thrown
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDispatchRoute()
|
||||||
|
{
|
||||||
|
$this->router->add('/users/(\d+)', 'UserController@show');
|
||||||
|
|
||||||
|
$match = $this->router->dispatch('/users/123');
|
||||||
|
$this->assertIsArray($match);
|
||||||
|
$this->assertEquals('UserController@show', $match['callback']);
|
||||||
|
$this->assertEquals(['123'], $match['params']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDispatchRouteWithMultipleParameters()
|
||||||
|
{
|
||||||
|
$this->router->add('/users/(\d+)/posts/(\d+)', 'PostController@show');
|
||||||
|
|
||||||
|
$match = $this->router->dispatch('/users/123/posts/456');
|
||||||
|
$this->assertIsArray($match);
|
||||||
|
$this->assertEquals('PostController@show', $match['callback']);
|
||||||
|
$this->assertEquals(['123', '456'], $match['params']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testNoMatchingRoute()
|
||||||
|
{
|
||||||
|
$this->router->add('/test', 'TestController@index');
|
||||||
|
|
||||||
|
$match = $this->router->dispatch('/nonexistent');
|
||||||
|
$this->assertNull($match);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDispatchWithQueryString()
|
||||||
|
{
|
||||||
|
$this->router->add('/test', 'TestController@index');
|
||||||
|
|
||||||
|
$match = $this->router->dispatch('/test?param=value');
|
||||||
|
$this->assertIsArray($match);
|
||||||
|
$this->assertEquals('TestController@index', $match['callback']);
|
||||||
|
$this->assertEquals([], $match['params']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOptionalParameters()
|
||||||
|
{
|
||||||
|
$this->router->add('/users(?:/(\d+))?', 'UserController@index');
|
||||||
|
|
||||||
|
// Test with parameter
|
||||||
|
$match1 = $this->router->dispatch('/users/123');
|
||||||
|
$this->assertIsArray($match1);
|
||||||
|
$this->assertEquals('UserController@index', $match1['callback']);
|
||||||
|
$this->assertEquals(['123'], $match1['params']);
|
||||||
|
|
||||||
|
// Test without parameter
|
||||||
|
$match2 = $this->router->dispatch('/users');
|
||||||
|
$this->assertIsArray($match2);
|
||||||
|
$this->assertEquals('UserController@index', $match2['callback']);
|
||||||
|
$this->assertEquals([], $match2['params']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testInvokeWithMissingController()
|
||||||
|
{
|
||||||
|
$router = new Router(''); // Empty controller path, not in dry-run mode
|
||||||
|
ob_start();
|
||||||
|
$router->dispatch('/test');
|
||||||
|
$output = ob_get_clean();
|
||||||
|
$this->assertEquals('404 page not found', $output);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
require_once dirname(__DIR__, 4) . '/app/classes/database.php';
|
||||||
|
require_once dirname(__DIR__, 4) . '/app/classes/settings.php';
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class SettingsTest extends TestCase
|
||||||
|
{
|
||||||
|
private $settings;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
$this->settings = new Settings(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetPlatformJsFileWithInvalidUrl()
|
||||||
|
{
|
||||||
|
$result = $this->settings->getPlatformJsFile('invalid-url', 'test.js');
|
||||||
|
$this->assertEquals('Invalid URL: invalid-url/test.js', $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetPlatformJsFileWithValidUrl()
|
||||||
|
{
|
||||||
|
$result = $this->settings->getPlatformJsFile('https://example.com', 'test.js');
|
||||||
|
$this->assertStringContainsString("can't be loaded", $result);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,164 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
require_once dirname(__DIR__, 4) . '/app/classes/database.php';
|
||||||
|
require_once dirname(__DIR__, 4) . '/app/classes/user.php';
|
||||||
|
require_once dirname(__DIR__, 4) . '/app/classes/ratelimiter.php';
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class UserTest extends TestCase
|
||||||
|
{
|
||||||
|
private $db;
|
||||||
|
private $user;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
// Set up test database
|
||||||
|
$this->db = new Database([
|
||||||
|
'type' => 'sqlite',
|
||||||
|
'dbFile' => ':memory:'
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Create users table
|
||||||
|
$this->db->getConnection()->exec("
|
||||||
|
CREATE TABLE users (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
username TEXT NOT NULL UNIQUE,
|
||||||
|
password TEXT NOT NULL
|
||||||
|
)
|
||||||
|
");
|
||||||
|
|
||||||
|
// Create users_meta table
|
||||||
|
$this->db->getConnection()->exec("
|
||||||
|
CREATE TABLE users_meta (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
user_id INTEGER NOT NULL,
|
||||||
|
name TEXT,
|
||||||
|
email TEXT,
|
||||||
|
timezone TEXT,
|
||||||
|
bio TEXT,
|
||||||
|
avatar TEXT,
|
||||||
|
FOREIGN KEY (user_id) REFERENCES users(id)
|
||||||
|
)
|
||||||
|
");
|
||||||
|
|
||||||
|
// Create tables for rate limiter
|
||||||
|
$this->db->getConnection()->exec("
|
||||||
|
CREATE TABLE login_attempts (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
ip_address TEXT NOT NULL,
|
||||||
|
username TEXT NOT NULL,
|
||||||
|
attempted_at TEXT DEFAULT (DATETIME('now'))
|
||||||
|
)
|
||||||
|
");
|
||||||
|
|
||||||
|
$this->db->getConnection()->exec("
|
||||||
|
CREATE TABLE ip_whitelist (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
ip_address TEXT NOT NULL UNIQUE,
|
||||||
|
is_network BOOLEAN DEFAULT 0 CHECK(is_network IN (0,1)),
|
||||||
|
description TEXT,
|
||||||
|
created_at TEXT DEFAULT (DATETIME('now')),
|
||||||
|
created_by TEXT
|
||||||
|
)
|
||||||
|
");
|
||||||
|
|
||||||
|
$this->db->getConnection()->exec("
|
||||||
|
CREATE TABLE ip_blacklist (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
ip_address TEXT NOT NULL UNIQUE,
|
||||||
|
is_network BOOLEAN DEFAULT 0 CHECK(is_network IN (0,1)),
|
||||||
|
reason TEXT,
|
||||||
|
expiry_time TEXT NULL,
|
||||||
|
created_at TEXT DEFAULT (DATETIME('now')),
|
||||||
|
created_by TEXT
|
||||||
|
)
|
||||||
|
");
|
||||||
|
|
||||||
|
$this->user = new User($this->db);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRegister()
|
||||||
|
{
|
||||||
|
$result = $this->user->register('testuser', 'password123');
|
||||||
|
$this->assertTrue($result);
|
||||||
|
|
||||||
|
// Verify user was created
|
||||||
|
$stmt = $this->db->getConnection()->prepare('SELECT * FROM users WHERE username = ?');
|
||||||
|
$stmt->execute(['testuser']);
|
||||||
|
$user = $stmt->fetch(\PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
$this->assertEquals('testuser', $user['username']);
|
||||||
|
$this->assertTrue(password_verify('password123', $user['password']));
|
||||||
|
|
||||||
|
// Verify user_meta was created
|
||||||
|
$stmt = $this->db->getConnection()->prepare('SELECT * FROM users_meta WHERE user_id = ?');
|
||||||
|
$stmt->execute([$user['id']]);
|
||||||
|
$meta = $stmt->fetch(\PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
$this->assertNotNull($meta);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testLogin()
|
||||||
|
{
|
||||||
|
// Create a test user
|
||||||
|
$password = 'password123';
|
||||||
|
$hashedPassword = password_hash($password, PASSWORD_DEFAULT);
|
||||||
|
|
||||||
|
$stmt = $this->db->getConnection()->prepare('INSERT INTO users (username, password) VALUES (?, ?)');
|
||||||
|
$stmt->execute(['testuser', $hashedPassword]);
|
||||||
|
|
||||||
|
// Mock $_SERVER['REMOTE_ADDR'] for rate limiter
|
||||||
|
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
|
||||||
|
|
||||||
|
// Test successful login
|
||||||
|
try {
|
||||||
|
$result = $this->user->login('testuser', $password);
|
||||||
|
$this->assertTrue($result);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$this->fail('Login should not throw an exception for valid credentials: ' . $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test failed login
|
||||||
|
try {
|
||||||
|
$this->user->login('testuser', 'wrongpassword');
|
||||||
|
$this->fail('Login should throw an exception for invalid credentials');
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$this->assertStringContainsString('Invalid credentials', $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test nonexistent user
|
||||||
|
try {
|
||||||
|
$this->user->login('nonexistent', $password);
|
||||||
|
$this->fail('Login should throw an exception for nonexistent user');
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$this->assertStringContainsString('Invalid credentials', $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetUserDetails()
|
||||||
|
{
|
||||||
|
// Create a test user
|
||||||
|
$stmt = $this->db->getConnection()->prepare('INSERT INTO users (username, password) VALUES (?, ?)');
|
||||||
|
$stmt->execute(['testuser', 'hashedpassword']);
|
||||||
|
$userId = $this->db->getConnection()->lastInsertId();
|
||||||
|
|
||||||
|
// Create user meta with some data
|
||||||
|
$stmt = $this->db->getConnection()->prepare('INSERT INTO users_meta (user_id, name, email) VALUES (?, ?, ?)');
|
||||||
|
$stmt->execute([$userId, 'Test User', 'test@example.com']);
|
||||||
|
|
||||||
|
$userDetails = $this->user->getUserDetails($userId);
|
||||||
|
$this->assertIsArray($userDetails);
|
||||||
|
$this->assertCount(1, $userDetails); // Should return one row
|
||||||
|
$user = $userDetails[0]; // Get the first row
|
||||||
|
$this->assertEquals('testuser', $user['username']);
|
||||||
|
$this->assertEquals('Test User', $user['name']);
|
||||||
|
$this->assertEquals('test@example.com', $user['email']);
|
||||||
|
|
||||||
|
// Test nonexistent user
|
||||||
|
$userDetails = $this->user->getUserDetails(999);
|
||||||
|
$this->assertEmpty($userDetails);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,143 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
require_once dirname(__DIR__, 4) . '/app/classes/validator.php';
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class ValidatorTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testRequired()
|
||||||
|
{
|
||||||
|
// Test valid data
|
||||||
|
$data = ['name' => 'John'];
|
||||||
|
$validator = new Validator($data);
|
||||||
|
$rules = ['name' => ['required' => true]];
|
||||||
|
|
||||||
|
$this->assertTrue($validator->validate($rules));
|
||||||
|
$this->assertEmpty($validator->getErrors());
|
||||||
|
|
||||||
|
// Test invalid data
|
||||||
|
$data = ['name' => ''];
|
||||||
|
$validator = new Validator($data);
|
||||||
|
$this->assertFalse($validator->validate($rules));
|
||||||
|
$this->assertNotEmpty($validator->getErrors());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testEmail()
|
||||||
|
{
|
||||||
|
// Test valid email
|
||||||
|
$data = ['email' => 'test@example.com'];
|
||||||
|
$validator = new Validator($data);
|
||||||
|
$rules = ['email' => ['email' => true]];
|
||||||
|
|
||||||
|
$this->assertTrue($validator->validate($rules));
|
||||||
|
$this->assertEmpty($validator->getErrors());
|
||||||
|
|
||||||
|
// Test invalid email
|
||||||
|
$data = ['email' => 'invalid-email'];
|
||||||
|
$validator = new Validator($data);
|
||||||
|
$this->assertFalse($validator->validate($rules));
|
||||||
|
$this->assertNotEmpty($validator->getErrors());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testMinLength()
|
||||||
|
{
|
||||||
|
// Test valid length
|
||||||
|
$data = ['password' => '123456'];
|
||||||
|
$validator = new Validator($data);
|
||||||
|
$rules = ['password' => ['min' => 6]];
|
||||||
|
|
||||||
|
$this->assertTrue($validator->validate($rules));
|
||||||
|
$this->assertEmpty($validator->getErrors());
|
||||||
|
|
||||||
|
// Test invalid length
|
||||||
|
$data = ['password' => '12345'];
|
||||||
|
$validator = new Validator($data);
|
||||||
|
$this->assertFalse($validator->validate($rules));
|
||||||
|
$this->assertNotEmpty($validator->getErrors());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testMaxLength()
|
||||||
|
{
|
||||||
|
// Test valid length
|
||||||
|
$data = ['username' => '12345'];
|
||||||
|
$validator = new Validator($data);
|
||||||
|
$rules = ['username' => ['max' => 5]];
|
||||||
|
|
||||||
|
$this->assertTrue($validator->validate($rules));
|
||||||
|
$this->assertEmpty($validator->getErrors());
|
||||||
|
|
||||||
|
// Test invalid length
|
||||||
|
$data = ['username' => '123456'];
|
||||||
|
$validator = new Validator($data);
|
||||||
|
$this->assertFalse($validator->validate($rules));
|
||||||
|
$this->assertNotEmpty($validator->getErrors());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testNumeric()
|
||||||
|
{
|
||||||
|
// Test valid number
|
||||||
|
$data = ['age' => '25'];
|
||||||
|
$validator = new Validator($data);
|
||||||
|
$rules = ['age' => ['numeric' => true]];
|
||||||
|
|
||||||
|
$this->assertTrue($validator->validate($rules));
|
||||||
|
$this->assertEmpty($validator->getErrors());
|
||||||
|
|
||||||
|
// Test invalid number
|
||||||
|
$data = ['age' => 'twenty-five'];
|
||||||
|
$validator = new Validator($data);
|
||||||
|
$this->assertFalse($validator->validate($rules));
|
||||||
|
$this->assertNotEmpty($validator->getErrors());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUrl()
|
||||||
|
{
|
||||||
|
// Test valid URL
|
||||||
|
$data = ['website' => 'https://example.com'];
|
||||||
|
$validator = new Validator($data);
|
||||||
|
$rules = ['website' => ['url' => true]];
|
||||||
|
|
||||||
|
$this->assertTrue($validator->validate($rules));
|
||||||
|
$this->assertEmpty($validator->getErrors());
|
||||||
|
|
||||||
|
// Test invalid URL
|
||||||
|
$data = ['website' => 'not-a-url'];
|
||||||
|
$validator = new Validator($data);
|
||||||
|
$this->assertFalse($validator->validate($rules));
|
||||||
|
$this->assertNotEmpty($validator->getErrors());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testMultipleRules()
|
||||||
|
{
|
||||||
|
// Test valid data
|
||||||
|
$data = ['email' => 'test@example.com'];
|
||||||
|
$validator = new Validator($data);
|
||||||
|
$rules = ['email' => [
|
||||||
|
'required' => true,
|
||||||
|
'email' => true,
|
||||||
|
'max' => 50
|
||||||
|
]];
|
||||||
|
|
||||||
|
$this->assertTrue($validator->validate($rules));
|
||||||
|
$this->assertEmpty($validator->getErrors());
|
||||||
|
|
||||||
|
// Test invalid data
|
||||||
|
$data = ['email' => str_repeat('a', 51) . '@example.com'];
|
||||||
|
$validator = new Validator($data);
|
||||||
|
$this->assertFalse($validator->validate($rules));
|
||||||
|
$this->assertNotEmpty($validator->getErrors());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCustomErrorMessages()
|
||||||
|
{
|
||||||
|
$data = ['age' => 'not-a-number'];
|
||||||
|
$validator = new Validator($data);
|
||||||
|
$rules = ['age' => ['numeric' => true]];
|
||||||
|
|
||||||
|
$this->assertFalse($validator->validate($rules));
|
||||||
|
$errors = $validator->getErrors();
|
||||||
|
$this->assertNotEmpty($errors);
|
||||||
|
$this->assertEquals('Must be a number', $errors['age'][0]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,113 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
require_once dirname(__DIR__, 4) . '/app/helpers/security.php';
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class SecurityHelperTest extends TestCase
|
||||||
|
{
|
||||||
|
private SecurityHelper $security;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
$this->security = SecurityHelper::getInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGenerateCsrfToken()
|
||||||
|
{
|
||||||
|
$token = $this->security->generateCsrfToken();
|
||||||
|
|
||||||
|
$this->assertNotEmpty($token);
|
||||||
|
$this->assertEquals(64, strlen($token)); // 32 bytes = 64 hex chars
|
||||||
|
$this->assertEquals($token, $_SESSION['csrf_token']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testVerifyCsrfToken()
|
||||||
|
{
|
||||||
|
$token = $this->security->generateCsrfToken();
|
||||||
|
|
||||||
|
$this->assertTrue($this->security->verifyCsrfToken($token));
|
||||||
|
$this->assertFalse($this->security->verifyCsrfToken('invalid_token'));
|
||||||
|
$this->assertFalse($this->security->verifyCsrfToken(''));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSanitizeString()
|
||||||
|
{
|
||||||
|
$input = '<script>alert("xss")</script>';
|
||||||
|
$expected = 'alert("xss")';
|
||||||
|
|
||||||
|
$this->assertEquals($expected, $this->security->sanitizeString($input));
|
||||||
|
$this->assertEquals('', $this->security->sanitizeString(null));
|
||||||
|
$this->assertEquals('', $this->security->sanitizeString([]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testValidateEmail()
|
||||||
|
{
|
||||||
|
$this->assertTrue($this->security->validateEmail('test@example.com'));
|
||||||
|
$this->assertTrue($this->security->validateEmail('user.name+tag@example.co.uk'));
|
||||||
|
$this->assertFalse($this->security->validateEmail('invalid.email'));
|
||||||
|
$this->assertFalse($this->security->validateEmail('@example.com'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testValidateInt()
|
||||||
|
{
|
||||||
|
$this->assertTrue($this->security->validateInt('123'));
|
||||||
|
$this->assertTrue($this->security->validateInt('-123'));
|
||||||
|
$this->assertFalse($this->security->validateInt('12.3'));
|
||||||
|
$this->assertFalse($this->security->validateInt('abc'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testValidateUrl()
|
||||||
|
{
|
||||||
|
$this->assertTrue($this->security->validateUrl('https://example.com'));
|
||||||
|
$this->assertTrue($this->security->validateUrl('http://sub.example.co.uk/path?query=1'));
|
||||||
|
$this->assertTrue($this->security->validateUrl('ftp://example.com')); // Any valid URL is accepted
|
||||||
|
$this->assertFalse($this->security->validateUrl('not-a-url'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSanitizeArray()
|
||||||
|
{
|
||||||
|
$input = [
|
||||||
|
'name' => '<b>John</b>',
|
||||||
|
'email' => 'john@example.com',
|
||||||
|
'nested' => [
|
||||||
|
'key' => '<i>value</i>'
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
$allowedKeys = ['name', 'email'];
|
||||||
|
$result = $this->security->sanitizeArray($input, $allowedKeys);
|
||||||
|
|
||||||
|
$this->assertArrayHasKey('name', $result);
|
||||||
|
$this->assertArrayHasKey('email', $result);
|
||||||
|
$this->assertArrayNotHasKey('nested', $result);
|
||||||
|
$this->assertEquals('John', $result['name']); // HTML tags are stripped
|
||||||
|
$this->assertEquals('john@example.com', $result['email']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testValidateFormData()
|
||||||
|
{
|
||||||
|
$data = [
|
||||||
|
'name' => 'John Doe',
|
||||||
|
'email' => 'invalid-email',
|
||||||
|
'age' => 'not-a-number',
|
||||||
|
'website' => 'not-a-url'
|
||||||
|
];
|
||||||
|
|
||||||
|
$rules = [
|
||||||
|
'name' => ['type' => 'string', 'required' => true, 'min' => 2, 'max' => 50],
|
||||||
|
'email' => ['type' => 'email', 'required' => true],
|
||||||
|
'age' => ['type' => 'integer', 'required' => true],
|
||||||
|
'website' => ['type' => 'url', 'required' => true]
|
||||||
|
];
|
||||||
|
|
||||||
|
$errors = $this->security->validateFormData($data, $rules);
|
||||||
|
|
||||||
|
$this->assertIsArray($errors);
|
||||||
|
$this->assertCount(3, $errors);
|
||||||
|
$this->assertArrayHasKey('email', $errors);
|
||||||
|
$this->assertArrayHasKey('age', $errors);
|
||||||
|
$this->assertArrayHasKey('website', $errors);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
// Set test environment
|
||||||
|
define('PHPUNIT_RUNNING', true);
|
||||||
|
|
||||||
|
// Configure session before starting it
|
||||||
|
ini_set('session.use_strict_mode', '1');
|
||||||
|
ini_set('session.use_only_cookies', '1');
|
||||||
|
ini_set('session.cookie_httponly', '1');
|
||||||
|
ini_set('session.cookie_secure', '1');
|
||||||
|
ini_set('session.cookie_samesite', 'Lax');
|
||||||
|
ini_set('session.gc_maxlifetime', 1440);
|
||||||
|
|
||||||
|
// Start session if not already started
|
||||||
|
//if (session_status() === PHP_SESSION_NONE) {
|
||||||
|
// session_start();
|
||||||
|
//}
|
||||||
|
|
||||||
|
// Load Composer's autoloader
|
||||||
|
require_once __DIR__ . '/vendor/autoload.php';
|
||||||
|
|
||||||
|
// Set error reporting
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
ini_set('display_errors', 1);
|
||||||
|
ini_set('display_startup_errors', 1);
|
||||||
|
|
||||||
|
// Set timezone
|
||||||
|
date_default_timezone_set('UTC');
|
||||||
|
|
||||||
|
// Define global variables needed by the application
|
||||||
|
$GLOBALS['app_root'] = '/';
|
||||||
|
$GLOBALS['config'] = [
|
||||||
|
'db' => [
|
||||||
|
'type' => 'sqlite',
|
||||||
|
'dbFile' => ':memory:'
|
||||||
|
],
|
||||||
|
'folder' => '/',
|
||||||
|
'domain' => 'localhost',
|
||||||
|
'login' => [
|
||||||
|
'max_attempts' => 5,
|
||||||
|
'lockout_time' => 900
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
// Initialize system_messages array
|
||||||
|
$GLOBALS['system_messages'] = [];
|
||||||
|
|
||||||
|
// Set up server variables
|
||||||
|
$_SERVER['PHP_SELF'] = '/index.php';
|
||||||
|
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
|
||||||
|
$_SERVER['HTTP_USER_AGENT'] = 'PHPUnit Test Browser';
|
||||||
|
$_SERVER['HTTP_HOST'] = 'localhost';
|
||||||
|
$_SERVER['REQUEST_URI'] = '/?page=login';
|
||||||
|
$_SERVER['HTTPS'] = 'on';
|
||||||
|
|
||||||
|
// Define global connectDB function
|
||||||
|
if (!function_exists('connectDB')) {
|
||||||
|
function connectDB($config) {
|
||||||
|
global $dbWeb;
|
||||||
|
return [
|
||||||
|
'db' => $dbWeb
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
{
|
||||||
|
"name": "lindeas/jilo-web-tests",
|
||||||
|
"description": "Test Suite for Jilo Web Application",
|
||||||
|
"type": "project",
|
||||||
|
"require": {
|
||||||
|
"php": ">=7.4"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "^9.5",
|
||||||
|
"phpunit/php-code-coverage": "^9.2",
|
||||||
|
"mockery/mockery": "^1.5"
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"files": [
|
||||||
|
"../../app/includes/errors.php",
|
||||||
|
"../../app/classes/database.php",
|
||||||
|
"../../app/classes/agent.php",
|
||||||
|
"../../app/classes/host.php",
|
||||||
|
"../../app/classes/platform.php",
|
||||||
|
"../../app/classes/server.php",
|
||||||
|
"../../app/classes/log.php",
|
||||||
|
"../../app/classes/feedback.php",
|
||||||
|
"../../app/classes/settings.php",
|
||||||
|
"../../app/classes/validator.php",
|
||||||
|
"../../app/classes/router.php",
|
||||||
|
"../../app/classes/ratelimiter.php",
|
||||||
|
"../../app/classes/user.php",
|
||||||
|
"../../app/helpers/security.php",
|
||||||
|
"../../app/includes/errors.php"
|
||||||
|
],
|
||||||
|
"classmap": [
|
||||||
|
"TestCase.php"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"autoload-dev": {
|
||||||
|
"psr-4": {
|
||||||
|
"Tests\\": "./"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"test": "phpunit",
|
||||||
|
"test-coverage": "phpunit --coverage-html coverage"
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"optimize-autoloader": true,
|
||||||
|
"preferred-install": "dist",
|
||||||
|
"sort-packages": true
|
||||||
|
},
|
||||||
|
"minimum-stability": "stable"
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:noNamespaceSchemaLocation="./vendor/phpunit/phpunit/phpunit.xsd"
|
||||||
|
bootstrap="bootstrap.php"
|
||||||
|
colors="true"
|
||||||
|
testdox="true">
|
||||||
|
<testsuites>
|
||||||
|
<testsuite name="Unit">
|
||||||
|
<directory suffix="Test.php">./Unit</directory>
|
||||||
|
</testsuite>
|
||||||
|
</testsuites>
|
||||||
|
<coverage processUncoveredFiles="true">
|
||||||
|
<include>
|
||||||
|
<directory suffix=".php">../../app</directory>
|
||||||
|
</include>
|
||||||
|
<exclude>
|
||||||
|
<directory>../../app/templates</directory>
|
||||||
|
<directory>../../app/includes</directory>
|
||||||
|
</exclude>
|
||||||
|
</coverage>
|
||||||
|
<php>
|
||||||
|
<env name="APP_ENV" value="testing"/>
|
||||||
|
<env name="DB_CONNECTION" value="sqlite"/>
|
||||||
|
<env name="DB_DATABASE" value=":memory:"/>
|
||||||
|
</php>
|
||||||
|
</phpunit>
|
Loading…
Reference in New Issue