feat: Complete Woles Framework v1.0 with enterprise-grade UI
- Add comprehensive error handling system with custom error pages - Implement professional enterprise-style design with Tailwind CSS - Create modular HMVC architecture with clean separation of concerns - Add security features: CSRF protection, XSS filtering, Argon2ID hashing - Include CLI tools for development workflow - Add error reporting dashboard with system monitoring - Implement responsive design with consistent slate color scheme - Replace all emoji icons with professional SVG icons - Add comprehensive test suite with PHPUnit - Include database migrations and seeders - Add proper exception handling with fallback pages - Implement template engine with custom syntax support - Add helper functions and facades for clean code - Include proper logging and debugging capabilities
This commit is contained in:
86
tests/RouterTest.php
Normal file
86
tests/RouterTest.php
Normal file
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
namespace Tests;
|
||||
|
||||
use App\Core\Router;
|
||||
|
||||
/**
|
||||
* Router test cases
|
||||
*/
|
||||
class RouterTest extends TestCase
|
||||
{
|
||||
private Router $router;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->router = new Router();
|
||||
}
|
||||
|
||||
public function testCanRegisterGetRoute(): void
|
||||
{
|
||||
$this->router->get('/test', 'TestController@index');
|
||||
|
||||
$routes = $this->router->getRoutes();
|
||||
$this->assertArrayHasKey('GET', $routes);
|
||||
$this->assertCount(1, $routes['GET']);
|
||||
}
|
||||
|
||||
public function testCanRegisterPostRoute(): void
|
||||
{
|
||||
$this->router->post('/test', 'TestController@store');
|
||||
|
||||
$routes = $this->router->getRoutes();
|
||||
$this->assertArrayHasKey('POST', $routes);
|
||||
$this->assertCount(1, $routes['POST']);
|
||||
}
|
||||
|
||||
public function testCanMatchSimpleRoute(): void
|
||||
{
|
||||
$this->router->get('/test', 'TestController@index');
|
||||
|
||||
$route = $this->router->match('GET', '/test');
|
||||
|
||||
$this->assertNotNull($route);
|
||||
$this->assertEquals('TestController@index', $route['handler']);
|
||||
}
|
||||
|
||||
public function testCanMatchRouteWithParameters(): void
|
||||
{
|
||||
$this->router->get('/users/{id}', 'UserController@show');
|
||||
|
||||
$route = $this->router->match('GET', '/users/123');
|
||||
|
||||
$this->assertNotNull($route);
|
||||
$this->assertEquals('UserController@show', $route['handler']);
|
||||
$this->assertEquals('123', $route['params']['id']);
|
||||
}
|
||||
|
||||
public function testReturnsNullForNonMatchingRoute(): void
|
||||
{
|
||||
$this->router->get('/test', 'TestController@index');
|
||||
|
||||
$route = $this->router->match('GET', '/nonexistent');
|
||||
|
||||
$this->assertNull($route);
|
||||
}
|
||||
|
||||
public function testCanMatchMultipleRoutes(): void
|
||||
{
|
||||
$this->router->get('/users', 'UserController@index');
|
||||
$this->router->get('/users/{id}', 'UserController@show');
|
||||
$this->router->post('/users', 'UserController@store');
|
||||
|
||||
$indexRoute = $this->router->match('GET', '/users');
|
||||
$showRoute = $this->router->match('GET', '/users/123');
|
||||
$storeRoute = $this->router->match('POST', '/users');
|
||||
|
||||
$this->assertNotNull($indexRoute);
|
||||
$this->assertNotNull($showRoute);
|
||||
$this->assertNotNull($storeRoute);
|
||||
|
||||
$this->assertEquals('UserController@index', $indexRoute['handler']);
|
||||
$this->assertEquals('UserController@show', $showRoute['handler']);
|
||||
$this->assertEquals('UserController@store', $storeRoute['handler']);
|
||||
}
|
||||
}
|
||||
82
tests/SecurityTest.php
Normal file
82
tests/SecurityTest.php
Normal file
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
namespace Tests;
|
||||
|
||||
use App\Core\Security;
|
||||
|
||||
/**
|
||||
* Security test cases
|
||||
*/
|
||||
class SecurityTest extends TestCase
|
||||
{
|
||||
private Security $security;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->security = new Security();
|
||||
}
|
||||
|
||||
public function testCanGenerateCsrfToken(): void
|
||||
{
|
||||
$token = $this->security->generateCsrfToken();
|
||||
|
||||
$this->assertIsString($token);
|
||||
$this->assertEquals(64, strlen($token)); // 32 bytes = 64 hex chars
|
||||
}
|
||||
|
||||
public function testCanVerifyCsrfToken(): void
|
||||
{
|
||||
$token = $this->security->generateCsrfToken();
|
||||
|
||||
$this->assertTrue($this->security->verifyCsrfToken($token));
|
||||
$this->assertFalse($this->security->verifyCsrfToken('invalid-token'));
|
||||
}
|
||||
|
||||
public function testCanSanitizeString(): void
|
||||
{
|
||||
$input = '<script>alert("xss")</script>Hello World';
|
||||
$sanitized = $this->security->sanitizeString($input);
|
||||
|
||||
$this->assertStringNotContainsString('<script>', $sanitized);
|
||||
$this->assertStringContainsString('Hello World', $sanitized);
|
||||
}
|
||||
|
||||
public function testCanEncryptAndDecryptData(): void
|
||||
{
|
||||
$data = 'Sensitive information';
|
||||
|
||||
$encrypted = $this->security->encrypt($data);
|
||||
$decrypted = $this->security->decrypt($encrypted);
|
||||
|
||||
$this->assertNotEquals($data, $encrypted);
|
||||
$this->assertEquals($data, $decrypted);
|
||||
}
|
||||
|
||||
public function testCanHashPassword(): void
|
||||
{
|
||||
$password = 'test-password';
|
||||
$hash = $this->security->hashPassword($password);
|
||||
|
||||
$this->assertIsString($hash);
|
||||
$this->assertNotEquals($password, $hash);
|
||||
$this->assertTrue($this->security->verifyPassword($password, $hash));
|
||||
}
|
||||
|
||||
public function testCanGenerateRandomString(): void
|
||||
{
|
||||
$random = $this->security->generateRandomString(16);
|
||||
|
||||
$this->assertIsString($random);
|
||||
$this->assertEquals(32, strlen($random)); // 16 bytes = 32 hex chars
|
||||
}
|
||||
|
||||
public function testPasswordVerificationWorks(): void
|
||||
{
|
||||
$password = 'test-password';
|
||||
$hash = $this->security->hashPassword($password);
|
||||
|
||||
$this->assertTrue($this->security->verifyPassword($password, $hash));
|
||||
$this->assertFalse($this->security->verifyPassword('wrong-password', $hash));
|
||||
}
|
||||
}
|
||||
65
tests/TestCase.php
Normal file
65
tests/TestCase.php
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace Tests;
|
||||
|
||||
use PHPUnit\Framework\TestCase as BaseTestCase;
|
||||
use App\Core\Bootstrap;
|
||||
use App\Core\Container;
|
||||
|
||||
/**
|
||||
* Base test case for NovaCore Framework
|
||||
*/
|
||||
abstract class TestCase extends BaseTestCase
|
||||
{
|
||||
protected ?Container $container = null;
|
||||
protected ?Bootstrap $bootstrap = null;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
// Set up test environment
|
||||
putenv('APP_ENV=testing');
|
||||
putenv('APP_DEBUG=true');
|
||||
|
||||
// Initialize container and bootstrap
|
||||
$this->container = new Container();
|
||||
$this->bootstrap = new Bootstrap();
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
parent::tearDown();
|
||||
|
||||
// Clean up
|
||||
$this->container = null;
|
||||
$this->bootstrap = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a test request
|
||||
*/
|
||||
protected function createRequest(array $data = [], string $method = 'GET'): void
|
||||
{
|
||||
$_SERVER['REQUEST_METHOD'] = $method;
|
||||
$_GET = $data;
|
||||
$_POST = $method === 'POST' ? $data : [];
|
||||
$_REQUEST = array_merge($_GET, $_POST);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that response contains text
|
||||
*/
|
||||
protected function assertResponseContains(string $content, string $text): void
|
||||
{
|
||||
$this->assertStringContainsString($text, $content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that response is JSON
|
||||
*/
|
||||
protected function assertJsonResponse(string $content): void
|
||||
{
|
||||
$this->assertJson($content);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user