Add comprehensive unit tests for Security, UserModel, and Validation utilities
- Implemented SecurityTest to validate token generation, CSRF protection, input sanitization, and rate limiting. - Created UserModelTest to ensure correct database operations for user management, including creation, updating, banning, and fetching active users. - Developed ValidationTest to verify input validation and sanitization for user IDs, nicknames, messages, and API requests. - Introduced Security and Validation utility classes with methods for secure token generation, input sanitization, and comprehensive validation rules.
This commit is contained in:
parent
5692874b10
commit
41cd7a4fd8
32 changed files with 5796 additions and 368 deletions
138
tests/bootstrap.php
Normal file
138
tests/bootstrap.php
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
<?php
|
||||
/**
|
||||
* PHPUnit Test Bootstrap
|
||||
* Sets up test environment and dependencies
|
||||
*/
|
||||
|
||||
// Define test environment
|
||||
define('TESTING', true);
|
||||
define('APP_ENV', 'testing');
|
||||
|
||||
// Include autoloader if it exists, otherwise manually load classes
|
||||
if (file_exists(__DIR__ . '/../includes/autoloader.php')) {
|
||||
require_once __DIR__ . '/../includes/autoloader.php';
|
||||
}
|
||||
|
||||
// Initialize error handling for tests
|
||||
if (class_exists('ErrorHandler')) {
|
||||
ErrorHandler::initialize();
|
||||
}
|
||||
|
||||
// Set up test database configuration
|
||||
$_ENV['APP_ENV'] = 'testing';
|
||||
$_ENV['DB_DATABASE'] = ':memory:'; // Use in-memory SQLite for tests
|
||||
$_ENV['DB_DRIVER'] = 'sqlite';
|
||||
|
||||
// Mock session for testing
|
||||
if (!isset($_SESSION)) {
|
||||
$_SESSION = [];
|
||||
}
|
||||
|
||||
// Mock POST/GET data if needed
|
||||
if (!isset($_POST)) {
|
||||
$_POST = [];
|
||||
}
|
||||
if (!isset($_GET)) {
|
||||
$_GET = [];
|
||||
}
|
||||
if (!isset($_SERVER)) {
|
||||
$_SERVER = [
|
||||
'REQUEST_METHOD' => 'GET',
|
||||
'HTTP_HOST' => 'localhost',
|
||||
'SERVER_NAME' => 'localhost',
|
||||
'SERVER_PORT' => '80',
|
||||
'REQUEST_URI' => '/',
|
||||
'SCRIPT_NAME' => '/index.php',
|
||||
'PHP_SELF' => '/index.php',
|
||||
'REMOTE_ADDR' => '127.0.0.1',
|
||||
'HTTP_USER_AGENT' => 'PHPUnit/Test'
|
||||
];
|
||||
}
|
||||
|
||||
// Initialize PDO for in-memory SQLite testing
|
||||
class TestDatabaseHelper
|
||||
{
|
||||
private static $pdo = null;
|
||||
|
||||
public static function getTestPdo()
|
||||
{
|
||||
if (self::$pdo === null) {
|
||||
self::$pdo = new PDO('sqlite::memory:');
|
||||
self::$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
self::$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
|
||||
|
||||
// Enable WAL mode for better testing performance
|
||||
self::$pdo->exec('PRAGMA journal_mode=WAL');
|
||||
self::$pdo->exec('PRAGMA synchronous=NORMAL');
|
||||
}
|
||||
|
||||
return self::$pdo;
|
||||
}
|
||||
|
||||
public static function setupTestSchema()
|
||||
{
|
||||
$pdo = self::getTestPdo();
|
||||
|
||||
// Create tables for testing
|
||||
$sql = file_get_contents(__DIR__ . '/../migrations/001_create_tables.sql');
|
||||
$pdo->exec($sql);
|
||||
|
||||
// Insert test data if needed
|
||||
self::insertTestData($pdo);
|
||||
}
|
||||
|
||||
private static function insertTestData($pdo)
|
||||
{
|
||||
// Insert some test users
|
||||
$pdo->exec("INSERT INTO users (user_id, nickname, ip_address, session_id, last_seen)
|
||||
VALUES ('test_user_1', 'TestUser1', '192.168.1.100', 'session_123', datetime('now'))");
|
||||
|
||||
$pdo->exec("INSERT INTO users (user_id, nickname, ip_address, session_id, last_seen)
|
||||
VALUES ('test_user_2', 'TestUser2', '192.168.1.101', 'session_456', datetime('now'))");
|
||||
|
||||
// Insert test messages
|
||||
$pdo->exec("INSERT INTO chat_messages (user_id, nickname, message, is_admin, ip_address, time_formatted)
|
||||
VALUES ('test_user_1', 'TestUser1', 'Hello from test user 1', 0, '192.168.1.100', '12:00')");
|
||||
|
||||
$pdo->exec("INSERT INTO chat_messages (user_id, nickname, message, is_admin, ip_address, time_formatted)
|
||||
VALUES ('test_user_2', 'TestUser2', 'Hello from test user 2', 0, '192.168.1.101', '12:01')");
|
||||
|
||||
// Insert test active viewers
|
||||
$pdo->exec("INSERT INTO active_viewers (user_id, nickname, ip_address, session_id, is_admin, last_seen)
|
||||
VALUES ('test_user_1', 'TestUser1', '192.168.1.100', 'session_123', 0, datetime('now'))");
|
||||
}
|
||||
|
||||
public static function teardown()
|
||||
{
|
||||
self::$pdo = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up any existing test artifacts
|
||||
function cleanupTestEnvironment()
|
||||
{
|
||||
// Clear test session data
|
||||
$_SESSION = [];
|
||||
|
||||
// Remove any test files
|
||||
$testFiles = [
|
||||
__DIR__ . '/../logs/app.log',
|
||||
__DIR__ . '/../active_viewers.json.backup',
|
||||
__DIR__ . '/../chat_messages.json.backup'
|
||||
];
|
||||
|
||||
foreach ($testFiles as $file) {
|
||||
if (file_exists($file)) {
|
||||
@unlink($file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set up test environment
|
||||
cleanupTestEnvironment();
|
||||
|
||||
// Register shutdown function to clean up
|
||||
register_shutdown_function(function() {
|
||||
TestDatabaseHelper::teardown();
|
||||
cleanupTestEnvironment();
|
||||
});
|
||||
187
tests/unit/SecurityTest.php
Normal file
187
tests/unit/SecurityTest.php
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
<?php
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* Test Security utility functions
|
||||
*/
|
||||
class SecurityTest extends TestCase
|
||||
{
|
||||
protected function setUp(): void
|
||||
{
|
||||
// Clear any previous session data
|
||||
$_SESSION = [];
|
||||
$_POST = [];
|
||||
$_GET = [];
|
||||
}
|
||||
|
||||
public function testGenerateSecureToken()
|
||||
{
|
||||
$token1 = Security::generateSecureToken(16);
|
||||
$token2 = Security::generateSecureToken(16);
|
||||
|
||||
// Test length
|
||||
$this->assertEquals(32, strlen($token1)); // 16 bytes = 32 hex chars
|
||||
$this->assertEquals(32, strlen($token2));
|
||||
|
||||
// Test uniqueness
|
||||
$this->assertNotEquals($token1, $token2);
|
||||
|
||||
// Test valid hex characters
|
||||
$this->assertMatchesRegularExpression('/^[a-f0-9]+$/', $token1);
|
||||
$this->assertMatchesRegularExpression('/^[a-f0-9]+$/', $token2);
|
||||
}
|
||||
|
||||
public function testGenerateSecureUserId()
|
||||
{
|
||||
$userId1 = Security::generateSecureUserId();
|
||||
$userId2 = Security::generateSecureUserId();
|
||||
|
||||
// Test format (32 char hex)
|
||||
$this->assertEquals(32, strlen($userId1));
|
||||
$this->assertEquals(32, strlen($userId2));
|
||||
|
||||
// Test uniqueness
|
||||
$this->assertNotEquals($userId1, $userId2);
|
||||
|
||||
// Test valid characters
|
||||
$this->assertMatchesRegularExpression('/^[a-f0-9]+$/', $userId1);
|
||||
$this->assertMatchesRegularExpression('/^[a-f0-9]+$/', $userId2);
|
||||
}
|
||||
|
||||
public function testGetClientIP()
|
||||
{
|
||||
// Test with default server vars
|
||||
$ip = Security::getClientIP();
|
||||
$this->assertEquals('127.0.0.1', $ip);
|
||||
|
||||
// Test with forwarded headers
|
||||
$_SERVER['HTTP_X_FORWARDED_FOR'] = '192.168.1.100, 10.0.0.1';
|
||||
$ip = Security::getClientIP();
|
||||
$this->assertEquals('192.168.1.100', $ip);
|
||||
|
||||
// Test with real IP header
|
||||
$_SERVER['HTTP_X_REAL_IP'] = '203.0.113.1';
|
||||
unset($_SERVER['HTTP_X_FORWARDED_FOR']);
|
||||
$ip = Security::getClientIP();
|
||||
$this->assertEquals('203.0.113.1', $ip);
|
||||
}
|
||||
|
||||
public function testSanitizeInput()
|
||||
{
|
||||
// Test string sanitization
|
||||
$input = '<script>alert("xss")</script>Hello World';
|
||||
$result = Security::sanitizeInput($input, 'string');
|
||||
$this->assertEquals('alert("xss")Hello World', $result);
|
||||
|
||||
// Test email sanitization
|
||||
$email = 'test@example.com<script>evil</script>';
|
||||
$result = Security::sanitizeInput($email, 'email');
|
||||
$this->assertEquals('test@example.com<script>evil</script>', $result);
|
||||
|
||||
// Test URL sanitization
|
||||
$url = 'http://example.com/path<script>evil</script>';
|
||||
$result = Security::sanitizeInput($url, 'url');
|
||||
$this->assertEquals('http://example.com/path', $result); // Scripts should be stripped
|
||||
}
|
||||
|
||||
public function testValidateCSRFToken()
|
||||
{
|
||||
// Generate a token
|
||||
$token = Security::generateCSRFToken();
|
||||
$_SESSION['csrf_token'] = $token;
|
||||
|
||||
// Test valid token
|
||||
$this->assertTrue(Security::validateCSRFToken($token));
|
||||
|
||||
// Test invalid token
|
||||
$this->assertFalse(Security::validateCSRFToken('invalid_token'));
|
||||
|
||||
// Test missing token
|
||||
$this->assertFalse(Security::validateCSRFToken(''));
|
||||
}
|
||||
|
||||
public function testCheckRateLimit()
|
||||
{
|
||||
$ip = '192.168.1.100';
|
||||
|
||||
// First request should succeed
|
||||
$result1 = Security::checkRateLimit($ip, 'test_action', 3, 60);
|
||||
$this->assertTrue($result1);
|
||||
|
||||
// Second request should succeed
|
||||
$result2 = Security::checkRateLimit($ip, 'test_action', 3, 60);
|
||||
$this->assertTrue($result2);
|
||||
|
||||
// Third request should succeed
|
||||
$result3 = Security::checkRateLimit($ip, 'test_action', 3, 60);
|
||||
$this->assertTrue($result3);
|
||||
|
||||
// Fourth request should fail (over limit)
|
||||
$result4 = Security::checkRateLimit($ip, 'test_action', 3, 60);
|
||||
$this->assertFalse($result4);
|
||||
}
|
||||
|
||||
public function testIsValidStreamUrl()
|
||||
{
|
||||
// Valid URLs
|
||||
$this->assertTrue(Security::isValidStreamUrl('http://127.0.0.1:8080/stream'));
|
||||
$this->assertTrue(Security::isValidStreamUrl('https://127.0.0.1:8080/stream'));
|
||||
$this->assertTrue(Security::isValidStreamUrl('http://localhost:8080/stream'));
|
||||
|
||||
// Invalid URLs
|
||||
$this->assertFalse(Security::isValidStreamUrl('http://evil.com/stream'));
|
||||
$this->assertFalse(Security::isValidStreamUrl('http://192.168.1.1/stream'));
|
||||
$this->assertFalse(Security::isValidStreamUrl('javascript:alert(1)'));
|
||||
$this->assertFalse(Security::isValidStreamUrl(''));
|
||||
}
|
||||
|
||||
public function testAdminAuthentication()
|
||||
{
|
||||
// Test without any auth setup
|
||||
$this->assertFalse(Security::isAdminAuthenticated());
|
||||
|
||||
// Set up session auth
|
||||
$_SESSION['admin_authenticated'] = true;
|
||||
$_SESSION['admin_login_time'] = time();
|
||||
|
||||
$this->assertTrue(Security::isAdminAuthenticated());
|
||||
}
|
||||
|
||||
public function testAuthenticateAdmin()
|
||||
{
|
||||
// This would need proper config setup for real testing
|
||||
// For now, test that the method exists and handles failures
|
||||
$result = Security::authenticateAdmin('invalid_user', 'invalid_pass');
|
||||
$this->assertFalse($result);
|
||||
}
|
||||
|
||||
public function testDetectSuspiciousActivity()
|
||||
{
|
||||
// Test with normal request
|
||||
$warnings = Security::detectSuspiciousActivity();
|
||||
$this->assertIsArray($warnings);
|
||||
|
||||
// Test with suspicious user agent
|
||||
$_SERVER['HTTP_USER_AGENT'] = 'sqlmap';
|
||||
$warnings = Security::detectSuspiciousActivity();
|
||||
$this->assertContains('Suspicious user agent detected', $warnings);
|
||||
|
||||
// Reset
|
||||
$_SERVER['HTTP_USER_AGENT'] = 'PHPUnit/Test';
|
||||
}
|
||||
|
||||
public function testLogSecurityEvent()
|
||||
{
|
||||
// Start output buffering to capture logs
|
||||
ob_start();
|
||||
|
||||
// Generate a security event
|
||||
Security::logSecurityEvent('test_event', ['test_data' => 'value']);
|
||||
|
||||
// The actual logging happens in ErrorHandler, so we test that no exceptions are thrown
|
||||
$this->assertTrue(true);
|
||||
|
||||
ob_end_clean();
|
||||
}
|
||||
}
|
||||
215
tests/unit/UserModelTest.php
Normal file
215
tests/unit/UserModelTest.php
Normal file
|
|
@ -0,0 +1,215 @@
|
|||
<?php
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* Test UserModel database operations
|
||||
*/
|
||||
class UserModelTest extends TestCase
|
||||
{
|
||||
private $userModel;
|
||||
private $testUserId;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
// Set up in-memory database for testing
|
||||
TestDatabaseHelper::setupTestSchema();
|
||||
$this->userModel = new UserModel();
|
||||
$this->testUserId = 'test_user_' . bin2hex(random_bytes(8));
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
// Clean up after each test
|
||||
$pdo = TestDatabaseHelper::getTestPdo();
|
||||
$pdo->exec('DELETE FROM users');
|
||||
$pdo->exec('DELETE FROM chat_messages');
|
||||
$pdo->exec('DELETE FROM active_viewers');
|
||||
$pdo->exec('DELETE FROM banned_users');
|
||||
}
|
||||
|
||||
public function testCreateOrUpdateNewUser()
|
||||
{
|
||||
$userData = [
|
||||
'nickname' => 'TestUser',
|
||||
'ip_address' => '192.168.1.100',
|
||||
'session_id' => 'session_123456'
|
||||
];
|
||||
|
||||
$result = $this->userModel->createOrUpdate($this->testUserId, $userData);
|
||||
|
||||
$this->assertNotFalse($result);
|
||||
|
||||
// Verify user was created
|
||||
$user = $this->userModel->getByUserId($this->testUserId);
|
||||
$this->assertNotFalse($user);
|
||||
$this->assertEquals($this->testUserId, $user['user_id']);
|
||||
$this->assertEquals('TestUser', $user['nickname']);
|
||||
$this->assertEquals('192.168.1.100', $user['ip_address']);
|
||||
}
|
||||
|
||||
public function testCreateOrUpdateExistingUser()
|
||||
{
|
||||
// First create user
|
||||
$this->userModel->createOrUpdate($this->testUserId, ['nickname' => 'OriginalName']);
|
||||
|
||||
// Update existing user
|
||||
$this->userModel->createOrUpdate($this->testUserId, ['nickname' => 'UpdatedName']);
|
||||
|
||||
$user = $this->userModel->getByUserId($this->testUserId);
|
||||
$this->assertEquals('UpdatedName', $user['nickname']);
|
||||
}
|
||||
|
||||
public function testGetActiveUsers()
|
||||
{
|
||||
// Create test users
|
||||
$userId1 = 'active_user_1';
|
||||
$userId2 = 'active_user_2';
|
||||
$userId3 = 'inactive_user';
|
||||
|
||||
// Add active users
|
||||
$this->userModel->createOrUpdate($userId1, ['nickname' => 'Active1']);
|
||||
$this->userModel->createOrUpdate($userId2, ['nickname' => 'Active2']);
|
||||
$this->userModel->createOrUpdate($userId3, ['nickname' => 'Inactive']);
|
||||
|
||||
// Simulate inactive user (old timestamp)
|
||||
$pdo = TestDatabaseHelper::getTestPdo();
|
||||
$pdo->exec("UPDATE users SET last_seen = datetime('now', '-40 seconds') WHERE user_id = '$userId3'");
|
||||
|
||||
$activeUsers = $this->userModel->getActiveUsers(30);
|
||||
|
||||
$this->assertCount(2, $activeUsers);
|
||||
|
||||
// Verify active users are returned
|
||||
$userIds = array_column($activeUsers, 'user_id');
|
||||
$this->assertContains($userId1, $userIds);
|
||||
$this->assertContains($userId2, $userIds);
|
||||
$this->assertNotContains($userId3, $userIds);
|
||||
}
|
||||
|
||||
public function testUpdateLastSeen()
|
||||
{
|
||||
// Create user
|
||||
$this->userModel->createOrUpdate($this->testUserId, ['nickname' => 'TestUser']);
|
||||
|
||||
// Get initial last_seen
|
||||
$user = $this->userModel->getByUserId($this->testUserId);
|
||||
$initialLastSeen = $user['last_seen'];
|
||||
|
||||
// Wait a moment to ensure different timestamp
|
||||
sleep(1);
|
||||
|
||||
// Update last seen
|
||||
$result = $this->userModel->updateLastSeen($this->testUserId);
|
||||
$this->assertNotFalse($result);
|
||||
|
||||
// Verify last seen was updated
|
||||
$updatedUser = $this->userModel->getByUserId($this->testUserId);
|
||||
$this->assertNotEquals($initialLastSeen, $updatedUser['last_seen']);
|
||||
}
|
||||
|
||||
public function testBanAndUnbanUser()
|
||||
{
|
||||
// Create user first
|
||||
$this->userModel->createOrUpdate($this->testUserId, ['nickname' => 'TestUser']);
|
||||
|
||||
// Test initial state - not banned
|
||||
$this->assertFalse($this->userModel->isBanned($this->testUserId));
|
||||
|
||||
// Ban user
|
||||
$result = $this->userModel->banUser($this->testUserId, 'admin_user', 'Test ban reason');
|
||||
$this->assertNotFalse($result);
|
||||
|
||||
// Verify user is banned
|
||||
$this->assertTrue($this->userModel->isBanned($this->testUserId));
|
||||
|
||||
// Get banned users list
|
||||
$bannedUsers = $this->userModel->getBannedUsers();
|
||||
$this->assertCount(1, $bannedUsers);
|
||||
$this->assertEquals($this->testUserId, $bannedUsers[0]['user_id']);
|
||||
$this->assertEquals('Test ban reason', $bannedUsers[0]['reason']);
|
||||
|
||||
// Unban user
|
||||
$result = $this->userModel->unbanUser($this->testUserId);
|
||||
$this->assertNotFalse($result);
|
||||
|
||||
// Verify user is no longer banned
|
||||
$this->assertFalse($this->userModel->isBanned($this->testUserId));
|
||||
|
||||
// Verify banned users list is empty
|
||||
$bannedUsers = $this->userModel->getBannedUsers();
|
||||
$this->assertCount(0, $bannedUsers);
|
||||
}
|
||||
|
||||
public function testUpdateNickname()
|
||||
{
|
||||
// Create user
|
||||
$this->userModel->createOrUpdate($this->testUserId, ['nickname' => 'OldName']);
|
||||
|
||||
// Update nickname
|
||||
$result = $this->userModel->updateNickname($this->testUserId, 'NewName');
|
||||
$this->assertNotFalse($result);
|
||||
|
||||
// Verify nickname was updated
|
||||
$user = $this->userModel->getByUserId($this->testUserId);
|
||||
$this->assertEquals('NewName', $user['nickname']);
|
||||
}
|
||||
|
||||
public function testCleanupOldRecords()
|
||||
{
|
||||
// Create user
|
||||
$this->userModel->createOrUpdate($this->testUserId, ['nickname' => 'TestUser']);
|
||||
|
||||
// Set last_seen to be very old
|
||||
$pdo = TestDatabaseHelper::getTestPdo();
|
||||
$pdo->exec("UPDATE users SET last_seen = datetime('now', '-40 days') WHERE user_id = '$this->testUserId'");
|
||||
|
||||
// Cleanup records older than 30 days
|
||||
$result = $this->userModel->cleanupOldRecords(30);
|
||||
$this->assertGreaterThan(0, $result); // Should have deleted at least one record
|
||||
|
||||
// Verify user was cleaned up
|
||||
$user = $this->userModel->getByUserId($this->testUserId);
|
||||
$this->assertFalse($user);
|
||||
}
|
||||
|
||||
public function testNonExistentUser()
|
||||
{
|
||||
$user = $this->userModel->getByUserId('nonexistent_user');
|
||||
$this->assertFalse($user);
|
||||
|
||||
// Test update on non-existent user
|
||||
$result = $this->userModel->updateLastSeen('nonexistent_user');
|
||||
$this->assertEquals(0, $result); // No rows affected
|
||||
}
|
||||
|
||||
public function testGetActiveUsersWithinTimeframe()
|
||||
{
|
||||
// Create users with different activity times
|
||||
$userId1 = 'recent_user';
|
||||
$userId2 = 'old_user';
|
||||
|
||||
$this->userModel->createOrUpdate($userId1, ['nickname' => 'Recent']);
|
||||
$this->userModel->createOrUpdate($userId2, ['nickname' => 'Old']);
|
||||
|
||||
// Make one user appear old
|
||||
$pdo = TestDatabaseHelper::getTestPdo();
|
||||
$pdo->exec("UPDATE users SET last_seen = datetime('now', '-1 hour') WHERE user_id = '$userId2'");
|
||||
|
||||
// Get users active within 30 minutes
|
||||
$activeUsers = $this->userModel->getActiveUsers(30); // 30 seconds for testing
|
||||
$this->assertCount(1, $activeUsers);
|
||||
$this->assertEquals($userId1, $activeUsers[0]['user_id']);
|
||||
}
|
||||
|
||||
public function testDatabaseConnectionFailure()
|
||||
{
|
||||
// This test would verify error handling in a real scenario
|
||||
// For now, we test that the model handles database operations gracefully
|
||||
$this->assertIsObject($this->userModel);
|
||||
|
||||
// Test that methods return false/null on failure rather than throwing exceptions
|
||||
$result = $this->userModel->getByUserId('invalid_id_format_x');
|
||||
$this->assertFalse($result);
|
||||
}
|
||||
}
|
||||
222
tests/unit/ValidationTest.php
Normal file
222
tests/unit/ValidationTest.php
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
<?php
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* Test Validation utility functions
|
||||
*/
|
||||
class ValidationTest extends TestCase
|
||||
{
|
||||
protected function setUp(): void
|
||||
{
|
||||
// Clear any previous test data
|
||||
$_POST = [];
|
||||
$_GET = [];
|
||||
}
|
||||
|
||||
public function testValidateUserId()
|
||||
{
|
||||
// Valid user IDs
|
||||
$result = Validation::validateUserId('a1b2c3d4e5f67890123456789012abcd');
|
||||
$this->assertTrue($result['valid']);
|
||||
$this->assertEquals('a1b2c3d4e5f67890123456789012abcd', $result['user_id']);
|
||||
|
||||
// Invalid user IDs
|
||||
$result = Validation::validateUserId('invalid_user_id');
|
||||
$this->assertFalse($result['valid']);
|
||||
|
||||
$result = Validation::validateUserId('a1b2c3d4'); // Too short
|
||||
$this->assertFalse($result['valid']);
|
||||
|
||||
$result = Validation::validateUserId('a1b2c3d4e5f67890123456789012abcdextra'); // Too long
|
||||
$this->assertFalse($result['valid']);
|
||||
|
||||
$result = Validation::validateUserId('gggggggggggggggggggggggggggggggg'); // Invalid chars
|
||||
$this->assertFalse($result['valid']);
|
||||
}
|
||||
|
||||
public function testValidateNickname()
|
||||
{
|
||||
// Valid nicknames
|
||||
$result = Validation::validateNickname('JohnDoe');
|
||||
$this->assertTrue($result['valid']);
|
||||
|
||||
$result = Validation::validateNickname('Test User');
|
||||
$this->assertTrue($result['valid']);
|
||||
|
||||
$result = Validation::validateNickname("O'Connor-Smith");
|
||||
$this->assertTrue($result['valid']);
|
||||
|
||||
// Invalid nicknames
|
||||
$result = Validation::validateNickname(''); // Empty
|
||||
$this->assertFalse($result['valid']);
|
||||
|
||||
$result = Validation::validateNickname('A'); // Too short
|
||||
$this->assertFalse($result['valid']);
|
||||
|
||||
$result = Validation::validateNickname(str_repeat('A', 21)); // Too long
|
||||
$this->assertFalse($result['valid']);
|
||||
|
||||
$result = Validation::validateNickname('Invalid@Name'); // Invalid chars
|
||||
$this->assertFalse($result['valid']);
|
||||
|
||||
$result = Validation::validateNickname('<script>evil</script>'); // XSS attempt
|
||||
$this->assertFalse($result['valid']);
|
||||
}
|
||||
|
||||
public function testValidateMessage()
|
||||
{
|
||||
// Valid messages
|
||||
$result = Validation::validateMessage('Hello World!');
|
||||
$this->assertTrue($result['valid']);
|
||||
|
||||
$result = Validation::validateMessage('This is a longer message with punctuation, numbers 123, and symbols @#$%!');
|
||||
$this->assertTrue($result['valid']);
|
||||
|
||||
// Invalid messages
|
||||
$result = Validation::validateMessage(''); // Empty
|
||||
$this->assertFalse($result['valid']);
|
||||
|
||||
$result = Validation::validateMessage(str_repeat('A', 1001)); // Too long
|
||||
$this->assertFalse($result['valid']);
|
||||
|
||||
$result = Validation::validateMessage('<script>alert("xss")</script>'); // XSS
|
||||
$this->assertFalse($result['valid']);
|
||||
}
|
||||
|
||||
public function testValidateMessageSend()
|
||||
{
|
||||
// Valid message send data
|
||||
$data = [
|
||||
'nickname' => 'TestUser',
|
||||
'message' => 'Hello World!',
|
||||
'user_agent' => 'Mozilla/5.0 (Test Browser)',
|
||||
'ip_address' => '192.168.1.1'
|
||||
];
|
||||
|
||||
$result = Validation::validateMessageSend($data);
|
||||
$this->assertTrue($result['valid']);
|
||||
$this->assertEquals($data['nickname'], $result['validated']['nickname']);
|
||||
$this->assertEquals($data['message'], $result['validated']['message']);
|
||||
|
||||
// Invalid message send data
|
||||
$invalidData = [
|
||||
'nickname' => 'Invalid@Name',
|
||||
'message' => '<script>evil</script>',
|
||||
];
|
||||
|
||||
$result = Validation::validateMessageSend($invalidData);
|
||||
$this->assertFalse($result['valid']);
|
||||
$this->assertArrayHasKey('errors', $result);
|
||||
}
|
||||
|
||||
public function testValidateHeartbeat()
|
||||
{
|
||||
// Valid heartbeat data
|
||||
$data = [
|
||||
'nickname' => 'TestUser',
|
||||
'user_id' => 'a1b2c3d4e5f67890123456789012abcd',
|
||||
'session_id' => 'session_123456'
|
||||
];
|
||||
|
||||
$result = Validation::validateHeartbeat($data);
|
||||
$this->assertTrue($result['valid']);
|
||||
$this->assertEquals($data['nickname'], $result['validated']['nickname']);
|
||||
|
||||
// Invalid heartbeat data
|
||||
$invalidData = [
|
||||
'nickname' => str_repeat('A', 21), // Too long
|
||||
'user_id' => 'invalid_id',
|
||||
];
|
||||
|
||||
$result = Validation::validateHeartbeat($invalidData);
|
||||
$this->assertFalse($result['valid']);
|
||||
$this->assertArrayHasKey('errors', $result);
|
||||
}
|
||||
|
||||
public function testValidateAdminLogin()
|
||||
{
|
||||
// Valid login data
|
||||
$result = Validation::validateAdminLogin('admin_user', 'valid_password');
|
||||
$this->assertTrue($result['valid']);
|
||||
$this->assertEquals('admin_user', $result['data']['username']);
|
||||
|
||||
// Invalid login data
|
||||
$result = Validation::validateAdminLogin('', 'password'); // Empty username
|
||||
$this->assertFalse($result['valid']);
|
||||
|
||||
$result = Validation::validateAdminLogin('admin', ''); // Empty password
|
||||
$this->assertFalse($result['valid']);
|
||||
|
||||
$result = Validation::validateAdminLogin('us', 'password'); // Username too short
|
||||
$this->assertFalse($result['valid']);
|
||||
|
||||
$result = Validation::validateAdminLogin('user@domain.com', 'password'); // Invalid username format
|
||||
$this->assertFalse($result['valid']);
|
||||
}
|
||||
|
||||
public function testIsValidEmail()
|
||||
{
|
||||
// Valid emails
|
||||
$this->assertTrue(Validation::isValidEmail('user@example.com'));
|
||||
$this->assertTrue(Validation::isValidEmail('test.user+tag@example.co.uk'));
|
||||
$this->assertTrue(Validation::isValidEmail('user@localhost'));
|
||||
|
||||
// Invalid emails
|
||||
$this->assertFalse(Validation::isValidEmail('invalid-email'));
|
||||
$this->assertFalse(Validation::isValidEmail('user@'));
|
||||
$this->assertFalse(Validation::isValidEmail('@example.com'));
|
||||
$this->assertFalse(Validation::isValidEmail('user@.com'));
|
||||
}
|
||||
|
||||
public function testIsValidURL()
|
||||
{
|
||||
// Valid URLs
|
||||
$this->assertTrue(Validation::isValidURL('http://example.com'));
|
||||
$this->assertTrue(Validation::isValidURL('https://example.com/path?query=1'));
|
||||
$this->assertTrue(Validation::isValidURL('ftp://example.com/file.txt'));
|
||||
|
||||
// Invalid URLs
|
||||
$this->assertFalse(Validation::isValidURL('not-a-url'));
|
||||
$this->assertFalse(Validation::isValidURL('javascript:alert(1)'));
|
||||
$this->assertFalse(Validation::isValidURL(''));
|
||||
}
|
||||
|
||||
public function testCleanString()
|
||||
{
|
||||
// Test normal cleaning
|
||||
$result = Validation::cleanString(' Hello World ');
|
||||
$this->assertEquals('Hello World', $result);
|
||||
|
||||
// Test with HTML entities
|
||||
$result = Validation::cleanString('Hello & World <tag>');
|
||||
$this->assertEquals('Hello & World <tag>', $result);
|
||||
|
||||
// Test with script tags (should be encoded)
|
||||
$result = Validation::cleanString('<script>alert(1)</script>Hello');
|
||||
$this->assertEquals('<script>alert(1)</script>Hello', $result);
|
||||
}
|
||||
|
||||
public function testLengthBetween()
|
||||
{
|
||||
// Test valid lengths
|
||||
$this->assertTrue(Validation::lengthBetween('test', 2, 10));
|
||||
$this->assertTrue(Validation::lengthBetween('test', 4, 4));
|
||||
|
||||
// Test invalid lengths
|
||||
$this->assertFalse(Validation::lengthBetween('t', 2, 10)); // Too short
|
||||
$this->assertFalse(Validation::lengthBetween('this_is_a_very_long_string', 2, 10)); // Too long
|
||||
}
|
||||
|
||||
public function testMatchesPattern()
|
||||
{
|
||||
// Test valid patterns
|
||||
$this->assertTrue(Validation::matchesPattern('123', '/^\d+$/'));
|
||||
$this->assertTrue(Validation::matchesPattern('abc123', '/^[a-zA-Z0-9]+$/'));
|
||||
$this->assertTrue(Validation::matchesPattern('test@example.com', '/^[^\s@]+@[^\s@]+\.[^\s@]+$/')); // Simple email regex
|
||||
|
||||
// Test invalid patterns
|
||||
$this->assertFalse(Validation::matchesPattern('abc', '/^\d+$/')); // Not numeric
|
||||
$this->assertFalse(Validation::matchesPattern('invalid-email', '/^[^\s@]+@[^\s@]+\.[^\s@]+$/')); // Not email
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue