tests and linting
Some checks failed
🧪✨ Tests Workflow / 🛡️ 🔒 Library Audit (push) Successful in -7s
🧪✨ Tests Workflow / 📝 ✨ Code Lint (push) Successful in -16s
🧪✨ Tests Workflow / 🧪 ✨ Database Migrations (push) Failing after -3s
🧪✨ Tests Workflow / 🛡️ 🔒 License Check (push) Successful in -4s
🧪✨ Tests Workflow / 🐙 🔍 Code Sniffer (push) Successful in 6s
🧪✨ Tests Workflow / 🧪 ✅ Unit Tests (push) Successful in -41s

This commit is contained in:
2026-01-29 19:28:57 -05:00
parent e9cb49d942
commit 4ee830327e
20 changed files with 112 additions and 86 deletions

View File

@@ -10,5 +10,6 @@ return new LicenseConfigurationBuilder()
'BSD-2-Clause', 'BSD-2-Clause',
'BSD-3-Clause', 'BSD-3-Clause',
'Apache-2.0', 'Apache-2.0',
'LGPL-2.1-only'
) )
->build(); ->build();

View File

@@ -1,6 +1,16 @@
{ {
"name": "siteworxpro/app", "name": "siteworxpro/app",
"type": "project", "type": "project",
"config": {
"optimize-autoloader": true,
"sort-packages": true,
"audit": {
"ignore": {
"PKSA-rkkf-636k-qjb3": "Dev only package",
"PKSA-z3gr-8qht-p93v": "Dev only package"
}
}
},
"autoload": { "autoload": {
"psr-4": { "psr-4": {
"Siteworxpro\\App\\": "src/", "Siteworxpro\\App\\": "src/",
@@ -37,12 +47,13 @@
"phpmailer/phpmailer": "^7.0" "phpmailer/phpmailer": "^7.0"
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "^12.4", "jetbrains/phpstorm-attributes": "^1.2",
"mockery/mockery": "^1.6", "kwn/php-rdkafka-stubs": "^2.2",
"squizlabs/php_codesniffer": "^4.0",
"lendable/composer-license-checker": "^1.3.0", "lendable/composer-license-checker": "^1.3.0",
"mockery/mockery": "^1.6",
"phpstan/phpstan": "^2.1.31", "phpstan/phpstan": "^2.1.31",
"kwn/php-rdkafka-stubs": "^2.2" "phpunit/phpunit": "^12.4",
"squizlabs/php_codesniffer": "^4.0"
}, },
"scripts": { "scripts": {
"tests:all": [ "tests:all": [

44
composer.lock generated
View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "4c2ae64539e4f6a700912cdc7d863493", "content-hash": "3063e521174c3d0c51a4b8143aee7e85",
"packages": [ "packages": [
{ {
"name": "brick/math", "name": "brick/math",
@@ -5739,6 +5739,48 @@
}, },
"time": "2025-04-30T06:54:44+00:00" "time": "2025-04-30T06:54:44+00:00"
}, },
{
"name": "jetbrains/phpstorm-attributes",
"version": "1.2",
"source": {
"type": "git",
"url": "https://github.com/JetBrains/phpstorm-attributes.git",
"reference": "64de815a4509c29e00d5e3474087fd24c171afc2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/JetBrains/phpstorm-attributes/zipball/64de815a4509c29e00d5e3474087fd24c171afc2",
"reference": "64de815a4509c29e00d5e3474087fd24c171afc2",
"shasum": ""
},
"type": "library",
"autoload": {
"psr-4": {
"JetBrains\\PhpStorm\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Apache-2.0"
],
"authors": [
{
"name": "JetBrains",
"homepage": "https://www.jetbrains.com"
}
],
"description": "PhpStorm specific attributes",
"keywords": [
"attributes",
"jetbrains",
"phpstorm"
],
"support": {
"issues": "https://youtrack.jetbrains.com/newIssue?project=WI",
"source": "https://github.com/JetBrains/phpstorm-attributes/tree/1.2"
},
"time": "2024-10-11T10:46:19+00:00"
},
{ {
"name": "kwn/php-rdkafka-stubs", "name": "kwn/php-rdkafka-stubs",
"version": "v2.2.1", "version": "v2.2.1",

View File

@@ -28,11 +28,11 @@ abstract class Command extends \Symfony\Component\Console\Command\Command implem
} }
/** /**
* @param ClimateOutput|OutputInterface $output * @param ClimateOutput $output
* @param ArgvInput|InputInterface $input * @param ClimateOutput|ArgvInput $input
* @return Client | null * @return Client | null
*/ */
protected function askForClient(ClimateOutput|OutputInterface $output, ArgvInput|InputInterface $input): ?Client protected function askForClient(ClimateOutput $output, ClimateOutput|ArgvInput $input): ?Client
{ {
/** @var Collection<Client> $clients */ /** @var Collection<Client> $clients */
$clients = Client::whereJsonContains('grant_types', 'authorization_code') $clients = Client::whereJsonContains('grant_types', 'authorization_code')
@@ -60,8 +60,8 @@ abstract class Command extends \Symfony\Component\Console\Command\Command implem
protected function askForUser( protected function askForUser(
Client $client, Client $client,
ClimateOutput|OutputInterface $output, ClimateOutput $output,
ArgvInput|InputInterface $input ClimateOutput|ArgvInput $input
): ?User { ): ?User {
$users = $client->users; $users = $client->users;

View File

@@ -14,7 +14,12 @@ interface CommandInterface
/** /**
* Execute the command. * Execute the command.
* *
* @param ArgvInput|ClimateOutput $input
* @param ClimateOutput $output
* @return int * @return int
*/ */
public function __invoke(InputInterface | ArgvInput $input, OutputInterface | ClimateOutput $output): int; public function __invoke(
ClimateOutput|ArgvInput $input,
ClimateOutput $output
): int;
} }

View File

@@ -9,6 +9,7 @@ use Siteworxpro\App\Cli\ClimateOutput;
use Siteworxpro\App\Cli\Commands\Command; use Siteworxpro\App\Cli\Commands\Command;
use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\ArgvInput; use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Input\InputInterface;
#[AsCommand('crypt:generate-key', 'Generate a new encryption key for the application')] #[AsCommand('crypt:generate-key', 'Generate a new encryption key for the application')]
class GenerateKey extends Command class GenerateKey extends Command

View File

@@ -11,6 +11,7 @@ use Siteworxpro\App\OAuth\Entities\Client;
use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\ArgvInput; use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Question\Question; use Symfony\Component\Console\Question\Question;
#[AsCommand(name: 'oauth:redirect-uri:create', description: 'Add a redirect URI to an existing OAuth client.')] #[AsCommand(name: 'oauth:redirect-uri:create', description: 'Add a redirect URI to an existing OAuth client.')]

View File

@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Siteworxpro\App\Cli\Commands\OAuth; namespace Siteworxpro\App\Cli\Commands\OAuth;
use Siteworxpro\App\Cli\ClimateOutput;
use Siteworxpro\App\Cli\Commands\Command; use Siteworxpro\App\Cli\Commands\Command;
use Siteworxpro\App\OAuth\Entities\Scope; use Siteworxpro\App\OAuth\Entities\Scope;
use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Attribute\AsCommand;
@@ -22,7 +23,7 @@ class AddScope extends Command
->addArgument('description', InputArgument::OPTIONAL, 'The description of the scope'); ->addArgument('description', InputArgument::OPTIONAL, 'The description of the scope');
} }
public function __invoke(ArgvInput | InputInterface $input, $output): int public function __invoke($input, $output): int
{ {
$name = $input->getArgument('name'); $name = $input->getArgument('name');
$description = $input->getArgument('description') ?? ''; $description = $input->getArgument('description') ?? '';

View File

@@ -7,15 +7,13 @@ namespace Siteworxpro\App\Cli\Commands\OAuth;
use Siteworxpro\App\Cli\ClimateOutput; use Siteworxpro\App\Cli\ClimateOutput;
use Siteworxpro\App\Cli\Commands\Command; use Siteworxpro\App\Cli\Commands\Command;
use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question; use Symfony\Component\Console\Question\Question;
#[AsCommand(name: 'oauth:client:edit', description: 'Edit an OAuth client.')] #[AsCommand(name: 'oauth:client:edit', description: 'Edit an OAuth client.')]
class ClientCapabilities extends Command class ClientCapabilities extends Command
{ {
public function __invoke(ArgvInput|InputInterface $input, ClimateOutput|OutputInterface $output): int public function __invoke($input, ClimateOutput|OutputInterface $output): int
{ {
$client = $this->askForClient($output, $input); $client = $this->askForClient($output, $input);

View File

@@ -22,11 +22,11 @@ class ListClients extends Command
} }
/** /**
* @param ArgvInput|InputInterface $input * @param ArgvInput|ClimateOutput|InputInterface $input
* @param ClimateOutput $output * @param ClimateOutput $output
* @return int * @return int
*/ */
public function __invoke(ArgvInput|InputInterface $input, $output): int public function __invoke(ClimateOutput|ArgvInput|InputInterface $input, $output): int
{ {
if ($input->getArgument('client-id')) { if ($input->getArgument('client-id')) {
$client = Client::find($input->getArgument('client-id')); $client = Client::find($input->getArgument('client-id'));

View File

@@ -5,10 +5,13 @@ declare(strict_types=1);
namespace Siteworxpro\App\Cli\Commands\Queue; namespace Siteworxpro\App\Cli\Commands\Queue;
use Siteworxpro\App\Async\Consumer; use Siteworxpro\App\Async\Consumer;
use Siteworxpro\App\Cli\ClimateOutput;
use Siteworxpro\App\Cli\Commands\Command; use Siteworxpro\App\Cli\Commands\Command;
use Siteworxpro\App\Cli\Commands\CommandInterface; use Siteworxpro\App\Cli\Commands\CommandInterface;
use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
#[AsCommand('queue:start', 'Start the queue consumer')] #[AsCommand('queue:start', 'Start the queue consumer')]
class Start extends Command implements CommandInterface class Start extends Command implements CommandInterface
@@ -23,7 +26,7 @@ class Start extends Command implements CommandInterface
); );
} }
public function __invoke($input, $output): int public function __invoke(ClimateOutput|ArgvInput|InputInterface $input, $output): int
{ {
$queues = $input->getArgument('queues'); $queues = $input->getArgument('queues');

View File

@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Siteworxpro\App\Cli\Commands\User; namespace Siteworxpro\App\Cli\Commands\User;
use Siteworxpro\App\Cli\ClimateOutput;
use Siteworxpro\App\Cli\Commands\Command; use Siteworxpro\App\Cli\Commands\Command;
use Siteworxpro\App\CommandBus\Commands\CreateUser; use Siteworxpro\App\CommandBus\Commands\CreateUser;
use Siteworxpro\App\Models\ClientUser; use Siteworxpro\App\Models\ClientUser;
@@ -12,12 +13,14 @@ use Siteworxpro\App\Services\Facades\CommandBus;
use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command as SCommand; use Symfony\Component\Console\Command\Command as SCommand;
use Symfony\Component\Console\Helper\QuestionHelper; use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Question\Question as QuestionInput; use Symfony\Component\Console\Question\Question as QuestionInput;
#[AsCommand('user:add', 'Add a new user associated with an OAuth client')] #[AsCommand('user:add', 'Add a new user associated with an OAuth client')]
class Add extends Command class Add extends Command
{ {
public function __invoke($input, $output): int public function __invoke(ClimateOutput|ArgvInput|InputInterface $input, ClimateOutput $output): int
{ {
$client = $this->askForClient($output, $input); $client = $this->askForClient($output, $input);
if (!$client) { if (!$client) {

View File

@@ -11,9 +11,7 @@ use Siteworxpro\App\Services\Facades\CommandBus;
use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Helper\QuestionHelper; use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Input\ArgvInput; use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question as QuestionInput; use Symfony\Component\Console\Question\Question as QuestionInput;
#[AsCommand('user:reset-password', 'Reset a user\'s password')] #[AsCommand('user:reset-password', 'Reset a user\'s password')]
@@ -29,7 +27,12 @@ class ResetPassword extends Command
); );
} }
public function __invoke(ArgvInput|InputInterface $input, ClimateOutput|OutputInterface $output): int /**
* @param ArgvInput|ClimateOutput $input
* @param ClimateOutput $output
* @return int
*/
public function __invoke(ClimateOutput|ArgvInput $input, ClimateOutput $output): int
{ {
$client = $this->askForClient($output, $input); $client = $this->askForClient($output, $input);

View File

@@ -8,7 +8,7 @@ readonly class CreateClient extends Command
{ {
/** /**
* @param string $clientName * @param string $clientName
* @param array<ClientGrant> $clientGrants * @param array<ClientGrant | mixed> $clientGrants
* @param string $clientDescription * @param string $clientDescription
*/ */
public function __construct( public function __construct(

View File

@@ -14,7 +14,7 @@ class LoginFailed extends Listener
/** /**
* Handle the event. * Handle the event.
* *
* @param \Siteworxpro\App\Events\Login\LoginFailed $event * @param string | \Siteworxpro\App\Events\Login\LoginFailed $event
* @param array $payload * @param array $payload
* @return AuditLog|null * @return AuditLog|null
*/ */

View File

@@ -193,7 +193,7 @@ class Dispatcher implements DispatcherContract, Arrayable
/** /**
* Push an event to be dispatched later. * Push an event to be dispatched later.
* *
* @param $event * @param mixed $event
* @param array $payload * @param array $payload
* @return void * @return void
*/ */

View File

@@ -13,15 +13,19 @@ class Log implements DriverInterface
public function send(Message $message): bool public function send(Message $message): bool
{ {
$logMessage = <<<'MSG' $logMessage = sprintf(
=============================================== "=================================" . PHP_EOL .
From: {$this->fromAddress} "Email sent via Log Driver" . PHP_EOL .
To: {$message->getTo()} "From: %s" . PHP_EOL .
Subject: {$message->getSubject()} "To: %s" . PHP_EOL .
Body: "Subject: %s" . PHP_EOL .
{$this->formatBodyForLog($message->getBody())} "Body: " . PHP_EOL . "%s" . PHP_EOL .
=============================================== "=================================",
MSG; $this->fromAddress,
$message->getTo(),
$message->getSubject(),
$this->formatBodyForLog($message->getBody())
);
Logger::info($logMessage); Logger::info($logMessage);

View File

@@ -13,12 +13,9 @@ class Smtp implements DriverInterface
{ {
private PHPMailer $mailer; private PHPMailer $mailer;
/**
* @throws Exception
*/
public function __construct() public function __construct()
{ {
$config = Config::get('mailer.smtp', []); $config = Config::get('mailer.smtp');
if (empty($config['host']) || empty($config['port'])) { if (empty($config['host']) || empty($config['port'])) {
throw new \InvalidArgumentException( throw new \InvalidArgumentException(
@@ -46,6 +43,9 @@ class Smtp implements DriverInterface
return true; return true;
} }
/**
* @throws Exception
*/
public function setFrom(string $address, string $name): void public function setFrom(string $address, string $name): void
{ {
$this->mailer->setFrom($address, $name, false); $this->mailer->setFrom($address, $name, false);

View File

@@ -38,7 +38,7 @@ use Siteworxpro\App\OAuth\ScopeRepository;
* @property Collection $grant_types * @property Collection $grant_types
* @property bool $confidential * @property bool $confidential
* *
* @property ClientCapabilities $capabilities * @property ClientCapabilities | array $capabilities
* @property-read Collection<ClientRedirectUri> $clientRedirectUris * @property-read Collection<ClientRedirectUri> $clientRedirectUris
* @property-read Scope[]|Collection $scopes * @property-read Scope[]|Collection $scopes
* @property-read Collection<User> $users * @property-read Collection<User> $users

View File

@@ -1,47 +0,0 @@
<?php
declare(strict_types=1);
namespace Siteworxpro\Tests\Events\Listeners;
use Illuminate\Database\Events\ConnectionEstablished;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Psr\Log\LogLevel;
use Siteworxpro\App\Events\EventListeners\Database\Connected;
use Siteworxpro\App\Log\Logger;
use Siteworxpro\Tests\Unit;
class ConnectedTest extends Unit
{
/**
* @throws ContainerExceptionInterface
* @throws \ReflectionException
* @throws NotFoundExceptionInterface
*/
protected function setUp(): void
{
parent::setUp();
$inputBuffer = fopen('php://memory', 'r+');
$logger = new Logger(LogLevel::DEBUG, $inputBuffer);
\Siteworxpro\App\Services\Facades\Logger::getFacadeContainer()->bind(Logger::class, fn() => $logger);
}
public function testHandlesEvent()
{
$this->expectNotToPerformAssertions();
$connectedEvent = $this->createMock(ConnectionEstablished::class);
$listener = new Connected();
$listener->__invoke($connectedEvent);
}
public function testThrowsException()
{
$this->expectException(\TypeError::class);
$listener = new Connected();
$listener->__invoke(new \stdClass());
}
}