From 0473a21e8e7f35aef117bf77f79620e152ec3bf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Somogyi?= Date: Sat, 28 Dec 2024 15:29:45 +0100 Subject: [PATCH 01/43] remove: phpunit removed from composer enh: pestphp/pest added to composer enh: pest init --- composer.json | 9 +- composer.lock | 3524 +++++++++++++++++++++++++++++++++++--------- tests/Pest.php | 45 + tests/TestCase.php | 10 + 4 files changed, 2902 insertions(+), 686 deletions(-) create mode 100644 tests/Pest.php create mode 100644 tests/TestCase.php diff --git a/composer.json b/composer.json index 56b5d4f..f44dc57 100644 --- a/composer.json +++ b/composer.json @@ -13,8 +13,8 @@ "php": "^8.2" }, "require-dev": { - "phpunit/phpunit": "^11.0", - "phpstan/phpstan": "^2.0" + "phpstan/phpstan": "^2.0", + "pestphp/pest": "^3.7" }, "autoload": { "psr-4": { @@ -25,5 +25,10 @@ "psr-4": { "SmartonDev\\HttpCache\\Tests\\": "tests/" } + }, + "config": { + "allow-plugins": { + "pestphp/pest-plugin": true + } } } diff --git a/composer.lock b/composer.lock index fd87071..5672b7e 100644 --- a/composer.lock +++ b/composer.lock @@ -4,21 +4,350 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "e32fb077c47b73a53271bf37e25230ef", + "content-hash": "dde026db5845d21e69f383fbbab78a71", "packages": [], "packages-dev": [ + { + "name": "brianium/paratest", + "version": "v7.7.0", + "source": { + "type": "git", + "url": "https://github.com/paratestphp/paratest.git", + "reference": "4fb3f73bc5a4c3146bac2850af7dc72435a32daf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paratestphp/paratest/zipball/4fb3f73bc5a4c3146bac2850af7dc72435a32daf", + "reference": "4fb3f73bc5a4c3146bac2850af7dc72435a32daf", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-simplexml": "*", + "fidry/cpu-core-counter": "^1.2.0", + "jean85/pretty-package-versions": "^2.1.0", + "php": "~8.2.0 || ~8.3.0 || ~8.4.0", + "phpunit/php-code-coverage": "^11.0.8", + "phpunit/php-file-iterator": "^5.1.0", + "phpunit/php-timer": "^7.0.1", + "phpunit/phpunit": "^11.5.1", + "sebastian/environment": "^7.2.0", + "symfony/console": "^6.4.14 || ^7.2.1", + "symfony/process": "^6.4.14 || ^7.2.0" + }, + "require-dev": { + "doctrine/coding-standard": "^12.0.0", + "ext-pcov": "*", + "ext-posix": "*", + "phpstan/phpstan": "^2.0.3", + "phpstan/phpstan-deprecation-rules": "^2.0.1", + "phpstan/phpstan-phpunit": "^2.0.1", + "phpstan/phpstan-strict-rules": "^2", + "squizlabs/php_codesniffer": "^3.11.1", + "symfony/filesystem": "^6.4.13 || ^7.2.0" + }, + "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.7.0" + }, + "funding": [ + { + "url": "https://github.com/sponsors/Slamdunk", + "type": "github" + }, + { + "url": "https://paypal.me/filippotessarotto", + "type": "paypal" + } + ], + "time": "2024-12-11T14:50:44+00:00" + }, + { + "name": "doctrine/deprecations", + "version": "1.1.4", + "source": { + "type": "git", + "url": "https://github.com/doctrine/deprecations.git", + "reference": "31610dbb31faa98e6b5447b62340826f54fbc4e9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/31610dbb31faa98e6b5447b62340826f54fbc4e9", + "reference": "31610dbb31faa98e6b5447b62340826f54fbc4e9", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9 || ^12", + "phpstan/phpstan": "1.4.10 || 2.0.3", + "phpstan/phpstan-phpunit": "^1.0 || ^2", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "psr/log": "^1 || ^2 || ^3" + }, + "suggest": { + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Deprecations\\": "src" + } + }, + "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.4" + }, + "time": "2024-12-07T21:18:45+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.0", + "version": "1.12.1", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c" + "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", - "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/123267b2c49fbf30d78a7b2d333f6be754b94845", + "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845", "shasum": "" }, "require": { @@ -57,7 +386,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 +394,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.1.0", + "version": "v5.3.1", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "683130c2ff8c2739f4822ff7ac5c873ec529abd1" + "reference": "8eea230464783aa9671db8eea6f8c6ac5285794b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/683130c2ff8c2739f4822ff7ac5c873ec529abd1", - "reference": "683130c2ff8c2739f4822ff7ac5c873ec529abd1", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/8eea230464783aa9671db8eea6f8c6ac5285794b", + "reference": "8eea230464783aa9671db8eea6f8c6ac5285794b", "shasum": "" }, "require": { @@ -121,95 +450,1768 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.1.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.3.1" }, - "time": "2024-07-01T20:03:41+00:00" + "time": "2024-10-08T18:51:32+00:00" }, { - "name": "phar-io/manifest", - "version": "2.0.4", + "name": "nunomaduro/collision", + "version": "v8.5.0", "source": { "type": "git", - "url": "https://github.com/phar-io/manifest.git", - "reference": "54750ef60c58e43759730615a392c31c80e23176" + "url": "https://github.com/nunomaduro/collision.git", + "reference": "f5c101b929c958e849a633283adff296ed5f38f5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", - "reference": "54750ef60c58e43759730615a392c31c80e23176", + "url": "https://api.github.com/repos/nunomaduro/collision/zipball/f5c101b929c958e849a633283adff296ed5f38f5", + "reference": "f5c101b929c958e849a633283adff296ed5f38f5", "shasum": "" }, "require": { - "ext-dom": "*", - "ext-libxml": "*", - "ext-phar": "*", - "ext-xmlwriter": "*", - "phar-io/version": "^3.0.1", - "php": "^7.2 || ^8.0" + "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-master": "2.0.x-dev" + "dev-8.x": "8.x-dev" } }, "autoload": { - "classmap": [ - "src/" - ] + "files": [ + "./src/Adapters/Phpunit/Autoload.php" + ], + "psr-4": { + "NunoMaduro\\Collision\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" + "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.3.0", + "source": { + "type": "git", + "url": "https://github.com/nunomaduro/termwind.git", + "reference": "52915afe6a1044e8b9cee1bcff836fb63acf9cda" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/52915afe6a1044e8b9cee1bcff836fb63acf9cda", + "reference": "52915afe6a1044e8b9cee1bcff836fb63acf9cda", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": "^8.2", + "symfony/console": "^7.1.8" + }, + "require-dev": { + "illuminate/console": "^11.33.2", + "laravel/pint": "^1.18.2", + "mockery/mockery": "^1.6.12", + "pestphp/pest": "^2.36.0", + "phpstan/phpstan": "^1.12.11", + "phpstan/phpstan-strict-rules": "^1.6.1", + "symfony/var-dumper": "^7.1.8", + "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.3.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-11-21T10:39:51+00:00" + }, + { + "name": "pestphp/pest", + "version": "v3.7.1", + "source": { + "type": "git", + "url": "https://github.com/pestphp/pest.git", + "reference": "bf3178473dcaa53b0458f21dfdb271306ea62512" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pestphp/pest/zipball/bf3178473dcaa53b0458f21dfdb271306ea62512", + "reference": "bf3178473dcaa53b0458f21dfdb271306ea62512", + "shasum": "" + }, + "require": { + "brianium/paratest": "^7.7.0", + "nunomaduro/collision": "^8.5.0", + "nunomaduro/termwind": "^2.3.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.5.1" + }, + "conflict": { + "filp/whoops": "<2.16.0", + "phpunit/phpunit": ">11.5.1", + "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.2.0", + "symfony/process": "^7.2.0" + }, + "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.7.1" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + } + ], + "time": "2024-12-12T11:52:01+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", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "54750ef60c58e43759730615a392c31c80e23176" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:33:53+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, + { + "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.1", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "e5e784149a09bd69d9a5e3b01c5cbd2e2bd653d8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/e5e784149a09bd69d9a5e3b01c5cbd2e2bd653d8", + "reference": "e5e784149a09bd69d9a5e3b01c5cbd2e2bd653d8", + "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.1" + }, + "time": "2024-12-07T09:39:29+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": "phpstan/phpdoc-parser", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "c00d78fb6b29658347f9d37ebe104bffadf36299" + }, + "dist": { + "type": "zip", + "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": "phpstan/phpstan", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan.git", + "reference": "50d276fc3bf1430ec315f2f109bbde2769821524" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/50d276fc3bf1430ec315f2f109bbde2769821524", + "reference": "50d276fc3bf1430ec315f2f109bbde2769821524", + "shasum": "" + }, + "require": { + "php": "^7.4|^8.0" + }, + "conflict": { + "phpstan/phpstan-shim": "*" + }, + "bin": [ + "phpstan", + "phpstan.phar" + ], + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPStan - PHP Static Analysis Tool", + "keywords": [ + "dev", + "static analysis" + ], + "support": { + "docs": "https://phpstan.org/user-guide/getting-started", + "forum": "https://github.com/phpstan/phpstan/discussions", + "issues": "https://github.com/phpstan/phpstan/issues", + "security": "https://github.com/phpstan/phpstan/security/policy", + "source": "https://github.com/phpstan/phpstan-src" + }, + "funding": [ + { + "url": "https://github.com/ondrejmirtes", + "type": "github" + }, + { + "url": "https://github.com/phpstan", + "type": "github" + } + ], + "time": "2024-12-17T17:14:01+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "11.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "418c59fd080954f8c4aa5631d9502ecda2387118" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/418c59fd080954f8c4aa5631d9502ecda2387118", + "reference": "418c59fd080954f8c4aa5631d9502ecda2387118", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^5.3.1", + "php": ">=8.2", + "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.2", + "theseer/tokenizer": "^1.2.3" + }, + "require-dev": { + "phpunit/phpunit": "^11.5.0" + }, + "suggest": { + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "11.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "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.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-12-11T12:34:27+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "5.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "118cfaaa8bc5aef3287bf315b6060b1174754af6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/118cfaaa8bc5aef3287bf315b6060b1174754af6", + "reference": "118cfaaa8bc5aef3287bf315b6060b1174754af6", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/5.1.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-08-27T05:02:59+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "5.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/c1ca3814734c07492b3d4c5f794f4b0995333da2", + "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^11.0" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "security": "https://github.com/sebastianbergmann/php-invoker/security/policy", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/5.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:07:44+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "3e0404dc6b300e6bf56415467ebcb3fe4f33e964" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/3e0404dc6b300e6bf56415467ebcb3fe4f33e964", + "reference": "3e0404dc6b300e6bf56415467ebcb3fe4f33e964", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:08:43+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "7.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3b415def83fbcb41f991d9ebf16ae4ad8b7837b3", + "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "security": "https://github.com/sebastianbergmann/php-timer/security/policy", + "source": "https://github.com/sebastianbergmann/php-timer/tree/7.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:09:35+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "11.5.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "2b94d4f2450b9869fa64a46fd8a6a41997aef56a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/2b94d4f2450b9869fa64a46fd8a6a41997aef56a", + "reference": "2b94d4f2450b9869fa64a46fd8a6a41997aef56a", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.12.1", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", + "php": ">=8.2", + "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.2.1", + "sebastian/diff": "^6.0.2", + "sebastian/environment": "^7.2.0", + "sebastian/exporter": "^6.3.0", + "sebastian/global-state": "^7.0.2", + "sebastian/object-enumerator": "^6.0.1", + "sebastian/type": "^5.1.0", + "sebastian/version": "^5.0.2", + "staabm/side-effects-detector": "^1.0.5" + }, + "suggest": { + "ext-soap": "To be able to generate mocks based on WSDL files" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "11.5-dev" + } + }, + "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.1" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsors.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "type": "tidelift" + } + ], + "time": "2024-12-11T10:52:48+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", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "15c5dd40dc4f38794d383bb95465193f5e0ae180" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/15c5dd40dc4f38794d383bb95465193f5e0ae180", + "reference": "15c5dd40dc4f38794d383bb95465193f5e0ae180", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/3.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:41:36+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "ee88b0cdbe74cf8dd3b54940ff17643c0d6543ca" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/ee88b0cdbe74cf8dd3b54940ff17643c0d6543ca", + "reference": "ee88b0cdbe74cf8dd3b54940ff17643c0d6543ca", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "security": "https://github.com/sebastianbergmann/code-unit/security/policy", + "source": "https://github.com/sebastianbergmann/code-unit/tree/3.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-12-12T09:59:06+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "183a9b2632194febd219bb9246eee421dad8d45e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/183a9b2632194febd219bb9246eee421dad8d45e", + "reference": "183a9b2632194febd219bb9246eee421dad8d45e", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "security": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/security/policy", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:45:54+00:00" + }, + { + "name": "sebastian/comparator", + "version": "6.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "43d129d6a0f81c78bee378b46688293eb7ea3739" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/43d129d6a0f81c78bee378b46688293eb7ea3739", + "reference": "43d129d6a0f81c78bee378b46688293eb7ea3739", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-mbstring": "*", + "php": ">=8.2", + "sebastian/diff": "^6.0", + "sebastian/exporter": "^6.0" + }, + "require-dev": { + "phpunit/phpunit": "^11.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" }, { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" + "name": "Volker Dusch", + "email": "github@wallbash.com" }, { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" } ], - "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], "support": { - "issues": "https://github.com/phar-io/manifest/issues", - "source": "https://github.com/phar-io/manifest/tree/2.0.4" + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "security": "https://github.com/sebastianbergmann/comparator/security/policy", + "source": "https://github.com/sebastianbergmann/comparator/tree/6.2.1" }, "funding": [ { - "url": "https://github.com/theseer", + "url": "https://github.com/sebastianbergmann", "type": "github" } ], - "time": "2024-03-03T12:33:53+00:00" + "time": "2024-10-31T05:30:08+00:00" }, { - "name": "phar-io/version", - "version": "3.2.1", + "name": "sebastian/complexity", + "version": "4.0.1", "source": { "type": "git", - "url": "https://github.com/phar-io/version.git", - "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "ee41d384ab1906c68852636b6de493846e13e5a0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", - "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/ee41d384ab1906c68852636b6de493846e13e5a0", + "reference": "ee41d384ab1906c68852636b6de493846e13e5a0", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0" + "nikic/php-parser": "^5.0", + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" }, "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, "autoload": { "classmap": [ "src/" @@ -220,127 +2222,121 @@ "BSD-3-Clause" ], "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", - "role": "Developer" + "role": "lead" } ], - "description": "Library for handling version information and constraints", + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", "support": { - "issues": "https://github.com/phar-io/version/issues", - "source": "https://github.com/phar-io/version/tree/3.2.1" + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "security": "https://github.com/sebastianbergmann/complexity/security/policy", + "source": "https://github.com/sebastianbergmann/complexity/tree/4.0.1" }, - "time": "2022-02-21T01:04:05+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:49:50+00:00" }, { - "name": "phpstan/phpstan", - "version": "2.0.4", + "name": "sebastian/diff", + "version": "6.0.2", "source": { "type": "git", - "url": "https://github.com/phpstan/phpstan.git", - "reference": "50d276fc3bf1430ec315f2f109bbde2769821524" + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/50d276fc3bf1430ec315f2f109bbde2769821524", - "reference": "50d276fc3bf1430ec315f2f109bbde2769821524", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/b4ccd857127db5d41a5b676f24b51371d76d8544", + "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544", "shasum": "" }, "require": { - "php": "^7.4|^8.0" + "php": ">=8.2" }, - "conflict": { - "phpstan/phpstan-shim": "*" + "require-dev": { + "phpunit/phpunit": "^11.0", + "symfony/process": "^4.2 || ^5" }, - "bin": [ - "phpstan", - "phpstan.phar" - ], "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, "autoload": { - "files": [ - "bootstrap.php" + "classmap": [ + "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], - "description": "PHPStan - PHP Static Analysis Tool", + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", "keywords": [ - "dev", - "static analysis" + "diff", + "udiff", + "unidiff", + "unified diff" ], "support": { - "docs": "https://phpstan.org/user-guide/getting-started", - "forum": "https://github.com/phpstan/phpstan/discussions", - "issues": "https://github.com/phpstan/phpstan/issues", - "security": "https://github.com/phpstan/phpstan/security/policy", - "source": "https://github.com/phpstan/phpstan-src" + "issues": "https://github.com/sebastianbergmann/diff/issues", + "security": "https://github.com/sebastianbergmann/diff/security/policy", + "source": "https://github.com/sebastianbergmann/diff/tree/6.0.2" }, "funding": [ { - "url": "https://github.com/ondrejmirtes", - "type": "github" - }, - { - "url": "https://github.com/phpstan", + "url": "https://github.com/sebastianbergmann", "type": "github" } ], - "time": "2024-12-17T17:14:01+00:00" + "time": "2024-07-03T04:53:05+00:00" }, { - "name": "phpunit/php-code-coverage", - "version": "11.0.5", + "name": "sebastian/environment", + "version": "7.2.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "19b6365ab8b59a64438c0c3f4241feeb480c9861" + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "855f3ae0ab316bbafe1ba4e16e9f3c078d24a0c5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/19b6365ab8b59a64438c0c3f4241feeb480c9861", - "reference": "19b6365ab8b59a64438c0c3f4241feeb480c9861", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/855f3ae0ab316bbafe1ba4e16e9f3c078d24a0c5", + "reference": "855f3ae0ab316bbafe1ba4e16e9f3c078d24a0c5", "shasum": "" }, "require": { - "ext-dom": "*", - "ext-libxml": "*", - "ext-xmlwriter": "*", - "nikic/php-parser": "^5.0", - "php": ">=8.2", - "phpunit/php-file-iterator": "^5.0", - "phpunit/php-text-template": "^4.0", - "sebastian/code-unit-reverse-lookup": "^4.0", - "sebastian/complexity": "^4.0", - "sebastian/environment": "^7.0", - "sebastian/lines-of-code": "^3.0", - "sebastian/version": "^5.0", - "theseer/tokenizer": "^1.2.0" + "php": ">=8.2" }, "require-dev": { "phpunit/phpunit": "^11.0" }, "suggest": { - "ext-pcov": "PHP extension that provides line coverage", - "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + "ext-posix": "*" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "11.0-dev" + "dev-main": "7.2-dev" } }, "autoload": { @@ -355,21 +2351,20 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "email": "sebastian@phpunit.de" } ], - "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", - "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "https://github.com/sebastianbergmann/environment", "keywords": [ - "coverage", - "testing", - "xunit" + "Xdebug", + "environment", + "hhvm" ], "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.5" + "issues": "https://github.com/sebastianbergmann/environment/issues", + "security": "https://github.com/sebastianbergmann/environment/security/policy", + "source": "https://github.com/sebastianbergmann/environment/tree/7.2.0" }, "funding": [ { @@ -377,32 +2372,34 @@ "type": "github" } ], - "time": "2024-07-03T05:05:37+00:00" + "time": "2024-07-03T04:54:44+00:00" }, { - "name": "phpunit/php-file-iterator", - "version": "5.0.1", + "name": "sebastian/exporter", + "version": "6.3.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "6ed896bf50bbbfe4d504a33ed5886278c78e4a26" + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "3473f61172093b2da7de1fb5782e1f24cc036dc3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/6ed896bf50bbbfe4d504a33ed5886278c78e4a26", - "reference": "6ed896bf50bbbfe4d504a33ed5886278c78e4a26", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/3473f61172093b2da7de1fb5782e1f24cc036dc3", + "reference": "3473f61172093b2da7de1fb5782e1f24cc036dc3", "shasum": "" }, "require": { - "php": ">=8.2" + "ext-mbstring": "*", + "php": ">=8.2", + "sebastian/recursion-context": "^6.0" }, "require-dev": { - "phpunit/phpunit": "^11.0" + "phpunit/phpunit": "^11.3" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "5.0-dev" + "dev-main": "6.1-dev" } }, "autoload": { @@ -417,20 +2414,35 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" } ], - "description": "FilterIterator implementation that filters files based on a list of suffixes.", - "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", "keywords": [ - "filesystem", - "iterator" + "export", + "exporter" ], "support": { - "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/5.0.1" + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "security": "https://github.com/sebastianbergmann/exporter/security/policy", + "source": "https://github.com/sebastianbergmann/exporter/tree/6.3.0" }, "funding": [ { @@ -438,36 +2450,35 @@ "type": "github" } ], - "time": "2024-07-03T05:06:37+00:00" + "time": "2024-12-05T09:17:50+00:00" }, { - "name": "phpunit/php-invoker", - "version": "5.0.1", + "name": "sebastian/global-state", + "version": "7.0.2", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-invoker.git", - "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2" + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "3be331570a721f9a4b5917f4209773de17f747d7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/c1ca3814734c07492b3d4c5f794f4b0995333da2", - "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/3be331570a721f9a4b5917f4209773de17f747d7", + "reference": "3be331570a721f9a4b5917f4209773de17f747d7", "shasum": "" }, "require": { - "php": ">=8.2" + "php": ">=8.2", + "sebastian/object-reflector": "^4.0", + "sebastian/recursion-context": "^6.0" }, "require-dev": { - "ext-pcntl": "*", + "ext-dom": "*", "phpunit/phpunit": "^11.0" }, - "suggest": { - "ext-pcntl": "*" - }, "type": "library", "extra": { "branch-alias": { - "dev-main": "5.0-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -482,19 +2493,18 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "email": "sebastian@phpunit.de" } ], - "description": "Invoke callables with a timeout", - "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "description": "Snapshotting of global state", + "homepage": "https://www.github.com/sebastianbergmann/global-state", "keywords": [ - "process" + "global state" ], "support": { - "issues": "https://github.com/sebastianbergmann/php-invoker/issues", - "security": "https://github.com/sebastianbergmann/php-invoker/security/policy", - "source": "https://github.com/sebastianbergmann/php-invoker/tree/5.0.1" + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "security": "https://github.com/sebastianbergmann/global-state/security/policy", + "source": "https://github.com/sebastianbergmann/global-state/tree/7.0.2" }, "funding": [ { @@ -502,23 +2512,24 @@ "type": "github" } ], - "time": "2024-07-03T05:07:44+00:00" + "time": "2024-07-03T04:57:36+00:00" }, { - "name": "phpunit/php-text-template", - "version": "4.0.1", + "name": "sebastian/lines-of-code", + "version": "3.0.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "3e0404dc6b300e6bf56415467ebcb3fe4f33e964" + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "d36ad0d782e5756913e42ad87cb2890f4ffe467a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/3e0404dc6b300e6bf56415467ebcb3fe4f33e964", - "reference": "3e0404dc6b300e6bf56415467ebcb3fe4f33e964", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/d36ad0d782e5756913e42ad87cb2890f4ffe467a", + "reference": "d36ad0d782e5756913e42ad87cb2890f4ffe467a", "shasum": "" }, "require": { + "nikic/php-parser": "^5.0", "php": ">=8.2" }, "require-dev": { @@ -527,7 +2538,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "4.0-dev" + "dev-main": "3.0-dev" } }, "autoload": { @@ -546,15 +2557,12 @@ "role": "lead" } ], - "description": "Simple template engine.", - "homepage": "https://github.com/sebastianbergmann/php-text-template/", - "keywords": [ - "template" - ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", "support": { - "issues": "https://github.com/sebastianbergmann/php-text-template/issues", - "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/4.0.1" + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/3.0.1" }, "funding": [ { @@ -562,24 +2570,26 @@ "type": "github" } ], - "time": "2024-07-03T05:08:43+00:00" + "time": "2024-07-03T04:58:38+00:00" }, { - "name": "phpunit/php-timer", - "version": "7.0.1", + "name": "sebastian/object-enumerator", + "version": "6.0.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3" + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "f5b498e631a74204185071eb41f33f38d64608aa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3b415def83fbcb41f991d9ebf16ae4ad8b7837b3", - "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/f5b498e631a74204185071eb41f33f38d64608aa", + "reference": "f5b498e631a74204185071eb41f33f38d64608aa", "shasum": "" }, "require": { - "php": ">=8.2" + "php": ">=8.2", + "sebastian/object-reflector": "^4.0", + "sebastian/recursion-context": "^6.0" }, "require-dev": { "phpunit/phpunit": "^11.0" @@ -587,7 +2597,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "7.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -602,19 +2612,15 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "email": "sebastian@phpunit.de" } ], - "description": "Utility class for timing", - "homepage": "https://github.com/sebastianbergmann/php-timer/", - "keywords": [ - "timer" - ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", "support": { - "issues": "https://github.com/sebastianbergmann/php-timer/issues", - "security": "https://github.com/sebastianbergmann/php-timer/security/policy", - "source": "https://github.com/sebastianbergmann/php-timer/tree/7.0.1" + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "security": "https://github.com/sebastianbergmann/object-enumerator/security/policy", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/6.0.1" }, "funding": [ { @@ -622,65 +2628,35 @@ "type": "github" } ], - "time": "2024-07-03T05:09:35+00:00" + "time": "2024-07-03T05:00:13+00:00" }, { - "name": "phpunit/phpunit", - "version": "11.2.8", + "name": "sebastian/object-reflector", + "version": "4.0.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "a7a29e8d3113806f18f99d670f580a30e8ffff39" + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "6e1a43b411b2ad34146dee7524cb13a068bb35f9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a7a29e8d3113806f18f99d670f580a30e8ffff39", - "reference": "a7a29e8d3113806f18f99d670f580a30e8ffff39", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/6e1a43b411b2ad34146dee7524cb13a068bb35f9", + "reference": "6e1a43b411b2ad34146dee7524cb13a068bb35f9", "shasum": "" }, "require": { - "ext-dom": "*", - "ext-json": "*", - "ext-libxml": "*", - "ext-mbstring": "*", - "ext-xml": "*", - "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.12.0", - "phar-io/manifest": "^2.0.4", - "phar-io/version": "^3.2.1", - "php": ">=8.2", - "phpunit/php-code-coverage": "^11.0.5", - "phpunit/php-file-iterator": "^5.0.1", - "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.0.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.0.1", - "sebastian/version": "^5.0.1" + "php": ">=8.2" }, - "suggest": { - "ext-soap": "To be able to generate mocks based on WSDL files" + "require-dev": { + "phpunit/phpunit": "^11.0" }, - "bin": [ - "phpunit" - ], "type": "library", "extra": { "branch-alias": { - "dev-main": "11.2-dev" + "dev-main": "4.0-dev" } }, "autoload": { - "files": [ - "src/Framework/Assert/Functions.php" - ], "classmap": [ "src/" ] @@ -692,50 +2668,36 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "email": "sebastian@phpunit.de" } ], - "description": "The PHP Unit Testing framework.", - "homepage": "https://phpunit.de/", - "keywords": [ - "phpunit", - "testing", - "xunit" - ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", "support": { - "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/11.2.8" + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "security": "https://github.com/sebastianbergmann/object-reflector/security/policy", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/4.0.1" }, "funding": [ - { - "url": "https://phpunit.de/sponsors.html", - "type": "custom" - }, { "url": "https://github.com/sebastianbergmann", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", - "type": "tidelift" } ], - "time": "2024-07-18T14:56:37+00:00" + "time": "2024-07-03T05:01:32+00:00" }, { - "name": "sebastian/cli-parser", - "version": "3.0.2", + "name": "sebastian/recursion-context", + "version": "6.0.2", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "15c5dd40dc4f38794d383bb95465193f5e0ae180" + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "694d156164372abbd149a4b85ccda2e4670c0e16" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/15c5dd40dc4f38794d383bb95465193f5e0ae180", - "reference": "15c5dd40dc4f38794d383bb95465193f5e0ae180", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/694d156164372abbd149a4b85ccda2e4670c0e16", + "reference": "694d156164372abbd149a4b85ccda2e4670c0e16", "shasum": "" }, "require": { @@ -747,7 +2709,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "3.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -762,16 +2724,23 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" } ], - "description": "Library for parsing CLI options", - "homepage": "https://github.com/sebastianbergmann/cli-parser", + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", "support": { - "issues": "https://github.com/sebastianbergmann/cli-parser/issues", - "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", - "source": "https://github.com/sebastianbergmann/cli-parser/tree/3.0.2" + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/6.0.2" }, "funding": [ { @@ -779,32 +2748,32 @@ "type": "github" } ], - "time": "2024-07-03T04:41:36+00:00" + "time": "2024-07-03T05:10:34+00:00" }, { - "name": "sebastian/code-unit", - "version": "3.0.1", + "name": "sebastian/type", + "version": "5.1.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit.git", - "reference": "6bb7d09d6623567178cf54126afa9c2310114268" + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "461b9c5da241511a2a0e8f240814fb23ce5c0aac" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/6bb7d09d6623567178cf54126afa9c2310114268", - "reference": "6bb7d09d6623567178cf54126afa9c2310114268", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/461b9c5da241511a2a0e8f240814fb23ce5c0aac", + "reference": "461b9c5da241511a2a0e8f240814fb23ce5c0aac", "shasum": "" }, "require": { "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^11.0" + "phpunit/phpunit": "^11.3" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.0-dev" + "dev-main": "5.1-dev" } }, "autoload": { @@ -823,12 +2792,12 @@ "role": "lead" } ], - "description": "Collection of value objects that represent the PHP code units", - "homepage": "https://github.com/sebastianbergmann/code-unit", + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", "support": { - "issues": "https://github.com/sebastianbergmann/code-unit/issues", - "security": "https://github.com/sebastianbergmann/code-unit/security/policy", - "source": "https://github.com/sebastianbergmann/code-unit/tree/3.0.1" + "issues": "https://github.com/sebastianbergmann/type/issues", + "security": "https://github.com/sebastianbergmann/type/security/policy", + "source": "https://github.com/sebastianbergmann/type/tree/5.1.0" }, "funding": [ { @@ -836,32 +2805,29 @@ "type": "github" } ], - "time": "2024-07-03T04:44:28+00:00" + "time": "2024-09-17T13:12:04+00:00" }, { - "name": "sebastian/code-unit-reverse-lookup", - "version": "4.0.1", + "name": "sebastian/version", + "version": "5.0.2", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "183a9b2632194febd219bb9246eee421dad8d45e" + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/183a9b2632194febd219bb9246eee421dad8d45e", - "reference": "183a9b2632194febd219bb9246eee421dad8d45e", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c687e3387b99f5b03b6caa64c74b63e2936ff874", + "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874", "shasum": "" }, "require": { "php": ">=8.2" }, - "require-dev": { - "phpunit/phpunit": "^11.0" - }, "type": "library", "extra": { "branch-alias": { - "dev-main": "4.0-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -876,15 +2842,16 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Looks up which function or method a line of code belongs to", - "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", "support": { - "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", - "security": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/security/policy", - "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/4.0.1" + "issues": "https://github.com/sebastianbergmann/version/issues", + "security": "https://github.com/sebastianbergmann/version/security/policy", + "source": "https://github.com/sebastianbergmann/version/tree/5.0.2" }, "funding": [ { @@ -892,760 +2859,891 @@ "type": "github" } ], - "time": "2024-07-03T04:45:54+00:00" + "time": "2024-10-09T05:16:32+00:00" }, { - "name": "sebastian/comparator", - "version": "6.0.1", + "name": "staabm/side-effects-detector", + "version": "1.0.5", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "131942b86d3587291067a94f295498ab6ac79c20" + "url": "https://github.com/staabm/side-effects-detector.git", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/131942b86d3587291067a94f295498ab6ac79c20", - "reference": "131942b86d3587291067a94f295498ab6ac79c20", + "url": "https://api.github.com/repos/staabm/side-effects-detector/zipball/d8334211a140ce329c13726d4a715adbddd0a163", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163", "shasum": "" }, "require": { - "ext-dom": "*", - "ext-mbstring": "*", - "php": ">=8.2", - "sebastian/diff": "^6.0", - "sebastian/exporter": "^6.0" + "ext-tokenizer": "*", + "php": "^7.4 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^11.0" + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^1.12.6", + "phpunit/phpunit": "^9.6.21", + "symfony/var-dumper": "^5.4.43", + "tomasvotruba/type-coverage": "1.0.0", + "tomasvotruba/unused-public": "1.0.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-main": "6.0-dev" - } - }, "autoload": { "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - } + "lib/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" ], - "description": "Provides the functionality to compare PHP values for equality", - "homepage": "https://github.com/sebastianbergmann/comparator", + "description": "A static analysis tool to detect side effects in PHP code", "keywords": [ - "comparator", - "compare", - "equality" + "static analysis" ], "support": { - "issues": "https://github.com/sebastianbergmann/comparator/issues", - "security": "https://github.com/sebastianbergmann/comparator/security/policy", - "source": "https://github.com/sebastianbergmann/comparator/tree/6.0.1" + "issues": "https://github.com/staabm/side-effects-detector/issues", + "source": "https://github.com/staabm/side-effects-detector/tree/1.0.5" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://github.com/staabm", "type": "github" } ], - "time": "2024-07-03T04:48:07+00:00" + "time": "2024-10-20T05:08:20+00:00" }, { - "name": "sebastian/complexity", - "version": "4.0.1", + "name": "symfony/console", + "version": "v7.2.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "ee41d384ab1906c68852636b6de493846e13e5a0" + "url": "https://github.com/symfony/console.git", + "reference": "fefcc18c0f5d0efe3ab3152f15857298868dc2c3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/ee41d384ab1906c68852636b6de493846e13e5a0", - "reference": "ee41d384ab1906c68852636b6de493846e13e5a0", + "url": "https://api.github.com/repos/symfony/console/zipball/fefcc18c0f5d0efe3ab3152f15857298868dc2c3", + "reference": "fefcc18c0f5d0efe3ab3152f15857298868dc2c3", "shasum": "" }, "require": { - "nikic/php-parser": "^5.0", - "php": ">=8.2" + "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": { - "phpunit/phpunit": "^11.0" + "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", - "extra": { - "branch-alias": { - "dev-main": "4.0-dev" - } - }, "autoload": { - "classmap": [ - "src/" + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Library for calculating the complexity of PHP code units", - "homepage": "https://github.com/sebastianbergmann/complexity", + "description": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command-line", + "console", + "terminal" + ], "support": { - "issues": "https://github.com/sebastianbergmann/complexity/issues", - "security": "https://github.com/sebastianbergmann/complexity/security/policy", - "source": "https://github.com/sebastianbergmann/complexity/tree/4.0.1" + "source": "https://github.com/symfony/console/tree/v7.2.1" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "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-07-03T04:49:50+00:00" + "time": "2024-12-11T03:49:26+00:00" }, { - "name": "sebastian/diff", - "version": "6.0.2", + "name": "symfony/deprecation-contracts", + "version": "v3.5.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544" + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/b4ccd857127db5d41a5b676f24b51371d76d8544", - "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", + "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", "shasum": "" }, "require": { - "php": ">=8.2" - }, - "require-dev": { - "phpunit/phpunit": "^11.0", - "symfony/process": "^4.2 || ^5" + "php": ">=8.1" }, "type": "library", "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, "branch-alias": { - "dev-main": "6.0-dev" + "dev-main": "3.5-dev" } }, "autoload": { - "classmap": [ - "src/" + "files": [ + "function.php" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Diff implementation", - "homepage": "https://github.com/sebastianbergmann/diff", - "keywords": [ - "diff", - "udiff", - "unidiff", - "unified diff" - ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", "support": { - "issues": "https://github.com/sebastianbergmann/diff/issues", - "security": "https://github.com/sebastianbergmann/diff/security/policy", - "source": "https://github.com/sebastianbergmann/diff/tree/6.0.2" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.1" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "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-07-03T04:53:05+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { - "name": "sebastian/environment", - "version": "7.2.0", + "name": "symfony/finder", + "version": "v7.2.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "855f3ae0ab316bbafe1ba4e16e9f3c078d24a0c5" + "url": "https://github.com/symfony/finder.git", + "reference": "6de263e5868b9a137602dd1e33e4d48bfae99c49" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/855f3ae0ab316bbafe1ba4e16e9f3c078d24a0c5", - "reference": "855f3ae0ab316bbafe1ba4e16e9f3c078d24a0c5", + "url": "https://api.github.com/repos/symfony/finder/zipball/6de263e5868b9a137602dd1e33e4d48bfae99c49", + "reference": "6de263e5868b9a137602dd1e33e4d48bfae99c49", "shasum": "" }, "require": { "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^11.0" - }, - "suggest": { - "ext-posix": "*" + "symfony/filesystem": "^6.4|^7.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-main": "7.2-dev" - } - }, "autoload": { - "classmap": [ - "src/" + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Provides functionality to handle HHVM/PHP environments", - "homepage": "https://github.com/sebastianbergmann/environment", - "keywords": [ - "Xdebug", - "environment", - "hhvm" - ], + "description": "Finds files and directories via an intuitive fluent interface", + "homepage": "https://symfony.com", "support": { - "issues": "https://github.com/sebastianbergmann/environment/issues", - "security": "https://github.com/sebastianbergmann/environment/security/policy", - "source": "https://github.com/sebastianbergmann/environment/tree/7.2.0" + "source": "https://github.com/symfony/finder/tree/v7.2.0" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "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-07-03T04:54:44+00:00" + "time": "2024-10-23T06:56:12+00:00" }, { - "name": "sebastian/exporter", - "version": "6.1.3", + "name": "symfony/polyfill-ctype", + "version": "v1.31.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "c414673eee9a8f9d51bbf8d61fc9e3ef1e85b20e" + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/c414673eee9a8f9d51bbf8d61fc9e3ef1e85b20e", - "reference": "c414673eee9a8f9d51bbf8d61fc9e3ef1e85b20e", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", "shasum": "" }, "require": { - "ext-mbstring": "*", - "php": ">=8.2", - "sebastian/recursion-context": "^6.0" + "php": ">=7.2" }, - "require-dev": { - "phpunit/phpunit": "^11.2" + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "6.1-dev" + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { - "classmap": [ - "src/" - ] + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" }, { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "https://www.github.com/sebastianbergmann/exporter", + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", "keywords": [ - "export", - "exporter" + "compatibility", + "ctype", + "polyfill", + "portable" ], "support": { - "issues": "https://github.com/sebastianbergmann/exporter/issues", - "security": "https://github.com/sebastianbergmann/exporter/security/policy", - "source": "https://github.com/sebastianbergmann/exporter/tree/6.1.3" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "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-07-03T04:56:19+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { - "name": "sebastian/global-state", - "version": "7.0.2", + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.31.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "3be331570a721f9a4b5917f4209773de17f747d7" + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/3be331570a721f9a4b5917f4209773de17f747d7", - "reference": "3be331570a721f9a4b5917f4209773de17f747d7", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", "shasum": "" }, "require": { - "php": ">=8.2", - "sebastian/object-reflector": "^4.0", - "sebastian/recursion-context": "^6.0" + "php": ">=7.2" }, - "require-dev": { - "ext-dom": "*", - "phpunit/phpunit": "^11.0" + "suggest": { + "ext-intl": "For best performance" }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "7.0-dev" + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { - "classmap": [ - "src/" - ] + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Snapshotting of global state", - "homepage": "https://www.github.com/sebastianbergmann/global-state", + "description": "Symfony polyfill for intl's grapheme_* functions", + "homepage": "https://symfony.com", "keywords": [ - "global state" + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" ], "support": { - "issues": "https://github.com/sebastianbergmann/global-state/issues", - "security": "https://github.com/sebastianbergmann/global-state/security/policy", - "source": "https://github.com/sebastianbergmann/global-state/tree/7.0.2" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.31.0" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "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-07-03T04:57:36+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { - "name": "sebastian/lines-of-code", - "version": "3.0.1", + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.31.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "d36ad0d782e5756913e42ad87cb2890f4ffe467a" + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "3833d7255cc303546435cb650316bff708a1c75c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/d36ad0d782e5756913e42ad87cb2890f4ffe467a", - "reference": "d36ad0d782e5756913e42ad87cb2890f4ffe467a", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", + "reference": "3833d7255cc303546435cb650316bff708a1c75c", "shasum": "" }, "require": { - "nikic/php-parser": "^5.0", - "php": ">=8.2" + "php": ">=7.2" }, - "require-dev": { - "phpunit/phpunit": "^11.0" + "suggest": { + "ext-intl": "For best performance" }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "3.0-dev" + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, "classmap": [ - "src/" + "Resources/stubs" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Library for counting the lines of code in PHP source code", - "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], "support": { - "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", - "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/3.0.1" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.31.0" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "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-07-03T04:58:38+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { - "name": "sebastian/object-enumerator", - "version": "6.0.1", + "name": "symfony/polyfill-mbstring", + "version": "v1.31.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "f5b498e631a74204185071eb41f33f38d64608aa" + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/f5b498e631a74204185071eb41f33f38d64608aa", - "reference": "f5b498e631a74204185071eb41f33f38d64608aa", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341", + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341", "shasum": "" }, "require": { - "php": ">=8.2", - "sebastian/object-reflector": "^4.0", - "sebastian/recursion-context": "^6.0" + "php": ">=7.2" }, - "require-dev": { - "phpunit/phpunit": "^11.0" + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "6.0-dev" + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { - "classmap": [ - "src/" - ] + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Traverses array structures and object graphs to enumerate all referenced objects", - "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], "support": { - "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", - "security": "https://github.com/sebastianbergmann/object-enumerator/security/policy", - "source": "https://github.com/sebastianbergmann/object-enumerator/tree/6.0.1" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "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-07-03T05:00:13+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { - "name": "sebastian/object-reflector", - "version": "4.0.1", + "name": "symfony/process", + "version": "v7.2.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "6e1a43b411b2ad34146dee7524cb13a068bb35f9" + "url": "https://github.com/symfony/process.git", + "reference": "d34b22ba9390ec19d2dd966c40aa9e8462f27a7e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/6e1a43b411b2ad34146dee7524cb13a068bb35f9", - "reference": "6e1a43b411b2ad34146dee7524cb13a068bb35f9", + "url": "https://api.github.com/repos/symfony/process/zipball/d34b22ba9390ec19d2dd966c40aa9e8462f27a7e", + "reference": "d34b22ba9390ec19d2dd966c40aa9e8462f27a7e", "shasum": "" }, "require": { "php": ">=8.2" }, - "require-dev": { - "phpunit/phpunit": "^11.0" - }, "type": "library", - "extra": { - "branch-alias": { - "dev-main": "4.0-dev" - } - }, "autoload": { - "classmap": [ - "src/" + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Allows reflection of object attributes, including inherited and non-public ones", - "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "description": "Executes commands in sub-processes", + "homepage": "https://symfony.com", "support": { - "issues": "https://github.com/sebastianbergmann/object-reflector/issues", - "security": "https://github.com/sebastianbergmann/object-reflector/security/policy", - "source": "https://github.com/sebastianbergmann/object-reflector/tree/4.0.1" + "source": "https://github.com/symfony/process/tree/v7.2.0" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "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-07-03T05:01:32+00:00" + "time": "2024-11-06T14:24:19+00:00" }, { - "name": "sebastian/recursion-context", - "version": "6.0.2", + "name": "symfony/service-contracts", + "version": "v3.5.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "694d156164372abbd149a4b85ccda2e4670c0e16" + "url": "https://github.com/symfony/service-contracts.git", + "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/694d156164372abbd149a4b85ccda2e4670c0e16", - "reference": "694d156164372abbd149a4b85ccda2e4670c0e16", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/e53260aabf78fb3d63f8d79d69ece59f80d5eda0", + "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0", "shasum": "" }, "require": { - "php": ">=8.2" + "php": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" }, - "require-dev": { - "phpunit/phpunit": "^11.0" + "conflict": { + "ext-psr": "<1.1|>=2" }, "type": "library", "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, "branch-alias": { - "dev-main": "6.0-dev" + "dev-main": "3.5-dev" } }, "autoload": { - "classmap": [ - "src/" + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + }, + "exclude-from-classmap": [ + "/Test/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { - "name": "Adam Harvey", - "email": "aharvey@php.net" + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Provides functionality to recursively process PHP variables", - "homepage": "https://github.com/sebastianbergmann/recursion-context", + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], "support": { - "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/6.0.2" + "source": "https://github.com/symfony/service-contracts/tree/v3.5.1" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "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-07-03T05:10:34+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { - "name": "sebastian/type", - "version": "5.0.1", + "name": "symfony/string", + "version": "v7.2.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/type.git", - "reference": "fb6a6566f9589e86661291d13eba708cce5eb4aa" + "url": "https://github.com/symfony/string.git", + "reference": "446e0d146f991dde3e73f45f2c97a9faad773c82" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/fb6a6566f9589e86661291d13eba708cce5eb4aa", - "reference": "fb6a6566f9589e86661291d13eba708cce5eb4aa", + "url": "https://api.github.com/repos/symfony/string/zipball/446e0d146f991dde3e73f45f2c97a9faad773c82", + "reference": "446e0d146f991dde3e73f45f2c97a9faad773c82", "shasum": "" }, "require": { - "php": ">=8.2" + "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": { - "phpunit/phpunit": "^11.0" + "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", - "extra": { - "branch-alias": { - "dev-main": "5.0-dev" - } - }, "autoload": { - "classmap": [ - "src/" + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Collection of value objects that represent the types of the PHP type system", - "homepage": "https://github.com/sebastianbergmann/type", + "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": { - "issues": "https://github.com/sebastianbergmann/type/issues", - "security": "https://github.com/sebastianbergmann/type/security/policy", - "source": "https://github.com/sebastianbergmann/type/tree/5.0.1" + "source": "https://github.com/symfony/string/tree/v7.2.0" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "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-07-03T05:11:49+00:00" + "time": "2024-11-13T13:31:26+00:00" }, { - "name": "sebastian/version", - "version": "5.0.1", + "name": "ta-tikoma/phpunit-architecture-test", + "version": "0.8.4", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "45c9debb7d039ce9b97de2f749c2cf5832a06ac4" + "url": "https://github.com/ta-tikoma/phpunit-architecture-test.git", + "reference": "89f0dea1cb0f0d5744d3ec1764a286af5e006636" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/45c9debb7d039ce9b97de2f749c2cf5832a06ac4", - "reference": "45c9debb7d039ce9b97de2f749c2cf5832a06ac4", + "url": "https://api.github.com/repos/ta-tikoma/phpunit-architecture-test/zipball/89f0dea1cb0f0d5744d3ec1764a286af5e006636", + "reference": "89f0dea1cb0f0d5744d3ec1764a286af5e006636", "shasum": "" }, "require": { - "php": ">=8.2" + "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" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "5.0-dev" - } + "require-dev": { + "laravel/pint": "^1.13.7", + "phpstan/phpstan": "^1.10.52" }, + "type": "library", "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "PHPUnit\\Architecture\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "Ni Shi", + "email": "futik0ma011@gmail.com" + }, + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" } ], - "description": "Library that helps with managing the version number of Git-hosted PHP projects", - "homepage": "https://github.com/sebastianbergmann/version", + "description": "Methods for testing application architecture", + "keywords": [ + "architecture", + "phpunit", + "stucture", + "test", + "testing" + ], "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" + "issues": "https://github.com/ta-tikoma/phpunit-architecture-test/issues", + "source": "https://github.com/ta-tikoma/phpunit-architecture-test/tree/0.8.4" }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2024-07-03T05:13:08+00:00" + "time": "2024-01-05T14:10:56+00:00" }, { "name": "theseer/tokenizer", @@ -1696,6 +3794,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/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/TestCase.php b/tests/TestCase.php new file mode 100644 index 0000000..cfb05b6 --- /dev/null +++ b/tests/TestCase.php @@ -0,0 +1,10 @@ + Date: Sat, 28 Dec 2024 15:46:24 +0100 Subject: [PATCH 02/43] enh: phpunit -> pest (CI / README.md) --- .github/workflows/{phpunit.yml => pest.yml} | 74 ++++++++++----------- README.md | 2 +- 2 files changed, 38 insertions(+), 38 deletions(-) rename .github/workflows/{phpunit.yml => pest.yml} (85%) diff --git a/.github/workflows/phpunit.yml b/.github/workflows/pest.yml similarity index 85% rename from .github/workflows/phpunit.yml rename to .github/workflows/pest.yml index 1ee890c..8268574 100644 --- a/.github/workflows/phpunit.yml +++ b/.github/workflows/pest.yml @@ -1,37 +1,37 @@ -name: Run PHPUnit Tests - -on: - push: - paths-ignore: - - 'README.md' - - 'LICENSE' - - 'CHANGELOG.md' - - 'CONTRIBUTING.md' - - 'doc/**' - -jobs: - phpunit: - runs-on: ubuntu-latest - - strategy: - matrix: - php-version: [ 8.2, 8.3, 8.4 ] - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set up PHP - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php-version }} - coverage: xdebug - - - name: Install dependencies - run: composer install --no-progress --no-suggest --prefer-dist - - - name: Configure Xdebug - run: echo "xdebug.mode=coverage" >> $GITHUB_ENV - - - name: Run PHPUnit - run: vendor/bin/phpunit \ No newline at end of file +name: Run Pest Tests + +on: + push: + paths-ignore: + - 'README.md' + - 'LICENSE' + - 'CHANGELOG.md' + - 'CONTRIBUTING.md' + - 'doc/**' + +jobs: + phpunit: + runs-on: ubuntu-latest + + strategy: + matrix: + php-version: [ 8.2, 8.3, 8.4 ] + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-version }} + coverage: xdebug + + - name: Install dependencies + run: composer install --no-progress --no-suggest --prefer-dist + + - name: Configure Xdebug + run: echo "xdebug.mode=coverage" >> $GITHUB_ENV + + - name: Run Pest + run: vendor/bin/pest \ No newline at end of file diff --git a/README.md b/README.md index aed8a32..738e4c0 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ![GitHub Release](https://img.shields.io/github/v/release/smartondev/httpcache?include_prereleases) [![GitHub License](https://img.shields.io/github/license/smartondev/httpcache)](LICENSE) -![PHPUnit - GitHub Actions](https://img.shields.io/github/actions/workflow/status/smartondev/httpcache/phpunit.yml?label=tests) +![Pest - GitHub Actions](https://img.shields.io/github/actions/workflow/status/smartondev/httpcache/phpunit.yml?label=tests) ![PHPStan level 10 - GitHub Actions](https://img.shields.io/github/actions/workflow/status/smartondev/httpcache/phpstan.yml?label=PHPStan%20level%2010) [![Coverage Status](https://img.shields.io/coverallsCoverage/github/smartondev/httpcache?label=coveralls)](https://coveralls.io/github/smartondev/httpcache?branch=main) [![Codecov](https://img.shields.io/codecov/c/github/smartondev/httpcache?label=codecov)](https://app.codecov.io/gh/smartondev/httpcache) From 2dfd2bff0bbeeb03843c7cb499555e793be815ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Somogyi?= Date: Sat, 28 Dec 2024 15:50:16 +0100 Subject: [PATCH 03/43] fix: CI job name (phpunit -> pest) --- .github/workflows/pest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pest.yml b/.github/workflows/pest.yml index 8268574..5486971 100644 --- a/.github/workflows/pest.yml +++ b/.github/workflows/pest.yml @@ -10,7 +10,7 @@ on: - 'doc/**' jobs: - phpunit: + pest: runs-on: ubuntu-latest strategy: From c8ce5ea32d200b4df54711feeb157da12dd7c480 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Somogyi?= Date: Mon, 30 Dec 2024 11:27:02 +0100 Subject: [PATCH 04/43] enh: add declare(strict_types=1) --- src/Builders/CacheHeaderBuilder.php | 1 + src/Builders/ETagHeaderBuilder.php | 1 + src/Contracts/HttpHeaderBuilderInterface.php | 1 + src/Exceptions/DateMalformedStringException.php | 1 + src/Helpers/HttpHeaderHelper.php | 1 + src/Helpers/TimeHelper.php | 1 + src/Matchers/ETagMatcher.php | 1 + src/Matchers/ETagMatcherResult.php | 1 + src/Matchers/MatcherHeaderAbstract.php | 1 + src/Matchers/ModifiedMatcher.php | 1 + src/Matchers/ModifiedMatcherResult.php | 1 + 11 files changed, 11 insertions(+) diff --git a/src/Builders/CacheHeaderBuilder.php b/src/Builders/CacheHeaderBuilder.php index 6a4f2a5..e07e29c 100644 --- a/src/Builders/CacheHeaderBuilder.php +++ b/src/Builders/CacheHeaderBuilder.php @@ -1,5 +1,6 @@ Date: Mon, 30 Dec 2024 11:27:21 +0100 Subject: [PATCH 05/43] remove: not used TestCase php --- tests/TestCase.php | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 tests/TestCase.php diff --git a/tests/TestCase.php b/tests/TestCase.php deleted file mode 100644 index cfb05b6..0000000 --- a/tests/TestCase.php +++ /dev/null @@ -1,10 +0,0 @@ - Date: Mon, 30 Dec 2024 11:28:08 +0100 Subject: [PATCH 06/43] comment: TimeHelper#durationToSeconds added php doc for months and years arguments --- src/Helpers/TimeHelper.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/Helpers/TimeHelper.php b/src/Helpers/TimeHelper.php index c985b4b..689fc6a 100644 --- a/src/Helpers/TimeHelper.php +++ b/src/Helpers/TimeHelper.php @@ -8,6 +8,21 @@ class TimeHelper { + /** + * @param int $seconds + * @param int $minutes + * @param int $hours + * @param int $days + * @param int $weeks + * @param int $months calculated as 30 days + * @param int $years calculated as 365 days + * @return int calculated seconds + * + * @example TimeHelper::durationToSeconds(37) // 37 seconds + * @example TimeHelper::durationToSeconds(hours: 1) // 1 hour + * @example TimeHelper::durationToSeconds(minutes: 30) // 30 minutes + * @example TimeHelper::durationToSeconds(seconds: 13, minutes: 2, hours: 1) // 1 hour, 2 minutes, 13 seconds + */ public static function durationToSeconds(int $seconds = 0, int $minutes = 0, int $hours = 0, From 009a84b6b43ae4de8ec1472a45004c54c43940f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Somogyi?= Date: Mon, 30 Dec 2024 11:28:48 +0100 Subject: [PATCH 07/43] enh: TimeHelper#toTimestamp more validation added --- src/Helpers/TimeHelper.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Helpers/TimeHelper.php b/src/Helpers/TimeHelper.php index 689fc6a..56ea46c 100644 --- a/src/Helpers/TimeHelper.php +++ b/src/Helpers/TimeHelper.php @@ -53,6 +53,12 @@ public static function toTimestamp(int|string|DateTime $input): int if ($input instanceof DateTime) { return $input->getTimestamp(); } + if (!is_string($input)) { + throw new \LogicException('Input must be int, string or DateTime object'); + } + if (trim($input) === '') { + throw new DateMalformedStringException('Empty string'); + } try { $input = new DateTime($input); } catch (\Exception $e) { From 6e081fd5fb72bece356d82299d4644ce0f80bd55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Somogyi?= Date: Mon, 30 Dec 2024 11:31:03 +0100 Subject: [PATCH 08/43] refactor: TimeHelperTest.php rewrite with php pest tests: TimeHelperTest added toTimestamp with exception --- tests/Helpers/TimeHelperTest.php | 65 +++++++++++++++++++------------- 1 file changed, 38 insertions(+), 27 deletions(-) diff --git a/tests/Helpers/TimeHelperTest.php b/tests/Helpers/TimeHelperTest.php index 6847593..11fff75 100644 --- a/tests/Helpers/TimeHelperTest.php +++ b/tests/Helpers/TimeHelperTest.php @@ -1,34 +1,45 @@ toBe(expected: 37, message: '37 seconds') + ->and(TimeHelper::durationToSeconds(hours: 1)) + ->toBe(expected: 3600, message: '1 hour') + ->and(TimeHelper::durationToSeconds(minutes: 30)) + ->toBe(expected: 1800, message: '30 minutes') + ->and(TimeHelper::durationToSeconds(seconds: 60)) + ->toBe(expected: 60, message: '60 seconds') + ->and(TimeHelper::durationToSeconds(days: 1)) + ->toBe(expected: 86400, message: '1 day') + ->and(TimeHelper::durationToSeconds(weeks: 1)) + ->toBe(expected: 604800, message: '1 week') + ->and(TimeHelper::durationToSeconds(months: 1)) + ->toBe(expected: 2592000, message: '1 month') + ->and(TimeHelper::durationToSeconds(years: 1)) + ->toBe(expected: 31536000, message: '1 year') + ->and(TimeHelper::durationToSeconds(seconds: 2, minutes: 2, hours: 1)) + ->toBe(expected: 3722, message: '1 hour, 2 minutes, 2 seconds'); +}); - public function testDurationToSeconds(): void - { - $this->assertSame(37, TimeHelper::durationToSeconds(37)); - $this->assertSame(3600, TimeHelper::durationToSeconds(hours: 1)); - $this->assertSame(1800, TimeHelper::durationToSeconds(minutes: 30)); - $this->assertSame(60, TimeHelper::durationToSeconds(seconds: 60)); - $this->assertSame(86400, TimeHelper::durationToSeconds(days: 1)); - $this->assertSame(604800, TimeHelper::durationToSeconds(weeks: 1)); - $this->assertSame(2592000, TimeHelper::durationToSeconds(months: 1)); - $this->assertSame(31536000, TimeHelper::durationToSeconds(years: 1)); - $this->assertSame(3722, TimeHelper::durationToSeconds(seconds: 2, minutes: 2, hours: 1)); - } +it('to timestamp', function (int|DateTime|string $input, int $expected) { + expect(TimeHelper::toTimestamp($input))->toBe($expected); +})->with([ + 'zero' => [0, 0], + '2021-10-12 08:00' => ['2021-10-12T08:00:00Z', 1634025600], + '2021-10-15 08:00' => ['2027-01-15T08:00:00Z', 1800000000], + '2038-01-19 03:14:07' => ['2038-01-19T03:14:07Z', 2147483647], + 'date time object' => [new DateTime('2021-10-12T08:00:00Z'), 1634025600] +]); - public function testToTimestamp(): void - { - $this->assertSame(0, TimeHelper::toTimestamp(0)); - $this->assertSame(1634025600, TimeHelper::toTimestamp('2021-10-12T08:00:00Z')); - $this->assertSame(1800000000, TimeHelper::toTimestamp('2027-01-15T08:00:00Z')); - $this->assertSame(2147483647, TimeHelper::toTimestamp('2038-01-19T03:14:07Z')); - $this->assertSame(1634025600, TimeHelper::toTimestamp(new \DateTime('2021-10-12T08:00:00Z'))); - } -} \ No newline at end of file +it('to timestamp malformed string', function (string $input) { + TimeHelper::toTimestamp($input); +})->throws(\SmartonDev\HttpCache\Exceptions\DateMalformedStringException::class) + ->with([ + 'malformed string' => ['malformed string'], + 'empty' => [''], + 'empty spaces' => [' '] + ]); From e63aa9c270eb9e5b72a0b22cb2a633b7a0321c78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Somogyi?= Date: Mon, 30 Dec 2024 11:34:38 +0100 Subject: [PATCH 09/43] fix: not necessary type check (PHPStan) --- src/Helpers/TimeHelper.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Helpers/TimeHelper.php b/src/Helpers/TimeHelper.php index 56ea46c..a41b917 100644 --- a/src/Helpers/TimeHelper.php +++ b/src/Helpers/TimeHelper.php @@ -53,9 +53,6 @@ public static function toTimestamp(int|string|DateTime $input): int if ($input instanceof DateTime) { return $input->getTimestamp(); } - if (!is_string($input)) { - throw new \LogicException('Input must be int, string or DateTime object'); - } if (trim($input) === '') { throw new DateMalformedStringException('Empty string'); } From 8571f764393959c06d901d38caf1da96b2c061c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Somogyi?= Date: Mon, 30 Dec 2024 11:40:39 +0100 Subject: [PATCH 10/43] composer: mockery/mockery added --- composer.json | 3 +- composer.lock | 136 +++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 137 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index f44dc57..4a2ec16 100644 --- a/composer.json +++ b/composer.json @@ -14,7 +14,8 @@ }, "require-dev": { "phpstan/phpstan": "^2.0", - "pestphp/pest": "^3.7" + "pestphp/pest": "^3.7", + "mockery/mockery": "^1.6" }, "autoload": { "psr-4": { diff --git a/composer.lock b/composer.lock index 5672b7e..4c232b3 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "dde026db5845d21e69f383fbbab78a71", + "content-hash": "cdc14d2976a702867aa426aa3fe3b585", "packages": [], "packages-dev": [ { @@ -277,6 +277,57 @@ ], "time": "2024-09-25T12:00:00+00:00" }, + { + "name": "hamcrest/hamcrest-php", + "version": "v2.0.1", + "source": { + "type": "git", + "url": "https://github.com/hamcrest/hamcrest-php.git", + "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/8c3d0a3f6af734494ad8f6fbbee0ba92422859f3", + "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3", + "shasum": "" + }, + "require": { + "php": "^5.3|^7.0|^8.0" + }, + "replace": { + "cordoval/hamcrest-php": "*", + "davedevelopment/hamcrest-php": "*", + "kodova/hamcrest-php": "*" + }, + "require-dev": { + "phpunit/php-file-iterator": "^1.4 || ^2.0", + "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.1-dev" + } + }, + "autoload": { + "classmap": [ + "hamcrest" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "This is the PHP port of Hamcrest Matchers", + "keywords": [ + "test" + ], + "support": { + "issues": "https://github.com/hamcrest/hamcrest-php/issues", + "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.0.1" + }, + "time": "2020-07-09T08:09:16+00:00" + }, { "name": "jean85/pretty-package-versions", "version": "2.1.0", @@ -336,6 +387,89 @@ }, "time": "2024-11-18T16:19:46+00:00" }, + { + "name": "mockery/mockery", + "version": "1.6.12", + "source": { + "type": "git", + "url": "https://github.com/mockery/mockery.git", + "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mockery/mockery/zipball/1f4efdd7d3beafe9807b08156dfcb176d18f1699", + "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699", + "shasum": "" + }, + "require": { + "hamcrest/hamcrest-php": "^2.0.1", + "lib-pcre": ">=7.0", + "php": ">=7.3" + }, + "conflict": { + "phpunit/phpunit": "<8.0" + }, + "require-dev": { + "phpunit/phpunit": "^8.5 || ^9.6.17", + "symplify/easy-coding-standard": "^12.1.14" + }, + "type": "library", + "autoload": { + "files": [ + "library/helpers.php", + "library/Mockery.php" + ], + "psr-4": { + "Mockery\\": "library/Mockery" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Pádraic Brady", + "email": "padraic.brady@gmail.com", + "homepage": "https://github.com/padraic", + "role": "Author" + }, + { + "name": "Dave Marshall", + "email": "dave.marshall@atstsolutions.co.uk", + "homepage": "https://davedevelopment.co.uk", + "role": "Developer" + }, + { + "name": "Nathanael Esayeas", + "email": "nathanael.esayeas@protonmail.com", + "homepage": "https://github.com/ghostwriter", + "role": "Lead Developer" + } + ], + "description": "Mockery is a simple yet flexible PHP mock object framework", + "homepage": "https://github.com/mockery/mockery", + "keywords": [ + "BDD", + "TDD", + "library", + "mock", + "mock objects", + "mockery", + "stub", + "test", + "test double", + "testing" + ], + "support": { + "docs": "https://docs.mockery.io/", + "issues": "https://github.com/mockery/mockery/issues", + "rss": "https://github.com/mockery/mockery/releases.atom", + "security": "https://github.com/mockery/mockery/security/advisories", + "source": "https://github.com/mockery/mockery" + }, + "time": "2024-05-16T03:13:13+00:00" + }, { "name": "myclabs/deep-copy", "version": "1.12.1", From bcbe26694a674602b2e009f1b0be43562bd4fbc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Somogyi?= Date: Mon, 30 Dec 2024 17:24:32 +0100 Subject: [PATCH 11/43] refactor: HttpHeaderHelperTest.php with pest --- tests/Helpers/HttpHeaderHelperTest.php | 143 ++++++++++--------------- 1 file changed, 54 insertions(+), 89 deletions(-) diff --git a/tests/Helpers/HttpHeaderHelperTest.php b/tests/Helpers/HttpHeaderHelperTest.php index 7ec67c9..4a22172 100644 --- a/tests/Helpers/HttpHeaderHelperTest.php +++ b/tests/Helpers/HttpHeaderHelperTest.php @@ -1,94 +1,59 @@ '"123456"'], 'ETag', '"123456"'], - [['ETag' => '"abcABC123456"'], 'ETag', '"abcABC123456"'], - [[], 'Any', null], - [['a' => 'b'], 'b', null], - [['lowercase' => 'CamelCase'], 'LOWERCASE', 'CamelCase'], - [['multiple-value' => ['first', 'second']], 'multiple-value', 'first'], - ]; - } - - #[DataProvider('dataProviderGetFirstHeaderValue')] - public function testGetFirstHeaderValue(array $headers, string $header, ?string $expectedValue): void - { - $this->assertSame($expectedValue, HttpHeaderHelper::getFirstHeaderValue($headers, $header)); - } - - public static function dataProviderToDateString(): array - { - return [ - [0, 'Thu, 01 Jan 1970 00:00:00 GMT'], - [1634025600, 'Tue, 12 Oct 2021 08:00:00 GMT'], - [1800000000, 'Fri, 15 Jan 2027 08:00:00 GMT'], - [2147483647, 'Tue, 19 Jan 2038 03:14:07 GMT'], - ]; - } - - #[DataProvider('dataProviderToDateString')] - public function testToDateString(int $timestamp, string $expectedDateString): void - { - $this->assertSame($expectedDateString, HttpHeaderHelper::toDateString($timestamp)); - } - - public static function dataProviderIsValidDateString(): array - { - return [ - ['', false], - ['apple', false], - ['Tue, 19 Jan 2038 03:14:07 GMT', true], - ['Mon, 19 Jan 2038 03:14:07 GMT', false], - ]; - } - - #[DataProvider('dataProviderIsValidDateString')] - public function testIsValidDateString(string $value, bool $expected): void - { - $this->assertSame($expected, HttpHeaderHelper::isValidDateString($value)); - } - - public static function dataProviderReplaceHeaders(): array - { - return [ - 'emptyInput replace' => [ - [], - ['content-type' => 'application/json'], - ['content-type' => 'application/json'], - ], - 'emptyInput, replace uppercase' => [ - [], - ['Content-Type' => 'application/json'], - ['content-type' => 'application/json'], - ], - 'uppercase input, replace lowercase' => [ - ['Content-Type' => 'text/plain'], - ['content-type' => 'application/json'], - ['content-type' => 'application/json'], - ], - 'uppercase input with more, replace lowercase' => [ - ['Content-Type' => 'text/plain', 'x-no-change' => '1234'], - ['content-type' => 'application/json'], - ['content-type' => 'application/json', 'x-no-change' => '1234'], - ], - ]; - } - - #[DataProvider('dataProviderReplaceHeaders')] - public function testReplaceHeaders(array $headers, array $replaceHeaders, array $expectedHeaders): void - { - $this->assertSame($expectedHeaders, HttpHeaderHelper::replaceHeaders($headers, $replaceHeaders)); - } - -} \ No newline at end of file +it('get first header value', function (array $headers, string $header, ?string $expected) { + expect(HttpHeaderHelper::getFirstHeaderValue($headers, $header))->toBe($expected); +})->with([ + [['ETag' => '"123456"'], 'ETag', '"123456"'], + [['ETag' => '"abcABC123456"'], 'ETag', '"abcABC123456"'], + [[], 'Any', null], + [['a' => 'b'], 'b', null], + [['lowercase' => 'CamelCase'], 'LOWERCASE', 'CamelCase'], + [['multiple-value' => ['first', 'second']], 'multiple-value', 'first'], +]); + +it('to date string', function (int $timestamp, string $expectedDateString) { + expect(HttpHeaderHelper::toDateString($timestamp))->toBe($expectedDateString); +})->with([ + [0, 'Thu, 01 Jan 1970 00:00:00 GMT'], + [1634025600, 'Tue, 12 Oct 2021 08:00:00 GMT'], + [1800000000, 'Fri, 15 Jan 2027 08:00:00 GMT'], + [2147483647, 'Tue, 19 Jan 2038 03:14:07 GMT'], +]); + +it('is valid date string', function (string $value, bool $expected) { + expect(HttpHeaderHelper::isValidDateString($value))->toBe($expected); +})->with([ + ['', false], + ['apple', false], + ['Tue, 19 Jan 2038 03:14:07 GMT', true], + ['Mon, 19 Jan 2038 03:14:07 GMT', false], +]); + +it('replace headers', function (array $headers, array $replaceHeaders, array $expected) { + expect(HttpHeaderHelper::replaceHeaders($headers, $replaceHeaders))->toBe($expected); +})->with([ + 'emptyInput replace' => [ + [], + ['content-type' => 'application/json'], + ['content-type' => 'application/json'], + ], + 'emptyInput, replace uppercase' => [ + [], + ['Content-Type' => 'application/json'], + ['content-type' => 'application/json'], + ], + 'uppercase input, replace lowercase' => [ + ['Content-Type' => 'text/plain'], + ['content-type' => 'application/json'], + ['content-type' => 'application/json'], + ], + 'uppercase input with more, replace lowercase' => [ + ['Content-Type' => 'text/plain', 'x-no-change' => '1234'], + ['content-type' => 'application/json'], + ['content-type' => 'application/json', 'x-no-change' => '1234'], + ], +]); From 9135dea6b36221c765cd75f28dd510716f631386 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Somogyi?= Date: Wed, 1 Jan 2025 10:09:03 +0100 Subject: [PATCH 12/43] comment: add phpdoc note to replaceHeaders --- src/Helpers/HttpHeaderHelper.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Helpers/HttpHeaderHelper.php b/src/Helpers/HttpHeaderHelper.php index 182c3cd..7032adc 100644 --- a/src/Helpers/HttpHeaderHelper.php +++ b/src/Helpers/HttpHeaderHelper.php @@ -46,6 +46,8 @@ public static function getFirstHeaderValue(array $headers, string $name): ?strin * @param array> $headers * @param array> $replaceHeaders * @return array> + * + * @note header names are converted to lowercase */ public static function replaceHeaders(array $headers, array $replaceHeaders): array { From 7052317ef11a416a29903dc5dbdc7cd22e35cf9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Somogyi?= Date: Wed, 1 Jan 2025 10:10:42 +0100 Subject: [PATCH 13/43] enh: MatcherHeaderAbstract.php header property changed to getHeaders enh: MatcherHeaderAbstract.php phpdoc notes added --- src/Matchers/ETagMatcher.php | 14 ++++++++------ src/Matchers/MatcherHeaderAbstract.php | 15 ++++++++++++++- src/Matchers/ModifiedMatcher.php | 14 ++++++++------ 3 files changed, 30 insertions(+), 13 deletions(-) diff --git a/src/Matchers/ETagMatcher.php b/src/Matchers/ETagMatcher.php index 2598f9c..ad79833 100644 --- a/src/Matchers/ETagMatcher.php +++ b/src/Matchers/ETagMatcher.php @@ -17,8 +17,9 @@ class ETagMatcher extends MatcherHeaderAbstract */ public function ifMatchHeader(string|array $ifMatch): static { - $this->headers = HttpHeaderHelper::replaceHeaders($this->headers, [self::IF_MATCH_HEADER => $ifMatch]); - return $this; + return $this->headers( + HttpHeaderHelper::replaceHeaders($this->getHeaders(), [self::IF_MATCH_HEADER => $ifMatch]) + ); } /** @@ -38,8 +39,9 @@ public function withIfMatchHeader(string|array $ifMatch): static */ public function ifNoneMatchHeaderValue(string|array $ifNoneMatch): static { - $this->headers = HttpHeaderHelper::replaceHeaders($this->headers, [self::IF_NONE_MATCH_HEADER => $ifNoneMatch]); - return $this; + return $this->headers( + HttpHeaderHelper::replaceHeaders($this->getHeaders(), [self::IF_NONE_MATCH_HEADER => $ifNoneMatch]) + ); } /** @@ -54,7 +56,7 @@ public function withIfNoneMatchHeaderValue(string|array $ifNoneMatch): static public function getIfNoneMatchHeader(): ?string { - return HttpHeaderHelper::getFirstHeaderValue($this->headers, self::IF_NONE_MATCH_HEADER); + return HttpHeaderHelper::getFirstHeaderValue($this->getHeaders(), self::IF_NONE_MATCH_HEADER); } public function hasIfNoneMatchHeader(): bool @@ -64,7 +66,7 @@ public function hasIfNoneMatchHeader(): bool public function getIfMatchHeader(): ?string { - return HttpHeaderHelper::getFirstHeaderValue($this->headers, self::IF_MATCH_HEADER); + return HttpHeaderHelper::getFirstHeaderValue($this->getHeaders(), self::IF_MATCH_HEADER); } public function hasIfMatchHeader(): bool diff --git a/src/Matchers/MatcherHeaderAbstract.php b/src/Matchers/MatcherHeaderAbstract.php index ad39e71..f1920b3 100644 --- a/src/Matchers/MatcherHeaderAbstract.php +++ b/src/Matchers/MatcherHeaderAbstract.php @@ -1,6 +1,7 @@ > */ - protected array $headers = []; + private array $headers = []; /** * @param array> $headers + * + * @note header names are converted to lowercase */ public function headers(array $headers): static { @@ -23,6 +26,8 @@ public function headers(array $headers): static /** * @param array> $headers + * + * @note header names are converted to lowercase */ public function withHeaders(array $headers): static { @@ -39,4 +44,12 @@ public function withoutHeaders(): static { return (clone $this)->resetHeaders(); } + + /** + * @return array> + */ + protected function getHeaders(): array + { + return $this->headers; + } } \ No newline at end of file diff --git a/src/Matchers/ModifiedMatcher.php b/src/Matchers/ModifiedMatcher.php index a4c3734..5715241 100644 --- a/src/Matchers/ModifiedMatcher.php +++ b/src/Matchers/ModifiedMatcher.php @@ -19,8 +19,9 @@ class ModifiedMatcher extends MatcherHeaderAbstract */ public function ifModifiedSinceHeader(string|array $ifModifiedSinceHeaderValue): static { - $this->headers = HttpHeaderHelper::replaceHeaders($this->headers, [self::IF_MODIFIED_SINCE_HEADER => $ifModifiedSinceHeaderValue]); - return $this; + return $this->headers( + HttpHeaderHelper::replaceHeaders($this->getHeaders(), [self::IF_MODIFIED_SINCE_HEADER => $ifModifiedSinceHeaderValue]) + ); } /** @@ -40,8 +41,9 @@ public function withIfModifiedSinceHeader(string|array $ifModifiedSinceHeaderVal */ public function ifUnmodifiedSinceHeader(string|array $ifUnmodifiedSinceHeaderValue): static { - $this->headers = HttpHeaderHelper::replaceHeaders($this->headers, [self::IF_UNMODIFIED_SINCE_HEADER => $ifUnmodifiedSinceHeaderValue]); - return $this; + return $this->headers( + HttpHeaderHelper::replaceHeaders($this->getHeaders(), [self::IF_UNMODIFIED_SINCE_HEADER => $ifUnmodifiedSinceHeaderValue]) + ); } /** @@ -56,7 +58,7 @@ public function withIfUnmodifiedSinceHeader(string|array $ifUnmodifiedSinceHeade public function getIfModifiedSinceHeader(): ?string { - return HttpHeaderHelper::getFirstHeaderValue($this->headers, self::IF_MODIFIED_SINCE_HEADER); + return HttpHeaderHelper::getFirstHeaderValue($this->getHeaders(), self::IF_MODIFIED_SINCE_HEADER); } public function hasIfModifiedSinceHeader(): bool @@ -98,7 +100,7 @@ public function getIfModifiedSinceHeaderAsTimestamp(): ?int public function getIfUnmodifiedSinceHeader(): ?string { - return HttpHeaderHelper::getFirstHeaderValue($this->headers, self::IF_UNMODIFIED_SINCE_HEADER); + return HttpHeaderHelper::getFirstHeaderValue($this->getHeaders(), self::IF_UNMODIFIED_SINCE_HEADER); } public function hasIfUnmodifiedSinceHeader(): bool From 254424e6a918b2a4f491c47c21fdc3458af8c6b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Somogyi?= Date: Wed, 1 Jan 2025 10:12:09 +0100 Subject: [PATCH 14/43] remove: MatcherHeaderAbstractMock.php not needed thanks to Mockery refactor: MatcherHeaderAbstractTest converted to php pest --- tests/Matchers/MatcherHeaderAbstractTest.php | 129 ++++++++++++------ .../Mocks/MatcherHeaderAbstractMock.php | 13 -- 2 files changed, 90 insertions(+), 52 deletions(-) delete mode 100644 tests/Matchers/Mocks/MatcherHeaderAbstractMock.php diff --git a/tests/Matchers/MatcherHeaderAbstractTest.php b/tests/Matchers/MatcherHeaderAbstractTest.php index 76a6a98..a477bb9 100644 --- a/tests/Matchers/MatcherHeaderAbstractTest.php +++ b/tests/Matchers/MatcherHeaderAbstractTest.php @@ -1,41 +1,92 @@ headers(['header' => 'value']); - $this->assertSame(['header' => 'value'], $matcher->getHeaders()); - } - - public function testWithHeaders(): void - { - $matcher = new MatcherHeaderAbstractMock(); - $matcher2 = $matcher->withHeaders(['header' => 'value']); - $this->assertSame([], $matcher->getHeaders()); - $this->assertSame(['header' => 'value'], $matcher2->getHeaders()); - } - - public function testResetHeaders(): void - { - $matcher = new MatcherHeaderAbstractMock(); - $matcher->headers(['header' => 'value']); - $matcher->resetHeaders(); - $this->assertSame([], $matcher->getHeaders()); - } - - public function testWithoutHeaders(): void - { - $matcher = new MatcherHeaderAbstractMock(); - $matcher->headers(['header' => 'value']); - $matcher2 = $matcher->withoutHeaders(); - $this->assertSame(['header' => 'value'], $matcher->getHeaders()); - $this->assertSame([], $matcher2->getHeaders()); - } -} \ No newline at end of file +declare(strict_types=1); + +use SmartonDev\HttpCache\Matchers\MatcherHeaderAbstract; + +it('test headers', function(array $input, array $expected) { + $headersMock = Mockery::mock(MatcherHeaderAbstract::class)->makePartial(); + $headersMock->headers($input); + + $headersMock->shouldAllowMockingProtectedMethods(); + $headersMock->shouldReceive('getHeaders')->passthru(); + expect($headersMock->getHeaders())->toBe($expected); +})->with([ + 'lowercase' => [ + ['etag' => '"123"'], + ['etag' => '"123"'], + ], + 'one' => [ + ['ETag' => '"123"'], + ['etag' => '"123"'], + ], + 'two' => [ + ['ETag' => '"123"', 'Cache-Control' => 'no-cache'], + ['etag' => '"123"', 'cache-control' => 'no-cache'], + ], + 'nested array' => [ + ['array' => ['123', '456']], + ['array' => ['123', '456']], + ], +]); + +it('reset headers', function(array $input) { + $headersMock = Mockery::mock(MatcherHeaderAbstract::class)->makePartial(); + $headersMock->headers($input); + $headersMock->resetHeaders(); + + $headersMock->shouldAllowMockingProtectedMethods(); + $headersMock->shouldReceive('getHeaders')->passthru(); + expect($headersMock->getHeaders())->toBeEmpty(); +})->with([ + 'empty' => [[]], + 'one' => [['ETag' => '"123"']], + 'two' => [['ETag' => '"123"', 'Cache-Control' => 'no-cache']], + 'nested array' => [['array' => ['123', '456']]], +]); + +it('with headers', function(array $input, array $add, array $expected) { + $headersMock = Mockery::mock(MatcherHeaderAbstract::class)->makePartial(); + $headersMock->headers($input); + $headersMock = $headersMock->withHeaders($add); + + $headersMock->shouldAllowMockingProtectedMethods(); + $headersMock->shouldReceive('getHeaders')->passthru(); + expect($headersMock->getHeaders())->toBe($expected); +})->with([ + 'empty' => [ + [], + ['ETag' => '"123"'], + ['etag' => '"123"'], + ], + 'one' => [ + ['ETag' => '"123"'], + ['Cache-Control' => 'no-cache'], + ['cache-control' => 'no-cache'], + ], + 'two' => [ + ['ETag' => '"123"', 'Cache-Control' => 'no-cache'], + ['Cache-Control' => 'no-store'], + ['cache-control' => 'no-store'], + ], + 'nested array' => [ + ['array' => ['123', '456']], + ['Foo' => ['Bar','Baz']], + ['foo' => ['Bar','Baz']], + ], +]); + +it('without headers', function(array $input) { + $headersMock = Mockery::mock(MatcherHeaderAbstract::class)->makePartial(); + $headersMock->headers($input); + $headersMock = $headersMock->withoutHeaders(); + + $headersMock->shouldAllowMockingProtectedMethods(); + $headersMock->shouldReceive('getHeaders')->passthru(); + expect($headersMock->getHeaders())->toBeEmpty(); +})->with([ + 'empty' => [[]], + 'one' => [['ETag' => '"123"']], + 'two' => [['ETag' => '"123"', 'Cache-Control' => 'no-cache']], + 'nested array' => [['array' => ['123', '456']]], +]); \ No newline at end of file diff --git a/tests/Matchers/Mocks/MatcherHeaderAbstractMock.php b/tests/Matchers/Mocks/MatcherHeaderAbstractMock.php deleted file mode 100644 index a7d0b97..0000000 --- a/tests/Matchers/Mocks/MatcherHeaderAbstractMock.php +++ /dev/null @@ -1,13 +0,0 @@ -headers; - } -} \ No newline at end of file From fab64a968a2647f39a03d8c6aa386324c245c504 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Somogyi?= Date: Wed, 1 Jan 2025 10:27:50 +0100 Subject: [PATCH 15/43] fix: MatcherHeaderAbstractTest#withHeaders initial headers changed --- tests/Matchers/MatcherHeaderAbstractTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Matchers/MatcherHeaderAbstractTest.php b/tests/Matchers/MatcherHeaderAbstractTest.php index a477bb9..4e297c9 100644 --- a/tests/Matchers/MatcherHeaderAbstractTest.php +++ b/tests/Matchers/MatcherHeaderAbstractTest.php @@ -47,7 +47,7 @@ it('with headers', function(array $input, array $add, array $expected) { $headersMock = Mockery::mock(MatcherHeaderAbstract::class)->makePartial(); - $headersMock->headers($input); + $headersMock->headers(['previous' => 'header']); $headersMock = $headersMock->withHeaders($add); $headersMock->shouldAllowMockingProtectedMethods(); From 6aaff6fde721afdd4fc1ff1d93b67e934856c47b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Somogyi?= Date: Thu, 2 Jan 2025 14:57:50 +0100 Subject: [PATCH 16/43] refactor: ETagMatcherTest.php with php pest --- tests/Matchers/ETagMatcherTest.php | 181 ++++++++++++----------------- 1 file changed, 75 insertions(+), 106 deletions(-) diff --git a/tests/Matchers/ETagMatcherTest.php b/tests/Matchers/ETagMatcherTest.php index 66b320d..5145469 100644 --- a/tests/Matchers/ETagMatcherTest.php +++ b/tests/Matchers/ETagMatcherTest.php @@ -1,112 +1,81 @@ headers(['If-Match' => '"123456"']); - $this->assertTrue( - $ETagCondition - ->matches('"123456"') - ->matchesIfMatchHeader() - ); - } +it('if match header', function (array $headers, ?string $etag, bool $expected) { + $matcher = (new ETagMatcher()) + ->headers($headers); + expect($matcher->matches($etag)->matchesIfMatchHeader())->toBe($expected) + ->and($matcher->matches($etag)->notMatchesIfMatchHeader())->toBe(!$expected); +})->with([ + 'match' => [ + ['If-Match' => '"123"'], + '"123"', + true, + ], + 'not match' => [ + ['If-Match' => '"123"'], + '"456"', + false, + ], + 'not match empty' => [ + [], + '"123"', + false, + ], + 'another header' => [ + ['x-foo' => '"123"'], + '"123"', + false, + ], + 'null' => [ + ['If-Match' => '"123"'], + null, + false, + ], + 'null empty headers' => [ + [], + null, + false, + ], +]); - public function testIsNoneMatch(): void - { - $ETagCondition = (new ETagMatcher()) - ->withHeaders(['If-None-Match' => '"123456"']); - $this->assertTrue( - $ETagCondition - ->matches('"123456"') - ->matchesIfNoneMatchHeader() - ); - } - - public function testIsMatchWithNullEtag(): void - { - $ETagCondition = (new ETagMatcher()) - ->withHeaders(['If-Match' => '"123456"']); - $matches = $ETagCondition->matches(null); - $this->assertFalse($matches->matchesIfMatchHeader()); - $this->assertTrue($matches->notMatchesIfMatchHeader()); - } - - public function testIsNoneMatchWithNullEtag(): void - { - $ETagCondition = (new ETagMatcher()) - ->withHeaders(['If-None-Match' => '"123456"']); - $matches = $ETagCondition->matches(null); - $this->assertFalse($matches->matchesIfNoneMatchHeader()); - $this->assertTrue($matches->notMatchesIfNoneMatchHeader()); - } - - public function testIsNoneMatchFail(): void - { - $ETagCondition = (new ETagMatcher()) - ->withHeaders(['If-None-Match' => '"123456"']); - $matches = $ETagCondition->matches('"1234567"'); - $this->assertFalse($matches->matchesIfNoneMatchHeader()); - $this->assertTrue($matches->notMatchesIfNoneMatchHeader()); - } - - public function testIsMatchFail(): void - { - $ETagCondition = (new ETagMatcher()) - ->withHeaders(['If-Match' => '"123456"']); - $matches = $ETagCondition->matches('"1234567"'); - $this->assertFalse($matches->matchesIfMatchHeader()); - $this->assertTrue($matches->notMatchesIfMatchHeader()); - } - - public function testMatchWithETagHeaderBuilder(): void - { - $ETagHeaderBuilder = (new ETagHeaderBuilder()) - ->withETag('123456') - ->withWeekEtag(); - $etag = $ETagHeaderBuilder->getETag(); - $ETagCondition = new ETagMatcher(); - $matchesIfMatch = $ETagCondition - ->withHeaders(['If-Match' => $etag]) - ->matches($etag); - $this->assertTrue($matchesIfMatch->matchesIfMatchHeader()); - $this->assertFalse($matchesIfMatch->notMatchesIfMatchHeader()); - - $matchesIfNoneMatch = $ETagCondition - ->withHeaders(['If-None-Match' => $etag]) - ->matches($etag); - $this->assertTrue($matchesIfNoneMatch->matchesIfNoneMatchHeader()); - $this->assertFalse($matchesIfNoneMatch->notMatchesIfNoneMatchHeader()); - } - - public function testIfMatchHeader(): void - { - $matcher = new ETagMatcher(); - $matcher2 = $matcher->withIfMatchHeader('"123"'); - $this->assertFalse($matcher->hasIfMatchHeader()); - $this->assertTrue($matcher2->hasIfMatchHeader()); - $this->assertSame('"123"', $matcher2->getIfMatchHeader()); - $matcher->ifMatchHeader('"456"'); - $this->assertTrue($matcher->hasIfMatchHeader()); - $this->assertSame('"456"', $matcher->getIfMatchHeader()); - } - - public function testIfNoneMatchHeader(): void - { - $matcher = new ETagMatcher(); - $matcher2 = $matcher->withIfNoneMatchHeaderValue('"123"'); - $this->assertFalse($matcher->hasIfNoneMatchHeader()); - $this->assertTrue($matcher2->hasIfNoneMatchHeader()); - $this->assertSame('"123"', $matcher2->getIfNoneMatchHeader()); - $matcher->ifNoneMatchHeaderValue('"456"'); - $this->assertTrue($matcher->hasIfNoneMatchHeader()); - $this->assertSame('"456"', $matcher->getIfNoneMatchHeader()); - } -} \ No newline at end of file +it('if none match header', function (array $headers, ?string $etag, bool $expected) { + $matcher = (new ETagMatcher()) + ->headers($headers); + expect($matcher->matches($etag)->matchesIfNoneMatchHeader())->toBe($expected) + ->and($matcher->matches($etag)->notMatchesIfNoneMatchHeader())->toBe(!$expected); +})->with([ + 'match' => [ + ['If-None-Match' => '"123"'], + '"123"', + true, + ], + 'not match' => [ + ['If-None-Match' => '"123"'], + '"456"', + false, + ], + 'not match empty' => [ + [], + '"123"', + false, + ], + 'another header' => [ + ['x-foo' => '"123"'], + '"123"', + false, + ], + 'null' => [ + ['If-None-Match' => '"123"'], + null, + false, + ], + 'null empty headers' => [ + [], + null, + false, + ], +]); From 93ec7ff9fd8018a52d043ceeca330fd74c5e4143 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Somogyi?= Date: Fri, 3 Jan 2025 20:21:17 +0100 Subject: [PATCH 17/43] enh: ModifiedMatcher with DateMalformedStringException --- src/Matchers/ModifiedMatcher.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Matchers/ModifiedMatcher.php b/src/Matchers/ModifiedMatcher.php index 5715241..2c400b7 100644 --- a/src/Matchers/ModifiedMatcher.php +++ b/src/Matchers/ModifiedMatcher.php @@ -89,7 +89,7 @@ public function getIfModifiedSinceHeaderAsTimestamp(): ?int return null; } if (!$this->isValidIfModifiedSinceHeader()) { - throw new \RuntimeException('Invalid If-Modified-Since header value'); + throw new DateMalformedStringException('Invalid If-Modified-Since header value'); } $time = $this->getIfModifiedSinceHeader(); if (null === $time) { @@ -117,7 +117,7 @@ public function getIfUnmodifiedSinceHeaderAsTimestamp(): ?int return null; } if (!$this->isValidIfUnmodifiedSinceHeader()) { - throw new \RuntimeException('Invalid If-Unmodified-Since header value'); + throw new DateMalformedStringException('Invalid If-Unmodified-Since header value'); } $time = $this->getIfUnmodifiedSinceHeader(); if (null === $time) { From cc1211ba4c5a1e9cb8fbeb1a3e9cc1ddd4392091 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Somogyi?= Date: Fri, 3 Jan 2025 20:21:40 +0100 Subject: [PATCH 18/43] refactor: ModifiedMatcherTest.php phpunit -> php pest --- tests/Matchers/ModifiedMatcherTest.php | 401 +++++++++++++++++-------- 1 file changed, 271 insertions(+), 130 deletions(-) diff --git a/tests/Matchers/ModifiedMatcherTest.php b/tests/Matchers/ModifiedMatcherTest.php index 95e8a0e..3eaa414 100644 --- a/tests/Matchers/ModifiedMatcherTest.php +++ b/tests/Matchers/ModifiedMatcherTest.php @@ -1,145 +1,286 @@ ''], true, false], - [['If-Unmodified-Since' => ''], false, true], - [['If-Modified-Since' => '', 'If-Unmodified-Since' => ''], true, true], - ]; - } - - #[DataProvider('dataProviderHeaderExists')] - public function testHeaderExists(array $headers, bool $expectedExistsIfModifiedSince, bool $expectedExistsIfUnmodifiedSince): void - { - $matcher = (new ModifiedMatcher())->headers($headers); - - $this->assertSame($expectedExistsIfModifiedSince, $matcher->hasIfModifiedSinceHeader()); - $this->assertSame($expectedExistsIfUnmodifiedSince, $matcher->hasIfUnmodifiedSinceHeader()); - } - - public static function dataProviderHeaderIsValid(): array - { - return [ - [[], false, false], - [['If-Modified-Since' => 'Tue, 15 Nov 1994 12:45:26 GMT'], true, false], - [['If-Modified-Since' => 'Tue, 15 Nov 1994 12:45:26 GMT', 'If-Unmodified-Since' => 'Tue, 15 Nov 1994 12:45:27 GMT'], true, true], - [['If-Modified-Since' => 'apple', 'If-Unmodified-Since' => 'Tue, 15 Nov 1994 12:45:27 GMT'], false, true], - [['If-Modified-Since' => 'apple', 'If-Unmodified-Since' => 'peach'], false, false], - ]; - } - - #[DataProvider('dataProviderHeaderIsValid')] - public function testHeaderIsValid(array $headers, bool $expectedIfModifiedSince, bool $expectedIfUnmodifiedSince): void - { - $matcher = (new ModifiedMatcher())->headers($headers); - - $this->assertSame($expectedIfModifiedSince, $matcher->isValidIfModifiedSinceHeader()); - $this->assertSame(!$expectedIfModifiedSince, $matcher->isInvalidIfModifiedSinceHeader()); - $this->assertSame($expectedIfUnmodifiedSince, $matcher->isValidIfUnmodifiedSinceHeader()); - $this->assertSame(!$expectedIfUnmodifiedSince, $matcher->isInvalidIfUnmodifiedSinceHeader()); - } - - public static function dataProviderHeaderAsTimestamp(): array - { - return [ - [[], null, null], - [['If-Modified-Since' => 'Tue, 15 Nov 1994 12:45:26 GMT'], 784903526, null], - [['If-Modified-Since' => 'Tue, 15 Nov 1994 12:45:26 GMT', 'If-Unmodified-Since' => 'Tue, 15 Nov 1994 12:45:27 GMT'], 784903526, 784903527], - ]; - } - - #[DataProvider('dataProviderHeaderAsTimestamp')] - public function testHeaderAsTimestamp(array $headers, - ?int $expectedIfModifiedSinceAsTimestamp, - ?int $expectedIfUnmodifiedSinceAsTimestamp): void - { - $matcher = (new ModifiedMatcher())->headers($headers); - $this->assertSame($expectedIfModifiedSinceAsTimestamp, $matcher->getIfModifiedSinceHeaderAsTimestamp()); - $this->assertSame($expectedIfUnmodifiedSinceAsTimestamp, $matcher->getIfUnmodifiedSinceHeaderAsTimestamp()); - } - - public function testGetModifiedSinceHeaderAsTimestampInvalidValue(): void - { - $matcher = (new ModifiedMatcher())->headers([ - 'If-Modified-Since' => 'apple', - ]); +it('has if modified since header', function (array $headers, bool $expected) { + $matcher = (new ModifiedMatcher()) + ->headers($headers); + expect($matcher->hasIfModifiedSinceHeader())->toBe($expected); +})->with([ + 'has' => [ + ['If-Modified-Since' => 'Sat, 29 Oct 1994 19:43:31 GMT'], + true, + ], + 'has empty' => [ + ['If-Modified-Since' => ''], + true, + ], + 'empty' => [ + [], + false, + ], + 'another header' => [ + ['x-foo' => 'bar'], + false, + ], +]); + +it('has if unmodified since header', function (array $headers, bool $expected) { + $matcher = (new ModifiedMatcher()) + ->headers($headers); + expect($matcher->hasIfUnmodifiedSinceHeader())->toBe($expected); +})->with([ + 'has' => [ + ['If-Unmodified-Since' => 'Sat, 29 Oct 1994 19:43:31 GMT'], + true, + ], + 'has empty' => [ + ['If-Unmodified-Since' => ''], + true, + ], + 'empty' => [ + [], + false, + ], + 'another header' => [ + ['x-foo' => 'bar'], + false, + ], +]); + +it('is valid/invalid if modified since header', function (array $headers, bool $expected) { + $matcher = (new ModifiedMatcher()) + ->headers($headers); + expect($matcher->isValidIfModifiedSinceHeader())->toBe($expected) + ->and($matcher->isInvalidIfModifiedSinceHeader())->toBe(!$expected); +})->with([ + 'valid' => [ + ['If-Modified-Since' => 'Sat, 29 Oct 1994 19:43:31 GMT'], + true, + ], + 'invalid date string' => [ + ['If-Modified-Since' => 'apple'], + false, + ], + 'invalid empty string' => [ + ['If-Modified-Since' => ''], + false, + ], + 'empty' => [ + [], + false, + ], + 'another header' => [ + ['x-foo' => 'bar'], + false, + ], +]); + +it('is valid/invalid if unmodified since header', function (array $headers, bool $expected) { + $matcher = (new ModifiedMatcher()) + ->headers($headers); + expect($matcher->isValidIfUnmodifiedSinceHeader())->toBe($expected) + ->and($matcher->isInvalidIfUnmodifiedSinceHeader())->toBe(!$expected); +})->with([ + 'valid' => [ + ['If-Unmodified-Since' => 'Sat, 29 Oct 1994 19:43:31 GMT'], + true, + ], + 'invalid date string' => [ + ['If-Unmodified-Since' => 'apple'], + false, + ], + 'invalid empty string' => [ + ['If-Unmodified-Since' => ''], + false, + ], + 'empty' => [ + [], + false, + ], + 'another header' => [ + ['x-foo' => 'bar'], + false, + ], +]); + +it('get if modified since header', function (array $headers, ?string $expected) { + $matcher = (new ModifiedMatcher()) + ->headers($headers); + expect($matcher->getIfModifiedSinceHeader())->toBe($expected); +})->with([ + 'has' => [ + ['If-Modified-Since' => 'Sat, 29 Oct 1994 19:43:31 GMT'], + 'Sat, 29 Oct 1994 19:43:31 GMT', + ], + 'has empty' => [ + ['If-Modified-Since' => ''], + '', + ], + 'empty' => [ + [], + null, + ], + 'another header' => [ + ['x-foo' => 'bar'], + null, + ], +]); + +it('get if unmodified since header', function (array $headers, ?string $expected) { + $matcher = (new ModifiedMatcher()) + ->headers($headers); + expect($matcher->getIfUnmodifiedSinceHeader())->toBe($expected); +})->with([ + 'has' => [ + ['If-Unmodified-Since' => 'Sat, 29 Oct 1994 19:43:31 GMT'], + 'Sat, 29 Oct 1994 19:43:31 GMT', + ], + 'has empty' => [ + ['If-Unmodified-Since' => ''], + '', + ], + 'empty' => [ + [], + null, + ], + 'another header' => [ + ['x-foo' => 'bar'], + null, + ], +]); + +it('get if modified since header as timestamp', function (array $headers, ?int $expected) { + $matcher = (new ModifiedMatcher()) + ->headers($headers); + expect($matcher->getIfModifiedSinceHeaderAsTimestamp())->toBe($expected); +})->with([ + 'has' => [ + ['If-Modified-Since' => 'Tue, 15 Nov 1994 12:45:26 GMT'], + 784903526, + ], + 'empty' => [ + [], + null, + ], + 'another header' => [ + ['x-foo' => 'bar'], + null, + ], +]); - $this->expectException(\RuntimeException::class); - $this->expectExceptionMessage('Invalid If-Modified-Since header value'); - $matcher->getIfModifiedSinceHeaderAsTimestamp(); - } +it('get if modified since header as timestamp invalid date string', function (array $headers) { + $matcher = (new ModifiedMatcher()) + ->headers($headers); - public function testGetUnmodifiedSinceHeaderAsTimestampInvalidValue(): void - { - $matcher = (new ModifiedMatcher())->headers([ - 'If-Unmodified-Since' => 'apple', + expect(fn() => $matcher->getIfModifiedSinceHeaderAsTimestamp()) + ->toThrow( + exception: DateMalformedStringException::class, + exceptionMessage: 'Invalid If-Modified-Since header value', + ); +})->with([ + 'any string' => [ + ['If-Modified-Since' => 'apple'], + ], + 'empty string' => [ + ['If-Modified-Since' => ''], + ], +]); + +it('get if unmodified since header as timestamp', function (array $headers, ?int $expected) { + $matcher = (new ModifiedMatcher()) + ->headers($headers); + expect($matcher->getIfUnmodifiedSinceHeaderAsTimestamp())->toBe($expected); +})->with([ + 'has' => [ + ['If-Unmodified-Since' => 'Tue, 15 Nov 1994 12:45:26 GMT'], + 784903526, + ], + 'empty' => [ + [], + null, + ], + 'another header' => [ + ['x-foo' => 'bar'], + null, + ], +]); + +it('get if unmodified since header as timestamp invalid date string', function (array $headers) { + $matcher = (new ModifiedMatcher()) + ->headers($headers); + + expect(fn() => $matcher->getIfUnmodifiedSinceHeaderAsTimestamp()) + ->toThrow( + exception: DateMalformedStringException::class, + exceptionMessage: 'Invalid If-Unmodified-Since header value', + ); +})->with([ + 'any string' => [ + ['If-Unmodified-Since' => 'apple'], + ], + 'empty string' => [ + ['If-Unmodified-Since' => ''], + ], +]); + +it('matches if modified since', function (string $value, string $before, string $after) { + $matcher = (new ModifiedMatcher()) + ->headers([ + 'If-Modified-Since' => $value, ]); + $dtBefore = new \DateTime($before); + $dtAfter = new \DateTime($after); + $dtEq = new \DateTime($value); + expect($matcher->matches($dtEq)->matchesModifiedAt())->toBeTrue() + ->and($matcher->matches($dtBefore)->matchesModifiedAt())->toBeFalse() + ->and($matcher->matches($dtAfter)->isModifiedSince())->toBeTrue(); +})->with([ + 'one' => [ + 'Tue, 15 Nov 1994 12:45:26 GMT', + 'Mon, 14 Nov 1994 12:45:26 GMT', + 'Wed, 16 Nov 1994 12:45:26 GMT', + ], +]); - $this->expectException(\RuntimeException::class); - $this->expectExceptionMessage('Invalid If-Unmodified-Since header value'); - $matcher->getIfUnmodifiedSinceHeaderAsTimestamp(); - } +it('matches if unmodified since', function (string $value, string $before, string $after) { + $matcher = (new ModifiedMatcher()) + ->headers([ + 'If-Unmodified-Since' => $value, + ]); + $dtBefore = new \DateTime($before); + $dtAfter = new \DateTime($after); + $dtEq = new \DateTime($value); + expect($matcher->matches($dtBefore)->isUnmodifiedSince())->toBeTrue() + ->and($matcher->matches($dtAfter)->isUnmodifiedSince())->toBeFalse() + ->and($matcher->matches($dtEq)->isModifiedSince())->toBeFalse(); +})->with([ + 'one' => [ + 'Tue, 15 Nov 1994 12:45:26 GMT', + 'Mon, 14 Nov 1994 12:45:26 GMT', + 'Wed, 16 Nov 1994 12:45:26 GMT', + ], +]); - public function testMatchesIfModifiedSince(): void - { - $matcher = (new ModifiedMatcher())->headers([ +it('if modified since header mutable/immutable', function () { + $matcher = (new ModifiedMatcher()) + ->headers([ 'If-Modified-Since' => 'Tue, 15 Nov 1994 12:45:26 GMT', ]); + $matcher2 = $matcher->withIfModifiedSinceHeader('Wed, 16 Nov 1994 12:45:26 GMT'); + expect($matcher->getIfModifiedSinceHeader())->toBe('Tue, 15 Nov 1994 12:45:26 GMT') + ->and($matcher2->getIfModifiedSinceHeader())->toBe('Wed, 16 Nov 1994 12:45:26 GMT'); +}); - $dtBefore = new \DateTime('Mon, 14 Nov 1994 12:45:26 GMT'); - $dtAfter = new \DateTime('Wed, 16 Nov 1994 12:45:26 GMT'); - $dtEq = new \DateTime('Tue, 15 Nov 1994 12:45:26 GMT'); - $this->assertTrue($matcher->matches($dtEq)->matchesModifiedAt()); - $this->assertFalse($matcher->matches($dtBefore)->matchesModifiedAt()); - $this->assertTrue($matcher->matches($dtAfter)->isModifiedSince()); - } - - public function testMatchesIfUnmodifiedSince(): void - { - $matcher = (new ModifiedMatcher())->headers([ +it('if unmodified since header mutable/immutable', function () { + $matcher = (new ModifiedMatcher()) + ->headers([ 'If-Unmodified-Since' => 'Tue, 15 Nov 1994 12:45:26 GMT', ]); - - $dtBefore = new \DateTime('Mon, 14 Nov 1994 12:45:26 GMT'); - $dtAfter = new \DateTime('Wed, 16 Nov 1994 12:45:26 GMT'); - $dtEq = new \DateTime('Tue, 15 Nov 1994 12:45:26 GMT'); - $this->assertTrue($matcher->matches($dtBefore)->isUnmodifiedSince()); - $this->assertFalse($matcher->matches($dtAfter)->isUnmodifiedSince()); - $this->assertFalse($matcher->matches($dtEq)->isModifiedSince()); - } - - public function testIfModifiedSinceHeader(): void - { - $matcher = new ModifiedMatcher(); - $matcher2 = $matcher->withIfModifiedSinceHeader('Tue, 15 Nov 1994 12:45:26 GMT'); - $this->assertFalse($matcher->hasIfModifiedSinceHeader()); - $this->assertTrue($matcher2->hasIfModifiedSinceHeader()); - $this->assertSame('Tue, 15 Nov 1994 12:45:26 GMT', $matcher2->getIfModifiedSinceHeader()); - $matcher->ifModifiedSinceHeader('Wed, 16 Nov 1994 12:45:26 GMT'); - $this->assertTrue($matcher->hasIfModifiedSinceHeader()); - $this->assertSame('Wed, 16 Nov 1994 12:45:26 GMT', $matcher->getIfModifiedSinceHeader()); - } - - public function testIfUnmodifiedSinceHeader(): void - { - $matcher = new ModifiedMatcher(); - $matcher2 = $matcher->withIfUnmodifiedSinceHeader('Tue, 15 Nov 1994 12:45:26 GMT'); - $this->assertFalse($matcher->hasIfUnmodifiedSinceHeader()); - $this->assertTrue($matcher2->hasIfUnmodifiedSinceHeader()); - $this->assertSame('Tue, 15 Nov 1994 12:45:26 GMT', $matcher2->getIfUnmodifiedSinceHeader()); - $matcher->ifUnmodifiedSinceHeader('Wed, 16 Nov 1994 12:45:26 GMT'); - $this->assertTrue($matcher->hasIfUnmodifiedSinceHeader()); - $this->assertSame('Wed, 16 Nov 1994 12:45:26 GMT', $matcher->getIfUnmodifiedSinceHeader()); - } -} \ No newline at end of file + $matcher2 = $matcher->withIfUnmodifiedSinceHeader('Wed, 16 Nov 1994 12:45:26 GMT'); + expect($matcher->getIfUnmodifiedSinceHeader())->toBe('Tue, 15 Nov 1994 12:45:26 GMT') + ->and($matcher2->getIfUnmodifiedSinceHeader())->toBe('Wed, 16 Nov 1994 12:45:26 GMT'); +}); From 12ecef250a28c8ddb8a1d1804884193f18b287cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Somogyi?= Date: Fri, 3 Jan 2025 20:29:02 +0100 Subject: [PATCH 19/43] refactor: coverage with PHP pest (CI) --- .github/workflows/coverals-and-codecov.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/coverals-and-codecov.yml b/.github/workflows/coverals-and-codecov.yml index a257e50..f8e470a 100644 --- a/.github/workflows/coverals-and-codecov.yml +++ b/.github/workflows/coverals-and-codecov.yml @@ -12,7 +12,7 @@ on: - 'doc/**' jobs: - phpunit: + coverage: runs-on: ubuntu-latest steps: @@ -31,8 +31,8 @@ jobs: - name: Configure Xdebug run: echo "xdebug.mode=coverage" >> $GITHUB_ENV - - name: Run PHPUnit - run: vendor/bin/phpunit --coverage-clover=coverage.xml + - name: Run PHP Pest tests + run: vendor/bin/pest --coverage-clover=coverage.xml - name: Send coverage to Coveralls uses: coverallsapp/github-action@v2 From 0418afd80b28f3eb69e2e29616ef7402d4462db2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Somogyi?= Date: Sat, 4 Jan 2025 06:50:53 +0100 Subject: [PATCH 20/43] enh: run coverage actions on all branch --- .github/workflows/coverals-and-codecov.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/coverals-and-codecov.yml b/.github/workflows/coverals-and-codecov.yml index f8e470a..dcd3675 100644 --- a/.github/workflows/coverals-and-codecov.yml +++ b/.github/workflows/coverals-and-codecov.yml @@ -2,8 +2,6 @@ name: Run coveralls and codecov on: push: - branches: - - main paths-ignore: - 'README.md' - 'LICENSE' From 991ae24d306622a502d6d0a4bfa27481350fe6a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Somogyi?= Date: Sat, 4 Jan 2025 07:29:29 +0100 Subject: [PATCH 21/43] refactor: ETagHeaderBuilderTest.php phpunit -> pest --- tests/Builders/ETagHeaderBuilderTest.php | 239 +++++++++++------------ 1 file changed, 113 insertions(+), 126 deletions(-) diff --git a/tests/Builders/ETagHeaderBuilderTest.php b/tests/Builders/ETagHeaderBuilderTest.php index faf4324..33ab873 100644 --- a/tests/Builders/ETagHeaderBuilderTest.php +++ b/tests/Builders/ETagHeaderBuilderTest.php @@ -1,131 +1,118 @@ '"123456"'], ['etag' => 'W/"123456"']], - ['abcABC123456', ['etag' => '"abcABC123456"'], ['etag' => 'W/"abcABC123456"']], - ]; - } - - #[DataProvider('dataProviderWithEtag')] - public function testWithETag(string $etag, array $expectedHeaders, array $expectedWeekHeaders): void - { - $builder = (new ETagHeaderBuilder()) - ->withETag($etag); - $this->assertSame( - $expectedWeekHeaders, - $builder - ->withWeekEtag() - ->toHeaders() - ); - $this->assertSame($expectedHeaders, $builder->toHeaders()); - } - - public static function dataProviderComputedEtag(): array - { - return [ - ['content123', 'md5', false, ['etag' => '"' . md5('content123') . '"']], - ['content456', 'md5', true, ['etag' => 'W/"' . md5('content456') . '"']], - ['content789', 'sha1', false, ['etag' => '"' . sha1('content789') . '"']], - ['contentABC', 'sha1', true, ['etag' => 'W/"' . sha1('contentABC') . '"']], - ['contentABCDE', 'strval', false, ['etag' => '"contentABCDE"']], - ['contentABCDEFG', 'strval', true, ['etag' => 'W/"contentABCDEFG"']], - [2, fn($d) => strval($d * 11), false, ['etag' => '"22"']], - [35, fn($d) => strval($d / 5), true, ['etag' => 'W/"7"']], - ]; - } - - #[DataProvider('dataProviderComputedEtag')] - public function testComputedEtag(mixed $data, callable $func, bool $weekEtag, array $expectedHeaders): void - { - $builder = (new ETagHeaderBuilder()) - ->computedETag($data, $func); - if ($weekEtag) { - $builder->weekETag(); - } - $this->assertSame($expectedHeaders, $builder->toHeaders()); - } - - #[DataProvider('dataProviderComputedEtag')] - public function testWithComputedEtag(mixed $data, callable $func, bool $weekEtag, array $expectedHeaders): void - { - $builder = (new ETagHeaderBuilder()) - ->withComputedETag($data, $func); - if ($weekEtag) { - $builder = $builder->withWeekETag(); - } - $this->assertSame($expectedHeaders, $builder->toHeaders()); - } - - public function testEmptyETag(): void - { - $builder = (new ETagHeaderBuilder()) - ->etag(''); - $this->assertNull($builder->getETag()); - - $builder = (new ETagHeaderBuilder()) - ->etag(' '); - $this->assertNull($builder->getETag()); - } - - public function testIsNotEmptyETag(): void - { - $builder = (new ETagHeaderBuilder()) - ->etag('123456'); - $this->assertTrue($builder->isNotEmpty()); - - $builder = (new ETagHeaderBuilder()) - ->etag(' '); - $this->assertFalse($builder->isNotEmpty()); - } - - public function testResetETag(): void - { - $builder = (new ETagHeaderBuilder()) - ->etag('123456'); - $this->assertTrue($builder->isNotEmpty()); - $this->assertFalse($builder->withoutETag()->isNotEmpty()); - $this->assertTrue($builder->isNotEmpty()); - $builder->resetETag(); - $this->assertFalse($builder->isNotEmpty()); - } - - public function testResetWeekETag(): void - { - $builder = (new ETagHeaderBuilder()) - ->etag('123456') - ->weekETag(); - $this->assertSame('W/"123456"', $builder->getETag()); - $this->assertSame('"123456"', $builder->withoutWeekETag()->getETag()); - $this->assertSame('W/"123456"', $builder->getETag()); - $builder->resetWeekETag(); - $this->assertSame('"123456"', $builder->getETag()); - } - - public function testEmpty(): void - { - $builder = new ETagHeaderBuilder(); - $this->assertSame([], $builder->toHeaders()); - } - - public function testToString(): void - { - $builder = (new ETagHeaderBuilder()) - ->etag('123456'); - $this->assertSame('"123456"', (string)$builder); - $this->assertSame($builder->getETag(), (string)$builder); - - $builder->weekETag(); - $this->assertSame('W/"123456"', (string)$builder); - $this->assertSame($builder->getETag(), (string)$builder); - } -} \ No newline at end of file +it('etag', function () { + $builder = (new ETagHeaderBuilder()) + ->etag('123456'); + expect($builder->toHeaders())->toBe(['etag' => '"123456"']); +}); + +it('etag null/string', function (?string $etag) { + $builder = (new ETagHeaderBuilder()) + ->etag($etag); + expect($builder->toHeaders())->toBeEmpty(); +})->with([ + 'null' => null, + 'empty' => '', + 'blank' => ' ', +]); + +it('with etag', function () { + $builder = (new ETagHeaderBuilder()) + ->etag('123456'); + $builder2 = $builder->withETag('654321'); + expect($builder->toHeaders())->toBe(['etag' => '"123456"']) + ->and($builder2->toHeaders())->toBe(['etag' => '"654321"']); +}); + +it('computed etag', function (mixed $data, callable $compute, array $expectedHeaders) { + $builder = (new ETagHeaderBuilder()) + ->computedETag($data, $compute); + expect($builder->toHeaders())->toBe($expectedHeaders); +})->with([ + 'md5' => [ + 'content123', + 'md5', + ['etag' => '"' . md5('content123') . '"'], + ], + 'sha1' => [ + 'content789', + 'sha1', + ['etag' => '"' . sha1('content789') . '"'], + ], + 'int multiple' => [ + 2, + fn($d) => strval($d * 11), + ['etag' => '"22"'], + ], +]); + +it('week etag', function () { + $builder = (new ETagHeaderBuilder()) + ->etag('123456') + ->weekETag(); + expect($builder->toHeaders())->toBe(['etag' => 'W/"123456"']); +}); + +it('is empty', function () { + $builder = (new ETagHeaderBuilder()) + ->etag('123456'); + expect($builder->isEmpty())->toBeFalse(); +}); + +it('is not empty', function () { + $builder = (new ETagHeaderBuilder()) + ->etag(null); + expect($builder->isEmpty())->toBeTrue(); +}); + +it('reset etag', function () { + $builder = (new ETagHeaderBuilder()) + ->etag('123456'); + expect($builder->toHeaders())->not()->toBeEmpty(); + $builder->resetETag(); + expect($builder->toHeaders())->toBeEmpty(); +}); + +it('without etag', function () { + $builder = (new ETagHeaderBuilder()) + ->etag('123456'); + expect($builder->withoutETag()->toHeaders())->toBeEmpty() + ->and($builder->toHeaders())->not()->toBeEmpty(); +}); + +it('weak etag', function () { + $builder = (new ETagHeaderBuilder()) + ->etag('123456') + ->weekETag(); + expect($builder->toHeaders())->toBe(['etag' => 'W/"123456"']); +}); + +it('with weak etag', function () { + $builder = (new ETagHeaderBuilder()) + ->etag('123456'); + expect($builder->withWeekETag()->toHeaders())->toBe(['etag' => 'W/"123456"']) + ->and($builder->toHeaders())->toBe(['etag' => '"123456"']); +}); + +it('initial empty', function () { + $builder = new ETagHeaderBuilder(); + expect($builder->toHeaders())->toBeEmpty(); +}); + +it('to string', function () { + $builder = (new ETagHeaderBuilder()) + ->etag('123456'); + expect((string)$builder)->toBe('"123456"') + ->and((string)$builder->withWeekETag())->toBe('W/"123456"'); +}); + +it('get etag', function () { + $builder = (new ETagHeaderBuilder()) + ->etag('123456'); + expect($builder->getETag())->toBe('"123456"') + ->and($builder->withWeekETag()->getETag())->toBe('W/"123456"'); +}); From 75926626d73626402e78733b4ee12c9e3c7c7efc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Somogyi?= Date: Sat, 4 Jan 2025 08:04:07 +0100 Subject: [PATCH 22/43] fix: ETagHeaderBuilder#etag() set and null set not working --- CHANGELOG.md | 1 + src/Builders/ETagHeaderBuilder.php | 11 ++++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8348d96..6cf9f4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## dev - enh: custom DateMalFormedStringException added +- fix: ETagHeaderBuilder#etag() etag set and already set with null -> null etag not stored ## 0.5.0 diff --git a/src/Builders/ETagHeaderBuilder.php b/src/Builders/ETagHeaderBuilder.php index d5bfcb8..b630705 100644 --- a/src/Builders/ETagHeaderBuilder.php +++ b/src/Builders/ETagHeaderBuilder.php @@ -16,17 +16,18 @@ class ETagHeaderBuilder implements HttpHeaderBuilderInterface /** * Set ETag header * - * If $etag is empty, it will be set to null + * @note If $etag is empty, it will be set to null + * @note If $etag is null, weekETag will be set to false and $isWeek will be ignored */ public function etag(null|string $etag, bool $isWeek = false): static { - if(null === $etag) { - return $this->weekETag(false); - } - if (trim($etag) === '') { + if (is_string($etag) && trim($etag) === '') { $etag = null; } $this->etag = $etag; + if(null === $etag) { + return $this->weekETag(false); + } return $this->weekETag($isWeek); } From ec062facfa8bc3ee462a91cfcabac6f600fc1715 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Somogyi?= Date: Sat, 4 Jan 2025 08:10:12 +0100 Subject: [PATCH 23/43] enh: improve ETagHeaderBuilder coverage --- tests/Builders/ETagHeaderBuilderTest.php | 65 +++++++++++++++++++++++- 1 file changed, 63 insertions(+), 2 deletions(-) diff --git a/tests/Builders/ETagHeaderBuilderTest.php b/tests/Builders/ETagHeaderBuilderTest.php index 33ab873..2fe3262 100644 --- a/tests/Builders/ETagHeaderBuilderTest.php +++ b/tests/Builders/ETagHeaderBuilderTest.php @@ -50,6 +50,44 @@ ], ]); +it('with computed etag', function (mixed $data, callable $compute, array $expectedHeaders) { + $builder = (new ETagHeaderBuilder()) + ->etag('123456'); + $builder2 = $builder->withComputedETag($data, $compute); + expect($builder->toHeaders())->toBe(['etag' => '"123456"']) + ->and($builder2->toHeaders())->toBe($expectedHeaders); +})->with([ + 'md5' => [ + 'content123', + 'md5', + ['etag' => '"' . md5('content123') . '"'], + ], + 'sha1' => [ + 'content789', + 'sha1', + ['etag' => '"' . sha1('content789') . '"'], + ], + 'int multiple' => [ + 2, + fn($d) => strval($d * 11), + ['etag' => '"22"'], + ], +]); + +it('computed etag with invalid return value', function (mixed $return) { + $builder = (new ETagHeaderBuilder()); + expect(fn() => $builder->computedETag('content123', fn() => $return))->toThrow( + exception: InvalidArgumentException::class, + message: 'ETag must be a string or null', + ); +})->with([ + 'array' => [[]], + 'object' => [new stdClass()], + 'int' => [123], + 'float' => [123.456], + 'bool' => [true], +]); + it('week etag', function () { $builder = (new ETagHeaderBuilder()) ->etag('123456') @@ -60,13 +98,15 @@ it('is empty', function () { $builder = (new ETagHeaderBuilder()) ->etag('123456'); - expect($builder->isEmpty())->toBeFalse(); + expect($builder->isEmpty())->toBeFalse() + ->and($builder->withEtag(null)->isEmpty())->toBeTrue(); }); it('is not empty', function () { $builder = (new ETagHeaderBuilder()) ->etag(null); - expect($builder->isEmpty())->toBeTrue(); + expect($builder->isNotEmpty())->toBeFalse() + ->and($builder->withEtag('123456')->isNotEmpty())->toBeTrue(); }); it('reset etag', function () { @@ -98,6 +138,22 @@ ->and($builder->toHeaders())->toBe(['etag' => '"123456"']); }); +it('without week etag', function () { + $builder = (new ETagHeaderBuilder()) + ->etag('123456') + ->weekETag(); + expect($builder->withoutWeekETag()->toHeaders())->toBe(['etag' => '"123456"']); +}); + +it('reset week etag', function () { + $builder = (new ETagHeaderBuilder()) + ->etag('123456') + ->weekETag(); + expect($builder->toHeaders())->toBe(['etag' => 'W/"123456"']); + $builder->resetWeekETag(); + expect($builder->toHeaders())->toBe(['etag' => '"123456"']); +}); + it('initial empty', function () { $builder = new ETagHeaderBuilder(); expect($builder->toHeaders())->toBeEmpty(); @@ -116,3 +172,8 @@ expect($builder->getETag())->toBe('"123456"') ->and($builder->withWeekETag()->getETag())->toBe('W/"123456"'); }); + +it('get etag if not set', function () { + $builder = new ETagHeaderBuilder(); + expect($builder->getETag())->toBeNull(); +}); From 77c0009f7db90095f4f68de43099f0931ba5e8df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Somogyi?= Date: Sat, 4 Jan 2025 09:27:11 +0100 Subject: [PATCH 24/43] enh: HttpHeaderHelper tests --- tests/Helpers/HttpHeaderHelperTest.php | 30 ++++++++++++++------------ 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/tests/Helpers/HttpHeaderHelperTest.php b/tests/Helpers/HttpHeaderHelperTest.php index 4a22172..d57f766 100644 --- a/tests/Helpers/HttpHeaderHelperTest.php +++ b/tests/Helpers/HttpHeaderHelperTest.php @@ -7,30 +7,32 @@ it('get first header value', function (array $headers, string $header, ?string $expected) { expect(HttpHeaderHelper::getFirstHeaderValue($headers, $header))->toBe($expected); })->with([ - [['ETag' => '"123456"'], 'ETag', '"123456"'], - [['ETag' => '"abcABC123456"'], 'ETag', '"abcABC123456"'], - [[], 'Any', null], - [['a' => 'b'], 'b', null], - [['lowercase' => 'CamelCase'], 'LOWERCASE', 'CamelCase'], - [['multiple-value' => ['first', 'second']], 'multiple-value', 'first'], + 'str 1' => [['ETag' => '"123456"'], 'ETag', '"123456"'], + 'str 2' => [['ETag' => '"abcABC123456"'], 'ETag', '"abcABC123456"'], + 'empty' => [[], 'Any', null], + 'not exists' => [['a' => 'b'], 'b', null], + 'lower-LOWER' => [['lowercase' => 'CamelCase'], 'LOWERCASE', 'CamelCase'], + 'UPPER-upper' => [['UPPERCASE' => 'camelCase'], 'uppercase', 'camelCase'], + 'array' => [['multiple-value' => ['first', 'second']], 'multiple-value', 'first'], + 'array empty' => [['multiple-value-empty' => []], 'multiple-value-empty', null], ]); it('to date string', function (int $timestamp, string $expectedDateString) { expect(HttpHeaderHelper::toDateString($timestamp))->toBe($expectedDateString); })->with([ - [0, 'Thu, 01 Jan 1970 00:00:00 GMT'], - [1634025600, 'Tue, 12 Oct 2021 08:00:00 GMT'], - [1800000000, 'Fri, 15 Jan 2027 08:00:00 GMT'], - [2147483647, 'Tue, 19 Jan 2038 03:14:07 GMT'], + 'timestamp zero' => [0, 'Thu, 01 Jan 1970 00:00:00 GMT'], + 'timestamp 1' => [1634025600, 'Tue, 12 Oct 2021 08:00:00 GMT'], + 'timestamp 2' => [1800000000, 'Fri, 15 Jan 2027 08:00:00 GMT'], + 'timestamp 3' => [2147483647, 'Tue, 19 Jan 2038 03:14:07 GMT'], ]); it('is valid date string', function (string $value, bool $expected) { expect(HttpHeaderHelper::isValidDateString($value))->toBe($expected); })->with([ - ['', false], - ['apple', false], - ['Tue, 19 Jan 2038 03:14:07 GMT', true], - ['Mon, 19 Jan 2038 03:14:07 GMT', false], + 'empty string' => ['', false], + 'not date string' => ['apple', false], + 'valid date string' => ['Tue, 19 Jan 2038 03:14:07 GMT', true], + 'valid date string with invalid weekday' => ['Mon, 19 Jan 2038 03:14:07 GMT', false], ]); it('replace headers', function (array $headers, array $replaceHeaders, array $expected) { From c9b32bd643189a0edfd68fbb43cc793d640fe360 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Somogyi?= Date: Sat, 4 Jan 2025 09:31:44 +0100 Subject: [PATCH 25/43] enh: ETagHeaderBuilder ignore coverage line --- src/Builders/ETagHeaderBuilder.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Builders/ETagHeaderBuilder.php b/src/Builders/ETagHeaderBuilder.php index b630705..bd06a0a 100644 --- a/src/Builders/ETagHeaderBuilder.php +++ b/src/Builders/ETagHeaderBuilder.php @@ -121,10 +121,10 @@ public function toHeaders(): array if ($this->isEmpty()) { return []; } - $etag = $this->getETag(); - if(null === $etag) { - throw new \LogicException('ETag is empty'); - } + $etag = $this->getETag() + // @codeCoverageIgnoreStart + ?? throw new \LogicException('ETag is empty'); + // @codeCoverageIgnoreEnd return [ self::ETAG_HEADER => $etag, ]; From 0dbd9f0f5680d95dc19973a349640b5b942cd6c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Somogyi?= Date: Sat, 4 Jan 2025 09:34:43 +0100 Subject: [PATCH 26/43] enh: ModifiedMatcher ignore coverage line --- src/Matchers/ModifiedMatcher.php | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/Matchers/ModifiedMatcher.php b/src/Matchers/ModifiedMatcher.php index 2c400b7..7d53cc9 100644 --- a/src/Matchers/ModifiedMatcher.php +++ b/src/Matchers/ModifiedMatcher.php @@ -1,6 +1,7 @@ isValidIfModifiedSinceHeader()) { throw new DateMalformedStringException('Invalid If-Modified-Since header value'); } - $time = $this->getIfModifiedSinceHeader(); - if (null === $time) { - throw new \LogicException('If-Modified-Since header is empty'); - } + $time = $this->getIfModifiedSinceHeader() + // @codeCoverageIgnoreStart + ?? throw new \LogicException('If-Modified-Since header is empty'); + // @codeCoverageIgnoreEnd return TimeHelper::toTimestamp($time); } @@ -119,10 +120,10 @@ public function getIfUnmodifiedSinceHeaderAsTimestamp(): ?int if (!$this->isValidIfUnmodifiedSinceHeader()) { throw new DateMalformedStringException('Invalid If-Unmodified-Since header value'); } - $time = $this->getIfUnmodifiedSinceHeader(); - if (null === $time) { - throw new \LogicException('If-Unmodified-Since header is empty'); - } + $time = $this->getIfUnmodifiedSinceHeader() + // @codeCoverageIgnoreStart + ?? throw new \LogicException('If-Unmodified-Since header is empty'); + // @codeCoverageIgnoreEnd return TimeHelper::toTimestamp($time); } From d22b60f0f2245c0913ed7f76939bb5a3d347a60a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Somogyi?= Date: Sat, 4 Jan 2025 12:22:45 +0100 Subject: [PATCH 27/43] enh: CacheHeaderBuilder#etag() with more argument check --- src/Builders/CacheHeaderBuilder.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Builders/CacheHeaderBuilder.php b/src/Builders/CacheHeaderBuilder.php index e07e29c..5438127 100644 --- a/src/Builders/CacheHeaderBuilder.php +++ b/src/Builders/CacheHeaderBuilder.php @@ -1,6 +1,7 @@ resetIfNoCache(); if ($etag instanceof ETagHeaderBuilder) { - $etag = $etag->getETag(); + $etag = (string) $etag; } if (is_string($etag) && trim($etag) === '') { - $etag = null; + return $this->resetETag(); + } + if(1 !== preg_match('!^(?:W/)?".+"$!', $etag)) { + throw new \InvalidArgumentException('ETag must be a quoted string with optional weak indicator'); } $this->etag = $etag; return $this; @@ -951,7 +955,7 @@ public function toHeaders(): array } if ($this->hasETag()) { - if(null === $this->etag) { + if (null === $this->etag) { throw new \LogicException('ETag is empty'); } $headers[ETagHeaderBuilder::ETAG_HEADER] = $this->etag; From ea67e4116671df1dce8ceccb5191218b9757eb66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Somogyi?= Date: Sat, 4 Jan 2025 12:23:33 +0100 Subject: [PATCH 28/43] enh: CacheHeaderBuilder added pest test --- tests/Builders/CacheHeaderBuilder/AgeTest.php | 33 +++++ .../Builders/CacheHeaderBuilder/ETagTest.php | 71 +++++++++ .../CacheHeaderBuilder/ImmutableTest.php | 33 +++++ .../CacheHeaderBuilder/MaxAgeTest.php | 135 ++++++++++++++++++ .../CacheHeaderBuilder/MustRevalidateTest.php | 33 +++++ .../CacheHeaderBuilder/MustUnderstandTest.php | 33 +++++ .../CacheHeaderBuilder/NoCacheTest.php | 33 +++++ .../CacheHeaderBuilder/NoStoreTest.php | 33 +++++ .../CacheHeaderBuilder/NoTransformTest.php | 33 +++++ .../CacheHeaderBuilder/PrivateTest.php | 33 +++++ .../ProxyRevalidateTest.php | 33 +++++ .../CacheHeaderBuilder/PublicTest.php | 33 +++++ 12 files changed, 536 insertions(+) create mode 100644 tests/Builders/CacheHeaderBuilder/AgeTest.php create mode 100644 tests/Builders/CacheHeaderBuilder/ETagTest.php create mode 100644 tests/Builders/CacheHeaderBuilder/ImmutableTest.php create mode 100644 tests/Builders/CacheHeaderBuilder/MaxAgeTest.php create mode 100644 tests/Builders/CacheHeaderBuilder/MustRevalidateTest.php create mode 100644 tests/Builders/CacheHeaderBuilder/MustUnderstandTest.php create mode 100644 tests/Builders/CacheHeaderBuilder/NoCacheTest.php create mode 100644 tests/Builders/CacheHeaderBuilder/NoStoreTest.php create mode 100644 tests/Builders/CacheHeaderBuilder/NoTransformTest.php create mode 100644 tests/Builders/CacheHeaderBuilder/PrivateTest.php create mode 100644 tests/Builders/CacheHeaderBuilder/ProxyRevalidateTest.php create mode 100644 tests/Builders/CacheHeaderBuilder/PublicTest.php diff --git a/tests/Builders/CacheHeaderBuilder/AgeTest.php b/tests/Builders/CacheHeaderBuilder/AgeTest.php new file mode 100644 index 0000000..4733647 --- /dev/null +++ b/tests/Builders/CacheHeaderBuilder/AgeTest.php @@ -0,0 +1,33 @@ +age(10)->toHeaders())->toBe(['age' => '10']) + ->and($builder->toHeaders())->toBe(['age' => '10']); +}); + +it('with age', function () { + $builder = new CacheHeaderBuilder(); + expect($builder->withAge(10)->toHeaders())->toBe(['age' => '10']) + ->and($builder->toHeaders())->toBeEmpty(); +}); + +it('reset age', function () { + $builder = (new CacheHeaderBuilder()) + ->age(10); + expect($builder->toHeaders())->toBe(['age' => '10']); + $builder->resetAge(); + expect($builder->toHeaders())->toBe([]); +}); + +it('without age', function () { + $builder = (new CacheHeaderBuilder()) + ->age(10); + expect($builder->toHeaders())->toBe(['age' => '10']) + ->and($builder->withoutAge()->toHeaders())->toBe([]) + ->and($builder->toHeaders())->toBe(['age' => '10']); +}); diff --git a/tests/Builders/CacheHeaderBuilder/ETagTest.php b/tests/Builders/CacheHeaderBuilder/ETagTest.php new file mode 100644 index 0000000..e8a207e --- /dev/null +++ b/tests/Builders/CacheHeaderBuilder/ETagTest.php @@ -0,0 +1,71 @@ +etag($etag)->toHeaders())->toBe($expectedHeaders) + ->and($builder->toHeaders())->toBe($expectedHeaders); +})->with([ + 'strong' => ['"123"', ['etag' => '"123"']], + 'weak' => ['W/"123"', ['etag' => 'W/"123"']], +]); + +it('with etag', function (string $etag, array $expectedHeaders) { + $builder = new CacheHeaderBuilder(); + expect($builder->withEtag($etag)->toHeaders())->toBe($expectedHeaders) + ->and($builder->toHeaders())->toBeEmpty(); +})->with([ + 'strong' => ['"123"', ['etag' => '"123"']], + 'weak' => ['W/"123"', ['etag' => 'W/"123"']], +]); + +it('reset etag', function () { + $builder = (new CacheHeaderBuilder()) + ->etag('"123"'); + expect($builder->toHeaders())->toBe(['etag' => '"123"']); + $builder->resetEtag(); + expect($builder->toHeaders())->toBe([]); +}); + +it('without etag', function () { + $builder = (new CacheHeaderBuilder()) + ->etag('"123"'); + expect($builder->toHeaders())->toBe(['etag' => '"123"']) + ->and($builder->withoutEtag()->toHeaders())->toBe([]) + ->and($builder->toHeaders())->toBe(['etag' => '"123"']); +}); + +it('invalid etag', function (string $etag) { + expect(fn() => (new CacheHeaderBuilder())->etag($etag)) + ->toThrow( + exception: InvalidArgumentException::class, + exceptionMessage: 'ETag must be a quoted string with optional weak indicator', + ); +})->with([ + 'numbers' => ['123'], + 'word' => ['apple'], + 'start with W/' => ['W/123'], + 'start with "W/' => ['"W/123'], + 'end with quote' => ['123"'], + 'start with quote' => ['"123'], +]); + +it('etag with builder', function () { + $etag = (new ETagHeaderBuilder()) + ->etag('123'); + $builder = new CacheHeaderBuilder(); + expect($builder->etag($etag)->toHeaders())->toBe(['etag' => '"123"']) + ->and($builder->etag($etag->weekETag())->toHeaders())->toBe(['etag' => 'W/"123"']); +}); + +it('with etag with builder', function () { + $etag = (new ETagHeaderBuilder()) + ->etag('123'); + $builder = new CacheHeaderBuilder(); + expect($builder->withEtag($etag)->toHeaders())->toBe(['etag' => '"123"']) + ->and($builder->withEtag($etag->weekETag())->toHeaders())->toBe(['etag' => 'W/"123"']); +}); diff --git a/tests/Builders/CacheHeaderBuilder/ImmutableTest.php b/tests/Builders/CacheHeaderBuilder/ImmutableTest.php new file mode 100644 index 0000000..e918bde --- /dev/null +++ b/tests/Builders/CacheHeaderBuilder/ImmutableTest.php @@ -0,0 +1,33 @@ +immutable()->toHeaders())->toBe(['cache-control' => 'immutable']) + ->and($builder->toHeaders())->toBe(['cache-control' => 'immutable']); +}); + +it('with immutable', function () { + $builder = new CacheHeaderBuilder(); + expect($builder->withImmutable()->toHeaders())->toBe(['cache-control' => 'immutable']) + ->and($builder->toHeaders())->toBeEmpty(); +}); + +it('reset immutable', function () { + $builder = (new CacheHeaderBuilder()) + ->immutable(); + expect($builder->toHeaders())->toBe(['cache-control' => 'immutable']); + $builder->resetImmutable(); + expect($builder->toHeaders())->toBe([]); +}); + +it('without immutable', function () { + $builder = (new CacheHeaderBuilder()) + ->immutable(); + expect($builder->toHeaders())->toBe(['cache-control' => 'immutable']) + ->and($builder->withoutImmutable()->toHeaders())->toBe([]) + ->and($builder->toHeaders())->toBe(['cache-control' => 'immutable']); +}); diff --git a/tests/Builders/CacheHeaderBuilder/MaxAgeTest.php b/tests/Builders/CacheHeaderBuilder/MaxAgeTest.php new file mode 100644 index 0000000..a42b160 --- /dev/null +++ b/tests/Builders/CacheHeaderBuilder/MaxAgeTest.php @@ -0,0 +1,135 @@ +toHeaders())->toBe($expectedHeaders); + $builder->maxAge(days: 3); + expect($builder->toHeaders())->toBe(['cache-control' => 'max-age=259200']); +})->with([ + '3600 sec' => [ + (new CacheHeaderBuilder()) + ->maxAge(3600), + ['cache-control' => 'max-age=3600'], + ], + '30 sec' => [ + (new CacheHeaderBuilder()) + ->maxAge(30), + ['cache-control' => 'max-age=30'], + ], + '40 sec named arg' => [ + (new CacheHeaderBuilder()) + ->maxAge(seconds: 40), + ['cache-control' => 'max-age=40'], + ], + '5 minutes named arg' => [ + (new CacheHeaderBuilder()) + ->maxAge(minutes: 5), + ['cache-control' => 'max-age=300'], + ], + '2 hours named arg' => [ + (new CacheHeaderBuilder()) + ->maxAge(hours: 2), + ['cache-control' => 'max-age=7200'], + ], + '1 day named arg' => [ + (new CacheHeaderBuilder()) + ->maxAge(seconds: 86400), + ['cache-control' => 'max-age=86400'], + ], + '2 week named arg' => [ + (new CacheHeaderBuilder()) + ->maxAge(weeks: 2), + ['cache-control' => 'max-age=1209600'], + ], + '1 month named arg' => [ + (new CacheHeaderBuilder()) + ->maxAge(months: 1), + ['cache-control' => 'max-age=2592000'], + ], + '1 year named arg' => [ + (new CacheHeaderBuilder()) + ->maxAge(years: 1), + ['cache-control' => 'max-age=31536000'], + ], + '2 hours 30 minutes' => [ + (new CacheHeaderBuilder()) + ->maxAge(minutes: 30, hours: 2), + ['cache-control' => 'max-age=9000'], + ], +]); + +it('with max age', function (CacheHeaderBuilder $builder, array $expectedHeaders) { + expect($builder->toHeaders())->toBe($expectedHeaders); + $builderNew = $builder->withMaxAge(60); + expect($builderNew->toHeaders())->toBe(['cache-control' => 'max-age=60']) + ->and($builder->toHeaders())->toBe($expectedHeaders); +})->with([ + '3600 sec' => [ + (new CacheHeaderBuilder()) + ->maxAge(3600), + ['cache-control' => 'max-age=3600'], + ], + '30 sec' => [ + (new CacheHeaderBuilder()) + ->maxAge(30), + ['cache-control' => 'max-age=30'], + ], + '40 sec named arg' => [ + (new CacheHeaderBuilder()) + ->maxAge(seconds: 40), + ['cache-control' => 'max-age=40'], + ], + '5 minutes named arg' => [ + (new CacheHeaderBuilder()) + ->maxAge(minutes: 5), + ['cache-control' => 'max-age=300'], + ], + '2 hours named arg' => [ + (new CacheHeaderBuilder()) + ->maxAge(hours: 2), + ['cache-control' => 'max-age=7200'], + ], + '1 day named arg' => [ + (new CacheHeaderBuilder()) + ->maxAge(seconds: 86400), + ['cache-control' => 'max-age=86400'], + ], + '2 week named arg' => [ + (new CacheHeaderBuilder()) + ->maxAge(weeks: 2), + ['cache-control' => 'max-age=1209600'], + ], + '1 month named arg' => [ + (new CacheHeaderBuilder()) + ->maxAge(months: 1), + ['cache-control' => 'max-age=2592000'], + ], + '1 year named arg' => [ + (new CacheHeaderBuilder()) + ->maxAge(years: 1), + ['cache-control' => 'max-age=31536000'], + ], + '2 hours 30 minutes' => [ + (new CacheHeaderBuilder()) + ->maxAge(minutes: 30, hours: 2), + ['cache-control' => 'max-age=9000'], + ], +]); + +it('reset max age', function () { + $builder = (new CacheHeaderBuilder()) + ->maxAge(3600); + expect($builder->toHeaders())->not()->toBeEmpty(); + $builder->resetMaxAge(); + expect($builder->toHeaders())->toBeEmpty(); +}); + +it('without max age', function () { + $builder = (new CacheHeaderBuilder()) + ->maxAge(3600); + expect($builder->withoutMaxAge()->toHeaders())->toBeEmpty() + ->and($builder->toHeaders())->not()->toBeEmpty(); +}); diff --git a/tests/Builders/CacheHeaderBuilder/MustRevalidateTest.php b/tests/Builders/CacheHeaderBuilder/MustRevalidateTest.php new file mode 100644 index 0000000..a4b5e2a --- /dev/null +++ b/tests/Builders/CacheHeaderBuilder/MustRevalidateTest.php @@ -0,0 +1,33 @@ +mustRevalidate()->toHeaders())->toBe(['cache-control' => 'must-revalidate']) + ->and($builder->toHeaders())->toBe(['cache-control' => 'must-revalidate']); +}); + +it('with must revalidate', function () { + $builder = new CacheHeaderBuilder(); + expect($builder->withMustRevalidate()->toHeaders())->toBe(['cache-control' => 'must-revalidate']) + ->and($builder->toHeaders())->toBeEmpty(); +}); + +it('reset must revalidate', function () { + $builder = (new CacheHeaderBuilder()) + ->mustRevalidate(); + expect($builder->toHeaders())->toBe(['cache-control' => 'must-revalidate']); + $builder->resetMustRevalidate(); + expect($builder->toHeaders())->toBe([]); +}); + +it('without must revalidate', function () { + $builder = (new CacheHeaderBuilder()) + ->mustRevalidate(); + expect($builder->toHeaders())->toBe(['cache-control' => 'must-revalidate']) + ->and($builder->withoutMustRevalidate()->toHeaders())->toBe([]) + ->and($builder->toHeaders())->toBe(['cache-control' => 'must-revalidate']); +}); diff --git a/tests/Builders/CacheHeaderBuilder/MustUnderstandTest.php b/tests/Builders/CacheHeaderBuilder/MustUnderstandTest.php new file mode 100644 index 0000000..c29be50 --- /dev/null +++ b/tests/Builders/CacheHeaderBuilder/MustUnderstandTest.php @@ -0,0 +1,33 @@ +mustUnderstand()->toHeaders())->toBe(['cache-control' => 'must-understand']) + ->and($builder->toHeaders())->toBe(['cache-control' => 'must-understand']); +}); + +it('with must understand', function () { + $builder = new CacheHeaderBuilder(); + expect($builder->withMustUnderstand()->toHeaders())->toBe(['cache-control' => 'must-understand']) + ->and($builder->toHeaders())->toBeEmpty(); +}); + +it('reset must understand', function () { + $builder = (new CacheHeaderBuilder()) + ->mustUnderstand(); + expect($builder->toHeaders())->toBe(['cache-control' => 'must-understand']); + $builder->resetMustUnderstand(); + expect($builder->toHeaders())->toBe([]); +}); + +it('without must understand', function () { + $builder = (new CacheHeaderBuilder()) + ->mustUnderstand(); + expect($builder->toHeaders())->toBe(['cache-control' => 'must-understand']) + ->and($builder->withoutMustUnderstand()->toHeaders())->toBe([]) + ->and($builder->toHeaders())->toBe(['cache-control' => 'must-understand']); +}); diff --git a/tests/Builders/CacheHeaderBuilder/NoCacheTest.php b/tests/Builders/CacheHeaderBuilder/NoCacheTest.php new file mode 100644 index 0000000..96b5fee --- /dev/null +++ b/tests/Builders/CacheHeaderBuilder/NoCacheTest.php @@ -0,0 +1,33 @@ + 'must-revalidate, no-cache, no-store, private', + 'pragma' => 'no-cache', + ]; + $builderNoCache = (new CacheHeaderBuilder()) + ->noCache(); + expect($builderNoCache->toHeaders())->toBe($noCacheExpectedHeaders); + $builder = $builderNoCache->withSharedMaxAge(60); + expect($builder->toHeaders())->not()->toBe($noCacheExpectedHeaders) + ->and($builder->toHeaders()['cache-control'])->not()->toContain('no-cache') + ->and($builderNoCache->toHeaders())->not()->toBe($builder->toHeaders()); +}); + +it('with no cache', function () { + $noCacheExpectedHeaders = [ + 'cache-control' => 'must-revalidate, no-cache, no-store, private', + 'pragma' => 'no-cache', + ]; + $builderNoCache = (new CacheHeaderBuilder()) + ->withNoCache(); + expect($builderNoCache->toHeaders())->toBe($noCacheExpectedHeaders); + $builder = $builderNoCache->withSharedMaxAge(60); + expect($builder->toHeaders())->not()->toBe($noCacheExpectedHeaders) + ->and($builder->toHeaders()['cache-control'])->not()->toContain('no-cache') + ->and($builderNoCache->toHeaders())->not()->toBe($builder->toHeaders()); +}); diff --git a/tests/Builders/CacheHeaderBuilder/NoStoreTest.php b/tests/Builders/CacheHeaderBuilder/NoStoreTest.php new file mode 100644 index 0000000..bd4ceec --- /dev/null +++ b/tests/Builders/CacheHeaderBuilder/NoStoreTest.php @@ -0,0 +1,33 @@ +noStore()->toHeaders())->toBe(['cache-control' => 'no-store']) + ->and($builder->toHeaders())->toBe(['cache-control' => 'no-store']); +}); + +it('with no store', function () { + $builder = new CacheHeaderBuilder(); + expect($builder->withNoStore()->toHeaders())->toBe(['cache-control' => 'no-store']) + ->and($builder->toHeaders())->toBeEmpty(); +}); + +it('reset no store', function () { + $builder = (new CacheHeaderBuilder()) + ->noStore(); + expect($builder->toHeaders())->toBe(['cache-control' => 'no-store']); + $builder->resetNoStore(); + expect($builder->toHeaders())->toBe([]); +}); + +it('without no store', function () { + $builder = (new CacheHeaderBuilder()) + ->noStore(); + expect($builder->toHeaders())->toBe(['cache-control' => 'no-store']) + ->and($builder->withoutNoStore()->toHeaders())->toBe([]) + ->and($builder->toHeaders())->toBe(['cache-control' => 'no-store']); +}); diff --git a/tests/Builders/CacheHeaderBuilder/NoTransformTest.php b/tests/Builders/CacheHeaderBuilder/NoTransformTest.php new file mode 100644 index 0000000..0ba6c52 --- /dev/null +++ b/tests/Builders/CacheHeaderBuilder/NoTransformTest.php @@ -0,0 +1,33 @@ +noTransform()->toHeaders())->toBe(['cache-control' => 'no-transform']) + ->and($builder->toHeaders())->toBe(['cache-control' => 'no-transform']); +}); + +it('with no transform', function () { + $builder = new CacheHeaderBuilder(); + expect($builder->withNoTransform()->toHeaders())->toBe(['cache-control' => 'no-transform']) + ->and($builder->toHeaders())->toBeEmpty(); +}); + +it('reset no transform', function () { + $builder = (new CacheHeaderBuilder()) + ->noTransform(); + expect($builder->toHeaders())->toBe(['cache-control' => 'no-transform']); + $builder->resetNoTransform(); + expect($builder->toHeaders())->toBe([]); +}); + +it('without no transform', function () { + $builder = (new CacheHeaderBuilder()) + ->noTransform(); + expect($builder->toHeaders())->toBe(['cache-control' => 'no-transform']) + ->and($builder->withoutNoTransform()->toHeaders())->toBe([]) + ->and($builder->toHeaders())->toBe(['cache-control' => 'no-transform']); +}); diff --git a/tests/Builders/CacheHeaderBuilder/PrivateTest.php b/tests/Builders/CacheHeaderBuilder/PrivateTest.php new file mode 100644 index 0000000..6b8d807 --- /dev/null +++ b/tests/Builders/CacheHeaderBuilder/PrivateTest.php @@ -0,0 +1,33 @@ +private()->toHeaders())->toBe(['cache-control' => 'private']) + ->and($builder->toHeaders())->toBe(['cache-control' => 'private']); +}); + +it('with private', function () { + $builder = new CacheHeaderBuilder(); + expect($builder->withPrivate()->toHeaders())->toBe(['cache-control' => 'private']) + ->and($builder->toHeaders())->toBeEmpty(); +}); + +it('reset private', function () { + $builder = (new CacheHeaderBuilder()) + ->private(); + expect($builder->toHeaders())->toBe(['cache-control' => 'private']); + $builder->resetPrivate(); + expect($builder->toHeaders())->toBe([]); +}); + +it('without private', function () { + $builder = (new CacheHeaderBuilder()) + ->private(); + expect($builder->toHeaders())->toBe(['cache-control' => 'private']) + ->and($builder->withoutPrivate()->toHeaders())->toBe([]) + ->and($builder->toHeaders())->toBe(['cache-control' => 'private']); +}); diff --git a/tests/Builders/CacheHeaderBuilder/ProxyRevalidateTest.php b/tests/Builders/CacheHeaderBuilder/ProxyRevalidateTest.php new file mode 100644 index 0000000..d058c92 --- /dev/null +++ b/tests/Builders/CacheHeaderBuilder/ProxyRevalidateTest.php @@ -0,0 +1,33 @@ +proxyRevalidate()->toHeaders())->toBe(['cache-control' => 'proxy-revalidate']) + ->and($builder->toHeaders())->toBe(['cache-control' => 'proxy-revalidate']); +}); + +it('with proxy revalidate', function () { + $builder = new CacheHeaderBuilder(); + expect($builder->withProxyRevalidate()->toHeaders())->toBe(['cache-control' => 'proxy-revalidate']) + ->and($builder->toHeaders())->toBeEmpty(); +}); + +it('reset proxy revalidate', function () { + $builder = (new CacheHeaderBuilder()) + ->proxyRevalidate(); + expect($builder->toHeaders())->toBe(['cache-control' => 'proxy-revalidate']); + $builder->resetProxyRevalidate(); + expect($builder->toHeaders())->toBe([]); +}); + +it('without proxy revalidate', function () { + $builder = (new CacheHeaderBuilder()) + ->proxyRevalidate(); + expect($builder->toHeaders())->toBe(['cache-control' => 'proxy-revalidate']) + ->and($builder->withoutProxyRevalidate()->toHeaders())->toBe([]) + ->and($builder->toHeaders())->toBe(['cache-control' => 'proxy-revalidate']); +}); diff --git a/tests/Builders/CacheHeaderBuilder/PublicTest.php b/tests/Builders/CacheHeaderBuilder/PublicTest.php new file mode 100644 index 0000000..234901c --- /dev/null +++ b/tests/Builders/CacheHeaderBuilder/PublicTest.php @@ -0,0 +1,33 @@ +public()->toHeaders())->toBe(['cache-control' => 'public']) + ->and($builder->toHeaders())->toBe(['cache-control' => 'public']); +}); + +it('with public', function () { + $builder = new CacheHeaderBuilder(); + expect($builder->withPublic()->toHeaders())->toBe(['cache-control' => 'public']) + ->and($builder->toHeaders())->toBeEmpty(); +}); + +it('reset public', function () { + $builder = (new CacheHeaderBuilder()) + ->public(); + expect($builder->toHeaders())->toBe(['cache-control' => 'public']); + $builder->resetPublic(); + expect($builder->toHeaders())->toBe([]); +}); + +it('without public', function () { + $builder = (new CacheHeaderBuilder()) + ->public(); + expect($builder->toHeaders())->toBe(['cache-control' => 'public']) + ->and($builder->withoutPublic()->toHeaders())->toBe([]) + ->and($builder->toHeaders())->toBe(['cache-control' => 'public']); +}); From a017982853aa065f66b2269ae1a9e0575a506aa2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Somogyi?= Date: Sat, 4 Jan 2025 12:31:05 +0100 Subject: [PATCH 29/43] fix: CacheHeaderBuilder#etag() static return and simplify code --- src/Builders/CacheHeaderBuilder.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Builders/CacheHeaderBuilder.php b/src/Builders/CacheHeaderBuilder.php index 5438127..de3c2bc 100644 --- a/src/Builders/CacheHeaderBuilder.php +++ b/src/Builders/CacheHeaderBuilder.php @@ -824,7 +824,7 @@ public function withoutStaleIfError(): static * If ETAG is empty, it will be reset. * * @param string|ETagHeaderBuilder $etag - * @return $this + * @return static */ public function etag(string|ETagHeaderBuilder $etag): static { @@ -832,7 +832,7 @@ public function etag(string|ETagHeaderBuilder $etag): static if ($etag instanceof ETagHeaderBuilder) { $etag = (string) $etag; } - if (is_string($etag) && trim($etag) === '') { + if (trim($etag) === '') { return $this->resetETag(); } if(1 !== preg_match('!^(?:W/)?".+"$!', $etag)) { @@ -848,7 +848,7 @@ public function etag(string|ETagHeaderBuilder $etag): static * If ETAG is empty, it will be reset. * * @param string|ETagHeaderBuilder $etag - * @return $this + * @return static */ public function withETag(string|ETagHeaderBuilder $etag): static { From e81d84c651de5e9463fdb84ca1c0bfd4baf39a0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Somogyi?= Date: Sun, 5 Jan 2025 13:41:24 +0100 Subject: [PATCH 30/43] fix: TimeHelper#toTimstamp if empty string then throw \InvalidArgumentException --- src/Helpers/TimeHelper.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Helpers/TimeHelper.php b/src/Helpers/TimeHelper.php index a41b917..ae4b3b0 100644 --- a/src/Helpers/TimeHelper.php +++ b/src/Helpers/TimeHelper.php @@ -54,7 +54,7 @@ public static function toTimestamp(int|string|DateTime $input): int return $input->getTimestamp(); } if (trim($input) === '') { - throw new DateMalformedStringException('Empty string'); + throw new \InvalidArgumentException('Date string is empty'); } try { $input = new DateTime($input); From 57f5e805dea48b9a2727bf24ed92f22c06ad6b37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Somogyi?= Date: Sun, 5 Jan 2025 13:42:01 +0100 Subject: [PATCH 31/43] fix: TimeHelper#toTimstamp DateMailformedStringException with specified error message and code removed --- src/Helpers/TimeHelper.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Helpers/TimeHelper.php b/src/Helpers/TimeHelper.php index ae4b3b0..266b45f 100644 --- a/src/Helpers/TimeHelper.php +++ b/src/Helpers/TimeHelper.php @@ -61,8 +61,7 @@ public static function toTimestamp(int|string|DateTime $input): int } catch (\Exception $e) { // before php8.3 \DateMalformedStringException is not available throw new DateMalformedStringException( - message: $e->getMessage(), - code: $e->getCode(), + message: 'Malformed date string', previous: $e, ); } From 5cecadcc465fb392ea6aafd94b27da244251cdc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Somogyi?= Date: Sun, 5 Jan 2025 13:42:32 +0100 Subject: [PATCH 32/43] enh: more added pest tests --- .../CacheHeaderBuilder/ExpiresTest.php | 81 +++++++++++ .../CacheHeaderBuilder/SharedMaxAgeTest.php | 136 ++++++++++++++++++ tests/Helpers/TimeHelperTest.php | 19 +-- 3 files changed, 228 insertions(+), 8 deletions(-) create mode 100644 tests/Builders/CacheHeaderBuilder/ExpiresTest.php create mode 100644 tests/Builders/CacheHeaderBuilder/SharedMaxAgeTest.php diff --git a/tests/Builders/CacheHeaderBuilder/ExpiresTest.php b/tests/Builders/CacheHeaderBuilder/ExpiresTest.php new file mode 100644 index 0000000..4613f36 --- /dev/null +++ b/tests/Builders/CacheHeaderBuilder/ExpiresTest.php @@ -0,0 +1,81 @@ +expires($expires)->toHeaders())->toBe($expectedHeaders) + ->and($builder->toHeaders())->toBe($expectedHeaders); +})->with([ + 'datetime object' => [ + new DateTime('2021-01-01 00:00:00'), + ['expires' => 'Fri, 01 Jan 2021 00:00:00 GMT'], + ], + 'timestamp' => [ + 1609459200, + ['expires' => 'Fri, 01 Jan 2021 00:00:00 GMT'], + ], + 'string' => [ + 'Fri, 01 Jan 2021 00:00:00 GMT', + ['expires' => 'Fri, 01 Jan 2021 00:00:00 GMT'], + ], +]); + +it('with expires', function (\DateTime|int|string $expires, array $expectedHeaders) { + $builder = new CacheHeaderBuilder(); + expect($builder->withExpires($expires)->toHeaders())->toBe($expectedHeaders) + ->and($builder->toHeaders())->toBeEmpty(); +})->with([ + 'datetime object' => [ + new DateTime('2021-01-01 00:00:00'), + ['expires' => 'Fri, 01 Jan 2021 00:00:00 GMT'], + ], + 'timestamp' => [ + 1609459200, + ['expires' => 'Fri, 01 Jan 2021 00:00:00 GMT'], + ], + 'string' => [ + 'Fri, 01 Jan 2021 00:00:00 GMT', + ['expires' => 'Fri, 01 Jan 2021 00:00:00 GMT'], + ], +]); + +it('reset expires', function () { + $builder = (new CacheHeaderBuilder()) + ->expires(new DateTime('2021-01-01 00:00:00')); + expect($builder->toHeaders())->toBe(['expires' => 'Fri, 01 Jan 2021 00:00:00 GMT']); + $builder->resetExpires(); + expect($builder->toHeaders())->toBe([]); +}); + +it('without expires', function () { + $builder = (new CacheHeaderBuilder()) + ->expires(new DateTime('2021-01-01 00:00:00')); + expect($builder->toHeaders())->toBe(['expires' => 'Fri, 01 Jan 2021 00:00:00 GMT']) + ->and($builder->withoutExpires()->toHeaders())->toBe([]) + ->and($builder->toHeaders())->toBe(['expires' => 'Fri, 01 Jan 2021 00:00:00 GMT']); +}); + +it('expires with invalid date', function (string $input, string $expectedExceptionClass, string $expectedExceptionMessage) { + expect(fn() => (new CacheHeaderBuilder())->expires($input))->toThrow( + exception: $expectedExceptionClass, + exceptionMessage: $expectedExceptionMessage + ); +})->with([ + 'malformed string' => ['malformed string', \SmartonDev\HttpCache\Exceptions\DateMalformedStringException::class, 'Malformed date string'], + 'empty' => ['', \InvalidArgumentException::class, 'Date string is empty'], + 'blank' => [' ', \InvalidArgumentException::class, 'Date string is empty'] +]); + +it('with expires with invalid date', function (string $input, string $expectedExceptionClass, string $expectedExceptionMessage) { + expect(fn() => (new CacheHeaderBuilder())->withExpires($input))->toThrow( + exception: $expectedExceptionClass, + exceptionMessage: $expectedExceptionMessage + ); +})->with([ + 'malformed string' => ['malformed string', \SmartonDev\HttpCache\Exceptions\DateMalformedStringException::class, 'Malformed date string'], + 'empty' => ['', \InvalidArgumentException::class, 'Date string is empty'], + 'blank' => [' ', \InvalidArgumentException::class, 'Date string is empty'] +]); \ No newline at end of file diff --git a/tests/Builders/CacheHeaderBuilder/SharedMaxAgeTest.php b/tests/Builders/CacheHeaderBuilder/SharedMaxAgeTest.php new file mode 100644 index 0000000..3a2acdb --- /dev/null +++ b/tests/Builders/CacheHeaderBuilder/SharedMaxAgeTest.php @@ -0,0 +1,136 @@ +toHeaders())->toBe($expectedHeaders); + $builder->sharedMaxAge(days: 3); + expect($builder->toHeaders())->toBe(['cache-control' => 's-maxage=259200']); +})->with([ + '3600 sec' => [ + (new CacheHeaderBuilder()) + ->sharedMaxAge(3600), + ['cache-control' => 's-maxage=3600'], + ], + '30 sec' => [ + (new CacheHeaderBuilder()) + ->sharedMaxAge(30), + ['cache-control' => 's-maxage=30'], + ], + '40 sec named arg' => [ + (new CacheHeaderBuilder()) + ->sharedMaxAge(seconds: 40), + ['cache-control' => 's-maxage=40'], + ], + '5 minutes named arg' => [ + (new CacheHeaderBuilder()) + ->sharedMaxAge(minutes: 5), + ['cache-control' => 's-maxage=300'], + ], + '2 hours named arg' => [ + (new CacheHeaderBuilder()) + ->sharedMaxAge(hours: 2), + ['cache-control' => 's-maxage=7200'], + ], + '1 day named arg' => [ + (new CacheHeaderBuilder()) + ->sharedMaxAge(seconds: 86400), + ['cache-control' => 's-maxage=86400'], + ], + '2 week named arg' => [ + (new CacheHeaderBuilder()) + ->sharedMaxAge(weeks: 2), + ['cache-control' => 's-maxage=1209600'], + ], + '1 month named arg' => [ + (new CacheHeaderBuilder()) + ->sharedMaxAge(months: 1), + ['cache-control' => 's-maxage=2592000'], + ], + '1 year named arg' => [ + (new CacheHeaderBuilder()) + ->sharedMaxAge(years: 1), + ['cache-control' => 's-maxage=31536000'], + ], + '2 hours 30 minutes' => [ + (new CacheHeaderBuilder()) + ->sharedMaxAge(minutes: 30, hours: 2), + ['cache-control' => 's-maxage=9000'], + ], +]); + +it('with shared max age', function (CacheHeaderBuilder $builder, array $expectedHeaders) { + expect($builder->toHeaders())->toBe($expectedHeaders); + $builderNew = $builder->withSharedMaxAge(60); + expect($builderNew->toHeaders())->toBe(['cache-control' => 's-maxage=60']) + ->and($builder->toHeaders())->toBe($expectedHeaders); +})->with([ + '3600 sec' => [ + (new CacheHeaderBuilder()) + ->withSharedMaxAge(3600), + ['cache-control' => 's-maxage=3600'], + ], + '30 sec' => [ + (new CacheHeaderBuilder()) + ->withSharedMaxAge(30), + ['cache-control' => 's-maxage=30'], + ], + '40 sec named arg' => [ + (new CacheHeaderBuilder()) + ->withSharedMaxAge(seconds: 40), + ['cache-control' => 's-maxage=40'], + ], + '5 minutes named arg' => [ + (new CacheHeaderBuilder()) + ->withSharedMaxAge(minutes: 5), + ['cache-control' => 's-maxage=300'], + ], + '2 hours named arg' => [ + (new CacheHeaderBuilder()) + ->withSharedMaxAge(hours: 2), + ['cache-control' => 's-maxage=7200'], + ], + '1 day named arg' => [ + (new CacheHeaderBuilder()) + ->withSharedMaxAge(seconds: 86400), + ['cache-control' => 's-maxage=86400'], + ], + '2 week named arg' => [ + (new CacheHeaderBuilder()) + ->withSharedMaxAge(weeks: 2), + ['cache-control' => 's-maxage=1209600'], + ], + '1 month named arg' => [ + (new CacheHeaderBuilder()) + ->withSharedMaxAge(months: 1), + ['cache-control' => 's-maxage=2592000'], + ], + '1 year named arg' => [ + (new CacheHeaderBuilder()) + ->withSharedMaxAge(years: 1), + ['cache-control' => 's-maxage=31536000'], + ], + '2 hours 30 minutes' => [ + (new CacheHeaderBuilder()) + ->withSharedMaxAge(minutes: 30, hours: 2), + ['cache-control' => 's-maxage=9000'], + ], +]); + +it('reset shared max age', function () { + $builder = (new CacheHeaderBuilder()) + ->sharedMaxAge(3600); + expect($builder->toHeaders())->toBe(['cache-control' => 's-maxage=3600']); + $builder->resetSharedMaxAge(); + expect($builder->toHeaders())->toBe([]); +}); + +it('without shared max age', function () { + $builder = (new CacheHeaderBuilder()) + ->sharedMaxAge(3600); + expect($builder->toHeaders())->toBe(['cache-control' => 's-maxage=3600']) + ->and($builder->withoutSharedMaxAge()->toHeaders())->toBe([]) + ->and($builder->toHeaders())->toBe(['cache-control' => 's-maxage=3600']); +}); diff --git a/tests/Helpers/TimeHelperTest.php b/tests/Helpers/TimeHelperTest.php index 11fff75..13adfb2 100644 --- a/tests/Helpers/TimeHelperTest.php +++ b/tests/Helpers/TimeHelperTest.php @@ -3,6 +3,7 @@ declare(strict_types=1); use SmartonDev\HttpCache\Helpers\TimeHelper; +use SmartonDev\HttpCache\Exceptions\DateMalformedStringException; it('duration to seconds', function () { expect(TimeHelper::durationToSeconds(37)) @@ -35,11 +36,13 @@ 'date time object' => [new DateTime('2021-10-12T08:00:00Z'), 1634025600] ]); -it('to timestamp malformed string', function (string $input) { - TimeHelper::toTimestamp($input); -})->throws(\SmartonDev\HttpCache\Exceptions\DateMalformedStringException::class) - ->with([ - 'malformed string' => ['malformed string'], - 'empty' => [''], - 'empty spaces' => [' '] - ]); +it('to timestamp malformed string', function (string $input, string $expectedExceptionClass, string $expectedExceptionMessage) { + expect(fn() => TimeHelper::toTimestamp($input))->toThrow( + exception: $expectedExceptionClass, + exceptionMessage: $expectedExceptionMessage + ); +})->with([ + 'malformed string' => ['malformed string', DateMalformedStringException::class, 'Malformed date string'], + 'empty' => ['', \InvalidArgumentException::class, 'Date string is empty'], + 'blank' => [' ', \InvalidArgumentException::class, 'Date string is empty'] +]); From 2e9721454bcb4bcc2a197667bc2326e0f2d47590 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Somogyi?= Date: Sun, 5 Jan 2025 14:34:23 +0100 Subject: [PATCH 33/43] enh: more added pest tests (CacheHeaderBuilder) --- .../Builders/CacheHeaderBuilder/ETagTest.php | 14 ++ .../CacheHeaderBuilder/LastModifiedTest.php | 88 ++++++++++++ .../CacheHeaderBuilder/NoCacheTest.php | 7 + .../StaleWhileRevalidateTest.php | 134 ++++++++++++++++++ 4 files changed, 243 insertions(+) create mode 100644 tests/Builders/CacheHeaderBuilder/LastModifiedTest.php create mode 100644 tests/Builders/CacheHeaderBuilder/StaleWhileRevalidateTest.php diff --git a/tests/Builders/CacheHeaderBuilder/ETagTest.php b/tests/Builders/CacheHeaderBuilder/ETagTest.php index e8a207e..f8330bb 100644 --- a/tests/Builders/CacheHeaderBuilder/ETagTest.php +++ b/tests/Builders/CacheHeaderBuilder/ETagTest.php @@ -69,3 +69,17 @@ expect($builder->withEtag($etag)->toHeaders())->toBe(['etag' => '"123"']) ->and($builder->withEtag($etag->weekETag())->toHeaders())->toBe(['etag' => 'W/"123"']); }); + +it('has etag', function () { + $builder = (new CacheHeaderBuilder()) + ->etag('"123"'); + expect($builder->hasEtag())->toBeTrue() + ->and($builder->withoutEtag()->hasEtag())->toBeFalse(); +}); + +it('get etag', function () { + $builder = (new CacheHeaderBuilder()) + ->etag('"123"'); + expect($builder->getEtag())->toBe('"123"') + ->and($builder->withoutEtag()->getEtag())->toBeNull(); +}); \ No newline at end of file diff --git a/tests/Builders/CacheHeaderBuilder/LastModifiedTest.php b/tests/Builders/CacheHeaderBuilder/LastModifiedTest.php new file mode 100644 index 0000000..4c33d69 --- /dev/null +++ b/tests/Builders/CacheHeaderBuilder/LastModifiedTest.php @@ -0,0 +1,88 @@ +lastModified($input)->toHeaders())->toBe($expectedHeaders) + ->and($builder->toHeaders())->toBe($expectedHeaders); +})->with([ + 'datetime object' => [ + new DateTime('2021-01-01 00:00:00'), + ['last-modified' => 'Fri, 01 Jan 2021 00:00:00 GMT'], + ], + 'timestamp' => [ + 1609459200, + ['last-modified' => 'Fri, 01 Jan 2021 00:00:00 GMT'], + ], + 'string' => [ + 'Fri, 01 Jan 2021 00:00:00 GMT', + ['last-modified' => 'Fri, 01 Jan 2021 00:00:00 GMT'], + ], +]); + +it('with last modified', function (\DateTime|int|string $input, array $expectedHeaders) { + $builder = new CacheHeaderBuilder(); + expect($builder->withLastModified($input)->toHeaders())->toBe($expectedHeaders) + ->and($builder->toHeaders())->toBeEmpty(); +})->with([ + 'datetime object' => [ + new DateTime('2021-01-01 00:00:00'), + ['last-modified' => 'Fri, 01 Jan 2021 00:00:00 GMT'], + ], + 'timestamp' => [ + 1609459200, + ['last-modified' => 'Fri, 01 Jan 2021 00:00:00 GMT'], + ], + 'string' => [ + 'Fri, 01 Jan 2021 00:00:00 GMT', + ['last-modified' => 'Fri, 01 Jan 2021 00:00:00 GMT'], + ], +]); + +it('reset last modified', function () { + $builder = (new CacheHeaderBuilder()) + ->lastModified(new DateTime('2021-01-01 00:00:00')); + expect($builder->toHeaders())->toBe(['last-modified' => 'Fri, 01 Jan 2021 00:00:00 GMT']); + $builder->resetLastModified(); + expect($builder->toHeaders())->toBe([]); +}); + +it('without last modified', function () { + $builder = (new CacheHeaderBuilder()) + ->lastModified(new DateTime('2021-01-01 00:00:00')); + expect($builder->toHeaders())->toBe(['last-modified' => 'Fri, 01 Jan 2021 00:00:00 GMT']) + ->and($builder->withoutLastModified()->toHeaders())->toBe([]) + ->and($builder->toHeaders())->toBe(['last-modified' => 'Fri, 01 Jan 2021 00:00:00 GMT']); +}); + +it('last modified with invalid date', function (string $input, string $expectedExceptionClass, string $expectedExceptionMessage) { + expect(fn() => (new CacheHeaderBuilder())->lastModified($input))->toThrow( + exception: $expectedExceptionClass, + exceptionMessage: $expectedExceptionMessage + ); +})->with([ + 'malformed string' => ['malformed string', \SmartonDev\HttpCache\Exceptions\DateMalformedStringException::class, 'Malformed date string'], + 'empty' => ['', \InvalidArgumentException::class, 'Date string is empty'], + 'blank' => [' ', \InvalidArgumentException::class, 'Date string is empty'] +]); + +it('with last modified with invalid date', function (string $input, string $expectedExceptionClass, string $expectedExceptionMessage) { + expect(fn() => (new CacheHeaderBuilder())->withLastModified($input))->toThrow( + exception: $expectedExceptionClass, + exceptionMessage: $expectedExceptionMessage + ); +})->with([ + 'malformed string' => ['malformed string', \SmartonDev\HttpCache\Exceptions\DateMalformedStringException::class, 'Malformed date string'], + 'empty' => ['', \InvalidArgumentException::class, 'Date string is empty'], + 'blank' => [' ', \InvalidArgumentException::class, 'Date string is empty'] +]); + +it('has last modified', function () { + $builder = (new CacheHeaderBuilder()) + ->lastModified(new DateTime('2021-01-01 00:00:00')); + expect($builder->hasLastModified())->toBeTrue() + ->and($builder->resetLastModified()->hasLastModified())->toBeFalse(); +}); \ No newline at end of file diff --git a/tests/Builders/CacheHeaderBuilder/NoCacheTest.php b/tests/Builders/CacheHeaderBuilder/NoCacheTest.php index 96b5fee..c846d45 100644 --- a/tests/Builders/CacheHeaderBuilder/NoCacheTest.php +++ b/tests/Builders/CacheHeaderBuilder/NoCacheTest.php @@ -31,3 +31,10 @@ ->and($builder->toHeaders()['cache-control'])->not()->toContain('no-cache') ->and($builderNoCache->toHeaders())->not()->toBe($builder->toHeaders()); }); + +it('is no cache', function () { + $builder = (new CacheHeaderBuilder()) + ->noCache(); + expect($builder->isNoCache())->toBeTrue() + ->and($builder->withSharedMaxAge(60)->isNoCache())->toBeFalse(); +}); \ No newline at end of file diff --git a/tests/Builders/CacheHeaderBuilder/StaleWhileRevalidateTest.php b/tests/Builders/CacheHeaderBuilder/StaleWhileRevalidateTest.php new file mode 100644 index 0000000..db022f4 --- /dev/null +++ b/tests/Builders/CacheHeaderBuilder/StaleWhileRevalidateTest.php @@ -0,0 +1,134 @@ +toHeaders())->toBe($expectedHeaders); + $builder->staleWhileRevalidate(days: 3); + expect($builder->toHeaders())->toBe(['cache-control' => 'stale-while-revalidate=259200']); +})->with([ + '3600 sec' => [ + (new CacheHeaderBuilder()) + ->staleWhileRevalidate(3600), + ['cache-control' => 'stale-while-revalidate=3600'], + ], + '30 sec' => [ + (new CacheHeaderBuilder()) + ->staleWhileRevalidate(30), + ['cache-control' => 'stale-while-revalidate=30'], + ], + '40 sec named arg' => [ + (new CacheHeaderBuilder()) + ->staleWhileRevalidate(seconds: 40), + ['cache-control' => 'stale-while-revalidate=40'], + ], + '5 minutes named arg' => [ + (new CacheHeaderBuilder()) + ->staleWhileRevalidate(minutes: 5), + ['cache-control' => 'stale-while-revalidate=300'], + ], + '2 hours named arg' => [ + (new CacheHeaderBuilder()) + ->staleWhileRevalidate(hours: 2), + ['cache-control' => 'stale-while-revalidate=7200'], + ], + '1 day named arg' => [ + (new CacheHeaderBuilder()) + ->staleWhileRevalidate(seconds: 86400), + ['cache-control' => 'stale-while-revalidate=86400'], + ], + '2 week named arg' => [ + (new CacheHeaderBuilder()) + ->staleWhileRevalidate(weeks: 2), + ['cache-control' => 'stale-while-revalidate=1209600'], + ], + '1 month named arg' => [ + (new CacheHeaderBuilder()) + ->staleWhileRevalidate(months: 1), + ['cache-control' => 'stale-while-revalidate=2592000'], + ], + '1 year named arg' => [ + (new CacheHeaderBuilder()) + ->staleWhileRevalidate(years: 1), + ['cache-control' => 'stale-while-revalidate=31536000'], + ], + '2 hours 30 minutes' => [ + (new CacheHeaderBuilder()) + ->staleWhileRevalidate(minutes: 30, hours: 2), + ['cache-control' => 'stale-while-revalidate=9000'], + ], +]); + +it('stale while revalidate', function (CacheHeaderBuilder $builder, array $expectedHeaders) { + expect($builder->toHeaders())->toBe($expectedHeaders); + $builder->staleWhileRevalidate(days: 3); + expect($builder->toHeaders())->toBe(['cache-control' => 'stale-while-revalidate=259200']); +})->with([ + '3600 sec' => [ + (new CacheHeaderBuilder()) + ->staleWhileRevalidate(3600), + ['cache-control' => 'stale-while-revalidate=3600'], + ], + '30 sec' => [ + (new CacheHeaderBuilder()) + ->staleWhileRevalidate(30), + ['cache-control' => 'stale-while-revalidate=30'], + ], + '40 sec named arg' => [ + (new CacheHeaderBuilder()) + ->staleWhileRevalidate(seconds: 40), + ['cache-control' => 'stale-while-revalidate=40'], + ], + '5 minutes named arg' => [ + (new CacheHeaderBuilder()) + ->staleWhileRevalidate(minutes: 5), + ['cache-control' => 'stale-while-revalidate=300'], + ], + '2 hours named arg' => [ + (new CacheHeaderBuilder()) + ->staleWhileRevalidate(hours: 2), + ['cache-control' => 'stale-while-revalidate=7200'], + ], + '1 day named arg' => [ + (new CacheHeaderBuilder()) + ->staleWhileRevalidate(seconds: 86400), + ['cache-control' => 'stale-while-revalidate=86400'], + ], + '2 week named arg' => [ + (new CacheHeaderBuilder()) + ->staleWhileRevalidate(weeks: 2), + ['cache-control' => 'stale-while-revalidate=1209600'], + ], + '1 month named arg' => [ + (new CacheHeaderBuilder()) + ->staleWhileRevalidate(months: 1), + ['cache-control' => 'stale-while-revalidate=2592000'], + ], + '1 year named arg' => [ + (new CacheHeaderBuilder()) + ->staleWhileRevalidate(years: 1), + ['cache-control' => 'stale-while-revalidate=31536000'], + ], + '2 hours 30 minutes' => [ + (new CacheHeaderBuilder()) + ->staleWhileRevalidate(minutes: 30, hours: 2), + ['cache-control' => 'stale-while-revalidate=9000'], + ], +]); + +it('reset stale while revalidate', function () { + $builder = (new CacheHeaderBuilder()) + ->staleWhileRevalidate(3600); + expect($builder->toHeaders())->not()->toBeEmpty(); + $builder->resetStaleWhileRevalidate(); + expect($builder->toHeaders())->toBeEmpty(); +}); + +it('without stale while revalidate', function () { + $builder = (new CacheHeaderBuilder()) + ->staleWhileRevalidate(3600); + expect($builder->withoutStaleWhileRevalidate()->toHeaders())->toBeEmpty() + ->and($builder->toHeaders())->not()->toBeEmpty(); +}); From 73cb42128ce70265c7bf6e1e9ab4a087560dd637 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Somogyi?= Date: Sun, 5 Jan 2025 15:02:50 +0100 Subject: [PATCH 34/43] enh: more added pest tests (CacheHeaderBuilder) --- .../CacheHeaderBuilder/NoCacheTest.php | 25 +++++- tests/Builders/CacheHeaderBuilderPestTest.php | 84 +++++++++++++++++++ 2 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 tests/Builders/CacheHeaderBuilderPestTest.php diff --git a/tests/Builders/CacheHeaderBuilder/NoCacheTest.php b/tests/Builders/CacheHeaderBuilder/NoCacheTest.php index c846d45..1d27649 100644 --- a/tests/Builders/CacheHeaderBuilder/NoCacheTest.php +++ b/tests/Builders/CacheHeaderBuilder/NoCacheTest.php @@ -37,4 +37,27 @@ ->noCache(); expect($builder->isNoCache())->toBeTrue() ->and($builder->withSharedMaxAge(60)->isNoCache())->toBeFalse(); -}); \ No newline at end of file +}); + +it('is no cache multiple stage', function() { + $builder = (new CacheHeaderBuilder()); + expect($builder->isNoCache())->toBeFalse(); + $builder->noCache(); + expect($builder->isNoCache())->toBeTrue(); + $builder->reset(); + expect($builder->isNoCache())->toBeFalse(); + + $builder->noCache(); + $builder->public(); + expect($builder->isNoCache())->toBeFalse(); +}); + +it('no cache reset', function () { + $builder = (new CacheHeaderBuilder()) + ->noCache(); + expect($builder->withReset()->toHeaders())->toBeEmpty() + ->and($builder->withPrivate()->toHeaders())->toBe(['cache-control' => 'private']) + ->and($builder->withPublic()->toHeaders())->toBe(['cache-control' => 'public']) + ->and($builder->withNoStore()->toHeaders())->toBe(['cache-control' => 'no-store']) + ->and($builder->withMustRevalidate()->toHeaders())->toBe(['cache-control' => 'must-revalidate']); +}); diff --git a/tests/Builders/CacheHeaderBuilderPestTest.php b/tests/Builders/CacheHeaderBuilderPestTest.php new file mode 100644 index 0000000..d51b4e3 --- /dev/null +++ b/tests/Builders/CacheHeaderBuilderPestTest.php @@ -0,0 +1,84 @@ +isEmpty())->toBeTrue() + ->and($builder->withMaxAge(10)->isEmpty())->toBeFalse(); +}); + +it('is empty multiple stage', function () { + $builder = new CacheHeaderBuilder(); + expect($builder->isEmpty())->toBeTrue(); + $builder->noCache(); + expect($builder->isEmpty())->toBeFalse(); + $builder->reset(); + expect($builder->isEmpty())->toBeTrue(); +}); + +it('is not empty', function () { + $builder = new CacheHeaderBuilder(); + expect($builder->isNotEmpty())->toBeFalse() + ->and($builder->withMaxAge(10)->isNotEmpty())->toBeTrue(); +}); + +it('is not empty multiple stage', function () { + $builder = new CacheHeaderBuilder(); + expect($builder->isNotEmpty())->toBeFalse(); + $builder->noCache(); + expect($builder->isNotEmpty())->toBeTrue(); + $builder->reset(); + expect($builder->isNotEmpty())->toBeFalse(); +}); + +it('reset', function (CacheHeaderBuilder $builder) { + expect($builder->toHeaders())->not()->toBeEmpty(); + $builder->reset(); + expect($builder->toHeaders())->toBeEmpty(); +})->with([ + 'no cache' => [(new CacheHeaderBuilder())->withNoCache()], + 'private' => [(new CacheHeaderBuilder())->withPrivate()], + 'public' => [(new CacheHeaderBuilder())->withPublic()], + 'no store' => [(new CacheHeaderBuilder())->withNoStore()], + 'must revalidate' => [(new CacheHeaderBuilder())->withMustRevalidate()], + 'proxy revalidate' => [(new CacheHeaderBuilder())->withProxyRevalidate()], + 'must understand' => [(new CacheHeaderBuilder())->withMustUnderstand()], + 'immutable' => [(new CacheHeaderBuilder())->withImmutable()], + 'no transform' => [(new CacheHeaderBuilder())->withNoTransform()], + 'stale while revalidate' => [(new CacheHeaderBuilder())->withStaleWhileRevalidate(3600)], + 'stale if error' => [(new CacheHeaderBuilder())->withStaleIfError(3600)], + 'expires' => [(new CacheHeaderBuilder())->withExpires('Sun, 05 Sep 2021 00:00:00 GMT')], + 'etag' => [(new CacheHeaderBuilder())->withETag((new ETagHeaderBuilder())->withETag('123456'))], + 'age' => [(new CacheHeaderBuilder())->withAge(1)], + 'shared max age' => [(new CacheHeaderBuilder())->withSharedMaxAge(3600)], + 'max age' => [(new CacheHeaderBuilder())->withMaxAge(3600)], + 'last modified' => [(new CacheHeaderBuilder())->withLastModified(1)], +]); + +it('with reset', function (CacheHeaderBuilder $builder) { + expect($builder->toHeaders())->not()->toBeEmpty() + ->and($builder->withReset()->toHeaders())->toBeEmpty() + ->and($builder->toHeaders())->not()->toBeEmpty(); +})->with([ + 'no cache' => [(new CacheHeaderBuilder())->withNoCache()], + 'private' => [(new CacheHeaderBuilder())->withPrivate()], + 'public' => [(new CacheHeaderBuilder())->withPublic()], + 'no store' => [(new CacheHeaderBuilder())->withNoStore()], + 'must revalidate' => [(new CacheHeaderBuilder())->withMustRevalidate()], + 'proxy revalidate' => [(new CacheHeaderBuilder())->withProxyRevalidate()], + 'must understand' => [(new CacheHeaderBuilder())->withMustUnderstand()], + 'immutable' => [(new CacheHeaderBuilder())->withImmutable()], + 'no transform' => [(new CacheHeaderBuilder())->withNoTransform()], + 'stale while revalidate' => [(new CacheHeaderBuilder())->withStaleWhileRevalidate(3600)], + 'stale if error' => [(new CacheHeaderBuilder())->withStaleIfError(3600)], + 'expires' => [(new CacheHeaderBuilder())->withExpires('Sun, 05 Sep 2021 00:00:00 GMT')], + 'etag' => [(new CacheHeaderBuilder())->withETag((new ETagHeaderBuilder())->withETag('123456'))], + 'age' => [(new CacheHeaderBuilder())->withAge(1)], + 'shared max age' => [(new CacheHeaderBuilder())->withSharedMaxAge(3600)], + 'max age' => [(new CacheHeaderBuilder())->withMaxAge(3600)], + 'last modified' => [(new CacheHeaderBuilder())->withLastModified(1)], +]); From 1c5732102d1f0936d06e57cdea0657c889f22eb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Somogyi?= Date: Sun, 5 Jan 2025 15:54:16 +0100 Subject: [PATCH 35/43] enh: more added pest tests (CacheHeaderBuilder) --- .../CacheHeaderBuilder/StaleIfErrorTest.php | 136 ++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 tests/Builders/CacheHeaderBuilder/StaleIfErrorTest.php diff --git a/tests/Builders/CacheHeaderBuilder/StaleIfErrorTest.php b/tests/Builders/CacheHeaderBuilder/StaleIfErrorTest.php new file mode 100644 index 0000000..5018e85 --- /dev/null +++ b/tests/Builders/CacheHeaderBuilder/StaleIfErrorTest.php @@ -0,0 +1,136 @@ +toHeaders())->toBe($expectedHeaders); + $builder->staleIfError(days: 3); + expect($builder->toHeaders())->toBe(['cache-control' => 'stale-if-error=259200']); +})->with([ + '3600 sec' => [ + (new CacheHeaderBuilder()) + ->staleIfError(3600), + ['cache-control' => 'stale-if-error=3600'], + ], + '30 sec' => [ + (new CacheHeaderBuilder()) + ->staleIfError(30), + ['cache-control' => 'stale-if-error=30'], + ], + '40 sec named arg' => [ + (new CacheHeaderBuilder()) + ->staleIfError(seconds: 40), + ['cache-control' => 'stale-if-error=40'], + ], + '5 minutes named arg' => [ + (new CacheHeaderBuilder()) + ->staleIfError(minutes: 5), + ['cache-control' => 'stale-if-error=300'], + ], + '2 hours named arg' => [ + (new CacheHeaderBuilder()) + ->staleIfError(hours: 2), + ['cache-control' => 'stale-if-error=7200'], + ], + '1 day named arg' => [ + (new CacheHeaderBuilder()) + ->staleIfError(seconds: 86400), + ['cache-control' => 'stale-if-error=86400'], + ], + '2 week named arg' => [ + (new CacheHeaderBuilder()) + ->staleIfError(weeks: 2), + ['cache-control' => 'stale-if-error=1209600'], + ], + '1 month named arg' => [ + (new CacheHeaderBuilder()) + ->staleIfError(months: 1), + ['cache-control' => 'stale-if-error=2592000'], + ], + '1 year named arg' => [ + (new CacheHeaderBuilder()) + ->staleIfError(years: 1), + ['cache-control' => 'stale-if-error=31536000'], + ], + '2 hours 30 minutes' => [ + (new CacheHeaderBuilder()) + ->staleIfError(minutes: 30, hours: 2), + ['cache-control' => 'stale-if-error=9000'], + ], +]); + +it('with stale if error', function (CacheHeaderBuilder $builder, array $expectedHeaders) { + expect($builder->toHeaders())->toBe($expectedHeaders); + $builderNew = $builder->withStaleIfError(60); + expect($builderNew->toHeaders())->toBe(['cache-control' => 'stale-if-error=60']) + ->and($builder->toHeaders())->toBe($expectedHeaders); +})->with([ + '3600 sec' => [ + (new CacheHeaderBuilder()) + ->withStaleIfError(3600), + ['cache-control' => 'stale-if-error=3600'], + ], + '30 sec' => [ + (new CacheHeaderBuilder()) + ->withStaleIfError(30), + ['cache-control' => 'stale-if-error=30'], + ], + '40 sec named arg' => [ + (new CacheHeaderBuilder()) + ->withStaleIfError(seconds: 40), + ['cache-control' => 'stale-if-error=40'], + ], + '5 minutes named arg' => [ + (new CacheHeaderBuilder()) + ->withStaleIfError(minutes: 5), + ['cache-control' => 'stale-if-error=300'], + ], + '2 hours named arg' => [ + (new CacheHeaderBuilder()) + ->withStaleIfError(hours: 2), + ['cache-control' => 'stale-if-error=7200'], + ], + '1 day named arg' => [ + (new CacheHeaderBuilder()) + ->withStaleIfError(seconds: 86400), + ['cache-control' => 'stale-if-error=86400'], + ], + '2 week named arg' => [ + (new CacheHeaderBuilder()) + ->withStaleIfError(weeks: 2), + ['cache-control' => 'stale-if-error=1209600'], + ], + '1 month named arg' => [ + (new CacheHeaderBuilder()) + ->withStaleIfError(months: 1), + ['cache-control' => 'stale-if-error=2592000'], + ], + '1 year named arg' => [ + (new CacheHeaderBuilder()) + ->withStaleIfError(years: 1), + ['cache-control' => 'stale-if-error=31536000'], + ], + '2 hours 30 minutes' => [ + (new CacheHeaderBuilder()) + ->withStaleIfError(minutes: 30, hours: 2), + ['cache-control' => 'stale-if-error=9000'], + ], +]); + +it('reset stale if error', function () { + $builder = (new CacheHeaderBuilder()) + ->staleIfError(3600); + expect($builder->toHeaders())->toBe(['cache-control' => 'stale-if-error=3600']); + $builder->resetStaleIfError(); + expect($builder->toHeaders())->toBe([]); +}); + +it('without stale if error', function () { + $builder = (new CacheHeaderBuilder()) + ->staleIfError(3600); + expect($builder->toHeaders())->toBe(['cache-control' => 'stale-if-error=3600']) + ->and($builder->withoutStaleIfError()->toHeaders())->toBe([]) + ->and($builder->toHeaders())->toBe(['cache-control' => 'stale-if-error=3600']); +}); From 583d092b9a7faf82e8b360834f86922b85fc738c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Somogyi?= Date: Sun, 5 Jan 2025 15:54:56 +0100 Subject: [PATCH 36/43] fix: StaleWhileRevalidateTest test name and with based tests --- .../StaleWhileRevalidateTest.php | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/tests/Builders/CacheHeaderBuilder/StaleWhileRevalidateTest.php b/tests/Builders/CacheHeaderBuilder/StaleWhileRevalidateTest.php index db022f4..7c3bb0c 100644 --- a/tests/Builders/CacheHeaderBuilder/StaleWhileRevalidateTest.php +++ b/tests/Builders/CacheHeaderBuilder/StaleWhileRevalidateTest.php @@ -4,7 +4,7 @@ use SmartonDev\HttpCache\Builders\CacheHeaderBuilder; -it('max age', function (CacheHeaderBuilder $builder, array $expectedHeaders) { +it('stale while revalidate', function (CacheHeaderBuilder $builder, array $expectedHeaders) { expect($builder->toHeaders())->toBe($expectedHeaders); $builder->staleWhileRevalidate(days: 3); expect($builder->toHeaders())->toBe(['cache-control' => 'stale-while-revalidate=259200']); @@ -61,59 +61,60 @@ ], ]); -it('stale while revalidate', function (CacheHeaderBuilder $builder, array $expectedHeaders) { +it('with stale while revalidate', function (CacheHeaderBuilder $builder, array $expectedHeaders) { expect($builder->toHeaders())->toBe($expectedHeaders); - $builder->staleWhileRevalidate(days: 3); - expect($builder->toHeaders())->toBe(['cache-control' => 'stale-while-revalidate=259200']); + $builderNew = $builder->withStaleWhileRevalidate(60); + expect($builderNew->toHeaders())->toBe(['cache-control' => 'stale-while-revalidate=60']) + ->and($builder->toHeaders())->toBe($expectedHeaders); })->with([ '3600 sec' => [ (new CacheHeaderBuilder()) - ->staleWhileRevalidate(3600), + ->withStaleWhileRevalidate(3600), ['cache-control' => 'stale-while-revalidate=3600'], ], '30 sec' => [ (new CacheHeaderBuilder()) - ->staleWhileRevalidate(30), + ->withStaleWhileRevalidate(30), ['cache-control' => 'stale-while-revalidate=30'], ], '40 sec named arg' => [ (new CacheHeaderBuilder()) - ->staleWhileRevalidate(seconds: 40), + ->withStaleWhileRevalidate(seconds: 40), ['cache-control' => 'stale-while-revalidate=40'], ], '5 minutes named arg' => [ (new CacheHeaderBuilder()) - ->staleWhileRevalidate(minutes: 5), + ->withStaleWhileRevalidate(minutes: 5), ['cache-control' => 'stale-while-revalidate=300'], ], '2 hours named arg' => [ (new CacheHeaderBuilder()) - ->staleWhileRevalidate(hours: 2), + ->withStaleWhileRevalidate(hours: 2), ['cache-control' => 'stale-while-revalidate=7200'], ], '1 day named arg' => [ (new CacheHeaderBuilder()) - ->staleWhileRevalidate(seconds: 86400), + ->withStaleWhileRevalidate(seconds: 86400), ['cache-control' => 'stale-while-revalidate=86400'], ], '2 week named arg' => [ (new CacheHeaderBuilder()) - ->staleWhileRevalidate(weeks: 2), + ->withStaleWhileRevalidate(weeks: 2), ['cache-control' => 'stale-while-revalidate=1209600'], ], '1 month named arg' => [ (new CacheHeaderBuilder()) - ->staleWhileRevalidate(months: 1), + ->withStaleWhileRevalidate(months: 1), ['cache-control' => 'stale-while-revalidate=2592000'], ], '1 year named arg' => [ (new CacheHeaderBuilder()) - ->staleWhileRevalidate(years: 1), + ->withStaleWhileRevalidate(years: 1), ['cache-control' => 'stale-while-revalidate=31536000'], ], '2 hours 30 minutes' => [ (new CacheHeaderBuilder()) - ->staleWhileRevalidate(minutes: 30, hours: 2), + ->withStaleWhileRevalidate(minutes: 30, hours: 2), ['cache-control' => 'stale-while-revalidate=9000'], ], ]); From 53032d8a74d28effc02a31df01b698e15056e71a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Somogyi?= Date: Sun, 5 Jan 2025 15:58:53 +0100 Subject: [PATCH 37/43] enh: CacheHeaderBuilder ignore coverage lines --- src/Builders/CacheHeaderBuilder.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Builders/CacheHeaderBuilder.php b/src/Builders/CacheHeaderBuilder.php index de3c2bc..ab3ad1c 100644 --- a/src/Builders/CacheHeaderBuilder.php +++ b/src/Builders/CacheHeaderBuilder.php @@ -955,10 +955,10 @@ public function toHeaders(): array } if ($this->hasETag()) { - if (null === $this->etag) { + $headers[ETagHeaderBuilder::ETAG_HEADER] = $this->etag ?? + // @codeCoverageIgnoreStart throw new \LogicException('ETag is empty'); - } - $headers[ETagHeaderBuilder::ETAG_HEADER] = $this->etag; + // @codeCoverageIgnoreEnd } return $headers; From 1bafaf6116e8c7ac9d99cfbc7056e9f0c28d868e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Somogyi?= Date: Sun, 5 Jan 2025 15:59:52 +0100 Subject: [PATCH 38/43] enh: CacheHeaderBuilder#etag()/withEtag() empty and blank etag tests --- tests/Builders/CacheHeaderBuilder/ETagTest.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/Builders/CacheHeaderBuilder/ETagTest.php b/tests/Builders/CacheHeaderBuilder/ETagTest.php index f8330bb..ac70873 100644 --- a/tests/Builders/CacheHeaderBuilder/ETagTest.php +++ b/tests/Builders/CacheHeaderBuilder/ETagTest.php @@ -12,6 +12,8 @@ })->with([ 'strong' => ['"123"', ['etag' => '"123"']], 'weak' => ['W/"123"', ['etag' => 'W/"123"']], + 'empty etag' => ['', []], + 'blank etag' => [' ', []], ]); it('with etag', function (string $etag, array $expectedHeaders) { @@ -21,6 +23,8 @@ })->with([ 'strong' => ['"123"', ['etag' => '"123"']], 'weak' => ['W/"123"', ['etag' => 'W/"123"']], + 'empty etag' => ['', []], + 'blank etag' => [' ', []], ]); it('reset etag', function () { From 4f721b5912aad234a6deb04aebc6b9e608ae6306 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Somogyi?= Date: Sun, 5 Jan 2025 16:00:34 +0100 Subject: [PATCH 39/43] remove: CacheHeaderBuilderTest phpunit tests --- tests/Builders/CacheHeaderBuilderTest.php | 508 ---------------------- 1 file changed, 508 deletions(-) delete mode 100644 tests/Builders/CacheHeaderBuilderTest.php diff --git a/tests/Builders/CacheHeaderBuilderTest.php b/tests/Builders/CacheHeaderBuilderTest.php deleted file mode 100644 index ffda813..0000000 --- a/tests/Builders/CacheHeaderBuilderTest.php +++ /dev/null @@ -1,508 +0,0 @@ - 'must-revalidate, no-cache, no-store, private', - 'pragma' => 'no-cache', - ]; - $builder = (new CacheHeaderBuilder()) - ->withNoCache(); - $this->assertSame($noCacheExpectedHeaders, $builder->toHeaders()); - $builder = $builder->withSharedMaxAge(10); - $this->assertNotEquals($noCacheExpectedHeaders, $builder->toHeaders()); - $this->assertFalse(strpos($builder->toHeaders()['cache-control'], 'no-cache')); - $this->assertSame($noCacheExpectedHeaders, $builder->withNoCache()->toHeaders()); - } - - public static function dataProviderMaxAgeWithDurations(): array - { - $builder = new CacheHeaderBuilder(); - return [ - '37' => [['cache-control' => 'max-age=37'], $builder->withMaxAge(37)], - 'hours:1' => [['cache-control' => 'max-age=3600'], $builder->withMaxAge(hours: 1)], - 'minutes:30' => [['cache-control' => 'max-age=1800'], $builder->withMaxAge(minutes: 30)], - 'seconds:60' => [['cache-control' => 'max-age=60'], $builder->withMaxAge(seconds: 60)], - 'days:1' => [['cache-control' => 'max-age=86400'], $builder->withMaxAge(days: 1)], - 'weeks:1' => [['cache-control' => 'max-age=604800'], $builder->withMaxAge(weeks: 1)], - 'months:1' => [['cache-control' => 'max-age=2592000'], $builder->withMaxAge(months: 1)], - 'years:1' => [['cache-control' => 'max-age=31536000'], $builder->withMaxAge(years: 1)], - 'seconds:2, minutes:2, hours:1' => [['cache-control' => 'max-age=3722'], $builder->withMaxAge(seconds: 2, minutes: 2, hours: 1)], - ]; - } - - #[DataProvider('dataProviderMaxAgeWithDurations')] - public function testMaxAgeWithDurations(array $expectedHeaders, CacheHeaderBuilder $builder): void - { - $this->assertSame($expectedHeaders, $builder->toHeaders()); - } - - public function testMaxAge(): void - { - $builder = (new CacheHeaderBuilder()) - ->withMaxAge(hours: 1); - $this->assertSame(['cache-control' => 'max-age=3600'], $builder->toHeaders()); - $this->assertSame( - ['cache-control' => 'max-age=1800'], - $builder->withMaxAge(1800) - ->toHeaders() - ); - } - - public function testResetMaxAge(): void - { - $builder = (new CacheHeaderBuilder()) - ->withMaxAge(hours: 1); - $this->assertNotSame([], $builder->toHeaders()); - $this->assertSame([], $builder->withoutMaxAge()->toHeaders()); - $this->assertNotSame([], $builder->toHeaders()); - $this->assertSame([], $builder->resetMaxAge()->toHeaders()); - $this->assertSame([], $builder->toHeaders()); - } - - public function testResetSharedMaxAge(): void - { - $builder = (new CacheHeaderBuilder()) - ->withSharedMaxAge(hours: 1); - $this->assertNotSame([], $builder->toHeaders()); - $this->assertSame([], $builder->withoutSharedMaxAge()->toHeaders()); - $this->assertNotSame([], $builder->toHeaders()); - $this->assertSame([], $builder->resetSharedMaxAge()->toHeaders()); - $this->assertSame([], $builder->toHeaders()); - } - - public function testResetMustRevalidate(): void - { - $builder = (new CacheHeaderBuilder()) - ->withMustRevalidate(); - $this->assertNotSame([], $builder->toHeaders()); - $this->assertSame([], $builder->withoutMustRevalidate()->toHeaders()); - $this->assertNotSame([], $builder->toHeaders()); - $this->assertSame([], $builder->resetMustRevalidate()->toHeaders()); - $this->assertSame([], $builder->toHeaders()); - } - - public function testResetProxyRevalidate(): void - { - $builder = (new CacheHeaderBuilder()) - ->withProxyRevalidate(); - $this->assertNotSame([], $builder->toHeaders()); - $this->assertSame([], $builder->withoutProxyRevalidate()->toHeaders()); - $this->assertNotSame([], $builder->toHeaders()); - $this->assertSame([], $builder->resetProxyRevalidate()->toHeaders()); - $this->assertSame([], $builder->toHeaders()); - } - - public function testResetNoStore(): void - { - $builder = (new CacheHeaderBuilder()) - ->withNoStore(); - $this->assertNotSame([], $builder->toHeaders()); - $this->assertSame([], $builder->withoutNoStore()->toHeaders()); - $this->assertNotSame([], $builder->toHeaders()); - $this->assertSame([], $builder->resetNoStore()->toHeaders()); - $this->assertSame([], $builder->toHeaders()); - } - - public function testResetPrivate(): void - { - $builder = (new CacheHeaderBuilder()) - ->withPrivate(); - $this->assertNotSame([], $builder->toHeaders()); - $this->assertSame([], $builder->withoutPrivate()->toHeaders()); - $this->assertNotSame([], $builder->toHeaders()); - $this->assertSame([], $builder->resetPrivate()->toHeaders()); - $this->assertSame([], $builder->toHeaders()); - } - - public function testResetPublic(): void - { - $builder = (new CacheHeaderBuilder()) - ->withPublic(); - $this->assertNotSame([], $builder->toHeaders()); - $this->assertSame([], $builder->withoutPublic()->toHeaders()); - $this->assertNotSame([], $builder->toHeaders()); - $this->assertSame([], $builder->resetPublic()->toHeaders()); - $this->assertSame([], $builder->toHeaders()); - } - - public function testResetMustUnderstand(): void - { - $builder = (new CacheHeaderBuilder()) - ->withMustUnderstand(); - $this->assertNotSame([], $builder->toHeaders()); - $this->assertSame([], $builder->withoutMustUnderstand()->toHeaders()); - $this->assertNotSame([], $builder->toHeaders()); - $this->assertSame([], $builder->resetMustUnderstand()->toHeaders()); - $this->assertSame([], $builder->toHeaders()); - } - - public function testResetNoTransform(): void - { - $builder = (new CacheHeaderBuilder()) - ->withNoTransform(); - $this->assertNotSame([], $builder->toHeaders()); - $this->assertSame([], $builder->withoutNoTransform()->toHeaders()); - $this->assertNotSame([], $builder->toHeaders()); - $this->assertSame([], $builder->resetNoTransform()->toHeaders()); - $this->assertSame([], $builder->toHeaders()); - } - - public function testResetImmutable(): void - { - $builder = (new CacheHeaderBuilder()) - ->withImmutable(); - $this->assertNotSame([], $builder->toHeaders()); - $this->assertSame([], $builder->withoutImmutable()->toHeaders()); - $this->assertNotSame([], $builder->toHeaders()); - $this->assertSame([], $builder->resetImmutable()->toHeaders()); - $this->assertSame([], $builder->toHeaders()); - } - - public function testResetStaleWhileRevalidate(): void - { - $builder = (new CacheHeaderBuilder()) - ->withStaleWhileRevalidate(3600); - $this->assertNotSame([], $builder->toHeaders()); - $this->assertSame([], $builder->withoutStaleWhileRevalidate()->toHeaders()); - $this->assertNotSame([], $builder->toHeaders()); - $this->assertSame([], $builder->resetStaleWhileRevalidate()->toHeaders()); - $this->assertSame([], $builder->toHeaders()); - } - - public function testResetStaleIfError(): void - { - $builder = (new CacheHeaderBuilder()) - ->withStaleIfError(3600); - $this->assertNotSame([], $builder->toHeaders()); - $this->assertSame([], $builder->withoutStaleIfError()->toHeaders()); - $this->assertNotSame([], $builder->toHeaders()); - $this->assertSame([], $builder->resetStaleIfError()->toHeaders()); - $this->assertSame([], $builder->toHeaders()); - } - - public function testResetETag(): void - { - $builder = (new CacheHeaderBuilder()) - ->withETag((new ETagHeaderBuilder())->withETag('123456')); - $this->assertNotSame([], $builder->toHeaders()); - $this->assertSame([], $builder->withoutETag()->toHeaders()); - $this->assertNotSame([], $builder->toHeaders()); - $this->assertSame([], $builder->resetETag()->toHeaders()); - $this->assertSame([], $builder->toHeaders()); - } - - public function testMaxAgeWithNoStore(): void - { - $builder = (new CacheHeaderBuilder()) - ->withMaxAge(3600) - ->withNoStore(); - $this->assertSame(['cache-control' => 'max-age=3600, no-store'], $builder->toHeaders()); - } - - public function testMaxAgeWithAge(): void - { - $builder = (new CacheHeaderBuilder()) - ->withMaxAge(3600) - ->withAge(1800); - $this->assertSame([ - 'cache-control' => 'max-age=3600', - 'age' => '1800', - ], $builder->toHeaders()); - } - - public function testSharedMaxAge(): void - { - $builder = (new CacheHeaderBuilder()) - ->withSharedMaxAge(hours: 1); - $this->assertSame(['cache-control' => 's-maxage=3600'], $builder->toHeaders()); - $this->assertSame( - ['cache-control' => 's-maxage=1800'], - $builder->withSharedMaxAge(minutes: 30) - ->toHeaders() - ); - } - - public function testNoStore(): void - { - $builder = new CacheHeaderBuilder(); - $this->assertSame(['cache-control' => 'no-store'], $builder->withNoStore()->toHeaders()); - $this->assertSame([], $builder->toHeaders()); - $this->assertSame(['cache-control' => 'no-store'], $builder->noStore()->toHeaders()); - $this->assertSame(['cache-control' => 'no-store'], $builder->toHeaders()); - } - - public function testPrivate(): void - { - $builder = new CacheHeaderBuilder(); - $this->assertSame(['cache-control' => 'private'], $builder->withPrivate()->toHeaders()); - $this->assertSame([], $builder->toHeaders()); - $this->assertSame(['cache-control' => 'private'], $builder->private()->toHeaders()); - $this->assertSame(['cache-control' => 'private'], $builder->toHeaders()); - } - - public function testPublic(): void - { - $builder = new CacheHeaderBuilder(); - $this->assertSame(['cache-control' => 'public'], $builder->withPublic()->toHeaders()); - $this->assertSame([], $builder->toHeaders()); - $this->assertSame(['cache-control' => 'public'], $builder->public()->toHeaders()); - $this->assertSame(['cache-control' => 'public'], $builder->toHeaders()); - } - - public function testMustRevalidate(): void - { - $builder = new CacheHeaderBuilder(); - $this->assertSame(['cache-control' => 'must-revalidate'], $builder->withMustRevalidate()->toHeaders()); - $this->assertSame([], $builder->toHeaders()); - $this->assertSame(['cache-control' => 'must-revalidate'], $builder->mustRevalidate()->toHeaders()); - $this->assertSame(['cache-control' => 'must-revalidate'], $builder->toHeaders()); - } - - public function testProxyRevalidate(): void - { - $builder = new CacheHeaderBuilder(); - $this->assertSame(['cache-control' => 'proxy-revalidate'], $builder->withProxyRevalidate()->toHeaders()); - $this->assertSame([], $builder->toHeaders()); - $this->assertSame(['cache-control' => 'proxy-revalidate'], $builder->proxyRevalidate()->toHeaders()); - $this->assertSame(['cache-control' => 'proxy-revalidate'], $builder->toHeaders()); - } - - public function testMustUnderstand(): void - { - $builder = new CacheHeaderBuilder(); - $this->assertSame(['cache-control' => 'must-understand'], $builder->withMustUnderstand()->toHeaders()); - $this->assertSame([], $builder->toHeaders()); - $this->assertSame(['cache-control' => 'must-understand'], $builder->mustUnderstand()->toHeaders()); - $this->assertSame(['cache-control' => 'must-understand'], $builder->toHeaders()); - } - - public function testImmutable(): void - { - $builder = new CacheHeaderBuilder(); - $this->assertSame(['cache-control' => 'immutable'], $builder->withImmutable()->toHeaders()); - $this->assertSame([], $builder->toHeaders()); - $this->assertSame(['cache-control' => 'immutable'], $builder->immutable()->toHeaders()); - $this->assertSame(['cache-control' => 'immutable'], $builder->toHeaders()); - } - - public function testNoTransform(): void - { - $builder = new CacheHeaderBuilder(); - $this->assertSame(['cache-control' => 'no-transform'], $builder->withNoTransform()->toHeaders()); - $this->assertSame([], $builder->toHeaders()); - $this->assertSame(['cache-control' => 'no-transform'], $builder->noTransform()->toHeaders()); - $this->assertSame(['cache-control' => 'no-transform'], $builder->toHeaders()); - } - - public function testLastModified(): void - { - $builder = new CacheHeaderBuilder(); - $this->assertSame(['last-modified' => 'Sun, 05 Sep 2021 00:00:00 GMT'], $builder->withLastModified(strtotime('Sun, 05 Sep 2021 00:00:00 GMT'))->toHeaders()); - $this->assertSame(['last-modified' => 'Sun, 05 Sep 2021 00:00:00 GMT'], $builder->withLastModified(new \DateTime('Sun, 05 Sep 2021 00:00:00 GMT'))->toHeaders()); - $this->assertSame(['last-modified' => 'Sun, 05 Sep 2021 00:00:00 GMT'], $builder->withLastModified('Sun, 05 Sep 2021 00:00:00 GMT')->toHeaders()); - - $builder = (new CacheHeaderBuilder())->lastModified(strtotime('Sun, 05 Sep 2021 00:00:00 GMT')); - $this->assertSame(['last-modified' => 'Sun, 05 Sep 2021 00:00:00 GMT'], $builder->toHeaders()); - $builder = (new CacheHeaderBuilder())->lastModified(new \DateTime('Sun, 05 Sep 2021 00:00:00 GMT')); - $this->assertSame(['last-modified' => 'Sun, 05 Sep 2021 00:00:00 GMT'], $builder->toHeaders()); - $builder = (new CacheHeaderBuilder())->lastModified('Sun, 05 Sep 2021 00:00:00 GMT'); - $this->assertSame(['last-modified' => 'Sun, 05 Sep 2021 00:00:00 GMT'], $builder->toHeaders()); - - $builder = new CacheHeaderBuilder(); - $this->assertSame(['last-modified' => 'Sun, 05 Sep 2021 00:00:00 GMT'], $builder->withLastModified(strtotime('Sun, 05 Sep 2021 00:00:00 GMT'))->toHeaders()); - $this->assertSame([], $builder->toHeaders()); - $this->assertSame(['last-modified' => 'Sun, 05 Sep 2021 00:00:00 GMT'], $builder->lastModified(strtotime('Sun, 05 Sep 2021 00:00:00 GMT'))->toHeaders()); - $this->assertSame(['last-modified' => 'Sun, 05 Sep 2021 00:00:00 GMT'], $builder->toHeaders()); - $this->assertSame([], $builder->withoutLastModified()->toHeaders()); - $this->assertNotSame([], $builder->toHeaders()); - $builder->resetLastModified(); - $this->assertSame([], $builder->toHeaders()); - } - - public function testAge(): void - { - $builder = new CacheHeaderBuilder(); - $this->assertSame(['age' => '1'], $builder->withAge(1)->toHeaders()); - $this->assertSame([], $builder->toHeaders()); - $this->assertSame(['age' => '1'], $builder->age(1)->toHeaders()); - $this->assertSame(['age' => '1'], $builder->toHeaders()); - $this->assertSame([], $builder->withoutAge()->toHeaders()); - $this->assertNotSame([], $builder->toHeaders()); - $builder->resetAge(); - $this->assertSame([], $builder->toHeaders()); - } - - public function testStaleWhileRevalidate(): void - { - $builder = (new CacheHeaderBuilder()) - ->withStaleWhileRevalidate(hours: 1); - $this->assertSame(['cache-control' => 'stale-while-revalidate=3600'], $builder->toHeaders()); - $this->assertSame( - ['cache-control' => 'stale-while-revalidate=1800'], - $builder->withStaleWhileRevalidate(minutes: 30) - ->toHeaders() - ); - } - - public static function dataProviderExpires(): array - { - return [ - ['Sun, 05 Sep 2021 00:00:00 GMT'], - ['Mon, 06 Sep 2021 01:00:00 GMT'], - ['Sun, 12 Sep 2021 01:02:03 GMT'], - ]; - } - - #[DataProvider('dataProviderExpires')] - public function testExpires(string $dt): void - { - $builder = new CacheHeaderBuilder(); - $this->assertSame(['expires' => $dt], $builder->withExpires(strtotime($dt))->toHeaders()); - $this->assertSame(['expires' => $dt], $builder->withExpires(new \DateTime($dt))->toHeaders()); - $this->assertSame(['expires' => $dt], $builder->withExpires($dt)->toHeaders()); - } - - public function testStaleIfError(): void - { - $builder = (new CacheHeaderBuilder()) - ->withStaleIfError(3600); - $this->assertSame(['cache-control' => 'stale-if-error=3600'], $builder->toHeaders()); - $this->assertSame( - ['cache-control' => 'stale-if-error=1800'], - $builder->withStaleIfError(minutes: 30) - ->toHeaders() - ); - } - - public function testWithETag(): void - { - $etagBuilder = (new ETagHeaderBuilder()) - ->withETag('123456'); - $builder = (new CacheHeaderBuilder()) - ->withETag($etagBuilder); - $headers = $builder->toHeaders(); - $this->assertSame(['etag' => '"123456"'], $headers); - } - - public function testWithEmptyEtag(): void - { - $builder = (new CacheHeaderBuilder()) - ->withETag(''); - $this->assertNull($builder->getETag()); - $this->assertFalse($builder->hasETag()); - - $builder = (new CacheHeaderBuilder()) - ->withETag(' '); - $this->assertNull($builder->getETag()); - $this->assertFalse($builder->hasETag()); - } - - public function testHasLastModified(): void - { - $builder = new CacheHeaderBuilder(); - $this->assertFalse($builder->hasLastModified()); - $builder->lastModified(1); - $this->assertTrue($builder->hasLastModified()); - $builder->resetLastModified(); - $this->assertFalse($builder->hasLastModified()); - } - - public function testIsEmpty(): void - { - $builder = new CacheHeaderBuilder(); - $this->assertTrue($builder->isEmpty()); - $builder->noCache(); - $this->assertFalse($builder->isEmpty()); - $builder->reset(); - $this->assertTrue($builder->isEmpty()); - } - - public function testIsNotEmpty(): void - { - $builder = new CacheHeaderBuilder(); - $this->assertFalse($builder->isNotEmpty()); - $builder->noCache(); - $this->assertTrue($builder->isNotEmpty()); - $builder->reset(); - $this->assertFalse($builder->isNotEmpty()); - } - - public function testNoCacheReset(): void - { - $builder = new CacheHeaderBuilder(); - $builder->noCache(); - $this->assertSame([], $builder->withReset()->toHeaders()); - $this->assertSame(['cache-control' => 'private'], $builder->withPrivate()->toHeaders()); - $this->assertSame(['cache-control' => 'public'], $builder->withPublic()->toHeaders()); - $this->assertSame(['cache-control' => 'no-store'], $builder->withNoStore()->toHeaders()); - $this->assertSame(['cache-control' => 'must-revalidate'], $builder->withMustRevalidate()->toHeaders()); - } - - public function testIsNoCache(): void - { - $builder = new CacheHeaderBuilder(); - $this->assertFalse($builder->isNoCache()); - $builder->noCache(); - $this->assertTrue($builder->isNoCache()); - $builder->reset(); - $this->assertFalse($builder->isNoCache()); - - $builder->noCache(); - $builder->public(); - $this->assertFalse($builder->isNoCache()); - } - - public static function dataProviderReset(): array - { - $builder = new CacheHeaderBuilder(); - return [ - 'noCache' => [$builder->withNoCache()], - 'private' => [$builder->withPrivate()], - 'public' => [$builder->withPublic()], - 'noStore' => [$builder->withNoStore()], - 'mustRevalidate' => [$builder->withMustRevalidate()], - 'proxyRevalidate' => [$builder->withProxyRevalidate()], - 'mustUnderstand' => [$builder->withMustUnderstand()], - 'immutable' => [$builder->withImmutable()], - 'noTransform' => [$builder->withNoTransform()], - 'staleWhileRevalidate' => [$builder->withStaleWhileRevalidate(3600)], - 'staleIfError' => [$builder->withStaleIfError(3600)], - 'expires' => [$builder->withExpires('Sun, 05 Sep 2021 00:00:00 GMT')], - 'etag' => [$builder->withETag((new ETagHeaderBuilder())->withETag('123456'))], - 'age' => [$builder->withAge(1)], - 'sharedMaxAge' => [$builder->withSharedMaxAge(3600)], - 'maxAge' => [$builder->withMaxAge(3600)], - 'lastModified' => [$builder->withLastModified(1)], - ]; - } - - #[DataProvider('dataProviderReset')] - public function testReset(CacheHeaderBuilder $builder): void - { - $builder->noCache(); - $this->assertNotSame([], $builder->toHeaders()); - $this->assertSame([], $builder->withReset()->toHeaders()); - $builder->reset(); - $this->assertSame([], $builder->toHeaders()); - } - - public function testResetExpires(): void - { - $builder = (new CacheHeaderBuilder()) - ->withExpires('Sun, 05 Sep 2021 00:00:00 GMT'); - $this->assertNotSame([], $builder->toHeaders()); - $this->assertSame([], $builder->withoutExpires()->toHeaders()); - $builder->resetExpires(); - $this->assertSame([], $builder->toHeaders()); - } -} \ No newline at end of file From 1cb2cfa33caab82cdeb0bbee6839aeb815cbfebc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Somogyi?= Date: Mon, 6 Jan 2025 16:18:05 +0100 Subject: [PATCH 40/43] rename: ETagMatcher#ifMatchHeader -> ifMatchHeaderValue / withIfMatchHeader -> withIfMatchHeaderValue --- CHANGELOG.md | 2 ++ src/Matchers/ETagMatcher.php | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6cf9f4b..7caddb0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ - enh: custom DateMalFormedStringException added - fix: ETagHeaderBuilder#etag() etag set and already set with null -> null etag not stored +- rename: ETagMatcher#ifMatchHeader() -> ETagMatcher#ifMatchHeaderValue() +- rename: ETagMatcher#withIfMatchHeader() -> ETagMatcher#withIfMatchHeaderValue() ## 0.5.0 diff --git a/src/Matchers/ETagMatcher.php b/src/Matchers/ETagMatcher.php index ad79833..834e9cc 100644 --- a/src/Matchers/ETagMatcher.php +++ b/src/Matchers/ETagMatcher.php @@ -15,7 +15,7 @@ class ETagMatcher extends MatcherHeaderAbstract * * @param string|array $ifMatch if-match header value or values */ - public function ifMatchHeader(string|array $ifMatch): static + public function ifMatchHeaderValue(string|array $ifMatch): static { return $this->headers( HttpHeaderHelper::replaceHeaders($this->getHeaders(), [self::IF_MATCH_HEADER => $ifMatch]) @@ -27,9 +27,9 @@ public function ifMatchHeader(string|array $ifMatch): static * * @param string|array $ifMatch if-match header value or values */ - public function withIfMatchHeader(string|array $ifMatch): static + public function withIfMatchHeaderValue(string|array $ifMatch): static { - return (clone $this)->ifMatchHeader($ifMatch); + return (clone $this)->ifMatchHeaderValue($ifMatch); } /** From 605d045de6a1230ad1eb826bb112d54640aa76ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Somogyi?= Date: Mon, 6 Jan 2025 16:18:43 +0100 Subject: [PATCH 41/43] enh: more php pest tests added --- tests/Builders/CacheHeaderBuilderPestTest.php | 38 ++++++++ tests/Matchers/ETagMatcherTest.php | 86 ++++++++++++++++++- 2 files changed, 122 insertions(+), 2 deletions(-) diff --git a/tests/Builders/CacheHeaderBuilderPestTest.php b/tests/Builders/CacheHeaderBuilderPestTest.php index d51b4e3..0f26c73 100644 --- a/tests/Builders/CacheHeaderBuilderPestTest.php +++ b/tests/Builders/CacheHeaderBuilderPestTest.php @@ -82,3 +82,41 @@ 'max age' => [(new CacheHeaderBuilder())->withMaxAge(3600)], 'last modified' => [(new CacheHeaderBuilder())->withLastModified(1)], ]); + +it('mixed', function (CacheHeaderBuilder $builder, array $expectedHeaders) { + foreach ($builder->toHeaders() as $name => $value) { + expect($expectedHeaders[$name]) + ->toEqualCanonicalizing(preg_split('!\s*,\s*!', $value)); + } +})->with([ + 'public maxage=3600' => [ + (new CacheHeaderBuilder()) + ->withPublic() + ->withMaxAge(3600), + ['cache-control' => ['public', 'max-age=3600']], + ], + 'public notransform smaxage=100 maxage=200' => [ + (new CacheHeaderBuilder()) + ->withPublic() + ->withNoTransform() + ->withSharedMaxAge(100) + ->withMaxAge(200), + ['cache-control' => ['public', 'no-transform', 's-maxage=100', 'max-age=200']], + ], + 'mustrevalidate nostore' => [ + (new CacheHeaderBuilder()) + ->withMustRevalidate() + ->withNoStore(), + ['cache-control' => ['must-revalidate', 'no-store']], + ], + 'public notransform smaxage=100 maxage=200 stalewhile=300 staleiferror=400' => [ + (new CacheHeaderBuilder()) + ->withPublic() + ->withNoTransform() + ->withSharedMaxAge(100) + ->withMaxAge(200) + ->withStaleWhileRevalidate(300) + ->withStaleIfError(400), + ['cache-control' => ['public', 'no-transform', 's-maxage=100', 'max-age=200', 'stale-while-revalidate=300', 'stale-if-error=400']], + ], +]); \ No newline at end of file diff --git a/tests/Matchers/ETagMatcherTest.php b/tests/Matchers/ETagMatcherTest.php index 5145469..ab3ebb1 100644 --- a/tests/Matchers/ETagMatcherTest.php +++ b/tests/Matchers/ETagMatcherTest.php @@ -4,7 +4,7 @@ use SmartonDev\HttpCache\Matchers\ETagMatcher; -it('if match header', function (array $headers, ?string $etag, bool $expected) { +it('if match header matches', function (array $headers, ?string $etag, bool $expected) { $matcher = (new ETagMatcher()) ->headers($headers); expect($matcher->matches($etag)->matchesIfMatchHeader())->toBe($expected) @@ -42,7 +42,7 @@ ], ]); -it('if none match header', function (array $headers, ?string $etag, bool $expected) { +it('if none match header matches', function (array $headers, ?string $etag, bool $expected) { $matcher = (new ETagMatcher()) ->headers($headers); expect($matcher->matches($etag)->matchesIfNoneMatchHeader())->toBe($expected) @@ -79,3 +79,85 @@ false, ], ]); + +it('has if match header', function (array $headers, bool $expected) { + $matcher = (new ETagMatcher()) + ->headers($headers); + expect($matcher->hasIfMatchHeader())->toBe($expected); +})->with([ + 'has' => [ + ['If-Match' => '"123"'], + true, + ], + 'has empty' => [ + ['If-Match' => ''], + true, + ], + 'empty' => [ + [], + false, + ], + 'another header' => [ + ['x-foo' => '"123"'], + false, + ], +]); + +it('if has none match header', function (array $headers, bool $expected) { + $matcher = (new ETagMatcher()) + ->headers($headers); + expect($matcher->hasIfNoneMatchHeader())->toBe($expected); +})->with([ + 'has' => [ + ['If-None-Match' => '"123"'], + true, + ], + 'has empty' => [ + ['If-None-Match' => ''], + true, + ], + 'empty' => [ + [], + false, + ], + 'another header' => [ + ['x-foo' => '"123"'], + false, + ], +]); + +it('if match header', function (string $ifMatchHeader, ?string $etag, bool $expected) { + $matcher = (new ETagMatcher()) + ->ifMatchHeaderValue($ifMatchHeader); + expect($matcher->matches($etag)->matchesIfMatchHeader())->toBe($expected) + ->and($matcher->matches($etag)->notMatchesIfMatchHeader())->toBe(!$expected); +})->with([ + 'match' => [ + '"123"', + '"123"', + true, + ], + 'not match' => [ + '"123"', + '"456"', + false, + ], +]); + +it('if none match header', function (string $ifNoneMatchHeader, ?string $etag, bool $expected) { + $matcher = (new ETagMatcher()) + ->ifNoneMatchHeaderValue($ifNoneMatchHeader); + expect($matcher->matches($etag)->matchesIfNoneMatchHeader())->toBe($expected) + ->and($matcher->matches($etag)->notMatchesIfNoneMatchHeader())->toBe(!$expected); +})->with([ + 'match' => [ + '"123"', + '"123"', + true, + ], + 'not match' => [ + '"123"', + '"456"', + false, + ], +]); \ No newline at end of file From 44e3c40e79b54d78024438bbec42b71db32b0e7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Somogyi?= Date: Wed, 8 Jan 2025 16:55:54 +0100 Subject: [PATCH 42/43] enh: more php pest tests added --- tests/Matchers/ETagMatcherTest.php | 40 ++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/tests/Matchers/ETagMatcherTest.php b/tests/Matchers/ETagMatcherTest.php index ab3ebb1..d6713e7 100644 --- a/tests/Matchers/ETagMatcherTest.php +++ b/tests/Matchers/ETagMatcherTest.php @@ -126,7 +126,7 @@ ], ]); -it('if match header', function (string $ifMatchHeader, ?string $etag, bool $expected) { +it('if match header value', function (string $ifMatchHeader, ?string $etag, bool $expected) { $matcher = (new ETagMatcher()) ->ifMatchHeaderValue($ifMatchHeader); expect($matcher->matches($etag)->matchesIfMatchHeader())->toBe($expected) @@ -144,7 +144,25 @@ ], ]); -it('if none match header', function (string $ifNoneMatchHeader, ?string $etag, bool $expected) { +it('with if match header value', function (string $ifMatchHeader, ?string $etag, bool $expected) { + $matcher = (new ETagMatcher()) + ->withIfMatchHeaderValue($ifMatchHeader); + expect($matcher->matches($etag)->matchesIfMatchHeader())->toBe($expected) + ->and($matcher->matches($etag)->notMatchesIfMatchHeader())->toBe(!$expected); +})->with([ + 'match' => [ + '"123"', + '"123"', + true, + ], + 'not match' => [ + '"123"', + '"456"', + false, + ], +]); + +it('if none match header value', function (string $ifNoneMatchHeader, ?string $etag, bool $expected) { $matcher = (new ETagMatcher()) ->ifNoneMatchHeaderValue($ifNoneMatchHeader); expect($matcher->matches($etag)->matchesIfNoneMatchHeader())->toBe($expected) @@ -160,4 +178,22 @@ '"456"', false, ], +]); + +it('with if none match header value', function (string $ifNoneMatchHeader, ?string $etag, bool $expected) { + $matcher = (new ETagMatcher()) + ->withIfNoneMatchHeaderValue($ifNoneMatchHeader); + expect($matcher->matches($etag)->matchesIfNoneMatchHeader())->toBe($expected) + ->and($matcher->matches($etag)->notMatchesIfNoneMatchHeader())->toBe(!$expected); +})->with([ + 'match' => [ + '"123"', + '"123"', + true, + ], + 'not match' => [ + '"123"', + '"456"', + false, + ], ]); \ No newline at end of file From 90c1c932009de177ad8b0ada6b556c9f8cede1ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Somogyi?= Date: Wed, 8 Jan 2025 16:56:28 +0100 Subject: [PATCH 43/43] enh: CI pest parallel run --- .github/workflows/coverals-and-codecov.yml | 2 +- .github/workflows/pest.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/coverals-and-codecov.yml b/.github/workflows/coverals-and-codecov.yml index dcd3675..a81ea16 100644 --- a/.github/workflows/coverals-and-codecov.yml +++ b/.github/workflows/coverals-and-codecov.yml @@ -30,7 +30,7 @@ jobs: run: echo "xdebug.mode=coverage" >> $GITHUB_ENV - name: Run PHP Pest tests - run: vendor/bin/pest --coverage-clover=coverage.xml + run: vendor/bin/pest --coverage-clover=coverage.xml --parallel - name: Send coverage to Coveralls uses: coverallsapp/github-action@v2 diff --git a/.github/workflows/pest.yml b/.github/workflows/pest.yml index 5486971..a6244ff 100644 --- a/.github/workflows/pest.yml +++ b/.github/workflows/pest.yml @@ -34,4 +34,4 @@ jobs: run: echo "xdebug.mode=coverage" >> $GITHUB_ENV - name: Run Pest - run: vendor/bin/pest \ No newline at end of file + run: vendor/bin/pest --parallel \ No newline at end of file