You've already forked Php-Template
initial
This commit is contained in:
13
.gitlab-ci.yml
Normal file
13
.gitlab-ci.yml
Normal file
@@ -0,0 +1,13 @@
|
||||
stages:
|
||||
- test
|
||||
|
||||
Unit Tests:
|
||||
rules:
|
||||
- if: '$CI_PIPELINE_SOURCE == "push"'
|
||||
when: always
|
||||
- when: never
|
||||
stage: test
|
||||
image: siteworxpro/composer
|
||||
script:
|
||||
- echo "Running unit tests..."
|
||||
- composer run tests:unit
|
||||
@@ -3,7 +3,8 @@
|
||||
"type": "project",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Siteworxpro\\App\\": "src/"
|
||||
"Siteworxpro\\App\\": "src/",
|
||||
"Siteworxpro\\Tests\\": "tests/"
|
||||
}
|
||||
},
|
||||
"require": {
|
||||
@@ -13,6 +14,28 @@
|
||||
"spiral/roadrunner-http": "^3.5",
|
||||
"nyholm/psr7": "^1.8",
|
||||
"illuminate/support": "^v12.10.2",
|
||||
"roadrunner-php/app-logger": "^1.2"
|
||||
"roadrunner-php/app-logger": "^1.2",
|
||||
"siteworxpro/config": "^1.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^12.1",
|
||||
"mockery/mockery": "^1.6"
|
||||
},
|
||||
"scripts": {
|
||||
"tests:unit": [
|
||||
"phpunit --colors=always --display-deprecations tests "
|
||||
]
|
||||
},
|
||||
"repositories": {
|
||||
"git.siteworxpro.com/24": {
|
||||
"type": "composer",
|
||||
"url": "https://git.siteworxpro.com/api/v4/group/24/-/packages/composer/packages.json",
|
||||
"options": {
|
||||
"ssl": {
|
||||
"verify_peer": false,
|
||||
"allow_self_signed": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1785
composer.lock
generated
1785
composer.lock
generated
File diff suppressed because it is too large
Load Diff
24
config.php
Normal file
24
config.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
use Siteworxpro\App\Helpers\Env;
|
||||
|
||||
return [
|
||||
|
||||
/**
|
||||
* The server configuration.
|
||||
*/
|
||||
'server' => [
|
||||
'port' => Env::get('HTTP_PORT', 9501),
|
||||
],
|
||||
|
||||
/**
|
||||
* The database configuration.
|
||||
*/
|
||||
'db' => [
|
||||
'driver' => Env::get('DB_DRIVER', 'pgsql'),
|
||||
'host' => Env::get('DB_HOST', 'localhost'),
|
||||
'database' => Env::get('DB_DATABASE', 'siteworxpro'),
|
||||
'username' => Env::get('DB_USERNAME', 'siteworxpro'),
|
||||
'password' => Env::get('DB_PASSWORD', 'password'),
|
||||
]
|
||||
];
|
||||
@@ -5,6 +5,9 @@ declare(strict_types=1);
|
||||
namespace Siteworxpro\App\Facades;
|
||||
|
||||
use Illuminate\Support\Facades\Facade;
|
||||
use Siteworx\Config\Exception\EmptyDirectoryException;
|
||||
use Siteworx\Config\Exception\FileNotFoundException;
|
||||
use Siteworx\Config\Exception\UnsupportedFormatException;
|
||||
|
||||
/**
|
||||
* Class Config
|
||||
@@ -12,24 +15,20 @@ use Illuminate\Support\Facades\Facade;
|
||||
* This class serves as a facade for the configuration settings of the application.
|
||||
* It extends the Facade class from the Illuminate\Support\Facades namespace.
|
||||
*
|
||||
* @method static string|int|bool|float get(string $key, string $castTo = 'string') Retrieve the configuration value for the given key.
|
||||
* @method static string|int|bool|float get(string $key) Retrieve the configuration value for the given key.
|
||||
*
|
||||
* @package Siteworx\App\Facades
|
||||
*/
|
||||
class Config extends Facade
|
||||
{
|
||||
public const string HTTP_PORT = 'HTTP_PORT';
|
||||
public const string LOG_LEVEL = 'LOG_LEVEL';
|
||||
public const string DB_DRIVER = 'DB_DRIVER';
|
||||
public const string DB_HOST = 'DB_HOST';
|
||||
public const string DB_DATABASE = 'DB_DATABASE';
|
||||
public const string DB_USER = 'DB_USER';
|
||||
public const string DB_PASSWORD = 'DB_PASSWORD';
|
||||
public const string DEV_MODE = 'DEV_MODE';
|
||||
|
||||
public static function getFacadeRoot(): \Siteworxpro\App\Helpers\Config
|
||||
/**
|
||||
* @throws UnsupportedFormatException
|
||||
* @throws FileNotFoundException
|
||||
* @throws EmptyDirectoryException
|
||||
*/
|
||||
public static function getFacadeRoot(): \Siteworx\Config\Config
|
||||
{
|
||||
return new \Siteworxpro\App\Helpers\Config();
|
||||
return \Siteworx\Config\Config::load(__DIR__ . '/../../config.php');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -39,6 +38,6 @@ class Config extends Facade
|
||||
*/
|
||||
protected static function getFacadeAccessor(): string
|
||||
{
|
||||
return \Siteworxpro\App\Helpers\Config::class;
|
||||
return 'config';
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Siteworxpro\App\Helpers;
|
||||
|
||||
use Psr\Log\LogLevel;
|
||||
use Siteworxpro\App\Facades\Config as FacadeConfig;
|
||||
|
||||
class Config
|
||||
{
|
||||
private const array DEFAULTS = [
|
||||
FacadeConfig::DB_DRIVER => 'pgsql',
|
||||
FacadeConfig::DB_HOST => 'localhost',
|
||||
FacadeConfig::DB_DATABASE => 'siteworx',
|
||||
FacadeConfig::DB_USER => 'siteworx',
|
||||
FacadeConfig::DB_PASSWORD => 'password',
|
||||
FacadeConfig::LOG_LEVEL => LogLevel::DEBUG,
|
||||
FacadeConfig::HTTP_PORT => '9501',
|
||||
FacadeConfig::DEV_MODE => true,
|
||||
];
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* @param string $castTo
|
||||
* @return string|int|bool|float
|
||||
*/
|
||||
public function get(string $key, string $castTo = 'string'): string|int|bool|float
|
||||
{
|
||||
return Env::get($key, self::DEFAULTS[$key] ?? null, $castTo);
|
||||
}
|
||||
}
|
||||
@@ -84,11 +84,11 @@ class Server
|
||||
{
|
||||
$capsule = new \Illuminate\Database\Capsule\Manager();
|
||||
$capsule->addConnection([
|
||||
'driver' => Config::get(Config::DB_DRIVER),
|
||||
'host' => Config::get(Config::DB_HOST),
|
||||
'database' => Config::get(Config::DB_DATABASE),
|
||||
'username' => Config::get(Config::DB_USER),
|
||||
'password' => Config::get(Config::DB_PASSWORD),
|
||||
'driver' => Config::get('db.driver'),
|
||||
'host' => Config::get('db.host'),
|
||||
'database' => Config::get('db.database'),
|
||||
'username' => Config::get('db.username'),
|
||||
'password' => Config::get('db.password'),
|
||||
'charset' => 'utf8',
|
||||
'collation' => 'utf8_unicode_ci',
|
||||
'prefix' => '',
|
||||
@@ -128,7 +128,7 @@ class Server
|
||||
{
|
||||
Logger::info(sprintf('Server started: %s', microtime(true)));
|
||||
Logger::info(sprintf('Server PID: %s', getmypid()));
|
||||
Logger::info(sprintf('Server Listening on: %s:%s', Config::get('HTTP_HOST'), Config::get('HTTP_PORT')));
|
||||
Logger::info(sprintf('Server Listening on: 0.0.0.0:%s', Config::get('server.port')));
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
|
||||
46
tests/Helpers/EnvTest.php
Normal file
46
tests/Helpers/EnvTest.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Siteworxpro\Tests\Helpers;
|
||||
|
||||
use Siteworxpro\App\Helpers\Env;
|
||||
use Siteworxpro\Tests\Unit;
|
||||
|
||||
class EnvTest extends Unit
|
||||
{
|
||||
public function testGetReturnsStringByDefault(): void
|
||||
{
|
||||
putenv('TEST_KEY=example');
|
||||
$result = Env::get('TEST_KEY');
|
||||
$this->assertSame('example', $result);
|
||||
}
|
||||
|
||||
public function testGetReturnsDefaultIfKeyNotSet(): void
|
||||
{
|
||||
putenv('TEST_KEY'); // Unset the environment variable
|
||||
$result = Env::get('TEST_KEY', 'default_value');
|
||||
$this->assertSame('default_value', $result);
|
||||
}
|
||||
|
||||
public function testGetCastsToBoolean(): void
|
||||
{
|
||||
putenv('TEST_KEY=true');
|
||||
$result = Env::get('TEST_KEY', null, 'bool');
|
||||
$this->assertTrue($result);
|
||||
}
|
||||
|
||||
public function testGetCastsToInteger(): void
|
||||
{
|
||||
putenv('TEST_KEY=123');
|
||||
$result = Env::get('TEST_KEY', null, 'int');
|
||||
$this->assertSame(123, $result);
|
||||
}
|
||||
|
||||
public function testGetCastsToFloat(): void
|
||||
{
|
||||
putenv('TEST_KEY=123.45');
|
||||
$result = Env::get('TEST_KEY', null, 'float');
|
||||
$this->assertSame(123.45, $result);
|
||||
}
|
||||
}
|
||||
43
tests/Http/JsonResponseFactoryTest.php
Normal file
43
tests/Http/JsonResponseFactoryTest.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Siteworxpro\Tests\Http;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Siteworxpro\App\Http\JsonResponseFactory;
|
||||
|
||||
class JsonResponseFactoryTest extends TestCase
|
||||
{
|
||||
public function testCreateJsonResponseReturnsValidResponse(): void
|
||||
{
|
||||
$data = ['key' => 'value'];
|
||||
$statusCode = 200;
|
||||
|
||||
$response = JsonResponseFactory::createJsonResponse($data, $statusCode);
|
||||
|
||||
$this->assertSame($statusCode, $response->getStatusCode());
|
||||
$this->assertSame('application/json', $response->getHeaderLine('Content-Type'));
|
||||
$this->assertSame(json_encode($data), (string) $response->getBody());
|
||||
}
|
||||
|
||||
public function testCreateJsonResponseHandlesEmptyData(): void
|
||||
{
|
||||
$data = [];
|
||||
$statusCode = 204;
|
||||
|
||||
$response = JsonResponseFactory::createJsonResponse($data, $statusCode);
|
||||
|
||||
$this->assertSame($statusCode, $response->getStatusCode());
|
||||
$this->assertSame('application/json', $response->getHeaderLine('Content-Type'));
|
||||
$this->assertSame(json_encode($data), (string) $response->getBody());
|
||||
}
|
||||
|
||||
public function testCreateJsonResponseThrowsExceptionOnInvalidData(): void
|
||||
{
|
||||
$this->expectException(\JsonException::class);
|
||||
|
||||
$data = ["invalid" => "\xB1\x31"];
|
||||
JsonResponseFactory::createJsonResponse($data);
|
||||
}
|
||||
}
|
||||
92
tests/Http/Middleware/CorsMiddlewareTest.php
Normal file
92
tests/Http/Middleware/CorsMiddlewareTest.php
Normal file
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Siteworxpro\Tests\Http\Middleware;
|
||||
|
||||
use Nyholm\Psr7\Response;
|
||||
use Nyholm\Psr7\ServerRequest;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
use Siteworxpro\App\Facades\Config;
|
||||
use Siteworxpro\App\Http\Middleware\CorsMiddleware;
|
||||
use Siteworxpro\Tests\Unit;
|
||||
|
||||
class CorsMiddlewareTest extends Unit
|
||||
{
|
||||
public function testAllowsConfiguredOrigin(): void
|
||||
{
|
||||
Config::shouldReceive('get')
|
||||
->with('CORS_ALLOWED_ORIGINS', 'https://example.com,https://another.com')
|
||||
->andReturn('https://example.com,https://another.com');
|
||||
|
||||
$middleware = new CorsMiddleware();
|
||||
$request = new ServerRequest('GET', '/')->withHeader('Origin', 'https://example.com');
|
||||
$handler = $this->mockHandler(new Response(200));
|
||||
|
||||
$response = $middleware->process($request, $handler);
|
||||
|
||||
$this->assertEquals('https://example.com', $response->getHeaderLine('Access-Control-Allow-Origin'));
|
||||
}
|
||||
|
||||
public function testBlocksUnconfiguredOrigin(): void
|
||||
{
|
||||
Config::shouldReceive('get')
|
||||
->with('CORS_ALLOWED_ORIGINS', 'https://example.com,https://another.com')
|
||||
->andReturn('https://example.com,https://another.com');
|
||||
|
||||
$middleware = new CorsMiddleware();
|
||||
$request = new ServerRequest('GET', '/')->withHeader('Origin', 'https://unauthorized.com');
|
||||
$handler = $this->mockHandler(new Response(200));
|
||||
|
||||
$response = $middleware->process($request, $handler);
|
||||
|
||||
$this->assertEquals('null', $response->getHeaderLine('Access-Control-Allow-Origin'));
|
||||
}
|
||||
|
||||
public function testHandlesOptionsRequest(): void
|
||||
{
|
||||
Config::shouldReceive('get')->with('CORS_ALLOWED_ORIGINS', '...')->andReturn('https://example.com');
|
||||
Config::shouldReceive('get')->with('CORS_ALLOW_CREDENTIALS', 'bool')->andReturn(false);
|
||||
Config::shouldReceive('get')->with('CORS_MAX_AGE')->andReturn('86400');
|
||||
|
||||
$middleware = new CorsMiddleware();
|
||||
$request = new ServerRequest('OPTIONS', '/')->withHeader('Origin', 'https://example.com');
|
||||
$handler = $this->mockHandler(new Response(200));
|
||||
|
||||
$response = $middleware->process($request, $handler);
|
||||
|
||||
$this->assertEquals(204, $response->getStatusCode());
|
||||
$this->assertEquals('86400', $response->getHeaderLine('Access-Control-Max-Age'));
|
||||
}
|
||||
|
||||
public function testAddsAllowCredentialsHeader(): void
|
||||
{
|
||||
Config::shouldReceive('get')->with('CORS_ALLOWED_ORIGINS', '...')->andReturn('https://example.com');
|
||||
Config::shouldReceive('get')->with('CORS_ALLOW_CREDENTIALS', 'bool')->andReturn(true);
|
||||
|
||||
$middleware = new CorsMiddleware();
|
||||
$request = new ServerRequest('GET', '/')->withHeader('Origin', 'https://example.com');
|
||||
$handler = $this->mockHandler(new Response(200));
|
||||
|
||||
$response = $middleware->process($request, $handler);
|
||||
|
||||
$this->assertEquals('true', $response->getHeaderLine('Access-Control-Allow-Credentials'));
|
||||
}
|
||||
|
||||
private function mockHandler(Response $response): RequestHandlerInterface
|
||||
{
|
||||
return new class($response) implements RequestHandlerInterface {
|
||||
private Response $response;
|
||||
|
||||
public function __construct(Response $response)
|
||||
{
|
||||
$this->response = $response;
|
||||
}
|
||||
|
||||
public function handle(\Psr\Http\Message\ServerRequestInterface $request): \Psr\Http\Message\ResponseInterface
|
||||
{
|
||||
return $this->response;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
12
tests/Unit.php
Normal file
12
tests/Unit.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Siteworxpro\Tests;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
abstract class Unit extends TestCase
|
||||
{
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user