From 4792f85c03bf76990554a14a2c3b9986cb4ce0ff Mon Sep 17 00:00:00 2001 From: Arman <407448+armanist@users.noreply.github.com> Date: Wed, 28 Oct 2026 20:43:13 +0400 Subject: [PATCH 01/10] [#622] Make the data providers static Co-Authored-By: Claude Sonnet 5.5 --- .../Console/Stages/Boot/ResolveEnvironmentStageTest.php | 4 ++-- tests/Unit/HttpClient/ResponseHeadersTest.php | 2 +- tests/Unit/Logger/LoggerTest.php | 2 +- tests/Unit/Mailer/MailerTest.php | 2 +- tests/Unit/Module/Templates/OpenApiTemplatesTest.php | 2 +- tests/Unit/Module/Templates/RedirectTemplatesTest.php | 2 +- tests/Unit/Session/Adapters/Database/DatabaseHandlerTest.php | 2 +- tests/Unit/Tracer/ErrorHandlerTest.php | 4 ++-- tests/Unit/Tracer/ExceptionSeverityResolverTest.php | 2 +- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/Unit/App/Adapters/Console/Stages/Boot/ResolveEnvironmentStageTest.php b/tests/Unit/App/Adapters/Console/Stages/Boot/ResolveEnvironmentStageTest.php index 8c7216e0e..63c04809a 100644 --- a/tests/Unit/App/Adapters/Console/Stages/Boot/ResolveEnvironmentStageTest.php +++ b/tests/Unit/App/Adapters/Console/Stages/Boot/ResolveEnvironmentStageTest.php @@ -89,7 +89,7 @@ public function testResolveEnvironmentStageThrowsTheParseExceptionWhenTheEnvFile /** * @return array */ - public function commandsThatRunWithoutTheEnvironment(): array + public static function commandsThatRunWithoutTheEnvironment(): array { return [ 'a CliCommand that does not require the environment' => ['no:env'], @@ -101,7 +101,7 @@ public function commandsThatRunWithoutTheEnvironment(): array /** * @return array}> */ - public function inputsThatNeedTheEnvironment(): array + public static function inputsThatNeedTheEnvironment(): array { return [ 'a CliCommand that requires the environment' => [['command' => 'needs:env']], diff --git a/tests/Unit/HttpClient/ResponseHeadersTest.php b/tests/Unit/HttpClient/ResponseHeadersTest.php index 6a8766862..c44510cd2 100644 --- a/tests/Unit/HttpClient/ResponseHeadersTest.php +++ b/tests/Unit/HttpClient/ResponseHeadersTest.php @@ -46,7 +46,7 @@ public function testResponseHeadersDeclaresTheReturnTypeOfItsInterfaceMethods(st /** * @return array */ - public function interfaceMethodProvider(): array + public static function interfaceMethodProvider(): array { return [ 'offsetSet' => ['offsetSet'], diff --git a/tests/Unit/Logger/LoggerTest.php b/tests/Unit/Logger/LoggerTest.php index aa40beab8..281f89325 100644 --- a/tests/Unit/Logger/LoggerTest.php +++ b/tests/Unit/Logger/LoggerTest.php @@ -70,7 +70,7 @@ public function __toString(): string /** * @return array */ - public function levelMethodProvider(): array + public static function levelMethodProvider(): array { return [ 'emergency' => ['emergency', LogLevel::EMERGENCY], diff --git a/tests/Unit/Mailer/MailerTest.php b/tests/Unit/Mailer/MailerTest.php index bc9737abf..5d2319e60 100644 --- a/tests/Unit/Mailer/MailerTest.php +++ b/tests/Unit/Mailer/MailerTest.php @@ -77,7 +77,7 @@ public function testHttpAdapterGetTransportErrorsReturnsArray(string $adapterCla /** * @return array}> */ - public function httpAdapterProvider(): array + public static function httpAdapterProvider(): array { return [ 'mailgun' => [MailgunAdapter::class, ['api_key' => 'test', 'domain' => 'test.com']], diff --git a/tests/Unit/Module/Templates/OpenApiTemplatesTest.php b/tests/Unit/Module/Templates/OpenApiTemplatesTest.php index 6cd2737c5..a7cc00bb7 100644 --- a/tests/Unit/Module/Templates/OpenApiTemplatesTest.php +++ b/tests/Unit/Module/Templates/OpenApiTemplatesTest.php @@ -81,7 +81,7 @@ public function testApiTemplatesDescribeTheApiWithAttributesOnly(string $templat /** * @return array */ - public function apiTemplateProvider(): array + public static function apiTemplateProvider(): array { return [ 'default-api' => ['DefaultApi'], diff --git a/tests/Unit/Module/Templates/RedirectTemplatesTest.php b/tests/Unit/Module/Templates/RedirectTemplatesTest.php index 62053abd3..de61a72ef 100644 --- a/tests/Unit/Module/Templates/RedirectTemplatesTest.php +++ b/tests/Unit/Module/Templates/RedirectTemplatesTest.php @@ -43,7 +43,7 @@ public function testTemplatesGoBackWithTheBackRedirect(string $template): void /** * @return array */ - public function redirectTemplateProvider(): array + public static function redirectTemplateProvider(): array { return [ 'demo-web' => ['DemoWeb'], diff --git a/tests/Unit/Session/Adapters/Database/DatabaseHandlerTest.php b/tests/Unit/Session/Adapters/Database/DatabaseHandlerTest.php index 398d7f99e..e06c56c56 100644 --- a/tests/Unit/Session/Adapters/Database/DatabaseHandlerTest.php +++ b/tests/Unit/Session/Adapters/Database/DatabaseHandlerTest.php @@ -85,7 +85,7 @@ private function insertSession(string $id, int $ttl): void /** * @return array */ - public function interfaceMethodProvider(): array + public static function interfaceMethodProvider(): array { return [ 'open' => ['open'], diff --git a/tests/Unit/Tracer/ErrorHandlerTest.php b/tests/Unit/Tracer/ErrorHandlerTest.php index 5f57f34f1..89487c2cf 100644 --- a/tests/Unit/Tracer/ErrorHandlerTest.php +++ b/tests/Unit/Tracer/ErrorHandlerTest.php @@ -278,7 +278,7 @@ public function testLogErrorFallsBackToErrorMethodForUnknownLevel(): void /** * @return array */ - public function deprecationSeverityProvider(): array + public static function deprecationSeverityProvider(): array { return [ 'deprecated' => [E_DEPRECATED], @@ -299,7 +299,7 @@ public function testGetErrorTypeMappings(\Throwable $throwable, string $expected /** * @return array */ - public function errorTypeProvider(): array + public static function errorTypeProvider(): array { return [ 'mapped-error-exception' => [new ErrorException('x', 0, E_WARNING), 'warning'], diff --git a/tests/Unit/Tracer/ExceptionSeverityResolverTest.php b/tests/Unit/Tracer/ExceptionSeverityResolverTest.php index c60981885..ae35eff71 100644 --- a/tests/Unit/Tracer/ExceptionSeverityResolverTest.php +++ b/tests/Unit/Tracer/ExceptionSeverityResolverTest.php @@ -29,7 +29,7 @@ public function testResolveMappings(\Throwable $throwable, string $expected): vo /** * @return array */ - public function mappingProvider(): array + public static function mappingProvider(): array { return [ 'mapped-error-exception' => [new ErrorException('x', 0, E_WARNING), 'warning'], From ae529a809b21e81ec15c8eab1c0fd6caa96118fa Mon Sep 17 00:00:00 2001 From: Arman <407448+armanist@users.noreply.github.com> Date: Thu, 29 Oct 2026 11:46:24 +0400 Subject: [PATCH 02/10] [#622] Stop the env command when .env.example is missing Co-Authored-By: Claude Sonnet 5.5 --- src/Console/Commands/EnvCommand.php | 1 + tests/Unit/Console/Commands/EnvCommandTest.php | 1 + 2 files changed, 2 insertions(+) diff --git a/src/Console/Commands/EnvCommand.php b/src/Console/Commands/EnvCommand.php index 171bf7ea7..5c7194ba3 100644 --- a/src/Console/Commands/EnvCommand.php +++ b/src/Console/Commands/EnvCommand.php @@ -64,6 +64,7 @@ public function exec(): void if (!$fs->exists(App::getBaseDir() . DS . '.env.example')) { $this->error('.env.example file not found'); + return; } if ($fs->exists(App::getBaseDir() . DS . '.env') && !$this->getOption('yes') && !$this->confirm('The operation will overwrite values of the existing .env and will create new one from .env.example. Continue?')) { diff --git a/tests/Unit/Console/Commands/EnvCommandTest.php b/tests/Unit/Console/Commands/EnvCommandTest.php index 717b7b00b..c71e34a65 100644 --- a/tests/Unit/Console/Commands/EnvCommandTest.php +++ b/tests/Unit/Console/Commands/EnvCommandTest.php @@ -109,6 +109,7 @@ public function testExecShowsErrorWhenEnvExampleMissing(): void $output = $this->tester->getDisplay(); $this->assertStringContainsString('.env.example file not found', $output); + $this->assertStringNotContainsString('.env is successfully generated', $output); } finally { if ($existed && file_exists($backup)) { rename($backup, $envExample); From 90205558f4bbcb558c1d61354d8548d450f18f32 Mon Sep 17 00:00:00 2001 From: Arman <407448+armanist@users.noreply.github.com> Date: Thu, 29 Oct 2026 11:50:52 +0400 Subject: [PATCH 03/10] [#622] Fix the session flush warning Co-Authored-By: Claude Sonnet 5.5 --- src/Session/Traits/SessionTrait.php | 5 ++++- .../Native/NativeSessionAdapterTest.php | 17 +++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/Session/Traits/SessionTrait.php b/src/Session/Traits/SessionTrait.php index 31f584297..480d8e5ad 100644 --- a/src/Session/Traits/SessionTrait.php +++ b/src/Session/Traits/SessionTrait.php @@ -103,7 +103,10 @@ public function delete(string $key): void public function flush(): void { self::$storage = []; - session_destroy(); + + if (session_status() === PHP_SESSION_ACTIVE) { + session_destroy(); + } } /** diff --git a/tests/Unit/Session/Adapters/Native/NativeSessionAdapterTest.php b/tests/Unit/Session/Adapters/Native/NativeSessionAdapterTest.php index b48f51c9e..c33249aec 100644 --- a/tests/Unit/Session/Adapters/Native/NativeSessionAdapterTest.php +++ b/tests/Unit/Session/Adapters/Native/NativeSessionAdapterTest.php @@ -81,6 +81,23 @@ public function testNativeSessionFlush(): void session_start(); } + public function testNativeSessionFlushWithoutAnActiveSessionDoesNotWarn(): void + { + $oldLevel = error_reporting(E_ALL); + + try { + $this->session->flush(); + + $this->session->flush(); + + $this->assertEmpty($this->session->all()); + } finally { + error_reporting($oldLevel); + + @session_start(); + } + } + public function testNativeSessionGetSessionId(): void { $this->assertEquals(session_id(), $this->session->getId()); From a08f6cb1b3c319b2d87ecb2f1f4f1cc53bb27af5 Mon Sep 17 00:00:00 2001 From: Arman <407448+armanist@users.noreply.github.com> Date: Thu, 29 Oct 2026 11:53:02 +0400 Subject: [PATCH 04/10] [#622] Fix the OpenAPI command warning Co-Authored-By: Claude Sonnet 5.5 --- tests/Unit/Console/Commands/OpenApiCommandTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Unit/Console/Commands/OpenApiCommandTest.php b/tests/Unit/Console/Commands/OpenApiCommandTest.php index 6d337dd04..60f025853 100644 --- a/tests/Unit/Console/Commands/OpenApiCommandTest.php +++ b/tests/Unit/Console/Commands/OpenApiCommandTest.php @@ -216,7 +216,7 @@ public function testExecCreatesTheResourcesFoldersOfAModuleWithoutThem(): void mkdir($controllersDir, 0777, true); file_put_contents($routesDir . DS . 'routes.php', " Date: Thu, 29 Oct 2026 11:54:57 +0400 Subject: [PATCH 05/10] [#622] Fix listDirectory on an empty response Co-Authored-By: Claude Sonnet 5.5 --- src/Storage/Adapters/Dropbox/DropboxFileSystemAdapter.php | 2 +- .../Adapters/GoogleDrive/GoogleDriveFileSystemAdapter.php | 2 +- .../Storage/Adapters/Dropbox/DropboxFileSystemAdapterTest.php | 2 +- .../Adapters/GoogleDrive/GoogleDriveFileSystemAdapterTest.php | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Storage/Adapters/Dropbox/DropboxFileSystemAdapter.php b/src/Storage/Adapters/Dropbox/DropboxFileSystemAdapter.php index a70ebbd87..480701bea 100644 --- a/src/Storage/Adapters/Dropbox/DropboxFileSystemAdapter.php +++ b/src/Storage/Adapters/Dropbox/DropboxFileSystemAdapter.php @@ -210,7 +210,7 @@ public function listDirectory(string $dirname) { try { $response = (array) $this->dropboxApp->rpcRequest(DropboxApp::ENDPOINT_LIST_FOLDER, $this->dropboxApp->path($dirname)); - return $response['entries']; + return $response['entries'] ?? false; } catch (Exception) { return false; } diff --git a/src/Storage/Adapters/GoogleDrive/GoogleDriveFileSystemAdapter.php b/src/Storage/Adapters/GoogleDrive/GoogleDriveFileSystemAdapter.php index d49298298..1f18e061e 100644 --- a/src/Storage/Adapters/GoogleDrive/GoogleDriveFileSystemAdapter.php +++ b/src/Storage/Adapters/GoogleDrive/GoogleDriveFileSystemAdapter.php @@ -235,7 +235,7 @@ public function listDirectory(string $dirname) 'fields' => '*', ]; $response = (array) $this->googleDriveApp->rpcRequest(GoogleDriveApp::FILE_METADATA_URL . '?' . http_build_query($params), [], 'GET'); - return $response['files']; + return $response['files'] ?? false; } catch (Exception) { return false; } diff --git a/tests/Unit/Storage/Adapters/Dropbox/DropboxFileSystemAdapterTest.php b/tests/Unit/Storage/Adapters/Dropbox/DropboxFileSystemAdapterTest.php index 07a80a9bd..dea5e6859 100644 --- a/tests/Unit/Storage/Adapters/Dropbox/DropboxFileSystemAdapterTest.php +++ b/tests/Unit/Storage/Adapters/Dropbox/DropboxFileSystemAdapterTest.php @@ -218,7 +218,7 @@ public function testDropboxListDirectory(): void self::$response = []; - $this->assertNull($this->fs->listDirectory('test')); + $this->assertFalse($this->fs->listDirectory('test')); } } diff --git a/tests/Unit/Storage/Adapters/GoogleDrive/GoogleDriveFileSystemAdapterTest.php b/tests/Unit/Storage/Adapters/GoogleDrive/GoogleDriveFileSystemAdapterTest.php index 90b53c116..5b685fb78 100644 --- a/tests/Unit/Storage/Adapters/GoogleDrive/GoogleDriveFileSystemAdapterTest.php +++ b/tests/Unit/Storage/Adapters/GoogleDrive/GoogleDriveFileSystemAdapterTest.php @@ -199,7 +199,7 @@ public function testGoogleDriveListDirectory(): void self::$response = []; - $this->assertNull($this->fs->listDirectory('test')); + $this->assertFalse($this->fs->listDirectory('test')); } } From 38fc53ce784d583f5448221e6c372807bc00fac5 Mon Sep 17 00:00:00 2001 From: Arman <407448+armanist@users.noreply.github.com> Date: Thu, 29 Oct 2026 12:06:02 +0400 Subject: [PATCH 06/10] [#622] Report PHP warnings in the test suite Co-Authored-By: Claude Sonnet 5.5 --- phpunit.xml | 2 +- tests/Unit/ErrorReportingTest.php | 13 +++++++++++++ tests/bootstrap.php | 2 -- 3 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 tests/Unit/ErrorReportingTest.php diff --git a/phpunit.xml b/phpunit.xml index 3b2122c21..91a20dfcb 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -10,7 +10,7 @@ convertDeprecationsToExceptions="false" > - + diff --git a/tests/Unit/ErrorReportingTest.php b/tests/Unit/ErrorReportingTest.php new file mode 100644 index 000000000..2e23a3043 --- /dev/null +++ b/tests/Unit/ErrorReportingTest.php @@ -0,0 +1,13 @@ +assertSame(E_ALL, error_reporting() & E_ALL); + } +} diff --git a/tests/bootstrap.php b/tests/bootstrap.php index ccada9cec..5c41896ec 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -1,7 +1,5 @@ Date: Thu, 29 Oct 2026 12:53:56 +0400 Subject: [PATCH 07/10] [#622] Upgrade PHPUnit to 10 Co-Authored-By: Claude Sonnet 5.5 --- composer.json | 2 +- phpunit.xml | 9 ++++----- tests/Unit/ErrorReportingTest.php | 13 ------------- 3 files changed, 5 insertions(+), 19 deletions(-) delete mode 100644 tests/Unit/ErrorReportingTest.php diff --git a/composer.json b/composer.json index 9858c62f4..a1350314f 100644 --- a/composer.json +++ b/composer.json @@ -56,7 +56,7 @@ "twig/twig": "^3.27" }, "require-dev": { - "phpunit/phpunit": "^9.0", + "phpunit/phpunit": "^10.5", "mockery/mockery": "^1.4", "mikey179/vfsstream": "^1.6", "rector/rector": "^2.3", diff --git a/phpunit.xml b/phpunit.xml index 91a20dfcb..2c5efdfa3 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -4,10 +4,9 @@ xsi:noNamespaceSchemaLocation="./vendor/phpunit/phpunit/phpunit.xsd" bootstrap="tests/bootstrap.php" colors="true" - verbose="true" stderr="true" stopOnFailure="false" - convertDeprecationsToExceptions="false" + cacheDirectory=".phpunit.cache" > @@ -19,9 +18,9 @@ - + ./src - - \ No newline at end of file + + diff --git a/tests/Unit/ErrorReportingTest.php b/tests/Unit/ErrorReportingTest.php deleted file mode 100644 index 2e23a3043..000000000 --- a/tests/Unit/ErrorReportingTest.php +++ /dev/null @@ -1,13 +0,0 @@ -assertSame(E_ALL, error_reporting() & E_ALL); - } -} From 47a355ff3db154d9fdffcae0ea660058bec3d3fb Mon Sep 17 00:00:00 2001 From: Arman <407448+armanist@users.noreply.github.com> Date: Thu, 29 Oct 2026 12:54:37 +0400 Subject: [PATCH 08/10] [#622] Add the PHPUnit 10 changes to the changelog Co-Authored-By: Claude Sonnet 5.5 --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 069a23a1c..2009671dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,12 @@ The format is based on Keep a Changelog. ### Changed - The `DemoWeb` and `Toolkit` module templates redirect back with `back()` instead of `redirect(get_referrer() ?? base_url())` and `redirectWith(get_referrer() ?? base_url(), $data)`, so generated code gets the same-origin check. Modules generated before keep their code (#541) - Twig is a required dependency again: `twig/twig` `^3.27` is in `require` and no longer only suggested, which reverses the 3.0.3 change that made it optional while the framework supported PHP 8.0 (#532). Twig `^3.27` needs PHP 8.1, the minimum since 3.1.0. An application that pins an older Twig must allow `^3.27`. Removed `RendererException::twigNotInstalled()` and the check in `TwigAdapter` that threw it when Twig was missing, and the Twig tests no longer skip (#626) +- The test suite runs on PHPUnit `^10.5` (a development dependency) and `phpunit.xml` uses its schema. It now sets `error_reporting` to `-1`: it was the text `E_ALL & ~E_DEPRECATED`, which PHP reads as `0`, so every PHP warning was hidden in the tests (#622) + +### Fixed +- `php qt core:env` no longer prints `.env is successfully generated` and no longer tries to copy the file when `.env.example` is missing: it stops after the error message (#622) +- `flush()` of the session adapters no longer raises a warning when no session is active, so calling it twice, or before a session was started, no longer throws (#622) +- `listDirectory()` of the Dropbox and Google Drive adapters returns `false`, as its contract says, when the API response has no `entries` or `files`. It could return `null` when PHP warnings were not reported (#622) ## [3.1.0] - 2026-10-24 From cdf55caefc4783ede748695db4d0b7b3c337b5fc Mon Sep 17 00:00:00 2001 From: Arman <407448+armanist@users.noreply.github.com> Date: Thu, 29 Oct 2026 13:32:20 +0400 Subject: [PATCH 09/10] [#622] Add the closure parameter type Rector asks for Co-Authored-By: Claude Sonnet 5.5 --- src/Debugger/Debugger.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Debugger/Debugger.php b/src/Debugger/Debugger.php index a4765b25d..583f2f6e3 100644 --- a/src/Debugger/Debugger.php +++ b/src/Debugger/Debugger.php @@ -249,7 +249,7 @@ protected function routeInfo(): array $info['View'] = request()->getCurrentModule() . '/Views/' . $viewFile; } - return array_filter($info, fn ($value): bool => $value !== null && $value !== []); + return array_filter($info, fn (string|array|null $value): bool => $value !== null && $value !== []); } /** From 620c49644d0e62e4423596884b69c7b51d9a0b55 Mon Sep 17 00:00:00 2001 From: Arman <407448+armanist@users.noreply.github.com> Date: Thu, 29 Oct 2026 14:43:30 +0400 Subject: [PATCH 10/10] [#622] Remove the app's error handlers after each test Co-Authored-By: Claude Sonnet 5.5 --- tests/Unit/AppTestCase.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/Unit/AppTestCase.php b/tests/Unit/AppTestCase.php index 7ac1fed5e..2c92b6e6a 100644 --- a/tests/Unit/AppTestCase.php +++ b/tests/Unit/AppTestCase.php @@ -55,6 +55,9 @@ public function tearDown(): void } $this->clearAppContext(); + + restore_error_handler(); + restore_exception_handler(); } protected function createContext(string $type = AppType::WEB): AppContext