diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index a8187aa..c7a444f 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -64,7 +64,7 @@ jobs: composer-options: ${{ matrix.composer-options }} - name: "Tests" - run: "vendor/bin/phpunit --testsuite=integration" + run: "vendor/bin/phpunit --testsuite=integration --no-coverage" mongodb: name: "mongodb" @@ -113,4 +113,4 @@ jobs: composer-options: ${{ matrix.composer-options }} - name: "Tests" - run: "vendor/bin/phpunit --testsuite=integration" + run: "vendor/bin/phpunit --testsuite=integration --no-coverage" diff --git a/Makefile b/Makefile index 3b49019..673af92 100644 --- a/Makefile +++ b/Makefile @@ -26,15 +26,15 @@ phpunit: vendor phpunit-unit phpunit-integration # .PHONY: phpunit-integration phpunit-integration: vendor ## run phpunit integration tests - MONGODB_URI="mongodb://localhost:27017" POSTGRES_URI="pgsql:host=localhost;port=5432;dbname=eventstore;user=postgres;password=postgres" vendor/bin/phpunit --testsuite=integration + MONGODB_URI="mongodb://localhost:27017" POSTGRES_URI="pgsql:host=localhost;port=5432;dbname=eventstore;user=postgres;password=postgres" vendor/bin/phpunit --testsuite=integrationn --no-coverage .PHONY: phpunit-integration-postgres phpunit-integration-postgres: vendor ## run phpunit integration tests on postgres - POSTGRES_URI="pgsql:host=localhost;port=5432;dbname=eventstore;user=postgres;password=postgres" vendor/bin/phpunit --testsuite=integration + POSTGRES_URI="pgsql:host=localhost;port=5432;dbname=eventstore;user=postgres;password=postgres" vendor/bin/phpunit --testsuite=integration --no-coverage .PHONY: phpunit-integration-mongodb phpunit-integration-mongodb: vendor ## run phpunit integration tests on mysql - MONGODB_URI="mongodb://localhost:27017" vendor/bin/phpunit --testsuite=integration + MONGODB_URI="mongodb://localhost:27017" vendor/bin/phpunit --testsuite=integration --no-coverage .PHONY: phpunit-unit phpunit-unit: vendor ## run phpunit unit tests @@ -42,7 +42,7 @@ phpunit-unit: vendor ## run phpu .PHONY: infection infection: vendor ## run infection - XDEBUG_MODE=coverage vendor/bin/infection --threads=3 + php -d memory_limit=312M vendor/bin/infection --threads=3 .PHONY: static static: phpstan cs ## run static analysers diff --git a/composer.json b/composer.json index 37201f9..ff1812c 100644 --- a/composer.json +++ b/composer.json @@ -26,16 +26,15 @@ }, "require-dev": { "ext-mongodb": "^2.1", - "infection/infection": "^0.31.9", + "infection/infection": "^0.32.6", "mongodb/mongodb": "^2.1", "patchlevel/coding-standard": "^1.3.0", "patchlevel/rango": "1.0.0-alpha.4", - "phpat/phpat": "^0.12.0", - "phpbench/phpbench": "^1.2.15", - "phpstan/phpstan": "^2.1.32", - "phpstan/phpstan-phpunit": "^2.0.8", - "phpunit/phpunit": "^11.5.17", - "symfony/var-dumper": "^6.4.0 || ^7.0.0 || ^8.0.0" + "phpat/phpat": "^0.12.4", + "phpbench/phpbench": "^1.6.1", + "phpstan/phpstan": "^2.1.46", + "phpstan/phpstan-phpunit": "^2.0.16", + "phpunit/phpunit": "^11.5.55" }, "config": { "preferred-install": { diff --git a/composer.lock b/composer.lock index da86a0b..2a56a69 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "029da272dcd1d95dd5a84818dde21ca7", + "content-hash": "5f6fd67afd1c794ec76dc5f3051a1d21", "packages": [ { "name": "patchlevel/hydrator", @@ -1241,16 +1241,16 @@ }, { "name": "infection/infection", - "version": "0.31.9", + "version": "0.32.6", "source": { "type": "git", "url": "https://github.com/infection/infection.git", - "reference": "f9628fcd7f76eadf24726e57a81937c42458232b" + "reference": "4ed769947eaf2ecf42203027301bad2bedf037e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/infection/infection/zipball/f9628fcd7f76eadf24726e57a81937c42458232b", - "reference": "f9628fcd7f76eadf24726e57a81937c42458232b", + "url": "https://api.github.com/repos/infection/infection/zipball/4ed769947eaf2ecf42203027301bad2bedf037e5", + "reference": "4ed769947eaf2ecf42203027301bad2bedf037e5", "shasum": "" }, "require": { @@ -1267,20 +1267,22 @@ "infection/include-interceptor": "^0.2.5", "infection/mutator": "^0.4", "justinrainbow/json-schema": "^6.0", - "nikic/php-parser": "^5.3", + "nikic/php-parser": "^5.6.2", "ondram/ci-detector": "^4.1.0", "php": "^8.2", - "sanmai/di-container": "^0.1.4", + "psr/log": "^2.0 || ^3.0", + "sanmai/di-container": "^0.1.12", "sanmai/duoclock": "^0.1.0", "sanmai/later": "^0.1.7", - "sanmai/pipeline": "^7.0", - "sebastian/diff": "^4.0 || ^5.0 || ^6.0 || ^7.0", - "symfony/console": "^6.4 || ^7.0", - "symfony/filesystem": "^6.4 || ^7.0", - "symfony/finder": "^6.4 || ^7.0", - "symfony/process": "^6.4 || ^7.0", + "sanmai/pipeline": "^7.2", + "sebastian/diff": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0", + "symfony/console": "^6.4 || ^7.0 || ^8.0", + "symfony/filesystem": "^6.4 || ^7.0 || ^8.0", + "symfony/finder": "^6.4 || ^7.0 || ^8.0", + "symfony/polyfill-php85": "^1.33", + "symfony/process": "^6.4 || ^7.0 || ^8.0", "thecodingmachine/safe": "^v3.0", - "webmozart/assert": "^1.11" + "webmozart/assert": "^1.11 || ^2.0" }, "conflict": { "antecedent/patchwork": "<2.1.25", @@ -1289,18 +1291,21 @@ "require-dev": { "ext-simplexml": "*", "fidry/makefile": "^1.0", + "fig/log-test": "^1.2", + "phpbench/phpbench": "^1.4", "phpstan/extension-installer": "^1.4", "phpstan/phpstan": "^2.1", "phpstan/phpstan-phpunit": "^2.0", "phpstan/phpstan-strict-rules": "^2.0", "phpstan/phpstan-webmozart-assert": "^2.0", "phpunit/phpunit": "^11.5.27", - "rector/rector": "^2.0", - "shipmonk/dead-code-detector": "^0.12.0", + "rector/rector": "^2.2.4", + "shipmonk/dead-code-detector": "^0.14.0", "shipmonk/name-collision-detector": "^2.1", "sidz/phpstan-rules": "^0.5.1", - "symfony/yaml": "^6.4 || ^7.0", - "thecodingmachine/phpstan-safe-rule": "^1.4" + "symfony/yaml": "^6.4 || ^7.0 || ^8.0", + "thecodingmachine/phpstan-safe-rule": "^1.4", + "webmozarts/strict-phpunit": "^7.15" }, "bin": [ "bin/infection" @@ -1356,7 +1361,7 @@ ], "support": { "issues": "https://github.com/infection/infection/issues", - "source": "https://github.com/infection/infection/tree/0.31.9" + "source": "https://github.com/infection/infection/tree/0.32.6" }, "funding": [ { @@ -1368,7 +1373,7 @@ "type": "open_collective" } ], - "time": "2025-10-27T12:00:54+00:00" + "time": "2026-02-26T14:34:26+00:00" }, { "name": "infection/mutator", @@ -1573,21 +1578,21 @@ }, { "name": "mongodb/mongodb", - "version": "2.1.2", + "version": "2.2.0", "source": { "type": "git", "url": "https://github.com/mongodb/mongo-php-library.git", - "reference": "0a2472ba9cbb932f7e43a8770aedb2fc30612a67" + "reference": "bbb13f969e37e047fd822527543df55fdc1c9298" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mongodb/mongo-php-library/zipball/0a2472ba9cbb932f7e43a8770aedb2fc30612a67", - "reference": "0a2472ba9cbb932f7e43a8770aedb2fc30612a67", + "url": "https://api.github.com/repos/mongodb/mongo-php-library/zipball/bbb13f969e37e047fd822527543df55fdc1c9298", + "reference": "bbb13f969e37e047fd822527543df55fdc1c9298", "shasum": "" }, "require": { "composer-runtime-api": "^2.0", - "ext-mongodb": "^2.1", + "ext-mongodb": "^2.2", "php": "^8.1", "psr/log": "^1.1.4|^2|^3", "symfony/polyfill-php85": "^1.32" @@ -1598,9 +1603,9 @@ "require-dev": { "doctrine/coding-standard": "^12.0", "phpunit/phpunit": "^10.5.35", - "rector/rector": "^2.1.4", + "rector/rector": "^2.3.4", "squizlabs/php_codesniffer": "^3.7", - "vimeo/psalm": "6.5.*" + "vimeo/psalm": "~6.14.2" }, "type": "library", "extra": { @@ -1644,9 +1649,9 @@ ], "support": { "issues": "https://github.com/mongodb/mongo-php-library/issues", - "source": "https://github.com/mongodb/mongo-php-library/tree/2.1.2" + "source": "https://github.com/mongodb/mongo-php-library/tree/2.2.0" }, - "time": "2025-10-06T12:12:40+00:00" + "time": "2026-02-11T11:39:56+00:00" }, { "name": "myclabs/deep-copy", @@ -4519,47 +4524,39 @@ }, { "name": "symfony/console", - "version": "v7.4.8", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "1e92e39c51f95b88e3d66fa2d9f06d1fb45dd707" + "reference": "5b66d385dc58f69652e56f78a4184615e3f2b7f7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/1e92e39c51f95b88e3d66fa2d9f06d1fb45dd707", - "reference": "1e92e39c51f95b88e3d66fa2d9f06d1fb45dd707", + "url": "https://api.github.com/repos/symfony/console/zipball/5b66d385dc58f69652e56f78a4184615e3f2b7f7", + "reference": "5b66d385dc58f69652e56f78a4184615e3f2b7f7", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-mbstring": "~1.0", + "php": ">=8.4", + "symfony/polyfill-mbstring": "^1.0", "symfony/service-contracts": "^2.5|^3", - "symfony/string": "^7.2|^8.0" - }, - "conflict": { - "symfony/dependency-injection": "<6.4", - "symfony/dotenv": "<6.4", - "symfony/event-dispatcher": "<6.4", - "symfony/lock": "<6.4", - "symfony/process": "<6.4" + "symfony/string": "^7.4|^8.0" }, "provide": { "psr/log-implementation": "1.0|2.0|3.0" }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^6.4|^7.0|^8.0", - "symfony/dependency-injection": "^6.4|^7.0|^8.0", - "symfony/event-dispatcher": "^6.4|^7.0|^8.0", - "symfony/http-foundation": "^6.4|^7.0|^8.0", - "symfony/http-kernel": "^6.4|^7.0|^8.0", - "symfony/lock": "^6.4|^7.0|^8.0", - "symfony/messenger": "^6.4|^7.0|^8.0", - "symfony/process": "^6.4|^7.0|^8.0", - "symfony/stopwatch": "^6.4|^7.0|^8.0", - "symfony/var-dumper": "^6.4|^7.0|^8.0" + "symfony/config": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/event-dispatcher": "^7.4|^8.0", + "symfony/http-foundation": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/lock": "^7.4|^8.0", + "symfony/messenger": "^7.4|^8.0", + "symfony/process": "^7.4|^8.0", + "symfony/stopwatch": "^7.4|^8.0", + "symfony/var-dumper": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -4593,7 +4590,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.4.8" + "source": "https://github.com/symfony/console/tree/v8.0.8" }, "funding": [ { @@ -4613,7 +4610,7 @@ "type": "tidelift" } ], - "time": "2026-03-30T13:54:39+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/deprecation-contracts", @@ -4684,25 +4681,25 @@ }, { "name": "symfony/filesystem", - "version": "v7.4.8", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "58b9790d12f9670b7f53a1c1738febd3108970a5" + "reference": "66b769ae743ce2d13e435528fbef4af03d623e5a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/58b9790d12f9670b7f53a1c1738febd3108970a5", - "reference": "58b9790d12f9670b7f53a1c1738febd3108970a5", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/66b769ae743ce2d13e435528fbef4af03d623e5a", + "reference": "66b769ae743ce2d13e435528fbef4af03d623e5a", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.4", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-mbstring": "~1.8" }, "require-dev": { - "symfony/process": "^6.4|^7.0|^8.0" + "symfony/process": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -4730,7 +4727,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v7.4.8" + "source": "https://github.com/symfony/filesystem/tree/v8.0.8" }, "funding": [ { @@ -4750,27 +4747,27 @@ "type": "tidelift" } ], - "time": "2026-03-24T13:12:05+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/finder", - "version": "v7.4.8", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "e0be088d22278583a82da281886e8c3592fbf149" + "reference": "8da41214757b87d97f181e3d14a4179286151007" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/e0be088d22278583a82da281886e8c3592fbf149", - "reference": "e0be088d22278583a82da281886e8c3592fbf149", + "url": "https://api.github.com/repos/symfony/finder/zipball/8da41214757b87d97f181e3d14a4179286151007", + "reference": "8da41214757b87d97f181e3d14a4179286151007", "shasum": "" }, "require": { - "php": ">=8.2" + "php": ">=8.4" }, "require-dev": { - "symfony/filesystem": "^6.4|^7.0|^8.0" + "symfony/filesystem": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -4798,7 +4795,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v7.4.8" + "source": "https://github.com/symfony/finder/tree/v8.0.8" }, "funding": [ { @@ -4818,7 +4815,7 @@ "type": "tidelift" } ], - "time": "2026-03-24T13:12:05+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/options-resolver", @@ -4893,16 +4890,16 @@ }, { "name": "symfony/polyfill-ctype", - "version": "v1.33.0", + "version": "v1.34.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" + "reference": "141046a8f9477948ff284fa65be2095baafb94f2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", - "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/141046a8f9477948ff284fa65be2095baafb94f2", + "reference": "141046a8f9477948ff284fa65be2095baafb94f2", "shasum": "" }, "require": { @@ -4952,7 +4949,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.34.0" }, "funding": [ { @@ -4972,20 +4969,20 @@ "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2026-04-10T16:19:22+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.33.0", + "version": "v1.34.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70" + "reference": "ad1b7b9092976d6c948b8a187cec9faaea9ec1df" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/380872130d3a5dd3ace2f4010d95125fde5d5c70", - "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/ad1b7b9092976d6c948b8a187cec9faaea9ec1df", + "reference": "ad1b7b9092976d6c948b8a187cec9faaea9ec1df", "shasum": "" }, "require": { @@ -5034,7 +5031,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.34.0" }, "funding": [ { @@ -5054,11 +5051,11 @@ "type": "tidelift" } ], - "time": "2025-06-27T09:58:17+00:00" + "time": "2026-04-10T16:19:22+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.33.0", + "version": "v1.34.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", @@ -5119,7 +5116,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.34.0" }, "funding": [ { @@ -5143,16 +5140,16 @@ }, { "name": "symfony/polyfill-mbstring", - "version": "v1.33.0", + "version": "v1.34.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" + "reference": "6a21eb99c6973357967f6ce3708cd55a6bec6315" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", - "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6a21eb99c6973357967f6ce3708cd55a6bec6315", + "reference": "6a21eb99c6973357967f6ce3708cd55a6bec6315", "shasum": "" }, "require": { @@ -5204,7 +5201,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.34.0" }, "funding": [ { @@ -5224,20 +5221,20 @@ "type": "tidelift" } ], - "time": "2024-12-23T08:48:59+00:00" + "time": "2026-04-10T17:25:58+00:00" }, { "name": "symfony/polyfill-php85", - "version": "v1.33.0", + "version": "v1.34.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php85.git", - "reference": "d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91" + "reference": "2c408a6bb0313e6001a83628dc5506100474254e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91", - "reference": "d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91", + "url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/2c408a6bb0313e6001a83628dc5506100474254e", + "reference": "2c408a6bb0313e6001a83628dc5506100474254e", "shasum": "" }, "require": { @@ -5284,7 +5281,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php85/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-php85/tree/v1.34.0" }, "funding": [ { @@ -5304,24 +5301,24 @@ "type": "tidelift" } ], - "time": "2025-06-23T16:12:55+00:00" + "time": "2026-04-10T16:50:15+00:00" }, { "name": "symfony/process", - "version": "v7.4.8", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "60f19cd3badc8de688421e21e4305eba50f8089a" + "reference": "cb8939aff03470d1a9d1d1b66d08c6fa71b3bbdc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/60f19cd3badc8de688421e21e4305eba50f8089a", - "reference": "60f19cd3badc8de688421e21e4305eba50f8089a", + "url": "https://api.github.com/repos/symfony/process/zipball/cb8939aff03470d1a9d1d1b66d08c6fa71b3bbdc", + "reference": "cb8939aff03470d1a9d1d1b66d08c6fa71b3bbdc", "shasum": "" }, "require": { - "php": ">=8.2" + "php": ">=8.4" }, "type": "library", "autoload": { @@ -5349,7 +5346,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.4.8" + "source": "https://github.com/symfony/process/tree/v8.0.8" }, "funding": [ { @@ -5369,7 +5366,7 @@ "type": "tidelift" } ], - "time": "2026-03-24T13:12:05+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/service-contracts", @@ -5548,93 +5545,6 @@ ], "time": "2026-03-30T15:14:47+00:00" }, - { - "name": "symfony/var-dumper", - "version": "v8.0.8", - "source": { - "type": "git", - "url": "https://github.com/symfony/var-dumper.git", - "reference": "cfb7badd53bf4177f6e9416cfbbccc13c0e773a1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/cfb7badd53bf4177f6e9416cfbbccc13c0e773a1", - "reference": "cfb7badd53bf4177f6e9416cfbbccc13c0e773a1", - "shasum": "" - }, - "require": { - "php": ">=8.4", - "symfony/polyfill-mbstring": "^1.0" - }, - "conflict": { - "symfony/console": "<7.4", - "symfony/error-handler": "<7.4" - }, - "require-dev": { - "symfony/console": "^7.4|^8.0", - "symfony/http-kernel": "^7.4|^8.0", - "symfony/process": "^7.4|^8.0", - "symfony/uid": "^7.4|^8.0", - "twig/twig": "^3.12" - }, - "bin": [ - "Resources/bin/var-dump-server" - ], - "type": "library", - "autoload": { - "files": [ - "Resources/functions/dump.php" - ], - "psr-4": { - "Symfony\\Component\\VarDumper\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides mechanisms for walking through any arbitrary PHP variable", - "homepage": "https://symfony.com", - "keywords": [ - "debug", - "dump" - ], - "support": { - "source": "https://github.com/symfony/var-dumper/tree/v8.0.8" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2026-03-31T07:15:36+00:00" - }, { "name": "thecodingmachine/safe", "version": "v3.4.0", @@ -5830,23 +5740,23 @@ }, { "name": "webmozart/assert", - "version": "1.12.1", + "version": "2.3.0", "source": { "type": "git", "url": "https://github.com/webmozarts/assert.git", - "reference": "9be6926d8b485f55b9229203f962b51ed377ba68" + "reference": "eb0d790f735ba6cff25c683a85a1da0eadeff9e4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/9be6926d8b485f55b9229203f962b51ed377ba68", - "reference": "9be6926d8b485f55b9229203f962b51ed377ba68", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/eb0d790f735ba6cff25c683a85a1da0eadeff9e4", + "reference": "eb0d790f735ba6cff25c683a85a1da0eadeff9e4", "shasum": "" }, "require": { "ext-ctype": "*", "ext-date": "*", "ext-filter": "*", - "php": "^7.2 || ^8.0" + "php": "^8.2" }, "suggest": { "ext-intl": "", @@ -5856,7 +5766,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.10-dev" + "dev-feature/2-0": "2.0-dev" } }, "autoload": { @@ -5872,6 +5782,10 @@ { "name": "Bernhard Schussek", "email": "bschussek@gmail.com" + }, + { + "name": "Woody Gilk", + "email": "woody.gilk@gmail.com" } ], "description": "Assertions to validate method input/output with nice error messages.", @@ -5882,9 +5796,9 @@ ], "support": { "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.12.1" + "source": "https://github.com/webmozarts/assert/tree/2.3.0" }, - "time": "2025-10-29T15:56:20+00:00" + "time": "2026-04-11T10:33:05+00:00" }, { "name": "webmozart/glob", diff --git a/infection.json.dist b/infection.json.dist index e8a9af1..8706308 100644 --- a/infection.json.dist +++ b/infection.json.dist @@ -16,7 +16,7 @@ "mutators": { "@default": true }, - "minMsi": 72, - "minCoveredMsi": 87, + "minMsi": 100, + "minCoveredMsi": 100, "testFrameworkOptions": "--testsuite=unit" } diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 9154d6d..02aaf79 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -66,6 +66,12 @@ parameters: count: 1 path: src/Repository/MongoDBRepository.php + - + message: '#^Parameter \#1 \$operations of method MongoDB\\Collection\:\:bulkWrite\(\) expects list\, non\-empty\-array\\}\}\}\> given\.$#' + identifier: argument.type + count: 1 + path: src/Repository/MongoDBRepository.php + - message: '#^Parameter \#2 \$data of method Patchlevel\\Hydrator\\HydratorWithContext\:\:hydrate\(\) expects array\, array\|object given\.$#' identifier: argument.type diff --git a/phpunit.xml.dist b/phpunit.xml.dist index e944c91..86cabb5 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -12,9 +12,17 @@ tests/Unit + + + + + + + src + diff --git a/src/Metadata/AttributeDocumentMetadataFactory.php b/src/Metadata/AttributeDocumentMetadataFactory.php index 46cfd89..fbf3f22 100644 --- a/src/Metadata/AttributeDocumentMetadataFactory.php +++ b/src/Metadata/AttributeDocumentMetadataFactory.php @@ -45,22 +45,8 @@ public function metadata(string $className): DocumentMetadata $collection = $attribute->collection; $database = $attribute->database; - $idProperty = null; $fields = []; - - foreach ($reflection->getProperties() as $reflectionProperty) { - $attributes = $reflectionProperty->getAttributes(Id::class); - - if ($attributes === []) { - continue; - } - - if ($idProperty !== null) { - throw new MultipleIdPropertiesFound($className); - } - - $idProperty = $reflectionProperty->getName(); - } + $idProperty = $this->getIdProperty($reflection); foreach ($reflection->getProperties() as $reflectionProperty) { if ($idProperty === $reflectionProperty->getName()) { @@ -78,10 +64,6 @@ public function metadata(string $className): DocumentMetadata $fields[$reflectionProperty->getName()] = $field; } - if ($idProperty === null) { - throw new NoIdPropertyFound($className); - } - return $this->metadataCache[$className] = new DocumentMetadata( $className, $database, @@ -115,4 +97,30 @@ private function indexes(ReflectionClass $reflection): array return $indexes; } + + /** @param ReflectionClass $reflection */ + private function getIdProperty(ReflectionClass $reflection): string + { + $idProperty = null; + + foreach ($reflection->getProperties() as $reflectionProperty) { + $attributes = $reflectionProperty->getAttributes(Id::class); + + if ($attributes === []) { + continue; + } + + if ($idProperty !== null) { + throw new MultipleIdPropertiesFound($reflection->name); + } + + $idProperty = $reflectionProperty->getName(); + } + + if ($idProperty === null) { + throw new NoIdPropertyFound($reflection->name); + } + + return $idProperty; + } } diff --git a/src/Repository/MongoDBRepository.php b/src/Repository/MongoDBRepository.php index 74fa261..85fa436 100644 --- a/src/Repository/MongoDBRepository.php +++ b/src/Repository/MongoDBRepository.php @@ -92,23 +92,23 @@ public function update(object ...$objects): void return; } - $this->collection->bulkWrite(array_map(function (object $object): array { - if ($object::class !== $this->metadata->className) { - throw new WrongClass($this->metadata->className, $object::class); - } - - $data = $this->hydrator->extract( - $object, - [DocumentMetadata::class => $this->metadata], - ); + $this->collection->bulkWrite(array_map( + function (object $object): array { + if ($object::class !== $this->metadata->className) { + throw new WrongClass($this->metadata->className, $object::class); + } - return [ - 'updateOne' => [ - ['_id' => $data['_id']], - ['$set' => $data], - ], - ]; - }, $objects)); + $data = $this->hydrator->extract($object, [DocumentMetadata::class => $this->metadata]); + + return [ + 'updateOne' => [ + ['_id' => $data['_id']], + ['$set' => $data], + ], + ]; + }, + $objects, + )); } /** diff --git a/tests/Unit/Fixtures/Profile.php b/tests/Unit/Fixtures/Profile.php index 8dded0f..a3dcfa4 100644 --- a/tests/Unit/Fixtures/Profile.php +++ b/tests/Unit/Fixtures/Profile.php @@ -11,6 +11,7 @@ #[Document('rango_documents')] #[Index('by_status', ['status' => 'asc'])] +#[Index('by_status_other', ['status' => 'desc'])] final readonly class Profile { /** diff --git a/tests/Unit/Metadata/AttributeDocumentMetadataFactoryTest.php b/tests/Unit/Metadata/AttributeDocumentMetadataFactoryTest.php new file mode 100644 index 0000000..9759493 --- /dev/null +++ b/tests/Unit/Metadata/AttributeDocumentMetadataFactoryTest.php @@ -0,0 +1,198 @@ + 'asc'], unique: false), + new Index('by_status_other', ['status' => 'desc'], unique: false), + ], + fields: ['id' => new FieldMapping('_id', [])], + ); + + $factory = new AttributeDocumentMetadataFactory(); + $metadata = $factory->metadata(Profile::class); + + self::assertEquals($expected, $metadata); + } + + public function testMetadataOnNonDocumentExpectClassIsNotAnDocument(): void + { + $factory = new AttributeDocumentMetadataFactory(); + $this->expectException(ClassIsNotAnDocument::class); + + $factory->metadata(Address::class); + } + + public function testMetadataInMemoryCache(): void + { + $factory = new AttributeDocumentMetadataFactory(); + $metadataFirst = $factory->metadata(Profile::class); + $metadataSecond = $factory->metadata(Profile::class); + + self::assertSame($metadataFirst, $metadataSecond); + } + + public function testMetadataIdNotFirstProperty(): void + { + $class = new #[Document('rango_documents')] + class () { + public function __construct( + public string $notId = '1', + #[Id] + public string $id = 'a', + ) { + } + }; + + $factory = new AttributeDocumentMetadataFactory(); + $metadata = $factory->metadata($class::class); + + self::assertSame('id', $metadata->idProperty); + } + + public function testMetadataNonIdDocument(): void + { + $class = new #[Document('rango_documents')] + class () { + public function __construct( + public string $id = '1', + ) { + } + }; + + $factory = new AttributeDocumentMetadataFactory(); + $this->expectException(NoIdPropertyFound::class); + + $factory->metadata($class::class); + } + + public function testMetadataNonIdDocumentWithoutProperties(): void + { + $class = new #[Document('rango_documents')] + class () { + }; + + $factory = new AttributeDocumentMetadataFactory(); + $this->expectException(NoIdPropertyFound::class); + + $factory->metadata($class::class); + } + + public function testMetadataMultipleIdDocument(): void + { + $class = new #[Document('rango_documents')] + class ('1', '2') { + public function __construct( + #[Id] + public string $id, + #[Id] + public string $id2, + ) { + } + }; + + $factory = new AttributeDocumentMetadataFactory(); + $this->expectException(MultipleIdPropertiesFound::class); + + $factory->metadata($class::class); + } + + public function testMetadataWithFieldResolver(): void + { + $expected = new DocumentMetadata( + className: Profile::class, + database: null, + collection: 'rango_documents', + idProperty: 'id', + indexes: [ + new Index('by_status', ['status' => 'asc'], unique: false), + new Index('by_status_other', ['status' => 'desc'], unique: false), + ], + fields: [ + 'id' => new FieldMapping('_id', []), + 'status' => new FieldMapping('_newStatus', []), + ], + ); + + $fieldResolver = new class implements FieldMappingResolver + { + public function resolve(ReflectionProperty $reflectionProperty): FieldMapping|null + { + if ($reflectionProperty->getName() === 'status') { + return new FieldMapping('_newStatus', []); + } + + return null; + } + }; + + $factory = new AttributeDocumentMetadataFactory($fieldResolver); + $metadata = $factory->metadata(Profile::class); + + self::assertEquals($expected, $metadata); + } + + public function testMetadataWithFieldResolverNotUsedForId(): void + { + $expected = new DocumentMetadata( + className: Profile::class, + database: null, + collection: 'rango_documents', + idProperty: 'id', + indexes: [ + new Index('by_status', ['status' => 'asc'], unique: false), + new Index('by_status_other', ['status' => 'desc'], unique: false), + ], + fields: ['id' => new FieldMapping('_id', [])], + ); + + $fieldResolver = new class implements FieldMappingResolver + { + public function resolve(ReflectionProperty $reflectionProperty): FieldMapping|null + { + if ($reflectionProperty->getName() === 'id') { + return new FieldMapping('_otherId', []); + } + + return null; + } + }; + + $factory = new AttributeDocumentMetadataFactory($fieldResolver); + $metadata = $factory->metadata(Profile::class); + + self::assertEquals($expected, $metadata); + } +} diff --git a/tests/Unit/Metadata/DocumentMetadataTest.php b/tests/Unit/Metadata/DocumentMetadataTest.php index acdc729..4f9d24b 100644 --- a/tests/Unit/Metadata/DocumentMetadataTest.php +++ b/tests/Unit/Metadata/DocumentMetadataTest.php @@ -6,9 +6,11 @@ use Patchlevel\ODM\Metadata\DocumentMetadata; use Patchlevel\ODM\Metadata\FieldMapping; +use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; use stdClass; +#[CoversClass(DocumentMetadata::class)] final class DocumentMetadataTest extends TestCase { public function testPropertyPathToFieldPathWithoutMapping(): void @@ -23,6 +25,18 @@ className: stdClass::class, self::assertSame('name', $metadata->propertyPathToFieldPath('name')); } + public function testPropertyPathToFieldPathWithoutMappingNested(): void + { + $metadata = new DocumentMetadata( + className: stdClass::class, + database: null, + collection: 'test', + idProperty: 'id', + ); + + self::assertSame('a.b.c', $metadata->propertyPathToFieldPath('a.b.c')); + } + public function testPropertyPathToFieldPathWithMapping(): void { $metadata = new DocumentMetadata( @@ -162,8 +176,8 @@ className: stdClass::class, ); self::assertSame( - ['_age' => ['$gt' => 18]], - $metadata->mapFilterToFieldPaths(['age' => ['$gt' => 18]]), + ['_age' => ['$gt' => 18, '$lt' => 30]], + $metadata->mapFilterToFieldPaths(['age' => ['$gt' => 18, '$lt' => 30]]), ); } diff --git a/tests/Unit/Metadata/StackHydratorFieldMappingResolverTest.php b/tests/Unit/Metadata/StackHydratorFieldMappingResolverTest.php index 1d77e6b..3eb12a4 100644 --- a/tests/Unit/Metadata/StackHydratorFieldMappingResolverTest.php +++ b/tests/Unit/Metadata/StackHydratorFieldMappingResolverTest.php @@ -11,9 +11,11 @@ use Patchlevel\ODM\Metadata\StackHydratorFieldMappingResolver; use Patchlevel\ODM\Tests\Unit\Fixtures\PersonalData; use Patchlevel\ODM\Tests\Unit\Fixtures\Profile; +use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; use ReflectionProperty; +#[CoversClass(StackHydratorFieldMappingResolver::class)] final class StackHydratorFieldMappingResolverTest extends TestCase { private StackHydratorFieldMappingResolver $resolver;