diff --git a/.gitattributes b/.gitattributes
index ca27cbc..5797c51 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -11,6 +11,7 @@ composer-require-* export-ignore
Makefile export-ignore
Dockerfile export-ignore
infection.* export-ignore
+testo.php export-ignore
tests export-ignore
docs export-ignore
resources/mock export-ignore
diff --git a/.github/labeler.yml b/.github/labeler.yml
index a11ab09..2a83e3a 100644
--- a/.github/labeler.yml
+++ b/.github/labeler.yml
@@ -19,6 +19,6 @@
"tests":
- changed-files:
- - any-glob-to-any-file: ['tests/**/*', 'phpunit*', 'psalm*']
+ - any-glob-to-any-file: ['tests/**/*', 'testo.php', 'psalm*']
...
diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml
index 80ff06c..de5f689 100644
--- a/.github/workflows/testing.yml
+++ b/.github/workflows/testing.yml
@@ -40,7 +40,6 @@ jobs:
- name: 🛠️ Setup problem matchers
run: |
echo "::add-matcher::${{ runner.tool_cache }}/php.json"
- echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"
- name: 🤖 Validate composer.json and composer.lock
run: composer validate --ansi --strict
@@ -50,13 +49,13 @@ jobs:
with:
dependency-versions: ${{ matrix.dependencies }}
- - name: 🧪 Collect code coverage with Xdebug and PhpUnit
+ - name: 🧪 Collect code coverage with Xdebug and Testo
run: composer test:cc
- name: 📤 Upload code coverage report to Codecov
uses: codecov/codecov-action@v4
with:
- files: runtime/phpunit/logs/clover.xml
+ files: runtime/coverage/clover.xml
token: ${{ secrets.CODECOV_TOKEN }}
verbose: true
@@ -72,7 +71,6 @@ jobs:
os:
- ubuntu-latest
php-version:
- - '8.1'
- '8.2'
- '8.3'
- '8.4'
@@ -94,7 +92,6 @@ jobs:
- name: 🛠️ Setup problem matchers
run: |
echo "::add-matcher::${{ runner.tool_cache }}/php.json"
- echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"
- name: 🤖 Validate composer.json and composer.lock
run: composer validate --ansi --strict
diff --git a/composer.json b/composer.json
index dd769c6..8aab38e 100644
--- a/composer.json
+++ b/composer.json
@@ -20,7 +20,7 @@
}
],
"require": {
- "php": ">=8.1",
+ "php": ">=8.2",
"composer/semver": "^3.4",
"internal/destroy": "^1.0",
"internal/path": "^1.2",
@@ -37,11 +37,13 @@
"require-dev": {
"buggregator/trap": "^1.15",
"dereuromark/composer-prefer-lowest": "^0.1.10",
- "phpunit/phpunit": "^10.5",
+ "llm/skills": "^1.9",
"roxblnfk/unpoly": "^1.8.1",
- "spiral/code-style": "^2.2.2",
- "ta-tikoma/phpunit-architecture-test": "^0.8.5",
- "vimeo/psalm": "^6.10"
+ "spiral/code-style": "^2.3.1",
+ "testo/bridge-mockery": "^0.1.2",
+ "testo/bridge-rector": "^0.2.2",
+ "testo/testo": "^0.10.40",
+ "vimeo/psalm": "^6.10 || ^7.0"
},
"suggest": {
"ext-simplexml": "to support XML configs parsing"
@@ -64,7 +66,8 @@
"config": {
"allow-plugins": {
"ergebnis/composer-normalize": true,
- "infection/extension-installer": true
+ "infection/extension-installer": true,
+ "llm/skills": true
},
"audit": {
"abandoned": "report"
@@ -93,23 +96,20 @@
"refactor": "rector process --config=rector.php",
"refactor:ci": "rector process --config=rector.php --dry-run --ansi",
"test": [
- "phpunit --color=always --no-coverage"
+ "testo --ansi --no-coverage"
],
"test:unit": [
- "@putenv XDEBUG_MODE=coverage",
- "phpunit --color=always --testsuite=Unit"
+ "testo --ansi --no-coverage --suite=Unit"
],
- "test:arch": [
- "@putenv XDEBUG_MODE=coverage",
- "phpunit --color=always --testsuite=Arch"
+ "test:integration": [
+ "testo --ansi --no-coverage --suite=Integration"
],
"test:acc": [
- "@putenv XDEBUG_MODE=coverage",
- "phpunit --color=always --testsuite=Acceptance"
+ "testo --ansi --no-coverage --suite=Acceptance"
],
"test:cc": [
"@putenv XDEBUG_MODE=coverage",
- "phpunit --coverage-clover=runtime/phpunit/logs/clover.xml --color=always"
+ "testo --ansi --coverage-clover=runtime/coverage/clover.xml"
]
}
}
diff --git a/composer.lock b/composer.lock
index b617b68..6600fef 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": "4baf1a24a63b58d6107569e1e30f73f3",
+ "content-hash": "98366586096d59339ec9087ff26a5891",
"packages": [
{
"name": "composer/semver",
@@ -207,16 +207,16 @@
},
{
"name": "internal/toml",
- "version": "1.1.2",
+ "version": "1.1.3",
"source": {
"type": "git",
"url": "https://github.com/php-internal/toml.git",
- "reference": "c6b2769e7b8ff25a9667337767eff6ec7967405c"
+ "reference": "b0e077794a179b0a39a4fba84185762a4425c472"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/php-internal/toml/zipball/c6b2769e7b8ff25a9667337767eff6ec7967405c",
- "reference": "c6b2769e7b8ff25a9667337767eff6ec7967405c",
+ "url": "https://api.github.com/repos/php-internal/toml/zipball/b0e077794a179b0a39a4fba84185762a4425c472",
+ "reference": "b0e077794a179b0a39a4fba84185762a4425c472",
"shasum": ""
},
"require": {
@@ -252,15 +252,15 @@
],
"support": {
"issues": "https://github.com/php-internal/toml/issues",
- "source": "https://github.com/php-internal/toml/tree/1.1.2"
+ "source": "https://github.com/php-internal/toml/tree/1.1.3"
},
"funding": [
{
- "url": "https://patreon.com/roxblnfk",
- "type": "patreon"
+ "url": "https://boosty.to/roxblnfk",
+ "type": "boosty"
}
],
- "time": "2026-03-31T11:16:07+00:00"
+ "time": "2026-06-28T16:28:37+00:00"
},
{
"name": "nyholm/psr7",
@@ -825,47 +825,47 @@
},
{
"name": "symfony/console",
- "version": "v6.4.39",
+ "version": "v7.4.16",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
- "reference": "c132f1215fe4aa45b70173cc00ce9a755dd31ec5"
+ "reference": "f4c69c9aed03abf933b294257d618bdd9b30a06d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/console/zipball/c132f1215fe4aa45b70173cc00ce9a755dd31ec5",
- "reference": "c132f1215fe4aa45b70173cc00ce9a755dd31ec5",
+ "url": "https://api.github.com/repos/symfony/console/zipball/f4c69c9aed03abf933b294257d618bdd9b30a06d",
+ "reference": "f4c69c9aed03abf933b294257d618bdd9b30a06d",
"shasum": ""
},
"require": {
- "php": ">=8.1",
+ "php": ">=8.2",
"symfony/deprecation-contracts": "^2.5|^3",
"symfony/polyfill-mbstring": "~1.0",
"symfony/service-contracts": "^2.5|^3",
- "symfony/string": "^5.4|^6.0|^7.0"
+ "symfony/string": "^7.2|^8.0"
},
"conflict": {
- "symfony/dependency-injection": "<5.4",
- "symfony/dotenv": "<5.4",
- "symfony/event-dispatcher": "<5.4",
- "symfony/lock": "<5.4",
- "symfony/process": "<5.4"
+ "symfony/dependency-injection": "<6.4",
+ "symfony/dotenv": "<6.4",
+ "symfony/event-dispatcher": "<6.4",
+ "symfony/lock": "<6.4",
+ "symfony/process": "<6.4"
},
"provide": {
"psr/log-implementation": "1.0|2.0|3.0"
},
"require-dev": {
"psr/log": "^1|^2|^3",
- "symfony/config": "^5.4|^6.0|^7.0",
- "symfony/dependency-injection": "^5.4|^6.0|^7.0",
- "symfony/event-dispatcher": "^5.4|^6.0|^7.0",
- "symfony/http-foundation": "^6.4|^7.0",
- "symfony/http-kernel": "^6.4|^7.0",
- "symfony/lock": "^5.4|^6.0|^7.0",
- "symfony/messenger": "^5.4|^6.0|^7.0",
- "symfony/process": "^5.4|^6.0|^7.0",
- "symfony/stopwatch": "^5.4|^6.0|^7.0",
- "symfony/var-dumper": "^5.4|^6.0|^7.0"
+ "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"
},
"type": "library",
"autoload": {
@@ -899,7 +899,7 @@
"terminal"
],
"support": {
- "source": "https://github.com/symfony/console/tree/v6.4.39"
+ "source": "https://github.com/symfony/console/tree/v7.4.16"
},
"funding": [
{
@@ -919,20 +919,20 @@
"type": "tidelift"
}
],
- "time": "2026-05-12T06:50:03+00:00"
+ "time": "2026-07-31T12:37:14+00:00"
},
{
"name": "symfony/deprecation-contracts",
- "version": "v3.7.0",
+ "version": "v3.7.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/deprecation-contracts.git",
- "reference": "50f59d1f3ca46d41ac911f97a78626b6756af35b"
+ "reference": "f3202fa1b5097b0af062dc978b32ecf63404e31d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/50f59d1f3ca46d41ac911f97a78626b6756af35b",
- "reference": "50f59d1f3ca46d41ac911f97a78626b6756af35b",
+ "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/f3202fa1b5097b0af062dc978b32ecf63404e31d",
+ "reference": "f3202fa1b5097b0af062dc978b32ecf63404e31d",
"shasum": ""
},
"require": {
@@ -970,7 +970,7 @@
"description": "A generic function and convention to trigger deprecation notices",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/deprecation-contracts/tree/v3.7.0"
+ "source": "https://github.com/symfony/deprecation-contracts/tree/v3.7.1"
},
"funding": [
{
@@ -990,24 +990,24 @@
"type": "tidelift"
}
],
- "time": "2026-04-13T15:52:40+00:00"
+ "time": "2026-06-05T06:23:12+00:00"
},
{
"name": "symfony/http-client",
- "version": "v6.4.37",
+ "version": "v7.4.16",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-client.git",
- "reference": "d40d3ac56e549056fedfb257fa58395b74cf964d"
+ "reference": "c513ed0ba5d1784a6b55fc84190dbe4451b12f41"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/http-client/zipball/d40d3ac56e549056fedfb257fa58395b74cf964d",
- "reference": "d40d3ac56e549056fedfb257fa58395b74cf964d",
+ "url": "https://api.github.com/repos/symfony/http-client/zipball/c513ed0ba5d1784a6b55fc84190dbe4451b12f41",
+ "reference": "c513ed0ba5d1784a6b55fc84190dbe4451b12f41",
"shasum": ""
},
"require": {
- "php": ">=8.1",
+ "php": ">=8.2",
"psr/log": "^1|^2|^3",
"symfony/deprecation-contracts": "^2.5|^3",
"symfony/http-client-contracts": "~3.4.4|^3.5.2",
@@ -1015,8 +1015,10 @@
"symfony/service-contracts": "^2.5|^3"
},
"conflict": {
+ "amphp/amp": "<2.5",
+ "amphp/socket": "<1.1",
"php-http/discovery": "<1.15",
- "symfony/http-foundation": "<6.3"
+ "symfony/http-foundation": "<6.4"
},
"provide": {
"php-http/async-client-implementation": "*",
@@ -1025,19 +1027,20 @@
"symfony/http-client-implementation": "3.0"
},
"require-dev": {
- "amphp/amp": "^2.5",
- "amphp/http-client": "^4.2.1",
- "amphp/http-tunnel": "^1.0",
- "amphp/socket": "^1.1",
+ "amphp/http-client": "^4.2.1|^5.0",
+ "amphp/http-tunnel": "^1.0|^2.0",
"guzzlehttp/promises": "^1.4|^2.0",
"nyholm/psr7": "^1.0",
"php-http/httplug": "^1.0|^2.0",
"psr/http-client": "^1.0",
- "symfony/dependency-injection": "^5.4|^6.0|^7.0",
- "symfony/http-kernel": "^5.4|^6.0|^7.0",
- "symfony/messenger": "^5.4|^6.0|^7.0",
- "symfony/process": "^5.4|^6.0|^7.0",
- "symfony/stopwatch": "^5.4|^6.0|^7.0"
+ "symfony/amphp-http-client-meta": "^1.0|^2.0",
+ "symfony/cache": "^6.4|^7.0|^8.0",
+ "symfony/dependency-injection": "^6.4|^7.0|^8.0",
+ "symfony/http-kernel": "^6.4|^7.0|^8.0",
+ "symfony/messenger": "^6.4|^7.0|^8.0",
+ "symfony/process": "^6.4|^7.0|^8.0",
+ "symfony/rate-limiter": "^6.4|^7.0|^8.0",
+ "symfony/stopwatch": "^6.4|^7.0|^8.0"
},
"type": "library",
"autoload": {
@@ -1068,7 +1071,7 @@
"http"
],
"support": {
- "source": "https://github.com/symfony/http-client/tree/v6.4.37"
+ "source": "https://github.com/symfony/http-client/tree/v7.4.16"
},
"funding": [
{
@@ -1088,20 +1091,20 @@
"type": "tidelift"
}
],
- "time": "2026-04-29T06:37:06+00:00"
+ "time": "2026-07-29T16:20:51+00:00"
},
{
"name": "symfony/http-client-contracts",
- "version": "v3.7.0",
+ "version": "v3.7.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-client-contracts.git",
- "reference": "4a2d00c37651c0bdc2b9e1c773487a8bf4edb12d"
+ "reference": "41fc42d276aeff21192465331ebbab7d83a743c0"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/4a2d00c37651c0bdc2b9e1c773487a8bf4edb12d",
- "reference": "4a2d00c37651c0bdc2b9e1c773487a8bf4edb12d",
+ "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/41fc42d276aeff21192465331ebbab7d83a743c0",
+ "reference": "41fc42d276aeff21192465331ebbab7d83a743c0",
"shasum": ""
},
"require": {
@@ -1150,7 +1153,7 @@
"standards"
],
"support": {
- "source": "https://github.com/symfony/http-client-contracts/tree/v3.7.0"
+ "source": "https://github.com/symfony/http-client-contracts/tree/v3.7.1"
},
"funding": [
{
@@ -1170,7 +1173,7 @@
"type": "tidelift"
}
],
- "time": "2026-03-06T13:17:50+00:00"
+ "time": "2026-06-05T06:23:12+00:00"
},
{
"name": "symfony/polyfill-ctype",
@@ -1257,16 +1260,16 @@
},
{
"name": "symfony/polyfill-intl-grapheme",
- "version": "v1.37.0",
+ "version": "v1.41.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-grapheme.git",
- "reference": "4864388bfbd3001ce88e234fab652acd91fdc57e"
+ "reference": "bb899c1db0aa8127dc3afe8cda4a67eb24915f8d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/4864388bfbd3001ce88e234fab652acd91fdc57e",
- "reference": "4864388bfbd3001ce88e234fab652acd91fdc57e",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/bb899c1db0aa8127dc3afe8cda4a67eb24915f8d",
+ "reference": "bb899c1db0aa8127dc3afe8cda4a67eb24915f8d",
"shasum": ""
},
"require": {
@@ -1315,7 +1318,7 @@
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.37.0"
+ "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.41.0"
},
"funding": [
{
@@ -1335,20 +1338,20 @@
"type": "tidelift"
}
],
- "time": "2026-04-26T13:13:48+00:00"
+ "time": "2026-07-28T08:25:59+00:00"
},
{
"name": "symfony/polyfill-intl-normalizer",
- "version": "v1.37.0",
+ "version": "v1.38.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-normalizer.git",
- "reference": "3833d7255cc303546435cb650316bff708a1c75c"
+ "reference": "2d446c214bdbe5b71bde5011b060a05fece3ae6b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c",
- "reference": "3833d7255cc303546435cb650316bff708a1c75c",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/2d446c214bdbe5b71bde5011b060a05fece3ae6b",
+ "reference": "2d446c214bdbe5b71bde5011b060a05fece3ae6b",
"shasum": ""
},
"require": {
@@ -1400,7 +1403,7 @@
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.37.0"
+ "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.38.0"
},
"funding": [
{
@@ -1420,20 +1423,20 @@
"type": "tidelift"
}
],
- "time": "2024-09-09T11:45:10+00:00"
+ "time": "2026-05-25T13:48:31+00:00"
},
{
"name": "symfony/polyfill-mbstring",
- "version": "v1.37.0",
+ "version": "v1.38.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
- "reference": "6a21eb99c6973357967f6ce3708cd55a6bec6315"
+ "reference": "d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6a21eb99c6973357967f6ce3708cd55a6bec6315",
- "reference": "6a21eb99c6973357967f6ce3708cd55a6bec6315",
+ "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6",
+ "reference": "d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6",
"shasum": ""
},
"require": {
@@ -1485,7 +1488,7 @@
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.37.0"
+ "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.38.2"
},
"funding": [
{
@@ -1505,20 +1508,20 @@
"type": "tidelift"
}
],
- "time": "2026-04-10T17:25:58+00:00"
+ "time": "2026-05-27T06:59:30+00:00"
},
{
"name": "symfony/polyfill-php83",
- "version": "v1.37.0",
+ "version": "v1.41.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php83.git",
- "reference": "3600c2cb22399e25bb226e4a135ce91eeb2a6149"
+ "reference": "5ea99087fb99c273a9b9236ed4c31e78b16103c6"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/3600c2cb22399e25bb226e4a135ce91eeb2a6149",
- "reference": "3600c2cb22399e25bb226e4a135ce91eeb2a6149",
+ "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/5ea99087fb99c273a9b9236ed4c31e78b16103c6",
+ "reference": "5ea99087fb99c273a9b9236ed4c31e78b16103c6",
"shasum": ""
},
"require": {
@@ -1565,7 +1568,7 @@
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-php83/tree/v1.37.0"
+ "source": "https://github.com/symfony/polyfill-php83/tree/v1.41.0"
},
"funding": [
{
@@ -1585,20 +1588,20 @@
"type": "tidelift"
}
],
- "time": "2026-04-10T17:25:58+00:00"
+ "time": "2026-07-01T12:47:55+00:00"
},
{
"name": "symfony/service-contracts",
- "version": "v3.7.0",
+ "version": "v3.7.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/service-contracts.git",
- "reference": "d25d82433a80eba6aa0e6c24b61d7370d99e444a"
+ "reference": "c0a284bab1ed8aa0417e3d69250ab437739563a0"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/service-contracts/zipball/d25d82433a80eba6aa0e6c24b61d7370d99e444a",
- "reference": "d25d82433a80eba6aa0e6c24b61d7370d99e444a",
+ "url": "https://api.github.com/repos/symfony/service-contracts/zipball/c0a284bab1ed8aa0417e3d69250ab437739563a0",
+ "reference": "c0a284bab1ed8aa0417e3d69250ab437739563a0",
"shasum": ""
},
"require": {
@@ -1652,7 +1655,7 @@
"standards"
],
"support": {
- "source": "https://github.com/symfony/service-contracts/tree/v3.7.0"
+ "source": "https://github.com/symfony/service-contracts/tree/v3.7.1"
},
"funding": [
{
@@ -1672,26 +1675,27 @@
"type": "tidelift"
}
],
- "time": "2026-03-28T09:44:51+00:00"
+ "time": "2026-06-16T09:55:08+00:00"
},
{
"name": "symfony/string",
- "version": "v6.4.39",
+ "version": "v7.4.15",
"source": {
"type": "git",
"url": "https://github.com/symfony/string.git",
- "reference": "62e3c927de664edadb5bef260987eb047a17a113"
+ "reference": "e394af32256bf9e7bf80849d95e589167c10097b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/string/zipball/62e3c927de664edadb5bef260987eb047a17a113",
- "reference": "62e3c927de664edadb5bef260987eb047a17a113",
+ "url": "https://api.github.com/repos/symfony/string/zipball/e394af32256bf9e7bf80849d95e589167c10097b",
+ "reference": "e394af32256bf9e7bf80849d95e589167c10097b",
"shasum": ""
},
"require": {
- "php": ">=8.1",
+ "php": ">=8.2",
+ "symfony/deprecation-contracts": "^2.5|^3.0",
"symfony/polyfill-ctype": "~1.8",
- "symfony/polyfill-intl-grapheme": "~1.0",
+ "symfony/polyfill-intl-grapheme": "~1.33",
"symfony/polyfill-intl-normalizer": "~1.0",
"symfony/polyfill-mbstring": "~1.0"
},
@@ -1699,10 +1703,11 @@
"symfony/translation-contracts": "<2.5"
},
"require-dev": {
- "symfony/http-client": "^5.4|^6.0|^7.0",
- "symfony/intl": "^6.2|^7.0",
+ "symfony/emoji": "^7.1|^8.0",
+ "symfony/http-client": "^6.4|^7.0|^8.0",
+ "symfony/intl": "^6.4|^7.0|^8.0",
"symfony/translation-contracts": "^2.5|^3.0",
- "symfony/var-exporter": "^5.4|^6.0|^7.0"
+ "symfony/var-exporter": "^6.4|^7.0|^8.0"
},
"type": "library",
"autoload": {
@@ -1741,7 +1746,7 @@
"utf8"
],
"support": {
- "source": "https://github.com/symfony/string/tree/v6.4.39"
+ "source": "https://github.com/symfony/string/tree/v7.4.15"
},
"funding": [
{
@@ -1761,7 +1766,7 @@
"type": "tidelift"
}
],
- "time": "2026-05-12T11:44:19+00:00"
+ "time": "2026-07-28T07:33:02+00:00"
},
{
"name": "yiisoft/injector",
@@ -1843,16 +1848,16 @@
"packages-dev": [
{
"name": "amphp/amp",
- "version": "v3.1.1",
+ "version": "v3.1.3",
"source": {
"type": "git",
"url": "https://github.com/amphp/amp.git",
- "reference": "fa0ab33a6f47a82929c38d03ca47ebb71086a93f"
+ "reference": "73c38b323ff8d790abf0f76c56fcc892bda4111a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/amphp/amp/zipball/fa0ab33a6f47a82929c38d03ca47ebb71086a93f",
- "reference": "fa0ab33a6f47a82929c38d03ca47ebb71086a93f",
+ "url": "https://api.github.com/repos/amphp/amp/zipball/73c38b323ff8d790abf0f76c56fcc892bda4111a",
+ "reference": "73c38b323ff8d790abf0f76c56fcc892bda4111a",
"shasum": ""
},
"require": {
@@ -1862,7 +1867,7 @@
"require-dev": {
"amphp/php-cs-fixer-config": "^2",
"phpunit/phpunit": "^9",
- "psalm/phar": "5.23.1"
+ "psalm/phar": "6.16.1"
},
"type": "library",
"autoload": {
@@ -1912,7 +1917,7 @@
],
"support": {
"issues": "https://github.com/amphp/amp/issues",
- "source": "https://github.com/amphp/amp/tree/v3.1.1"
+ "source": "https://github.com/amphp/amp/tree/v3.1.3"
},
"funding": [
{
@@ -1920,7 +1925,7 @@
"type": "github"
}
],
- "time": "2025-08-27T21:42:00+00:00"
+ "time": "2026-07-19T17:59:20+00:00"
},
{
"name": "amphp/byte-stream",
@@ -2153,16 +2158,16 @@
},
{
"name": "amphp/parallel",
- "version": "v2.3.4",
+ "version": "v2.4.0",
"source": {
"type": "git",
"url": "https://github.com/amphp/parallel.git",
- "reference": "3ad45d1cff1bfbfe832c79671e6a4a1017dd9921"
+ "reference": "37f5b2754fadc229c00f9416bd68fb8d04529a81"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/amphp/parallel/zipball/3ad45d1cff1bfbfe832c79671e6a4a1017dd9921",
- "reference": "3ad45d1cff1bfbfe832c79671e6a4a1017dd9921",
+ "url": "https://api.github.com/repos/amphp/parallel/zipball/37f5b2754fadc229c00f9416bd68fb8d04529a81",
+ "reference": "37f5b2754fadc229c00f9416bd68fb8d04529a81",
"shasum": ""
},
"require": {
@@ -2225,7 +2230,7 @@
],
"support": {
"issues": "https://github.com/amphp/parallel/issues",
- "source": "https://github.com/amphp/parallel/tree/v2.3.4"
+ "source": "https://github.com/amphp/parallel/tree/v2.4.0"
},
"funding": [
{
@@ -2233,7 +2238,7 @@
"type": "github"
}
],
- "time": "2026-05-06T19:26:51+00:00"
+ "time": "2026-05-16T16:54:01+00:00"
},
{
"name": "amphp/parser",
@@ -2299,16 +2304,16 @@
},
{
"name": "amphp/pipeline",
- "version": "v1.2.4",
+ "version": "v1.2.7",
"source": {
"type": "git",
"url": "https://github.com/amphp/pipeline.git",
- "reference": "a044733e080940d1483f56caff0c412ad6982776"
+ "reference": "cf2d67696c2015ea7c7fd8f6ec5f8ed7c2d17c17"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/amphp/pipeline/zipball/a044733e080940d1483f56caff0c412ad6982776",
- "reference": "a044733e080940d1483f56caff0c412ad6982776",
+ "url": "https://api.github.com/repos/amphp/pipeline/zipball/cf2d67696c2015ea7c7fd8f6ec5f8ed7c2d17c17",
+ "reference": "cf2d67696c2015ea7c7fd8f6ec5f8ed7c2d17c17",
"shasum": ""
},
"require": {
@@ -2354,7 +2359,7 @@
],
"support": {
"issues": "https://github.com/amphp/pipeline/issues",
- "source": "https://github.com/amphp/pipeline/tree/v1.2.4"
+ "source": "https://github.com/amphp/pipeline/tree/v1.2.7"
},
"funding": [
{
@@ -2362,20 +2367,20 @@
"type": "github"
}
],
- "time": "2026-05-06T05:37:57+00:00"
+ "time": "2026-07-26T14:50:43+00:00"
},
{
"name": "amphp/process",
- "version": "v2.0.3",
+ "version": "v2.1.0",
"source": {
"type": "git",
"url": "https://github.com/amphp/process.git",
- "reference": "52e08c09dec7511d5fbc1fb00d3e4e79fc77d58d"
+ "reference": "583959df17d00304ad7b0b32285373f985935643"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/amphp/process/zipball/52e08c09dec7511d5fbc1fb00d3e4e79fc77d58d",
- "reference": "52e08c09dec7511d5fbc1fb00d3e4e79fc77d58d",
+ "url": "https://api.github.com/repos/amphp/process/zipball/583959df17d00304ad7b0b32285373f985935643",
+ "reference": "583959df17d00304ad7b0b32285373f985935643",
"shasum": ""
},
"require": {
@@ -2389,7 +2394,7 @@
"amphp/php-cs-fixer-config": "^2",
"amphp/phpunit-util": "^3",
"phpunit/phpunit": "^9",
- "psalm/phar": "^5.4"
+ "psalm/phar": "6.16.1"
},
"type": "library",
"autoload": {
@@ -2422,7 +2427,7 @@
"homepage": "https://amphp.org/process",
"support": {
"issues": "https://github.com/amphp/process/issues",
- "source": "https://github.com/amphp/process/tree/v2.0.3"
+ "source": "https://github.com/amphp/process/tree/v2.1.0"
},
"funding": [
{
@@ -2430,7 +2435,7 @@
"type": "github"
}
],
- "time": "2024-04-19T03:13:44+00:00"
+ "time": "2026-05-31T15:11:55+00:00"
},
{
"name": "amphp/serialization",
@@ -2660,16 +2665,16 @@
},
{
"name": "buggregator/trap",
- "version": "1.15.2",
+ "version": "1.16.0",
"source": {
"type": "git",
"url": "https://github.com/buggregator/trap.git",
- "reference": "f146f04ee7be14740d8b35311f1a136a929976ed"
+ "reference": "a0634a3963b7708d53bffd383e4d1976bf57af06"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/buggregator/trap/zipball/f146f04ee7be14740d8b35311f1a136a929976ed",
- "reference": "f146f04ee7be14740d8b35311f1a136a929976ed",
+ "url": "https://api.github.com/repos/buggregator/trap/zipball/a0634a3963b7708d53bffd383e4d1976bf57af06",
+ "reference": "a0634a3963b7708d53bffd383e4d1976bf57af06",
"shasum": ""
},
"require": {
@@ -2683,6 +2688,7 @@
"php-http/message": "^1.15",
"psr/container": "^1.1 || ^2.0",
"psr/http-message": "^1.1 || ^2",
+ "psr/log": "^2 || ^3",
"symfony/console": "^6.4 || ^7 || ^8",
"symfony/var-dumper": "^6.3 || ^7 || ^8",
"yiisoft/injector": "^1.2"
@@ -2747,7 +2753,7 @@
],
"support": {
"issues": "https://github.com/buggregator/trap/issues",
- "source": "https://github.com/buggregator/trap/tree/1.15.2"
+ "source": "https://github.com/buggregator/trap/tree/1.16.0"
},
"funding": [
{
@@ -2755,7 +2761,7 @@
"type": "boosty"
}
],
- "time": "2026-04-07T21:19:09+00:00"
+ "time": "2026-06-16T12:15:11+00:00"
},
{
"name": "clue/stream-filter",
@@ -2825,28 +2831,29 @@
},
{
"name": "composer/pcre",
- "version": "3.3.2",
+ "version": "3.4.0",
"source": {
"type": "git",
"url": "https://github.com/composer/pcre.git",
- "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e"
+ "reference": "d5a341b3fb61f3001970940afb1d332968a183ed"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/composer/pcre/zipball/b2bed4734f0cc156ee1fe9c0da2550420d99a21e",
- "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e",
+ "url": "https://api.github.com/repos/composer/pcre/zipball/d5a341b3fb61f3001970940afb1d332968a183ed",
+ "reference": "d5a341b3fb61f3001970940afb1d332968a183ed",
"shasum": ""
},
"require": {
"php": "^7.4 || ^8.0"
},
"conflict": {
- "phpstan/phpstan": "<1.11.10"
+ "phpstan/phpstan": "<2.2.2"
},
"require-dev": {
- "phpstan/phpstan": "^1.12 || ^2",
- "phpstan/phpstan-strict-rules": "^1 || ^2",
- "phpunit/phpunit": "^8 || ^9"
+ "phpstan/phpstan": "^2",
+ "phpstan/phpstan-deprecation-rules": "^2",
+ "phpstan/phpstan-strict-rules": "^2",
+ "phpunit/phpunit": "^9"
},
"type": "library",
"extra": {
@@ -2884,7 +2891,7 @@
],
"support": {
"issues": "https://github.com/composer/pcre/issues",
- "source": "https://github.com/composer/pcre/tree/3.3.2"
+ "source": "https://github.com/composer/pcre/tree/3.4.0"
},
"funding": [
{
@@ -2894,13 +2901,9 @@
{
"url": "https://github.com/composer",
"type": "github"
- },
- {
- "url": "https://tidelift.com/funding/github/packagist/composer/composer",
- "type": "tidelift"
}
],
- "time": "2024-11-12T16:29:46+00:00"
+ "time": "2026-06-07T11:47:49+00:00"
},
{
"name": "composer/xdebug-handler",
@@ -3315,6 +3318,57 @@
],
"time": "2025-08-14T07:29:31+00:00"
},
+ {
+ "name": "hamcrest/hamcrest-php",
+ "version": "v2.1.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/hamcrest/hamcrest-php.git",
+ "reference": "f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487",
+ "reference": "f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.4|^8.0"
+ },
+ "replace": {
+ "cordoval/hamcrest-php": "*",
+ "davedevelopment/hamcrest-php": "*",
+ "kodova/hamcrest-php": "*"
+ },
+ "require-dev": {
+ "phpunit/php-file-iterator": "^1.4 || ^2.0 || ^3.0",
+ "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0 || ^8.0 || ^9.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.1-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "hamcrest"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "description": "This is the PHP port of Hamcrest Matchers",
+ "keywords": [
+ "test"
+ ],
+ "support": {
+ "issues": "https://github.com/hamcrest/hamcrest-php/issues",
+ "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.1.1"
+ },
+ "time": "2025-04-30T06:54:44+00:00"
+ },
{
"name": "kelunik/certificate",
"version": "v1.1.3",
@@ -3556,64 +3610,159 @@
"time": "2026-03-08T20:05:35+00:00"
},
{
- "name": "myclabs/deep-copy",
- "version": "1.13.4",
+ "name": "llm/skills",
+ "version": "1.9.0",
"source": {
"type": "git",
- "url": "https://github.com/myclabs/DeepCopy.git",
- "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a"
+ "url": "https://github.com/roxblnfk/skills.git",
+ "reference": "a3d0114a2d3db957071560e26fdb2c11e1b9103f"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a",
- "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a",
+ "url": "https://api.github.com/repos/roxblnfk/skills/zipball/a3d0114a2d3db957071560e26fdb2c11e1b9103f",
+ "reference": "a3d0114a2d3db957071560e26fdb2c11e1b9103f",
"shasum": ""
},
"require": {
- "php": "^7.1 || ^8.0"
+ "composer-plugin-api": "^2.0",
+ "internal/path": "^1.2",
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "buggregator/trap": "^1.10",
+ "composer/composer": "^2.9.0",
+ "infection/infection": "dev-testo-bridge-82 as 0.32.6",
+ "roxblnfk/unpoly": "^1.8",
+ "spiral/code-style": "^2.3",
+ "testo/bridge-infection": "0.1 - 1",
+ "testo/testo": "0.1 - 1",
+ "vimeo/psalm": "^7"
+ },
+ "type": "composer-plugin",
+ "extra": {
+ "class": "LLM\\Skills\\Composer\\SkillsPlugin"
+ },
+ "autoload": {
+ "psr-4": {
+ "LLM\\Skills\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Aleksei Gagarin",
+ "email": "roxblnfk@gmail.com"
+ }
+ ],
+ "description": "AI skills discovery and management system for LLM agents",
+ "keywords": [
+ "agents",
+ "ai",
+ "ai-agents",
+ "ai-skills",
+ "claude-code",
+ "dev",
+ "llm",
+ "llm-agents",
+ "skills"
+ ],
+ "support": {
+ "issues": "https://github.com/roxblnfk/skills/issues",
+ "source": "https://github.com/roxblnfk/skills"
+ },
+ "funding": [
+ {
+ "url": "https://boosty.to/roxblnfk",
+ "type": "boosty"
+ }
+ ],
+ "time": "2026-07-08T22:20:08+00:00"
+ },
+ {
+ "name": "mockery/mockery",
+ "version": "1.6.12",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/mockery/mockery.git",
+ "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/mockery/mockery/zipball/1f4efdd7d3beafe9807b08156dfcb176d18f1699",
+ "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699",
+ "shasum": ""
+ },
+ "require": {
+ "hamcrest/hamcrest-php": "^2.0.1",
+ "lib-pcre": ">=7.0",
+ "php": ">=7.3"
},
"conflict": {
- "doctrine/collections": "<1.6.8",
- "doctrine/common": "<2.13.3 || >=3 <3.2.2"
+ "phpunit/phpunit": "<8.0"
},
"require-dev": {
- "doctrine/collections": "^1.6.8",
- "doctrine/common": "^2.13.3 || ^3.2.2",
- "phpspec/prophecy": "^1.10",
- "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13"
+ "phpunit/phpunit": "^8.5 || ^9.6.17",
+ "symplify/easy-coding-standard": "^12.1.14"
},
"type": "library",
"autoload": {
"files": [
- "src/DeepCopy/deep_copy.php"
+ "library/helpers.php",
+ "library/Mockery.php"
],
"psr-4": {
- "DeepCopy\\": "src/DeepCopy/"
+ "Mockery\\": "library/Mockery"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "MIT"
+ "BSD-3-Clause"
],
- "description": "Create deep copies (clones) of your objects",
+ "authors": [
+ {
+ "name": "Pádraic Brady",
+ "email": "padraic.brady@gmail.com",
+ "homepage": "https://github.com/padraic",
+ "role": "Author"
+ },
+ {
+ "name": "Dave Marshall",
+ "email": "dave.marshall@atstsolutions.co.uk",
+ "homepage": "https://davedevelopment.co.uk",
+ "role": "Developer"
+ },
+ {
+ "name": "Nathanael Esayeas",
+ "email": "nathanael.esayeas@protonmail.com",
+ "homepage": "https://github.com/ghostwriter",
+ "role": "Lead Developer"
+ }
+ ],
+ "description": "Mockery is a simple yet flexible PHP mock object framework",
+ "homepage": "https://github.com/mockery/mockery",
"keywords": [
- "clone",
- "copy",
- "duplicate",
- "object",
- "object graph"
+ "BDD",
+ "TDD",
+ "library",
+ "mock",
+ "mock objects",
+ "mockery",
+ "stub",
+ "test",
+ "test double",
+ "testing"
],
"support": {
- "issues": "https://github.com/myclabs/DeepCopy/issues",
- "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4"
+ "docs": "https://docs.mockery.io/",
+ "issues": "https://github.com/mockery/mockery/issues",
+ "rss": "https://github.com/mockery/mockery/releases.atom",
+ "security": "https://github.com/mockery/mockery/security/advisories",
+ "source": "https://github.com/mockery/mockery"
},
- "funding": [
- {
- "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy",
- "type": "tidelift"
- }
- ],
- "time": "2025-08-01T08:46:24+00:00"
+ "time": "2024-05-16T03:13:13+00:00"
},
{
"name": "netresearch/jsonmapper",
@@ -3668,20 +3817,19 @@
},
{
"name": "nikic/php-parser",
- "version": "v5.7.0",
+ "version": "v5.8.0",
"source": {
"type": "git",
"url": "https://github.com/nikic/PHP-Parser.git",
- "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82"
+ "reference": "044a6a392ff8ad0d61f14370a5fbbd0a0107152f"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/dca41cd15c2ac9d055ad70dbfd011130757d1f82",
- "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82",
+ "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/044a6a392ff8ad0d61f14370a5fbbd0a0107152f",
+ "reference": "044a6a392ff8ad0d61f14370a5fbbd0a0107152f",
"shasum": ""
},
"require": {
- "ext-ctype": "*",
"ext-json": "*",
"ext-tokenizer": "*",
"php": ">=7.4"
@@ -3720,140 +3868,22 @@
],
"support": {
"issues": "https://github.com/nikic/PHP-Parser/issues",
- "source": "https://github.com/nikic/PHP-Parser/tree/v5.7.0"
- },
- "time": "2025-12-06T11:56:16+00:00"
- },
- {
- "name": "phar-io/manifest",
- "version": "2.0.4",
- "source": {
- "type": "git",
- "url": "https://github.com/phar-io/manifest.git",
- "reference": "54750ef60c58e43759730615a392c31c80e23176"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176",
- "reference": "54750ef60c58e43759730615a392c31c80e23176",
- "shasum": ""
- },
- "require": {
- "ext-dom": "*",
- "ext-libxml": "*",
- "ext-phar": "*",
- "ext-xmlwriter": "*",
- "phar-io/version": "^3.0.1",
- "php": "^7.2 || ^8.0"
+ "source": "https://github.com/nikic/PHP-Parser/tree/v5.8.0"
},
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "2.0.x-dev"
- }
- },
- "autoload": {
- "classmap": [
- "src/"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "BSD-3-Clause"
- ],
- "authors": [
- {
- "name": "Arne Blankerts",
- "email": "arne@blankerts.de",
- "role": "Developer"
- },
- {
- "name": "Sebastian Heuer",
- "email": "sebastian@phpeople.de",
- "role": "Developer"
- },
- {
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de",
- "role": "Developer"
- }
- ],
- "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)",
- "support": {
- "issues": "https://github.com/phar-io/manifest/issues",
- "source": "https://github.com/phar-io/manifest/tree/2.0.4"
- },
- "funding": [
- {
- "url": "https://github.com/theseer",
- "type": "github"
- }
- ],
- "time": "2024-03-03T12:33:53+00:00"
- },
- {
- "name": "phar-io/version",
- "version": "3.2.1",
- "source": {
- "type": "git",
- "url": "https://github.com/phar-io/version.git",
- "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74",
- "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74",
- "shasum": ""
- },
- "require": {
- "php": "^7.2 || ^8.0"
- },
- "type": "library",
- "autoload": {
- "classmap": [
- "src/"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "BSD-3-Clause"
- ],
- "authors": [
- {
- "name": "Arne Blankerts",
- "email": "arne@blankerts.de",
- "role": "Developer"
- },
- {
- "name": "Sebastian Heuer",
- "email": "sebastian@phpeople.de",
- "role": "Developer"
- },
- {
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de",
- "role": "Developer"
- }
- ],
- "description": "Library for handling version information and constraints",
- "support": {
- "issues": "https://github.com/phar-io/version/issues",
- "source": "https://github.com/phar-io/version/tree/3.2.1"
- },
- "time": "2022-02-21T01:04:05+00:00"
+ "time": "2026-07-04T14:30:18+00:00"
},
{
"name": "php-cs-fixer/shim",
- "version": "v3.95.2",
+ "version": "v3.95.18",
"source": {
"type": "git",
"url": "https://github.com/PHP-CS-Fixer/shim.git",
- "reference": "319bd80c8db64ab5f7b79a19178299045bdb9957"
+ "reference": "9b815f2ba5c581faaaec1386dcda4c16d511e6bb"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/PHP-CS-Fixer/shim/zipball/319bd80c8db64ab5f7b79a19178299045bdb9957",
- "reference": "319bd80c8db64ab5f7b79a19178299045bdb9957",
+ "url": "https://api.github.com/repos/PHP-CS-Fixer/shim/zipball/9b815f2ba5c581faaaec1386dcda4c16d511e6bb",
+ "reference": "9b815f2ba5c581faaaec1386dcda4c16d511e6bb",
"shasum": ""
},
"require": {
@@ -3890,9 +3920,9 @@
"description": "A tool to automatically fix PHP code style",
"support": {
"issues": "https://github.com/PHP-CS-Fixer/shim/issues",
- "source": "https://github.com/PHP-CS-Fixer/shim/tree/v3.95.2"
+ "source": "https://github.com/PHP-CS-Fixer/shim/tree/v3.95.18"
},
- "time": "2026-05-15T09:21:09+00:00"
+ "time": "2026-07-30T15:46:28+00:00"
},
{
"name": "php-http/message",
@@ -4141,16 +4171,16 @@
},
{
"name": "phpstan/phpdoc-parser",
- "version": "2.3.2",
+ "version": "2.3.3",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpdoc-parser.git",
- "reference": "a004701b11273a26cd7955a61d67a7f1e525a45a"
+ "reference": "fb19eedd2bb67ff8cf7a5502ad329e701d6398a3"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/a004701b11273a26cd7955a61d67a7f1e525a45a",
- "reference": "a004701b11273a26cd7955a61d67a7f1e525a45a",
+ "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/fb19eedd2bb67ff8cf7a5502ad329e701d6398a3",
+ "reference": "fb19eedd2bb67ff8cf7a5502ad329e701d6398a3",
"shasum": ""
},
"require": {
@@ -4182,452 +4212,196 @@
"description": "PHPDoc parser with support for nullable, intersection and generic types",
"support": {
"issues": "https://github.com/phpstan/phpdoc-parser/issues",
- "source": "https://github.com/phpstan/phpdoc-parser/tree/2.3.2"
+ "source": "https://github.com/phpstan/phpdoc-parser/tree/2.3.3"
},
- "time": "2026-01-25T14:56:51+00:00"
+ "time": "2026-07-08T07:01:06+00:00"
},
{
- "name": "phpunit/php-code-coverage",
- "version": "10.1.16",
- "source": {
- "type": "git",
- "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
- "reference": "7e308268858ed6baedc8704a304727d20bc07c77"
- },
+ "name": "phpstan/phpstan",
+ "version": "2.2.8",
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/7e308268858ed6baedc8704a304727d20bc07c77",
- "reference": "7e308268858ed6baedc8704a304727d20bc07c77",
+ "url": "https://api.github.com/repos/phpstan/phpstan/zipball/e285254e60f33c21902efef4a926ca0987c06804",
+ "reference": "e285254e60f33c21902efef4a926ca0987c06804",
"shasum": ""
},
"require": {
- "ext-dom": "*",
- "ext-libxml": "*",
- "ext-xmlwriter": "*",
- "nikic/php-parser": "^4.19.1 || ^5.1.0",
- "php": ">=8.1",
- "phpunit/php-file-iterator": "^4.1.0",
- "phpunit/php-text-template": "^3.0.1",
- "sebastian/code-unit-reverse-lookup": "^3.0.0",
- "sebastian/complexity": "^3.2.0",
- "sebastian/environment": "^6.1.0",
- "sebastian/lines-of-code": "^2.0.2",
- "sebastian/version": "^4.0.1",
- "theseer/tokenizer": "^1.2.3"
- },
- "require-dev": {
- "phpunit/phpunit": "^10.1"
+ "php": "^7.4|^8.0"
},
- "suggest": {
- "ext-pcov": "PHP extension that provides line coverage",
- "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage"
+ "conflict": {
+ "phpstan/phpstan-shim": "*"
},
+ "bin": [
+ "phpstan",
+ "phpstan.phar"
+ ],
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-main": "10.1.x-dev"
- }
- },
"autoload": {
- "classmap": [
- "src/"
+ "files": [
+ "bootstrap.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "BSD-3-Clause"
+ "MIT"
],
"authors": [
{
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de",
- "role": "lead"
+ "name": "Ondřej Mirtes"
+ },
+ {
+ "name": "Markus Staab"
+ },
+ {
+ "name": "Vincent Langlet"
}
],
- "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
- "homepage": "https://github.com/sebastianbergmann/php-code-coverage",
+ "description": "PHPStan - PHP Static Analysis Tool",
"keywords": [
- "coverage",
- "testing",
- "xunit"
+ "dev",
+ "static analysis"
],
"support": {
- "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
- "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
- "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.16"
+ "docs": "https://phpstan.org/user-guide/getting-started",
+ "forum": "https://github.com/phpstan/phpstan/discussions",
+ "issues": "https://github.com/phpstan/phpstan/issues",
+ "security": "https://github.com/phpstan/phpstan/security/policy",
+ "source": "https://github.com/phpstan/phpstan-src"
},
"funding": [
{
- "url": "https://github.com/sebastianbergmann",
+ "url": "https://github.com/ondrejmirtes",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/phpstan",
"type": "github"
}
],
- "time": "2024-08-22T04:31:57+00:00"
+ "time": "2026-08-04T22:21:45+00:00"
},
{
- "name": "phpunit/php-file-iterator",
- "version": "4.1.0",
+ "name": "psr/event-dispatcher",
+ "version": "1.0.0",
"source": {
"type": "git",
- "url": "https://github.com/sebastianbergmann/php-file-iterator.git",
- "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c"
+ "url": "https://github.com/php-fig/event-dispatcher.git",
+ "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/a95037b6d9e608ba092da1b23931e537cadc3c3c",
- "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c",
+ "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0",
+ "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0",
"shasum": ""
},
"require": {
- "php": ">=8.1"
- },
- "require-dev": {
- "phpunit/phpunit": "^10.0"
+ "php": ">=7.2.0"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "4.0-dev"
+ "dev-master": "1.0.x-dev"
}
},
"autoload": {
- "classmap": [
- "src/"
- ]
+ "psr-4": {
+ "Psr\\EventDispatcher\\": "src/"
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "BSD-3-Clause"
+ "MIT"
],
"authors": [
{
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de",
- "role": "lead"
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
}
],
- "description": "FilterIterator implementation that filters files based on a list of suffixes.",
- "homepage": "https://github.com/sebastianbergmann/php-file-iterator/",
+ "description": "Standard interfaces for event handling.",
"keywords": [
- "filesystem",
- "iterator"
+ "events",
+ "psr",
+ "psr-14"
],
"support": {
- "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues",
- "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy",
- "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/4.1.0"
+ "issues": "https://github.com/php-fig/event-dispatcher/issues",
+ "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0"
},
- "funding": [
- {
- "url": "https://github.com/sebastianbergmann",
- "type": "github"
- }
- ],
- "time": "2023-08-31T06:24:48+00:00"
+ "time": "2019-01-08T18:20:26+00:00"
},
{
- "name": "phpunit/php-invoker",
- "version": "4.0.0",
+ "name": "rector/rector",
+ "version": "2.6.1",
"source": {
"type": "git",
- "url": "https://github.com/sebastianbergmann/php-invoker.git",
- "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7"
+ "url": "https://github.com/rectorphp/rector.git",
+ "reference": "b8e68f058bca43e01a2e1caa51ef022d6551ed95"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7",
- "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7",
+ "url": "https://api.github.com/repos/rectorphp/rector/zipball/b8e68f058bca43e01a2e1caa51ef022d6551ed95",
+ "reference": "b8e68f058bca43e01a2e1caa51ef022d6551ed95",
"shasum": ""
},
"require": {
- "php": ">=8.1"
+ "php": "^7.4|^8.0",
+ "phpstan/phpstan": "^2.2.6"
},
- "require-dev": {
- "ext-pcntl": "*",
- "phpunit/phpunit": "^10.0"
- },
- "suggest": {
- "ext-pcntl": "*"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-main": "4.0-dev"
- }
- },
- "autoload": {
- "classmap": [
- "src/"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "BSD-3-Clause"
- ],
- "authors": [
- {
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de",
- "role": "lead"
- }
- ],
- "description": "Invoke callables with a timeout",
- "homepage": "https://github.com/sebastianbergmann/php-invoker/",
- "keywords": [
- "process"
- ],
- "support": {
- "issues": "https://github.com/sebastianbergmann/php-invoker/issues",
- "source": "https://github.com/sebastianbergmann/php-invoker/tree/4.0.0"
- },
- "funding": [
- {
- "url": "https://github.com/sebastianbergmann",
- "type": "github"
- }
- ],
- "time": "2023-02-03T06:56:09+00:00"
- },
- {
- "name": "phpunit/php-text-template",
- "version": "3.0.1",
- "source": {
- "type": "git",
- "url": "https://github.com/sebastianbergmann/php-text-template.git",
- "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/0c7b06ff49e3d5072f057eb1fa59258bf287a748",
- "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748",
- "shasum": ""
- },
- "require": {
- "php": ">=8.1"
- },
- "require-dev": {
- "phpunit/phpunit": "^10.0"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-main": "3.0-dev"
- }
- },
- "autoload": {
- "classmap": [
- "src/"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "BSD-3-Clause"
- ],
- "authors": [
- {
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de",
- "role": "lead"
- }
- ],
- "description": "Simple template engine.",
- "homepage": "https://github.com/sebastianbergmann/php-text-template/",
- "keywords": [
- "template"
- ],
- "support": {
- "issues": "https://github.com/sebastianbergmann/php-text-template/issues",
- "security": "https://github.com/sebastianbergmann/php-text-template/security/policy",
- "source": "https://github.com/sebastianbergmann/php-text-template/tree/3.0.1"
- },
- "funding": [
- {
- "url": "https://github.com/sebastianbergmann",
- "type": "github"
- }
- ],
- "time": "2023-08-31T14:07:24+00:00"
- },
- {
- "name": "phpunit/php-timer",
- "version": "6.0.0",
- "source": {
- "type": "git",
- "url": "https://github.com/sebastianbergmann/php-timer.git",
- "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/e2a2d67966e740530f4a3343fe2e030ffdc1161d",
- "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d",
- "shasum": ""
- },
- "require": {
- "php": ">=8.1"
- },
- "require-dev": {
- "phpunit/phpunit": "^10.0"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-main": "6.0-dev"
- }
- },
- "autoload": {
- "classmap": [
- "src/"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "BSD-3-Clause"
- ],
- "authors": [
- {
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de",
- "role": "lead"
- }
- ],
- "description": "Utility class for timing",
- "homepage": "https://github.com/sebastianbergmann/php-timer/",
- "keywords": [
- "timer"
- ],
- "support": {
- "issues": "https://github.com/sebastianbergmann/php-timer/issues",
- "source": "https://github.com/sebastianbergmann/php-timer/tree/6.0.0"
- },
- "funding": [
- {
- "url": "https://github.com/sebastianbergmann",
- "type": "github"
- }
- ],
- "time": "2023-02-03T06:57:52+00:00"
- },
- {
- "name": "phpunit/phpunit",
- "version": "10.5.63",
- "source": {
- "type": "git",
- "url": "https://github.com/sebastianbergmann/phpunit.git",
- "reference": "33198268dad71e926626b618f3ec3966661e4d90"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/33198268dad71e926626b618f3ec3966661e4d90",
- "reference": "33198268dad71e926626b618f3ec3966661e4d90",
- "shasum": ""
- },
- "require": {
- "ext-dom": "*",
- "ext-json": "*",
- "ext-libxml": "*",
- "ext-mbstring": "*",
- "ext-xml": "*",
- "ext-xmlwriter": "*",
- "myclabs/deep-copy": "^1.13.4",
- "phar-io/manifest": "^2.0.4",
- "phar-io/version": "^3.2.1",
- "php": ">=8.1",
- "phpunit/php-code-coverage": "^10.1.16",
- "phpunit/php-file-iterator": "^4.1.0",
- "phpunit/php-invoker": "^4.0.0",
- "phpunit/php-text-template": "^3.0.1",
- "phpunit/php-timer": "^6.0.0",
- "sebastian/cli-parser": "^2.0.1",
- "sebastian/code-unit": "^2.0.0",
- "sebastian/comparator": "^5.0.5",
- "sebastian/diff": "^5.1.1",
- "sebastian/environment": "^6.1.0",
- "sebastian/exporter": "^5.1.4",
- "sebastian/global-state": "^6.0.2",
- "sebastian/object-enumerator": "^5.0.0",
- "sebastian/recursion-context": "^5.0.1",
- "sebastian/type": "^4.0.0",
- "sebastian/version": "^4.0.1"
+ "conflict": {
+ "rector/rector-doctrine": "*",
+ "rector/rector-downgrade-php": "*",
+ "rector/rector-phpunit": "*",
+ "rector/rector-symfony": "*"
},
"suggest": {
- "ext-soap": "To be able to generate mocks based on WSDL files"
+ "ext-dom": "To manipulate phpunit.xml via the custom-rule command"
},
"bin": [
- "phpunit"
+ "bin/rector"
],
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-main": "10.5-dev"
- }
- },
"autoload": {
"files": [
- "src/Framework/Assert/Functions.php"
- ],
- "classmap": [
- "src/"
+ "bootstrap.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "BSD-3-Clause"
- ],
- "authors": [
- {
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de",
- "role": "lead"
- }
+ "MIT"
],
- "description": "The PHP Unit Testing framework.",
- "homepage": "https://phpunit.de/",
+ "description": "Instant Upgrade and Automated Refactoring of any PHP code",
+ "homepage": "https://getrector.com/",
"keywords": [
- "phpunit",
- "testing",
- "xunit"
+ "automation",
+ "dev",
+ "migration",
+ "refactoring"
],
"support": {
- "issues": "https://github.com/sebastianbergmann/phpunit/issues",
- "security": "https://github.com/sebastianbergmann/phpunit/security/policy",
- "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.63"
+ "issues": "https://github.com/rectorphp/rector/issues",
+ "source": "https://github.com/rectorphp/rector/tree/2.6.1"
},
"funding": [
{
- "url": "https://phpunit.de/sponsors.html",
- "type": "custom"
- },
- {
- "url": "https://github.com/sebastianbergmann",
+ "url": "https://github.com/tomasvotruba",
"type": "github"
- },
- {
- "url": "https://liberapay.com/sebastianbergmann",
- "type": "liberapay"
- },
- {
- "url": "https://thanks.dev/u/gh/sebastianbergmann",
- "type": "thanks_dev"
- },
- {
- "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit",
- "type": "tidelift"
}
],
- "time": "2026-01-27T05:48:37+00:00"
+ "time": "2026-08-03T17:30:34+00:00"
},
{
"name": "revolt/event-loop",
- "version": "v1.0.8",
+ "version": "v1.0.9",
"source": {
"type": "git",
"url": "https://github.com/revoltphp/event-loop.git",
- "reference": "b6fc06dce8e9b523c9946138fa5e62181934f91c"
+ "reference": "44061cf513e53c6200372fc935ac42271566295d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/revoltphp/event-loop/zipball/b6fc06dce8e9b523c9946138fa5e62181934f91c",
- "reference": "b6fc06dce8e9b523c9946138fa5e62181934f91c",
+ "url": "https://api.github.com/repos/revoltphp/event-loop/zipball/44061cf513e53c6200372fc935ac42271566295d",
+ "reference": "44061cf513e53c6200372fc935ac42271566295d",
"shasum": ""
},
"require": {
@@ -4637,7 +4411,7 @@
"ext-json": "*",
"jetbrains/phpstorm-stubs": "^2019.3",
"phpunit/phpunit": "^9",
- "psalm/phar": "^5.15"
+ "psalm/phar": "6.16.*"
},
"type": "library",
"extra": {
@@ -4684,35 +4458,34 @@
],
"support": {
"issues": "https://github.com/revoltphp/event-loop/issues",
- "source": "https://github.com/revoltphp/event-loop/tree/v1.0.8"
+ "source": "https://github.com/revoltphp/event-loop/tree/v1.0.9"
},
- "time": "2025-08-27T21:33:23+00:00"
+ "time": "2026-05-16T17:55:38+00:00"
},
{
"name": "roxblnfk/unpoly",
- "version": "1.8.1.1",
+ "version": "1.8.2",
"source": {
"type": "git",
"url": "https://github.com/roxblnfk/unpoly.git",
- "reference": "ec84dc56c97320acde5ab8875a39de36fdce36a9"
+ "reference": "680f8ed460513935aa896391402afe0e4660c3bf"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/roxblnfk/unpoly/zipball/ec84dc56c97320acde5ab8875a39de36fdce36a9",
- "reference": "ec84dc56c97320acde5ab8875a39de36fdce36a9",
+ "url": "https://api.github.com/repos/roxblnfk/unpoly/zipball/680f8ed460513935aa896391402afe0e4660c3bf",
+ "reference": "680f8ed460513935aa896391402afe0e4660c3bf",
"shasum": ""
},
"require": {
- "php": ">=8.1"
+ "php": ">=8.2"
},
"replace": {
- "symfony/polyfill-php70": "*",
- "symfony/polyfill-php71": "*",
"symfony/polyfill-php72": "*",
"symfony/polyfill-php73": "*",
"symfony/polyfill-php74": "*",
"symfony/polyfill-php80": "*",
- "symfony/polyfill-php81": "*"
+ "symfony/polyfill-php81": "*",
+ "symfony/polyfill-php82": "*"
},
"type": "metapackage",
"notification-url": "https://packagist.org/downloads/",
@@ -4734,7 +4507,7 @@
],
"support": {
"issues": "https://github.com/roxblnfk/unpoly/issues",
- "source": "https://github.com/roxblnfk/unpoly/tree/1.8.1.1"
+ "source": "https://github.com/roxblnfk/unpoly/tree/1.8.2"
},
"funding": [
{
@@ -4742,32 +4515,33 @@
"type": "patreon"
}
],
- "time": "2024-02-06T20:26:09+00:00"
+ "time": "2024-02-06T20:28:31+00:00"
},
{
- "name": "sebastian/cli-parser",
- "version": "2.0.1",
+ "name": "sebastian/diff",
+ "version": "6.0.2",
"source": {
"type": "git",
- "url": "https://github.com/sebastianbergmann/cli-parser.git",
- "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084"
+ "url": "https://github.com/sebastianbergmann/diff.git",
+ "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/c34583b87e7b7a8055bf6c450c2c77ce32a24084",
- "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084",
+ "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/b4ccd857127db5d41a5b676f24b51371d76d8544",
+ "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544",
"shasum": ""
},
"require": {
- "php": ">=8.1"
+ "php": ">=8.2"
},
"require-dev": {
- "phpunit/phpunit": "^10.0"
+ "phpunit/phpunit": "^11.0",
+ "symfony/process": "^4.2 || ^5"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "2.0-dev"
+ "dev-main": "6.0-dev"
}
},
"autoload": {
@@ -4782,16 +4556,25 @@
"authors": [
{
"name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de",
- "role": "lead"
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Kore Nordmann",
+ "email": "mail@kore-nordmann.de"
}
],
- "description": "Library for parsing CLI options",
- "homepage": "https://github.com/sebastianbergmann/cli-parser",
+ "description": "Diff implementation",
+ "homepage": "https://github.com/sebastianbergmann/diff",
+ "keywords": [
+ "diff",
+ "udiff",
+ "unidiff",
+ "unified diff"
+ ],
"support": {
- "issues": "https://github.com/sebastianbergmann/cli-parser/issues",
- "security": "https://github.com/sebastianbergmann/cli-parser/security/policy",
- "source": "https://github.com/sebastianbergmann/cli-parser/tree/2.0.1"
+ "issues": "https://github.com/sebastianbergmann/diff/issues",
+ "security": "https://github.com/sebastianbergmann/diff/security/policy",
+ "source": "https://github.com/sebastianbergmann/diff/tree/6.0.2"
},
"funding": [
{
@@ -4799,429 +4582,469 @@
"type": "github"
}
],
- "time": "2024-03-02T07:12:49+00:00"
+ "time": "2024-07-03T04:53:05+00:00"
},
{
- "name": "sebastian/code-unit",
- "version": "2.0.0",
+ "name": "spatie/array-to-xml",
+ "version": "3.4.4",
"source": {
"type": "git",
- "url": "https://github.com/sebastianbergmann/code-unit.git",
- "reference": "a81fee9eef0b7a76af11d121767abc44c104e503"
+ "url": "https://github.com/spatie/array-to-xml.git",
+ "reference": "88b2f3852a922dd73177a68938f8eb2ec70c7224"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/a81fee9eef0b7a76af11d121767abc44c104e503",
- "reference": "a81fee9eef0b7a76af11d121767abc44c104e503",
+ "url": "https://api.github.com/repos/spatie/array-to-xml/zipball/88b2f3852a922dd73177a68938f8eb2ec70c7224",
+ "reference": "88b2f3852a922dd73177a68938f8eb2ec70c7224",
"shasum": ""
},
"require": {
- "php": ">=8.1"
+ "ext-dom": "*",
+ "php": "^8.0"
},
"require-dev": {
- "phpunit/phpunit": "^10.0"
+ "mockery/mockery": "^1.2",
+ "pestphp/pest": "^1.21",
+ "spatie/pest-plugin-snapshots": "^1.1"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "2.0-dev"
+ "dev-main": "3.x-dev"
}
},
"autoload": {
- "classmap": [
- "src/"
- ]
+ "psr-4": {
+ "Spatie\\ArrayToXml\\": "src"
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "BSD-3-Clause"
+ "MIT"
],
"authors": [
{
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de",
- "role": "lead"
+ "name": "Freek Van der Herten",
+ "email": "freek@spatie.be",
+ "homepage": "https://freek.dev",
+ "role": "Developer"
}
],
- "description": "Collection of value objects that represent the PHP code units",
- "homepage": "https://github.com/sebastianbergmann/code-unit",
+ "description": "Convert an array to xml",
+ "homepage": "https://github.com/spatie/array-to-xml",
+ "keywords": [
+ "array",
+ "convert",
+ "xml"
+ ],
"support": {
- "issues": "https://github.com/sebastianbergmann/code-unit/issues",
- "source": "https://github.com/sebastianbergmann/code-unit/tree/2.0.0"
+ "source": "https://github.com/spatie/array-to-xml/tree/3.4.4"
},
"funding": [
{
- "url": "https://github.com/sebastianbergmann",
+ "url": "https://spatie.be/open-source/support-us",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/spatie",
"type": "github"
}
],
- "time": "2023-02-03T06:58:43+00:00"
+ "time": "2025-12-15T09:00:41+00:00"
},
{
- "name": "sebastian/code-unit-reverse-lookup",
- "version": "3.0.0",
+ "name": "spiral/code-style",
+ "version": "v2.3.1",
"source": {
"type": "git",
- "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git",
- "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d"
+ "url": "https://github.com/spiral/code-style.git",
+ "reference": "a0407ffcb300c9d2977cb3d1c36af0d13b2ffb72"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/5e3a687f7d8ae33fb362c5c0743794bbb2420a1d",
- "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d",
+ "url": "https://api.github.com/repos/spiral/code-style/zipball/a0407ffcb300c9d2977cb3d1c36af0d13b2ffb72",
+ "reference": "a0407ffcb300c9d2977cb3d1c36af0d13b2ffb72",
"shasum": ""
},
"require": {
- "php": ">=8.1"
+ "php": ">=8.0",
+ "php-cs-fixer/shim": "^3.64"
},
"require-dev": {
- "phpunit/phpunit": "^10.0"
+ "phpunit/phpunit": "^10.5",
+ "spiral/dumper": "^3.3",
+ "vimeo/psalm": "^5.26"
},
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-main": "3.0-dev"
- }
- },
"autoload": {
- "classmap": [
- "src/"
- ]
+ "psr-4": {
+ "Spiral\\CodeStyle\\": "src/"
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "BSD-3-Clause"
+ "MIT"
],
"authors": [
{
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de"
+ "name": "Aleksandr Novikov",
+ "email": "aleksandr.novikov@spiralscout.com"
+ },
+ {
+ "name": "Aleksei Gagarin",
+ "email": "alexey.gagarin@spiralscout.com"
}
],
- "description": "Looks up which function or method a line of code belongs to",
- "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/",
+ "description": "Code style and static analysis tools rulesets collection",
+ "homepage": "https://github.com/spiral/code-style",
"support": {
- "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues",
- "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/3.0.0"
+ "source": "https://github.com/spiral/code-style/tree/v2.3.1"
},
"funding": [
{
- "url": "https://github.com/sebastianbergmann",
+ "url": "https://github.com/sponsors/spiral",
"type": "github"
}
],
- "time": "2023-02-03T06:59:15+00:00"
+ "time": "2025-10-06T17:24:48+00:00"
},
{
- "name": "sebastian/comparator",
- "version": "5.0.5",
+ "name": "symfony/filesystem",
+ "version": "v7.4.15",
"source": {
"type": "git",
- "url": "https://github.com/sebastianbergmann/comparator.git",
- "reference": "55dfef806eb7dfeb6e7a6935601fef866f8ca48d"
+ "url": "https://github.com/symfony/filesystem.git",
+ "reference": "ff16a16bf87fdf264638b8f6995b3515975e3c79"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/55dfef806eb7dfeb6e7a6935601fef866f8ca48d",
- "reference": "55dfef806eb7dfeb6e7a6935601fef866f8ca48d",
+ "url": "https://api.github.com/repos/symfony/filesystem/zipball/ff16a16bf87fdf264638b8f6995b3515975e3c79",
+ "reference": "ff16a16bf87fdf264638b8f6995b3515975e3c79",
"shasum": ""
},
"require": {
- "ext-dom": "*",
- "ext-mbstring": "*",
- "php": ">=8.1",
- "sebastian/diff": "^5.0",
- "sebastian/exporter": "^5.0"
+ "php": ">=8.2",
+ "symfony/polyfill-ctype": "~1.8",
+ "symfony/polyfill-mbstring": "~1.8"
},
"require-dev": {
- "phpunit/phpunit": "^10.5"
+ "symfony/process": "^6.4|^7.0|^8.0"
},
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-main": "5.0-dev"
- }
- },
"autoload": {
- "classmap": [
- "src/"
+ "psr-4": {
+ "Symfony\\Component\\Filesystem\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "BSD-3-Clause"
+ "MIT"
],
"authors": [
{
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de"
- },
- {
- "name": "Jeff Welch",
- "email": "whatthejeff@gmail.com"
- },
- {
- "name": "Volker Dusch",
- "email": "github@wallbash.com"
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
},
{
- "name": "Bernhard Schussek",
- "email": "bschussek@2bepublished.at"
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
}
],
- "description": "Provides the functionality to compare PHP values for equality",
- "homepage": "https://github.com/sebastianbergmann/comparator",
- "keywords": [
- "comparator",
- "compare",
- "equality"
- ],
+ "description": "Provides basic utilities for the filesystem",
+ "homepage": "https://symfony.com",
"support": {
- "issues": "https://github.com/sebastianbergmann/comparator/issues",
- "security": "https://github.com/sebastianbergmann/comparator/security/policy",
- "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.5"
+ "source": "https://github.com/symfony/filesystem/tree/v7.4.15"
},
"funding": [
{
- "url": "https://github.com/sebastianbergmann",
- "type": "github"
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
},
{
- "url": "https://liberapay.com/sebastianbergmann",
- "type": "liberapay"
+ "url": "https://github.com/fabpot",
+ "type": "github"
},
{
- "url": "https://thanks.dev/u/gh/sebastianbergmann",
- "type": "thanks_dev"
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
},
{
- "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator",
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
- "time": "2026-01-24T09:25:16+00:00"
+ "time": "2026-07-22T07:36:05+00:00"
},
{
- "name": "sebastian/complexity",
- "version": "3.2.0",
+ "name": "symfony/finder",
+ "version": "v7.4.14",
"source": {
"type": "git",
- "url": "https://github.com/sebastianbergmann/complexity.git",
- "reference": "68ff824baeae169ec9f2137158ee529584553799"
+ "url": "https://github.com/symfony/finder.git",
+ "reference": "13b38720174286f55d1761152b575a8d1436fc25"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68ff824baeae169ec9f2137158ee529584553799",
- "reference": "68ff824baeae169ec9f2137158ee529584553799",
+ "url": "https://api.github.com/repos/symfony/finder/zipball/13b38720174286f55d1761152b575a8d1436fc25",
+ "reference": "13b38720174286f55d1761152b575a8d1436fc25",
"shasum": ""
},
"require": {
- "nikic/php-parser": "^4.18 || ^5.0",
- "php": ">=8.1"
+ "php": ">=8.2"
},
"require-dev": {
- "phpunit/phpunit": "^10.0"
+ "symfony/filesystem": "^6.4|^7.0|^8.0"
},
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-main": "3.2-dev"
- }
- },
"autoload": {
- "classmap": [
- "src/"
+ "psr-4": {
+ "Symfony\\Component\\Finder\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "BSD-3-Clause"
+ "MIT"
],
"authors": [
{
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de",
- "role": "lead"
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
}
],
- "description": "Library for calculating the complexity of PHP code units",
- "homepage": "https://github.com/sebastianbergmann/complexity",
+ "description": "Finds files and directories via an intuitive fluent interface",
+ "homepage": "https://symfony.com",
"support": {
- "issues": "https://github.com/sebastianbergmann/complexity/issues",
- "security": "https://github.com/sebastianbergmann/complexity/security/policy",
- "source": "https://github.com/sebastianbergmann/complexity/tree/3.2.0"
+ "source": "https://github.com/symfony/finder/tree/v7.4.14"
},
"funding": [
{
- "url": "https://github.com/sebastianbergmann",
+ "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": "2023-12-21T08:37:17+00:00"
+ "time": "2026-06-27T08:31:18+00:00"
},
{
- "name": "sebastian/diff",
- "version": "5.1.1",
+ "name": "symfony/polyfill-php84",
+ "version": "v1.38.1",
"source": {
"type": "git",
- "url": "https://github.com/sebastianbergmann/diff.git",
- "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e"
+ "url": "https://github.com/symfony/polyfill-php84.git",
+ "reference": "f4e1dfaee5b74aba5964fe1fd4dfc7ba5e3085fa"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/c41e007b4b62af48218231d6c2275e4c9b975b2e",
- "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e",
+ "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/f4e1dfaee5b74aba5964fe1fd4dfc7ba5e3085fa",
+ "reference": "f4e1dfaee5b74aba5964fe1fd4dfc7ba5e3085fa",
"shasum": ""
},
"require": {
- "php": ">=8.1"
- },
- "require-dev": {
- "phpunit/phpunit": "^10.0",
- "symfony/process": "^6.4"
+ "php": ">=7.2"
},
"type": "library",
"extra": {
- "branch-alias": {
- "dev-main": "5.1-dev"
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
}
},
"autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Php84\\": ""
+ },
"classmap": [
- "src/"
+ "Resources/stubs"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "BSD-3-Clause"
+ "MIT"
],
"authors": [
{
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de"
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
},
{
- "name": "Kore Nordmann",
- "email": "mail@kore-nordmann.de"
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
}
],
- "description": "Diff implementation",
- "homepage": "https://github.com/sebastianbergmann/diff",
+ "description": "Symfony polyfill backporting some PHP 8.4+ features to lower PHP versions",
+ "homepage": "https://symfony.com",
"keywords": [
- "diff",
- "udiff",
- "unidiff",
- "unified diff"
+ "compatibility",
+ "polyfill",
+ "portable",
+ "shim"
],
"support": {
- "issues": "https://github.com/sebastianbergmann/diff/issues",
- "security": "https://github.com/sebastianbergmann/diff/security/policy",
- "source": "https://github.com/sebastianbergmann/diff/tree/5.1.1"
+ "source": "https://github.com/symfony/polyfill-php84/tree/v1.38.1"
},
"funding": [
{
- "url": "https://github.com/sebastianbergmann",
+ "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": "2024-03-02T07:15:17+00:00"
+ "time": "2026-05-26T12:51:13+00:00"
},
{
- "name": "sebastian/environment",
- "version": "6.1.0",
+ "name": "symfony/var-dumper",
+ "version": "v7.4.15",
"source": {
"type": "git",
- "url": "https://github.com/sebastianbergmann/environment.git",
- "reference": "8074dbcd93529b357029f5cc5058fd3e43666984"
+ "url": "https://github.com/symfony/var-dumper.git",
+ "reference": "04ba4add636a95ff437af3a5a9499bb1d6c6d4bd"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/8074dbcd93529b357029f5cc5058fd3e43666984",
- "reference": "8074dbcd93529b357029f5cc5058fd3e43666984",
+ "url": "https://api.github.com/repos/symfony/var-dumper/zipball/04ba4add636a95ff437af3a5a9499bb1d6c6d4bd",
+ "reference": "04ba4add636a95ff437af3a5a9499bb1d6c6d4bd",
"shasum": ""
},
"require": {
- "php": ">=8.1"
+ "php": ">=8.2",
+ "symfony/deprecation-contracts": "^2.5|^3",
+ "symfony/polyfill-mbstring": "~1.0"
},
- "require-dev": {
- "phpunit/phpunit": "^10.0"
+ "conflict": {
+ "symfony/console": "<6.4"
},
- "suggest": {
- "ext-posix": "*"
+ "require-dev": {
+ "symfony/console": "^6.4|^7.0|^8.0",
+ "symfony/http-kernel": "^6.4|^7.0|^8.0",
+ "symfony/process": "^6.4|^7.0|^8.0",
+ "symfony/uid": "^6.4|^7.0|^8.0",
+ "twig/twig": "^3.12|^4.0"
},
+ "bin": [
+ "Resources/bin/var-dump-server"
+ ],
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-main": "6.1-dev"
- }
- },
"autoload": {
- "classmap": [
- "src/"
+ "files": [
+ "Resources/functions/dump.php"
+ ],
+ "psr-4": {
+ "Symfony\\Component\\VarDumper\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "BSD-3-Clause"
+ "MIT"
],
"authors": [
{
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de"
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
}
],
- "description": "Provides functionality to handle HHVM/PHP environments",
- "homepage": "https://github.com/sebastianbergmann/environment",
+ "description": "Provides mechanisms for walking through any arbitrary PHP variable",
+ "homepage": "https://symfony.com",
"keywords": [
- "Xdebug",
- "environment",
- "hhvm"
+ "debug",
+ "dump"
],
"support": {
- "issues": "https://github.com/sebastianbergmann/environment/issues",
- "security": "https://github.com/sebastianbergmann/environment/security/policy",
- "source": "https://github.com/sebastianbergmann/environment/tree/6.1.0"
+ "source": "https://github.com/symfony/var-dumper/tree/v7.4.15"
},
"funding": [
{
- "url": "https://github.com/sebastianbergmann",
+ "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": "2024-03-23T08:47:14+00:00"
+ "time": "2026-07-21T15:13:06+00:00"
},
{
- "name": "sebastian/exporter",
- "version": "5.1.4",
+ "name": "testo/assert",
+ "version": "0.1.12",
"source": {
"type": "git",
- "url": "https://github.com/sebastianbergmann/exporter.git",
- "reference": "0735b90f4da94969541dac1da743446e276defa6"
+ "url": "https://github.com/php-testo/assert.git",
+ "reference": "c53eda41f1546e6ccf2ab306831530150c4844cf"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/0735b90f4da94969541dac1da743446e276defa6",
- "reference": "0735b90f4da94969541dac1da743446e276defa6",
+ "url": "https://api.github.com/repos/php-testo/assert/zipball/c53eda41f1546e6ccf2ab306831530150c4844cf",
+ "reference": "c53eda41f1546e6ccf2ab306831530150c4844cf",
"shasum": ""
},
"require": {
- "ext-mbstring": "*",
- "php": ">=8.1",
- "sebastian/recursion-context": "^5.0"
- },
- "require-dev": {
- "phpunit/phpunit": "^10.5"
+ "php": ">=8.2",
+ "testo/testo": "0.10.39 - 1"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "5.1-dev"
+ "dev-1.x": "1.x-dev"
}
},
"autoload": {
- "classmap": [
- "src/"
- ]
+ "files": [
+ "Assert.php",
+ "Expect.php"
+ ],
+ "psr-4": {
+ "Testo\\Assert\\": "src/"
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@@ -5229,90 +5052,60 @@
],
"authors": [
{
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de"
- },
- {
- "name": "Jeff Welch",
- "email": "whatthejeff@gmail.com"
- },
- {
- "name": "Volker Dusch",
- "email": "github@wallbash.com"
- },
- {
- "name": "Adam Harvey",
- "email": "aharvey@php.net"
- },
- {
- "name": "Bernhard Schussek",
- "email": "bschussek@gmail.com"
+ "name": "Aleksei Gagarin (roxblnfk)",
+ "homepage": "https://github.com/roxblnfk"
}
],
- "description": "Provides the functionality to export PHP variables for visualization",
- "homepage": "https://www.github.com/sebastianbergmann/exporter",
+ "description": "Assert plugin for the Testo testing framework.",
"keywords": [
- "export",
- "exporter"
+ "assert",
+ "testo"
],
"support": {
- "issues": "https://github.com/sebastianbergmann/exporter/issues",
- "security": "https://github.com/sebastianbergmann/exporter/security/policy",
- "source": "https://github.com/sebastianbergmann/exporter/tree/5.1.4"
+ "source": "https://github.com/php-testo/assert/tree/0.1.12"
},
"funding": [
{
- "url": "https://github.com/sebastianbergmann",
- "type": "github"
- },
- {
- "url": "https://liberapay.com/sebastianbergmann",
- "type": "liberapay"
- },
- {
- "url": "https://thanks.dev/u/gh/sebastianbergmann",
- "type": "thanks_dev"
- },
- {
- "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter",
- "type": "tidelift"
+ "url": "https://boosty.to/roxblnfk",
+ "type": "boosty"
}
],
- "time": "2025-09-24T06:09:11+00:00"
+ "time": "2026-08-06T07:43:13+00:00"
},
{
- "name": "sebastian/global-state",
- "version": "6.0.2",
+ "name": "testo/bench",
+ "version": "0.1.8",
"source": {
"type": "git",
- "url": "https://github.com/sebastianbergmann/global-state.git",
- "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9"
+ "url": "https://github.com/php-testo/bench.git",
+ "reference": "d1219cda0a31085f654893a79deaadaef74c04b2"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/987bafff24ecc4c9ac418cab1145b96dd6e9cbd9",
- "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9",
+ "url": "https://api.github.com/repos/php-testo/bench/zipball/d1219cda0a31085f654893a79deaadaef74c04b2",
+ "reference": "d1219cda0a31085f654893a79deaadaef74c04b2",
"shasum": ""
},
"require": {
- "php": ">=8.1",
- "sebastian/object-reflector": "^3.0",
- "sebastian/recursion-context": "^5.0"
- },
- "require-dev": {
- "ext-dom": "*",
- "phpunit/phpunit": "^10.0"
+ "php": ">=8.2",
+ "testo/data": "^0.1.7",
+ "testo/filter": "^0.1.6",
+ "testo/inline": "^0.1.8",
+ "testo/testo": "0.10.40 - 1"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "6.0-dev"
+ "dev-1.x": "1.x-dev"
}
},
"autoload": {
- "classmap": [
- "src/"
- ]
+ "files": [
+ "Bench.php"
+ ],
+ "psr-4": {
+ "Testo\\Bench\\": "src/"
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@@ -5320,59 +5113,60 @@
],
"authors": [
{
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de"
+ "name": "Aleksei Gagarin (roxblnfk)",
+ "homepage": "https://github.com/roxblnfk"
}
],
- "description": "Snapshotting of global state",
- "homepage": "https://www.github.com/sebastianbergmann/global-state",
+ "description": "Benchmark plugin for the Testo testing framework.",
"keywords": [
- "global state"
+ "benchmark",
+ "testo"
],
"support": {
- "issues": "https://github.com/sebastianbergmann/global-state/issues",
- "security": "https://github.com/sebastianbergmann/global-state/security/policy",
- "source": "https://github.com/sebastianbergmann/global-state/tree/6.0.2"
+ "source": "https://github.com/php-testo/bench/tree/0.1.8"
},
"funding": [
{
- "url": "https://github.com/sebastianbergmann",
- "type": "github"
+ "url": "https://boosty.to/roxblnfk",
+ "type": "boosty"
}
],
- "time": "2024-03-02T07:19:19+00:00"
+ "time": "2026-08-10T12:39:35+00:00"
},
{
- "name": "sebastian/lines-of-code",
- "version": "2.0.2",
+ "name": "testo/bridge-mockery",
+ "version": "0.1.2",
"source": {
"type": "git",
- "url": "https://github.com/sebastianbergmann/lines-of-code.git",
- "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0"
+ "url": "https://github.com/php-testo/bridge-mockery.git",
+ "reference": "24f0c6f102ec2014a8e69af90c5e4c37af34af4a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/856e7f6a75a84e339195d48c556f23be2ebf75d0",
- "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0",
+ "url": "https://api.github.com/repos/php-testo/bridge-mockery/zipball/24f0c6f102ec2014a8e69af90c5e4c37af34af4a",
+ "reference": "24f0c6f102ec2014a8e69af90c5e4c37af34af4a",
"shasum": ""
},
"require": {
- "nikic/php-parser": "^4.18 || ^5.0",
- "php": ">=8.1"
+ "mockery/mockery": "^1.6",
+ "php": ">=8.2",
+ "testo/testo": "0.10.39 - 1"
},
"require-dev": {
- "phpunit/phpunit": "^10.0"
+ "testo/assert": "^0.1.12",
+ "testo/codecov": "^0.1.12",
+ "testo/test": "^0.1.6"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "2.0-dev"
+ "dev-1.x": "1.x-dev"
}
},
"autoload": {
- "classmap": [
- "src/"
- ]
+ "psr-4": {
+ "Testo\\Bridge\\Mockery\\": "src/"
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@@ -5380,58 +5174,66 @@
],
"authors": [
{
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de",
- "role": "lead"
+ "name": "Aleksei Gagarin (roxblnfk)",
+ "homepage": "https://github.com/roxblnfk"
}
],
- "description": "Library for counting the lines of code in PHP source code",
- "homepage": "https://github.com/sebastianbergmann/lines-of-code",
+ "description": "Mockery bridge for the Testo testing framework.",
+ "keywords": [
+ "mock",
+ "mockery",
+ "testing",
+ "testo"
+ ],
"support": {
- "issues": "https://github.com/sebastianbergmann/lines-of-code/issues",
- "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy",
- "source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.2"
+ "source": "https://github.com/php-testo/bridge-mockery/tree/0.1.2"
},
"funding": [
{
- "url": "https://github.com/sebastianbergmann",
- "type": "github"
+ "url": "https://boosty.to/roxblnfk",
+ "type": "boosty"
}
],
- "time": "2023-12-21T08:38:20+00:00"
+ "time": "2026-08-06T07:43:13+00:00"
},
{
- "name": "sebastian/object-enumerator",
- "version": "5.0.0",
+ "name": "testo/bridge-rector",
+ "version": "0.2.2",
"source": {
"type": "git",
- "url": "https://github.com/sebastianbergmann/object-enumerator.git",
- "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906"
+ "url": "https://github.com/php-testo/bridge-rector.git",
+ "reference": "c392c434bcf13c4a1ccce986f6002e125075fdad"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/202d0e344a580d7f7d04b3fafce6933e59dae906",
- "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906",
+ "url": "https://api.github.com/repos/php-testo/bridge-rector/zipball/c392c434bcf13c4a1ccce986f6002e125075fdad",
+ "reference": "c392c434bcf13c4a1ccce986f6002e125075fdad",
"shasum": ""
},
"require": {
- "php": ">=8.1",
- "sebastian/object-reflector": "^3.0",
- "sebastian/recursion-context": "^5.0"
+ "internal/path": "^1.2",
+ "php": ">=8.2",
+ "rector/rector": "^2.0"
},
"require-dev": {
- "phpunit/phpunit": "^10.0"
+ "testo/assert": "^0.1.12",
+ "testo/data": "^0.1.7",
+ "testo/filter": "^0.1.6",
+ "testo/testo": "0.10.40 - 1"
+ },
+ "suggest": {
+ "testo/testo": "To test your Rector rules inline with the bundled Testo\\Bridge\\Rector\\Testing harness."
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "5.0-dev"
+ "dev-1.x": "1.x-dev"
}
},
"autoload": {
- "classmap": [
- "src/"
- ]
+ "psr-4": {
+ "Testo\\Bridge\\Rector\\": "src/"
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@@ -5439,54 +5241,61 @@
],
"authors": [
{
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de"
+ "name": "Aleksei Gagarin (roxblnfk)",
+ "homepage": "https://github.com/roxblnfk"
}
],
- "description": "Traverses array structures and object graphs to enumerate all referenced objects",
- "homepage": "https://github.com/sebastianbergmann/object-enumerator/",
+ "description": "Rector rules to convert test suites between PEST, PHPUnit and Testo.",
+ "keywords": [
+ "migration",
+ "pest",
+ "phpunit",
+ "rector",
+ "testo"
+ ],
"support": {
- "issues": "https://github.com/sebastianbergmann/object-enumerator/issues",
- "source": "https://github.com/sebastianbergmann/object-enumerator/tree/5.0.0"
+ "source": "https://github.com/php-testo/bridge-rector/tree/0.2.2"
},
"funding": [
{
- "url": "https://github.com/sebastianbergmann",
- "type": "github"
+ "url": "https://boosty.to/roxblnfk",
+ "type": "boosty"
}
],
- "time": "2023-02-03T07:08:32+00:00"
+ "time": "2026-08-10T12:39:37+00:00"
},
{
- "name": "sebastian/object-reflector",
- "version": "3.0.0",
+ "name": "testo/bridge-symfony-console",
+ "version": "0.1.8",
"source": {
"type": "git",
- "url": "https://github.com/sebastianbergmann/object-reflector.git",
- "reference": "24ed13d98130f0e7122df55d06c5c4942a577957"
+ "url": "https://github.com/php-testo/bridge-symfony-console.git",
+ "reference": "9927456f43b4c01c839d7e24efd7c3c8fa60e68d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/24ed13d98130f0e7122df55d06c5c4942a577957",
- "reference": "24ed13d98130f0e7122df55d06c5c4942a577957",
+ "url": "https://api.github.com/repos/php-testo/bridge-symfony-console/zipball/9927456f43b4c01c839d7e24efd7c3c8fa60e68d",
+ "reference": "9927456f43b4c01c839d7e24efd7c3c8fa60e68d",
"shasum": ""
},
"require": {
- "php": ">=8.1"
- },
- "require-dev": {
- "phpunit/phpunit": "^10.0"
+ "php": ">=8.2",
+ "symfony/console": "^6.4 || ^7 || ^8.0",
+ "testo/testo": "0.10.33 - 1"
},
+ "bin": [
+ "bin/testo"
+ ],
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "3.0-dev"
+ "dev-1.x": "1.x-dev"
}
},
"autoload": {
- "classmap": [
- "src/"
- ]
+ "psr-4": {
+ "Testo\\Bridge\\Symfony\\Console\\": "src/"
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@@ -5494,54 +5303,58 @@
],
"authors": [
{
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de"
+ "name": "Aleksei Gagarin (roxblnfk)",
+ "homepage": "https://github.com/roxblnfk"
}
],
- "description": "Allows reflection of object attributes, including inherited and non-public ones",
- "homepage": "https://github.com/sebastianbergmann/object-reflector/",
+ "description": "Symfony Console bridge for the Testo testing framework.",
+ "keywords": [
+ "console",
+ "symfony",
+ "testo"
+ ],
"support": {
- "issues": "https://github.com/sebastianbergmann/object-reflector/issues",
- "source": "https://github.com/sebastianbergmann/object-reflector/tree/3.0.0"
+ "source": "https://github.com/php-testo/bridge-symfony-console/tree/0.1.8"
},
"funding": [
{
- "url": "https://github.com/sebastianbergmann",
- "type": "github"
+ "url": "https://boosty.to/roxblnfk",
+ "type": "boosty"
}
],
- "time": "2023-02-03T07:06:18+00:00"
+ "time": "2026-06-29T20:42:34+00:00"
},
{
- "name": "sebastian/recursion-context",
- "version": "5.0.1",
+ "name": "testo/codecov",
+ "version": "0.1.12",
"source": {
"type": "git",
- "url": "https://github.com/sebastianbergmann/recursion-context.git",
- "reference": "47e34210757a2f37a97dcd207d032e1b01e64c7a"
+ "url": "https://github.com/php-testo/codecov.git",
+ "reference": "6e82b139788739fab2a94e52b56b0b26194c11ba"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/47e34210757a2f37a97dcd207d032e1b01e64c7a",
- "reference": "47e34210757a2f37a97dcd207d032e1b01e64c7a",
+ "url": "https://api.github.com/repos/php-testo/codecov/zipball/6e82b139788739fab2a94e52b56b0b26194c11ba",
+ "reference": "6e82b139788739fab2a94e52b56b0b26194c11ba",
"shasum": ""
},
"require": {
- "php": ">=8.1"
- },
- "require-dev": {
- "phpunit/phpunit": "^10.5"
+ "ext-xmlwriter": "*",
+ "php": ">=8.2",
+ "testo/data": "^0.1.7",
+ "testo/inline": "^0.1.7",
+ "testo/testo": "0.10.39 - 1"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "5.0-dev"
+ "dev-1.x": "1.x-dev"
}
},
"autoload": {
- "classmap": [
- "src/"
- ]
+ "psr-4": {
+ "Testo\\Codecov\\": "src/"
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@@ -5549,75 +5362,54 @@
],
"authors": [
{
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de"
- },
- {
- "name": "Jeff Welch",
- "email": "whatthejeff@gmail.com"
- },
- {
- "name": "Adam Harvey",
- "email": "aharvey@php.net"
+ "name": "Aleksei Gagarin (roxblnfk)",
+ "homepage": "https://github.com/roxblnfk"
}
],
- "description": "Provides functionality to recursively process PHP variables",
- "homepage": "https://github.com/sebastianbergmann/recursion-context",
+ "description": "Code coverage plugin for the Testo testing framework.",
+ "keywords": [
+ "code-coverage",
+ "testo"
+ ],
"support": {
- "issues": "https://github.com/sebastianbergmann/recursion-context/issues",
- "security": "https://github.com/sebastianbergmann/recursion-context/security/policy",
- "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.1"
+ "source": "https://github.com/php-testo/codecov/tree/0.1.12"
},
"funding": [
{
- "url": "https://github.com/sebastianbergmann",
- "type": "github"
- },
- {
- "url": "https://liberapay.com/sebastianbergmann",
- "type": "liberapay"
- },
- {
- "url": "https://thanks.dev/u/gh/sebastianbergmann",
- "type": "thanks_dev"
- },
- {
- "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context",
- "type": "tidelift"
+ "url": "https://boosty.to/roxblnfk",
+ "type": "boosty"
}
],
- "time": "2025-08-10T07:50:56+00:00"
+ "time": "2026-08-06T07:45:32+00:00"
},
{
- "name": "sebastian/type",
- "version": "4.0.0",
+ "name": "testo/convention",
+ "version": "0.1.4",
"source": {
"type": "git",
- "url": "https://github.com/sebastianbergmann/type.git",
- "reference": "462699a16464c3944eefc02ebdd77882bd3925bf"
+ "url": "https://github.com/php-testo/convention.git",
+ "reference": "ad33205f07e29030aaf4d776fa2c068445971b46"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/462699a16464c3944eefc02ebdd77882bd3925bf",
- "reference": "462699a16464c3944eefc02ebdd77882bd3925bf",
+ "url": "https://api.github.com/repos/php-testo/convention/zipball/ad33205f07e29030aaf4d776fa2c068445971b46",
+ "reference": "ad33205f07e29030aaf4d776fa2c068445971b46",
"shasum": ""
},
"require": {
- "php": ">=8.1"
- },
- "require-dev": {
- "phpunit/phpunit": "^10.0"
+ "php": ">=8.2",
+ "testo/testo": "0.10.34 - 1"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "4.0-dev"
+ "dev-1.x": "1.x-dev"
}
},
"autoload": {
- "classmap": [
- "src/"
- ]
+ "psr-4": {
+ "Testo\\Convention\\": "src/"
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@@ -5625,52 +5417,55 @@
],
"authors": [
{
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de",
- "role": "lead"
+ "name": "Aleksei Gagarin (roxblnfk)",
+ "homepage": "https://github.com/roxblnfk"
}
],
- "description": "Collection of value objects that represent the types of the PHP type system",
- "homepage": "https://github.com/sebastianbergmann/type",
+ "description": "Naming convention plugin for the Testo testing framework.",
+ "keywords": [
+ "convention",
+ "testo"
+ ],
"support": {
- "issues": "https://github.com/sebastianbergmann/type/issues",
- "source": "https://github.com/sebastianbergmann/type/tree/4.0.0"
+ "source": "https://github.com/php-testo/convention/tree/0.1.4"
},
"funding": [
{
- "url": "https://github.com/sebastianbergmann",
- "type": "github"
+ "url": "https://boosty.to/roxblnfk",
+ "type": "boosty"
}
],
- "time": "2023-02-03T07:10:45+00:00"
+ "time": "2026-07-01T13:40:27+00:00"
},
{
- "name": "sebastian/version",
- "version": "4.0.1",
+ "name": "testo/data",
+ "version": "0.1.7",
"source": {
"type": "git",
- "url": "https://github.com/sebastianbergmann/version.git",
- "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17"
+ "url": "https://github.com/php-testo/data.git",
+ "reference": "ca70f7b7c29bab22b6abb4e00783b80c18551b5a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c51fa83a5d8f43f1402e3f32a005e6262244ef17",
- "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17",
+ "url": "https://api.github.com/repos/php-testo/data/zipball/ca70f7b7c29bab22b6abb4e00783b80c18551b5a",
+ "reference": "ca70f7b7c29bab22b6abb4e00783b80c18551b5a",
"shasum": ""
},
"require": {
- "php": ">=8.1"
+ "php": ">=8.2",
+ "testo/filter": "^0.1.6",
+ "testo/testo": "0.10.39 - 1"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "4.0-dev"
+ "dev-1.x": "1.x-dev"
}
},
"autoload": {
- "classmap": [
- "src/"
- ]
+ "psr-4": {
+ "Testo\\Data\\": "src/"
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@@ -5678,539 +5473,510 @@
],
"authors": [
{
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de",
- "role": "lead"
+ "name": "Aleksei Gagarin (roxblnfk)",
+ "homepage": "https://github.com/roxblnfk"
}
],
- "description": "Library that helps with managing the version number of Git-hosted PHP projects",
- "homepage": "https://github.com/sebastianbergmann/version",
+ "description": "Data provider plugin for the Testo testing framework.",
+ "keywords": [
+ "data-provider",
+ "testo"
+ ],
"support": {
- "issues": "https://github.com/sebastianbergmann/version/issues",
- "source": "https://github.com/sebastianbergmann/version/tree/4.0.1"
+ "source": "https://github.com/php-testo/data/tree/0.1.7"
},
"funding": [
{
- "url": "https://github.com/sebastianbergmann",
- "type": "github"
+ "url": "https://boosty.to/roxblnfk",
+ "type": "boosty"
}
],
- "time": "2023-02-07T11:34:05+00:00"
+ "time": "2026-08-06T07:43:21+00:00"
},
{
- "name": "spatie/array-to-xml",
- "version": "3.4.4",
+ "name": "testo/fiber",
+ "version": "0.1.2",
"source": {
"type": "git",
- "url": "https://github.com/spatie/array-to-xml.git",
- "reference": "88b2f3852a922dd73177a68938f8eb2ec70c7224"
+ "url": "https://github.com/php-testo/fiber.git",
+ "reference": "12c71a9d1873e37c61d98b4a07fe74f264293287"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/spatie/array-to-xml/zipball/88b2f3852a922dd73177a68938f8eb2ec70c7224",
- "reference": "88b2f3852a922dd73177a68938f8eb2ec70c7224",
+ "url": "https://api.github.com/repos/php-testo/fiber/zipball/12c71a9d1873e37c61d98b4a07fe74f264293287",
+ "reference": "12c71a9d1873e37c61d98b4a07fe74f264293287",
"shasum": ""
},
"require": {
- "ext-dom": "*",
- "php": "^8.0"
- },
- "require-dev": {
- "mockery/mockery": "^1.2",
- "pestphp/pest": "^1.21",
- "spatie/pest-plugin-snapshots": "^1.1"
+ "php": ">=8.2",
+ "testo/testo": "0.10.40 - 1"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "3.x-dev"
+ "dev-1.x": "1.x-dev"
}
},
"autoload": {
"psr-4": {
- "Spatie\\ArrayToXml\\": "src"
+ "Testo\\Fiber\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "MIT"
+ "BSD-3-Clause"
],
"authors": [
{
- "name": "Freek Van der Herten",
- "email": "freek@spatie.be",
- "homepage": "https://freek.dev",
- "role": "Developer"
+ "name": "Aleksei Gagarin (roxblnfk)",
+ "homepage": "https://github.com/roxblnfk"
}
],
- "description": "Convert an array to xml",
- "homepage": "https://github.com/spatie/array-to-xml",
+ "description": "Fiber/coroutine execution plugin for the Testo testing framework: run tests as cooperatively-scheduled plain fibers.",
"keywords": [
- "array",
- "convert",
- "xml"
+ "concurrency",
+ "coroutine",
+ "fiber",
+ "scheduler",
+ "testo"
],
"support": {
- "source": "https://github.com/spatie/array-to-xml/tree/3.4.4"
+ "source": "https://github.com/php-testo/fiber/tree/0.1.2"
},
"funding": [
{
- "url": "https://spatie.be/open-source/support-us",
- "type": "custom"
- },
- {
- "url": "https://github.com/spatie",
- "type": "github"
+ "url": "https://boosty.to/roxblnfk",
+ "type": "boosty"
}
],
- "time": "2025-12-15T09:00:41+00:00"
+ "time": "2026-08-10T12:39:45+00:00"
},
{
- "name": "spiral/code-style",
- "version": "v2.3.1",
+ "name": "testo/filter",
+ "version": "0.1.6",
"source": {
"type": "git",
- "url": "https://github.com/spiral/code-style.git",
- "reference": "a0407ffcb300c9d2977cb3d1c36af0d13b2ffb72"
+ "url": "https://github.com/php-testo/filter.git",
+ "reference": "dd3bc254ccece5008ec22d3cdba313b43a1f786d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/spiral/code-style/zipball/a0407ffcb300c9d2977cb3d1c36af0d13b2ffb72",
- "reference": "a0407ffcb300c9d2977cb3d1c36af0d13b2ffb72",
+ "url": "https://api.github.com/repos/php-testo/filter/zipball/dd3bc254ccece5008ec22d3cdba313b43a1f786d",
+ "reference": "dd3bc254ccece5008ec22d3cdba313b43a1f786d",
"shasum": ""
},
"require": {
- "php": ">=8.0",
- "php-cs-fixer/shim": "^3.64"
- },
- "require-dev": {
- "phpunit/phpunit": "^10.5",
- "spiral/dumper": "^3.3",
- "vimeo/psalm": "^5.26"
+ "php": ">=8.2",
+ "testo/testo": "0.10.39 - 1"
},
"type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-1.x": "1.x-dev"
+ }
+ },
"autoload": {
+ "files": [
+ "Filter.php"
+ ],
"psr-4": {
- "Spiral\\CodeStyle\\": "src/"
+ "Testo\\Filter\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "MIT"
+ "BSD-3-Clause"
],
"authors": [
{
- "name": "Aleksandr Novikov",
- "email": "aleksandr.novikov@spiralscout.com"
- },
- {
- "name": "Aleksei Gagarin",
- "email": "alexey.gagarin@spiralscout.com"
+ "name": "Aleksei Gagarin (roxblnfk)",
+ "homepage": "https://github.com/roxblnfk"
}
],
- "description": "Code style and static analysis tools rulesets collection",
- "homepage": "https://github.com/spiral/code-style",
+ "description": "Test filtering plugin for the Testo testing framework.",
+ "keywords": [
+ "filter",
+ "testo"
+ ],
"support": {
- "source": "https://github.com/spiral/code-style/tree/v2.3.1"
+ "source": "https://github.com/php-testo/filter/tree/0.1.6"
},
"funding": [
{
- "url": "https://github.com/sponsors/spiral",
- "type": "github"
+ "url": "https://boosty.to/roxblnfk",
+ "type": "boosty"
}
],
- "time": "2025-10-06T17:24:48+00:00"
+ "time": "2026-08-06T07:42:41+00:00"
},
{
- "name": "symfony/filesystem",
- "version": "v6.4.39",
+ "name": "testo/inline",
+ "version": "0.1.8",
"source": {
"type": "git",
- "url": "https://github.com/symfony/filesystem.git",
- "reference": "c507b077756b4e3e09adbbe7975fac81cd3722ca"
+ "url": "https://github.com/php-testo/inline.git",
+ "reference": "f6a8f10c3241d05916a5bbb1412df68e3f610b10"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/filesystem/zipball/c507b077756b4e3e09adbbe7975fac81cd3722ca",
- "reference": "c507b077756b4e3e09adbbe7975fac81cd3722ca",
+ "url": "https://api.github.com/repos/php-testo/inline/zipball/f6a8f10c3241d05916a5bbb1412df68e3f610b10",
+ "reference": "f6a8f10c3241d05916a5bbb1412df68e3f610b10",
"shasum": ""
},
"require": {
- "php": ">=8.1",
- "symfony/polyfill-ctype": "~1.8",
- "symfony/polyfill-mbstring": "~1.8"
- },
- "require-dev": {
- "symfony/process": "^5.4|^6.4|^7.0"
+ "php": ">=8.2",
+ "testo/assert": "^0.1.12",
+ "testo/data": "^0.1.7",
+ "testo/filter": "^0.1.6",
+ "testo/testo": "0.10.40 - 1"
},
"type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-1.x": "1.x-dev"
+ }
+ },
"autoload": {
"psr-4": {
- "Symfony\\Component\\Filesystem\\": ""
- },
- "exclude-from-classmap": [
- "/Tests/"
- ]
+ "Testo\\Inline\\": "src/"
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "MIT"
+ "BSD-3-Clause"
],
"authors": [
{
- "name": "Fabien Potencier",
- "email": "fabien@symfony.com"
- },
- {
- "name": "Symfony Community",
- "homepage": "https://symfony.com/contributors"
+ "name": "Aleksei Gagarin (roxblnfk)",
+ "homepage": "https://github.com/roxblnfk"
}
],
- "description": "Provides basic utilities for the filesystem",
- "homepage": "https://symfony.com",
+ "description": "Inline test plugin for the Testo testing framework.",
+ "keywords": [
+ "inline",
+ "testo"
+ ],
"support": {
- "source": "https://github.com/symfony/filesystem/tree/v6.4.39"
+ "source": "https://github.com/php-testo/inline/tree/0.1.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"
+ "url": "https://boosty.to/roxblnfk",
+ "type": "boosty"
}
],
- "time": "2026-05-07T13:11:42+00:00"
+ "time": "2026-08-10T12:39:25+00:00"
},
{
- "name": "symfony/finder",
- "version": "v6.4.34",
+ "name": "testo/lifecycle",
+ "version": "0.1.5",
"source": {
"type": "git",
- "url": "https://github.com/symfony/finder.git",
- "reference": "9590e86be1d1c57bfbb16d0dd040345378c20896"
+ "url": "https://github.com/php-testo/lifecycle.git",
+ "reference": "961ae522548719aa74d7484c335b0f7b0e080638"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/finder/zipball/9590e86be1d1c57bfbb16d0dd040345378c20896",
- "reference": "9590e86be1d1c57bfbb16d0dd040345378c20896",
+ "url": "https://api.github.com/repos/php-testo/lifecycle/zipball/961ae522548719aa74d7484c335b0f7b0e080638",
+ "reference": "961ae522548719aa74d7484c335b0f7b0e080638",
"shasum": ""
},
"require": {
- "php": ">=8.1"
- },
- "require-dev": {
- "symfony/filesystem": "^6.0|^7.0"
+ "php": ">=8.2",
+ "testo/testo": "0.10.34 - 1"
},
"type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-1.x": "1.x-dev"
+ }
+ },
"autoload": {
"psr-4": {
- "Symfony\\Component\\Finder\\": ""
- },
- "exclude-from-classmap": [
- "/Tests/"
- ]
+ "Testo\\Lifecycle\\": "src/"
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "MIT"
+ "BSD-3-Clause"
],
"authors": [
{
- "name": "Fabien Potencier",
- "email": "fabien@symfony.com"
- },
- {
- "name": "Symfony Community",
- "homepage": "https://symfony.com/contributors"
+ "name": "Aleksei Gagarin (roxblnfk)",
+ "homepage": "https://github.com/roxblnfk"
}
],
- "description": "Finds files and directories via an intuitive fluent interface",
- "homepage": "https://symfony.com",
+ "description": "Lifecycle hooks plugin for the Testo testing framework.",
+ "keywords": [
+ "lifecycle",
+ "testo"
+ ],
"support": {
- "source": "https://github.com/symfony/finder/tree/v6.4.34"
+ "source": "https://github.com/php-testo/lifecycle/tree/0.1.5"
},
"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"
+ "url": "https://boosty.to/roxblnfk",
+ "type": "boosty"
}
],
- "time": "2026-01-28T15:16:37+00:00"
+ "time": "2026-07-01T13:40:46+00:00"
},
{
- "name": "symfony/polyfill-php84",
- "version": "v1.37.0",
+ "name": "testo/repeat",
+ "version": "0.1.9",
"source": {
"type": "git",
- "url": "https://github.com/symfony/polyfill-php84.git",
- "reference": "88486db2c389b290bf87ff1de7ebc1e13e42bb06"
+ "url": "https://github.com/php-testo/repeat.git",
+ "reference": "9d20e475e6fe94d0bb7a43d6ab2b12c69d77a0f5"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/88486db2c389b290bf87ff1de7ebc1e13e42bb06",
- "reference": "88486db2c389b290bf87ff1de7ebc1e13e42bb06",
+ "url": "https://api.github.com/repos/php-testo/repeat/zipball/9d20e475e6fe94d0bb7a43d6ab2b12c69d77a0f5",
+ "reference": "9d20e475e6fe94d0bb7a43d6ab2b12c69d77a0f5",
"shasum": ""
},
"require": {
- "php": ">=7.2"
+ "php": ">=8.2",
+ "testo/testo": "0.10.39 - 1"
},
"type": "library",
"extra": {
- "thanks": {
- "url": "https://github.com/symfony/polyfill",
- "name": "symfony/polyfill"
+ "branch-alias": {
+ "dev-1.x": "1.x-dev"
}
},
"autoload": {
"files": [
- "bootstrap.php"
+ "Repeat.php"
],
"psr-4": {
- "Symfony\\Polyfill\\Php84\\": ""
- },
- "classmap": [
- "Resources/stubs"
- ]
+ "Testo\\Repeat\\": "src/"
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "MIT"
+ "BSD-3-Clause"
],
"authors": [
{
- "name": "Nicolas Grekas",
- "email": "p@tchwork.com"
- },
- {
- "name": "Symfony Community",
- "homepage": "https://symfony.com/contributors"
+ "name": "Aleksei Gagarin (roxblnfk)",
+ "homepage": "https://github.com/roxblnfk"
}
],
- "description": "Symfony polyfill backporting some PHP 8.4+ features to lower PHP versions",
- "homepage": "https://symfony.com",
+ "description": "Repeat policy plugin for the Testo testing framework.",
"keywords": [
- "compatibility",
- "polyfill",
- "portable",
- "shim"
+ "repeat",
+ "test",
+ "testo"
],
"support": {
- "source": "https://github.com/symfony/polyfill-php84/tree/v1.37.0"
+ "source": "https://github.com/php-testo/repeat/tree/0.1.9"
},
"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"
+ "url": "https://boosty.to/roxblnfk",
+ "type": "boosty"
}
],
- "time": "2026-04-10T18:47:49+00:00"
+ "time": "2026-08-06T07:42:29+00:00"
},
{
- "name": "symfony/var-dumper",
- "version": "v6.4.36",
+ "name": "testo/retry",
+ "version": "0.1.5",
"source": {
"type": "git",
- "url": "https://github.com/symfony/var-dumper.git",
- "reference": "7c8ad9ce4faf6c8a99948e70ce02b601a0439782"
+ "url": "https://github.com/php-testo/retry.git",
+ "reference": "8e158e8341a5824b96f252fc711988daf15694d2"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/var-dumper/zipball/7c8ad9ce4faf6c8a99948e70ce02b601a0439782",
- "reference": "7c8ad9ce4faf6c8a99948e70ce02b601a0439782",
+ "url": "https://api.github.com/repos/php-testo/retry/zipball/8e158e8341a5824b96f252fc711988daf15694d2",
+ "reference": "8e158e8341a5824b96f252fc711988daf15694d2",
"shasum": ""
},
"require": {
- "php": ">=8.1",
- "symfony/deprecation-contracts": "^2.5|^3",
- "symfony/polyfill-mbstring": "~1.0"
- },
- "conflict": {
- "symfony/console": "<5.4"
- },
- "require-dev": {
- "symfony/console": "^5.4|^6.0|^7.0",
- "symfony/error-handler": "^6.3|^7.0",
- "symfony/http-kernel": "^5.4|^6.0|^7.0",
- "symfony/process": "^5.4|^6.0|^7.0",
- "symfony/uid": "^5.4|^6.0|^7.0",
- "twig/twig": "^2.13|^3.0.4"
+ "php": ">=8.2",
+ "testo/testo": "0.10.39 - 1"
},
- "bin": [
- "Resources/bin/var-dump-server"
- ],
"type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-1.x": "1.x-dev"
+ }
+ },
"autoload": {
"files": [
- "Resources/functions/dump.php"
+ "Retry.php"
],
"psr-4": {
- "Symfony\\Component\\VarDumper\\": ""
- },
- "exclude-from-classmap": [
- "/Tests/"
- ]
+ "Testo\\Retry\\": "src/"
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "MIT"
+ "BSD-3-Clause"
],
"authors": [
{
- "name": "Nicolas Grekas",
- "email": "p@tchwork.com"
- },
- {
- "name": "Symfony Community",
- "homepage": "https://symfony.com/contributors"
+ "name": "Aleksei Gagarin (roxblnfk)",
+ "homepage": "https://github.com/roxblnfk"
}
],
- "description": "Provides mechanisms for walking through any arbitrary PHP variable",
- "homepage": "https://symfony.com",
+ "description": "Retry policy plugin for the Testo testing framework.",
"keywords": [
- "debug",
- "dump"
+ "retry",
+ "testo"
],
"support": {
- "source": "https://github.com/symfony/var-dumper/tree/v6.4.36"
+ "source": "https://github.com/php-testo/retry/tree/0.1.5"
},
"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"
+ "url": "https://boosty.to/roxblnfk",
+ "type": "boosty"
}
],
- "time": "2026-03-30T15:36:00+00:00"
+ "time": "2026-08-06T07:43:03+00:00"
},
{
- "name": "ta-tikoma/phpunit-architecture-test",
- "version": "0.8.7",
+ "name": "testo/test",
+ "version": "0.1.6",
"source": {
"type": "git",
- "url": "https://github.com/ta-tikoma/phpunit-architecture-test.git",
- "reference": "1248f3f506ca9641d4f68cebcd538fa489754db8"
+ "url": "https://github.com/php-testo/test.git",
+ "reference": "a67bb2c742936c6180175b3eda7baec8b9990954"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/ta-tikoma/phpunit-architecture-test/zipball/1248f3f506ca9641d4f68cebcd538fa489754db8",
- "reference": "1248f3f506ca9641d4f68cebcd538fa489754db8",
+ "url": "https://api.github.com/repos/php-testo/test/zipball/a67bb2c742936c6180175b3eda7baec8b9990954",
+ "reference": "a67bb2c742936c6180175b3eda7baec8b9990954",
"shasum": ""
},
"require": {
- "nikic/php-parser": "^4.18.0 || ^5.0.0",
- "php": "^8.1.0",
- "phpdocumentor/reflection-docblock": "^5.3.0 || ^6.0.0",
- "phpunit/phpunit": "^10.5.5 || ^11.0.0 || ^12.0.0 || ^13.0.0",
- "symfony/finder": "^6.4.0 || ^7.0.0 || ^8.0.0"
- },
- "require-dev": {
- "laravel/pint": "^1.13.7",
- "phpstan/phpstan": "^1.10.52"
+ "php": ">=8.2",
+ "testo/testo": "0.10.34 - 1"
},
"type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-1.x": "1.x-dev"
+ }
+ },
"autoload": {
+ "files": [
+ "Test.php"
+ ],
"psr-4": {
- "PHPUnit\\Architecture\\": "src/"
+ "Testo\\Test\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "MIT"
+ "BSD-3-Clause"
],
"authors": [
{
- "name": "Ni Shi",
- "email": "futik0ma011@gmail.com"
- },
- {
- "name": "Nuno Maduro",
- "email": "enunomaduro@gmail.com"
+ "name": "Aleksei Gagarin (roxblnfk)",
+ "homepage": "https://github.com/roxblnfk"
}
],
- "description": "Methods for testing application architecture",
+ "description": "Test attribute plugin for the Testo testing framework.",
"keywords": [
- "architecture",
- "phpunit",
- "stucture",
"test",
- "testing"
+ "testo"
],
"support": {
- "issues": "https://github.com/ta-tikoma/phpunit-architecture-test/issues",
- "source": "https://github.com/ta-tikoma/phpunit-architecture-test/tree/0.8.7"
+ "source": "https://github.com/php-testo/test/tree/0.1.6"
},
- "time": "2026-02-17T17:25:14+00:00"
+ "funding": [
+ {
+ "url": "https://boosty.to/roxblnfk",
+ "type": "boosty"
+ }
+ ],
+ "time": "2026-07-01T13:41:02+00:00"
},
{
- "name": "theseer/tokenizer",
- "version": "1.3.1",
+ "name": "testo/testo",
+ "version": "0.10.40",
"source": {
"type": "git",
- "url": "https://github.com/theseer/tokenizer.git",
- "reference": "b7489ce515e168639d17feec34b8847c326b0b3c"
+ "url": "https://github.com/php-testo/testo.git",
+ "reference": "390425b1f0d2c7560e1acc00e09da2ef7a7b5eef"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b7489ce515e168639d17feec34b8847c326b0b3c",
- "reference": "b7489ce515e168639d17feec34b8847c326b0b3c",
+ "url": "https://api.github.com/repos/php-testo/testo/zipball/390425b1f0d2c7560e1acc00e09da2ef7a7b5eef",
+ "reference": "390425b1f0d2c7560e1acc00e09da2ef7a7b5eef",
"shasum": ""
},
"require": {
- "ext-dom": "*",
"ext-tokenizer": "*",
- "ext-xmlwriter": "*",
- "php": "^7.2 || ^8.0"
+ "internal/destroy": "^1.0",
+ "internal/path": "^1.2",
+ "php": ">=8.2",
+ "psr/container": "1 - 2",
+ "psr/event-dispatcher": "^1.0",
+ "psr/log": "^2.0 || ^3.0",
+ "symfony/console": "^6.4 || ^7 || ^8.0",
+ "symfony/finder": "^6.4 || ^7 || ^8.0",
+ "testo/assert": "^0.1.12",
+ "testo/bench": "^0.1.8",
+ "testo/bridge-symfony-console": "^0.1.8",
+ "testo/codecov": "^0.1.12",
+ "testo/convention": "^0.1.4",
+ "testo/data": "^0.1.7",
+ "testo/fiber": "^0.1.2",
+ "testo/filter": "^0.1.6",
+ "testo/inline": "^0.1.8",
+ "testo/lifecycle": "^0.1.5",
+ "testo/repeat": "^0.1.9",
+ "testo/retry": "^0.1.5",
+ "testo/test": "^0.1.6",
+ "yiisoft/injector": "^1.2"
+ },
+ "replace": {
+ "internal/container": "*",
+ "internal/fiber": "*"
+ },
+ "require-dev": {
+ "bamarni/composer-bin-plugin": "^1.8",
+ "buggregator/trap": "^1.10",
+ "internal/dload": "^1.6",
+ "llm/skills": "^1.3",
+ "php-vcr/php-vcr": "^1.6",
+ "rector/rector": "^2.5.2",
+ "roxblnfk/unpoly": "1.8.2",
+ "testo/bridge-infection": "^0.1.8",
+ "testo/bridge-mockery": "^0.1.2",
+ "testo/bridge-rector": "^0.2.2",
+ "testo/bridge-revolt": "^0.1.1",
+ "testo/bridge-vcr": "^0.1.0",
+ "testo/facade": "^0.1.1"
+ },
+ "suggest": {
+ "llm/skills": "Ship Testo's bundled AI-agent skills into your project's skills directory automatically.",
+ "testo/bridge-infection": "Provides integration with Infection PHP mutation testing framework.",
+ "testo/bridge-mockery": "Provides integration with the Mockery mocking framework."
},
"type": "library",
+ "extra": {
+ "skills": {
+ "source": "skills"
+ },
+ "bamarni-bin": {
+ "bin-links": true,
+ "forward-command": false,
+ "target-directory": "tools"
+ }
+ },
"autoload": {
- "classmap": [
- "src/"
- ]
+ "psr-4": {
+ "Testo\\": "core/",
+ "Internal\\Fiber\\": "internal/fiber/src",
+ "Internal\\Container\\": "internal/container/src"
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@@ -6218,23 +5984,30 @@
],
"authors": [
{
- "name": "Arne Blankerts",
- "email": "arne@blankerts.de",
- "role": "Developer"
+ "name": "Aleksei Gagarin (roxblnfk)",
+ "homepage": "https://github.com/roxblnfk"
}
],
- "description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
+ "description": "A lightweight PHP testing framework.",
+ "homepage": "https://php-testo.github.io/",
+ "keywords": [
+ "dev",
+ "test",
+ "testing",
+ "tests"
+ ],
"support": {
- "issues": "https://github.com/theseer/tokenizer/issues",
- "source": "https://github.com/theseer/tokenizer/tree/1.3.1"
+ "docs": "https://php-testo.github.io/",
+ "issues": "https://github.com/php-testo/testo/issues",
+ "source": "https://github.com/php-testo/testo"
},
"funding": [
{
- "url": "https://github.com/theseer",
- "type": "github"
+ "url": "https://boosty.to/roxblnfk",
+ "type": "boosty"
}
],
- "time": "2025-11-17T20:03:58+00:00"
+ "time": "2026-08-10T12:38:50+00:00"
},
{
"name": "vimeo/psalm",
@@ -6356,23 +6129,23 @@
},
{
"name": "webmozart/assert",
- "version": "1.12.1",
+ "version": "2.4.1",
"source": {
"type": "git",
"url": "https://github.com/webmozarts/assert.git",
- "reference": "9be6926d8b485f55b9229203f962b51ed377ba68"
+ "reference": "2ccb7c2e821038c03a3e6e1700c570c158c55f70"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/webmozarts/assert/zipball/9be6926d8b485f55b9229203f962b51ed377ba68",
- "reference": "9be6926d8b485f55b9229203f962b51ed377ba68",
+ "url": "https://api.github.com/repos/webmozarts/assert/zipball/2ccb7c2e821038c03a3e6e1700c570c158c55f70",
+ "reference": "2ccb7c2e821038c03a3e6e1700c570c158c55f70",
"shasum": ""
},
"require": {
"ext-ctype": "*",
"ext-date": "*",
"ext-filter": "*",
- "php": "^7.2 || ^8.0"
+ "php": "^8.2"
},
"suggest": {
"ext-intl": "",
@@ -6381,8 +6154,12 @@
},
"type": "library",
"extra": {
+ "psalm": {
+ "pluginClass": "Webmozart\\Assert\\PsalmPlugin"
+ },
"branch-alias": {
- "dev-master": "1.10-dev"
+ "dev-master": "2.0-dev",
+ "dev-feature/2-0": "2.0-dev"
}
},
"autoload": {
@@ -6398,6 +6175,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.",
@@ -6408,9 +6189,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.4.1"
},
- "time": "2025-10-29T15:56:20+00:00"
+ "time": "2026-06-15T15:31:57+00:00"
}
],
"aliases": [],
@@ -6419,8 +6200,8 @@
"prefer-stable": true,
"prefer-lowest": false,
"platform": {
- "php": ">=8.1"
+ "php": ">=8.2"
},
"platform-dev": {},
- "plugin-api-version": "2.6.0"
+ "plugin-api-version": "2.9.0"
}
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
deleted file mode 100644
index a2468bc..0000000
--- a/phpunit.xml.dist
+++ /dev/null
@@ -1,46 +0,0 @@
-
-
-
-
- tests/Acceptance
-
-
- tests/Unit
-
-
- tests/Integration
-
-
- tests/Arch
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- src
-
-
- tests
- src/Test
-
-
-
diff --git a/psalm.xml b/psalm.xml
index 9885caa..9cda8d7 100644
--- a/psalm.xml
+++ b/psalm.xml
@@ -40,5 +40,7 @@
+
+
diff --git a/testo.php b/testo.php
new file mode 100644
index 0000000..53e3b81
--- /dev/null
+++ b/testo.php
@@ -0,0 +1,28 @@
+software = 'trap';
$downloadConfig->version = '1.13.16';
$downloadConfig->type = Type::Phar;
$downloadConfig->extractPath = (string) $this->destinationDir;
- // Act
$this->dload->addTask($downloadConfig);
$this->dload->run();
// Assert - Check that trap.phar was downloaded
$expectedPharPath = (string) $this->destinationDir->join('trap.phar');
- self::assertFileExists($expectedPharPath, 'Trap PHAR should be downloaded to destination directory');
+ Assert::true(\file_exists($expectedPharPath), 'Trap PHAR should be downloaded to destination directory');
// Verify the file is not empty
- self::assertGreaterThan(1024, \filesize($expectedPharPath), 'Downloaded PHAR should have substantial size');
+ Assert::int(\filesize($expectedPharPath))->greaterThan(1024, 'Downloaded PHAR should have substantial size');
// Verify file permissions (should be executable)
if (PHP_OS_FAMILY !== 'Windows') {
- self::assertTrue(\is_executable($expectedPharPath), 'PHAR file should be executable');
+ Assert::true(\is_executable($expectedPharPath), 'PHAR file should be executable');
}
}
- public function testDownloadsTrapPharSuccessfullyWithForceOption(): void
+ #[Test]
+ public function downloadsTrapPharSuccessfullyWithForceOption(): void
{
- // Arrange
$downloadConfig = new DownloadConfig();
$downloadConfig->software = 'trap';
$downloadConfig->version = '1.13.16';
$downloadConfig->type = Type::Phar;
$downloadConfig->extractPath = (string) $this->destinationDir;
- // Act
$this->dload->addTask($downloadConfig);
$this->dload->run();
$this->dload->addTask($downloadConfig, true);
@@ -77,20 +78,20 @@ public function testDownloadsTrapPharSuccessfullyWithForceOption(): void
// Assert - Check that trap.phar was downloaded
$expectedPharPath = (string) $this->destinationDir->join('trap.phar');
- self::assertFileExists($expectedPharPath, 'Trap PHAR should be downloaded to destination directory');
+ Assert::true(\file_exists($expectedPharPath), 'Trap PHAR should be downloaded to destination directory');
// Verify the file is not empty
- self::assertGreaterThan(1024, \filesize($expectedPharPath), 'Downloaded PHAR should have substantial size');
+ Assert::int(\filesize($expectedPharPath))->greaterThan(1024, 'Downloaded PHAR should have substantial size');
// Verify file permissions (should be executable)
if (PHP_OS_FAMILY !== 'Windows') {
- self::assertTrue(\is_executable($expectedPharPath), 'PHAR file should be executable');
+ Assert::true(\is_executable($expectedPharPath), 'PHAR file should be executable');
}
}
- public function testDownloadsTomlTestGzBinary(): void
+ #[Test]
+ public function downloadsTomlTestGzBinary(): void
{
- // Arrange
$dload = $this->buildDLoad($this->createTomlTestXmlConfig());
$downloadConfig = new DownloadConfig();
$downloadConfig->software = 'toml-test';
@@ -98,46 +99,45 @@ public function testDownloadsTomlTestGzBinary(): void
$downloadConfig->type = Type::Binary;
$downloadConfig->extractPath = (string) $this->destinationDir;
- // Act
$dload->addTask($downloadConfig);
$dload->run();
// Assert - Check that toml-test binary was downloaded and extracted from .gz
$os = OperatingSystem::fromGlobals();
$expectedPath = (string) $this->destinationDir->join('toml-test' . $os->getBinaryExtension());
- self::assertFileExists($expectedPath, 'toml-test binary should be downloaded and extracted from .gz archive');
- self::assertGreaterThan(1024, \filesize($expectedPath), 'Downloaded binary should have substantial size');
+ Assert::true(\file_exists($expectedPath), 'toml-test binary should be downloaded and extracted from .gz archive');
+ Assert::int(\filesize($expectedPath))->greaterThan(1024, 'Downloaded binary should have substantial size');
if (\PHP_OS_FAMILY !== 'Windows') {
- self::assertTrue(\is_executable($expectedPath), 'Binary file should be executable');
+ Assert::true(\is_executable($expectedPath), 'Binary file should be executable');
}
}
- public function testDownloadsTrapBinary(): void
+ #[Test]
+ public function downloadsTrapBinary(): void
{
- // Arrange
$downloadConfig = new DownloadConfig();
$downloadConfig->software = 'trap';
$downloadConfig->version = '1.13.16';
$downloadConfig->type = Type::Binary;
$downloadConfig->extractPath = (string) $this->destinationDir;
- // Act
$this->dload->addTask($downloadConfig);
$this->dload->run();
// Assert - Check that Trap binary was downloaded and extracted
$os = OperatingSystem::fromGlobals();
$expectedPharPath = (string) $this->destinationDir->join('trap' . $os->getBinaryExtension());
- self::assertFileExists($expectedPharPath, 'Trap binary should be downloaded to destination directory');
+ Assert::true(\file_exists($expectedPharPath), 'Trap binary should be downloaded to destination directory');
// Verify the file is not empty
- self::assertGreaterThan(1024, \filesize($expectedPharPath), 'Downloaded binary should have substantial size');
+ Assert::int(\filesize($expectedPharPath))->greaterThan(1024, 'Downloaded binary should have substantial size');
- self::assertTrue(\is_executable($expectedPharPath), 'Binary file should be executable');
+ Assert::true(\is_executable($expectedPharPath), 'Binary file should be executable');
}
- protected function setUp(): void
+ #[BeforeTest]
+ protected function prepare(): void
{
// Set up test directory structure
$projectRoot = Path::create(\dirname(__DIR__, 2));
@@ -148,7 +148,8 @@ protected function setUp(): void
$this->dload = $this->buildDLoad($this->createTrapXmlConfig());
}
- protected function tearDown(): void
+ #[AfterTest]
+ protected function cleanup(): void
{
// Clean up test directories
if ($this->testRuntimeDir->isDir()) {
diff --git a/tests/Arch/ArchTest.php b/tests/Arch/ArchTest.php
deleted file mode 100644
index 2599b16..0000000
--- a/tests/Arch/ArchTest.php
+++ /dev/null
@@ -1,43 +0,0 @@
-layer();
-
- foreach ($layer as $object) {
- foreach ($object->uses as $use) {
- foreach ($functions as $function) {
- $function === $use and throw new \Exception(
- \sprintf(
- 'Function `%s()` is used in %s.',
- $function,
- $object->name,
- ),
- );
- }
- }
- }
-
- $this->assertTrue(true);
- }
-}
diff --git a/tests/Integration/Module/Archive/ArchiveIntegrationTest.php b/tests/Integration/Module/Archive/ArchiveIntegrationTest.php
index 79cae5d..607e54a 100644
--- a/tests/Integration/Module/Archive/ArchiveIntegrationTest.php
+++ b/tests/Integration/Module/Archive/ArchiveIntegrationTest.php
@@ -4,14 +4,19 @@
namespace Internal\DLoad\Tests\Integration\Module\Archive;
+use Internal\DLoad\Module\Archive\Archive;
use Internal\DLoad\Module\Archive\ArchiveFactory;
use Internal\DLoad\Module\Archive\Internal\NullArchive;
use Internal\DLoad\Module\Archive\Internal\PharArchive;
use Internal\DLoad\Module\Archive\Internal\TarPharArchive;
use Internal\DLoad\Module\Archive\Internal\ZipPharArchive;
-use PHPUnit\Framework\Attributes\DataProvider;
-use PHPUnit\Framework\Attributes\Group;
-use PHPUnit\Framework\TestCase;
+use Testo\Assert;
+use Testo\Core\Exception\SkipTest;
+use Testo\Data\DataProvider;
+use Testo\Filter\Group;
+use Testo\Lifecycle\AfterTest;
+use Testo\Lifecycle\BeforeTest;
+use Testo\Test;
/**
* Integration tests for Archive module
@@ -20,7 +25,7 @@
* They require the phar extension to be enabled and temporary files to be created.
*/
#[Group('integration')]
-final class ArchiveIntegrationTest extends TestCase
+final class ArchiveIntegrationTest
{
private string $tempDir;
private ArchiveFactory $factory;
@@ -34,32 +39,30 @@ public static function provideArchiveTypes(): \Generator
}
#[DataProvider('provideArchiveTypes')]
- public function testFactoryCreateReturnsCorrectImplementation(
+ #[Test]
+ public function factoryCreateReturnsCorrectImplementation(
string $extension,
string $className,
): void {
// Skip if we can't verify the implementation type
if (!\class_exists($className)) {
- self::markTestSkipped("Class $className not available");
+ throw new SkipTest("Class $className not available");
}
- // Arrange - create mock file with extension
- $file = $this->createMock(\SplFileInfo::class);
- $file->method('getFilename')->willReturn('test.' . $extension);
- $file->method('isFile')->willReturn(true);
- $file->method('isReadable')->willReturn(true);
+ $file = \Mockery::mock(\SplFileInfo::class);
+ $file->allows('getFilename')->andReturn('test.' . $extension);
+ $file->allows('isFile')->andReturn(true);
+ $file->allows('isReadable')->andReturn(true);
- // Act - create archive handler
$archive = $this->factory->create($file);
- // Assert - check implementation type
- self::assertInstanceOf($className, $archive);
+ Assert::instanceOf($archive, $className);
}
- public function testFactoryExtendWithCustomImplementation(): void
+ #[Test]
+ public function factoryExtendWithCustomImplementation(): void
{
- // Arrange - create custom archive mock
- $customArchive = $this->createMock('Internal\DLoad\Module\Archive\Archive');
+ $customArchive = \Mockery::mock(Archive::class);
// Register custom implementation for .custom extension
$this->factory->extend(
@@ -68,24 +71,22 @@ public function testFactoryExtendWithCustomImplementation(): void
['custom'],
);
- // Create mock file with custom extension
- $file = $this->createMock(\SplFileInfo::class);
- $file->method('getFilename')->willReturn('test.custom');
- $file->method('isFile')->willReturn(true);
- $file->method('isReadable')->willReturn(true);
+ $file = \Mockery::mock(\SplFileInfo::class);
+ $file->allows('getFilename')->andReturn('test.custom');
+ $file->allows('isFile')->andReturn(true);
+ $file->allows('isReadable')->andReturn(true);
- // Act
$archive = $this->factory->create($file);
- // Assert
- self::assertSame($customArchive, $archive);
+ Assert::same($archive, $customArchive);
}
- protected function setUp(): void
+ #[BeforeTest]
+ protected function prepare(): void
{
// Skip tests if phar extension is not available
if (!\class_exists(\PharData::class)) {
- self::markTestSkipped('Phar extension is not available');
+ throw new SkipTest('Phar extension is not available');
}
// Create temporary directory for test files in project runtime
@@ -97,7 +98,8 @@ protected function setUp(): void
$this->factory = new ArchiveFactory();
}
- protected function tearDown(): void
+ #[AfterTest]
+ protected function cleanup(): void
{
// Clean up temporary directory
if (\is_dir($this->tempDir)) {
diff --git a/tests/Unit/Module/Archive/API/ArchiveExceptionTest.php b/tests/Unit/Module/Archive/API/ArchiveExceptionTest.php
index 2bb5423..0ad9cb4 100644
--- a/tests/Unit/Module/Archive/API/ArchiveExceptionTest.php
+++ b/tests/Unit/Module/Archive/API/ArchiveExceptionTest.php
@@ -5,54 +5,48 @@
namespace Internal\DLoad\Tests\Unit\Module\Archive\API;
use Internal\DLoad\Module\Archive\Exception\ArchiveException;
-use PHPUnit\Framework\Attributes\CoversClass;
-use PHPUnit\Framework\TestCase;
+use Testo\Assert;
+use Testo\Codecov\Covers;
+use Testo\Test;
-#[CoversClass(ArchiveException::class)]
-final class ArchiveExceptionTest extends TestCase
+#[Covers(ArchiveException::class)]
+final class ArchiveExceptionTest
{
- public function testExceptionInheritsFromRuntimeException(): void
+ #[Test]
+ public function exceptionInheritsFromRuntimeException(): void
{
- // Arrange
$exception = new ArchiveException('Test message');
- // Assert
- self::assertInstanceOf(\RuntimeException::class, $exception);
+ Assert::instanceOf($exception, \RuntimeException::class);
}
- public function testExceptionReturnsCorrectMessage(): void
+ #[Test]
+ public function exceptionReturnsCorrectMessage(): void
{
- // Arrange
$message = 'Archive extraction failed: test reason';
- // Act
$exception = new ArchiveException($message);
- // Assert
- self::assertSame($message, $exception->getMessage());
+ Assert::same($exception->getMessage(), $message);
}
- public function testExceptionCanHaveCustomCode(): void
+ #[Test]
+ public function exceptionCanHaveCustomCode(): void
{
- // Arrange
$code = 123;
- // Act
$exception = new ArchiveException('Test message', $code);
- // Assert
- self::assertSame($code, $exception->getCode());
+ Assert::same($exception->getCode(), $code);
}
- public function testExceptionCanHavePreviousException(): void
+ #[Test]
+ public function exceptionCanHavePreviousException(): void
{
- // Arrange
$previous = new \Exception('Previous error');
- // Act
$exception = new ArchiveException('Test message', 0, $previous);
- // Assert
- self::assertSame($previous, $exception->getPrevious());
+ Assert::same($exception->getPrevious(), $previous);
}
}
diff --git a/tests/Unit/Module/Archive/API/ArchiveFactoryTest.php b/tests/Unit/Module/Archive/API/ArchiveFactoryTest.php
index b2dcc3d..7ad2e38 100644
--- a/tests/Unit/Module/Archive/API/ArchiveFactoryTest.php
+++ b/tests/Unit/Module/Archive/API/ArchiveFactoryTest.php
@@ -11,12 +11,18 @@
use Internal\DLoad\Module\Archive\Internal\TarPharArchive;
use Internal\DLoad\Module\Archive\Internal\ZipPharArchive;
use Internal\DLoad\Tests\Unit\Module\Archive\Stub\ArchiveFixtureGenerator;
-use PHPUnit\Framework\Attributes\CoversClass;
-use PHPUnit\Framework\Attributes\DataProvider;
-use PHPUnit\Framework\TestCase;
-
-#[CoversClass(ArchiveFactory::class)]
-final class ArchiveFactoryTest extends TestCase
+use Testo\Assert;
+use Testo\Codecov\Covers;
+use Testo\Core\Exception\SkipTest;
+use Testo\Data\DataProvider;
+use Testo\Expect;
+use Testo\Lifecycle\AfterClass;
+use Testo\Lifecycle\BeforeClass;
+use Testo\Lifecycle\BeforeTest;
+use Testo\Test;
+
+#[Covers(ArchiveFactory::class)]
+final class ArchiveFactoryTest
{
private ArchiveFactory $factory;
private static string $fixturesDir;
@@ -37,18 +43,39 @@ public static function provideArchiveFiles(): \Generator
yield 'phar file' => ['archive.phar', PharArchive::class];
}
+ #[BeforeClass]
+ public static function prepareClass(): void
+ {
+ // Define project's test runtime directory
+ $projectRoot = \dirname(__DIR__, 5); // Five levels up from this file
+ self::$fixturesDir = $projectRoot . '/runtime/tests/archive-fixtures';
+
+ // Create archive fixtures
+ self::$fixtureGenerator = new ArchiveFixtureGenerator(self::$fixturesDir);
+ self::$archiveFixtures = self::$fixtureGenerator->generateArchives();
+ }
+
+ #[AfterClass]
+ public static function cleanupClass(): void
+ {
+ // Clean up fixtures
+ if (self::$fixtureGenerator !== null) {
+ self::$fixtureGenerator->cleanup();
+ }
+ }
+
#[DataProvider('provideDefaultSupportedExtensions')]
- public function testGetSupportedExtensionsReturnsDefaultExtensions(string $extension): void
+ #[Test]
+ public function getSupportedExtensionsReturnsDefaultExtensions(string $extension): void
{
- // Act
$extensions = $this->factory->getSupportedExtensions();
- // Assert
- self::assertContains($extension, $extensions);
+ Assert::contains($extensions, $extension);
}
#[DataProvider('provideArchiveFiles')]
- public function testCreateReturnsCorrectArchiveTypeForExtension(
+ #[Test]
+ public function createReturnsCorrectArchiveTypeForExtension(
string $filename,
string $expectedClass,
): void {
@@ -59,24 +86,22 @@ public function testCreateReturnsCorrectArchiveTypeForExtension(
}
if (!isset(self::$archiveFixtures[$extension])) {
- self::markTestSkipped("Archive fixture for {$extension} could not be created");
+ throw new SkipTest("Archive fixture for {$extension} could not be created");
}
// Arrange - use actual file
$filePath = self::$archiveFixtures[$extension];
$file = new \SplFileInfo($filePath);
- // Act
$archive = $this->factory->create($file);
- // Assert
- self::assertInstanceOf($expectedClass, $archive);
+ Assert::instanceOf($archive, $expectedClass);
}
- public function testExtendAddsCustomMatcher(): void
+ #[Test]
+ public function extendAddsCustomMatcher(): void
{
- // Arrange
- $mockArchive = $this->createMock(Archive::class);
+ $mockArchive = \Mockery::mock(Archive::class);
$customExtension = 'custom';
$this->factory->extend(
@@ -87,45 +112,39 @@ public function testExtendAddsCustomMatcher(): void
$file = $this->createFileInfoMock('test.custom');
- // Act
$result = $this->factory->create($file);
$extensions = $this->factory->getSupportedExtensions();
- // Assert
- self::assertSame($mockArchive, $result);
- self::assertContains($customExtension, $extensions);
+ Assert::same($result, $mockArchive);
+ Assert::contains($extensions, $customExtension);
}
- public function testCreateReturnsNullArchiveForNonArchiveFile(): void
+ #[Test]
+ public function createReturnsNullArchiveForNonArchiveFile(): void
{
- // Arrange
$file = $this->createFileInfoMock('binary-executable');
- // Act
$archive = $this->factory->create($file);
- // Assert
- self::assertInstanceOf(NullArchive::class, $archive);
+ Assert::instanceOf($archive, NullArchive::class);
}
- public function testCreateThrowsExceptionForInvalidFile(): void
+ #[Test]
+ public function createThrowsExceptionForInvalidFile(): void
{
- // Arrange
- $file = $this->createMock(\SplFileInfo::class);
- $file->method('getFilename')->willReturn('invalid-file');
- $file->method('isFile')->willReturn(false);
+ $file = \Mockery::mock(\SplFileInfo::class);
+ $file->allows('getFilename')->andReturn('invalid-file');
+ $file->allows('isFile')->andReturn(false);
- // Assert
- $this->expectException(\InvalidArgumentException::class);
+ Expect::exception(\InvalidArgumentException::class);
- // Act
$this->factory->create($file);
}
- public function testExtendPrioritizesNewMatchersOverExisting(): void
+ #[Test]
+ public function extendPrioritizesNewMatchersOverExisting(): void
{
- // Arrange
- $mockArchive = $this->createMock(Archive::class);
+ $mockArchive = \Mockery::mock(Archive::class);
$zipFile = $this->createFileInfoMock('test.zip');
// Override the default zip handler
@@ -135,14 +154,13 @@ public function testExtendPrioritizesNewMatchersOverExisting(): void
[],
);
- // Act
$result = $this->factory->create($zipFile);
- // Assert
- self::assertSame($mockArchive, $result);
+ Assert::same($result, $mockArchive);
}
- public function testNullArchiveUsedAsLastResort(): void
+ #[Test]
+ public function nullArchiveUsedAsLastResort(): void
{
// Arrange - create custom matcher that always returns null
$this->factory->extend(
@@ -152,35 +170,15 @@ public function testNullArchiveUsedAsLastResort(): void
$file = $this->createFileInfoMock('unknown-file-type');
- // Act
$archive = $this->factory->create($file);
// Assert - should fall back to NullArchive
- self::assertInstanceOf(NullArchive::class, $archive);
- }
-
- public static function setUpBeforeClass(): void
- {
- // Define project's test runtime directory
- $projectRoot = \dirname(__DIR__, 5); // Five levels up from this file
- self::$fixturesDir = $projectRoot . '/runtime/tests/archive-fixtures';
-
- // Create archive fixtures
- self::$fixtureGenerator = new ArchiveFixtureGenerator(self::$fixturesDir);
- self::$archiveFixtures = self::$fixtureGenerator->generateArchives();
- }
-
- public static function tearDownAfterClass(): void
- {
- // Clean up fixtures
- if (self::$fixtureGenerator !== null) {
- self::$fixtureGenerator->cleanup();
- }
+ Assert::instanceOf($archive, NullArchive::class);
}
- protected function setUp(): void
+ #[BeforeTest]
+ protected function prepare(): void
{
- // Arrange
$this->factory = new ArchiveFactory();
}
@@ -190,11 +188,11 @@ protected function setUp(): void
*/
private function createFileInfoMock(string $filename): \SplFileInfo
{
- $file = $this->createMock(\SplFileInfo::class);
- $file->method('getFilename')->willReturn($filename);
- $file->method('isFile')->willReturn(true);
- $file->method('isReadable')->willReturn(true);
- $file->method('getPathname')->willReturn('/path/to/' . $filename);
+ $file = \Mockery::mock(\SplFileInfo::class);
+ $file->allows('getFilename')->andReturn($filename);
+ $file->allows('isFile')->andReturn(true);
+ $file->allows('isReadable')->andReturn(true);
+ $file->allows('getPathname')->andReturn('/path/to/' . $filename);
return $file;
}
diff --git a/tests/Unit/Module/Archive/API/ArchiveTest.php b/tests/Unit/Module/Archive/API/ArchiveTest.php
index 6927bff..5684ecb 100644
--- a/tests/Unit/Module/Archive/API/ArchiveTest.php
+++ b/tests/Unit/Module/Archive/API/ArchiveTest.php
@@ -7,12 +7,15 @@
use Internal\DLoad\Module\Archive\Archive;
use Internal\DLoad\Module\Archive\Exception\ArchiveException;
use Internal\DLoad\Tests\Unit\Module\Archive\Stub\TestArchive;
-use PHPUnit\Framework\Attributes\CoversClass;
-use PHPUnit\Framework\Attributes\DataProvider;
-use PHPUnit\Framework\TestCase;
-
-#[CoversClass(Archive::class)]
-final class ArchiveTest extends TestCase
+use Testo\Assert;
+use Testo\Codecov\Covers;
+use Testo\Data\DataProvider;
+use Testo\Expect;
+use Testo\Lifecycle\BeforeTest;
+use Testo\Test;
+
+#[Covers(Archive::class)]
+final class ArchiveTest
{
private \SplFileInfo $archiveFile;
@@ -31,9 +34,9 @@ public static function provideFileTypes(): \Generator
yield 'non-existent extension' => [$files, 'jpg', 0];
}
- public function testExtractYieldsFilesFromArchive(): void
+ #[Test]
+ public function extractYieldsFilesFromArchive(): void
{
- // Arrange
$file1 = new \SplFileInfo('/path/to/file1.txt');
$file2 = new \SplFileInfo('/path/to/file2.txt');
@@ -41,64 +44,56 @@ public function testExtractYieldsFilesFromArchive(): void
$archive->addFile('file1.txt', $file1);
$archive->addFile('file2.txt', $file2);
- // Act
$result = [];
foreach ($archive->extract() as $path => $fileInfo) {
$result[$path] = $fileInfo;
}
- // Assert
- self::assertCount(2, $result);
- self::assertSame($file1, $result['file1.txt']);
- self::assertSame($file2, $result['file2.txt']);
+ Assert::count($result, 2);
+ Assert::same($result['file1.txt'], $file1);
+ Assert::same($result['file2.txt'], $file2);
}
- public function testExtractThrowsArchiveException(): void
+ #[Test]
+ public function extractThrowsArchiveException(): void
{
- // Arrange
$archive = new TestArchive($this->archiveFile);
$archive->throwExceptionOnExtract('Custom error message');
- // Assert
- $this->expectException(ArchiveException::class);
- $this->expectExceptionMessage('Custom error message');
+ Expect::exception(ArchiveException::class)->withMessage('Custom error message');
- // Act
\iterator_to_array($archive->extract());
}
- public function testExtractReturnsDestinationFileWhenRequested(): void
+ #[Test]
+ public function extractReturnsDestinationFileWhenRequested(): void
{
- // Arrange
$sourceFile = new \SplFileInfo('/path/to/source.txt');
$destinationFile = new \SplFileInfo('/path/to/destination.txt');
$archive = new TestArchive($this->archiveFile);
$archive->addFile('source.txt', $sourceFile);
- // Act
$generator = $archive->extract();
$path = $generator->key();
$info = $generator->current();
$result = $generator->send($destinationFile);
- // Assert
- self::assertSame('source.txt', $path);
- self::assertSame($sourceFile, $info);
- self::assertSame($destinationFile, $result);
+ Assert::same($path, 'source.txt');
+ Assert::same($info, $sourceFile);
+ Assert::same($result, $destinationFile);
}
#[DataProvider('provideFileTypes')]
- public function testExtractFilteringByFileType(array $files, string $extension, int $expectedCount): void
+ #[Test]
+ public function extractFilteringByFileType(array $files, string $extension, int $expectedCount): void
{
- // Arrange
$archive = new TestArchive($this->archiveFile);
foreach ($files as $path => $fileInfo) {
$archive->addFile($path, $fileInfo);
}
- // Act
$extracted = [];
foreach ($archive->extract() as $path => $fileInfo) {
// Filter files by extension
@@ -107,13 +102,12 @@ public function testExtractFilteringByFileType(array $files, string $extension,
}
}
- // Assert
- self::assertCount($expectedCount, $extracted);
+ Assert::count($extracted, $expectedCount);
}
- protected function setUp(): void
+ #[BeforeTest]
+ protected function prepare(): void
{
- // Arrange
$this->archiveFile = new \SplFileInfo(__FILE__); // Use this file as a valid file
}
}
diff --git a/tests/Unit/Module/Archive/Internal/ArchiveTest.php b/tests/Unit/Module/Archive/Internal/ArchiveTest.php
index 2569c3c..41d1a06 100644
--- a/tests/Unit/Module/Archive/Internal/ArchiveTest.php
+++ b/tests/Unit/Module/Archive/Internal/ArchiveTest.php
@@ -5,55 +5,49 @@
namespace Internal\DLoad\Tests\Unit\Module\Archive\Internal;
use Internal\DLoad\Module\Archive\Internal\Archive;
-use PHPUnit\Framework\Attributes\CoversClass;
-use PHPUnit\Framework\TestCase;
+use Testo\Assert;
+use Testo\Codecov\Covers;
+use Testo\Expect;
+use Testo\Test;
-#[CoversClass(Archive::class)]
-final class ArchiveTest extends TestCase
+#[Covers(Archive::class)]
+final class ArchiveTest
{
- public function testConstructorThrowsExceptionWhenFileDoesNotExist(): void
+ #[Test]
+ public function constructorThrowsExceptionWhenFileDoesNotExist(): void
{
- // Arrange
- $file = $this->createMock(\SplFileInfo::class);
- $file->method('isFile')->willReturn(false);
- $file->method('getFilename')->willReturn('non-existent.zip');
+ $file = \Mockery::mock(\SplFileInfo::class);
+ $file->allows('isFile')->andReturn(false);
+ $file->allows('getFilename')->andReturn('non-existent.zip');
- // Assert
- $this->expectException(\InvalidArgumentException::class);
- $this->expectExceptionMessage('Archive "non-existent.zip" is not a file.');
+ Expect::exception(\InvalidArgumentException::class)->withMessage('Archive "non-existent.zip" is not a file.');
- // Act
$this->createArchiveInstance($file);
}
- public function testConstructorThrowsExceptionWhenFileIsNotReadable(): void
+ #[Test]
+ public function constructorThrowsExceptionWhenFileIsNotReadable(): void
{
- // Arrange
- $file = $this->createMock(\SplFileInfo::class);
- $file->method('isFile')->willReturn(true);
- $file->method('isReadable')->willReturn(false);
- $file->method('getFilename')->willReturn('unreadable.zip');
+ $file = \Mockery::mock(\SplFileInfo::class);
+ $file->allows('isFile')->andReturn(true);
+ $file->allows('isReadable')->andReturn(false);
+ $file->allows('getFilename')->andReturn('unreadable.zip');
- // Assert
- $this->expectException(\InvalidArgumentException::class);
- $this->expectExceptionMessage('Archive file "unreadable.zip" is not readable.');
+ Expect::exception(\InvalidArgumentException::class)->withMessage('Archive file "unreadable.zip" is not readable.');
- // Act
$this->createArchiveInstance($file);
}
- public function testConstructorSucceedsWithValidFile(): void
+ #[Test]
+ public function constructorSucceedsWithValidFile(): void
{
- // Arrange
- $file = $this->createMock(\SplFileInfo::class);
- $file->method('isFile')->willReturn(true);
- $file->method('isReadable')->willReturn(true);
+ $file = \Mockery::mock(\SplFileInfo::class);
+ $file->allows('isFile')->andReturn(true);
+ $file->allows('isReadable')->andReturn(true);
- // Act
$archive = $this->createArchiveInstance($file);
- // Assert
- self::assertInstanceOf(Archive::class, $archive);
+ Assert::instanceOf($archive, Archive::class);
}
/**
diff --git a/tests/Unit/Module/Archive/Internal/NullArchiveTest.php b/tests/Unit/Module/Archive/Internal/NullArchiveTest.php
index cef161e..7458b87 100644
--- a/tests/Unit/Module/Archive/Internal/NullArchiveTest.php
+++ b/tests/Unit/Module/Archive/Internal/NullArchiveTest.php
@@ -5,57 +5,52 @@
namespace Internal\DLoad\Tests\Unit\Module\Archive\Internal;
use Internal\DLoad\Module\Archive\Internal\NullArchive;
-use PHPUnit\Framework\Attributes\CoversClass;
-use PHPUnit\Framework\TestCase;
-
-#[CoversClass(NullArchive::class)]
-final class NullArchiveTest extends TestCase
+use Testo\Assert;
+use Testo\Codecov\Covers;
+use Testo\Core\Exception\SkipTest;
+use Testo\Expect;
+use Testo\Test;
+
+#[Covers(NullArchive::class)]
+final class NullArchiveTest
{
- public function testConstructorValidatesFile(): void
+ #[Test]
+ public function constructorValidatesFile(): void
{
- // Arrange
- $file = $this->createMock(\SplFileInfo::class);
- $file->method('isFile')->willReturn(false);
- $file->method('isReadable')->willReturn(true); // Must return true for parent constructor
- $file->method('getFilename')->willReturn('not-a-file');
+ $file = \Mockery::mock(\SplFileInfo::class);
+ $file->allows('isFile')->andReturn(false);
+ $file->allows('isReadable')->andReturn(true); // Must return true for parent constructor
+ $file->allows('getFilename')->andReturn('not-a-file');
- // Assert
- $this->expectException(\InvalidArgumentException::class);
- $this->expectExceptionMessage('Archive "not-a-file" is not a file.');
+ Expect::exception(\InvalidArgumentException::class)->withMessage('Archive "not-a-file" is not a file.');
- // Act
new NullArchive($file);
}
- public function testExtractYieldsFileAsItself(): void
+ #[Test]
+ public function extractYieldsFileAsItself(): void
{
- // Arrange
- $sourceFile = $this->createMock(\SplFileInfo::class);
- $sourceFile->method('isFile')->willReturn(true);
- $sourceFile->method('isReadable')->willReturn(true);
- $sourceFile->method('getPathname')->willReturn('/path/to/source-file');
- $sourceFile->method('getFilename')->willReturn('source-file');
+ $sourceFile = \Mockery::mock(\SplFileInfo::class);
+ $sourceFile->allows('isFile')->andReturn(true);
+ $sourceFile->allows('isReadable')->andReturn(true);
+ $sourceFile->allows('getPathname')->andReturn('/path/to/source-file');
+ $sourceFile->allows('getFilename')->andReturn('source-file');
$archive = new NullArchive($sourceFile);
- // Act
$generator = $archive->extract();
- // Assert - Check the file is yielded
$key = $generator->key();
$value = $generator->current();
- self::assertSame('/path/to/source-file', $key);
- self::assertSame($sourceFile, $value);
+ Assert::same($key, '/path/to/source-file');
+ Assert::same($value, $sourceFile);
}
- public function testExtractCopiesFileWhenDestinationProvided(): void
+ #[Test]
+ public function extractCopiesFileWhenDestinationProvided(): never
{
- // This test would require mocking the global copy function
- // In a real-world scenario, I'd use a package like mockery/php-overload, but for now,
- // I'll focus on the unit tests that don't require global function mocking
-
- // Instead, we'll verify the behavior through the integration test
- $this->addToAssertionCount(1);
+ // Copying goes through the global `copy()` function, which cannot be stubbed here.
+ throw new SkipTest('Covered by ArchiveIntegrationTest.');
}
}
diff --git a/tests/Unit/Module/Archive/Internal/PharAwareArchiveTest.php b/tests/Unit/Module/Archive/Internal/PharAwareArchiveTest.php
index 1729e5e..64ae48f 100644
--- a/tests/Unit/Module/Archive/Internal/PharAwareArchiveTest.php
+++ b/tests/Unit/Module/Archive/Internal/PharAwareArchiveTest.php
@@ -6,37 +6,36 @@
use Internal\DLoad\Module\Archive\Exception\ArchiveException;
use Internal\DLoad\Module\Archive\Internal\PharAwareArchive;
-use PHPUnit\Framework\Attributes\CoversClass;
-use PHPUnit\Framework\TestCase;
+use Testo\Codecov\Covers;
+use Testo\Expect;
+use Testo\Lifecycle\BeforeTest;
+use Testo\Test;
-#[CoversClass(PharAwareArchive::class)]
-final class PharAwareArchiveTest extends TestCase
+#[Covers(PharAwareArchive::class)]
+final class PharAwareArchiveTest
{
private \SplFileInfo $fileInfo;
- public function testExtractThrowsExceptionWhenArchiveIsNotReadable(): void
+ #[Test]
+ public function extractThrowsExceptionWhenArchiveIsNotReadable(): void
{
- // Arrange
- $pharData = $this->createMock(\PharData::class);
- $pharData->method('isReadable')->willReturn(false);
- $pharData->method('getPathname')->willReturn('unreadable.phar');
+ $pharData = \Mockery::mock(\PharData::class);
+ $pharData->allows('isReadable')->andReturn(false);
+ $pharData->allows('getPathname')->andReturn('unreadable.phar');
$archive = $this->createPharAwareArchive($pharData);
- // Assert
- $this->expectException(ArchiveException::class);
- $this->expectExceptionMessage('Could not open "unreadable.phar" for reading.');
+ Expect::exception(ArchiveException::class)->withMessage('Could not open "unreadable.phar" for reading.');
- // Act
\iterator_to_array($archive->extract());
}
- protected function setUp(): void
+ #[BeforeTest]
+ protected function prepare(): void
{
- // Arrange
- $this->fileInfo = $this->createMock(\SplFileInfo::class);
- $this->fileInfo->method('isFile')->willReturn(true);
- $this->fileInfo->method('isReadable')->willReturn(true);
+ $this->fileInfo = \Mockery::mock(\SplFileInfo::class);
+ $this->fileInfo->allows('isFile')->andReturn(true);
+ $this->fileInfo->allows('isReadable')->andReturn(true);
}
/**
diff --git a/tests/Unit/Module/Archive/Internal/TarPharArchiveTest.php b/tests/Unit/Module/Archive/Internal/TarPharArchiveTest.php
index b6c0097..3df78cb 100644
--- a/tests/Unit/Module/Archive/Internal/TarPharArchiveTest.php
+++ b/tests/Unit/Module/Archive/Internal/TarPharArchiveTest.php
@@ -5,25 +5,23 @@
namespace Internal\DLoad\Tests\Unit\Module\Archive\Internal;
use Internal\DLoad\Module\Archive\Internal\TarPharArchive;
-use PHPUnit\Framework\Attributes\CoversClass;
-use PHPUnit\Framework\TestCase;
+use Testo\Codecov\Covers;
+use Testo\Expect;
+use Testo\Test;
-#[CoversClass(TarPharArchive::class)]
-final class TarPharArchiveTest extends TestCase
+#[Covers(TarPharArchive::class)]
+final class TarPharArchiveTest
{
- public function testConstructorValidatesFile(): void
+ #[Test]
+ public function constructorValidatesFile(): void
{
- // Arrange
- $file = $this->createMock(\SplFileInfo::class);
- $file->method('isFile')->willReturn(true);
- $file->method('isReadable')->willReturn(false);
- $file->method('getFilename')->willReturn('unreadable.tar.gz');
+ $file = \Mockery::mock(\SplFileInfo::class);
+ $file->allows('isFile')->andReturn(true);
+ $file->allows('isReadable')->andReturn(false);
+ $file->allows('getFilename')->andReturn('unreadable.tar.gz');
- // Assert
- $this->expectException(\InvalidArgumentException::class);
- $this->expectExceptionMessage('Archive file "unreadable.tar.gz" is not readable.');
+ Expect::exception(\InvalidArgumentException::class)->withMessage('Archive file "unreadable.tar.gz" is not readable.');
- // Act
new TarPharArchive($file);
}
}
diff --git a/tests/Unit/Module/Archive/Internal/ZipPharArchiveTest.php b/tests/Unit/Module/Archive/Internal/ZipPharArchiveTest.php
index 4211987..f6f2372 100644
--- a/tests/Unit/Module/Archive/Internal/ZipPharArchiveTest.php
+++ b/tests/Unit/Module/Archive/Internal/ZipPharArchiveTest.php
@@ -5,24 +5,22 @@
namespace Internal\DLoad\Tests\Unit\Module\Archive\Internal;
use Internal\DLoad\Module\Archive\Internal\ZipPharArchive;
-use PHPUnit\Framework\Attributes\CoversClass;
-use PHPUnit\Framework\TestCase;
+use Testo\Codecov\Covers;
+use Testo\Expect;
+use Testo\Test;
-#[CoversClass(ZipPharArchive::class)]
-final class ZipPharArchiveTest extends TestCase
+#[Covers(ZipPharArchive::class)]
+final class ZipPharArchiveTest
{
- public function testConstructorValidatesFile(): void
+ #[Test]
+ public function constructorValidatesFile(): void
{
- // Arrange
- $file = $this->createMock(\SplFileInfo::class);
- $file->method('isFile')->willReturn(false);
- $file->method('getFilename')->willReturn('invalid.zip');
+ $file = \Mockery::mock(\SplFileInfo::class);
+ $file->allows('isFile')->andReturn(false);
+ $file->allows('getFilename')->andReturn('invalid.zip');
- // Assert
- $this->expectException(\InvalidArgumentException::class);
- $this->expectExceptionMessage('Archive "invalid.zip" is not a file.');
+ Expect::exception(\InvalidArgumentException::class)->withMessage('Archive "invalid.zip" is not a file.');
- // Act
new ZipPharArchive($file);
}
}
diff --git a/tests/Unit/Module/Binary/BinaryVersionTest.php b/tests/Unit/Module/Binary/BinaryVersionTest.php
index 01cbee0..17e1583 100644
--- a/tests/Unit/Module/Binary/BinaryVersionTest.php
+++ b/tests/Unit/Module/Binary/BinaryVersionTest.php
@@ -5,12 +5,13 @@
namespace Internal\DLoad\Tests\Unit\Module\Binary;
use Internal\DLoad\Module\Binary\BinaryVersion;
-use PHPUnit\Framework\Attributes\CoversClass;
-use PHPUnit\Framework\Attributes\DataProvider;
-use PHPUnit\Framework\TestCase;
+use Testo\Assert;
+use Testo\Codecov\Covers;
+use Testo\Data\DataProvider;
+use Testo\Test;
-#[CoversClass(BinaryVersion::class)]
-final class BinaryVersionTest extends TestCase
+#[Covers(BinaryVersion::class)]
+final class BinaryVersionTest
{
/**
* Provides test cases for semantic version extraction.
@@ -132,41 +133,37 @@ public static function provideFallbackVersionOutputs(): \Generator
* Tests that the resolver correctly extracts semantic versions.
*/
#[DataProvider('provideSemanticVersionOutputs')]
- public function testResolveVersionExtractsSemanticVersions(string $output, string $string, ?string $number): void
+ #[Test]
+ public function resolveVersionExtractsSemanticVersions(string $output, string $string, ?string $number): void
{
- // Act
$result = BinaryVersion::fromBinaryOutput($output);
- // Assert
- self::assertSame($string, $result->string);
- self::assertSame($number ?? $string, $result->number);
+ Assert::same($result->string, $string);
+ Assert::same($result->number, $number ?? $string);
}
/**
* Tests that the resolver correctly extracts versions using fallback patterns.
*/
#[DataProvider('provideFallbackVersionOutputs')]
- public function testResolveVersionExtractsVersionsWithFallbacks(string $output, ?string $number): void
+ #[Test]
+ public function resolveVersionExtractsVersionsWithFallbacks(string $output, ?string $number): void
{
- // Act
$result = BinaryVersion::fromBinaryOutput($output);
- // Assert
- self::assertSame($number, $result->number);
+ Assert::same($result->number, $number);
}
/**
* Tests that the resolver returns null when no version can be extracted.
*/
- public function testResolveVersionReturnsNullWhenNoVersionFound(): void
+ #[Test]
+ public function resolveVersionReturnsNullWhenNoVersionFound(): void
{
- // Arrange
$output = 'This output contains no version information.';
- // Act
$result = BinaryVersion::fromBinaryOutput($output);
- // Assert
- self::assertNull($result->number);
+ Assert::null($result->number);
}
}
diff --git a/tests/Unit/Module/Common/ArchitectureTest.php b/tests/Unit/Module/Common/ArchitectureTest.php
index 93ccfd9..422c236 100644
--- a/tests/Unit/Module/Common/ArchitectureTest.php
+++ b/tests/Unit/Module/Common/ArchitectureTest.php
@@ -5,10 +5,11 @@
namespace Internal\DLoad\Tests\Unit\Module\Common;
use Internal\DLoad\Module\Common\Architecture;
-use PHPUnit\Framework\Attributes\DataProvider;
-use PHPUnit\Framework\TestCase;
+use Testo\Assert;
+use Testo\Data\DataProvider;
+use Testo\Test;
-class ArchitectureTest extends TestCase
+class ArchitectureTest
{
public static function provideBuildNames(): iterable
{
@@ -23,8 +24,9 @@ public static function provideBuildNames(): iterable
}
#[DataProvider('provideBuildNames')]
- public function testTryFromBuildName(string $name, ?Architecture $expected): void
+ #[Test]
+ public function tryFromBuildName(string $name, ?Architecture $expected): void
{
- self::assertSame($expected, Architecture::tryFromBuildName($name));
+ Assert::same(Architecture::tryFromBuildName($name), $expected);
}
}
diff --git a/tests/Unit/Module/Common/FileSystem/PathTest.php b/tests/Unit/Module/Common/FileSystem/PathTest.php
index 5cdbe43..73797ff 100644
--- a/tests/Unit/Module/Common/FileSystem/PathTest.php
+++ b/tests/Unit/Module/Common/FileSystem/PathTest.php
@@ -5,12 +5,15 @@
namespace Internal\DLoad\Tests\Unit\Module\Common\FileSystem;
use Internal\Path;
-use PHPUnit\Framework\Attributes\CoversClass;
-use PHPUnit\Framework\Attributes\DataProvider;
-use PHPUnit\Framework\TestCase;
-
-#[CoversClass(Path::class)]
-final class PathTest extends TestCase
+use Testo\Assert;
+use Testo\Codecov\Covers;
+use Testo\Core\Exception\SkipTest;
+use Testo\Data\DataProvider;
+use Testo\Expect;
+use Testo\Test;
+
+#[Covers(Path::class)]
+final class PathTest
{
public static function providePathsForAbsoluteDetection(): \Generator
{
@@ -37,312 +40,266 @@ public static function providePathsForParent(): \Generator
yield ['some/path/file.txt', 'some/path'];
}
- public function testCreateReturnsPathInstance(): void
+ #[Test]
+ public function createReturnsPathInstance(): void
{
- // Arrange & Act
$path = Path::create('test/path');
- // Assert
- self::assertInstanceOf(Path::class, $path);
+ Assert::instanceOf($path, Path::class);
}
- public function testCreateWithEmptyPathReturnsCurrentDirectory(): void
+ #[Test]
+ public function createWithEmptyPathReturnsCurrentDirectory(): void
{
- // Arrange & Act
$path = Path::create('');
- // Assert
- self::assertSame('.', (string) $path);
+ Assert::same((string) $path, '.');
}
- public function testCreateNormalizesDirectorySeparators(): void
+ #[Test]
+ public function createNormalizesDirectorySeparators(): void
{
- // Arrange & Act
$path = Path::create('test\\path/mixed/separators\\here');
- // Assert
- self::assertSame('test/path/mixed/separators/here', (string) $path);
+ Assert::same((string) $path, 'test/path/mixed/separators/here');
}
- public function testCreateRemovesMultipleSeparators(): void
+ #[Test]
+ public function createRemovesMultipleSeparators(): void
{
- // Arrange & Act
$path = Path::create('test//path///extra//separators');
- // Assert
- self::assertSame('test/path/extra/separators', (string) $path);
+ Assert::same((string) $path, 'test/path/extra/separators');
}
- public function testCreateResolvesCurrentDirectorySegments(): void
+ #[Test]
+ public function createResolvesCurrentDirectorySegments(): void
{
- // Arrange & Act
$path = Path::create('test/./path/./current');
- // Assert
- self::assertSame('test/path/current', (string) $path);
+ Assert::same((string) $path, 'test/path/current');
}
- public function testCreateResolvesParentDirectorySegments(): void
+ #[Test]
+ public function createResolvesParentDirectorySegments(): void
{
- // Arrange & Act
$path = Path::create('test/parent/../path');
- // Assert
- self::assertSame('test/path', (string) $path);
+ Assert::same((string) $path, 'test/path');
}
- public function testCreateThrowsExceptionForInvalidParentNavigation(): void
+ #[Test]
+ public function createThrowsExceptionForInvalidParentNavigation(): void
{
- // Arrange & Assert
- $this->expectException(\LogicException::class);
- $this->expectExceptionMessage('Cannot go up from root');
+ Expect::exception(\LogicException::class)->withMessageContaining('Cannot go up from root');
- // Act
Path::create('/test/../..');
}
- public function testJoinPathComponents(): void
+ #[Test]
+ public function joinPathComponents(): void
{
- // Arrange
$path = Path::create('base/path');
- // Act
$result = $path->join('additional', 'components');
- // Assert
- self::assertSame('base/path/additional/components', (string) $result);
+ Assert::same((string) $result, 'base/path/additional/components');
}
- public function testJoinWithEmptyComponentsIgnoresThem(): void
+ #[Test]
+ public function joinWithEmptyComponentsIgnoresThem(): void
{
- // Arrange
$path = Path::create('base/path');
- // Act
$result = $path->join('', 'component', '');
- // Assert
- self::assertSame('base/path/component', (string) $result);
+ Assert::same((string) $result, 'base/path/component');
}
- public function testJoinWithPathObjects(): void
+ #[Test]
+ public function joinWithPathObjects(): void
{
- // Arrange
$path = Path::create('base/path');
$additionalPath = Path::create('additional/path');
- // Assert (prepare for expected exception)
- $this->expectException(\LogicException::class);
- $this->expectExceptionMessage('Joining an absolute path is not allowed');
+ Expect::exception(\LogicException::class)->withMessageContaining('Joining an absolute path is not allowed');
- // Act
// Using an absolute Path object which should throw
$path->join($additionalPath->absolute());
}
- public function testJoinWithRelativePathObjects(): void
+ #[Test]
+ public function joinWithRelativePathObjects(): void
{
- // Arrange
$path = Path::create('base/path');
$additionalPath = Path::create('additional/path');
- // Act
$result = $path->join($additionalPath);
- // Assert
- self::assertSame('base/path/additional/path', (string) $result);
+ Assert::same((string) $result, 'base/path/additional/path');
}
- public function testJoinWithAbsolutePathString(): void
+ #[Test]
+ public function joinWithAbsolutePathString(): void
{
- // Arrange
$path = Path::create('base/path');
- // Assert (prepare for expected exception)
- $this->expectException(\LogicException::class);
- $this->expectExceptionMessage('Joining an absolute path is not allowed');
+ Expect::exception(\LogicException::class)->withMessageContaining('Joining an absolute path is not allowed');
- // Act
$path->join('/absolute/path');
}
- public function testName(): void
+ #[Test]
+ public function name(): void
{
- // Arrange
$path = Path::create('some/path/file.txt');
- // Act
$name = $path->name();
- // Assert
- self::assertSame('file.txt', $name);
+ Assert::same($name, 'file.txt');
}
- public function testNameWithNoDirectoryComponents(): void
+ #[Test]
+ public function nameWithNoDirectoryComponents(): void
{
- // Arrange
$path = Path::create('file.txt');
- // Act
$name = $path->name();
- // Assert
- self::assertSame('file.txt', $name);
+ Assert::same($name, 'file.txt');
}
- public function testStem(): void
+ #[Test]
+ public function stem(): void
{
- // Arrange
$path = Path::create('some/path/file.txt');
- // Act
$stem = $path->stem();
- // Assert
- self::assertSame('file', $stem);
+ Assert::same($stem, 'file');
}
- public function testStemWithNoExtension(): void
+ #[Test]
+ public function stemWithNoExtension(): void
{
- // Arrange
$path = Path::create('some/path/file');
- // Act
$stem = $path->stem();
- // Assert
- self::assertSame('file', $stem);
+ Assert::same($stem, 'file');
}
- public function testStemWithMultipleDots(): void
+ #[Test]
+ public function stemWithMultipleDots(): void
{
- // Arrange
$path = Path::create('some/path/file.config.json');
- // Act
$stem = $path->stem();
- // Assert
- self::assertSame('file.config', $stem);
+ Assert::same($stem, 'file.config');
}
- public function testStemWithHiddenFile(): void
+ #[Test]
+ public function stemWithHiddenFile(): void
{
- // Arrange
$path = Path::create('some/path/.hidden');
- // Act
$stem = $path->stem();
- // Assert
- self::assertSame('.hidden', $stem);
+ Assert::same($stem, '.hidden');
}
- public function testExtension(): void
+ #[Test]
+ public function extension(): void
{
- // Arrange
$path = Path::create('some/path/file.txt');
- // Act
$extension = $path->extension();
- // Assert
- self::assertSame('txt', $extension);
+ Assert::same($extension, 'txt');
}
- public function testExtensionWithMultipleDots(): void
+ #[Test]
+ public function extensionWithMultipleDots(): void
{
- // Arrange
$path = Path::create('some/path/file.config.json');
- // Act
$extension = $path->extension();
- // Assert
- self::assertSame('json', $extension);
+ Assert::same($extension, 'json');
}
- public function testExtensionWithNoExtension(): void
+ #[Test]
+ public function extensionWithNoExtension(): void
{
- // Arrange
$path = Path::create('some/path/file');
- // Act
$extension = $path->extension();
- // Assert
- self::assertSame('', $extension);
+ Assert::same($extension, '');
}
- public function testExtensionWithHiddenFile(): void
+ #[Test]
+ public function extensionWithHiddenFile(): void
{
- // Arrange
$path = Path::create('some/path/.hidden');
- // Act
$extension = $path->extension();
- // Assert
- self::assertSame('hidden', $extension);
+ Assert::same($extension, 'hidden');
}
#[DataProvider('providePathsForParent')]
- public function testParent(string $inputPath, string $expectedParent): void
+ #[Test]
+ public function parent(string $inputPath, string $expectedParent): void
{
- // Arrange
$path = Path::create($inputPath);
- // Act
$parent = $path->parent();
- // Assert
- self::assertSame($expectedParent, (string) $parent);
+ Assert::same((string) $parent, $expectedParent);
}
#[DataProvider('providePathsForAbsoluteDetection')]
- public function testIsAbsolute(string $pathString, bool $expected): void
+ #[Test]
+ public function isAbsolute(string $pathString, bool $expected): void
{
- // Arrange
$path = Path::create($pathString);
- // Act
$isAbsolute = $path->isAbsolute();
- // Assert
- self::assertSame($expected, $isAbsolute, "Path '$pathString' should be " . ($expected ? 'absolute' : 'relative'));
+ Assert::same($isAbsolute, $expected, "Path '$pathString' should be " . ($expected ? 'absolute' : 'relative'));
}
- public function testIsRelative(): void
+ #[Test]
+ public function isRelative(): void
{
- // Arrange
$absolutePath = DIRECTORY_SEPARATOR === '\\'
? Path::create('C:/Users/test')
: Path::create('/home/user');
$relativePath = Path::create('relative/path');
- // Act & Assert
- self::assertFalse($absolutePath->isRelative());
- self::assertTrue($relativePath->isRelative());
+ Assert::false($absolutePath->isRelative());
+ Assert::true($relativePath->isRelative());
}
/**
* This test uses real filesystem access to check if a path exists.
* It creates a temporary file and checks its existence.
*/
- public function testExists(): void
+ #[Test]
+ public function exists(): void
{
- // Arrange
$tempFile = \tempnam(\sys_get_temp_dir(), 'path_test_');
- self::assertIsString($tempFile, 'Failed to create temp file');
+ Assert::true(\is_string($tempFile), 'Failed to create temp file');
$path = Path::create($tempFile);
$nonExistingPath = Path::create('non/existing/path/file.txt');
- // Act & Assert
try {
- self::assertTrue($path->exists());
- self::assertFalse($nonExistingPath->exists());
+ Assert::true($path->exists());
+ Assert::false($nonExistingPath->exists());
} finally {
// Clean up
@\unlink($tempFile);
@@ -353,98 +310,91 @@ public function testExists(): void
* Note: This test might have limitations depending on the environment.
* It checks the expected behavior of isDir without requiring an actual directory to exist.
*/
- public function testIsDir(): void
+ #[Test]
+ public function isDir(): void
{
- // Arrange
$currentDirPath = Path::create('.');
$parentDirPath = Path::create('..');
$filePath = Path::create('file.txt');
- // Act & Assert
- self::assertTrue($currentDirPath->isDir());
- self::assertTrue($parentDirPath->isDir());
- self::assertFalse($filePath->isDir());
+ Assert::true($currentDirPath->isDir());
+ Assert::true($parentDirPath->isDir());
+ Assert::false($filePath->isDir());
}
/**
* Note: This test might have limitations depending on the environment.
* It checks the expected behavior of isFile without requiring an actual file to exist.
*/
- public function testIsFile(): void
+ #[Test]
+ public function isFile(): void
{
- // Arrange
$currentDirPath = Path::create('.');
$parentDirPath = Path::create('..');
$filePath = Path::create('file.txt');
// Create a temporary file to test with
$tempFile = \tempnam(\sys_get_temp_dir(), 'path_test_');
- self::assertIsString($tempFile, 'Failed to create temp file');
+ Assert::true(\is_string($tempFile), 'Failed to create temp file');
$realFilePath = Path::create($tempFile);
- // Act & Assert
try {
- self::assertFalse($currentDirPath->isFile());
- self::assertFalse($parentDirPath->isFile());
- self::assertFalse($filePath->isFile()); // Doesn't exist yet
- self::assertTrue($realFilePath->isFile(), "Temporary file should be a file `$realFilePath`");
+ Assert::false($currentDirPath->isFile());
+ Assert::false($parentDirPath->isFile());
+ Assert::false($filePath->isFile()); // Doesn't exist yet
+ Assert::true($realFilePath->isFile(), "Temporary file should be a file `$realFilePath`");
} finally {
// Clean up
@\unlink($tempFile);
}
}
- public function testAbsoluteForAlreadyAbsolutePath(): void
+ #[Test]
+ public function absoluteForAlreadyAbsolutePath(): void
{
- // Arrange
$absolutePath = DIRECTORY_SEPARATOR === '\\'
? Path::create('C:/Users/test')
: Path::create('/home/user');
- // Act
$result = $absolutePath->absolute();
- // Assert
- self::assertSame((string) $absolutePath, (string) $result);
+ Assert::same((string) $result, (string) $absolutePath);
}
- public function testAbsoluteForRelativePath(): void
+ #[Test]
+ public function absoluteForRelativePath(): void
{
- // Arrange
$relativePath = Path::create('relative/path');
// Skip this test if we can't get cwd
$cwd = \getcwd();
if ($cwd === false) {
- self::markTestSkipped('Cannot get current working directory');
+ throw new SkipTest('Cannot get current working directory');
}
$expected = Path::create($cwd . DIRECTORY_SEPARATOR . 'relative/path');
- // Act
$result = $relativePath->absolute();
- // Assert
- self::assertSame((string) $expected, (string) $result);
+ Assert::same((string) $result, (string) $expected);
}
- public function testCreateWindowsTmpFile(): void
+ #[Test]
+ public function createWindowsTmpFile(): void
{
$path = Path::create('C:\Users\roxbl\AppData\Local\Temp\patB6E7.tmp');
- self::assertSame('C:/Users/roxbl/AppData/Local/Temp/patB6E7.tmp', (string) $path);
+ Assert::same((string) $path, 'C:/Users/roxbl/AppData/Local/Temp/patB6E7.tmp');
}
- public function testToString(): void
+ #[Test]
+ public function toString(): void
{
- // Arrange
$pathString = 'some/path/file.txt';
$path = Path::create($pathString);
- // Act
$result = (string) $path;
- // Assert
- self::assertSame('some/path/file.txt', $result);
+ Assert::same($result, 'some/path/file.txt');
}
}
diff --git a/tests/Unit/Module/Common/OperatingSystemTest.php b/tests/Unit/Module/Common/OperatingSystemTest.php
index 4bc35f6..40b4436 100644
--- a/tests/Unit/Module/Common/OperatingSystemTest.php
+++ b/tests/Unit/Module/Common/OperatingSystemTest.php
@@ -5,10 +5,11 @@
namespace Internal\DLoad\Tests\Unit\Module\Common;
use Internal\DLoad\Module\Common\OperatingSystem;
-use PHPUnit\Framework\Attributes\DataProvider;
-use PHPUnit\Framework\TestCase;
+use Testo\Assert;
+use Testo\Data\DataProvider;
+use Testo\Test;
-class OperatingSystemTest extends TestCase
+class OperatingSystemTest
{
public static function provideBuildNames(): iterable
{
@@ -23,8 +24,9 @@ public static function provideBuildNames(): iterable
}
#[DataProvider('provideBuildNames')]
- public function testTryFromBuildName(string $name, ?OperatingSystem $expected): void
+ #[Test]
+ public function tryFromBuildName(string $name, ?OperatingSystem $expected): void
{
- self::assertSame($expected, OperatingSystem::tryFromBuildName($name));
+ Assert::same(OperatingSystem::tryFromBuildName($name), $expected);
}
}
diff --git a/tests/Unit/Module/Common/StabilityTest.php b/tests/Unit/Module/Common/StabilityTest.php
index ee77300..e397405 100644
--- a/tests/Unit/Module/Common/StabilityTest.php
+++ b/tests/Unit/Module/Common/StabilityTest.php
@@ -6,12 +6,13 @@
use Internal\DLoad\Module\Common\Input\Build;
use Internal\DLoad\Module\Common\Stability;
-use PHPUnit\Framework\Attributes\CoversClass;
-use PHPUnit\Framework\Attributes\DataProvider;
-use PHPUnit\Framework\TestCase;
+use Testo\Assert;
+use Testo\Codecov\Covers;
+use Testo\Data\DataProvider;
+use Testo\Test;
-#[CoversClass(Stability::class)]
-final class StabilityTest extends TestCase
+#[Covers(Stability::class)]
+final class StabilityTest
{
public static function provideStabilityCasesWithExpectedWeights(): \Generator
{
@@ -92,191 +93,162 @@ public static function provideStabilityTransitivityScenarios(): \Generator
];
}
- public function testAllCasesHaveUniqueValues(): void
+ #[Test]
+ public function allCasesHaveUniqueValues(): void
{
- // Arrange
$cases = Stability::cases();
$values = \array_map(static fn(Stability $case): string => $case->value, $cases);
- // Act
$uniqueValues = \array_unique($values);
- // Assert
- self::assertCount(\count($values), $uniqueValues, 'All stability cases should have unique values');
+ Assert::count($uniqueValues, \count($values), 'All stability cases should have unique values');
}
#[DataProvider('provideStabilityCasesWithExpectedWeights')]
- public function testGetWeightReturnsExpectedValue(Stability $stability, int $expectedWeight): void
+ #[Test]
+ public function getWeightReturnsExpectedValue(Stability $stability, int $expectedWeight): void
{
- // Act
$weight = $stability->getWeight();
- // Assert
- self::assertSame($expectedWeight, $weight);
+ Assert::same($weight, $expectedWeight);
}
- public function testWeightsAreInDescendingOrder(): void
+ #[Test]
+ public function weightsAreInDescendingOrder(): void
{
- // Arrange
$cases = Stability::cases();
$weights = \array_map(static fn(Stability $case): int => $case->getWeight(), $cases);
- // Act
$sortedWeights = $weights;
\rsort($sortedWeights);
- // Assert
- self::assertSame($sortedWeights, $weights, 'Stability weights should be in descending order in the enum definition');
+ Assert::same($weights, $sortedWeights, 'Stability weights should be in descending order in the enum definition');
}
#[DataProvider('provideStabilityMeetsMinimumScenarios')]
- public function testMeetsMinimumComparesStabilityLevelsCorrectly(
+ #[Test]
+ public function meetsMinimumComparesStabilityLevelsCorrectly(
Stability $current,
Stability $minimum,
bool $expectedResult,
): void {
- // Act
$result = $current->meetsMinimum($minimum);
- // Assert
- self::assertSame($expectedResult, $result);
+ Assert::same($result, $expectedResult);
}
- public function testFromGlobalsReturnsStable(): void
+ #[Test]
+ public function fromGlobalsReturnsStable(): void
{
- // Act
$stability = Stability::fromGlobals();
- // Assert
- self::assertSame(Stability::Stable, $stability);
+ Assert::same($stability, Stability::Stable);
}
#[DataProvider('provideValidStabilityStrings')]
- public function testFromStringReturnsCorrectStabilityForValidStrings(string $input, Stability $expected): void
+ #[Test]
+ public function fromStringReturnsCorrectStabilityForValidStrings(string $input, Stability $expected): void
{
- // Act
$result = Stability::fromString($input);
- // Assert
- self::assertSame($expected, $result);
+ Assert::same($result, $expected);
}
#[DataProvider('provideInvalidStabilityStrings')]
- public function testFromStringReturnsNullForInvalidStrings(string $input): void
+ #[Test]
+ public function fromStringReturnsNullForInvalidStrings(string $input): void
{
- // Act
$result = Stability::fromString($input);
- // Assert
- self::assertNull($result);
+ Assert::null($result);
}
#[DataProvider('provideBuildConfigurationsForCreate')]
- public function testCreateReturnsCorrectStabilityFromBuildConfig(
+ #[Test]
+ public function createReturnsCorrectStabilityFromBuildConfig(
?string $buildStability,
Stability $expectedStability,
): void {
- // Arrange
$build = new Build();
$build->stability = $buildStability;
- // Act
$result = Stability::create($build);
- // Assert
- self::assertSame($expectedStability, $result);
+ Assert::same($result, $expectedStability);
}
- public function testCreateWithNullBuildStabilityUsesFromGlobals(): void
+ #[Test]
+ public function createWithNullBuildStabilityUsesFromGlobals(): void
{
- // Arrange
$build = new Build();
$build->stability = null;
- // Act
$result = Stability::create($build);
- // Assert
- self::assertSame(Stability::fromGlobals(), $result);
+ Assert::same($result, Stability::fromGlobals());
}
- public function testCreateWithInvalidBuildStabilityUsesFromGlobals(): void
+ #[Test]
+ public function createWithInvalidBuildStabilityUsesFromGlobals(): void
{
- // Arrange
$build = new Build();
$build->stability = 'completely-invalid-stability';
- // Act
$result = Stability::create($build);
- // Assert
- self::assertSame(Stability::fromGlobals(), $result);
+ Assert::same($result, Stability::fromGlobals());
}
- public function testEnumImplementsFactoriableInterface(): void
+ #[Test]
+ public function enumImplementsFactoriableInterface(): void
{
- // Assert
- self::assertContains('Internal\DLoad\Service\Factoriable', \class_implements(Stability::class));
+ Assert::contains(\class_implements(Stability::class), 'Internal\DLoad\Service\Factoriable');
}
- public function testAllEnumValuesAreStrings(): void
+ #[Test]
+ public function allEnumValuesAreStrings(): void
{
- // Arrange
$cases = Stability::cases();
- // Act & Assert
foreach ($cases as $case) {
- self::assertIsString($case->value, "Stability case {$case->name} should have a string value");
+ Assert::true(\is_string($case->value), "Stability case {$case->name} should have a string value");
}
}
#[DataProvider('provideStabilityTransitivityScenarios')]
- public function testMeetsMinimumTransitivity(
+ #[Test]
+ public function meetsMinimumTransitivity(
Stability $first,
Stability $second,
Stability $third,
bool $expectedResult,
): void {
- // Arrange
$firstMeetsSecond = $first->meetsMinimum($second);
$secondMeetsThird = $second->meetsMinimum($third);
- // Act
$firstMeetsThird = $first->meetsMinimum($third);
- // Assert
if ($firstMeetsSecond && $secondMeetsThird) {
- self::assertSame(
- $expectedResult,
- $firstMeetsThird,
- 'If A meets B and B meets C, then A should meet C (transitivity)',
- );
+ Assert::same($firstMeetsThird, $expectedResult, 'If A meets B and B meets C, then A should meet C (transitivity)');
} else {
// If the premise is false, we can't test transitivity
- self::assertTrue(true, 'Transitivity test skipped due to false premise');
+ Assert::true(true, 'Transitivity test skipped due to false premise');
}
}
- public function testStabilityOrderingIsConsistent(): void
+ #[Test]
+ public function stabilityOrderingIsConsistent(): void
{
- // Arrange
$cases = Stability::cases();
- // Act & Assert
for ($i = 0; $i < \count($cases) - 1; $i++) {
for ($j = $i + 1; $j < \count($cases); $j++) {
$higher = $cases[$i];
$lower = $cases[$j];
- self::assertTrue(
- $higher->meetsMinimum($lower),
- "Stability {$higher->name} should meet minimum {$lower->name} based on enum order",
- );
+ Assert::true($higher->meetsMinimum($lower), "Stability {$higher->name} should meet minimum {$lower->name} based on enum order");
- self::assertFalse(
- $lower->meetsMinimum($higher),
- "Stability {$lower->name} should not meet minimum {$higher->name} based on enum order",
- );
+ Assert::false($lower->meetsMinimum($higher), "Stability {$lower->name} should not meet minimum {$higher->name} based on enum order");
}
}
}
diff --git a/tests/Unit/Module/Downloader/Exception/NothingExtractedTest.php b/tests/Unit/Module/Downloader/Exception/NothingExtractedTest.php
index 448399c..ead7520 100644
--- a/tests/Unit/Module/Downloader/Exception/NothingExtractedTest.php
+++ b/tests/Unit/Module/Downloader/Exception/NothingExtractedTest.php
@@ -5,46 +5,42 @@
namespace Internal\DLoad\Tests\Unit\Module\Downloader\Exception;
use Internal\DLoad\Module\Downloader\Exception\NothingExtracted;
-use PHPUnit\Framework\Attributes\CoversClass;
-use PHPUnit\Framework\TestCase;
+use Testo\Assert;
+use Testo\Codecov\Covers;
+use Testo\Test;
-#[CoversClass(NothingExtracted::class)]
-final class NothingExtractedTest extends TestCase
+#[Covers(NothingExtracted::class)]
+final class NothingExtractedTest
{
- public function testMessageListsRulesAndArchiveContent(): void
+ #[Test]
+ public function messageListsRulesAndArchiveContent(): void
{
- // Arrange
$exception = new NothingExtracted(
assetName: 'roadrunner-2025.1.15-windows-amd64.zip',
rules: ['binary `/^roadrunner-.*/`'],
files: ['CHANGELOG.md', 'LICENSE', 'rr.exe'],
);
- // Act
$message = $exception->getMessage();
- // Assert
- self::assertStringContainsString(
+ Assert::string($message)->contains(
'Nothing was extracted from `roadrunner-2025.1.15-windows-amd64.zip`: '
. 'none of the 3 file(s) inside matches the extraction rules.',
- $message,
);
- self::assertStringContainsString('Extraction rules: binary `/^roadrunner-.*/`', $message);
- self::assertStringContainsString('Files in the asset: CHANGELOG.md, LICENSE, rr.exe', $message);
+ Assert::string($message)->contains('Extraction rules: binary `/^roadrunner-.*/`');
+ Assert::string($message)->contains('Files in the asset: CHANGELOG.md, LICENSE, rr.exe');
}
- public function testMessageTruncatesLongFileList(): void
+ #[Test]
+ public function messageTruncatesLongFileList(): void
{
- // Arrange
$files = \array_map(static fn(int $i): string => "file-{$i}.txt", \range(1, 25));
- // Act
$message = (new NothingExtracted('archive.tar.gz', [], $files))->getMessage();
- // Assert
- self::assertStringContainsString('Extraction rules: none', $message);
- self::assertStringContainsString('file-20.txt', $message);
- self::assertStringNotContainsString('file-21.txt', $message);
- self::assertStringContainsString('and 5 more', $message);
+ Assert::string($message)->contains('Extraction rules: none');
+ Assert::string($message)->contains('file-20.txt');
+ Assert::string($message)->notContains('file-21.txt');
+ Assert::string($message)->contains('and 5 more');
}
}
diff --git a/tests/Unit/Module/Downloader/Internal/Diagnostics/DownloadDiagnosticsTest.php b/tests/Unit/Module/Downloader/Internal/Diagnostics/DownloadDiagnosticsTest.php
index 174229d..df1e7d3 100644
--- a/tests/Unit/Module/Downloader/Internal/Diagnostics/DownloadDiagnosticsTest.php
+++ b/tests/Unit/Module/Downloader/Internal/Diagnostics/DownloadDiagnosticsTest.php
@@ -12,63 +12,57 @@
use Internal\DLoad\Module\Config\Schema\Embed\Software;
use Internal\DLoad\Module\Downloader\Internal\Diagnostics\DownloadDiagnostics;
use Internal\DLoad\Module\Repository\Exception\ApiException;
-use PHPUnit\Framework\Attributes\CoversClass;
-use PHPUnit\Framework\TestCase;
-
-#[CoversClass(DownloadDiagnostics::class)]
-#[CoversClass(\Internal\DLoad\Module\Downloader\Internal\Diagnostics\RepositoryAttempt::class)]
-#[CoversClass(\Internal\DLoad\Module\Downloader\Internal\Diagnostics\ReleaseAttempt::class)]
-final class DownloadDiagnosticsTest extends TestCase
+use Testo\Assert;
+use Testo\Codecov\Covers;
+use Testo\Test;
+
+#[Covers(DownloadDiagnostics::class)]
+#[Covers(\Internal\DLoad\Module\Downloader\Internal\Diagnostics\RepositoryAttempt::class)]
+#[Covers(\Internal\DLoad\Module\Downloader\Internal\Diagnostics\ReleaseAttempt::class)]
+final class DownloadDiagnosticsTest
{
- public function testReportDescribesTheRequestedConditions(): void
+ #[Test]
+ public function reportDescribesTheRequestedConditions(): void
{
- // Arrange
$diagnostics = self::diagnostics(version: '^2.0', type: Type::Binary);
- // Act
$report = $diagnostics->render();
- // Assert
- self::assertStringContainsString(
+ Assert::string($report)->contains(
'Requested: version `^2.0`, OS `linux`, architecture `amd64`, '
. 'minimum stability `stable`, asset type `binary`.',
- $report,
);
}
- public function testReportMentionsMissingRepositoryConfiguration(): void
+ #[Test]
+ public function reportMentionsMissingRepositoryConfiguration(): void
{
- // Arrange
$diagnostics = self::diagnostics();
- // Act
$report = $diagnostics->render();
- // Assert
- self::assertStringContainsString('No repositories are configured for `app`', $report);
+ Assert::string($report)->contains('No repositories are configured for `app`');
}
- public function testReportListsAvailableReleasesWhenNothingMatches(): void
+ #[Test]
+ public function reportListsAvailableReleasesWhenNothingMatches(): void
{
- // Arrange
$diagnostics = self::diagnostics(version: '^5.0');
$repository = $diagnostics->addRepository('github', 'owner/repo', '/^app-.*/');
$repository->matchedReleases = 0;
$repository->registerFetchedReleases(['v1.2.0', 'v1.1.0']);
- // Act
$report = $diagnostics->render();
- // Assert
- self::assertStringContainsString('Tried 1 repository(ies):', $report);
- self::assertStringContainsString('1) github `owner/repo`', $report);
- self::assertStringContainsString('0 release(s) match the requested version and stability.', $report);
- self::assertStringContainsString('Releases available in the repository: v1.2.0, v1.1.0', $report);
+ Assert::string($report)->contains('Tried 1 repository(ies):');
+ Assert::string($report)->contains('1) github `owner/repo`');
+ Assert::string($report)->contains('0 release(s) match the requested version and stability.');
+ Assert::string($report)->contains('Releases available in the repository: v1.2.0, v1.1.0');
}
- public function testReportListsCheckedReleasesWithTheirAssets(): void
+ #[Test]
+ public function reportListsCheckedReleasesWithTheirAssets(): void
{
- // Arrange
$diagnostics = self::diagnostics();
$repository = $diagnostics->addRepository('github', 'owner/repo', '/^app-.*/');
$repository->matchedReleases = 2;
@@ -81,23 +75,18 @@ public function testReportListsCheckedReleasesWithTheirAssets(): void
$failed->registerAssets(['app-1.1.0-linux-amd64.tar.gz']);
$failed->addFailure('app-1.1.0-linux-amd64.tar.gz', new \RuntimeException('Broken archive'));
- // Act
$report = $diagnostics->render();
- // Assert
- self::assertStringContainsString('2 release(s) match the requested version and stability.', $report);
- self::assertStringContainsString('Checked releases:', $report);
- self::assertStringContainsString('- v1.2.0: 2 asset(s), no asset matches OS `linux`', $report);
- self::assertStringContainsString('Assets: app-1.2.0-darwin-arm64.tar.gz, app-1.2.0-windows-amd64.zip', $report);
- self::assertStringContainsString(
- 'Failed asset `app-1.1.0-linux-amd64.tar.gz`: Broken archive',
- $report,
- );
+ Assert::string($report)->contains('2 release(s) match the requested version and stability.');
+ Assert::string($report)->contains('Checked releases:');
+ Assert::string($report)->contains('- v1.2.0: 2 asset(s), no asset matches OS `linux`');
+ Assert::string($report)->contains('Assets: app-1.2.0-darwin-arm64.tar.gz, app-1.2.0-windows-amd64.zip');
+ Assert::string($report)->contains('Failed asset `app-1.1.0-linux-amd64.tar.gz`: Broken archive');
}
- public function testReportContainsRepositoryLevelError(): void
+ #[Test]
+ public function reportContainsRepositoryLevelError(): void
{
- // Arrange
$diagnostics = self::diagnostics();
$repository = $diagnostics->addRepository('github', 'owner/repo', '/^app-.*/');
$repository->error = new ApiException(
@@ -108,19 +97,17 @@ public function testReportContainsRepositoryLevelError(): void
$fallback = $diagnostics->addRepository('gitlab', 'group/app', '/^app-.*/');
$fallback->matchedReleases = 0;
- // Act
$report = $diagnostics->render();
- // Assert
- self::assertStringContainsString('Tried 2 repository(ies):', $report);
- self::assertStringContainsString('GitHub API rate limit exceeded.', $report);
- self::assertStringContainsString('Set the GITHUB_TOKEN environment variable.', $report);
- self::assertStringContainsString('2) gitlab `group/app`', $report);
+ Assert::string($report)->contains('Tried 2 repository(ies):');
+ Assert::string($report)->contains('GitHub API rate limit exceeded.');
+ Assert::string($report)->contains('Set the GITHUB_TOKEN environment variable.');
+ Assert::string($report)->contains('2) gitlab `group/app`');
}
- public function testReleaseWithoutFetchedAssetListIsNotReportedAsEmpty(): void
+ #[Test]
+ public function releaseWithoutFetchedAssetListIsNotReportedAsEmpty(): void
{
- // Arrange
$diagnostics = self::diagnostics();
$repository = $diagnostics->addRepository('github', 'owner/repo', '/^app-.*/');
$repository->matchedReleases = 1;
@@ -128,17 +115,15 @@ public function testReleaseWithoutFetchedAssetListIsNotReportedAsEmpty(): void
// An error interrupted the attempt before the asset list was fetched
$repository->addRelease('v1.2.0')->reason = 'GitHub API is unavailable: HTTP 502 Bad Gateway';
- // Act
$report = $diagnostics->render();
- // Assert
- self::assertStringContainsString('- v1.2.0: asset list not loaded, GitHub API is unavailable', $report);
- self::assertStringNotContainsString('0 asset(s)', $report);
+ Assert::string($report)->contains('- v1.2.0: asset list not loaded, GitHub API is unavailable');
+ Assert::string($report)->notContains('0 asset(s)');
}
- public function testReportLimitsTheNumberOfDescribedReleases(): void
+ #[Test]
+ public function reportLimitsTheNumberOfDescribedReleases(): void
{
- // Arrange
$diagnostics = self::diagnostics();
$repository = $diagnostics->addRepository('github', 'owner/repo', '/^app-.*/');
$repository->matchedReleases = 8;
@@ -147,14 +132,12 @@ public function testReportLimitsTheNumberOfDescribedReleases(): void
$repository->addRelease("v1.0.{$i}")->registerAssets(["app-1.0.{$i}-linux-amd64.tar.gz"]);
}
- // Act
$report = $diagnostics->render();
- // Assert
- self::assertStringContainsString('- v1.0.8:', $report);
- self::assertStringContainsString('- v1.0.6:', $report);
- self::assertStringNotContainsString('- v1.0.5:', $report);
- self::assertStringContainsString('and 5 more release(s)', $report);
+ Assert::string($report)->contains('- v1.0.8:');
+ Assert::string($report)->contains('- v1.0.6:');
+ Assert::string($report)->notContains('- v1.0.5:');
+ Assert::string($report)->contains('and 5 more release(s)');
}
/**
diff --git a/tests/Unit/Module/Repository/AssetsCollectionTest.php b/tests/Unit/Module/Repository/AssetsCollectionTest.php
index 90c4e81..35c74e9 100644
--- a/tests/Unit/Module/Repository/AssetsCollectionTest.php
+++ b/tests/Unit/Module/Repository/AssetsCollectionTest.php
@@ -11,12 +11,14 @@
use Internal\DLoad\Tests\Unit\Module\Repository\Stub\AssetStub;
use Internal\DLoad\Tests\Unit\Module\Repository\Stub\ReleaseStub;
use Internal\DLoad\Tests\Unit\Module\Repository\Stub\RepositoryStub;
-use PHPUnit\Framework\Attributes\CoversClass;
-use PHPUnit\Framework\Attributes\DataProvider;
-use PHPUnit\Framework\TestCase;
-
-#[CoversClass(AssetsCollection::class)]
-final class AssetsCollectionTest extends TestCase
+use Testo\Assert;
+use Testo\Codecov\Covers;
+use Testo\Data\DataProvider;
+use Testo\Lifecycle\BeforeTest;
+use Testo\Test;
+
+#[Covers(AssetsCollection::class)]
+final class AssetsCollectionTest
{
private RepositoryStub $repository;
private ReleaseStub $release;
@@ -61,42 +63,39 @@ public static function provideNamePatterns(): \Generator
];
}
- public function testWhereOperatingSystemFiltersAssetsByOs(): void
+ #[Test]
+ public function whereOperatingSystemFiltersAssetsByOs(): void
{
- // Act
$result = $this->collection->whereOperatingSystem(OperatingSystem::Linux);
- // Assert
- self::assertCount(2, $result);
+ Assert::count($result, 2);
foreach ($result as $asset) {
- self::assertSame(OperatingSystem::Linux, $asset->getOperatingSystem());
+ Assert::same($asset->getOperatingSystem(), OperatingSystem::Linux);
}
}
- public function testWhereArchitectureFiltersAssetsByArchitecture(): void
+ #[Test]
+ public function whereArchitectureFiltersAssetsByArchitecture(): void
{
- // Act
$result = $this->collection->whereArchitecture(Architecture::ARM_64);
- // Assert
- self::assertCount(3, $result);
+ Assert::count($result, 3);
foreach ($result as $asset) {
- self::assertSame(Architecture::ARM_64, $asset->getArchitecture());
+ Assert::same($asset->getArchitecture(), Architecture::ARM_64);
}
}
#[DataProvider('provideNamePatterns')]
- public function testWhereNameMatchesFiltersAssetsByNamePattern(
+ #[Test]
+ public function whereNameMatchesFiltersAssetsByNamePattern(
string $pattern,
array $expectedMatches,
): void {
- // Act
$result = $this->collection->whereNameMatches($pattern);
- // Assert
- self::assertCount(\count($expectedMatches), $result);
+ Assert::count($result, \count($expectedMatches));
$actualNames = \array_map(
static fn($asset) => $asset->getName(),
@@ -104,68 +103,64 @@ public function testWhereNameMatchesFiltersAssetsByNamePattern(
);
foreach ($expectedMatches as $expectedName) {
- self::assertContains($expectedName, $actualNames);
+ Assert::contains($actualNames, $expectedName);
}
}
- public function testWhereNameMatchesWithInvalidPattern(): void
+ #[Test]
+ public function whereNameMatchesWithInvalidPattern(): void
{
- // Act
$new = $this->collection->whereNameMatches('/invalid[pattern/');
- // Assert
- self::assertCount(0, $new);
+ Assert::count($new, 0);
}
- public function testChainedFiltersWorkCorrectly(): void
+ #[Test]
+ public function chainedFiltersWorkCorrectly(): void
{
- // Act
$result = $this->collection
->whereOperatingSystem(OperatingSystem::Linux)
->whereArchitecture(Architecture::ARM_64);
- // Assert
- self::assertCount(1, $result);
+ Assert::count($result, 1);
$asset = $result->first();
- self::assertSame('package-1.2.3-linux-arm64.tar.gz', $asset->getName());
+ Assert::same($asset->getName(), 'package-1.2.3-linux-arm64.tar.gz');
}
- public function testFirstReturnsFirstAssetOrNull(): void
+ #[Test]
+ public function firstReturnsFirstAssetOrNull(): void
{
// Act with non-empty collection
$first = $this->collection->first();
- // Assert
- self::assertNotNull($first);
- self::assertSame('package-1.2.3-linux-x64.tar.gz', $first->getName());
+ Assert::notNull($first);
+ Assert::same($first->getName(), 'package-1.2.3-linux-x64.tar.gz');
// Act with empty collection
$empty = new AssetsCollection([]);
$result = $empty->first();
- // Assert
- self::assertNull($result);
+ Assert::null($result);
}
- public function testEmptyReturnsTrueForEmptyCollection(): void
+ #[Test]
+ public function emptyReturnsTrueForEmptyCollection(): void
{
// Act with non-empty collection
$resultNonEmpty = $this->collection->empty();
- // Assert
- self::assertFalse($resultNonEmpty);
+ Assert::false($resultNonEmpty);
// Act with empty collection
$empty = new AssetsCollection([]);
$resultEmpty = $empty->empty();
- // Assert
- self::assertTrue($resultEmpty);
+ Assert::true($resultEmpty);
}
- protected function setUp(): void
+ #[BeforeTest]
+ protected function prepare(): void
{
- // Arrange
$this->repository = new RepositoryStub('vendor/package');
$this->release = new ReleaseStub(
$this->repository,
diff --git a/tests/Unit/Module/Repository/Internal/CachedGeneratorTest.php b/tests/Unit/Module/Repository/Internal/CachedGeneratorTest.php
index cb992ad..6609518 100644
--- a/tests/Unit/Module/Repository/Internal/CachedGeneratorTest.php
+++ b/tests/Unit/Module/Repository/Internal/CachedGeneratorTest.php
@@ -6,12 +6,13 @@
use Generator;
use Internal\DLoad\Module\Repository\Internal\CachedGenerator;
-use PHPUnit\Framework\Attributes\CoversClass;
-use PHPUnit\Framework\Attributes\DataProvider;
-use PHPUnit\Framework\TestCase;
+use Testo\Assert;
+use Testo\Codecov\Covers;
+use Testo\Data\DataProvider;
+use Testo\Test;
-#[CoversClass(CachedGenerator::class)]
-final class CachedGeneratorTest extends TestCase
+#[Covers(CachedGenerator::class)]
+final class CachedGeneratorTest
{
/**
* Data provider for traversable types test.
@@ -36,9 +37,9 @@ public static function provideTraversables(): \Generator
/**
* Tests that the generator correctly caches items as they are yielded.
*/
- public function testIterationCachesYieldedItems(): void
+ #[Test]
+ public function iterationCachesYieldedItems(): void
{
- // Arrange
$generator = $this->createGenerator(5);
$cachedGenerator = new CachedGenerator($generator);
@@ -56,134 +57,119 @@ public function testIterationCachesYieldedItems(): void
// Get all items using a new iteration
$allItems = \iterator_to_array($cachedGenerator);
- // Assert
- self::assertCount(3, $iteratedItems);
- self::assertSame([0, 1, 2], $iteratedItems);
+ Assert::count($iteratedItems, 3);
+ Assert::same($iteratedItems, [0, 1, 2]);
// self::assertCount(5, $allItems);
- self::assertSame([0, 1, 2, 3, 4], $allItems);
+ Assert::same($allItems, [0, 1, 2, 3, 4]);
}
/**
* Tests that first() returns the first element from the generator.
*/
- public function testFirstReturnsFirstElement(): void
+ #[Test]
+ public function firstReturnsFirstElement(): void
{
- // Arrange
$generator = $this->createGenerator(3);
$cachedGenerator = new CachedGenerator($generator);
- // Act
$firstItem = $cachedGenerator->first();
- // Assert
- self::assertSame(0, $firstItem);
+ Assert::same($firstItem, 0);
}
/**
* Tests that first() returns null when the generator is empty.
*/
- public function testFirstReturnsNullForEmptyGenerator(): void
+ #[Test]
+ public function firstReturnsNullForEmptyGenerator(): void
{
- // Arrange
$generator = $this->createGenerator(0);
$cachedGenerator = new CachedGenerator($generator);
- // Act
$firstItem = $cachedGenerator->first();
- // Assert
- self::assertNull($firstItem);
+ Assert::null($firstItem);
}
/**
* Tests that first() can retrieve the first item without affecting future iterations.
*/
- public function testFirstDoesNotConsumeItemFromIteration(): void
+ #[Test]
+ public function firstDoesNotConsumeItemFromIteration(): void
{
- // Arrange
$generator = $this->createGenerator(3);
$cachedGenerator = new CachedGenerator($generator);
- // Act
$firstItem = $cachedGenerator->first();
$allItems = \iterator_to_array($cachedGenerator);
- // Assert
- self::assertSame(0, $firstItem);
- self::assertCount(3, $allItems);
- self::assertSame([0, 1, 2], $allItems);
+ Assert::same($firstItem, 0);
+ Assert::count($allItems, 3);
+ Assert::same($allItems, [0, 1, 2]);
}
/**
* Tests that isEmpty() correctly identifies empty generators.
*/
- public function testIsEmptyReturnsTrueForEmptyGenerator(): void
+ #[Test]
+ public function isEmptyReturnsTrueForEmptyGenerator(): void
{
- // Arrange
$generator = $this->createGenerator(0);
$cachedGenerator = new CachedGenerator($generator);
- // Act
$isEmpty = $cachedGenerator->isEmpty();
- // Assert
- self::assertTrue($isEmpty);
+ Assert::true($isEmpty);
}
/**
* Tests that isEmpty() correctly identifies non-empty generators.
*/
- public function testIsEmptyReturnsFalseForNonEmptyGenerator(): void
+ #[Test]
+ public function isEmptyReturnsFalseForNonEmptyGenerator(): void
{
- // Arrange
$generator = $this->createGenerator(1);
$cachedGenerator = new CachedGenerator($generator);
- // Act
$isEmpty = $cachedGenerator->isEmpty();
- // Assert
- self::assertFalse($isEmpty);
+ Assert::false($isEmpty);
}
/**
* Tests that count() correctly returns the number of items in the generator.
*/
- public function testCountReturnsCorrectItemCount(): void
+ #[Test]
+ public function countReturnsCorrectItemCount(): void
{
- // Arrange
$generator = $this->createGenerator(5);
$cachedGenerator = new CachedGenerator($generator);
- // Act
$count = $cachedGenerator->count();
- // Assert
- self::assertSame(5, $count);
+ Assert::same($count, 5);
}
/**
* Tests that count() returns zero for an empty generator.
*/
- public function testCountReturnsZeroForEmptyGenerator(): void
+ #[Test]
+ public function countReturnsZeroForEmptyGenerator(): void
{
- // Arrange
$generator = $this->createGenerator(0);
$cachedGenerator = new CachedGenerator($generator);
- // Act
$count = $cachedGenerator->count();
- // Assert
- self::assertSame(0, $count);
+ Assert::same($count, 0);
}
/**
* Tests that partial iteration followed by count() returns the correct total count.
*/
- public function testPartialIterationFollowedByCountReturnsCorrectTotal(): void
+ #[Test]
+ public function partialIterationFollowedByCountReturnsCorrectTotal(): void
{
- // Arrange
$generator = $this->createGenerator(5);
$cachedGenerator = new CachedGenerator($generator);
@@ -198,16 +184,15 @@ public function testPartialIterationFollowedByCountReturnsCorrectTotal(): void
// Get count
$count = $cachedGenerator->count();
- // Assert
- self::assertSame(5, $count);
+ Assert::same($count, 5);
}
/**
* Tests that the cache persists after a complete iteration.
*/
- public function testCachePersistsAfterCompleteIteration(): void
+ #[Test]
+ public function cachePersistsAfterCompleteIteration(): void
{
- // Arrange
$generator = $this->createGenerator(3);
$cachedGenerator = new CachedGenerator($generator);
@@ -217,9 +202,8 @@ public function testCachePersistsAfterCompleteIteration(): void
// Second iteration should use cache
$secondIteration = \iterator_to_array($cachedGenerator);
- // Assert
- self::assertSame($firstIteration, $secondIteration);
- self::assertSame([0, 1, 2], $firstIteration);
+ Assert::same($secondIteration, $firstIteration);
+ Assert::same($firstIteration, [0, 1, 2]);
}
/**
@@ -229,24 +213,22 @@ public function testCachePersistsAfterCompleteIteration(): void
* @param array $expected The expected result
*/
#[DataProvider('provideTraversables')]
- public function testHandlesVariousTraversableTypes(\Traversable $traversable, array $expected): void
+ #[Test]
+ public function handlesVariousTraversableTypes(\Traversable $traversable, array $expected): void
{
- // Arrange
$cachedGenerator = new CachedGenerator($traversable);
- // Act
$result = \iterator_to_array($cachedGenerator);
- // Assert
- self::assertSame($expected, $result);
+ Assert::same($result, $expected);
}
/**
* Tests that cached generator works correctly with nested generators.
*/
- public function testHandlesNestedGenerators(): void
+ #[Test]
+ public function handlesNestedGenerators(): void
{
- // Arrange
$nestedGenerator = function () {
yield from $this->createGenerator(2);
yield from $this->createGenerator(2, 10);
@@ -254,23 +236,20 @@ public function testHandlesNestedGenerators(): void
$cachedGenerator = new CachedGenerator($nestedGenerator());
- // Act
$result = \iterator_to_array($cachedGenerator);
- // Assert
- self::assertSame([0, 1, 10, 11], $result);
+ Assert::same($result, [0, 1, 10, 11]);
}
/**
* Tests behavior with large datasets to ensure memory effectiveness.
*/
- public function testHandlesLargeDatasets(): void
+ #[Test]
+ public function handlesLargeDatasets(): void
{
- // Arrange
$largeGenerator = $this->createGenerator(1000);
$cachedGenerator = new CachedGenerator($largeGenerator);
- // Act
$firstAccess = $cachedGenerator->first();
// Partially iterate
@@ -286,10 +265,9 @@ public function testHandlesLargeDatasets(): void
// Get count without completing iteration
$totalCount = $cachedGenerator->count();
- // Assert
- self::assertSame(0, $firstAccess);
- self::assertCount(10, $partialIteration);
- self::assertSame(1000, $totalCount);
+ Assert::same($firstAccess, 0);
+ Assert::count($partialIteration, 10);
+ Assert::same($totalCount, 1000);
}
/**
diff --git a/tests/Unit/Module/Repository/Internal/CollectionTest.php b/tests/Unit/Module/Repository/Internal/CollectionTest.php
index 9aed9b9..3791cfa 100644
--- a/tests/Unit/Module/Repository/Internal/CollectionTest.php
+++ b/tests/Unit/Module/Repository/Internal/CollectionTest.php
@@ -6,12 +6,13 @@
use Internal\DLoad\Module\Repository\Internal\Collection;
use Internal\DLoad\Module\Repository\Internal\Paginator;
-use PHPUnit\Framework\Attributes\CoversClass;
-use PHPUnit\Framework\Attributes\DataProvider;
-use PHPUnit\Framework\TestCase;
+use Testo\Assert;
+use Testo\Codecov\Covers;
+use Testo\Data\DataProvider;
+use Testo\Test;
-#[CoversClass(Collection::class)]
-final class CollectionTest extends TestCase
+#[Covers(Collection::class)]
+final class CollectionTest
{
public static function provideLimitCases(): \Generator
{
@@ -36,14 +37,15 @@ public static function provideLimitCases(): \Generator
];
}
- public function testCollectionWithIterable(): void
+ #[Test]
+ public function collectionWithIterable(): void
{
// Create a test collection class
$testCollection = new class([]) extends Collection {};
// Test with array
$arrayCollection = $testCollection::create(['item1', 'item2', 'item3']);
- $this->assertEquals(['item1', 'item2', 'item3'], $arrayCollection->toArray());
+ Assert::equals($arrayCollection->toArray(), ['item1', 'item2', 'item3']);
// Test with generator
$generator = static function () {
@@ -52,10 +54,11 @@ public function testCollectionWithIterable(): void
};
$generatorCollection = $testCollection::create($generator());
- $this->assertEquals(['gen1', 'gen2'], $generatorCollection->toArray());
+ Assert::equals($generatorCollection->toArray(), ['gen1', 'gen2']);
}
- public function testCollectionWithPaginator(): void
+ #[Test]
+ public function collectionWithPaginator(): void
{
// Create a test collection class
$testCollection = new class([]) extends Collection {};
@@ -72,13 +75,11 @@ public function testCollectionWithPaginator(): void
$collection = $testCollection::create($paginator);
// Test toArray() loads all pages
- $this->assertEquals(
- ['page1-item1', 'page1-item2', 'page2-item1', 'page2-item2'],
- $collection->toArray(),
- );
+ Assert::equals($collection->toArray(), ['page1-item1', 'page1-item2', 'page2-item1', 'page2-item2']);
}
- public function testFilterChaining(): void
+ #[Test]
+ public function filterChaining(): void
{
// Create a test collection class
$testCollection = new class([]) extends Collection {};
@@ -93,10 +94,11 @@ public function testFilterChaining(): void
->filter(static fn($item) => $item % 2 === 0);
// Check result
- $this->assertEquals([4, 6], $filtered->toArray());
+ Assert::equals($filtered->toArray(), [4, 6]);
}
- public function testFilterWithPaginator(): void
+ #[Test]
+ public function filterWithPaginator(): void
{
// Create a test collection class
$testCollection = new class([]) extends Collection {};
@@ -119,10 +121,11 @@ public function testFilterWithPaginator(): void
->filter(static fn($item) => $item % 2 === 0);
// Check result
- $this->assertEquals([4, 6, 8], $filtered->toArray());
+ Assert::equals($filtered->toArray(), [4, 6, 8]);
}
- public function testFirst(): void
+ #[Test]
+ public function first(): void
{
// Create a test collection class
$testCollection = new class([]) extends Collection {};
@@ -143,7 +146,7 @@ public function testFirst(): void
$paginator = Paginator::createFromGenerator($pageLoader(), null);
// It always starts the generator when the closure is called
- $this->assertTrue($pagesLoaded[0]);
+ Assert::true($pagesLoaded[0]);
// Create collection with paginator
$collection = $testCollection::create($paginator);
@@ -152,60 +155,55 @@ public function testFirst(): void
$filtered = $collection->filter(static fn($item) => $item->num < 7);
// At this point, no pages should be loaded
- $this->assertFalse($pagesLoaded[1]);
- $this->assertFalse($pagesLoaded[2]);
+ Assert::false($pagesLoaded[1]);
+ Assert::false($pagesLoaded[2]);
// Get first matching item
$first = $filtered->first();
- self::assertSame(0, $first->num);
+ Assert::same($first->num, 0);
// We got '0' from the 0-page, so we need to load the next pages
- $this->assertFalse($pagesLoaded[1]);
- $this->assertFalse($pagesLoaded[2]);
-
+ Assert::false($pagesLoaded[1]);
+ Assert::false($pagesLoaded[2]);
// Now the 1st page should be loaded to get "1" value
$first = $filtered->first(static fn($item) => $item->num > 0);
- self::assertSame(1, $first->num);
- $this->assertTrue($pagesLoaded[1]);
- $this->assertFalse($pagesLoaded[2]);
+ Assert::same($first->num, 1);
+ Assert::true($pagesLoaded[1]);
+ Assert::false($pagesLoaded[2]);
}
#[DataProvider('provideLimitCases')]
- public function testLimit(array $sourceItems, int $limit, array $expectedItems): void
+ #[Test]
+ public function limit(array $sourceItems, int $limit, array $expectedItems): void
{
- // Arrange
$testCollection = new class([]) extends Collection {};
$collection = $testCollection::create($sourceItems);
- // Act
$limited = $collection->limit($limit);
- // Assert
- self::assertEquals($expectedItems, $limited->toArray());
+ Assert::equals($limited->toArray(), $expectedItems);
// Check count matches expected
- self::assertCount(\count($expectedItems), $limited);
+ Assert::count($limited, \count($expectedItems));
}
- public function testLimitWithFilter(): void
+ #[Test]
+ public function limitWithFilter(): void
{
- // Arrange
$testCollection = new class([]) extends Collection {};
$collection = $testCollection::create([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
- // Act
$filtered = $collection
->filter(static fn($item) => $item > 3)
->limit(2);
- // Assert
- self::assertEquals([4, 5], $filtered->toArray());
- self::assertCount(2, $filtered);
+ Assert::equals($filtered->toArray(), [4, 5]);
+ Assert::count($filtered, 2);
}
- public function testLimitWithPaginator(): void
+ #[Test]
+ public function limitWithPaginator(): void
{
- // Arrange
$testCollection = new class([]) extends Collection {};
$pageLoader = static function (): \Generator {
@@ -217,34 +215,32 @@ public function testLimitWithPaginator(): void
$paginator = Paginator::createFromGenerator($pageLoader(), null);
$collection = $testCollection::create($paginator);
- // Act
$limited = $collection->limit(4);
- // Assert
- self::assertEquals([1, 2, 3, 4], $limited->toArray());
- self::assertCount(4, $limited);
+ Assert::equals($limited->toArray(), [1, 2, 3, 4]);
+ Assert::count($limited, 4);
}
- public function testLimitWithZeroCountResetsLimit(): void
+ #[Test]
+ public function limitWithZeroCountResetsLimit(): void
{
- // Arrange
$testCollection = new class([]) extends Collection {};
$collection = $testCollection::create([1, 2, 3, 4, 5]);
$limitedCollection = $collection->limit(2);
// Verify the limit was applied
- self::assertEquals([1, 2], $limitedCollection->toArray());
+ Assert::equals($limitedCollection->toArray(), [1, 2]);
// Act - apply zero limit to reset the limit
$resetCollection = $limitedCollection->limit(0);
// Assert - should have all items
- self::assertEquals([1, 2, 3, 4, 5], $resetCollection->toArray());
+ Assert::equals($resetCollection->toArray(), [1, 2, 3, 4, 5]);
}
- public function testLimitResetWithFilteredCollection(): void
+ #[Test]
+ public function limitResetWithFilteredCollection(): void
{
- // Arrange
$testCollection = new class([]) extends Collection {};
$collection = $testCollection::create([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
@@ -254,40 +250,37 @@ public function testLimitResetWithFilteredCollection(): void
->limit(2);
// Verify initial state
- self::assertEquals([4, 5], $filtered->toArray());
+ Assert::equals($filtered->toArray(), [4, 5]);
// Act - reset limit
$resetLimited = $filtered->limit(0);
// Assert - filter should still be applied, but not the limit
- self::assertEquals([4, 5, 6, 7, 8, 9, 10], $resetLimited->toArray());
+ Assert::equals($resetLimited->toArray(), [4, 5, 6, 7, 8, 9, 10]);
}
- public function testCountWithLimit(): void
+ #[Test]
+ public function countWithLimit(): void
{
- // Arrange
$testCollection = new class([]) extends Collection {};
$collection = $testCollection::create([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
- // Act
$limited = $collection->limit(3);
- // Assert
- self::assertCount(3, $limited);
+ Assert::count($limited, 3);
}
- public function testEmptyWithLimit(): void
+ #[Test]
+ public function emptyWithLimit(): void
{
- // Arrange
$testCollection = new class([]) extends Collection {};
$collection = $testCollection::create([1, 2, 3]);
- // Act & Assert
- self::assertFalse($collection->limit(1)->empty());
- self::assertFalse($collection->limit(0)->empty());
+ Assert::false($collection->limit(1)->empty());
+ Assert::false($collection->limit(0)->empty());
// With an empty source collection
$emptyCollection = $testCollection::create([]);
- self::assertTrue($emptyCollection->limit(5)->empty());
+ Assert::true($emptyCollection->limit(5)->empty());
}
}
diff --git a/tests/Unit/Module/Repository/Internal/GitHub/Api/ClientTest.php b/tests/Unit/Module/Repository/Internal/GitHub/Api/ClientTest.php
index 5ff50d2..b27ad59 100644
--- a/tests/Unit/Module/Repository/Internal/GitHub/Api/ClientTest.php
+++ b/tests/Unit/Module/Repository/Internal/GitHub/Api/ClientTest.php
@@ -11,19 +11,23 @@
use Internal\DLoad\Module\Repository\Exception\RateLimitException;
use Internal\DLoad\Module\Repository\Exception\RepositoryNotFoundException;
use Internal\DLoad\Module\Repository\Internal\GitHub\Api\Client;
+use Internal\DLoad\Tests\Unit\Module\Repository\Internal\GitHub\Stub\ClientExceptionStub;
use Internal\DLoad\Tests\Unit\Module\Repository\Internal\GitHub\Stub\ClientStub;
use Internal\DLoad\Tests\Unit\Module\Repository\Internal\GitHub\Stub\GitHubConfigStub;
use Internal\DLoad\Tests\Unit\Module\Repository\Internal\GitHub\Stub\HttpFactoryStub;
use Internal\DLoad\Tests\Unit\Module\Repository\Stub\ResponseStub;
-use PHPUnit\Framework\Attributes\CoversClass;
-use PHPUnit\Framework\Attributes\DataProvider;
-use PHPUnit\Framework\TestCase;
-use Psr\Http\Client\ClientExceptionInterface;
+use Psr\Http\Client\ClientInterface;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\UriInterface;
-
-#[CoversClass(Client::class)]
-final class ClientTest extends TestCase
+use Testo\Assert;
+use Testo\Codecov\Covers;
+use Testo\Data\DataProvider;
+use Testo\Expect;
+use Testo\Lifecycle\BeforeTest;
+use Testo\Test;
+
+#[Covers(Client::class)]
+final class ClientTest
{
private HttpFactoryStub $httpFactory;
private ClientStub $httpClient;
@@ -130,32 +134,30 @@ public static function provideErrorScenarios(): \Generator
];
}
- public function testRequestAddsDefaultHeaders(): void
+ #[Test]
+ public function requestAddsDefaultHeaders(): void
{
- // Arrange
$method = 'GET';
- $uri = $this->createMock(UriInterface::class);
- $request = $this->createMock(RequestInterface::class);
+ $uri = \Mockery::mock(UriInterface::class)->shouldIgnoreMissing();
+ $request = \Mockery::mock(RequestInterface::class)->shouldIgnoreMissing();
$response = ResponseStub::ok();
$this->httpFactory = $this->httpFactory->withRequest($method, $uri, $request);
$this->httpClient = $this->httpClient->withResponse($request, $response);
$this->client = new Client($this->httpFactory, $this->httpClient, $this->gitHubConfig);
- // Act
$result = $this->client->request($method, $uri);
- // Assert
- self::assertSame($response, $result);
+ Assert::same($result, $response);
}
- public function testRequestWithAuthTokenAddsAuthorizationHeader(): void
+ #[Test]
+ public function requestWithAuthTokenAddsAuthorizationHeader(): void
{
- // Arrange
$token = 'github_pat_test_token_123';
$method = 'GET';
- $uri = $this->createMock(UriInterface::class);
- $request = $this->createMock(RequestInterface::class);
+ $uri = \Mockery::mock(UriInterface::class)->shouldIgnoreMissing();
+ $request = \Mockery::mock(RequestInterface::class)->shouldIgnoreMissing();
$response = ResponseStub::ok();
$gitHubConfigWithToken = GitHubConfigStub::withToken($token);
@@ -163,142 +165,125 @@ public function testRequestWithAuthTokenAddsAuthorizationHeader(): void
$clientWithToken = new Client($this->httpFactory, $this->httpClient, $gitHubConfigWithToken);
- // Act
$result = $clientWithToken->request($method, $uri);
- // Assert
- self::assertEquals($response, $result);
+ Assert::equals($result, $response);
}
- public function testRequestWithoutTokenDoesNotAddAuthorizationHeader(): void
+ #[Test]
+ public function requestWithoutTokenDoesNotAddAuthorizationHeader(): void
{
- // Arrange
$method = 'GET';
- $uri = $this->createMock(UriInterface::class);
- $request = $this->createMock(RequestInterface::class);
+ $uri = \Mockery::mock(UriInterface::class)->shouldIgnoreMissing();
+ $request = \Mockery::mock(RequestInterface::class)->shouldIgnoreMissing();
$response = ResponseStub::ok();
$this->httpClient = $this->httpClient->withResponse($request, $response);
- // Act
$result = $this->client->request($method, $uri);
- // Assert
- self::assertEquals($response, $result);
+ Assert::equals($result, $response);
}
- public function testDetectsRateLimitResponseAndThrowsException(): void
+ #[Test]
+ public function detectsRateLimitResponseAndThrowsException(): void
{
- // Arrange
$method = 'GET';
- $uri = $this->createMock(UriInterface::class);
- $request = $this->createMock(RequestInterface::class);
+ $uri = \Mockery::mock(UriInterface::class)->shouldIgnoreMissing();
+ $request = \Mockery::mock(RequestInterface::class)->shouldIgnoreMissing();
$rateLimitResponse = ResponseStub::githubRateLimit();
$this->httpFactory = $this->httpFactory->withRequest($method, $uri, $request);
$this->httpClient = $this->httpClient->withResponse($request, $rateLimitResponse);
$this->client = new Client($this->httpFactory, $this->httpClient, $this->gitHubConfig);
- // Assert (before Act for exceptions)
- $this->expectException(RateLimitException::class);
- $this->expectExceptionMessage('rate limit exceeded');
+ Expect::exception(RateLimitException::class)->withMessageContaining('rate limit exceeded');
- // Act
$this->client->request($method, $uri);
}
- public function testRateLimitMessageSuggestsTokenWhenThereIsNoToken(): void
+ #[Test]
+ public function rateLimitMessageSuggestsTokenWhenThereIsNoToken(): void
{
- // Arrange
- $request = $this->createMock(RequestInterface::class);
+ $request = \Mockery::mock(RequestInterface::class)->shouldIgnoreMissing();
$this->httpClient = $this->httpClient->withResponse($request, ResponseStub::githubRateLimit());
$this->client = new Client($this->httpFactory, $this->httpClient, $this->gitHubConfig);
- // Act
try {
$this->client->sendRequest($request);
- self::fail('RateLimitException is expected.');
+ Assert::fail('RateLimitException is expected.');
} catch (RateLimitException $e) {
- // Assert
- self::assertStringContainsString('GITHUB_TOKEN', $e->getMessage());
- self::assertStringContainsString('No API token is configured', $e->getMessage());
+ Assert::string($e->getMessage())->contains('GITHUB_TOKEN');
+ Assert::string($e->getMessage())->contains('No API token is configured');
}
}
- public function testAuthenticationMessageMentionsConfiguredToken(): void
+ #[Test]
+ public function authenticationMessageMentionsConfiguredToken(): void
{
- // Arrange
- $request = $this->createMock(RequestInterface::class);
+ $request = \Mockery::mock(RequestInterface::class)->shouldIgnoreMissing();
$response = new ResponseStub(401, [], \json_encode(['message' => 'Bad credentials']));
$this->httpClient = $this->httpClient->withResponse($request, $response);
$client = new Client($this->httpFactory, $this->httpClient, GitHubConfigStub::withToken('invalid-token'));
- // Act
try {
$client->sendRequest($request);
- self::fail('AuthenticationException is expected.');
+ Assert::fail('AuthenticationException is expected.');
} catch (AuthenticationException $e) {
- // Assert
- self::assertStringContainsString('Bad credentials', $e->getMessage());
- self::assertStringContainsString('invalid, expired or revoked', $e->getMessage());
+ Assert::string($e->getMessage())->contains('Bad credentials');
+ Assert::string($e->getMessage())->contains('invalid, expired or revoked');
}
}
- public function testSendRequestDelegatesToHttpClient(): void
+ #[Test]
+ public function sendRequestDelegatesToHttpClient(): void
{
- // Arrange
- $request = $this->createMock(RequestInterface::class);
+ $request = \Mockery::mock(RequestInterface::class)->shouldIgnoreMissing();
$response = ResponseStub::ok();
$this->httpClient = $this->httpClient->withResponse($request, $response);
$this->client = new Client($this->httpFactory, $this->httpClient, $this->gitHubConfig);
- // Act
$result = $this->client->sendRequest($request);
- // Assert
- self::assertSame($response, $result);
+ Assert::same($result, $response);
}
- public function testSendRequestWrapsClientExceptionsIntoApiException(): void
+ #[Test]
+ public function sendRequestWrapsClientExceptionsIntoApiException(): void
{
- // Arrange
- $request = $this->createMock(RequestInterface::class);
- $clientException = $this->createMock(ClientExceptionInterface::class);
+ $request = \Mockery::mock(RequestInterface::class)->shouldIgnoreMissing();
+ $clientException = new ClientExceptionStub('connection reset');
$this->httpClient = $this->httpClient->withException($request, $clientException);
$this->client = new Client($this->httpFactory, $this->httpClient, $this->gitHubConfig);
- // Act
try {
$this->client->sendRequest($request);
- self::fail('ApiException is expected.');
+ Assert::fail('ApiException is expected.');
} catch (ApiException $e) {
- // Assert
- self::assertStringContainsString('Failed to reach GitHub API', $e->getMessage());
- self::assertSame($clientException, $e->getPrevious());
+ Assert::string($e->getMessage())->contains('Failed to reach GitHub API');
+ Assert::same($e->getPrevious(), $clientException);
}
}
#[DataProvider('provideRequestHeaders')]
- public function testRequestMergesHeadersCorrectly(array $additionalHeaders): void
+ #[Test]
+ public function requestMergesHeadersCorrectly(array $additionalHeaders): void
{
- // Arrange
$method = 'POST';
- $uri = $this->createMock(UriInterface::class);
- $request = $this->createMock(RequestInterface::class);
+ $uri = \Mockery::mock(UriInterface::class)->shouldIgnoreMissing();
+ $request = \Mockery::mock(RequestInterface::class)->shouldIgnoreMissing();
$response = ResponseStub::ok();
$this->httpFactory = $this->httpFactory->withRequest($method, $uri, $request);
$this->httpClient = $this->httpClient->withResponse($request, $response);
$this->client = new Client($this->httpFactory, $this->httpClient, $this->gitHubConfig);
- // Act
$result = $this->client->request($method, $uri, $additionalHeaders);
- // Assert
- self::assertSame($response, $result);
+ Assert::same($result, $response);
}
/**
@@ -306,35 +291,33 @@ public function testRequestMergesHeadersCorrectly(array $additionalHeaders): voi
* @param class-string<\Throwable>|null $expectedException
*/
#[DataProvider('provideErrorScenarios')]
- public function testUnsuccessfulResponsesAreConvertedIntoExceptions(
+ #[Test]
+ public function unsuccessfulResponsesAreConvertedIntoExceptions(
int $statusCode,
string $responseBody,
array $headers,
?string $expectedException,
): void {
- // Arrange
- $request = $this->createMock(RequestInterface::class);
+ $request = \Mockery::mock(RequestInterface::class)->shouldIgnoreMissing();
$response = new ResponseStub($statusCode, $headers, $responseBody);
$this->httpClient = $this->httpClient->withResponse($request, $response);
$this->client = new Client($this->httpFactory, $this->httpClient, $this->gitHubConfig);
- $expectedException === null or $this->expectException($expectedException);
+ $expectedException === null or Expect::exception($expectedException);
- // Act
$result = $this->client->sendRequest($request);
- // Assert
- self::assertSame($response, $result);
+ Assert::same($result, $response);
}
- protected function setUp(): void
+ #[BeforeTest]
+ protected function prepare(): void
{
- // Arrange (common setup)
$this->httpFactory = new HttpFactoryStub(
- uriFactory: fn() => $this->createMock(UriInterface::class),
- requestFactory: fn() => $this->createMock(RequestInterface::class),
- clientFactory: fn() => $this->createMock(\Psr\Http\Client\ClientInterface::class),
+ uriFactory: static fn() => \Mockery::mock(UriInterface::class)->shouldIgnoreMissing(),
+ requestFactory: static fn() => \Mockery::mock(RequestInterface::class)->shouldIgnoreMissing(),
+ clientFactory: static fn() => \Mockery::mock(ClientInterface::class)->shouldIgnoreMissing(),
);
$this->httpClient = new ClientStub();
$this->gitHubConfig = GitHubConfigStub::withoutToken();
diff --git a/tests/Unit/Module/Repository/Internal/GitHub/Api/ResponseValidatorTest.php b/tests/Unit/Module/Repository/Internal/GitHub/Api/ResponseValidatorTest.php
index d095510..6d13d98 100644
--- a/tests/Unit/Module/Repository/Internal/GitHub/Api/ResponseValidatorTest.php
+++ b/tests/Unit/Module/Repository/Internal/GitHub/Api/ResponseValidatorTest.php
@@ -11,29 +11,29 @@
use Internal\DLoad\Module\Repository\Internal\GitHub\Api\ResponseValidator;
use Internal\DLoad\Tests\Unit\Module\Repository\Stub\ResponseStub;
use Nyholm\Psr7\Request;
-use PHPUnit\Framework\Attributes\CoversClass;
-use PHPUnit\Framework\TestCase;
use Psr\Http\Message\RequestInterface;
-
-#[CoversClass(ResponseValidator::class)]
-#[CoversClass(\Internal\DLoad\Module\Repository\Internal\ResponseValidator::class)]
-final class ResponseValidatorTest extends TestCase
+use Testo\Assert;
+use Testo\Codecov\Covers;
+use Testo\Expect;
+use Testo\Test;
+
+#[Covers(ResponseValidator::class)]
+#[Covers(\Internal\DLoad\Module\Repository\Internal\ResponseValidator::class)]
+final class ResponseValidatorTest
{
- public function testSuccessfulResponsePassesValidation(): void
+ #[Test]
+ public function successfulResponsePassesValidation(): void
{
- // Arrange
$validator = new ResponseValidator(authenticated: false);
- // Act
$validator->validate(self::releasesRequest(), ResponseStub::ok('[]'));
- // Assert
- self::assertTrue(true, 'Successful responses must not throw.');
+ Assert::true(true, 'Successful responses must not throw.');
}
- public function testRateLimitWithoutTokenExplainsAnonymousLimit(): void
+ #[Test]
+ public function rateLimitWithoutTokenExplainsAnonymousLimit(): void
{
- // Arrange
$validator = new ResponseValidator(authenticated: false);
$response = new ResponseStub(
403,
@@ -41,17 +41,14 @@ public function testRateLimitWithoutTokenExplainsAnonymousLimit(): void
\json_encode(['message' => 'API rate limit exceeded for 1.2.3.4.']),
);
- // Assert (before Act for exceptions)
- $this->expectException(RateLimitException::class);
- $this->expectExceptionMessage('60 requests per hour');
+ Expect::exception(RateLimitException::class)->withMessageContaining('60 requests per hour');
- // Act
$validator->validate(self::releasesRequest(), $response);
}
- public function testRateLimitWithTokenReportsSpentQuotaAndResetTime(): void
+ #[Test]
+ public function rateLimitWithTokenReportsSpentQuotaAndResetTime(): void
{
- // Arrange
$validator = new ResponseValidator(authenticated: true);
$resetsAt = \time() + 600;
$response = new ResponseStub(
@@ -60,22 +57,20 @@ public function testRateLimitWithTokenReportsSpentQuotaAndResetTime(): void
\json_encode(['message' => 'API rate limit exceeded']),
);
- // Act
try {
$validator->validate(self::releasesRequest(), $response);
- self::fail('RateLimitException is expected.');
+ Assert::fail('RateLimitException is expected.');
} catch (RateLimitException $e) {
- // Assert
- self::assertStringContainsString('spent its quota', $e->getMessage());
- self::assertStringContainsString('GITHUB_TOKEN', $e->getMessage());
- self::assertNotNull($e->resetAt);
- self::assertSame($resetsAt, $e->resetAt->getTimestamp());
+ Assert::string($e->getMessage())->contains('spent its quota');
+ Assert::string($e->getMessage())->contains('GITHUB_TOKEN');
+ Assert::notNull($e->resetAt);
+ Assert::same($e->resetAt->getTimestamp(), $resetsAt);
}
}
- public function testSecondaryRateLimitIsRecognized(): void
+ #[Test]
+ public function secondaryRateLimitIsRecognized(): void
{
- // Arrange
$validator = new ResponseValidator(authenticated: true);
$response = new ResponseStub(
403,
@@ -83,113 +78,97 @@ public function testSecondaryRateLimitIsRecognized(): void
\json_encode(['message' => 'You have exceeded a secondary rate limit. Please wait a few minutes.']),
);
- // Assert (before Act for exceptions)
- $this->expectException(RateLimitException::class);
- $this->expectExceptionMessage('secondary rate limit exceeded');
+ Expect::exception(RateLimitException::class)->withMessageContaining('secondary rate limit exceeded');
- // Act
$validator->validate(self::releasesRequest(), $response);
}
- public function testForbiddenResponseMentionsRepositoryAndToken(): void
+ #[Test]
+ public function forbiddenResponseMentionsRepositoryAndToken(): void
{
- // Arrange
$validator = new ResponseValidator(authenticated: true);
$response = new ResponseStub(403, [], \json_encode(['message' => 'Resource not accessible by integration']));
- // Act
try {
$validator->validate(self::releasesRequest(), $response);
- self::fail('AccessDeniedException is expected.');
+ Assert::fail('AccessDeniedException is expected.');
} catch (AccessDeniedException $e) {
- // Assert
- self::assertSame('owner/repo', $e->repository);
- self::assertStringContainsString('Resource not accessible by integration', $e->getMessage());
- self::assertStringContainsString('no read access to this repository', $e->getMessage());
+ Assert::same($e->repository, 'owner/repo');
+ Assert::string($e->getMessage())->contains('Resource not accessible by integration');
+ Assert::string($e->getMessage())->contains('no read access to this repository');
}
}
- public function testNotFoundResponseSuggestsCheckingRepositoryAddress(): void
+ #[Test]
+ public function notFoundResponseSuggestsCheckingRepositoryAddress(): void
{
- // Arrange
$validator = new ResponseValidator(authenticated: false);
$response = new ResponseStub(404, [], \json_encode(['message' => 'Not Found']));
- // Act
try {
$validator->validate(self::releasesRequest(), $response);
- self::fail('RepositoryNotFoundException is expected.');
+ Assert::fail('RepositoryNotFoundException is expected.');
} catch (RepositoryNotFoundException $e) {
- // Assert
- self::assertStringContainsString('repository `owner/repo`', $e->getMessage());
- self::assertStringContainsString('GITHUB_TOKEN', $e->getMessage());
+ Assert::string($e->getMessage())->contains('repository `owner/repo`');
+ Assert::string($e->getMessage())->contains('GITHUB_TOKEN');
}
}
- public function testServerErrorIsReportedAsTemporaryFailure(): void
+ #[Test]
+ public function serverErrorIsReportedAsTemporaryFailure(): void
{
- // Arrange
$validator = new ResponseValidator(authenticated: false);
$response = new ResponseStub(503, [], 'Service Unavailable', 'Service Unavailable');
- // Assert (before Act for exceptions)
- $this->expectException(ApiException::class);
- $this->expectExceptionMessage('GitHub API is unavailable: HTTP 503');
+ Expect::exception(ApiException::class)->withMessageContaining('GitHub API is unavailable: HTTP 503');
- // Act
$validator->validate(self::releasesRequest(), $response);
}
- public function testRepositoryIsResolvedFromAssetDownloadUrl(): void
+ #[Test]
+ public function repositoryIsResolvedFromAssetDownloadUrl(): void
{
- // Arrange
$validator = new ResponseValidator(authenticated: false);
$request = new Request('GET', 'https://github.com/owner/repo/releases/download/v1.0.0/asset.zip');
$response = new ResponseStub(404, [], \json_encode(['message' => 'Not Found']));
- // Act
try {
$validator->validate($request, $response);
- self::fail('RepositoryNotFoundException is expected.');
+ Assert::fail('RepositoryNotFoundException is expected.');
} catch (RepositoryNotFoundException $e) {
- // Assert
- self::assertSame('owner/repo', $e->repository);
+ Assert::same($e->repository, 'owner/repo');
}
}
- public function testTransportFailureKeepsTheOriginalError(): void
+ #[Test]
+ public function transportFailureKeepsTheOriginalError(): void
{
- // Arrange
$validator = new ResponseValidator(authenticated: false);
$original = new \RuntimeException('Could not resolve host: api.github.com');
- // Act
$exception = $validator->transportFailure(self::releasesRequest(), $original);
- // Assert
- self::assertStringContainsString('Failed to reach GitHub API', $exception->getMessage());
- self::assertStringContainsString('Could not resolve host', $exception->getMessage());
- self::assertSame($original, $exception->getPrevious());
- self::assertSame('owner/repo', $exception->repository);
+ Assert::string($exception->getMessage())->contains('Failed to reach GitHub API');
+ Assert::string($exception->getMessage())->contains('Could not resolve host');
+ Assert::same($exception->getPrevious(), $original);
+ Assert::same($exception->repository, 'owner/repo');
}
- public function testLongApiMessageIsTruncatedWithoutBreakingUtf8(): void
+ #[Test]
+ public function longApiMessageIsTruncatedWithoutBreakingUtf8(): void
{
- // Arrange
$validator = new ResponseValidator(authenticated: false);
// An ASCII prefix shifts the byte-based cut into the middle of a multibyte character
$apiMessage = 'x' . \str_repeat('я', 400);
$response = new ResponseStub(422, [], \json_encode(['message' => $apiMessage]));
- // Act
try {
$validator->validate(self::releasesRequest(), $response);
- self::fail('ApiException is expected.');
+ Assert::fail('ApiException is expected.');
} catch (ApiException $e) {
- // Assert
$message = $e->getMessage();
- self::assertSame(1, \preg_match('//u', $message), 'The message must stay valid UTF-8.');
- self::assertStringContainsString('…', $message);
+ Assert::same(\preg_match('//u', $message), 1, 'The message must stay valid UTF-8.');
+ Assert::string($message)->contains('…');
}
}
diff --git a/tests/Unit/Module/Repository/Internal/GitHub/Stub/ClientExceptionStub.php b/tests/Unit/Module/Repository/Internal/GitHub/Stub/ClientExceptionStub.php
new file mode 100644
index 0000000..5d23923
--- /dev/null
+++ b/tests/Unit/Module/Repository/Internal/GitHub/Stub/ClientExceptionStub.php
@@ -0,0 +1,15 @@
+getMethod() . '|' . (string) $request->getUri();
+ return (string) \spl_object_id($request);
}
}
diff --git a/tests/Unit/Module/Repository/Internal/GitHub/Stub/HttpFactoryStub.php b/tests/Unit/Module/Repository/Internal/GitHub/Stub/HttpFactoryStub.php
index 2a8f4d3..85dd76d 100644
--- a/tests/Unit/Module/Repository/Internal/GitHub/Stub/HttpFactoryStub.php
+++ b/tests/Unit/Module/Repository/Internal/GitHub/Stub/HttpFactoryStub.php
@@ -6,7 +6,7 @@
use Internal\DLoad\Module\HttpClient\Factory;
use Internal\DLoad\Module\HttpClient\Method;
-use PHPUnit\Framework\MockObject\MockObject;
+use Mockery\MockInterface;
use Psr\Http\Client\ClientInterface;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\UriInterface;
@@ -14,7 +14,7 @@
/**
* HTTP Factory stub for GitHub API tests.
*
- * Provides controllable URI and request creation for testing using PHPUnit mocks.
+ * Provides controllable URI and request creation for testing using Mockery mocks.
*/
final class HttpFactoryStub implements Factory
{
@@ -29,9 +29,9 @@ final class HttpFactoryStub implements Factory
private array $predefinedRequests = [];
/**
- * @param callable(): MockObject $uriFactory
- * @param callable(): MockObject $requestFactory
- * @param callable(): MockObject $clientFactory
+ * @param callable(): (UriInterface&MockInterface) $uriFactory
+ * @param callable(): (RequestInterface&MockInterface) $requestFactory
+ * @param callable(): (ClientInterface&MockInterface) $clientFactory
*/
public function __construct(
private readonly mixed $uriFactory,
@@ -59,9 +59,9 @@ public function uri(string $path, array $query = []): UriInterface
return $this->predefinedUris[$path];
}
- /** @var MockObject&UriInterface $uri */
+ /** @var UriInterface&MockInterface $uri */
$uri = ($this->uriFactory)();
- $uri->method('__toString')->willReturn("https://api.github.com{$path}");
+ $uri->allows('__toString')->andReturn("https://api.github.com{$path}");
return $uri;
}
@@ -78,18 +78,18 @@ public function request(
return $this->predefinedRequests[$requestKey];
}
- /** @var MockObject&RequestInterface $request */
+ /** @var RequestInterface&MockInterface $request */
$request = ($this->requestFactory)();
- $request->method('getMethod')->willReturn($methodString);
- $request->method('getUri')->willReturn($uri);
- $request->method('getHeaders')->willReturn($headers);
+ $request->allows('getMethod')->andReturn($methodString);
+ $request->allows('getUri')->andReturn($uri);
+ $request->allows('getHeaders')->andReturn($headers);
return $request;
}
public function client(): ClientInterface
{
- /** @var MockObject&ClientInterface $client */
+ /** @var ClientInterface&MockInterface $client */
$client = ($this->clientFactory)();
return $client;
diff --git a/tests/Unit/Module/Repository/Internal/GitLab/Api/ResponseValidatorTest.php b/tests/Unit/Module/Repository/Internal/GitLab/Api/ResponseValidatorTest.php
index 2be6501..fd8e2ea 100644
--- a/tests/Unit/Module/Repository/Internal/GitLab/Api/ResponseValidatorTest.php
+++ b/tests/Unit/Module/Repository/Internal/GitLab/Api/ResponseValidatorTest.php
@@ -9,47 +9,44 @@
use Internal\DLoad\Module\Repository\Internal\GitLab\Api\ResponseValidator;
use Internal\DLoad\Tests\Unit\Module\Repository\Stub\ResponseStub;
use Nyholm\Psr7\Request;
-use PHPUnit\Framework\Attributes\CoversClass;
-use PHPUnit\Framework\TestCase;
+use Testo\Assert;
+use Testo\Codecov\Covers;
+use Testo\Test;
-#[CoversClass(ResponseValidator::class)]
-final class ResponseValidatorTest extends TestCase
+#[Covers(ResponseValidator::class)]
+final class ResponseValidatorTest
{
- public function testProjectPathIsDecodedFromApiUrl(): void
+ #[Test]
+ public function projectPathIsDecodedFromApiUrl(): void
{
- // Arrange
$validator = new ResponseValidator(authenticated: false);
$request = new Request('GET', 'https://gitlab.com/api/v4/projects/group%2Fproject/releases?page=1');
$response = new ResponseStub(404, [], \json_encode(['message' => '404 Project Not Found']));
- // Act
try {
$validator->validate($request, $response);
- self::fail('RepositoryNotFoundException is expected.');
+ Assert::fail('RepositoryNotFoundException is expected.');
} catch (RepositoryNotFoundException $e) {
- // Assert
- self::assertSame('group/project', $e->repository);
- self::assertStringContainsString('project `group/project`', $e->getMessage());
- self::assertStringContainsString('GITLAB_TOKEN', $e->getMessage());
+ Assert::same($e->repository, 'group/project');
+ Assert::string($e->getMessage())->contains('project `group/project`');
+ Assert::string($e->getMessage())->contains('GITLAB_TOKEN');
}
}
- public function testTooManyRequestsIsReportedAsRateLimit(): void
+ #[Test]
+ public function tooManyRequestsIsReportedAsRateLimit(): void
{
- // Arrange
$validator = new ResponseValidator(authenticated: false);
$request = new Request('GET', 'https://gitlab.com/api/v4/projects/group%2Fproject/releases');
$response = new ResponseStub(429, ['retry-after' => ['30']], '');
- // Act
try {
$validator->validate($request, $response);
- self::fail('RateLimitException is expected.');
+ Assert::fail('RateLimitException is expected.');
} catch (RateLimitException $e) {
- // Assert
- self::assertStringContainsString('GitLab API rate limit exceeded', $e->getMessage());
- self::assertStringContainsString('GITLAB_TOKEN', $e->getMessage());
- self::assertNotNull($e->resetAt);
+ Assert::string($e->getMessage())->contains('GitLab API rate limit exceeded');
+ Assert::string($e->getMessage())->contains('GITLAB_TOKEN');
+ Assert::notNull($e->resetAt);
}
}
}
diff --git a/tests/Unit/Module/Repository/Internal/PaginatorTest.php b/tests/Unit/Module/Repository/Internal/PaginatorTest.php
index fa8df1b..f512d94 100644
--- a/tests/Unit/Module/Repository/Internal/PaginatorTest.php
+++ b/tests/Unit/Module/Repository/Internal/PaginatorTest.php
@@ -5,11 +5,14 @@
namespace Internal\DLoad\Tests\Unit\Module\Repository\Internal;
use Internal\DLoad\Module\Repository\Internal\Paginator;
-use PHPUnit\Framework\TestCase;
+use Testo\Assert;
+use Testo\Expect;
+use Testo\Test;
-final class PaginatorTest extends TestCase
+final class PaginatorTest
{
- public function testCreateFromGenerator(): void
+ #[Test]
+ public function createFromGenerator(): void
{
// Create a generator that yields arrays of items for each page
$loader = static function (): \Generator {
@@ -21,27 +24,28 @@ public function testCreateFromGenerator(): void
$paginator = Paginator::createFromGenerator($loader(), null);
// Test that we get the first page items
- $this->assertEquals(['Item 1', 'Item 2'], $paginator->getPageItems());
- $this->assertEquals(1, $paginator->getPageNumber());
+ Assert::equals($paginator->getPageItems(), ['Item 1', 'Item 2']);
+ Assert::equals($paginator->getPageNumber(), 1);
// Test getting next page
$page2 = $paginator->getNextPage();
- $this->assertNotNull($page2);
- $this->assertEquals(['Item 3', 'Item 4'], $page2->getPageItems());
- $this->assertEquals(2, $page2->getPageNumber());
+ Assert::notNull($page2);
+ Assert::equals($page2->getPageItems(), ['Item 3', 'Item 4']);
+ Assert::equals($page2->getPageNumber(), 2);
// Test getting third page
$page3 = $page2->getNextPage();
- $this->assertNotNull($page3);
- $this->assertEquals(['Item 5'], $page3->getPageItems());
- $this->assertEquals(3, $page3->getPageNumber());
+ Assert::notNull($page3);
+ Assert::equals($page3->getPageItems(), ['Item 5']);
+ Assert::equals($page3->getPageNumber(), 3);
// Test that there's no fourth page
$page4 = $page3->getNextPage();
- $this->assertNull($page4);
+ Assert::null($page4);
}
- public function testIteration(): void
+ #[Test]
+ public function iteration(): void
{
// Create a generator that yields arrays of items for each page
$loader = static function (): \Generator {
@@ -53,10 +57,11 @@ public function testIteration(): void
// Test iterating through all items
$items = \iterator_to_array($paginator);
- $this->assertEquals(['Item 1', 'Item 2', 'Item 3', 'Item 4'], $items);
+ Assert::equals($items, ['Item 1', 'Item 2', 'Item 3', 'Item 4']);
}
- public function testCountWithCounter(): void
+ #[Test]
+ public function countWithCounter(): void
{
// Create a generator
$loader = static function (): \Generator {
@@ -70,10 +75,11 @@ public function testCountWithCounter(): void
$paginator = Paginator::createFromGenerator($loader(), $counter);
// Test count()
- $this->assertEquals(4, $paginator->count());
+ Assert::equals($paginator->count(), 4);
}
- public function testCountWithoutCounter(): void
+ #[Test]
+ public function countWithoutCounter(): void
{
// Create a generator
$loader = static function (): \Generator {
@@ -83,7 +89,7 @@ public function testCountWithoutCounter(): void
$paginator = Paginator::createFromGenerator($loader(), null);
// Test that count() throws an exception when no counter is provided
- $this->expectException(\LogicException::class);
+ Expect::exception(\LogicException::class);
$paginator->count();
}
}
diff --git a/tests/Unit/Module/Repository/ReleasesCollectionTest.php b/tests/Unit/Module/Repository/ReleasesCollectionTest.php
index e5fbeda..dbaa534 100644
--- a/tests/Unit/Module/Repository/ReleasesCollectionTest.php
+++ b/tests/Unit/Module/Repository/ReleasesCollectionTest.php
@@ -14,11 +14,13 @@
use Internal\DLoad\Tests\Unit\Module\Repository\Stub\AssetStub;
use Internal\DLoad\Tests\Unit\Module\Repository\Stub\ReleaseStub;
use Internal\DLoad\Tests\Unit\Module\Repository\Stub\RepositoryStub;
-use PHPUnit\Framework\Attributes\CoversClass;
-use PHPUnit\Framework\TestCase;
+use Testo\Assert;
+use Testo\Codecov\Covers;
+use Testo\Lifecycle\BeforeTest;
+use Testo\Test;
-#[CoversClass(ReleasesCollection::class)]
-final class ReleasesCollectionTest extends TestCase
+#[Covers(ReleasesCollection::class)]
+final class ReleasesCollectionTest
{
private RepositoryStub $repository;
@@ -27,84 +29,78 @@ final class ReleasesCollectionTest extends TestCase
private ReleasesCollection $collection;
- public function testSatisfiesFiltersReleasesByVersionConstraint(): void
+ #[Test]
+ public function satisfiesFiltersReleasesByVersionConstraint(): void
{
- // Act
$result = $this->collection->satisfies(Constraint::fromConstraintString('^1.0.0'));
- // Assert
- self::assertCount(2, $result, 'Should only include 1.0.0 and 1.5.0 versions');
+ Assert::count($result, 2, 'Should only include 1.0.0 and 1.5.0 versions');
$versions = \array_map(
static fn(ReleaseInterface $release): string => $release->getName(),
\iterator_to_array($result),
);
- self::assertContains('1.0.0', $versions);
- self::assertContains('1.5.0', $versions);
- self::assertNotContains('2.0.0', $versions);
+ Assert::contains($versions, '1.0.0');
+ Assert::contains($versions, '1.5.0');
+ Assert::iterable($versions)->notContains('2.0.0');
}
- public function testNotSatisfiesFiltersOutReleasesByVersionConstraint(): void
+ #[Test]
+ public function notSatisfiesFiltersOutReleasesByVersionConstraint(): void
{
- // Act
$result = $this->collection->notSatisfies(Constraint::fromConstraintString('^1.0.0'));
- // Assert
- self::assertGreaterThan(0, $result->count());
- self::assertNotContains('1.0.0', $this->getVersionsFromCollection($result));
- self::assertNotContains('1.5.0', $this->getVersionsFromCollection($result));
- self::assertContains('2.0.0', $this->getVersionsFromCollection($result));
+ Assert::int($result->count())->greaterThan(0);
+ Assert::iterable($this->getVersionsFromCollection($result))->notContains('1.0.0');
+ Assert::iterable($this->getVersionsFromCollection($result))->notContains('1.5.0');
+ Assert::contains($this->getVersionsFromCollection($result), '2.0.0');
}
- public function testStabilityFiltersReleasesByExactStability(): void
+ #[Test]
+ public function stabilityFiltersReleasesByExactStability(): void
{
- // Act
$result = $this->collection->stability(Stability::Beta);
- // Assert
- self::assertCount(1, $result);
- self::assertSame('2.1.0-beta', $result->first()->getName());
+ Assert::count($result, 1);
+ Assert::same($result->first()->getName(), '2.1.0-beta');
}
- public function testStableFiltersToOnlyStableReleases(): void
+ #[Test]
+ public function stableFiltersToOnlyStableReleases(): void
{
- // Act
$result = $this->collection->stable();
- // Assert
- self::assertCount(3, $result, 'Should only include stable versions');
+ Assert::count($result, 3, 'Should only include stable versions');
foreach ($result as $release) {
- self::assertSame(Stability::Stable, $release->getVersion()->stability);
+ Assert::same($release->getVersion()->stability, Stability::Stable);
}
}
- public function testMinimumStabilityFiltersReleasesByMinimumStabilityLevel(): void
+ #[Test]
+ public function minimumStabilityFiltersReleasesByMinimumStabilityLevel(): void
{
- // Act
$result = $this->collection->minimumStability(Stability::RC);
- // Assert
$stabilities = [];
foreach ($result as $release) {
$stabilities[] = $release->getVersion()->stability;
}
- self::assertContains(Stability::Stable, $stabilities);
- self::assertContains(Stability::RC, $stabilities);
- self::assertNotContains(Stability::Beta, $stabilities);
- self::assertNotContains(Stability::Alpha, $stabilities);
+ Assert::contains($stabilities, Stability::Stable);
+ Assert::contains($stabilities, Stability::RC);
+ Assert::iterable($stabilities)->notContains(Stability::Beta);
+ Assert::iterable($stabilities)->notContains(Stability::Alpha);
}
- public function testSortByVersionSortsReleasesByVersionDescending(): void
+ #[Test]
+ public function sortByVersionSortsReleasesByVersionDescending(): void
{
- // Act
$result = $this->collection->sortByVersion();
$versions = $this->getVersionsFromCollection($result);
- // Assert
- self::assertSame([
+ Assert::same(\array_values($versions), [
// Expected order by semantic version, newest first
'2.1.0-beta',
'2.1.0-alpha',
@@ -112,10 +108,11 @@ public function testSortByVersionSortsReleasesByVersionDescending(): void
'2.0.0',
'1.5.0',
'1.0.0',
- ], \array_values($versions));
+ ]);
}
- public function testChainedFiltersWorkCorrectly(): void
+ #[Test]
+ public function chainedFiltersWorkCorrectly(): void
{
// Act - Get stable releases that satisfy version constraint and sort them
$result = $this->collection
@@ -123,12 +120,12 @@ public function testChainedFiltersWorkCorrectly(): void
->satisfies(Constraint::fromConstraintString('^1.0.0'))
->sortByVersion();
- // Assert
$versions = $this->getVersionsFromCollection($result);
- self::assertSame(['1.5.0', '1.0.0'], \array_values($versions));
+ Assert::same(\array_values($versions), ['1.5.0', '1.0.0']);
}
- public function testWithAssetsFiltersReleasesWithAssets(): void
+ #[Test]
+ public function withAssetsFiltersReleasesWithAssets(): void
{
// Arrange - Set up assets for the second release (1.5.0)
$release = $this->releases[1]; // 1.5.0 release
@@ -145,18 +142,16 @@ public function testWithAssetsFiltersReleasesWithAssets(): void
$release->setAssets($assets);
- // Act
$result = $this->collection->withAssets();
- // Assert
- self::assertCount(1, $result);
- self::assertSame('1.5.0', $result->first()->getName());
- self::assertCount(1, $result->first()->getAssets());
+ Assert::count($result, 1);
+ Assert::same($result->first()->getName(), '1.5.0');
+ Assert::count($result->first()->getAssets(), 1);
}
- protected function setUp(): void
+ #[BeforeTest]
+ protected function prepare(): void
{
- // Arrange
$this->repository = new RepositoryStub('vendor/package');
// Create a series of releases with different versions and stabilities
diff --git a/tests/Unit/Module/Repository/RepositoryProviderTest.php b/tests/Unit/Module/Repository/RepositoryProviderTest.php
index bc67a35..3ad7cd6 100644
--- a/tests/Unit/Module/Repository/RepositoryProviderTest.php
+++ b/tests/Unit/Module/Repository/RepositoryProviderTest.php
@@ -8,12 +8,15 @@
use Internal\DLoad\Module\Repository\Repository;
use Internal\DLoad\Module\Repository\RepositoryFactory;
use Internal\DLoad\Module\Repository\RepositoryProvider;
-use PHPUnit\Framework\Attributes\CoversClass;
-use PHPUnit\Framework\Attributes\DataProvider;
-use PHPUnit\Framework\TestCase;
-
-#[CoversClass(RepositoryProvider::class)]
-final class RepositoryProviderTest extends TestCase
+use Testo\Assert;
+use Testo\Codecov\Covers;
+use Testo\Data\DataProvider;
+use Testo\Expect;
+use Testo\Lifecycle\BeforeTest;
+use Testo\Test;
+
+#[Covers(RepositoryProvider::class)]
+final class RepositoryProviderTest
{
private RepositoryProvider $repositoryProvider;
@@ -37,127 +40,116 @@ public static function provideRepositoryConfigs(): \Generator
yield 'custom config without support' => [$customConfig, false];
}
- public function testAddRepositoryFactoryReturnsSelf(): void
+ #[Test]
+ public function addRepositoryFactoryReturnsSelf(): void
{
- // Arrange
- $factory = $this->createMock(RepositoryFactory::class);
+ $factory = \Mockery::mock(RepositoryFactory::class);
- // Act
$result = $this->repositoryProvider->addRepositoryFactory($factory);
- // Assert
- self::assertSame($this->repositoryProvider, $result);
+ Assert::same($result, $this->repositoryProvider);
}
- public function testGetByConfigReturnsRepositoryFromSupportingFactory(): void
+ #[Test]
+ public function getByConfigReturnsRepositoryFromSupportingFactory(): void
{
- // Arrange
$config = new RepositoryConfig();
$config->type = 'github';
$config->uri = 'vendor/package';
- $repository = $this->createMock(Repository::class);
+ $repository = \Mockery::mock(Repository::class);
- $unsupportedFactory = $this->createMock(RepositoryFactory::class);
- $unsupportedFactory->method('supports')->with($config)->willReturn(false);
- $unsupportedFactory->expects(self::never())->method('create');
+ $unsupportedFactory = \Mockery::mock(RepositoryFactory::class);
+ $unsupportedFactory->allows('supports')->with($config)->andReturn(false);
+ $unsupportedFactory->shouldNotReceive('create');
- $supportedFactory = $this->createMock(RepositoryFactory::class);
- $supportedFactory->method('supports')->with($config)->willReturn(true);
- $supportedFactory->method('create')->with($config)->willReturn($repository);
+ $supportedFactory = \Mockery::mock(RepositoryFactory::class);
+ $supportedFactory->allows('supports')->with($config)->andReturn(true);
+ $supportedFactory->allows('create')->with($config)->andReturn($repository);
// Add factories to provider (order matters - first unsupported, then supported)
$this->repositoryProvider->addRepositoryFactory($unsupportedFactory);
$this->repositoryProvider->addRepositoryFactory($supportedFactory);
- // Act
$result = $this->repositoryProvider->getByConfig($config);
- // Assert
- self::assertSame($repository, $result);
+ Assert::same($result, $repository);
}
- public function testGetByConfigUsesFirstSupportingFactory(): void
+ #[Test]
+ public function getByConfigUsesFirstSupportingFactory(): void
{
- // Arrange
$config = new RepositoryConfig();
$config->type = 'github';
$config->uri = 'vendor/package';
- $repository1 = $this->createMock(Repository::class);
- $repository2 = $this->createMock(Repository::class);
+ $repository1 = \Mockery::mock(Repository::class);
+ $repository2 = \Mockery::mock(Repository::class);
- $firstFactory = $this->createMock(RepositoryFactory::class);
- $firstFactory->method('supports')->with($config)->willReturn(true);
- $firstFactory->method('create')->with($config)->willReturn($repository1);
+ $firstFactory = \Mockery::mock(RepositoryFactory::class);
+ $firstFactory->allows('supports')->with($config)->andReturn(true);
+ $firstFactory->allows('create')->with($config)->andReturn($repository1);
- $secondFactory = $this->createMock(RepositoryFactory::class);
- $secondFactory->method('supports')->with($config)->willReturn(true);
- $secondFactory->expects(self::never())->method('create');
+ $secondFactory = \Mockery::mock(RepositoryFactory::class);
+ $secondFactory->allows('supports')->with($config)->andReturn(true);
+ $secondFactory->shouldNotReceive('create');
// Add both factories (both support the config, but first one should be used)
$this->repositoryProvider->addRepositoryFactory($firstFactory);
$this->repositoryProvider->addRepositoryFactory($secondFactory);
- // Act
$result = $this->repositoryProvider->getByConfig($config);
- // Assert
- self::assertSame($repository1, $result);
+ Assert::same($result, $repository1);
}
- public function testGetByConfigThrowsExceptionWhenNoFactorySupportsConfig(): void
+ #[Test]
+ public function getByConfigThrowsExceptionWhenNoFactorySupportsConfig(): void
{
- // Arrange
$config = new RepositoryConfig();
$config->type = 'unsupported';
$config->uri = 'vendor/package';
- $factory = $this->createMock(RepositoryFactory::class);
- $factory->method('supports')->with($config)->willReturn(false);
+ $factory = \Mockery::mock(RepositoryFactory::class);
+ $factory->allows('supports')->with($config)->andReturn(false);
$this->repositoryProvider->addRepositoryFactory($factory);
- // Assert (before Act for exceptions)
- $this->expectException(\RuntimeException::class);
- $this->expectExceptionMessage("No factory found for repository type `unsupported`.");
+ Expect::exception(\RuntimeException::class)->withMessage("No factory found for repository type `unsupported`.");
- // Act
$this->repositoryProvider->getByConfig($config);
}
#[DataProvider('provideRepositoryConfigs')]
- public function testGetByConfigWithVariousConfigs(RepositoryConfig $config, bool $factorySupports): void
+ #[Test]
+ public function getByConfigWithVariousConfigs(RepositoryConfig $config, bool $factorySupports): void
{
- // Arrange
- $repository = $this->createMock(Repository::class);
+ $repository = \Mockery::mock(Repository::class);
- $factory = $this->createMock(RepositoryFactory::class);
- $factory->method('supports')->with($config)->willReturn($factorySupports);
+ $factory = \Mockery::mock(RepositoryFactory::class);
+ $factory->allows('supports')->with($config)->andReturn($factorySupports);
if ($factorySupports) {
- $factory->method('create')->with($config)->willReturn($repository);
+ $factory->allows('create')->with($config)->andReturn($repository);
}
$this->repositoryProvider->addRepositoryFactory($factory);
// Assert expectation for exception if no factory supports
if (!$factorySupports) {
- $this->expectException(\RuntimeException::class);
- $this->expectExceptionMessage("No factory found for repository type `{$config->type}`.");
+ Expect::exception(\RuntimeException::class)->withMessage("No factory found for repository type `{$config->type}`.");
}
- // Act
$result = $this->repositoryProvider->getByConfig($config);
// Assert result if factory supports
if ($factorySupports) {
- self::assertSame($repository, $result);
+ Assert::same($result, $repository);
}
}
- protected function setUp(): void
+ #[BeforeTest]
+ protected function prepare(): void
{
- // Arrange (common setup)
$this->repositoryProvider = new RepositoryProvider();
}
}
diff --git a/tests/Unit/Module/Velox/Internal/Config/Pipeline/ConfigPipelineTest.php b/tests/Unit/Module/Velox/Internal/Config/Pipeline/ConfigPipelineTest.php
index 09c9c20..72b9b4a 100644
--- a/tests/Unit/Module/Velox/Internal/Config/Pipeline/ConfigPipelineTest.php
+++ b/tests/Unit/Module/Velox/Internal/Config/Pipeline/ConfigPipelineTest.php
@@ -10,44 +10,42 @@
use Internal\DLoad\Module\Velox\Internal\Config\Pipeline\ConfigProcessor;
use Internal\DLoad\Module\Velox\Internal\Config\Pipeline\TomlData;
use Internal\Path;
-use PHPUnit\Framework\Attributes\CoversClass;
-use PHPUnit\Framework\TestCase;
+use Testo\Assert;
+use Testo\Codecov\Covers;
+use Testo\Lifecycle\BeforeTest;
+use Testo\Test;
-#[CoversClass(ConfigPipeline::class)]
-final class ConfigPipelineTest extends TestCase
+#[Covers(ConfigPipeline::class)]
+final class ConfigPipelineTest
{
private VeloxAction $veloxAction;
private Path $buildDir;
- public function testConstructorCreatesInstanceWithProcessors(): void
+ #[Test]
+ public function constructorCreatesInstanceWithProcessors(): void
{
- // Arrange
- $processor1 = $this->createMock(ConfigProcessor::class);
- $processor2 = $this->createMock(ConfigProcessor::class);
+ $processor1 = \Mockery::mock(ConfigProcessor::class);
+ $processor2 = \Mockery::mock(ConfigProcessor::class);
$processors = [$processor1, $processor2];
- // Act
$pipeline = new ConfigPipeline($processors);
- // Assert
- self::assertInstanceOf(ConfigPipeline::class, $pipeline);
+ Assert::instanceOf($pipeline, ConfigPipeline::class);
}
- public function testConstructorCreatesInstanceWithEmptyProcessors(): void
+ #[Test]
+ public function constructorCreatesInstanceWithEmptyProcessors(): void
{
- // Arrange
$processors = [];
- // Act
$pipeline = new ConfigPipeline($processors);
- // Assert
- self::assertInstanceOf(ConfigPipeline::class, $pipeline);
+ Assert::instanceOf($pipeline, ConfigPipeline::class);
}
- public function testProcessWithEmptyProcessorsReturnsOriginalContext(): void
+ #[Test]
+ public function processWithEmptyProcessorsReturnsOriginalContext(): void
{
- // Arrange
$processors = [];
$pipeline = new ConfigPipeline($processors);
$originalContext = new ConfigContext(
@@ -57,17 +55,15 @@ public function testProcessWithEmptyProcessorsReturnsOriginalContext(): void
['metadata' => 'test'],
);
- // Act
$result = $pipeline->process($originalContext);
- // Assert
- self::assertSame($originalContext, $result);
+ Assert::same($result, $originalContext);
}
- public function testProcessWithSingleProcessorCallsProcessor(): void
+ #[Test]
+ public function processWithSingleProcessorCallsProcessor(): void
{
- // Arrange
- $processor = $this->createMock(ConfigProcessor::class);
+ $processor = \Mockery::mock(ConfigProcessor::class);
$originalContext = new ConfigContext(
$this->veloxAction,
$this->buildDir,
@@ -81,27 +77,25 @@ public function testProcessWithSingleProcessorCallsProcessor(): void
[],
);
- $processor->expects(self::once())
- ->method('__invoke')
+ $processor->expects('__invoke')
+ ->once()
->with($originalContext)
- ->willReturn($modifiedContext);
+ ->andReturn($modifiedContext);
$pipeline = new ConfigPipeline([$processor]);
- // Act
$result = $pipeline->process($originalContext);
- // Assert
- self::assertSame($modifiedContext, $result);
- self::assertNotSame($originalContext, $result);
+ Assert::same($result, $modifiedContext);
+ Assert::notSame($result, $originalContext);
}
- public function testProcessWithMultipleProcessorsCallsThemInSequence(): void
+ #[Test]
+ public function processWithMultipleProcessorsCallsThemInSequence(): void
{
- // Arrange
- $processor1 = $this->createMock(ConfigProcessor::class);
- $processor2 = $this->createMock(ConfigProcessor::class);
- $processor3 = $this->createMock(ConfigProcessor::class);
+ $processor1 = \Mockery::mock(ConfigProcessor::class);
+ $processor2 = \Mockery::mock(ConfigProcessor::class);
+ $processor3 = \Mockery::mock(ConfigProcessor::class);
$originalContext = new ConfigContext(
$this->veloxAction,
@@ -129,36 +123,34 @@ public function testProcessWithMultipleProcessorsCallsThemInSequence(): void
);
// Set up expectations for sequential processing
- $processor1->expects(self::once())
- ->method('__invoke')
+ $processor1->expects('__invoke')
+ ->once()
->with($originalContext)
- ->willReturn($context1);
+ ->andReturn($context1);
- $processor2->expects(self::once())
- ->method('__invoke')
+ $processor2->expects('__invoke')
+ ->once()
->with($context1)
- ->willReturn($context2);
+ ->andReturn($context2);
- $processor3->expects(self::once())
- ->method('__invoke')
+ $processor3->expects('__invoke')
+ ->once()
->with($context2)
- ->willReturn($finalContext);
+ ->andReturn($finalContext);
$pipeline = new ConfigPipeline([$processor1, $processor2, $processor3]);
- // Act
$result = $pipeline->process($originalContext);
- // Assert
- self::assertSame($finalContext, $result);
- self::assertSame(['step' => '3'], $result->tomlData->getData());
+ Assert::same($result, $finalContext);
+ Assert::same($result->tomlData->getData(), ['step' => '3']);
}
- public function testProcessPassesThroughComplexContextChanges(): void
+ #[Test]
+ public function processPassesThroughComplexContextChanges(): void
{
- // Arrange
- $processor1 = $this->createMock(ConfigProcessor::class);
- $processor2 = $this->createMock(ConfigProcessor::class);
+ $processor1 = \Mockery::mock(ConfigProcessor::class);
+ $processor2 = \Mockery::mock(ConfigProcessor::class);
$originalTomlData = new TomlData(['initial' => 'data']);
$originalMetadata = ['version' => '1.0'];
@@ -187,31 +179,29 @@ public function testProcessPassesThroughComplexContextChanges(): void
$finalMetadata,
);
- $processor1->expects(self::once())
- ->method('__invoke')
+ $processor1->expects('__invoke')
+ ->once()
->with($originalContext)
- ->willReturn($intermediateContext);
+ ->andReturn($intermediateContext);
- $processor2->expects(self::once())
- ->method('__invoke')
+ $processor2->expects('__invoke')
+ ->once()
->with($intermediateContext)
- ->willReturn($finalContext);
+ ->andReturn($finalContext);
$pipeline = new ConfigPipeline([$processor1, $processor2]);
- // Act
$result = $pipeline->process($originalContext);
- // Assert
- self::assertSame($finalContext, $result);
- self::assertSame(['initial' => 'data', 'added_by_p1' => 'value1'], $result->tomlData->getData());
- self::assertSame(['version' => '1.0', 'processed_by' => 'p2'], $result->metadata);
+ Assert::same($result, $finalContext);
+ Assert::same($result->tomlData->getData(), ['initial' => 'data', 'added_by_p1' => 'value1']);
+ Assert::same($result->metadata, ['version' => '1.0', 'processed_by' => 'p2']);
}
- public function testProcessPreservesContextImmutability(): void
+ #[Test]
+ public function processPreservesContextImmutability(): void
{
- // Arrange
- $processor = $this->createMock(ConfigProcessor::class);
+ $processor = \Mockery::mock(ConfigProcessor::class);
$originalTomlData = new TomlData(['original' => 'data']);
$originalMetadata = ['original' => 'metadata'];
$originalContext = new ConfigContext(
@@ -228,31 +218,30 @@ public function testProcessPreservesContextImmutability(): void
['modified' => 'metadata'],
);
- $processor->expects(self::once())
- ->method('__invoke')
+ $processor->expects('__invoke')
+ ->once()
->with($originalContext)
- ->willReturn($modifiedContext);
+ ->andReturn($modifiedContext);
$pipeline = new ConfigPipeline([$processor]);
- // Act
$result = $pipeline->process($originalContext);
// Assert - Original context should remain unchanged
- self::assertSame(['original' => 'data'], $originalContext->tomlData->getData());
- self::assertSame(['original' => 'metadata'], $originalContext->metadata);
+ Assert::same($originalContext->tomlData->getData(), ['original' => 'data']);
+ Assert::same($originalContext->metadata, ['original' => 'metadata']);
// Result should have modified data
- self::assertSame(['modified' => 'data'], $result->tomlData->getData());
- self::assertSame(['modified' => 'metadata'], $result->metadata);
+ Assert::same($result->tomlData->getData(), ['modified' => 'data']);
+ Assert::same($result->metadata, ['modified' => 'metadata']);
}
- public function testProcessWithProcessorThatReturnsUnchangedContext(): void
+ #[Test]
+ public function processWithProcessorThatReturnsUnchangedContext(): void
{
- // Arrange
- $processor1 = $this->createMock(ConfigProcessor::class);
- $processor2 = $this->createMock(ConfigProcessor::class);
- $processor3 = $this->createMock(ConfigProcessor::class);
+ $processor1 = \Mockery::mock(ConfigProcessor::class);
+ $processor2 = \Mockery::mock(ConfigProcessor::class);
+ $processor3 = \Mockery::mock(ConfigProcessor::class);
$originalContext = new ConfigContext(
$this->veloxAction,
@@ -276,37 +265,35 @@ public function testProcessWithProcessorThatReturnsUnchangedContext(): void
);
// First processor modifies context
- $processor1->expects(self::once())
- ->method('__invoke')
+ $processor1->expects('__invoke')
+ ->once()
->with($originalContext)
- ->willReturn($modifiedContext);
+ ->andReturn($modifiedContext);
// Second processor returns context unchanged (simulating conditional processing)
- $processor2->expects(self::once())
- ->method('__invoke')
+ $processor2->expects('__invoke')
+ ->once()
->with($modifiedContext)
- ->willReturn($modifiedContext);
+ ->andReturn($modifiedContext);
// Third processor modifies context again
- $processor3->expects(self::once())
- ->method('__invoke')
+ $processor3->expects('__invoke')
+ ->once()
->with($modifiedContext)
- ->willReturn($finalContext);
+ ->andReturn($finalContext);
$pipeline = new ConfigPipeline([$processor1, $processor2, $processor3]);
- // Act
$result = $pipeline->process($originalContext);
- // Assert
- self::assertSame($finalContext, $result);
- self::assertSame(['data' => 'modified_by_p3'], $result->tomlData->getData());
+ Assert::same($result, $finalContext);
+ Assert::same($result->tomlData->getData(), ['data' => 'modified_by_p3']);
}
- public function testProcessMaintainsActionAndBuildDirThroughPipeline(): void
+ #[Test]
+ public function processMaintainsActionAndBuildDirThroughPipeline(): void
{
- // Arrange
- $processor = $this->createMock(ConfigProcessor::class);
+ $processor = \Mockery::mock(ConfigProcessor::class);
$originalContext = new ConfigContext(
$this->veloxAction,
$this->buildDir,
@@ -322,37 +309,35 @@ public function testProcessMaintainsActionAndBuildDirThroughPipeline(): void
['modified' => 'metadata'],
);
- $processor->expects(self::once())
- ->method('__invoke')
+ $processor->expects('__invoke')
+ ->once()
->with($originalContext)
- ->willReturn($modifiedContext);
+ ->andReturn($modifiedContext);
$pipeline = new ConfigPipeline([$processor]);
- // Act
$result = $pipeline->process($originalContext);
- // Assert
- self::assertSame($this->veloxAction, $result->action);
- self::assertSame($this->buildDir, $result->buildDir);
- self::assertSame(['modified' => 'data'], $result->tomlData->getData());
- self::assertSame(['modified' => 'metadata'], $result->metadata);
+ Assert::same($result->action, $this->veloxAction);
+ Assert::same($result->buildDir, $this->buildDir);
+ Assert::same($result->tomlData->getData(), ['modified' => 'data']);
+ Assert::same($result->metadata, ['modified' => 'metadata']);
}
- public function testProcessHandlesLargeNumberOfProcessors(): void
+ #[Test]
+ public function processHandlesLargeNumberOfProcessors(): void
{
- // Arrange
$processors = [];
$expectedValue = 0;
// Create 10 processors that each increment a counter in the TOML data
for ($i = 0; $i < 10; $i++) {
- $processor = $this->createMock(ConfigProcessor::class);
+ $processor = \Mockery::mock(ConfigProcessor::class);
$expectedValue = $i + 1;
- $processor->expects(self::once())
- ->method('__invoke')
- ->willReturnCallback(static function (ConfigContext $context) use ($expectedValue): ConfigContext {
+ $processor->expects('__invoke')
+ ->once()
+ ->andReturnUsing(static function (ConfigContext $context) use ($expectedValue): ConfigContext {
return $context->withTomlData(new TomlData(['counter' => $expectedValue]));
});
@@ -368,14 +353,13 @@ public function testProcessHandlesLargeNumberOfProcessors(): void
$pipeline = new ConfigPipeline($processors);
- // Act
$result = $pipeline->process($originalContext);
- // Assert
- self::assertSame(['counter' => 10], $result->tomlData->getData());
+ Assert::same($result->tomlData->getData(), ['counter' => 10]);
}
- protected function setUp(): void
+ #[BeforeTest]
+ protected function prepare(): void
{
$this->veloxAction = new VeloxAction();
$this->buildDir = Path::create('/tmp/build');
diff --git a/tests/Unit/Module/Velox/Internal/Config/Pipeline/Processor/LocalFileProcessorTest.php b/tests/Unit/Module/Velox/Internal/Config/Pipeline/Processor/LocalFileProcessorTest.php
index 75acc52..5861150 100644
--- a/tests/Unit/Module/Velox/Internal/Config/Pipeline/Processor/LocalFileProcessorTest.php
+++ b/tests/Unit/Module/Velox/Internal/Config/Pipeline/Processor/LocalFileProcessorTest.php
@@ -10,12 +10,16 @@
use Internal\DLoad\Module\Velox\Internal\Config\Pipeline\Processor\LocalFileProcessor;
use Internal\DLoad\Module\Velox\Internal\Config\Pipeline\TomlData;
use Internal\Path;
-use PHPUnit\Framework\Attributes\CoversClass;
-use PHPUnit\Framework\Attributes\DataProvider;
-use PHPUnit\Framework\TestCase;
-
-#[CoversClass(LocalFileProcessor::class)]
-final class LocalFileProcessorTest extends TestCase
+use Testo\Assert;
+use Testo\Codecov\Covers;
+use Testo\Core\Exception\SkipTest;
+use Testo\Data\DataProvider;
+use Testo\Expect;
+use Testo\Lifecycle\BeforeTest;
+use Testo\Test;
+
+#[Covers(LocalFileProcessor::class)]
+final class LocalFileProcessorTest
{
private LocalFileProcessor $processor;
private VeloxAction $veloxAction;
@@ -50,9 +54,9 @@ public static function provideValidTomlFiles(): \Generator
];
}
- public function testInvokeReturnsOriginalContextWhenConfigFileIsNull(): void
+ #[Test]
+ public function invokeReturnsOriginalContextWhenConfigFileIsNull(): void
{
- // Arrange
$this->veloxAction->configFile = null;
$originalContext = new ConfigContext(
$this->veloxAction,
@@ -61,16 +65,14 @@ public function testInvokeReturnsOriginalContextWhenConfigFileIsNull(): void
['initial' => 'metadata'],
);
- // Act
$result = $this->processor->__invoke($originalContext);
- // Assert
- self::assertSame($originalContext, $result);
+ Assert::same($result, $originalContext);
}
- public function testInvokeThrowsExceptionWhenConfigFileDoesNotExist(): void
+ #[Test]
+ public function invokeThrowsExceptionWhenConfigFileDoesNotExist(): void
{
- // Arrange
$configPath = '/non/existent/path.toml';
$this->veloxAction->configFile = $configPath;
$context = new ConfigContext(
@@ -80,22 +82,19 @@ public function testInvokeThrowsExceptionWhenConfigFileDoesNotExist(): void
[],
);
- // Assert (before Act for exceptions)
- $this->expectException(ConfigException::class);
- $this->expectExceptionMessage("Local config file not found: {$configPath}");
+ Expect::exception(ConfigException::class)->withMessage("Local config file not found: {$configPath}");
- // Act
$this->processor->__invoke($context);
}
- public function testInvokeThrowsExceptionWhenFileCannotBeRead(): void
+ #[Test]
+ public function invokeThrowsExceptionWhenFileCannotBeRead(): void
{
// Skip this test on Windows as chmod doesn't work the same way
if (PHP_OS_FAMILY === 'Windows') {
- self::markTestSkipped('File permission tests are not reliable on Windows');
+ throw new SkipTest('File permission tests are not reliable on Windows');
}
- // Arrange
$tempFile = $this->createTempFile('test content');
$this->veloxAction->configFile = $tempFile;
$context = new ConfigContext(
@@ -108,11 +107,8 @@ public function testInvokeThrowsExceptionWhenFileCannotBeRead(): void
// Make file unreadable by changing permissions
\chmod($tempFile, 0000);
- // Assert (before Act for exceptions)
- $this->expectException(ConfigException::class);
- $this->expectExceptionMessage("Failed to read local config file: {$tempFile}");
+ Expect::exception(ConfigException::class)->withMessageContaining("Failed to read local config file: {$tempFile}");
- // Act
try {
$this->processor->__invoke($context);
} finally {
@@ -122,9 +118,9 @@ public function testInvokeThrowsExceptionWhenFileCannotBeRead(): void
}
}
- public function testInvokeSuccessfullyProcessesValidTomlFile(): void
+ #[Test]
+ public function invokeSuccessfullyProcessesValidTomlFile(): void
{
- // Arrange
$tomlContent = 'binary_name = "custom-roadrunner"' . "\n" .
'[github.plugins.logger]' . "\n" .
'ref = "master"';
@@ -139,28 +135,26 @@ public function testInvokeSuccessfullyProcessesValidTomlFile(): void
['original' => 'metadata'],
);
- // Act
$result = $this->processor->__invoke($context);
- // Assert
- self::assertNotSame($context, $result);
+ Assert::notSame($result, $context);
$resultData = $result->tomlData->getData();
- self::assertSame('base_data', $resultData['existing']);
- self::assertSame('custom-roadrunner', $resultData['binary_name']);
- self::assertSame('master', $resultData['github']['plugins']['logger']['ref']);
+ Assert::same($resultData['existing'], 'base_data');
+ Assert::same($resultData['binary_name'], 'custom-roadrunner');
+ Assert::same($resultData['github']['plugins']['logger']['ref'], 'master');
- self::assertTrue($result->metadata['local_file_applied']);
- self::assertSame(\str_replace('\\', '/', $tempFile), $result->metadata['local_file_path']);
- self::assertSame('metadata', $result->metadata['original']);
+ Assert::true($result->metadata['local_file_applied']);
+ Assert::same($result->metadata['local_file_path'], \str_replace('\\', '/', $tempFile));
+ Assert::same($result->metadata['original'], 'metadata');
// Clean up
\unlink($tempFile);
}
- public function testInvokeMergesLocalDataWithExistingData(): void
+ #[Test]
+ public function invokeMergesLocalDataWithExistingData(): void
{
- // Arrange
$tomlContent = '[roadrunner]' . "\n" .
'version = "2023.3.0"' . "\n" .
'[github.plugins.http]' . "\n" .
@@ -179,28 +173,26 @@ public function testInvokeMergesLocalDataWithExistingData(): void
[],
);
- // Act
$result = $this->processor->__invoke($context);
- // Assert
$resultData = $result->tomlData->getData();
// Verify merge behavior
- self::assertSame('rr', $resultData['roadrunner']['binary']);
- self::assertSame('2023.3.0', $resultData['roadrunner']['version']);
- self::assertSame('v1.0.0', $resultData['github']['plugins']['logger']['ref']);
- self::assertSame('v4.7.0', $resultData['github']['plugins']['http']['ref']);
+ Assert::same($resultData['roadrunner']['binary'], 'rr');
+ Assert::same($resultData['roadrunner']['version'], '2023.3.0');
+ Assert::same($resultData['github']['plugins']['logger']['ref'], 'v1.0.0');
+ Assert::same($resultData['github']['plugins']['http']['ref'], 'v4.7.0');
// Clean up
\unlink($tempFile);
}
#[DataProvider('provideValidTomlFiles')]
- public function testInvokeHandlesVariousTomlFormats(
+ #[Test]
+ public function invokeHandlesVariousTomlFormats(
string $tomlContent,
array $expectedKeys,
): void {
- // Arrange
$tempFile = $this->createTempFile($tomlContent);
$this->veloxAction->configFile = $tempFile;
$context = new ConfigContext(
@@ -210,20 +202,18 @@ public function testInvokeHandlesVariousTomlFormats(
[],
);
- // Act
$result = $this->processor->__invoke($context);
- // Assert
$resultData = $result->tomlData->getData();
if (\array_key_exists('_empty', $expectedKeys)) {
// Special case for empty file test
- self::assertEmpty($resultData);
+ Assert::blank($resultData);
} else {
foreach ($expectedKeys as $key => $expectedValue) {
- self::assertArrayHasKey($key, $resultData);
+ Assert::array($resultData)->hasKeys($key);
if ($expectedValue !== null) {
- self::assertSame($expectedValue, $resultData[$key]);
+ Assert::same($resultData[$key], $expectedValue);
}
}
}
@@ -232,9 +222,9 @@ public function testInvokeHandlesVariousTomlFormats(
\unlink($tempFile);
}
- public function testInvokePreservesContextImmutability(): void
+ #[Test]
+ public function invokePreservesContextImmutability(): void
{
- // Arrange
$tomlContent = 'new_key = "new_value"';
$tempFile = $this->createTempFile($tomlContent);
@@ -248,28 +238,27 @@ public function testInvokePreservesContextImmutability(): void
$originalMetadata,
);
- // Act
$result = $this->processor->__invoke($originalContext);
// Assert - Original context should remain unchanged
- self::assertSame(['original' => 'data'], $originalContext->tomlData->getData());
- self::assertSame(['original' => 'metadata'], $originalContext->metadata);
- self::assertSame($this->veloxAction, $originalContext->action);
- self::assertSame($this->buildDir, $originalContext->buildDir);
+ Assert::same($originalContext->tomlData->getData(), ['original' => 'data']);
+ Assert::same($originalContext->metadata, ['original' => 'metadata']);
+ Assert::same($originalContext->action, $this->veloxAction);
+ Assert::same($originalContext->buildDir, $this->buildDir);
// Result should have new data
$resultData = $result->tomlData->getData();
- self::assertSame('data', $resultData['original']);
- self::assertSame('new_value', $resultData['new_key']);
- self::assertTrue($result->metadata['local_file_applied']);
+ Assert::same($resultData['original'], 'data');
+ Assert::same($resultData['new_key'], 'new_value');
+ Assert::true($result->metadata['local_file_applied']);
// Clean up
\unlink($tempFile);
}
- public function testInvokePreservesActionAndBuildDir(): void
+ #[Test]
+ public function invokePreservesActionAndBuildDir(): void
{
- // Arrange
$tomlContent = 'test = "value"';
$tempFile = $this->createTempFile($tomlContent);
@@ -281,20 +270,18 @@ public function testInvokePreservesActionAndBuildDir(): void
[],
);
- // Act
$result = $this->processor->__invoke($context);
- // Assert
- self::assertSame($this->veloxAction, $result->action);
- self::assertSame($this->buildDir, $result->buildDir);
+ Assert::same($result->action, $this->veloxAction);
+ Assert::same($result->buildDir, $this->buildDir);
// Clean up
\unlink($tempFile);
}
- public function testInvokeAddsCorrectMetadata(): void
+ #[Test]
+ public function invokeAddsCorrectMetadata(): void
{
- // Arrange
$tomlContent = 'test_key = "test_value"';
$tempFile = $this->createTempFile($tomlContent);
@@ -307,24 +294,22 @@ public function testInvokeAddsCorrectMetadata(): void
$originalMetadata,
);
- // Act
$result = $this->processor->__invoke($context);
- // Assert
- self::assertTrue($result->metadata['local_file_applied']);
- self::assertSame(\str_replace('\\', '/', $tempFile), $result->metadata['local_file_path']);
+ Assert::true($result->metadata['local_file_applied']);
+ Assert::same($result->metadata['local_file_path'], \str_replace('\\', '/', $tempFile));
// Verify existing metadata is preserved
- self::assertSame('value', $result->metadata['existing']);
- self::assertSame(42, $result->metadata['count']);
+ Assert::same($result->metadata['existing'], 'value');
+ Assert::same($result->metadata['count'], 42);
// Clean up
\unlink($tempFile);
}
- public function testInvokeWithRelativeConfigPath(): void
+ #[Test]
+ public function invokeWithRelativeConfigPath(): void
{
- // Arrange
$tomlContent = 'relative_test = "success"';
$tempFile = $this->createTempFile($tomlContent);
$relativePath = \basename($tempFile);
@@ -341,22 +326,20 @@ public function testInvokeWithRelativeConfigPath(): void
[],
);
- // Act
$result = $this->processor->__invoke($context);
- // Assert
$resultData = $result->tomlData->getData();
- self::assertSame('success', $resultData['relative_test']);
- self::assertTrue($result->metadata['local_file_applied']);
+ Assert::same($resultData['relative_test'], 'success');
+ Assert::true($result->metadata['local_file_applied']);
// Clean up
\chdir($originalDir);
\unlink($tempFile);
}
- protected function setUp(): void
+ #[BeforeTest]
+ protected function prepare(): void
{
- // Arrange (common setup)
$this->processor = new LocalFileProcessor();
$this->veloxAction = new VeloxAction();
$this->buildDir = Path::create('/tmp/build');
diff --git a/tests/Unit/Module/Velox/Internal/Config/Pipeline/TomlDataTest.php b/tests/Unit/Module/Velox/Internal/Config/Pipeline/TomlDataTest.php
index aa94681..18b05e4 100644
--- a/tests/Unit/Module/Velox/Internal/Config/Pipeline/TomlDataTest.php
+++ b/tests/Unit/Module/Velox/Internal/Config/Pipeline/TomlDataTest.php
@@ -5,12 +5,13 @@
namespace Internal\DLoad\Tests\Unit\Module\Velox\Internal\Config\Pipeline;
use Internal\DLoad\Module\Velox\Internal\Config\Pipeline\TomlData;
-use PHPUnit\Framework\Attributes\CoversClass;
-use PHPUnit\Framework\Attributes\DataProvider;
-use PHPUnit\Framework\TestCase;
+use Testo\Assert;
+use Testo\Codecov\Covers;
+use Testo\Data\DataProvider;
+use Testo\Test;
-#[CoversClass(TomlData::class)]
-final class TomlDataTest extends TestCase
+#[Covers(TomlData::class)]
+final class TomlDataTest
{
public static function provideSetPathData(): \Generator
{
@@ -66,46 +67,41 @@ public static function provideQuotedValues(): \Generator
];
}
- public function testConstructorCreatesEmptyInstance(): void
+ #[Test]
+ public function constructorCreatesEmptyInstance(): void
{
- // Act
$tomlData = new TomlData();
- // Assert
- self::assertSame([], $tomlData->getData());
+ Assert::same($tomlData->getData(), []);
}
- public function testConstructorCreatesInstanceWithData(): void
+ #[Test]
+ public function constructorCreatesInstanceWithData(): void
{
- // Arrange
$data = ['key' => 'value', 'section' => ['nested' => 'data']];
- // Act
$tomlData = new TomlData($data);
- // Assert
- self::assertSame($data, $tomlData->getData());
+ Assert::same($tomlData->getData(), $data);
}
- public function testFromStringCreatesInstanceFromTomlString(): void
+ #[Test]
+ public function fromStringCreatesInstanceFromTomlString(): void
{
- // Arrange
$toml = "key = \"value\"\n\n[section]\nnested = \"data\"";
$expectedData = [
'key' => 'value',
'section' => ['nested' => 'data'],
];
- // Act
$tomlData = TomlData::fromString($toml);
- // Assert
- self::assertSame($expectedData, $tomlData->getData());
+ Assert::same($tomlData->getData(), $expectedData);
}
- public function testMergeCreatesNewInstanceWithMergedData(): void
+ #[Test]
+ public function mergeCreatesNewInstanceWithMergedData(): void
{
- // Arrange
$data1 = ['key1' => 'value1', 'section' => ['nested1' => 'data1']];
$data2 = ['key2' => 'value2', 'section' => ['nested2' => 'data2']];
$tomlData1 = new TomlData($data1);
@@ -119,18 +115,16 @@ public function testMergeCreatesNewInstanceWithMergedData(): void
],
];
- // Act
$merged = $tomlData1->merge($tomlData2);
- // Assert
- self::assertNotSame($tomlData1, $merged);
- self::assertNotSame($tomlData2, $merged);
- self::assertEquals($expectedMerged, $merged->getData());
+ Assert::notSame($merged, $tomlData1);
+ Assert::notSame($merged, $tomlData2);
+ Assert::equals($merged->getData(), $expectedMerged);
}
- public function testMergeOverwritesExistingKeys(): void
+ #[Test]
+ public function mergeOverwritesExistingKeys(): void
{
- // Arrange
$data1 = ['key' => 'original', 'section' => ['nested' => 'original']];
$data2 = ['key' => 'updated', 'section' => ['nested' => 'updated']];
$tomlData1 = new TomlData($data1);
@@ -140,16 +134,14 @@ public function testMergeOverwritesExistingKeys(): void
'section' => ['nested' => 'updated'],
];
- // Act
$merged = $tomlData1->merge($tomlData2);
- // Assert
- self::assertSame($expectedMerged, $merged->getData());
+ Assert::same($merged->getData(), $expectedMerged);
}
- public function testMergeHandlesNestedArrays(): void
+ #[Test]
+ public function mergeHandlesNestedArrays(): void
{
- // Arrange
$data1 = ['section' => ['key1' => 'value1', 'nested' => ['deep1' => 'data1']]];
$data2 = ['section' => ['key2' => 'value2', 'nested' => ['deep2' => 'data2']]];
$tomlData1 = new TomlData($data1);
@@ -165,32 +157,28 @@ public function testMergeHandlesNestedArrays(): void
],
];
- // Act
$merged = $tomlData1->merge($tomlData2);
- // Assert
- self::assertEquals($expectedMerged, $merged->getData());
+ Assert::equals($merged->getData(), $expectedMerged);
}
#[DataProvider('provideSetPathData')]
- public function testSetCreatesNewInstanceWithUpdatedValue(string $path, mixed $value, array $expectedData): void
+ #[Test]
+ public function setCreatesNewInstanceWithUpdatedValue(string $path, mixed $value, array $expectedData): void
{
- // Arrange
$initialData = ['existing' => 'value'];
$tomlData = new TomlData($initialData);
- // Act
$updated = $tomlData->set($path, $value);
- // Assert
- self::assertNotSame($tomlData, $updated);
- self::assertSame($expectedData, $updated->getData());
- self::assertSame($initialData, $tomlData->getData()); // Original unchanged
+ Assert::notSame($updated, $tomlData);
+ Assert::same($updated->getData(), $expectedData);
+ Assert::same($tomlData->getData(), $initialData); // Original unchanged
}
- public function testToTomlConvertsDataToTomlString(): void
+ #[Test]
+ public function toTomlConvertsDataToTomlString(): void
{
- // Arrange
$data = [
'key1' => 'value1',
'key2' => 'value2',
@@ -210,16 +198,14 @@ public function testToTomlConvertsDataToTomlString(): void
TOML;
- // Act
$result = $tomlData->toToml();
- // Assert
- self::assertSame($expectedToml, $result);
+ Assert::same($result, $expectedToml);
}
- public function testToTomlHandlesNestedSections(): void
+ #[Test]
+ public function toTomlHandlesNestedSections(): void
{
- // Arrange
$data = [
'github' => [
'plugins' => [
@@ -231,16 +217,14 @@ public function testToTomlHandlesNestedSections(): void
$tomlData = new TomlData($data);
$expectedToml = "[github.plugins]\nlogger = 'enabled'\ncache = 'disabled'\n";
- // Act
$result = $tomlData->toToml();
- // Assert
- self::assertSame($expectedToml, $result);
+ Assert::same($result, $expectedToml);
}
- public function testToTomlHandlesMixedSectionTypes(): void
+ #[Test]
+ public function toTomlHandlesMixedSectionTypes(): void
{
- // Arrange
$data = [
'roadrunner' => [
'simple' => 'value',
@@ -256,79 +240,69 @@ public function testToTomlHandlesMixedSectionTypes(): void
TOML;
- // Act
$result = $tomlData->toToml();
- // Assert
- self::assertSame($expectedToml, $result);
+ Assert::same($result, $expectedToml);
}
- public function testToTomlHandlesEmptyData(): void
+ #[Test]
+ public function toTomlHandlesEmptyData(): void
{
- // Arrange
$tomlData = new TomlData();
- // Act
$result = $tomlData->toToml();
- // Assert
- self::assertSame('', $result);
+ Assert::same($result, '');
}
- public function testRoundTripConversion(): void
+ #[Test]
+ public function roundTripConversion(): void
{
- // Arrange
$originalToml = "key = \"value\"\n\n[section]\nnested = \"data\"";
- // Act
$tomlData = TomlData::fromString($originalToml);
$convertedToml = $tomlData->toToml();
$roundTripData = TomlData::fromString($convertedToml);
- // Assert
- self::assertSame($tomlData->getData(), $roundTripData->getData());
+ Assert::same($roundTripData->getData(), $tomlData->getData());
}
#[DataProvider('provideQuotedValues')]
- public function testFromStringHandlesQuotedValues(string $toml, array $expectedData): void
+ #[Test]
+ public function fromStringHandlesQuotedValues(string $toml, array $expectedData): void
{
- // Act
$tomlData = TomlData::fromString($toml);
- // Assert
- self::assertSame($expectedData, $tomlData->getData());
+ Assert::same($tomlData->getData(), $expectedData);
}
- public function testImmutabilityOfOriginalData(): void
+ #[Test]
+ public function immutabilityOfOriginalData(): void
{
- // Arrange
$originalData = ['key' => 'value'];
$tomlData = new TomlData($originalData);
- // Act
$tomlData->set('newkey', 'newvalue');
$tomlData->merge(new TomlData(['otherkey' => 'othervalue']));
// Assert - original data and instance should be unchanged
- self::assertSame(['key' => 'value'], $tomlData->getData());
- self::assertSame(['key' => 'value'], $originalData);
+ Assert::same($tomlData->getData(), ['key' => 'value']);
+ Assert::same($originalData, ['key' => 'value']);
}
- public function testGetDataReturnsReadOnlyArray(): void
+ #[Test]
+ public function getDataReturnsReadOnlyArray(): void
{
- // Arrange
$tomlData = new TomlData(['key' => 'value']);
- // Act
$data = $tomlData->getData();
- // Assert
- self::assertSame(['key' => 'value'], $data);
+ Assert::same($data, ['key' => 'value']);
}
- public function testToTomlHandlesDeeplyNestedSections(): void
+ #[Test]
+ public function toTomlHandlesDeeplyNestedSections(): void
{
- // Arrange
$data = [
'github' => [
'plugins' => [
@@ -359,16 +333,14 @@ public function testToTomlHandlesDeeplyNestedSections(): void
TOML;
- // Act
$result = $tomlData->toToml();
- // Assert
- self::assertSame($expectedToml, $result);
+ Assert::same($result, $expectedToml);
}
- public function testToTomlHandlesInlineArrays(): void
+ #[Test]
+ public function toTomlHandlesInlineArrays(): void
{
- // Arrange
$data = [
'features' => ['logging', 'caching', 'metrics'],
'ports' => [8080, 9090, 3000],
@@ -380,16 +352,14 @@ public function testToTomlHandlesInlineArrays(): void
TOML;
- // Act
$result = $tomlData->toToml();
- // Assert
- self::assertSame($expectedToml, $result);
+ Assert::same($result, $expectedToml);
}
- public function testToTomlHandlesMixedTopLevelAndNestedStructures(): void
+ #[Test]
+ public function toTomlHandlesMixedTopLevelAndNestedStructures(): void
{
- // Arrange
$data = [
'roadrunner' => [
'ref' => 'v2025.1.1',
@@ -430,14 +400,13 @@ public function testToTomlHandlesMixedTopLevelAndNestedStructures(): void
TOML;
- // Act
$result = $tomlData->toToml();
- // Assert
- self::assertSame($expectedToml, $result);
+ Assert::same($result, $expectedToml);
}
- public function testFromStringAndToTomlRoundTripWithNestedArrays(): void
+ #[Test]
+ public function fromStringAndToTomlRoundTripWithNestedArrays(): void
{
// Arrange - This mimics the structure from velox.toml
$originalToml = <<toToml();
$roundTripData = TomlData::fromString($convertedToml);
- // Assert
- self::assertSame($tomlData->getData(), $roundTripData->getData());
+ Assert::same($roundTripData->getData(), $tomlData->getData());
}
- public function testParseTomlWithComplexNestedStructure(): void
+ #[Test]
+ public function parseTomlWithComplexNestedStructure(): void
{
- // Arrange
$toml = <<getData());
+ Assert::same($tomlData->getData(), $expectedData);
}
}
diff --git a/tests/Unit/Module/Version/ConstraintTest.php b/tests/Unit/Module/Version/ConstraintTest.php
index 6a1c9df..e4d4502 100644
--- a/tests/Unit/Module/Version/ConstraintTest.php
+++ b/tests/Unit/Module/Version/ConstraintTest.php
@@ -7,12 +7,14 @@
use Internal\DLoad\Module\Common\Stability;
use Internal\DLoad\Module\Version\Constraint;
use Internal\DLoad\Module\Version\Version;
-use PHPUnit\Framework\Attributes\CoversClass;
-use PHPUnit\Framework\Attributes\DataProvider;
-use PHPUnit\Framework\TestCase;
-
-#[CoversClass(Constraint::class)]
-final class ConstraintTest extends TestCase
+use Testo\Assert;
+use Testo\Codecov\Covers;
+use Testo\Data\DataProvider;
+use Testo\Expect;
+use Testo\Test;
+
+#[Covers(Constraint::class)]
+final class ConstraintTest
{
public static function provideValidConstraints(): \Generator
{
@@ -306,80 +308,70 @@ public static function provideComparableConstraints(): \Generator
}
#[DataProvider('provideValidConstraints')]
- public function testFromConstraintStringWithValidInput(
+ #[Test]
+ public function fromConstraintStringWithValidInput(
string $constraint,
string $expectedBaseVersion,
?string $expectedFeatureSuffix,
Stability $expectedStability,
string $description,
): void {
- // Act
$result = Constraint::fromConstraintString($constraint);
- // Assert
- self::assertSame($expectedBaseVersion, $result->versionConstraint, "Base version for: {$description}");
- self::assertSame($expectedFeatureSuffix, $result->featureSuffix, "Feature suffix for: {$description}");
- self::assertSame($expectedStability, $result->minimumStability, "Stability for: {$description}");
+ Assert::same($result->versionConstraint, $expectedBaseVersion, "Base version for: {$description}");
+ Assert::same($result->featureSuffix, $expectedFeatureSuffix, "Feature suffix for: {$description}");
+ Assert::same($result->minimumStability, $expectedStability, "Stability for: {$description}");
}
#[DataProvider('provideInvalidConstraints')]
- public function testFromConstraintStringWithInvalidInput(
+ #[Test]
+ public function fromConstraintStringWithInvalidInput(
string $constraint,
string $expectedExceptionMessage,
string $description,
): void {
- // Assert
- $this->expectException(\InvalidArgumentException::class);
- $this->expectExceptionMessage($expectedExceptionMessage);
+ Expect::exception(\InvalidArgumentException::class)->withMessageContaining($expectedExceptionMessage);
- // Act
Constraint::fromConstraintString($constraint);
}
- public function testGetBaseConstraint(): void
+ #[Test]
+ public function getBaseConstraint(): void
{
- // Arrange
$constraint = Constraint::fromConstraintString('^2.12.0', 'feature', Stability::Beta);
- // Assert
- self::assertSame('^2.12.0', $constraint->versionConstraint);
+ Assert::same($constraint->versionConstraint, '^2.12.0');
}
- public function testToStringWithBaseVersionOnly(): void
+ #[Test]
+ public function toStringWithBaseVersionOnly(): void
{
- // Arrange
$constraint = Constraint::fromConstraintString('^2.12.0', null, Stability::Stable);
- // Act
$result = (string) $constraint;
- // Assert
- self::assertSame('^2.12.0', $result);
+ Assert::same($result, '^2.12.0');
}
- public function testToStringWithFeatureSuffixAndCustomStability(): void
+ #[Test]
+ public function toStringWithFeatureSuffixAndCustomStability(): void
{
- // Arrange
$constraint = Constraint::fromConstraintString('^2.12.0-feature@beta');
- // Act
$result = (string) $constraint;
- // Assert
- self::assertSame('^2.12.0-feature@beta', $result);
+ Assert::same($result, '^2.12.0-feature@beta');
}
#[DataProvider('provideComparableConstraints')]
- public function testIsSatisfiedBy(string $constraint, string $version, bool $expected): void
+ #[Test]
+ public function isSatisfiedBy(string $constraint, string $version, bool $expected): void
{
- // Arrange
$constraintObj = Constraint::fromConstraintString($constraint);
$versionObj = Version::fromVersionString($version);
- // Act
$result = $constraintObj->isSatisfiedBy($versionObj);
- // Assert
- self::assertSame($expected, $result, "Constraint: {$constraint}, Version: {$version}");
+ Assert::same($result, $expected, "Constraint: {$constraint}, Version: {$version}");
}
}
diff --git a/tests/Unit/Module/Version/VersionTest.php b/tests/Unit/Module/Version/VersionTest.php
index a729a56..23f50c5 100644
--- a/tests/Unit/Module/Version/VersionTest.php
+++ b/tests/Unit/Module/Version/VersionTest.php
@@ -6,12 +6,14 @@
use Internal\DLoad\Module\Common\Stability;
use Internal\DLoad\Module\Version\Version;
-use PHPUnit\Framework\Attributes\CoversClass;
-use PHPUnit\Framework\Attributes\DataProvider;
-use PHPUnit\Framework\TestCase;
-
-#[CoversClass(Version::class)]
-final class VersionTest extends TestCase
+use Testo\Assert;
+use Testo\Codecov\Covers;
+use Testo\Data\DataProvider;
+use Testo\Expect;
+use Testo\Test;
+
+#[Covers(Version::class)]
+final class VersionTest
{
/**
* Provides test cases for successful version string parsing.
@@ -131,140 +133,125 @@ public static function provideVersionsAndExpectedStability(): \Generator
* Tests that valid version strings are parsed correctly.
*/
#[DataProvider('provideValidVersionStrings')]
- public function testFromVersionStringParsesValidVersions(
+ #[Test]
+ public function fromVersionStringParsesValidVersions(
string $input,
string $expectedString,
string $expectedNumber,
?string $expectedSuffix,
Stability $expectedStability,
): void {
- // Act
$version = Version::fromVersionString($input);
- // Assert
- self::assertSame($expectedString, $version->string);
- self::assertSame($expectedNumber, $version->number);
- self::assertSame($expectedSuffix, $version->suffix);
- self::assertSame($expectedStability, $version->stability);
+ Assert::same($version->string, $expectedString);
+ Assert::same($version->number, $expectedNumber);
+ Assert::same($version->suffix, $expectedSuffix);
+ Assert::same($version->stability, $expectedStability);
}
/**
* Tests that invalid version strings throw InvalidArgumentException.
*/
#[DataProvider('provideInvalidVersionStrings')]
- public function testFromVersionStringThrowsExceptionForInvalidInput(string $input): void
+ #[Test]
+ public function fromVersionStringThrowsExceptionForInvalidInput(string $input): void
{
- // Assert
- $this->expectException(\InvalidArgumentException::class);
- $this->expectExceptionMessage("Failed version string: {$input}.");
+ Expect::exception(\InvalidArgumentException::class)->withMessage("Failed version string: {$input}.");
- // Act
Version::fromVersionString($input);
}
/**
* Tests the empty version factory method.
*/
- public function testEmptyCreatesVersionWithEmptyString(): void
+ #[Test]
+ public function emptyCreatesVersionWithEmptyString(): void
{
- // Act
$version = Version::empty();
- // Assert
- self::assertSame('', $version->string);
- self::assertNull($version->number);
- self::assertNull($version->suffix);
- self::assertNull($version->stability);
+ Assert::same($version->string, '');
+ Assert::null($version->number);
+ Assert::null($version->suffix);
+ Assert::null($version->stability);
}
/**
* Tests the __toString method returns the version number.
*/
- public function testToStringReturnsVersionNumber(): void
+ #[Test]
+ public function toStringReturnsVersionNumber(): void
{
- // Arrange
$version = Version::fromVersionString('1.2.3-beta');
- // Act
$result = (string) $version;
- // Assert
- self::assertSame('1.2.3-beta', $result);
+ Assert::same($result, '1.2.3-beta');
}
/**
* Tests the __toString method with empty version.
*/
- public function testToStringWithEmptyVersionReturnsEmptyString(): void
+ #[Test]
+ public function toStringWithEmptyVersionReturnsEmptyString(): void
{
- // Arrange
$version = Version::empty();
- // Act
$result = (string) $version;
- // Assert
- self::assertSame('', $result);
+ Assert::same($result, '');
}
/**
* Tests that version properties are readonly.
*/
- public function testVersionPropertiesAreReadonly(): void
+ #[Test]
+ public function versionPropertiesAreReadonly(): void
{
- // Arrange
$version = Version::fromVersionString('1.2.3-beta-feature');
- // Act & Assert
- self::assertSame('1.2.3-beta-feature', $version->string);
- self::assertSame('1.2.3', $version->number);
- self::assertSame('feature', $version->suffix);
- self::assertSame(Stability::Beta, $version->stability);
+ Assert::same($version->string, '1.2.3-beta-feature');
+ Assert::same($version->number, '1.2.3');
+ Assert::same($version->suffix, 'feature');
+ Assert::same($version->stability, Stability::Beta);
}
/**
* Tests that stability is correctly determined for complex version strings.
*/
- public function testStabilityDetectionInComplexVersions(): void
+ #[Test]
+ public function stabilityDetectionInComplexVersions(): void
{
- // Arrange & Act & Assert
$version1 = Version::fromVersionString('1.2.3-feature-build-rc');
- self::assertSame(Stability::RC, $version1->stability);
- self::assertSame('feature-build', $version1->suffix);
+ Assert::same($version1->stability, Stability::RC);
+ Assert::same($version1->suffix, 'feature-build');
$version2 = Version::fromVersionString('1.2.3-alpha-feature-build');
- self::assertSame(Stability::Alpha, $version2->stability);
- self::assertSame('feature-build', $version2->suffix);
+ Assert::same($version2->stability, Stability::Alpha);
+ Assert::same($version2->suffix, 'feature-build');
}
/**
* Tests that parseStability correctly identifies stability from version strings
*/
#[DataProvider('provideVersionsAndExpectedStability')]
- public function testParseStability(string $version, Stability $expected, string $description): void
+ #[Test]
+ public function parseStability(string $version, Stability $expected, string $description): void
{
- // Act
$version = Version::fromVersionString($version);
$stability = $version->stability;
- // Assert
- self::assertSame(
- $expected,
- $stability,
- \sprintf('%s: Version "%s" should be recognized as %s stability', $description, $version, $expected->value),
- );
+ Assert::same($stability, $expected, \sprintf('%s: Version "%s" should be recognized as %s stability', $description, $version, $expected->value));
}
/**
* Tests that parseStability correctly identifies stability from version strings
*/
#[DataProvider('provideInvalidVersionStrings')]
- public function testParseStabilityInvalidCases(string $version): void
+ #[Test]
+ public function parseStabilityInvalidCases(string $version): void
{
- // Assert
- $this->expectException(\InvalidArgumentException::class);
+ Expect::exception(\InvalidArgumentException::class);
- // Act
$version = Version::fromVersionString($version);
}
}