diff --git a/.gitignore b/.gitignore index 1c4cf3b..f28efc4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ .idea vendor -*.tests.txt +*-tests.txt diff --git a/composer.json b/composer.json index 2ce0175..5679c5f 100644 --- a/composer.json +++ b/composer.json @@ -20,8 +20,8 @@ "php": "8.2 - 8.3" }, "require-dev": { - "phpunit/phpunit": "^11", - "phpoption/phpoption": "^1" + "phpoption/phpoption": "^1", + "pestphp/pest": "^3.5" }, "autoload": { "psr-4": { @@ -30,10 +30,15 @@ }, "autoload-dev": { "psr-4": { - "Test\\": "./tests" + "Tests\\": "./tests" } }, "archive": { "exclude": [ "/tests", "/.gitattributes", "/.gitignore", "/.git", "/composer.lock" ] + }, + "config": { + "allow-plugins": { + "pestphp/pest-plugin": true + } } } diff --git a/composer.lock b/composer.lock index 8aa0a4a..6013f38 100644 --- a/composer.lock +++ b/composer.lock @@ -4,21 +4,352 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "e08185f1629cdda5af3af5405a707797", + "content-hash": "87ff0fadc5082e5dfa570a11147687dd", "packages": [], "packages-dev": [ { - "name": "myclabs/deep-copy", - "version": "1.12.0", + "name": "brianium/paratest", + "version": "v7.6.0", "source": { "type": "git", - "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c" + "url": "https://github.com/paratestphp/paratest.git", + "reference": "68ff89a8de47d086588e391a516d2a5b5fde6254" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", - "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", + "url": "https://api.github.com/repos/paratestphp/paratest/zipball/68ff89a8de47d086588e391a516d2a5b5fde6254", + "reference": "68ff89a8de47d086588e391a516d2a5b5fde6254", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-simplexml": "*", + "fidry/cpu-core-counter": "^1.2.0", + "jean85/pretty-package-versions": "^2.0.6", + "php": "~8.2.0 || ~8.3.0 || ~8.4.0", + "phpunit/php-code-coverage": "^11.0.7", + "phpunit/php-file-iterator": "^5.1.0", + "phpunit/php-timer": "^7.0.1", + "phpunit/phpunit": "^11.4.1", + "sebastian/environment": "^7.2.0", + "symfony/console": "^6.4.11 || ^7.1.5", + "symfony/process": "^6.4.8 || ^7.1.5" + }, + "require-dev": { + "doctrine/coding-standard": "^12.0.0", + "ext-pcov": "*", + "ext-posix": "*", + "phpstan/phpstan": "^1.12.6", + "phpstan/phpstan-deprecation-rules": "^1.2.1", + "phpstan/phpstan-phpunit": "^1.4.0", + "phpstan/phpstan-strict-rules": "^1.6.1", + "squizlabs/php_codesniffer": "^3.10.3", + "symfony/filesystem": "^6.4.9 || ^7.1.5" + }, + "bin": [ + "bin/paratest", + "bin/paratest_for_phpstorm" + ], + "type": "library", + "autoload": { + "psr-4": { + "ParaTest\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Brian Scaturro", + "email": "scaturrob@gmail.com", + "role": "Developer" + }, + { + "name": "Filippo Tessarotto", + "email": "zoeslam@gmail.com", + "role": "Developer" + } + ], + "description": "Parallel testing for PHP", + "homepage": "https://github.com/paratestphp/paratest", + "keywords": [ + "concurrent", + "parallel", + "phpunit", + "testing" + ], + "support": { + "issues": "https://github.com/paratestphp/paratest/issues", + "source": "https://github.com/paratestphp/paratest/tree/v7.6.0" + }, + "funding": [ + { + "url": "https://github.com/sponsors/Slamdunk", + "type": "github" + }, + { + "url": "https://paypal.me/filippotessarotto", + "type": "paypal" + } + ], + "time": "2024-10-15T12:38:31+00:00" + }, + { + "name": "doctrine/deprecations", + "version": "1.1.3", + "source": { + "type": "git", + "url": "https://github.com/doctrine/deprecations.git", + "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab", + "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9", + "phpstan/phpstan": "1.4.10 || 1.10.15", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "psalm/plugin-phpunit": "0.18.4", + "psr/log": "^1 || ^2 || ^3", + "vimeo/psalm": "4.30.0 || 5.12.0" + }, + "suggest": { + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", + "homepage": "https://www.doctrine-project.org/", + "support": { + "issues": "https://github.com/doctrine/deprecations/issues", + "source": "https://github.com/doctrine/deprecations/tree/1.1.3" + }, + "time": "2024-01-30T19:34:25+00:00" + }, + { + "name": "fidry/cpu-core-counter", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/theofidry/cpu-core-counter.git", + "reference": "8520451a140d3f46ac33042715115e290cf5785f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/8520451a140d3f46ac33042715115e290cf5785f", + "reference": "8520451a140d3f46ac33042715115e290cf5785f", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "fidry/makefile": "^0.2.0", + "fidry/php-cs-fixer-config": "^1.1.2", + "phpstan/extension-installer": "^1.2.0", + "phpstan/phpstan": "^1.9.2", + "phpstan/phpstan-deprecation-rules": "^1.0.0", + "phpstan/phpstan-phpunit": "^1.2.2", + "phpstan/phpstan-strict-rules": "^1.4.4", + "phpunit/phpunit": "^8.5.31 || ^9.5.26", + "webmozarts/strict-phpunit": "^7.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Fidry\\CpuCoreCounter\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Théo FIDRY", + "email": "theo.fidry@gmail.com" + } + ], + "description": "Tiny utility to get the number of CPU cores.", + "keywords": [ + "CPU", + "core" + ], + "support": { + "issues": "https://github.com/theofidry/cpu-core-counter/issues", + "source": "https://github.com/theofidry/cpu-core-counter/tree/1.2.0" + }, + "funding": [ + { + "url": "https://github.com/theofidry", + "type": "github" + } + ], + "time": "2024-08-06T10:04:20+00:00" + }, + { + "name": "filp/whoops", + "version": "2.16.0", + "source": { + "type": "git", + "url": "https://github.com/filp/whoops.git", + "reference": "befcdc0e5dce67252aa6322d82424be928214fa2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/filp/whoops/zipball/befcdc0e5dce67252aa6322d82424be928214fa2", + "reference": "befcdc0e5dce67252aa6322d82424be928214fa2", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0", + "psr/log": "^1.0.1 || ^2.0 || ^3.0" + }, + "require-dev": { + "mockery/mockery": "^1.0", + "phpunit/phpunit": "^7.5.20 || ^8.5.8 || ^9.3.3", + "symfony/var-dumper": "^4.0 || ^5.0" + }, + "suggest": { + "symfony/var-dumper": "Pretty print complex values better with var-dumper available", + "whoops/soap": "Formats errors as SOAP responses" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "Whoops\\": "src/Whoops/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Filipe Dobreira", + "homepage": "https://github.com/filp", + "role": "Developer" + } + ], + "description": "php error handling for cool kids", + "homepage": "https://filp.github.io/whoops/", + "keywords": [ + "error", + "exception", + "handling", + "library", + "throwable", + "whoops" + ], + "support": { + "issues": "https://github.com/filp/whoops/issues", + "source": "https://github.com/filp/whoops/tree/2.16.0" + }, + "funding": [ + { + "url": "https://github.com/denis-sokolov", + "type": "github" + } + ], + "time": "2024-09-25T12:00:00+00:00" + }, + { + "name": "jean85/pretty-package-versions", + "version": "2.1.0", + "source": { + "type": "git", + "url": "https://github.com/Jean85/pretty-package-versions.git", + "reference": "3c4e5f62ba8d7de1734312e4fff32f67a8daaf10" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/3c4e5f62ba8d7de1734312e4fff32f67a8daaf10", + "reference": "3c4e5f62ba8d7de1734312e4fff32f67a8daaf10", + "shasum": "" + }, + "require": { + "composer-runtime-api": "^2.1.0", + "php": "^7.4|^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.2", + "jean85/composer-provided-replaced-stub-package": "^1.0", + "phpstan/phpstan": "^1.4", + "phpunit/phpunit": "^7.5|^8.5|^9.6", + "vimeo/psalm": "^4.3 || ^5.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Jean85\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alessandro Lai", + "email": "alessandro.lai85@gmail.com" + } + ], + "description": "A library to get pretty versions strings of installed dependencies", + "keywords": [ + "composer", + "package", + "release", + "versions" + ], + "support": { + "issues": "https://github.com/Jean85/pretty-package-versions/issues", + "source": "https://github.com/Jean85/pretty-package-versions/tree/2.1.0" + }, + "time": "2024-11-18T16:19:46+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.12.1", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/123267b2c49fbf30d78a7b2d333f6be754b94845", + "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845", "shasum": "" }, "require": { @@ -57,7 +388,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.12.0" + "source": "https://github.com/myclabs/DeepCopy/tree/1.12.1" }, "funding": [ { @@ -65,20 +396,20 @@ "type": "tidelift" } ], - "time": "2024-06-12T14:39:25+00:00" + "time": "2024-11-08T17:47:46+00:00" }, { "name": "nikic/php-parser", - "version": "v5.3.0", + "version": "v5.3.1", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "3abf7425cd284141dc5d8d14a9ee444de3345d1a" + "reference": "8eea230464783aa9671db8eea6f8c6ac5285794b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/3abf7425cd284141dc5d8d14a9ee444de3345d1a", - "reference": "3abf7425cd284141dc5d8d14a9ee444de3345d1a", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/8eea230464783aa9671db8eea6f8c6ac5285794b", + "reference": "8eea230464783aa9671db8eea6f8c6ac5285794b", "shasum": "" }, "require": { @@ -121,9 +452,517 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.3.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.3.1" }, - "time": "2024-09-29T13:56:26+00:00" + "time": "2024-10-08T18:51:32+00:00" + }, + { + "name": "nunomaduro/collision", + "version": "v8.5.0", + "source": { + "type": "git", + "url": "https://github.com/nunomaduro/collision.git", + "reference": "f5c101b929c958e849a633283adff296ed5f38f5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nunomaduro/collision/zipball/f5c101b929c958e849a633283adff296ed5f38f5", + "reference": "f5c101b929c958e849a633283adff296ed5f38f5", + "shasum": "" + }, + "require": { + "filp/whoops": "^2.16.0", + "nunomaduro/termwind": "^2.1.0", + "php": "^8.2.0", + "symfony/console": "^7.1.5" + }, + "conflict": { + "laravel/framework": "<11.0.0 || >=12.0.0", + "phpunit/phpunit": "<10.5.1 || >=12.0.0" + }, + "require-dev": { + "larastan/larastan": "^2.9.8", + "laravel/framework": "^11.28.0", + "laravel/pint": "^1.18.1", + "laravel/sail": "^1.36.0", + "laravel/sanctum": "^4.0.3", + "laravel/tinker": "^2.10.0", + "orchestra/testbench-core": "^9.5.3", + "pestphp/pest": "^2.36.0 || ^3.4.0", + "sebastian/environment": "^6.1.0 || ^7.2.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider" + ] + }, + "branch-alias": { + "dev-8.x": "8.x-dev" + } + }, + "autoload": { + "files": [ + "./src/Adapters/Phpunit/Autoload.php" + ], + "psr-4": { + "NunoMaduro\\Collision\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "Cli error handling for console/command-line PHP applications.", + "keywords": [ + "artisan", + "cli", + "command-line", + "console", + "error", + "handling", + "laravel", + "laravel-zero", + "php", + "symfony" + ], + "support": { + "issues": "https://github.com/nunomaduro/collision/issues", + "source": "https://github.com/nunomaduro/collision" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://www.patreon.com/nunomaduro", + "type": "patreon" + } + ], + "time": "2024-10-15T16:06:32+00:00" + }, + { + "name": "nunomaduro/termwind", + "version": "v2.2.0", + "source": { + "type": "git", + "url": "https://github.com/nunomaduro/termwind.git", + "reference": "42c84e4e8090766bbd6445d06cd6e57650626ea3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/42c84e4e8090766bbd6445d06cd6e57650626ea3", + "reference": "42c84e4e8090766bbd6445d06cd6e57650626ea3", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": "^8.2", + "symfony/console": "^7.1.5" + }, + "require-dev": { + "illuminate/console": "^11.28.0", + "laravel/pint": "^1.18.1", + "mockery/mockery": "^1.6.12", + "pestphp/pest": "^2.36.0", + "phpstan/phpstan": "^1.12.6", + "phpstan/phpstan-strict-rules": "^1.6.1", + "symfony/var-dumper": "^7.1.5", + "thecodingmachine/phpstan-strict-rules": "^1.0.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Termwind\\Laravel\\TermwindServiceProvider" + ] + }, + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "files": [ + "src/Functions.php" + ], + "psr-4": { + "Termwind\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "Its like Tailwind CSS, but for the console.", + "keywords": [ + "cli", + "console", + "css", + "package", + "php", + "style" + ], + "support": { + "issues": "https://github.com/nunomaduro/termwind/issues", + "source": "https://github.com/nunomaduro/termwind/tree/v2.2.0" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://github.com/xiCO2k", + "type": "github" + } + ], + "time": "2024-10-15T16:15:16+00:00" + }, + { + "name": "pestphp/pest", + "version": "v3.5.1", + "source": { + "type": "git", + "url": "https://github.com/pestphp/pest.git", + "reference": "179d46ce97d52bcb3f791449ae94025c3f32e3e3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pestphp/pest/zipball/179d46ce97d52bcb3f791449ae94025c3f32e3e3", + "reference": "179d46ce97d52bcb3f791449ae94025c3f32e3e3", + "shasum": "" + }, + "require": { + "brianium/paratest": "^7.6.0", + "nunomaduro/collision": "^8.5.0", + "nunomaduro/termwind": "^2.2.0", + "pestphp/pest-plugin": "^3.0.0", + "pestphp/pest-plugin-arch": "^3.0.0", + "pestphp/pest-plugin-mutate": "^3.0.5", + "php": "^8.2.0", + "phpunit/phpunit": "^11.4.3" + }, + "conflict": { + "filp/whoops": "<2.16.0", + "phpunit/phpunit": ">11.4.3", + "sebastian/exporter": "<6.0.0", + "webmozart/assert": "<1.11.0" + }, + "require-dev": { + "pestphp/pest-dev-tools": "^3.3.0", + "pestphp/pest-plugin-type-coverage": "^3.1.0", + "symfony/process": "^7.1.6" + }, + "bin": [ + "bin/pest" + ], + "type": "library", + "extra": { + "pest": { + "plugins": [ + "Pest\\Mutate\\Plugins\\Mutate", + "Pest\\Plugins\\Configuration", + "Pest\\Plugins\\Bail", + "Pest\\Plugins\\Cache", + "Pest\\Plugins\\Coverage", + "Pest\\Plugins\\Init", + "Pest\\Plugins\\Environment", + "Pest\\Plugins\\Help", + "Pest\\Plugins\\Memory", + "Pest\\Plugins\\Only", + "Pest\\Plugins\\Printer", + "Pest\\Plugins\\ProcessIsolation", + "Pest\\Plugins\\Profile", + "Pest\\Plugins\\Retry", + "Pest\\Plugins\\Snapshot", + "Pest\\Plugins\\Verbose", + "Pest\\Plugins\\Version", + "Pest\\Plugins\\Parallel" + ] + }, + "phpstan": { + "includes": [ + "extension.neon" + ] + } + }, + "autoload": { + "files": [ + "src/Functions.php", + "src/Pest.php" + ], + "psr-4": { + "Pest\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "The elegant PHP Testing Framework.", + "keywords": [ + "framework", + "pest", + "php", + "test", + "testing", + "unit" + ], + "support": { + "issues": "https://github.com/pestphp/pest/issues", + "source": "https://github.com/pestphp/pest/tree/v3.5.1" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + } + ], + "time": "2024-10-31T16:12:45+00:00" + }, + { + "name": "pestphp/pest-plugin", + "version": "v3.0.0", + "source": { + "type": "git", + "url": "https://github.com/pestphp/pest-plugin.git", + "reference": "e79b26c65bc11c41093b10150c1341cc5cdbea83" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pestphp/pest-plugin/zipball/e79b26c65bc11c41093b10150c1341cc5cdbea83", + "reference": "e79b26c65bc11c41093b10150c1341cc5cdbea83", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^2.0.0", + "composer-runtime-api": "^2.2.2", + "php": "^8.2" + }, + "conflict": { + "pestphp/pest": "<3.0.0" + }, + "require-dev": { + "composer/composer": "^2.7.9", + "pestphp/pest": "^3.0.0", + "pestphp/pest-dev-tools": "^3.0.0" + }, + "type": "composer-plugin", + "extra": { + "class": "Pest\\Plugin\\Manager" + }, + "autoload": { + "psr-4": { + "Pest\\Plugin\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "The Pest plugin manager", + "keywords": [ + "framework", + "manager", + "pest", + "php", + "plugin", + "test", + "testing", + "unit" + ], + "support": { + "source": "https://github.com/pestphp/pest-plugin/tree/v3.0.0" + }, + "funding": [ + { + "url": "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=66BYDWAT92N6L", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://www.patreon.com/nunomaduro", + "type": "patreon" + } + ], + "time": "2024-09-08T23:21:41+00:00" + }, + { + "name": "pestphp/pest-plugin-arch", + "version": "v3.0.0", + "source": { + "type": "git", + "url": "https://github.com/pestphp/pest-plugin-arch.git", + "reference": "0a27e55a270cfe73d8cb70551b91002ee2cb64b0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pestphp/pest-plugin-arch/zipball/0a27e55a270cfe73d8cb70551b91002ee2cb64b0", + "reference": "0a27e55a270cfe73d8cb70551b91002ee2cb64b0", + "shasum": "" + }, + "require": { + "pestphp/pest-plugin": "^3.0.0", + "php": "^8.2", + "ta-tikoma/phpunit-architecture-test": "^0.8.4" + }, + "require-dev": { + "pestphp/pest": "^3.0.0", + "pestphp/pest-dev-tools": "^3.0.0" + }, + "type": "library", + "extra": { + "pest": { + "plugins": [ + "Pest\\Arch\\Plugin" + ] + } + }, + "autoload": { + "files": [ + "src/Autoload.php" + ], + "psr-4": { + "Pest\\Arch\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "The Arch plugin for Pest PHP.", + "keywords": [ + "arch", + "architecture", + "framework", + "pest", + "php", + "plugin", + "test", + "testing", + "unit" + ], + "support": { + "source": "https://github.com/pestphp/pest-plugin-arch/tree/v3.0.0" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + } + ], + "time": "2024-09-08T23:23:55+00:00" + }, + { + "name": "pestphp/pest-plugin-mutate", + "version": "v3.0.5", + "source": { + "type": "git", + "url": "https://github.com/pestphp/pest-plugin-mutate.git", + "reference": "e10dbdc98c9e2f3890095b4fe2144f63a5717e08" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pestphp/pest-plugin-mutate/zipball/e10dbdc98c9e2f3890095b4fe2144f63a5717e08", + "reference": "e10dbdc98c9e2f3890095b4fe2144f63a5717e08", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^5.2.0", + "pestphp/pest-plugin": "^3.0.0", + "php": "^8.2", + "psr/simple-cache": "^3.0.0" + }, + "require-dev": { + "pestphp/pest": "^3.0.8", + "pestphp/pest-dev-tools": "^3.0.0", + "pestphp/pest-plugin-type-coverage": "^3.0.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Pest\\Mutate\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Sandro Gehri", + "email": "sandrogehri@gmail.com" + } + ], + "description": "Mutates your code to find untested cases", + "keywords": [ + "framework", + "mutate", + "mutation", + "pest", + "php", + "plugin", + "test", + "testing", + "unit" + ], + "support": { + "source": "https://github.com/pestphp/pest-plugin-mutate/tree/v3.0.5" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/gehrisandro", + "type": "github" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + } + ], + "time": "2024-09-22T07:54:40+00:00" }, { "name": "phar-io/manifest", @@ -243,6 +1082,181 @@ }, "time": "2022-02-21T01:04:05+00:00" }, + { + "name": "phpdocumentor/reflection-common", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + }, + "time": "2020-06-27T09:03:43+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "5.6.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "f3558a4c23426d12bffeaab463f8a8d8b681193c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/f3558a4c23426d12bffeaab463f8a8d8b681193c", + "reference": "f3558a4c23426d12bffeaab463f8a8d8b681193c", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.1", + "ext-filter": "*", + "php": "^7.4 || ^8.0", + "phpdocumentor/reflection-common": "^2.2", + "phpdocumentor/type-resolver": "^1.7", + "phpstan/phpdoc-parser": "^1.7|^2.0", + "webmozart/assert": "^1.9.1" + }, + "require-dev": { + "mockery/mockery": "~1.3.5 || ~1.6.0", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-mockery": "^1.1", + "phpstan/phpstan-webmozart-assert": "^1.2", + "phpunit/phpunit": "^9.5", + "psalm/phar": "^5.26" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + }, + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.6.0" + }, + "time": "2024-11-12T11:25:25+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "1.10.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "679e3ce485b99e84c775d28e2e96fade9a7fb50a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/679e3ce485b99e84c775d28e2e96fade9a7fb50a", + "reference": "679e3ce485b99e84c775d28e2e96fade9a7fb50a", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.0", + "php": "^7.3 || ^8.0", + "phpdocumentor/reflection-common": "^2.0", + "phpstan/phpdoc-parser": "^1.18|^2.0" + }, + "require-dev": { + "ext-tokenizer": "*", + "phpbench/phpbench": "^1.2", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.1", + "phpunit/phpunit": "^9.5", + "rector/rector": "^0.13.9", + "vimeo/psalm": "^4.25" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-1.x": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.10.0" + }, + "time": "2024-11-09T15:12:26+00:00" + }, { "name": "phpoption/phpoption", "version": "1.9.3", @@ -319,36 +1333,83 @@ "time": "2024-07-20T21:41:07+00:00" }, { - "name": "phpunit/php-code-coverage", - "version": "11.0.6", + "name": "phpstan/phpdoc-parser", + "version": "2.0.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "ebdffc9e09585dafa71b9bffcdb0a229d4704c45" + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "c00d78fb6b29658347f9d37ebe104bffadf36299" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/ebdffc9e09585dafa71b9bffcdb0a229d4704c45", - "reference": "ebdffc9e09585dafa71b9bffcdb0a229d4704c45", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/c00d78fb6b29658347f9d37ebe104bffadf36299", + "reference": "c00d78fb6b29658347f9d37ebe104bffadf36299", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "doctrine/annotations": "^2.0", + "nikic/php-parser": "^5.3.0", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^9.6", + "symfony/process": "^5.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPStan\\PhpDocParser\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "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.0.0" + }, + "time": "2024-10-13T11:29:49+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "11.0.7", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "f7f08030e8811582cc459871d28d6f5a1a4d35ca" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/f7f08030e8811582cc459871d28d6f5a1a4d35ca", + "reference": "f7f08030e8811582cc459871d28d6f5a1a4d35ca", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^5.1.0", + "nikic/php-parser": "^5.3.1", "php": ">=8.2", - "phpunit/php-file-iterator": "^5.0.1", + "phpunit/php-file-iterator": "^5.1.0", "phpunit/php-text-template": "^4.0.1", "sebastian/code-unit-reverse-lookup": "^4.0.1", "sebastian/complexity": "^4.0.1", "sebastian/environment": "^7.2.0", "sebastian/lines-of-code": "^3.0.1", - "sebastian/version": "^5.0.1", + "sebastian/version": "^5.0.2", "theseer/tokenizer": "^1.2.3" }, "require-dev": { - "phpunit/phpunit": "^11.0" + "phpunit/phpunit": "^11.4.1" }, "suggest": { "ext-pcov": "PHP extension that provides line coverage", @@ -386,7 +1447,7 @@ "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/11.0.6" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/11.0.7" }, "funding": [ { @@ -394,7 +1455,7 @@ "type": "github" } ], - "time": "2024-08-22T04:37:56+00:00" + "time": "2024-10-09T06:21:38+00:00" }, { "name": "phpunit/php-file-iterator", @@ -643,16 +1704,16 @@ }, { "name": "phpunit/phpunit", - "version": "11.3.6", + "version": "11.4.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "d62c45a19c665bb872c2a47023a0baf41a98bb2b" + "reference": "e8e8ed1854de5d36c088ec1833beae40d2dedd76" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/d62c45a19c665bb872c2a47023a0baf41a98bb2b", - "reference": "d62c45a19c665bb872c2a47023a0baf41a98bb2b", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/e8e8ed1854de5d36c088ec1833beae40d2dedd76", + "reference": "e8e8ed1854de5d36c088ec1833beae40d2dedd76", "shasum": "" }, "require": { @@ -666,21 +1727,21 @@ "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", "php": ">=8.2", - "phpunit/php-code-coverage": "^11.0.6", + "phpunit/php-code-coverage": "^11.0.7", "phpunit/php-file-iterator": "^5.1.0", "phpunit/php-invoker": "^5.0.1", "phpunit/php-text-template": "^4.0.1", "phpunit/php-timer": "^7.0.1", "sebastian/cli-parser": "^3.0.2", "sebastian/code-unit": "^3.0.1", - "sebastian/comparator": "^6.1.0", + "sebastian/comparator": "^6.1.1", "sebastian/diff": "^6.0.2", "sebastian/environment": "^7.2.0", "sebastian/exporter": "^6.1.3", "sebastian/global-state": "^7.0.2", "sebastian/object-enumerator": "^6.0.1", "sebastian/type": "^5.1.0", - "sebastian/version": "^5.0.1" + "sebastian/version": "^5.0.2" }, "suggest": { "ext-soap": "To be able to generate mocks based on WSDL files" @@ -691,7 +1752,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "11.3-dev" + "dev-main": "11.4-dev" } }, "autoload": { @@ -723,7 +1784,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/11.3.6" + "source": "https://github.com/sebastianbergmann/phpunit/tree/11.4.3" }, "funding": [ { @@ -739,7 +1800,161 @@ "type": "tidelift" } ], - "time": "2024-09-19T10:54:28+00:00" + "time": "2024-10-28T13:07:50+00:00" + }, + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, + { + "name": "psr/log", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.2" + }, + "time": "2024-09-11T13:17:53+00:00" + }, + { + "name": "psr/simple-cache", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/simple-cache.git", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\SimpleCache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interfaces for simple caching", + "keywords": [ + "cache", + "caching", + "psr", + "psr-16", + "simple-cache" + ], + "support": { + "source": "https://github.com/php-fig/simple-cache/tree/3.0.0" + }, + "time": "2021-10-29T13:26:27+00:00" }, { "name": "sebastian/cli-parser", @@ -913,16 +2128,16 @@ }, { "name": "sebastian/comparator", - "version": "6.1.0", + "version": "6.2.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "fa37b9e2ca618cb051d71b60120952ee8ca8b03d" + "reference": "43d129d6a0f81c78bee378b46688293eb7ea3739" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa37b9e2ca618cb051d71b60120952ee8ca8b03d", - "reference": "fa37b9e2ca618cb051d71b60120952ee8ca8b03d", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/43d129d6a0f81c78bee378b46688293eb7ea3739", + "reference": "43d129d6a0f81c78bee378b46688293eb7ea3739", "shasum": "" }, "require": { @@ -933,12 +2148,12 @@ "sebastian/exporter": "^6.0" }, "require-dev": { - "phpunit/phpunit": "^11.3" + "phpunit/phpunit": "^11.4" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "6.1-dev" + "dev-main": "6.2-dev" } }, "autoload": { @@ -978,7 +2193,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", "security": "https://github.com/sebastianbergmann/comparator/security/policy", - "source": "https://github.com/sebastianbergmann/comparator/tree/6.1.0" + "source": "https://github.com/sebastianbergmann/comparator/tree/6.2.1" }, "funding": [ { @@ -986,7 +2201,7 @@ "type": "github" } ], - "time": "2024-09-11T15:42:56+00:00" + "time": "2024-10-31T05:30:08+00:00" }, { "name": "sebastian/complexity", @@ -1612,16 +2827,16 @@ }, { "name": "sebastian/version", - "version": "5.0.1", + "version": "5.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/version.git", - "reference": "45c9debb7d039ce9b97de2f749c2cf5832a06ac4" + "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/45c9debb7d039ce9b97de2f749c2cf5832a06ac4", - "reference": "45c9debb7d039ce9b97de2f749c2cf5832a06ac4", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c687e3387b99f5b03b6caa64c74b63e2936ff874", + "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874", "shasum": "" }, "require": { @@ -1654,7 +2869,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/version/issues", "security": "https://github.com/sebastianbergmann/version/security/policy", - "source": "https://github.com/sebastianbergmann/version/tree/5.0.1" + "source": "https://github.com/sebastianbergmann/version/tree/5.0.2" }, "funding": [ { @@ -1662,7 +2877,839 @@ "type": "github" } ], - "time": "2024-07-03T05:13:08+00:00" + "time": "2024-10-09T05:16:32+00:00" + }, + { + "name": "symfony/console", + "version": "v7.1.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "ff04e5b5ba043d2badfb308197b9e6b42883fcd5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/ff04e5b5ba043d2badfb308197b9e6b42883fcd5", + "reference": "ff04e5b5ba043d2badfb308197b9e6b42883fcd5", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-mbstring": "~1.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^6.4|^7.0" + }, + "conflict": { + "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": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/event-dispatcher": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/lock": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/stopwatch": "^6.4|^7.0", + "symfony/var-dumper": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command-line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v7.1.8" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-11-06T14:23:19+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", + "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-04-18T09:32:20+00:00" + }, + { + "name": "symfony/finder", + "version": "v7.1.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "2cb89664897be33f78c65d3d2845954c8d7a43b8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/2cb89664897be33f78c65d3d2845954c8d7a43b8", + "reference": "2cb89664897be33f78c65d3d2845954c8d7a43b8", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "symfony/filesystem": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Finds files and directories via an intuitive fluent interface", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/finder/tree/v7.1.6" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-10-01T08:31:23+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's grapheme_* functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "3833d7255cc303546435cb650316bff708a1c75c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", + "reference": "3833d7255cc303546435cb650316bff708a1c75c", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341", + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/process", + "version": "v7.1.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "42783370fda6e538771f7c7a36e9fa2ee3a84892" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/42783370fda6e538771f7c7a36e9fa2ee3a84892", + "reference": "42783370fda6e538771f7c7a36e9fa2ee3a84892", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Executes commands in sub-processes", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/process/tree/v7.1.8" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-11-06T14:23:19+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v3.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", + "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v3.5.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-04-18T09:32:20+00:00" + }, + { + "name": "symfony/string", + "version": "v7.1.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "591ebd41565f356fcd8b090fe64dbb5878f50281" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/591ebd41565f356fcd8b090fe64dbb5878f50281", + "reference": "591ebd41565f356fcd8b090fe64dbb5878f50281", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/translation-contracts": "<2.5" + }, + "require-dev": { + "symfony/emoji": "^7.1", + "symfony/error-handler": "^6.4|^7.0", + "symfony/http-client": "^6.4|^7.0", + "symfony/intl": "^6.4|^7.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "homepage": "https://symfony.com", + "keywords": [ + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], + "support": { + "source": "https://github.com/symfony/string/tree/v7.1.8" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-11-13T13:31:21+00:00" + }, + { + "name": "ta-tikoma/phpunit-architecture-test", + "version": "0.8.4", + "source": { + "type": "git", + "url": "https://github.com/ta-tikoma/phpunit-architecture-test.git", + "reference": "89f0dea1cb0f0d5744d3ec1764a286af5e006636" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ta-tikoma/phpunit-architecture-test/zipball/89f0dea1cb0f0d5744d3ec1764a286af5e006636", + "reference": "89f0dea1cb0f0d5744d3ec1764a286af5e006636", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.18.0 || ^5.0.0", + "php": "^8.1.0", + "phpdocumentor/reflection-docblock": "^5.3.0", + "phpunit/phpunit": "^10.5.5 || ^11.0.0", + "symfony/finder": "^6.4.0 || ^7.0.0" + }, + "require-dev": { + "laravel/pint": "^1.13.7", + "phpstan/phpstan": "^1.10.52" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPUnit\\Architecture\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ni Shi", + "email": "futik0ma011@gmail.com" + }, + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "Methods for testing application architecture", + "keywords": [ + "architecture", + "phpunit", + "stucture", + "test", + "testing" + ], + "support": { + "issues": "https://github.com/ta-tikoma/phpunit-architecture-test/issues", + "source": "https://github.com/ta-tikoma/phpunit-architecture-test/tree/0.8.4" + }, + "time": "2024-01-05T14:10:56+00:00" }, { "name": "theseer/tokenizer", @@ -1713,6 +3760,64 @@ } ], "time": "2024-03-03T12:36:25+00:00" + }, + { + "name": "webmozart/assert", + "version": "1.11.0", + "source": { + "type": "git", + "url": "https://github.com/webmozarts/assert.git", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "php": "^7.2 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<0.12.20", + "vimeo/psalm": "<4.6.1 || 4.6.2" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.13" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.11.0" + }, + "time": "2022-06-03T18:03:27+00:00" } ], "aliases": [], diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..0c12bb9 --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,17 @@ + + + + + ./tests + + + + + ./src + + + diff --git a/tests/OptionTest.php b/tests/OptionTest.php deleted file mode 100644 index f1638d8..0000000 --- a/tests/OptionTest.php +++ /dev/null @@ -1,368 +0,0 @@ - - * @license MIT - */ - -declare(strict_types=1); - -namespace Test; - -use BadMethodCallException; -use BitBadger\InspiredByFSharp\Option; -use InvalidArgumentException; -use PhpOption\{None, Some}; -use PHPUnit\Framework\Attributes\TestDox; -use PHPUnit\Framework\TestCase; - -/** - * Unit tests for the Option class - */ -class OptionTest extends TestCase -{ - #[TestDox('Get succeeds for Some')] - public function testGetSucceedsForSome(): void - { - $it = Option::Some(9); - $this->assertTrue($it->isSome(), 'The option should have been "Some"'); - $this->assertEquals(9, $it->get(), 'The value was incorrect'); - } - - #[TestDox('Get fails for None')] - public function testGetFailsForNone(): void - { - $this->expectException(InvalidArgumentException::class); - Option::None()->get(); - } - - #[TestDox('IsNone succeeds with None')] - public function testIsNoneSucceedsWithNone(): void - { - $this->assertTrue(Option::None()->isNone(), '"None" should return true'); - } - - #[TestDox('IsNone succeeds with Some')] - public function testIsNoneSucceedsWithSome(): void - { - $this->assertFalse(Option::Some(8)->isNone(), '"Some" should return false'); - } - - #[TestDox('IsSome succeeds with None')] - public function testIsSomeSucceedsWithNone(): void - { - $this->assertFalse(Option::None()->isSome(), '"None" should return false'); - } - - #[TestDox('IsSome succeeds with Some')] - public function testIsSomeSucceedsWithSome(): void - { - $this->assertTrue(Option::Some('boo')->isSome(), '"Some" should return true'); - } - - #[TestDox('GetOrDefault succeeds with None')] - public function testGetOrDefaultSucceedsWithNone(): void - { - $this->assertEquals('yes', Option::None()->getOrDefault('yes'), 'Value should have been default'); - } - - #[TestDox('GetOrDefault succeeds with Some')] - public function testGetOrDefaultSucceedsWithSome(): void - { - $this->assertEquals('no', Option::Some('no')->getOrDefault('yes'), 'Value should have been from option'); - } - - #[TestDox('GetOrCall succeeds with None')] - public function testGetOrCallSucceedsWithNone(): void - { - $value = Option::None()->getOrCall(new class { public function __invoke(): string { return 'called'; } }); - $this->assertEquals('called', $value, 'The value should have been obtained from the callable'); - } - - #[TestDox('GetOrCall succeeds with Some')] - public function testGetOrCallSucceedsWithSome(): void - { - $value = Option::Some('passed')->getOrCall( - new class { public function __invoke(): string { return 'called'; } }); - $this->assertEquals('passed', $value, 'The value should have been obtained from the option'); - } - - #[TestDox('GetOrThrow succeeds with Some')] - public function testGetOrThrowSucceedsWithSome(): void - { - $value = Option::Some('no throw')->getOrThrow(fn() => new BadMethodCallException('oops')); - $this->assertEquals('no throw', $value, 'The "Some" value should have been returned'); - } - - #[TestDox('GetOrThrow succeeds with None')] - public function testGetOrThrowSucceedsWithNone(): void - { - $this->expectException(BadMethodCallException::class); - Option::None()->getOrThrow(fn() => new BadMethodCallException('oops')); - } - - #[TestDox('Bind succeeds with None')] - public function testBindSucceedsWithNone(): void - { - $original = Option::None(); - $bound = $original->bind(fn($it) => Option::Some('value')); - $this->assertTrue($bound->isNone(), 'The option should have been None'); - $this->assertSame($original, $bound, 'The same None instance should have been returned'); - } - - #[TestDox('Bind succeeds with Some and Some')] - public function testBindSucceedsWithSomeAndSome(): void - { - $bound = Option::Some('hello')->bind(fn($it) => Option::Some('goodbye')); - $this->assertTrue($bound->isSome(), 'The option should have been Some'); - $this->assertEquals('goodbye', $bound->get(), 'The bound function was not called'); - } - - #[TestDox('Bind succeeds with Some and None')] - public function testBindSucceedsWithSomeAndNone(): void - { - $bound = Option::Some('greetings')->bind(fn($it) => Option::None()); - $this->assertTrue($bound->isNone(), 'The option should have been None'); - } - - #[TestDox('Contains succeeds with None')] - public function testContainsSucceedsWithNone(): void - { - $this->assertFalse(Option::None()->contains(null), '"None" should always return false'); - } - - #[TestDox('Contains succeeds with Some when strictly equal')] - public function testContainsSucceedsWithSomeWhenStrictlyEqual(): void - { - $this->assertTrue(Option::Some(3)->contains(3), '"Some" with strict equality should be true'); - } - - #[TestDox('Contains succeeds with Some when strictly unequal')] - public function testContainsSucceedsWithSomeWhenStrictlyUnequal(): void - { - $this->assertFalse(Option::Some('3')->contains(3), '"Some" with strict equality should be false'); - } - - #[TestDox('Contains succeeds with Some when loosely equal')] - public function testContainsSucceedsWithSomeWhenLooselyEqual(): void - { - $this->assertTrue(Option::Some('3')->contains(3, strict: false), '"Some" with loose equality should be true'); - } - - #[TestDox('Contains succeeds with Some when loosely unequal')] - public function testContainsSucceedsWithSomeWhenLooselyUnequal(): void - { - $this->assertFalse(Option::Some('3')->contains(4, strict: false), '"Some" with loose equality should be false'); - } - - #[TestDox('Exists succeeds with Some when matching')] - public function testExistsSucceedsWithSomeWhenMatching(): void - { - $this->assertTrue(Option::Some(14)->exists(fn($it) => $it < 100), 'Exists should have returned true'); - } - - #[TestDox('Exists succeeds with Some when not matching')] - public function testExistsSucceedsWithSomeWhenNotMatching(): void - { - $this->assertFalse(Option::Some(14)->exists(fn($it) => $it > 100), 'Exists should have returned false'); - } - - #[TestDox('Exists succeeds with None')] - public function testExistsSucceedsWithNone(): void - { - $this->assertFalse(Option::None()->exists(fn($it) => true), 'Exists should have returned false'); - } - - #[TestDox('Map succeeds with None')] - public function testMapSucceedsWithNone(): void - { - $tattle = new class { public bool $called = false; }; - $none = Option::None(); - $mapped = $none->map(function () use ($tattle) - { - $tattle->called = true; - return 'hello'; - }); - $this->assertTrue($mapped->isNone(), 'The mapped option should be "None"'); - $this->assertFalse($tattle->called, 'The mapping function should not have been called'); - $this->assertSame($none, $mapped, 'The same "None" instance should have been returned'); - } - - #[TestDox('Map succeeds with Some')] - public function testMapSucceedsWithSome(): void - { - $mapped = Option::Some('abc ')->map(fn($it) => str_repeat($it, 2)); - $this->assertTrue($mapped->isSome(), 'The mapped option should be "Some"'); - $this->assertEquals('abc abc ', $mapped->get(), 'The mapping function was not called correctly'); - } - - #[TestDox('Map fails with Some when mapping is null')] - public function testMapFailsWithSomeWhenMappingIsNull(): void - { - $this->expectException(InvalidArgumentException::class); - Option::Some('oof')->map(fn($it) => null); - } - - #[TestDox('Iter succeeds with None')] - public function testIterSucceedsWithNone(): void - { - $target = new class { public mixed $called = null; }; - Option::None()->iter(function () use ($target) { $target->called = 'uh oh'; }); - $this->assertNull($target->called, 'The function should not have been called'); - } - - #[TestDox('Iter succeeds with Some')] - public function testIterSucceedsWithSome(): void - { - $target = new class { public mixed $called = null; }; - Option::Some(33)->iter(function ($it) use ($target) { $target->called = $it; }); - $this->assertEquals(33, $target->called, 'The function should have been called with the "Some" value'); - } - - #[TestDox('Filter succeeds with None')] - public function testFilterSucceedsWithNone(): void - { - $tattle = new class { public bool $called = false; }; - $none = Option::None(); - $filtered = $none->filter(function () use ($tattle) - { - $tattle->called = true; - return true; - }); - $this->assertTrue($filtered->isNone(), 'The filtered option should have been "None"'); - $this->assertFalse($tattle->called, 'The callable should not have been called'); - $this->assertSame($none, $filtered, 'The "None" instance returned should have been the one passed'); - } - - #[TestDox('Filter succeeds with Some when true')] - public function testFilterSucceedsWithSomeWhenTrue(): void - { - $some = Option::Some(12); - $filtered = $some->filter(fn($it) => $it % 2 === 0); - $this->assertTrue($filtered->isSome(), 'The filtered option should have been "Some"'); - $this->assertEquals(12, $filtered->get(), 'The filtered option value is incorrect'); - $this->assertSame($some, $filtered, 'The same "Some" instance should have been returned'); - } - - #[TestDox('Filter succeeds with Some when false')] - public function testFilterSucceedsWithSomeWhenFalse(): void - { - $some = Option::Some(23); - $filtered = $some->filter(fn($it) => $it % 2 === 0); - $this->assertTrue($filtered->isNone(), 'The filtered option should have been "None"'); - } - - #[TestDox('Unwrap succeeds with None')] - public function testUnwrapSucceedsWithNone(): void - { - $this->assertNull(Option::None()->unwrap(), '"None" should return null'); - } - - #[TestDox('Unwrap succeeds with Some')] - public function testUnwrapSucceedsWithSome(): void - { - $this->assertEquals('boy howdy', Option::Some('boy howdy')->unwrap(), '"Some" should return its value'); - } - - #[TestDox('Tap succeeds with Some')] - public function testTapSucceedsWithSome(): void - { - $value = ''; - $original = Option::Some('testing'); - $tapped = $original->tap( - function (Option $it) use (&$value) { $value = $it->isSome() ? $it->get() : 'none'; }); - $this->assertEquals('testing', $value, 'The tapped function was not called'); - $this->assertSame($original, $tapped, 'The same option should have been returned'); - } - - #[TestDox('Tap succeeds with None')] - public function testTapSucceedsWithNone(): void - { - $value = ''; - $original = Option::None(); - $tapped = $original->tap( - function (Option $it) use (&$value) { $value = $it->isSome() ? $it->get() : 'none'; }); - $this->assertEquals('none', $value, 'The tapped function was not called'); - $this->assertSame($original, $tapped, 'The same option should have been returned'); - } - - #[TestDox('ToArray succeeds with Some')] - public function testToArraySucceedsWithSome(): void - { - $arr = Option::Some('15')->toArray(); - $this->assertNotNull($arr, 'The array should not have been null'); - $this->assertEquals(['15'], $arr, 'The array was not created correctly'); - } - - #[TestDox('ToArray succeeds with None')] - public function testToArraySucceedsWithNone(): void - { - $arr = Option::None()->toArray(); - $this->assertNotNull($arr, 'The array should not have been null'); - $this->assertEmpty($arr, 'The array should have been empty'); - } - - #[TestDox('ToPhpOption succeeds for Some')] - public function testToPhpOptionSucceedsForSome(): void - { - $opt = Option::Some('php')->toPhpOption(); - $this->assertNotNull($opt, 'The PhpOption should not have been null'); - $this->assertInstanceOf('PhpOption\Some', $opt, 'The PhpOption should have been "Some"'); - $this->assertTrue($opt->isDefined(), 'There should have been a value for the PhpOption'); - $this->assertEquals('php', $opt->get(), 'The value was not correct'); - } - - #[TestDox('ToPhpOption succeeds for None')] - public function testToPhpOptionSucceedsForNone(): void - { - $opt = Option::None()->toPhpOption(); - $this->assertNotNull($opt, 'The PhpOption should not have been null'); - $this->assertInstanceOf('PhpOption\None', $opt, 'The PhpOption should have been "None"'); - $this->assertFalse($opt->isDefined(), 'There should not have been a value for the PhpOption'); - } - - public function testSomeSucceedsWithValue(): void - { - $it = Option::Some('hello'); - $this->assertTrue($it->isSome(), 'The option should have been "Some"'); - } - - public function testSomeFailsWithNull(): void - { - $this->expectException(InvalidArgumentException::class); - Option::Some(null); - } - - public function testNoneSucceeds(): void - { - $it = Option::None(); - $this->assertTrue($it->isNone(), 'The option should have been "None"'); - } - - public function testOfSucceedsWithNull(): void - { - $it = Option::of(null); - $this->assertTrue($it->isNone(), '"null" should have created a "None" option'); - } - - public function testOfSucceedsWithNonNull(): void - { - $it = Option::of('test'); - $this->assertTrue($it->isSome(), 'A non-null value should have created a "Some" option'); - $this->assertEquals('test', $it->get(), 'The value was not assigned correctly'); - } - - #[TestDox('Of succeeds with PhpOption\Some')] - public function testOfSucceedsWithPhpOptionSome(): void - { - $it = Option::of(Some::create('something')); - $this->assertTrue($it->isSome(), 'A "Some" PhpOption should have created a "Some" option'); - $this->assertEquals('something', $it->get(), 'The value was not assigned correctly'); - } - - #[TestDox('Of succeeds with PhpOption\None')] - public function testOfSucceedsWithPhpOptionNone(): void - { - $it = Option::of(None::create()); - $this->assertTrue($it->isNone(), 'A "None" PhpOption should have created a "None" option'); - } -} diff --git a/tests/Pest.php b/tests/Pest.php new file mode 100644 index 0000000..fd279ad --- /dev/null +++ b/tests/Pest.php @@ -0,0 +1,45 @@ +extend(Tests\TestCase::class)->in('Feature'); + +/* +|-------------------------------------------------------------------------- +| Expectations +|-------------------------------------------------------------------------- +| +| When you're writing tests, you often need to check that values meet certain conditions. The +| "expect()" function gives you access to a set of "expectations" methods that you can use +| to assert different things. Of course, you may extend the Expectation API at any time. +| +*/ + +expect()->extend('toBeOne', function () { + return $this->toBe(1); +}); + +/* +|-------------------------------------------------------------------------- +| Functions +|-------------------------------------------------------------------------- +| +| While Pest is very powerful out-of-the-box, you may have some testing code specific to your +| project that you don't want to repeat in every file. Here you can also expose helpers as +| global functions to help you to reduce the number of lines of code in your test files. +| +*/ + +function something() +{ + // .. +} diff --git a/tests/ResultTest.php b/tests/ResultTest.php deleted file mode 100644 index 25067fc..0000000 --- a/tests/ResultTest.php +++ /dev/null @@ -1,313 +0,0 @@ - - * @license MIT - */ - -declare(strict_types=1); - -namespace Test; - -use BitBadger\InspiredByFSharp\Result; -use InvalidArgumentException; -use PHPUnit\Framework\Attributes\TestDox; -use PHPUnit\Framework\TestCase; - -/** - * Unit tests for the Result class - */ -class ResultTest extends TestCase -{ - #[TestDox('GetOK succeeds for OK result')] - public function testGetOKSucceedsForOKResult(): void - { - $result = Result::OK('yay'); - $this->assertEquals('yay', $result->getOK(), 'The OK result should have been returned'); - } - - #[TestDox('GetOK fails for Error result')] - public function testGetOKFailsForErrorResult(): void - { - $this->expectException(InvalidArgumentException::class); - Result::Error('whoops')->getOK(); - } - - #[TestDox('GetError succeeds for Error result')] - public function testGetErrorSucceedsForErrorResult(): void - { - $result = Result::Error('boo'); - $this->assertEquals('boo', $result->getError(), 'The Error result should have been returned'); - } - - #[TestDox('GetError fails for OK result')] - public function testGetErrorFailsForOKResult(): void - { - $this->expectException(InvalidArgumentException::class); - Result::OK('yeah')->getError(); - } - - #[TestDox('IsOK succeeds for OK result')] - public function testIsOKSucceedsForOKResult(): void - { - $result = Result::OK('ok'); - $this->assertTrue($result->isOK(), 'The check for "OK" should have returned true'); - } - - #[TestDox('IsOK succeeds for Error result')] - public function testIsOKSucceedsForErrorResult(): void - { - $result = Result::Error('error'); - $this->assertFalse($result->isOK(), 'The check for "OK" should have returned false'); - } - - #[TestDox('IsError succeeds for Error result')] - public function testIsErrorSucceedsForErrorResult(): void - { - $result = Result::Error('not ok'); - $this->assertTrue($result->isError(), 'The check for "Error" should have returned true'); - } - - #[TestDox('IsError succeeds for OK result')] - public function testIsErrorSucceedsForOKResult(): void - { - $result = Result::OK('fine'); - $this->assertFalse($result->isError(), 'The check for "Error" should have returned false'); - } - - #[TestDox('Bind succeeds for OK with OK')] - public function testBindSucceedsForOKWithOK(): void - { - $result = Result::OK('one')->bind(fn($it) => Result::OK("$it two")); - $this->assertTrue($result->isOK(), 'The result should have been OK'); - $this->assertEquals('one two', $result->getOK(), 'The bound function was not called'); - } - - #[TestDox('Bind succeeds for OK with Error')] - public function testBindSucceedsForOKWithError(): void - { - $result = Result::OK('three')->bind(fn($it) => Result::Error('back to two')); - $this->assertTrue($result->isError(), 'The result should have been Error'); - $this->assertEquals('back to two', $result->getError(), 'The bound function was not called'); - } - - #[TestDox('Bind succeeds for Error')] - public function testBindSucceedsForError(): void - { - $original = Result::Error('oops'); - $result = $original->bind(fn($it) => Result::OK('never mind - it worked!')); - $this->assertTrue($result->isError(), 'The result should have been Error'); - $this->assertSame($original, $result, 'The same Error result should have been returned'); - } - - - #[TestDox('Contains succeeds for Error result')] - public function testContainsSucceedsForErrorResult(): void - { - $this->assertFalse(Result::Error('ouch')->contains('ouch'), '"Error" should always return false'); - } - - #[TestDox('Contains succeeds for OK result when strictly equal')] - public function testContainsSucceedsForOKResultWhenStrictlyEqual(): void - { - $this->assertTrue(Result::OK(18)->contains(18), '"OK" with strict equality should be true'); - } - - #[TestDox('Contains succeeds for OK result when strictly unequal')] - public function testContainsSucceedsForOKResultWhenStrictlyUnequal(): void - { - $this->assertFalse(Result::OK(18)->contains('18'), '"OK" with strict equality should be false'); - } - - #[TestDox('Contains succeeds for OK result when loosely equal')] - public function testContainsSucceedsForOKResultWhenLooselyEqual(): void - { - $this->assertTrue(Result::OK(18)->contains('18', strict: false), '"OK" with loose equality should be true'); - } - - #[TestDox('Contains succeeds for OK result when loosely unequal')] - public function testContainsSucceedsForOKResultWhenLooselyUnequal(): void - { - $this->assertFalse(Result::OK(18)->contains(17, strict: false), '"OK" with loose equality should be false'); - } - - #[TestDox('Exists succeeds for OK result when matching')] - public function testExistsSucceedsForOKResultWhenMatching(): void - { - $this->assertTrue(Result::OK(14)->exists(fn($it) => $it < 100), 'Exists should have returned true'); - } - - #[TestDox('Exists succeeds for OK result when not matching')] - public function testExistsSucceedsForOKResultWhenNotMatching(): void - { - $this->assertFalse(Result::OK(14)->exists(fn($it) => $it > 100), 'Exists should have returned false'); - } - - #[TestDox('Exists succeeds for Error result')] - public function testExistsSucceedsForErrorResult(): void - { - $this->assertFalse(Result::Error(true)->exists(fn($it) => true), 'Exists should have returned false'); - } - - #[TestDox('Map succeeds for OK result')] - public function testMapSucceedsForOKResult(): void - { - $ok = Result::OK('yard'); - $mapped = $ok->map(fn($it) => strrev($it)); - $this->assertTrue($mapped->isOK(), 'The mapped result should be "OK"'); - $this->assertEquals('dray', $mapped->getOK(), 'The mapping function was not called correctly'); - } - - #[TestDox('Map fails for OK result when mapping is null')] - public function testMapFailsForOKResultWhenMappingIsNull(): void - { - $this->expectException(InvalidArgumentException::class); - Result::OK('not null')->map(fn($it) => null); - } - - #[TestDox('Map succeeds for Error result')] - public function testMapSucceedsForErrorResult(): void - { - $tattle = new class { public bool $called = false; }; - $error = Result::Error('nope'); - $mapped = $error->map(function () use ($tattle) - { - $tattle->called = true; - return 'hello'; - }); - $this->assertTrue($mapped->isError(), 'The mapped result should be "Error"'); - $this->assertFalse($tattle->called, 'The mapping function should not have been called'); - $this->assertSame($error, $mapped, 'The same "Error" instance should have been returned'); - } - - #[TestDox('MapError succeeds for OK result')] - public function testMapErrorSucceedsForOKResult(): void - { - $tattle = new class { public bool $called = false; }; - $ok = Result::OK('sure'); - $mapped = $ok->mapError(function () use ($tattle) - { - $tattle->called = true; - return 'hello'; - }); - $this->assertTrue($mapped->isOK(), 'The mapped result should be "OK"'); - $this->assertFalse($tattle->called, 'The mapping function should not have been called'); - $this->assertSame($ok, $mapped, 'The same "OK" instance should have been returned'); - } - - #[TestDox('MapError succeeds for Error result')] - public function testMapErrorSucceedsForErrorResult(): void - { - $error = Result::Error('taco'); - $mapped = $error->mapError(fn($it) => str_repeat('*', strlen($it))); - $this->assertTrue($mapped->isError(), 'The mapped result should be "Error"'); - $this->assertEquals('****', $mapped->getError(), 'The mapping function was not called correctly'); - } - - #[TestDox('MapError fails for Error result when mapping is null')] - public function testMapErrorFailsForErrorResultWhenMappingIsNull(): void - { - $this->expectException(InvalidArgumentException::class); - Result::Error('pizza')->mapError(fn($it) => null); - } - - #[TestDox('Iter succeeds for OK result')] - public function testIterSucceedsForOKResult(): void - { - $target = new class { public mixed $called = null; }; - Result::OK(77)->iter(function ($it) use ($target) { $target->called = $it; }); - $this->assertEquals(77, $target->called, 'The function should have been called with the "OK" value'); - } - - #[TestDox('Iter succeeds for Error result')] - public function testIterSucceedsForErrorResult(): void - { - $target = new class { public mixed $called = null; }; - Result::Error('')->iter(function () use ($target) { $target->called = 'uh oh'; }); - $this->assertNull($target->called, 'The function should not have been called'); - } - - #[TestDox('ToArray succeeds for OK result')] - public function testToArraySucceedsForOKResult(): void - { - $arr = Result::OK('yay')->toArray(); - $this->assertNotNull($arr, 'The array should not have been null'); - $this->assertEquals(['yay'], $arr, 'The array was not created correctly'); - } - - #[TestDox('ToArray succeeds for Error result')] - public function testToArraySucceedsForErrorResult(): void - { - $arr = Result::Error('oh no')->toArray(); - $this->assertNotNull($arr, 'The array should not have been null'); - $this->assertEmpty($arr, 'The array should have been empty'); - } - - #[TestDox('ToOption succeeds for OK result')] - public function testToOptionSucceedsForOKResult() - { - $value = Result::OK(99)->toOption(); - $this->assertTrue($value->isSome(), 'An "OK" result should map to a "Some" option'); - $this->assertEquals(99, $value->get(), 'The value is not correct'); - } - - #[TestDox('ToOption succeeds for Error result')] - public function testToOptionSucceedsForErrorResult() - { - $value = Result::Error('file not found')->toOption(); - $this->assertTrue($value->isNone(), 'An "Error" result should map to a "None" option'); - } - - #[TestDox('Tap succeeds for OK result')] - public function testTapSucceedsForOKResult(): void - { - $value = ''; - $original = Result::OK('working'); - $tapped = $original->tap(function (Result $it) use (&$value) { - $value = $it->isOK() ? 'OK: ' . $it->getOK() : 'Error: ' . $it->getError(); - }); - $this->assertEquals('OK: working', $value, 'The tapped function was not called'); - $this->assertSame($original, $tapped, 'The same result should have been returned'); - } - - #[TestDox('Tap succeeds for Error result')] - public function testTapSucceedsForErrorResult(): void - { - $value = ''; - $original = Result::Error('failed'); - $tapped = $original->tap(function (Result $it) use (&$value) { - $value = $it->isOK() ? 'OK: ' . $it->getOK() : 'Error: ' . $it->getError(); - }); - $this->assertEquals('Error: failed', $value, 'The tapped function was not called'); - $this->assertSame($original, $tapped, 'The same result should have been returned'); - } - - #[TestDox('OK succeeds for non null result')] - public function testOKSucceedsForNonNullResult(): void - { - $result = Result::OK('something'); - $this->assertTrue($result->isOK(), 'The result should have been "OK"'); - $this->assertEquals('something', $result->getOK(), 'The "OK" value was incorrect'); - } - - #[TestDox('OK fails for null result')] - public function testOKFailsForNullResult(): void - { - $this->expectException(InvalidArgumentException::class); - Result::OK(null); - } - - #[TestDox('Error succeeds for non null result')] - public function testErrorSucceedsForNonNullResult(): void - { - $result = Result::Error('sad trombone'); - $this->assertTrue($result->isError(), 'The result should have been "Error"'); - $this->assertEquals('sad trombone', $result->getError(), 'The "Error" value was incorrect'); - } - - #[TestDox('Error fails for null result')] - public function testErrorFailsForNullResult(): void - { - $this->expectException(InvalidArgumentException::class); - Result::Error(null); - } -} diff --git a/tests/Unit/OptionTest.php b/tests/Unit/OptionTest.php new file mode 100644 index 0000000..2d2b821 --- /dev/null +++ b/tests/Unit/OptionTest.php @@ -0,0 +1,256 @@ + + * @license MIT + */ + +declare(strict_types=1); + +use BitBadger\InspiredByFSharp\Option; +use PhpOption\{None, Some}; + +describe('->get()', function () { + test('retrieves the value for Some', function () { + expect(Option::Some(9)->get())->toBe(9); + }); + test('throws an exception for None', function () { + expect(fn() => Option::None()->get())->toThrow(InvalidArgumentException::class); + }); +}); + +describe('->isNone()', function () { + test('returns true for None', function () { + expect(Option::None()->isNone())->toBeTrue(); + }); + test('returns false for Some', function () { + expect(Option::Some(8)->isNone())->toBeFalse(); + }); +}); + +describe('->isSome()', function () { + test('returns false for None', function () { + expect(Option::None()->isSome())->toBeFalse(); + }); + test('returns true for Some', function () { + expect(Option::Some('boo')->isSome())->toBeTrue(); + }); +}); + +describe('->getOrDefault()', function () { + test('returns default value for None', function () { + expect(Option::None()->getOrDefault('yes'))->toBe('yes'); + }); + test('returns option value for Some', function () { + expect(Option::Some('no')->getOrDefault('yes'))->toBe('no'); + }); +}); + +describe('->getOrCall()', function () { + test('returns value from callable for None', function () { + expect(Option::None()->getOrCall(fn() => 'called'))->toBe('called'); + }); + test('returns option value for Some', function () { + expect(Option::Some('passed')->getOrCall(fn() => 'called'))->toBe('passed'); + }); +}); + +describe('->getOrThrow()', function () { + test('throws an exception for None', function () { + expect(fn() => Option::None()->getOrThrow(fn() => throw new BadMethodCallException())) + ->toThrow(BadMethodCallException::class); + }); + test('returns option value for Some', function () { + expect(Option::Some('no throw')->getOrThrow(fn() => throw new BadMethodCallException()))->toBe('no throw'); + }); +}); + +describe('->bind()', function () { + test('returns None when binding against None', function () { + $original = Option::None(); + $bound = $original->bind(fn($it) => Option::Some('value')); + expect($bound)->isNone()->toBeTrue()->and($bound)->toBe($original); + }); + test('returns Some when binding against Some with Some', function () { + expect(Option::Some('hello')->bind(fn($it) => Option::Some('goodbye'))) + ->isSome()->toBeTrue()->get()->toBe('goodbye'); + }); + test('returns None when binding against Some with None', function () { + expect(Option::Some('greetings')->bind(fn($it) => Option::None()))->isNone()->toBeTrue(); + }); +}); + +describe('->contains()', function () { + test('returns false for None', function () { + expect(Option::None()->contains(null))->toBeFalse(); + }); + test('returns true for Some when strict equality is matched', function () { + expect(Option::Some(3)->contains(3))->toBeTrue(); + }); + test('returns false for Some when strict equality is not matched', function () { + expect(Option::Some('3')->contains(3))->toBeFalse(); + }); + test('returns true for Some when loose equality is matched', function () { + expect(Option::Some('3')->contains(3, strict: false))->toBeTrue(); + }); + test('returns false for Some when loose equality is not matched', function () { + expect(Option::Some('3')->contains(4, strict: false))->toBeFalse(); + }); +}); + +describe('->exists()', function () { + test('returns false for None', function () { + expect(Option::None()->exists(fn($it) => true))->toBeFalse(); + }); + test('returns true for Some and matching condition', function () { + expect(Option::Some(14)->exists(fn($it) => $it < 100))->toBeTrue(); + }); + test('returns false for Some and non-matching condition', function () { + expect(Option::Some(14)->exists(fn($it) => $it > 100))->toBeFalse(); + }); +}); + +describe('->map()', function () { + test('does nothing for None', function () { + $tattle = new class { public bool $called = false; }; + $none = Option::None(); + $mapped = $none->map(function () use ($tattle) + { + $tattle->called = true; + return 'hello'; + }); + expect($mapped) + ->isNone()->toBeTrue() + ->and($tattle->called)->toBeFalse() + ->and($mapped)->toBe($none); + }); + test('maps value for Some', function () { + expect(Option::Some('abc ')->map(fn($it) => str_repeat($it, 2))) + ->isSome()->toBeTrue()->get()->toBe('abc abc '); + }); + test('throws an exception if mapping returns null', function () { + expect(fn() => Option::Some('oof')->map(fn($it) => null))->toThrow(InvalidArgumentException::class); + }); +}); + +describe('->iter()', function () { + test('does nothing for None', function () { + $target = new class { public mixed $called = null; }; + Option::None()->iter(function () use ($target) { $target->called = 'uh oh'; }); + expect($target->called)->toBeNull(); + }); + test('iterates for Some', function () { + $target = new class { public mixed $called = null; }; + Option::Some(33)->iter(function ($it) use ($target) { $target->called = $it; }); + expect($target->called)->toBe(33); + }); +}); + +describe('->filter()', function () { + test('does nothing for None', function () { + $tattle = new class { public bool $called = false; }; + $none = Option::None(); + $filtered = $none->filter(function () use ($tattle) + { + $tattle->called = true; + return true; + }); + expect($filtered) + ->isNone()->toBeTrue() + ->and($tattle->called)->toBeFalse() + ->and($filtered)->toBe($none); + }); + test('returns Some when filter is matched', function () { + $some = Option::Some(12); + $filtered = $some->filter(fn($it) => $it % 2 === 0); + expect($filtered) + ->isSome()->toBeTrue() + ->get()->toBe(12) + ->and($filtered)->toBe($some); + }); + test('returns None when filter is not matched', function () { + expect(Option::Some(23)->filter(fn($it) => $it % 2 === 0)->isNone())->toBeTrue(); + }); +}); + +describe('->unwrap()', function () { + test('returns null for None', function () { + expect(Option::None()->unwrap())->toBeNull(); + }); + test('returns option value for Some', function () { + expect(Option::Some('boy howdy')->unwrap())->toBe('boy howdy'); + }); +}); + +describe('->tap()', function () { + test('is called for None', function () { + $value = ''; + $original = Option::None(); + $tapped = $original->tap( + function (Option $it) use (&$value) { $value = $it->isSome() ? $it->get() : 'none'; }); + expect($value)->toBe('none')->and($original)->toBe($tapped); + }); + test('is called for Some', function () { + $value = ''; + $original = Option::Some('testing'); + $tapped = $original->tap( + function (Option $it) use (&$value) { $value = $it->isSome() ? $it->get() : 'none'; }); + expect($value)->toBe('testing')->and($original)->toBe($tapped); + }); +}); + +describe('->toArray()', function () { + test('returns empty array for None', function () { + expect(Option::None()->toArray())->not->toBeNull()->toBeEmpty(); + }); + test('returns one-item array for Some', function () { + expect(Option::Some('15')->toArray())->not->toBeNull()->toBe(['15']); + }); +}); + +describe('->toPhpOption()', function () { + test('converts None', function () { + expect(Option::None()->toPhpOption()) + ->toBeInstanceOf('PhpOption\None') + ->not->toBeNull() + ->isDefined()->toBeFalse(); + }); + test('converts Some', function () { + expect(Option::Some('php')->toPhpOption()) + ->toBeInstanceOf('PhpOption\Some') + ->not->toBeNull() + ->isDefined()->toBeTrue() + ->get()->toBe('php'); + }); +}); + +describe('::Some()', function () { + test('creates a Some option when given a value', function () { + expect(Option::Some('hello'))->not->toBeNull()->isSome()->toBeTrue(); + }); + test('throws an exception when given null', function () { + expect(fn() => Option::Some(null))->toThrow(InvalidArgumentException::class); + }); +}); + +describe('::None()', function () { + test('creates a None option', function () { + expect(Option::None())->not->toBeNull()->isNone()->toBeTrue(); + }); +}); + +describe('::of()', function () { + test('creates a None option when given null', function () { + expect(Option::of(null))->not->toBeNull()->isNone()->toBeTrue(); + }); + test('creates a Some option when given a value', function () { + expect(Option::of('test'))->not->toBeNull() + ->isSome()->toBeTrue() + ->get()->toBe('test'); + }); + test('creates a None option when given PhpOption\None', function () { + expect(Option::of(None::create()))->isNone()->toBeTrue(); + }); + test('creates a Some option when given PhpOption\Some', function () { + expect(Option::of(Some::create('something')))->isSome()->toBeTrue()->get()->toBe('something'); + }); +}); diff --git a/tests/Unit/ResultTest.php b/tests/Unit/ResultTest.php new file mode 100644 index 0000000..509da59 --- /dev/null +++ b/tests/Unit/ResultTest.php @@ -0,0 +1,206 @@ + + * @license MIT + */ + +declare(strict_types=1); + +use BitBadger\InspiredByFSharp\{Option, Result}; + +describe('->getOK()', function () { + test('returns OK value for OK result', function () { + expect(Result::OK('yay')->getOK())->toBe('yay'); + }); + test('throws an exception for Error result', function () { + expect(fn() => Result::Error('whoops')->getOK())->toThrow(InvalidArgumentException::class); + }); +}); + +describe('->getError()', function () { + test('throws an exception for OK result', function () { + expect(fn() => Result::OK('yeah')->getError())->toThrow(InvalidArgumentException::class); + }); + test('returns Error value for Error result', function () { + expect(Result::Error('boo')->getError())->toBe('boo'); + }); +}); + +describe('->isOK()', function () { + test('returns true for OK result', function () { + expect(Result::OK('ok')->isOK())->toBeTrue(); + }); + test('returns false for Error result', function () { + expect(Result::Error('error')->isOK())->toBeFalse(); + }); +}); + +describe('->isError()', function () { + test('returns false for OK result', function () { + expect(Result::OK('fine')->isError())->toBeFalse(); + }); + test('returns true for Error result', function () { + expect(Result::Error('not ok')->isError())->toBeTrue(); + }); +}); + +describe('->bind()', function () { + test('returns OK when binding against OK with OK', function () { + expect(Result::OK('one')->bind(fn($it) => Result::OK("$it two"))) + ->isOK()->toBeTrue()->getOK()->toBe('one two'); + }); + test('returns Error when binding against OK with Error', function () { + expect(Result::OK('three')->bind(fn($it) => Result::Error('back to two'))) + ->isError()->toBeTrue()->getError()->toBe('back to two'); + }); + test('returns Error when binding against Error', function () { + $original = Result::Error('oops'); + $result = $original->bind(fn($it) => Result::OK('never mind - it worked!')); + expect($result->isError())->toBeTrue() + ->and($result)->toBe($original); + }); +}); + +describe('->contains()', function () { + test('returns true when OK is strictly equal', function () { + expect(Result::OK(18)->contains(18))->toBeTrue(); + }); + test('returns false when OK is not strictly equal', function () { + expect(Result::OK(18)->contains('18'))->toBeFalse(); + }); + test('returns true when OK is loosely equal', function () { + expect(Result::OK(18)->contains('18', strict: false))->toBeTrue(); + }); + test('returns false when OK is not loosely equal', function () { + expect(Result::OK(18)->contains(17, strict: false))->toBeFalse(); + }); + test('returns false for Error', function () { + expect(Result::Error('ouch')->contains('ouch'))->toBeFalse(); + }); +}); + +describe('->exists()', function () { + test('returns true for OK when condition matches', function () { + expect(Result::OK(14)->exists(fn($it) => $it < 100))->toBeTrue(); + }); + test('returns false for OK when condition does not match', function () { + expect(Result::OK(14)->exists(fn($it) => $it > 100))->toBeFalse(); + }); + test('returns false for Error', function () { + expect(Result::Error(true)->exists(fn($it) => true))->toBeFalse(); + }); +}); + +describe('->map()', function () { + test('maps value for OK', function () { + expect(Result::OK('yard')->map(fn($it) => strrev($it))) + ->isOK()->toBeTrue()->getOK()->toBe('dray'); + }); + test('throws an exception for OK when mapping result is null', function () { + expect(fn() => Result::OK('not null')->map(fn($it) => null))->toThrow(InvalidArgumentException::class); + }); + test('does nothing for Error', function () { + $tattle = new class { public bool $called = false; }; + $error = Result::Error('nope'); + $mapped = $error->map(function () use ($tattle) + { + $tattle->called = true; + return 'hello'; + }); + expect($mapped) + ->isError()->toBeTrue() + ->and($tattle->called)->toBeFalse() + ->and($mapped)->toBe($error); + }); +}); + +describe('->mapError()', function () { + test('does nothing for OK', function () { + $tattle = new class { public bool $called = false; }; + $ok = Result::OK('sure'); + $mapped = $ok->mapError(function () use ($tattle) + { + $tattle->called = true; + return 'hello'; + }); + expect($mapped) + ->isOK()->toBeTrue() + ->and($tattle->called)->toBeFalse() + ->and($mapped)->toBe($ok); + }); + test('maps value for Error', function () { + expect(Result::Error('taco')->mapError(fn($it) => str_repeat('*', strlen($it)))) + ->isError()->toBeTrue()->getError()->toBe('****'); + }); + test('throws an exception for Error when mapping result is null', function () { + expect(fn() => Result::Error('pizza')->mapError(fn($it) => null))->toThrow(InvalidArgumentException::class); + }); +}); + +describe('->iter()', function () { + test('iterates for OK', function () { + $target = new class { public mixed $called = null; }; + Result::OK(77)->iter(function ($it) use ($target) { $target->called = $it; }); + expect($target->called)->toBe(77); + }); + test('does nothing for Error', function () { + $target = new class { public mixed $called = null; }; + Result::Error('')->iter(function () use ($target) { $target->called = 'uh oh'; }); + expect($target->called)->toBeNull(); + }); +}); + +describe('->toArray()', function () { + test('returns a one-item array for OK', function () { + expect(Result::OK('yay')->toArray())->not->toBeNull()->toBe(['yay']); + }); + test('returns an empty array for Error', function () { + expect(Result::Error('oh no')->toArray())->not->toBeNull()->toBeEmpty(); + }); +}); + +describe('->toOption()', function () { + test('returns a Some option for OK', function () { + expect(Result::OK(99)->toOption())->isSome()->toBeTrue()->get()->toBe(99); + }); + test('returns a None option for Error', function () { + expect(Result::Error('file not found')->toOption())->isNone()->toBeTrue(); + }); +}); + +describe('->tap()', function () { + test('is called for OK', function () { + $value = ''; + $original = Result::OK('working'); + $tapped = $original->tap(function (Result $it) use (&$value) { + $value = $it->isOK() ? 'OK: ' . $it->getOK() : 'Error: ' . $it->getError(); + }); + expect($value)->toBe('OK: working')->and($tapped)->toBe($original); + }); + test('is called for Error', function () { + $value = ''; + $original = Result::Error('failed'); + $tapped = $original->tap(function (Result $it) use (&$value) { + $value = $it->isOK() ? 'OK: ' . $it->getOK() : 'Error: ' . $it->getError(); + }); + expect($value)->toBe('Error: failed')->and($tapped)->toBe($original); + }); +}); + +describe('::OK()', function () { + test('creates an OK result for a non-null value', function () { + expect(Result::OK('something'))->isOK()->toBeTrue()->getOK()->toBe('something'); + }); + test('throws an exception for OK with a null value', function () { + expect(fn() => Result::OK(null))->toThrow(InvalidArgumentException::class); + }); +}); + +describe('::Error()', function () { + test('creates an Error result for a non-null value', function () { + expect(Result::Error('sad trombone'))->isError()->toBeTrue()->getError()->toBe('sad trombone'); + }); + test('throws an exception for Error with a null value', function () { + expect(fn() => Result::Error(null))->toThrow(InvalidArgumentException::class); + }); +});