From 4ef3c67102e30db6c1bd3519eae6d1420fa544d8 Mon Sep 17 00:00:00 2001 From: Contributte AI Date: Fri, 6 Feb 2026 19:59:14 +0000 Subject: [PATCH 1/5] CI: use @master reference, add workflow_dispatch, add PHP 8.5 tests, add coverage workflow, remove kodiak --- .github/.kodiak.toml | 10 ---------- .github/workflows/codesniffer.yml | 5 +++-- .github/workflows/coverage.yml | 18 ++++++++++++++++++ .github/workflows/phpstan.yml | 7 ++++--- .github/workflows/tests.yml | 28 ++++++++++++++++++---------- 5 files changed, 43 insertions(+), 25 deletions(-) delete mode 100644 .github/.kodiak.toml create mode 100644 .github/workflows/coverage.yml diff --git a/.github/.kodiak.toml b/.github/.kodiak.toml deleted file mode 100644 index 60c34b6..0000000 --- a/.github/.kodiak.toml +++ /dev/null @@ -1,10 +0,0 @@ -version = 1 - -[merge] -automerge_label = "automerge" -blacklist_title_regex = "^WIP.*" -blacklist_labels = ["WIP"] -method = "rebase" -delete_branch_on_merge = true -notify_on_conflict = true -optimistic_updates = false diff --git a/.github/workflows/codesniffer.yml b/.github/workflows/codesniffer.yml index 69d3d77..a58ac4f 100644 --- a/.github/workflows/codesniffer.yml +++ b/.github/workflows/codesniffer.yml @@ -2,6 +2,7 @@ name: "Codesniffer" on: pull_request: + workflow_dispatch: push: branches: ["*"] @@ -10,8 +11,8 @@ on: - cron: "0 8 * * 1" jobs: - build: + codesniffer: name: "Codesniffer" - uses: contributte/.github/.github/workflows/codesniffer.yml@v1 + uses: contributte/.github/.github/workflows/codesniffer.yml@master with: php: "8.2" diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml new file mode 100644 index 0000000..fac01f8 --- /dev/null +++ b/.github/workflows/coverage.yml @@ -0,0 +1,18 @@ +name: "Coverage" + +on: + pull_request: + workflow_dispatch: + + push: + branches: ["*"] + + schedule: + - cron: "0 9 * * 1" + +jobs: + coverage: + name: "Nette Tester" + uses: contributte/.github/.github/workflows/nette-tester-coverage-v2.yml@master + with: + php: "8.2" diff --git a/.github/workflows/phpstan.yml b/.github/workflows/phpstan.yml index 7cd7dc2..13ceb07 100644 --- a/.github/workflows/phpstan.yml +++ b/.github/workflows/phpstan.yml @@ -2,16 +2,17 @@ name: "Phpstan" on: pull_request: + workflow_dispatch: push: branches: ["*"] schedule: - - cron: "0 8 * * 1" + - cron: "0 10 * * 1" jobs: - build: + phpstan: name: "Phpstan" - uses: contributte/.github/.github/workflows/phpstan.yml@v1 + uses: contributte/.github/.github/workflows/phpstan.yml@master with: php: "8.2" diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 768684a..ac636ef 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -2,28 +2,36 @@ name: "Nette Tester" on: pull_request: + workflow_dispatch: push: branches: ["*"] schedule: - - cron: "0 8 * * 1" + - cron: "0 10 * * 1" jobs: - build84: + test85: name: "Nette Tester" - uses: contributte/.github/.github/workflows/nette-tester-mysql.yml@v1 + uses: contributte/.github/.github/workflows/nette-tester-mysql.yml@master with: - php: "8.4" + php: "8.5" - build83: + test84: name: "Nette Tester" - uses: contributte/.github/.github/workflows/nette-tester-mysql.yml@v1 + uses: contributte/.github/.github/workflows/nette-tester-mysql.yml@master with: - php: "8.3" + php: "8.4" - build82: + test83: name: "Nette Tester" - uses: contributte/.github/.github/workflows/nette-tester-mysql.yml@v1 + uses: contributte/.github/.github/workflows/nette-tester-mysql.yml@master with: - php: "8.2" + php: "8.3" + + test82: + name: "Nette Tester" + uses: contributte/.github/.github/workflows/nette-tester-mysql.yml@master + with: + php: "8.2" + composer: "composer update --no-interaction --no-progress --prefer-dist --prefer-stable --prefer-lowest" From 730b5b315bcb4cc1d66b83392b83f880616281ef Mon Sep 17 00:00:00 2001 From: Contributte AI Date: Fri, 6 Feb 2026 19:59:30 +0000 Subject: [PATCH 2/5] Tests: use contributte/tester, add Toolkit::test(), add native type hints, cleanup bootstrap --- tests/.coveralls.yml | 4 ---- tests/.gitignore | 10 ---------- tests/Cases/E2E/BooksTest.phpt | 15 ++++++++++++-- tests/Cases/Exceptions.phpt | 17 ++++++++++------ tests/Cases/ExecutableQueryObject.phpt | 7 +++++-- tests/Cases/QueryObject.phpt | 3 ++- .../Cases/QueryObjectContextAwareManager.phpt | 12 +++++++---- tests/Cases/QueryObjectExtension.phpt | 8 +++++--- tests/Cases/Queryable.phpt | 7 +++++-- tests/Cases/TRepositoryQueryable.phpt | 20 ++++++++++++------- tests/Mocks/Model/Book/BookMapper.php | 3 +++ tests/Mocks/Model/User/UserMapper.php | 3 +++ tests/Mocks/SimpleConnection.php | 6 +----- tests/bootstrap.container.php | 3 ++- 14 files changed, 71 insertions(+), 47 deletions(-) delete mode 100644 tests/.coveralls.yml delete mode 100644 tests/.gitignore diff --git a/tests/.coveralls.yml b/tests/.coveralls.yml deleted file mode 100644 index 403f48b..0000000 --- a/tests/.coveralls.yml +++ /dev/null @@ -1,4 +0,0 @@ -# for php-coveralls -service_name: github-actions -coverage_clover: coverage.xml -json_path: coverage.json \ No newline at end of file diff --git a/tests/.gitignore b/tests/.gitignore deleted file mode 100644 index f0d3402..0000000 --- a/tests/.gitignore +++ /dev/null @@ -1,10 +0,0 @@ -# Folders - recursive -*.expected -*.actual - -# Folders -/tmp - -# Files -/*.log -/*.html diff --git a/tests/Cases/E2E/BooksTest.phpt b/tests/Cases/E2E/BooksTest.phpt index 2f2fc82..72289c2 100644 --- a/tests/Cases/E2E/BooksTest.phpt +++ b/tests/Cases/E2E/BooksTest.phpt @@ -6,10 +6,12 @@ use Contributte\Nextras\Orm\QueryObject\Queryable; use Contributte\Nextras\Orm\QueryObject\QueryObjectContextAwareManager; use Contributte\Nextras\Orm\QueryObject\QueryObjectManager; use Nette\DI\Container; +use Nextras\Dbal\Drivers\Exception\ConnectionException; use Nextras\Dbal\IConnection; use Nextras\Dbal\Result\Result; use Nextras\Orm\Collection\ICollection; use Tester\Assert; +use Tester\Environment; use Tester\TestCase; use Tests\Mocks\Model\Book\AllBooksExecutableQueryObject; use Tests\Mocks\Model\Book\AllBooksQueryObject; @@ -19,6 +21,15 @@ use Tests\Mocks\Model\User\User; $container = require_once __DIR__ . '/../../bootstrap.container.php'; +/** @var IConnection $connection */ +$connection = $container->getByType(IConnection::class); + +try { + $connection->connect(); +} catch (ConnectionException $e) { + Environment::skip('MySQL connection not available: ' . $e->getMessage()); +} + final class BooksTest extends TestCase { @@ -124,8 +135,8 @@ final class BooksTest extends TestCase $sql = file_get_contents(__DIR__ . '/../../Fixtures/mysql.sql'); assert($sql !== false); - foreach (array_filter(array_map('trim', explode(';', $sql))) as $query) { - $connection->query('%raw', $query); + foreach (array_filter(array_map('trim', explode(';', $sql))) as $statement) { + $connection->query('%raw', $statement); } } diff --git a/tests/Cases/Exceptions.phpt b/tests/Cases/Exceptions.phpt index d882805..b51c2ce 100644 --- a/tests/Cases/Exceptions.phpt +++ b/tests/Cases/Exceptions.phpt @@ -2,32 +2,37 @@ use Contributte\Nextras\Orm\QueryObject\Exception\InvalidHydrationModeException; use Contributte\Nextras\Orm\QueryObject\Exception\InvalidObjectCreationException; +use Contributte\Tester\Toolkit; use Tester\Assert; require_once __DIR__ . '/../bootstrap.php'; -test('InvalidHydrationModeException extends LogicException', function (): void { +// Test: InvalidHydrationModeException extends LogicException +Toolkit::test(static function (): void { $exception = new InvalidHydrationModeException('Test message'); Assert::type(LogicException::class, $exception); Assert::same('Test message', $exception->getMessage()); }); -test('InvalidObjectCreationException extends LogicException', function (): void { +// Test: InvalidObjectCreationException extends LogicException +Toolkit::test(static function (): void { $exception = new InvalidObjectCreationException('Creation failed'); Assert::type(LogicException::class, $exception); Assert::same('Creation failed', $exception->getMessage()); }); -test('InvalidHydrationModeException can be thrown and caught', function (): void { - Assert::exception(function (): void { +// Test: InvalidHydrationModeException can be thrown and caught +Toolkit::test(static function (): void { + Assert::exception(static function (): void { throw new InvalidHydrationModeException('Invalid hydration mode "99"'); }, InvalidHydrationModeException::class, 'Invalid hydration mode "99"'); }); -test('InvalidObjectCreationException can be thrown and caught', function (): void { - Assert::exception(function (): void { +// Test: InvalidObjectCreationException can be thrown and caught +Toolkit::test(static function (): void { + Assert::exception(static function (): void { throw new InvalidObjectCreationException('Created object must be typed of QueryObject'); }, InvalidObjectCreationException::class, 'Created object must be typed of QueryObject'); }); diff --git a/tests/Cases/ExecutableQueryObject.phpt b/tests/Cases/ExecutableQueryObject.phpt index ab1860e..9e0731d 100644 --- a/tests/Cases/ExecutableQueryObject.phpt +++ b/tests/Cases/ExecutableQueryObject.phpt @@ -1,6 +1,7 @@ fetch(new QueryBuilder($platform)); diff --git a/tests/Cases/QueryObjectContextAwareManager.phpt b/tests/Cases/QueryObjectContextAwareManager.phpt index 6089b32..35a4180 100644 --- a/tests/Cases/QueryObjectContextAwareManager.phpt +++ b/tests/Cases/QueryObjectContextAwareManager.phpt @@ -3,6 +3,7 @@ use Contributte\Nextras\Orm\QueryObject\Exception\InvalidObjectCreationException; use Contributte\Nextras\Orm\QueryObject\QueryObject; use Contributte\Nextras\Orm\QueryObject\QueryObjectContextAwareManager; +use Contributte\Tester\Toolkit; use Nette\DI\Container; use Nextras\Dbal\Connection; use Nextras\Dbal\QueryBuilder\QueryBuilder; @@ -12,7 +13,8 @@ use Tests\Mocks\SimpleQueryObject; require_once __DIR__ . '/../bootstrap.php'; -test('QueryObjectContextAwareManager create returns QueryObject', function (): void { +// Test: QueryObjectContextAwareManager create returns QueryObject +Toolkit::test(static function (): void { $queryObject = new SimpleQueryObject(); $container = Mockery::mock(Container::class); @@ -30,7 +32,8 @@ test('QueryObjectContextAwareManager create returns QueryObject', function (): v Mockery::close(); }); -test('QueryObjectContextAwareManager create throws InvalidObjectCreationException for non-QueryObject', function (): void { +// Test: QueryObjectContextAwareManager create throws InvalidObjectCreationException for non-QueryObject +Toolkit::test(static function (): void { $nonQueryObject = new stdClass(); $container = Mockery::mock(Container::class); @@ -41,14 +44,15 @@ test('QueryObjectContextAwareManager create throws InvalidObjectCreationExceptio $manager = new QueryObjectContextAwareManager($container); - Assert::exception(function () use ($manager): void { + Assert::exception(static function () use ($manager): void { $manager->create(stdClass::class); }, InvalidObjectCreationException::class); Mockery::close(); }); -test('QueryObjectContextAwareManager fetch returns Result', function (): void { +// Test: QueryObjectContextAwareManager fetch returns Result +Toolkit::test(static function (): void { $connection = Mockery::mock(Connection::class); $queryBuilder = Mockery::mock(QueryBuilder::class); $result = Mockery::mock(Result::class); diff --git a/tests/Cases/QueryObjectExtension.phpt b/tests/Cases/QueryObjectExtension.phpt index 46f5f6d..2e16551 100644 --- a/tests/Cases/QueryObjectExtension.phpt +++ b/tests/Cases/QueryObjectExtension.phpt @@ -2,6 +2,8 @@ use Contributte\Nextras\Orm\QueryObject\DI\NextrasQueryObjectExtension; use Contributte\Nextras\Orm\QueryObject\QueryObjectManager; +use Contributte\Tester\Environment; +use Contributte\Tester\Toolkit; use Nette\DI\Compiler; use Nette\DI\Container; use Nette\DI\ContainerLoader; @@ -9,9 +11,9 @@ use Tester\Assert; require_once __DIR__ . '/../bootstrap.php'; -test('NextrasQueryObjectExtension registers QueryObjectManager service', function (): void { - $loader = new ContainerLoader(TEMP_DIR); - $class = $loader->load(function (Compiler $compiler): void { +Toolkit::test(static function (): void { + $loader = new ContainerLoader(Environment::getTestDir()); + $class = $loader->load(static function (Compiler $compiler): void { $compiler->addExtension('nextrasqueryobject', new NextrasQueryObjectExtension()); }, microtime()); diff --git a/tests/Cases/Queryable.phpt b/tests/Cases/Queryable.phpt index 5039d4e..2d4d473 100644 --- a/tests/Cases/Queryable.phpt +++ b/tests/Cases/Queryable.phpt @@ -1,14 +1,17 @@ shouldReceive('select') ->with('*') @@ -122,7 +127,8 @@ test('TRepositoryQueryable fetch with HYDRATION_ENTITY returns ICollection', fun Mockery::close(); }); -test('TRepositoryQueryable fetch throws InvalidHydrationModeException for invalid mode', function (): void { +// Test: TRepositoryQueryable fetch throws InvalidHydrationModeException for invalid mode +Toolkit::test(static function (): void { $connection = Mockery::mock(Connection::class); $queryBuilder = Mockery::mock(QueryBuilder::class); @@ -145,7 +151,7 @@ test('TRepositoryQueryable fetch throws InvalidHydrationModeException for invali $queryObject = new SimpleQueryObject(); - Assert::exception(function () use ($repo, $queryObject): void { + Assert::exception(static function () use ($repo, $queryObject): void { $repo->fetch($queryObject, 999); }, InvalidHydrationModeException::class, 'Invalid hydration mode "999"'); diff --git a/tests/Mocks/Model/Book/BookMapper.php b/tests/Mocks/Model/Book/BookMapper.php index df2e0b7..ce5238d 100644 --- a/tests/Mocks/Model/Book/BookMapper.php +++ b/tests/Mocks/Model/Book/BookMapper.php @@ -4,6 +4,9 @@ use Nextras\Orm\Mapper\Dbal\DbalMapper; +/** + * @extends DbalMapper + */ final class BookMapper extends DbalMapper { diff --git a/tests/Mocks/Model/User/UserMapper.php b/tests/Mocks/Model/User/UserMapper.php index ce5f45d..779594b 100644 --- a/tests/Mocks/Model/User/UserMapper.php +++ b/tests/Mocks/Model/User/UserMapper.php @@ -4,6 +4,9 @@ use Nextras\Orm\Mapper\Dbal\DbalMapper; +/** + * @extends DbalMapper + */ final class UserMapper extends DbalMapper { diff --git a/tests/Mocks/SimpleConnection.php b/tests/Mocks/SimpleConnection.php index 4faa165..410e1f7 100644 --- a/tests/Mocks/SimpleConnection.php +++ b/tests/Mocks/SimpleConnection.php @@ -2,9 +2,7 @@ namespace Tests\Mocks; -use Mockery; use Nextras\Dbal\Connection; -use Nextras\Dbal\Platforms\IPlatform; use Nextras\Dbal\QueryBuilder\QueryBuilder; final class SimpleConnection extends Connection @@ -12,9 +10,7 @@ final class SimpleConnection extends Connection public function createQueryBuilder(): QueryBuilder { - $platform = Mockery::mock(IPlatform::class); - - return new QueryBuilder($platform); + return new QueryBuilder($this->getPlatform()); } } diff --git a/tests/bootstrap.container.php b/tests/bootstrap.container.php index e202da8..94c14e0 100644 --- a/tests/bootstrap.container.php +++ b/tests/bootstrap.container.php @@ -1,6 +1,7 @@ load(function (Compiler $compiler): void { $compiler->addExtension('inject', new InjectExtension()); $compiler->addExtension('di', new DIExtension()); From 51deb9c2e90de15ca3e46811c388099257a5670a Mon Sep 17 00:00:00 2001 From: Contributte AI Date: Fri, 6 Feb 2026 19:59:47 +0000 Subject: [PATCH 3/5] Config: update to contributte/qa ruleset, add native type hints, fix code style --- .editorconfig | 2 +- .gitattributes | 6 +++--- .gitignore | 8 ++++++-- phpstan.neon | 14 ++++++++++---- ruleset.xml | 12 ++++++++---- 5 files changed, 28 insertions(+), 14 deletions(-) diff --git a/.editorconfig b/.editorconfig index 3faf149..5e5b915 100644 --- a/.editorconfig +++ b/.editorconfig @@ -11,6 +11,6 @@ indent_style = tab indent_size = tab tab_width = 4 -[{*.json, *.yaml, *.yml, *.md}] +[*.{json,yaml,yml,md}] indent_style = space indent_size = 2 diff --git a/.gitattributes b/.gitattributes index c52e7b9..aad8529 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,10 +1,10 @@ -# Not archived .docs export-ignore -tests export-ignore .editorconfig export-ignore .gitattributes export-ignore .gitignore export-ignore .travis.yml export-ignore -phpstan.neon export-ignore +Makefile export-ignore README.md export-ignore +phpstan.neon export-ignore ruleset.xml export-ignore +tests export-ignore diff --git a/.gitignore b/.gitignore index b1b6f4d..f0b3670 100755 --- a/.gitignore +++ b/.gitignore @@ -6,5 +6,9 @@ /composer.lock # Tests -/temp -/coverage.xml +/tests/tmp +/coverage.* +/tests/**/*.log +/tests/**/*.html +/tests/**/*.expected +/tests/**/*.actual diff --git a/phpstan.neon b/phpstan.neon index b84b608..ced0e50 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,12 +1,18 @@ includes: - - vendor/phpstan/phpstan-deprecation-rules/rules.neon - - vendor/phpstan/phpstan-nette/extension.neon - - vendor/phpstan/phpstan-nette/rules.neon - - vendor/phpstan/phpstan-strict-rules/rules.neon + - vendor/contributte/phpstan/phpstan.neon parameters: level: 9 phpVersion: 80200 + + scanDirectories: + - src + + fileExtensions: + - php + paths: - src + ignoreErrors: + - identifier: trait.unused diff --git a/ruleset.xml b/ruleset.xml index bee4c4f..d26d240 100644 --- a/ruleset.xml +++ b/ruleset.xml @@ -10,10 +10,17 @@ + + + + - + + + /tests/Cases + /tests/Cases @@ -23,9 +30,6 @@ /tests/Cases - - /tests/Cases - /tests/tmp From d45187ddeb31f259cdfd1e1c0ca7be89f840f9fc Mon Sep 17 00:00:00 2001 From: Contributte AI Date: Fri, 6 Feb 2026 20:00:02 +0000 Subject: [PATCH 4/5] Composer: use contributte/qa, contributte/tester, contributte/phpstan, use full versions --- composer.json | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/composer.json b/composer.json index 710aa48..2734155 100755 --- a/composer.json +++ b/composer.json @@ -15,15 +15,12 @@ "nextras/dbal": "^5.0.0" }, "require-dev": { - "contributte/qa": "^0.4.0", - "contributte/tester": "^0.3.0", - "nette/di": "^3.0.1", - "nextras/orm": "^5.0.0", - "mockery/mockery": "^1.3.0", - "phpstan/phpstan": "^1.0.0", - "phpstan/phpstan-strict-rules": "^1.0.0", - "phpstan/phpstan-deprecation-rules": "^1.0.0", - "phpstan/phpstan-nette": "^1.0.0" + "contributte/phpstan": "~0.2.0", + "contributte/qa": "~0.4.0", + "contributte/tester": "~0.3.0", + "mockery/mockery": "^1.6.0", + "nette/di": "^3.2.0", + "nextras/orm": "^5.0.0" }, "autoload": { "psr-4": { @@ -37,14 +34,15 @@ }, "minimum-stability": "dev", "prefer-stable": true, - "extra": { - "branch-alias": { - "dev-master": "0.7.x-dev" - } - }, "config": { + "sort-packages": true, "allow-plugins": { "dealerdirect/phpcodesniffer-composer-installer": true } + }, + "extra": { + "branch-alias": { + "dev-master": "0.7.x-dev" + } } } From 22748c50f3eb18958306581d3e07b0a3a502508b Mon Sep 17 00:00:00 2001 From: Contributte AI Date: Wed, 11 Feb 2026 17:30:57 +0000 Subject: [PATCH 5/5] Tests: fix PHP 8.5 compatibility --- tests/Cases/E2E/BooksTest.phpt | 7 +++++++ tests/Cases/TRepositoryQueryable.phpt | 1 - 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/Cases/E2E/BooksTest.phpt b/tests/Cases/E2E/BooksTest.phpt index 72289c2..b95dbef 100644 --- a/tests/Cases/E2E/BooksTest.phpt +++ b/tests/Cases/E2E/BooksTest.phpt @@ -19,6 +19,13 @@ use Tests\Mocks\Model\Book\Book; use Tests\Mocks\Model\Book\BookRepository; use Tests\Mocks\Model\User\User; +require_once __DIR__ . '/../../bootstrap.php'; + +// nextras/orm 5.0.x is not yet compatible with PHP 8.5 (null array offset in AbstractEntity) +if (PHP_VERSION_ID >= 80500) { + Environment::skip('nextras/orm is not yet compatible with PHP 8.5'); +} + $container = require_once __DIR__ . '/../../bootstrap.container.php'; /** @var IConnection $connection */ diff --git a/tests/Cases/TRepositoryQueryable.phpt b/tests/Cases/TRepositoryQueryable.phpt index b0290de..222835e 100644 --- a/tests/Cases/TRepositoryQueryable.phpt +++ b/tests/Cases/TRepositoryQueryable.phpt @@ -37,7 +37,6 @@ Toolkit::test(static function (): void { // Using reflection to verify connection was stored $reflection = new ReflectionClass($repo); $property = $reflection->getProperty('connection'); - $property->setAccessible(true); Assert::same($connection, $property->getValue($repo));