From c99b89ef685470debabbc59c0a2b0264474d1b7c Mon Sep 17 00:00:00 2001 From: Trusted97 Date: Thu, 27 Nov 2025 14:09:04 +0100 Subject: [PATCH 1/5] refactor: phpunit test --- composer.lock | 666 +++++++++--------- src/Validator/Constraints/Repository.php | 1 - tests/Application/ApplicationKernelTest.php | 45 ++ .../Satis/Command/BuildCommandTest.php | 164 ----- tests/Controller/AdminControllerTest.php | 131 ++-- tests/Controller/SecurityControllerTest.php | 61 +- tests/Integration/BuildCommandTest.php | 173 +++++ tests/Manager/ManagerConfigValidatorTest.php | 44 +- tests/Persister/FilePersisterTest.php | 82 +-- tests/Traits/SchemaValidatorTrait.php | 6 +- tests/Traits/VfsTrait.php | 3 +- tests/app/ApplicationKernelTest.php | 31 - 12 files changed, 723 insertions(+), 684 deletions(-) create mode 100644 tests/Application/ApplicationKernelTest.php delete mode 100644 tests/Composer/Satis/Command/BuildCommandTest.php create mode 100644 tests/Integration/BuildCommandTest.php delete mode 100644 tests/app/ApplicationKernelTest.php diff --git a/composer.lock b/composer.lock index 3066541..ed89ee7 100644 --- a/composer.lock +++ b/composer.lock @@ -8,16 +8,16 @@ "packages": [ { "name": "composer/ca-bundle", - "version": "1.5.8", + "version": "1.5.9", "source": { "type": "git", "url": "https://github.com/composer/ca-bundle.git", - "reference": "719026bb30813accb68271fee7e39552a58e9f65" + "reference": "1905981ee626e6f852448b7aaa978f8666c5bc54" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/ca-bundle/zipball/719026bb30813accb68271fee7e39552a58e9f65", - "reference": "719026bb30813accb68271fee7e39552a58e9f65", + "url": "https://api.github.com/repos/composer/ca-bundle/zipball/1905981ee626e6f852448b7aaa978f8666c5bc54", + "reference": "1905981ee626e6f852448b7aaa978f8666c5bc54", "shasum": "" }, "require": { @@ -64,7 +64,7 @@ "support": { "irc": "irc://irc.freenode.org/composer", "issues": "https://github.com/composer/ca-bundle/issues", - "source": "https://github.com/composer/ca-bundle/tree/1.5.8" + "source": "https://github.com/composer/ca-bundle/tree/1.5.9" }, "funding": [ { @@ -76,26 +76,26 @@ "type": "github" } ], - "time": "2025-08-20T18:49:47+00:00" + "time": "2025-11-06T11:46:17+00:00" }, { "name": "composer/class-map-generator", - "version": "1.6.2", + "version": "1.7.0", "source": { "type": "git", "url": "https://github.com/composer/class-map-generator.git", - "reference": "ba9f089655d4cdd64e762a6044f411ccdaec0076" + "reference": "2373419b7709815ed323ebf18c3c72d03ff4a8a6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/class-map-generator/zipball/ba9f089655d4cdd64e762a6044f411ccdaec0076", - "reference": "ba9f089655d4cdd64e762a6044f411ccdaec0076", + "url": "https://api.github.com/repos/composer/class-map-generator/zipball/2373419b7709815ed323ebf18c3c72d03ff4a8a6", + "reference": "2373419b7709815ed323ebf18c3c72d03ff4a8a6", "shasum": "" }, "require": { "composer/pcre": "^2.1 || ^3.1", "php": "^7.2 || ^8.0", - "symfony/finder": "^4.4 || ^5.3 || ^6 || ^7" + "symfony/finder": "^4.4 || ^5.3 || ^6 || ^7 || ^8" }, "require-dev": { "phpstan/phpstan": "^1.12 || ^2", @@ -103,7 +103,7 @@ "phpstan/phpstan-phpunit": "^1 || ^2", "phpstan/phpstan-strict-rules": "^1.1 || ^2", "phpunit/phpunit": "^8", - "symfony/filesystem": "^5.4 || ^6" + "symfony/filesystem": "^5.4 || ^6 || ^7 || ^8" }, "type": "library", "extra": { @@ -133,7 +133,7 @@ ], "support": { "issues": "https://github.com/composer/class-map-generator/issues", - "source": "https://github.com/composer/class-map-generator/tree/1.6.2" + "source": "https://github.com/composer/class-map-generator/tree/1.7.0" }, "funding": [ { @@ -145,30 +145,31 @@ "type": "github" } ], - "time": "2025-08-20T18:52:43+00:00" + "time": "2025-11-19T10:41:15+00:00" }, { "name": "composer/composer", - "version": "2.8.12", + "version": "2.9.2", "source": { "type": "git", "url": "https://github.com/composer/composer.git", - "reference": "3e38919bc9a2c3c026f2151b5e56d04084ce8f0b" + "reference": "8d5358f147c63a3a681b002076deff8c90e0b19d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/composer/zipball/3e38919bc9a2c3c026f2151b5e56d04084ce8f0b", - "reference": "3e38919bc9a2c3c026f2151b5e56d04084ce8f0b", + "url": "https://api.github.com/repos/composer/composer/zipball/8d5358f147c63a3a681b002076deff8c90e0b19d", + "reference": "8d5358f147c63a3a681b002076deff8c90e0b19d", "shasum": "" }, "require": { "composer/ca-bundle": "^1.5", "composer/class-map-generator": "^1.4.0", "composer/metadata-minifier": "^1.0", - "composer/pcre": "^2.2 || ^3.2", + "composer/pcre": "^2.3 || ^3.3", "composer/semver": "^3.3", "composer/spdx-licenses": "^1.5.7", "composer/xdebug-handler": "^2.0.2 || ^3.0.3", + "ext-json": "*", "justinrainbow/json-schema": "^6.5.1", "php": "^7.2.5 || ^8.0", "psr/log": "^1.0 || ^2.0 || ^3.0", @@ -176,13 +177,14 @@ "seld/jsonlint": "^1.4", "seld/phar-utils": "^1.2", "seld/signal-handler": "^2.0", - "symfony/console": "^5.4.47 || ^6.4.25 || ^7.1.10", - "symfony/filesystem": "^5.4.45 || ^6.4.24 || ^7.1.10", - "symfony/finder": "^5.4.45 || ^6.4.24 || ^7.1.10", + "symfony/console": "^5.4.47 || ^6.4.25 || ^7.1.10 || ^8.0", + "symfony/filesystem": "^5.4.45 || ^6.4.24 || ^7.1.10 || ^8.0", + "symfony/finder": "^5.4.45 || ^6.4.24 || ^7.1.10 || ^8.0", "symfony/polyfill-php73": "^1.24", "symfony/polyfill-php80": "^1.24", "symfony/polyfill-php81": "^1.24", - "symfony/process": "^5.4.47 || ^6.4.25 || ^7.1.10" + "symfony/polyfill-php84": "^1.30", + "symfony/process": "^5.4.47 || ^6.4.25 || ^7.1.10 || ^8.0" }, "require-dev": { "phpstan/phpstan": "^1.11.8", @@ -190,12 +192,13 @@ "phpstan/phpstan-phpunit": "^1.4.0", "phpstan/phpstan-strict-rules": "^1.6.0", "phpstan/phpstan-symfony": "^1.4.0", - "symfony/phpunit-bridge": "^6.4.25 || ^7.3.3" + "symfony/phpunit-bridge": "^6.4.25 || ^7.3.3 || ^8.0" }, "suggest": { - "ext-openssl": "Enabling the openssl extension allows you to access https URLs for repositories and packages", - "ext-zip": "Enabling the zip extension allows you to unzip archives", - "ext-zlib": "Allow gzip compression of HTTP requests" + "ext-curl": "Provides HTTP support (will fallback to PHP streams if missing)", + "ext-openssl": "Enables access to repositories and packages over HTTPS", + "ext-zip": "Allows direct extraction of ZIP archives (unzip/7z binaries will be used instead if available)", + "ext-zlib": "Enables gzip for HTTP requests" }, "bin": [ "bin/composer" @@ -208,7 +211,7 @@ ] }, "branch-alias": { - "dev-main": "2.8-dev" + "dev-main": "2.9-dev" } }, "autoload": { @@ -243,7 +246,7 @@ "irc": "ircs://irc.libera.chat:6697/composer", "issues": "https://github.com/composer/composer/issues", "security": "https://github.com/composer/composer/security/policy", - "source": "https://github.com/composer/composer/tree/2.8.12" + "source": "https://github.com/composer/composer/tree/2.9.2" }, "funding": [ { @@ -255,7 +258,7 @@ "type": "github" } ], - "time": "2025-09-19T11:41:59+00:00" + "time": "2025-11-19T20:57:25+00:00" }, { "name": "composer/metadata-minifier", @@ -411,21 +414,21 @@ "source": { "type": "git", "url": "https://github.com/composer/satis.git", - "reference": "d5f4538e28249d02f27df1ca4f53c90f2d827322" + "reference": "6b515891070938ada62b575311ab5bb33d86d084" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/satis/zipball/d5f4538e28249d02f27df1ca4f53c90f2d827322", - "reference": "d5f4538e28249d02f27df1ca4f53c90f2d827322", + "url": "https://api.github.com/repos/composer/satis/zipball/6b515891070938ada62b575311ab5bb33d86d084", + "reference": "6b515891070938ada62b575311ab5bb33d86d084", "shasum": "" }, "require": { "composer-plugin-api": "^2.3.0", - "composer/composer": "^2.8.12", + "composer/composer": "^2.9.2", "ext-zip": "*", - "symfony/console": "^7.3.4", - "twig/html-extra": "^3.21.0", - "twig/twig": "^3.21.1" + "symfony/console": "^7.3.6", + "twig/html-extra": "^3.22.1", + "twig/twig": "^3.22.0" }, "require-dev": { "ext-json": "*", @@ -490,7 +493,7 @@ "type": "custom" } ], - "time": "2025-10-28T13:10:05+00:00" + "time": "2025-11-24T14:06:02+00:00" }, { "name": "composer/semver", @@ -990,28 +993,28 @@ }, { "name": "incenteev/composer-parameter-handler", - "version": "v2.2.0", + "version": "v2.3.0", "source": { "type": "git", "url": "https://github.com/Incenteev/ParameterHandler.git", - "reference": "90bffce926e96b365579a2ef024aab457f4b80c5" + "reference": "4d9a7c72322daaa11343fb885c15dc3fa096e3ac" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Incenteev/ParameterHandler/zipball/90bffce926e96b365579a2ef024aab457f4b80c5", - "reference": "90bffce926e96b365579a2ef024aab457f4b80c5", + "url": "https://api.github.com/repos/Incenteev/ParameterHandler/zipball/4d9a7c72322daaa11343fb885c15dc3fa096e3ac", + "reference": "4d9a7c72322daaa11343fb885c15dc3fa096e3ac", "shasum": "" }, "require": { "php": ">=7.4", - "symfony/yaml": "^5.4 || ^6.0 || ^7.0" + "symfony/yaml": "^5.4 || ^6.0 || ^7.0 || ^8.0" }, "require-dev": { "composer/composer": "^2.0@dev", "phpspec/prophecy-phpunit": "^2.1", "phpunit/phpunit": "^9.6", - "symfony/filesystem": "^5.4 || ^6.0 || ^7.0", - "symfony/phpunit-bridge": "^6.4.1 || ^7.0.1" + "symfony/filesystem": "^5.4 || ^6.0 || ^7.0 || ^8.0", + "symfony/phpunit-bridge": "^6.4.1 || ^7.0.1 || ^8.0" }, "type": "library", "extra": { @@ -1041,22 +1044,22 @@ ], "support": { "issues": "https://github.com/Incenteev/ParameterHandler/issues", - "source": "https://github.com/Incenteev/ParameterHandler/tree/v2.2.0" + "source": "https://github.com/Incenteev/ParameterHandler/tree/v2.3.0" }, - "time": "2023-12-09T10:31:14+00:00" + "time": "2025-11-26T14:10:46+00:00" }, { "name": "justinrainbow/json-schema", - "version": "6.6.0", + "version": "6.6.1", "source": { "type": "git", "url": "https://github.com/jsonrainbow/json-schema.git", - "reference": "68ba7677532803cc0c5900dd5a4d730537f2b2f3" + "reference": "fd8e5c6b1badb998844ad34ce0abcd71a0aeb396" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/jsonrainbow/json-schema/zipball/68ba7677532803cc0c5900dd5a4d730537f2b2f3", - "reference": "68ba7677532803cc0c5900dd5a4d730537f2b2f3", + "url": "https://api.github.com/repos/jsonrainbow/json-schema/zipball/fd8e5c6b1badb998844ad34ce0abcd71a0aeb396", + "reference": "fd8e5c6b1badb998844ad34ce0abcd71a0aeb396", "shasum": "" }, "require": { @@ -1116,9 +1119,9 @@ ], "support": { "issues": "https://github.com/jsonrainbow/json-schema/issues", - "source": "https://github.com/jsonrainbow/json-schema/tree/6.6.0" + "source": "https://github.com/jsonrainbow/json-schema/tree/6.6.1" }, - "time": "2025-10-10T11:34:09+00:00" + "time": "2025-11-07T18:30:29+00:00" }, { "name": "laminas/laminas-diactoros", @@ -1476,16 +1479,16 @@ }, { "name": "phpdocumentor/reflection-docblock", - "version": "5.6.3", + "version": "5.6.4", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "94f8051919d1b0369a6bcc7931d679a511c03fe9" + "reference": "90a04bcbf03784066f16038e87e23a0a83cee3c2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/94f8051919d1b0369a6bcc7931d679a511c03fe9", - "reference": "94f8051919d1b0369a6bcc7931d679a511c03fe9", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/90a04bcbf03784066f16038e87e23a0a83cee3c2", + "reference": "90a04bcbf03784066f16038e87e23a0a83cee3c2", "shasum": "" }, "require": { @@ -1534,22 +1537,22 @@ "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.3" + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.6.4" }, - "time": "2025-08-01T19:43:32+00:00" + "time": "2025-11-17T21:13:10+00:00" }, { "name": "phpdocumentor/type-resolver", - "version": "1.10.0", + "version": "1.12.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "679e3ce485b99e84c775d28e2e96fade9a7fb50a" + "reference": "92a98ada2b93d9b201a613cb5a33584dde25f195" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/679e3ce485b99e84c775d28e2e96fade9a7fb50a", - "reference": "679e3ce485b99e84c775d28e2e96fade9a7fb50a", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/92a98ada2b93d9b201a613cb5a33584dde25f195", + "reference": "92a98ada2b93d9b201a613cb5a33584dde25f195", "shasum": "" }, "require": { @@ -1592,27 +1595,27 @@ "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" + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.12.0" }, - "time": "2024-11-09T15:12:26+00:00" + "time": "2025-11-21T15:09:14+00:00" }, { "name": "phpspec/prophecy", - "version": "v1.23.1", + "version": "v1.24.0", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "9500f939e4b22c40c3d5cca5f10837f2a9c87cb0" + "reference": "a24f1bda2d00a03877f7f99d9e6b150baf543f6d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/9500f939e4b22c40c3d5cca5f10837f2a9c87cb0", - "reference": "9500f939e4b22c40c3d5cca5f10837f2a9c87cb0", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/a24f1bda2d00a03877f7f99d9e6b150baf543f6d", + "reference": "a24f1bda2d00a03877f7f99d9e6b150baf543f6d", "shasum": "" }, "require": { "doctrine/instantiator": "^1.2 || ^2.0", - "php": "8.2.* || 8.3.* || 8.4.*", + "php": "8.2.* || 8.3.* || 8.4.* || 8.5.*", "phpdocumentor/reflection-docblock": "^5.2", "sebastian/comparator": "^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0", "sebastian/recursion-context": "^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0", @@ -1663,9 +1666,9 @@ ], "support": { "issues": "https://github.com/phpspec/prophecy/issues", - "source": "https://github.com/phpspec/prophecy/tree/v1.23.1" + "source": "https://github.com/phpspec/prophecy/tree/v1.24.0" }, - "time": "2025-10-27T22:44:31+00:00" + "time": "2025-11-21T13:10:52+00:00" }, { "name": "phpstan/phpdoc-parser", @@ -2814,16 +2817,16 @@ }, { "name": "symfony/cache", - "version": "v7.3.5", + "version": "v7.3.6", "source": { "type": "git", "url": "https://github.com/symfony/cache.git", - "reference": "4a55feb59664f49042a0824c0f955e2f4c1412ad" + "reference": "1277a1ec61c8d93ea61b2a59738f1deb9bfb6701" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/cache/zipball/4a55feb59664f49042a0824c0f955e2f4c1412ad", - "reference": "4a55feb59664f49042a0824c0f955e2f4c1412ad", + "url": "https://api.github.com/repos/symfony/cache/zipball/1277a1ec61c8d93ea61b2a59738f1deb9bfb6701", + "reference": "1277a1ec61c8d93ea61b2a59738f1deb9bfb6701", "shasum": "" }, "require": { @@ -2892,7 +2895,7 @@ "psr6" ], "support": { - "source": "https://github.com/symfony/cache/tree/v7.3.5" + "source": "https://github.com/symfony/cache/tree/v7.3.6" }, "funding": [ { @@ -2912,7 +2915,7 @@ "type": "tidelift" } ], - "time": "2025-10-16T13:55:38+00:00" + "time": "2025-10-30T13:22:58+00:00" }, { "name": "symfony/cache-contracts", @@ -3066,16 +3069,16 @@ }, { "name": "symfony/config", - "version": "v7.3.4", + "version": "v7.3.6", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "8a09223170046d2cfda3d2e11af01df2c641e961" + "reference": "9d18eba95655a3152ae4c1d53c6cc34eb4d4a0b7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/8a09223170046d2cfda3d2e11af01df2c641e961", - "reference": "8a09223170046d2cfda3d2e11af01df2c641e961", + "url": "https://api.github.com/repos/symfony/config/zipball/9d18eba95655a3152ae4c1d53c6cc34eb4d4a0b7", + "reference": "9d18eba95655a3152ae4c1d53c6cc34eb4d4a0b7", "shasum": "" }, "require": { @@ -3121,7 +3124,7 @@ "description": "Helps you find, load, combine, autofill and validate configuration values of any kind", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/config/tree/v7.3.4" + "source": "https://github.com/symfony/config/tree/v7.3.6" }, "funding": [ { @@ -3141,20 +3144,20 @@ "type": "tidelift" } ], - "time": "2025-09-22T12:46:16+00:00" + "time": "2025-11-02T08:04:43+00:00" }, { "name": "symfony/console", - "version": "v7.3.5", + "version": "v7.3.6", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "cdb80fa5869653c83cfe1a9084a673b6daf57ea7" + "reference": "c28ad91448f86c5f6d9d2c70f0cf68bf135f252a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/cdb80fa5869653c83cfe1a9084a673b6daf57ea7", - "reference": "cdb80fa5869653c83cfe1a9084a673b6daf57ea7", + "url": "https://api.github.com/repos/symfony/console/zipball/c28ad91448f86c5f6d9d2c70f0cf68bf135f252a", + "reference": "c28ad91448f86c5f6d9d2c70f0cf68bf135f252a", "shasum": "" }, "require": { @@ -3219,7 +3222,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.3.5" + "source": "https://github.com/symfony/console/tree/v7.3.6" }, "funding": [ { @@ -3239,20 +3242,20 @@ "type": "tidelift" } ], - "time": "2025-10-14T15:46:26+00:00" + "time": "2025-11-04T01:21:42+00:00" }, { "name": "symfony/dependency-injection", - "version": "v7.3.4", + "version": "v7.3.6", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "82119812ab0bf3425c1234d413efd1b19bb92ae4" + "reference": "98af8bb46c56aedd9dd5a7f0414fc72bf2dcfe69" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/82119812ab0bf3425c1234d413efd1b19bb92ae4", - "reference": "82119812ab0bf3425c1234d413efd1b19bb92ae4", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/98af8bb46c56aedd9dd5a7f0414fc72bf2dcfe69", + "reference": "98af8bb46c56aedd9dd5a7f0414fc72bf2dcfe69", "shasum": "" }, "require": { @@ -3303,7 +3306,7 @@ "description": "Allows you to standardize and centralize the way objects are constructed in your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dependency-injection/tree/v7.3.4" + "source": "https://github.com/symfony/dependency-injection/tree/v7.3.6" }, "funding": [ { @@ -3323,7 +3326,7 @@ "type": "tidelift" } ], - "time": "2025-09-11T10:12:26+00:00" + "time": "2025-10-31T10:11:11+00:00" }, { "name": "symfony/deprecation-contracts", @@ -3472,16 +3475,16 @@ }, { "name": "symfony/error-handler", - "version": "v7.3.4", + "version": "v7.3.6", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "99f81bc944ab8e5dae4f21b4ca9972698bbad0e4" + "reference": "bbe40bfab84323d99dab491b716ff142410a92a8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/99f81bc944ab8e5dae4f21b4ca9972698bbad0e4", - "reference": "99f81bc944ab8e5dae4f21b4ca9972698bbad0e4", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/bbe40bfab84323d99dab491b716ff142410a92a8", + "reference": "bbe40bfab84323d99dab491b716ff142410a92a8", "shasum": "" }, "require": { @@ -3529,7 +3532,7 @@ "description": "Provides tools to manage errors and ease debugging PHP code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/error-handler/tree/v7.3.4" + "source": "https://github.com/symfony/error-handler/tree/v7.3.6" }, "funding": [ { @@ -3549,7 +3552,7 @@ "type": "tidelift" } ], - "time": "2025-09-11T10:12:26+00:00" + "time": "2025-10-31T19:12:50+00:00" }, { "name": "symfony/event-dispatcher", @@ -3713,16 +3716,16 @@ }, { "name": "symfony/filesystem", - "version": "v7.3.2", + "version": "v7.3.6", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "edcbb768a186b5c3f25d0643159a787d3e63b7fd" + "reference": "e9bcfd7837928ab656276fe00464092cc9e1826a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/edcbb768a186b5c3f25d0643159a787d3e63b7fd", - "reference": "edcbb768a186b5c3f25d0643159a787d3e63b7fd", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/e9bcfd7837928ab656276fe00464092cc9e1826a", + "reference": "e9bcfd7837928ab656276fe00464092cc9e1826a", "shasum": "" }, "require": { @@ -3759,7 +3762,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v7.3.2" + "source": "https://github.com/symfony/filesystem/tree/v7.3.6" }, "funding": [ { @@ -3779,7 +3782,7 @@ "type": "tidelift" } ], - "time": "2025-07-07T08:17:47+00:00" + "time": "2025-11-05T09:52:27+00:00" }, { "name": "symfony/finder", @@ -3851,31 +3854,32 @@ }, { "name": "symfony/flex", - "version": "v2.8.2", + "version": "v2.10.0", "source": { "type": "git", "url": "https://github.com/symfony/flex.git", - "reference": "f356aa35f3cf3d2f46c31d344c1098eb2d260426" + "reference": "9cd384775973eabbf6e8b05784dda279fc67c28d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/flex/zipball/f356aa35f3cf3d2f46c31d344c1098eb2d260426", - "reference": "f356aa35f3cf3d2f46c31d344c1098eb2d260426", + "url": "https://api.github.com/repos/symfony/flex/zipball/9cd384775973eabbf6e8b05784dda279fc67c28d", + "reference": "9cd384775973eabbf6e8b05784dda279fc67c28d", "shasum": "" }, "require": { "composer-plugin-api": "^2.1", - "php": ">=8.0" + "php": ">=8.1" }, "conflict": { - "composer/semver": "<1.7.2" + "composer/semver": "<1.7.2", + "symfony/dotenv": "<5.4" }, "require-dev": { "composer/composer": "^2.1", - "symfony/dotenv": "^5.4|^6.0", - "symfony/filesystem": "^5.4|^6.0", - "symfony/phpunit-bridge": "^5.4|^6.0", - "symfony/process": "^5.4|^6.0" + "symfony/dotenv": "^6.4|^7.4|^8.0", + "symfony/filesystem": "^6.4|^7.4|^8.0", + "symfony/phpunit-bridge": "^6.4|^7.4|^8.0", + "symfony/process": "^6.4|^7.4|^8.0" }, "type": "composer-plugin", "extra": { @@ -3899,7 +3903,7 @@ "description": "Composer plugin for Symfony", "support": { "issues": "https://github.com/symfony/flex/issues", - "source": "https://github.com/symfony/flex/tree/v2.8.2" + "source": "https://github.com/symfony/flex/tree/v2.10.0" }, "funding": [ { @@ -3919,20 +3923,20 @@ "type": "tidelift" } ], - "time": "2025-08-22T07:17:23+00:00" + "time": "2025-11-16T09:38:19+00:00" }, { "name": "symfony/form", - "version": "v7.3.5", + "version": "v7.3.6", "source": { "type": "git", "url": "https://github.com/symfony/form.git", - "reference": "c8032766d5b198865d00b7d4471fc787a0a7f5e4" + "reference": "a8f32aa19b322bf46cbaaafa89c132eb662ecfe5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/form/zipball/c8032766d5b198865d00b7d4471fc787a0a7f5e4", - "reference": "c8032766d5b198865d00b7d4471fc787a0a7f5e4", + "url": "https://api.github.com/repos/symfony/form/zipball/a8f32aa19b322bf46cbaaafa89c132eb662ecfe5", + "reference": "a8f32aa19b322bf46cbaaafa89c132eb662ecfe5", "shasum": "" }, "require": { @@ -4000,7 +4004,7 @@ "description": "Allows to easily create, process and reuse HTML forms", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/form/tree/v7.3.5" + "source": "https://github.com/symfony/form/tree/v7.3.6" }, "funding": [ { @@ -4020,20 +4024,20 @@ "type": "tidelift" } ], - "time": "2025-10-10T11:54:12+00:00" + "time": "2025-10-31T09:25:04+00:00" }, { "name": "symfony/framework-bundle", - "version": "v7.3.5", + "version": "v7.3.6", "source": { "type": "git", "url": "https://github.com/symfony/framework-bundle.git", - "reference": "ebd42b1fc2652b96d33520195ea0f6e55c36f09d" + "reference": "cabfdfa82bc4f75d693a329fe263d96937636b77" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/ebd42b1fc2652b96d33520195ea0f6e55c36f09d", - "reference": "ebd42b1fc2652b96d33520195ea0f6e55c36f09d", + "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/cabfdfa82bc4f75d693a329fe263d96937636b77", + "reference": "cabfdfa82bc4f75d693a329fe263d96937636b77", "shasum": "" }, "require": { @@ -4158,7 +4162,7 @@ "description": "Provides a tight integration between Symfony components and the Symfony full-stack framework", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/framework-bundle/tree/v7.3.5" + "source": "https://github.com/symfony/framework-bundle/tree/v7.3.6" }, "funding": [ { @@ -4178,20 +4182,20 @@ "type": "tidelift" } ], - "time": "2025-10-16T16:16:53+00:00" + "time": "2025-10-30T09:42:24+00:00" }, { "name": "symfony/http-foundation", - "version": "v7.3.5", + "version": "v7.3.7", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "ce31218c7cac92eab280762c4375fb70a6f4f897" + "reference": "db488a62f98f7a81d5746f05eea63a74e55bb7c4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/ce31218c7cac92eab280762c4375fb70a6f4f897", - "reference": "ce31218c7cac92eab280762c4375fb70a6f4f897", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/db488a62f98f7a81d5746f05eea63a74e55bb7c4", + "reference": "db488a62f98f7a81d5746f05eea63a74e55bb7c4", "shasum": "" }, "require": { @@ -4241,7 +4245,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v7.3.5" + "source": "https://github.com/symfony/http-foundation/tree/v7.3.7" }, "funding": [ { @@ -4261,20 +4265,20 @@ "type": "tidelift" } ], - "time": "2025-10-24T21:42:11+00:00" + "time": "2025-11-08T16:41:12+00:00" }, { "name": "symfony/http-kernel", - "version": "v7.3.5", + "version": "v7.3.7", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "24fd3f123532e26025f49f1abefcc01a69ef15ab" + "reference": "10b8e9b748ea95fa4539c208e2487c435d3c87ce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/24fd3f123532e26025f49f1abefcc01a69ef15ab", - "reference": "24fd3f123532e26025f49f1abefcc01a69ef15ab", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/10b8e9b748ea95fa4539c208e2487c435d3c87ce", + "reference": "10b8e9b748ea95fa4539c208e2487c435d3c87ce", "shasum": "" }, "require": { @@ -4359,7 +4363,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v7.3.5" + "source": "https://github.com/symfony/http-kernel/tree/v7.3.7" }, "funding": [ { @@ -4379,7 +4383,7 @@ "type": "tidelift" } ], - "time": "2025-10-28T10:19:01+00:00" + "time": "2025-11-12T11:38:40+00:00" }, { "name": "symfony/lock", @@ -4465,16 +4469,16 @@ }, { "name": "symfony/messenger", - "version": "v7.3.3", + "version": "v7.3.6", "source": { "type": "git", "url": "https://github.com/symfony/messenger.git", - "reference": "d9e04339404ba2dcd04c24172125516dc0e06c35" + "reference": "58a7efa3bebadbe4cdd8f7577c5856f0e3ea3978" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/messenger/zipball/d9e04339404ba2dcd04c24172125516dc0e06c35", - "reference": "d9e04339404ba2dcd04c24172125516dc0e06c35", + "url": "https://api.github.com/repos/symfony/messenger/zipball/58a7efa3bebadbe4cdd8f7577c5856f0e3ea3978", + "reference": "58a7efa3bebadbe4cdd8f7577c5856f0e3ea3978", "shasum": "" }, "require": { @@ -4534,7 +4538,7 @@ "description": "Helps applications send and receive messages to/from other applications or via message queues", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/messenger/tree/v7.3.3" + "source": "https://github.com/symfony/messenger/tree/v7.3.6" }, "funding": [ { @@ -4554,43 +4558,41 @@ "type": "tidelift" } ], - "time": "2025-08-13T11:49:31+00:00" + "time": "2025-11-06T11:17:34+00:00" }, { "name": "symfony/mime", - "version": "v7.3.4", + "version": "v8.0.0", "source": { "type": "git", "url": "https://github.com/symfony/mime.git", - "reference": "b1b828f69cbaf887fa835a091869e55df91d0e35" + "reference": "7576ce3b2b4d3a2a7fe7020a07a392065d6ffd40" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/b1b828f69cbaf887fa835a091869e55df91d0e35", - "reference": "b1b828f69cbaf887fa835a091869e55df91d0e35", + "url": "https://api.github.com/repos/symfony/mime/zipball/7576ce3b2b4d3a2a7fe7020a07a392065d6ffd40", + "reference": "7576ce3b2b4d3a2a7fe7020a07a392065d6ffd40", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.4", "symfony/polyfill-intl-idn": "^1.10", "symfony/polyfill-mbstring": "^1.0" }, "conflict": { "egulias/email-validator": "~3.0.0", "phpdocumentor/reflection-docblock": "<3.2.2", - "phpdocumentor/type-resolver": "<1.4.0", - "symfony/mailer": "<6.4", - "symfony/serializer": "<6.4.3|>7.0,<7.0.3" + "phpdocumentor/type-resolver": "<1.4.0" }, "require-dev": { "egulias/email-validator": "^2.1.10|^3.1|^4", "league/html-to-markdown": "^5.0", "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/process": "^6.4|^7.0", - "symfony/property-access": "^6.4|^7.0", - "symfony/property-info": "^6.4|^7.0", - "symfony/serializer": "^6.4.3|^7.0.3" + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/process": "^7.4|^8.0", + "symfony/property-access": "^7.4|^8.0", + "symfony/property-info": "^7.4|^8.0", + "symfony/serializer": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -4622,7 +4624,7 @@ "mime-type" ], "support": { - "source": "https://github.com/symfony/mime/tree/v7.3.4" + "source": "https://github.com/symfony/mime/tree/v8.0.0" }, "funding": [ { @@ -4642,20 +4644,20 @@ "type": "tidelift" } ], - "time": "2025-09-16T08:38:17+00:00" + "time": "2025-11-16T10:17:21+00:00" }, { "name": "symfony/monolog-bridge", - "version": "v7.3.5", + "version": "v7.3.6", "source": { "type": "git", "url": "https://github.com/symfony/monolog-bridge.git", - "reference": "c66a65049c75f3ddf03d73c8c9ed61405779ce47" + "reference": "48e8542ba35afd2293a8c8fd4bcf8abe46357ddf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/monolog-bridge/zipball/c66a65049c75f3ddf03d73c8c9ed61405779ce47", - "reference": "c66a65049c75f3ddf03d73c8c9ed61405779ce47", + "url": "https://api.github.com/repos/symfony/monolog-bridge/zipball/48e8542ba35afd2293a8c8fd4bcf8abe46357ddf", + "reference": "48e8542ba35afd2293a8c8fd4bcf8abe46357ddf", "shasum": "" }, "require": { @@ -4704,7 +4706,7 @@ "description": "Provides integration for Monolog with various Symfony components", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/monolog-bridge/tree/v7.3.5" + "source": "https://github.com/symfony/monolog-bridge/tree/v7.3.6" }, "funding": [ { @@ -4724,48 +4726,43 @@ "type": "tidelift" } ], - "time": "2025-10-14T19:16:15+00:00" + "time": "2025-11-01T09:17:24+00:00" }, { "name": "symfony/monolog-bundle", - "version": "v3.10.0", + "version": "v3.11.0", "source": { "type": "git", "url": "https://github.com/symfony/monolog-bundle.git", - "reference": "414f951743f4aa1fd0f5bf6a0e9c16af3fe7f181" + "reference": "e12eb92655b234cd50c21cda648088847a7ec777" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/monolog-bundle/zipball/414f951743f4aa1fd0f5bf6a0e9c16af3fe7f181", - "reference": "414f951743f4aa1fd0f5bf6a0e9c16af3fe7f181", + "url": "https://api.github.com/repos/symfony/monolog-bundle/zipball/e12eb92655b234cd50c21cda648088847a7ec777", + "reference": "e12eb92655b234cd50c21cda648088847a7ec777", "shasum": "" }, "require": { + "composer-runtime-api": "^2.0", "monolog/monolog": "^1.25.1 || ^2.0 || ^3.0", - "php": ">=7.2.5", - "symfony/config": "^5.4 || ^6.0 || ^7.0", - "symfony/dependency-injection": "^5.4 || ^6.0 || ^7.0", - "symfony/http-kernel": "^5.4 || ^6.0 || ^7.0", - "symfony/monolog-bridge": "^5.4 || ^6.0 || ^7.0" + "php": ">=8.1", + "symfony/config": "^6.4 || ^7.0", + "symfony/dependency-injection": "^6.4 || ^7.0", + "symfony/deprecation-contracts": "^2.5 || ^3.0", + "symfony/http-kernel": "^6.4 || ^7.0", + "symfony/monolog-bridge": "^6.4 || ^7.0", + "symfony/polyfill-php84": "^1.30" }, "require-dev": { - "symfony/console": "^5.4 || ^6.0 || ^7.0", - "symfony/phpunit-bridge": "^6.3 || ^7.0", - "symfony/yaml": "^5.4 || ^6.0 || ^7.0" + "symfony/console": "^6.4 || ^7.0", + "symfony/phpunit-bridge": "^7.3.3", + "symfony/yaml": "^6.4 || ^7.0" }, "type": "symfony-bundle", - "extra": { - "branch-alias": { - "dev-master": "3.x-dev" - } - }, "autoload": { "psr-4": { - "Symfony\\Bundle\\MonologBundle\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "Symfony\\Bundle\\MonologBundle\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -4789,7 +4786,7 @@ ], "support": { "issues": "https://github.com/symfony/monolog-bundle/issues", - "source": "https://github.com/symfony/monolog-bundle/tree/v3.10.0" + "source": "https://github.com/symfony/monolog-bundle/tree/v3.11.0" }, "funding": [ { @@ -4800,12 +4797,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2023-11-06T17:08:13+00:00" + "time": "2025-11-27T09:16:19+00:00" }, { "name": "symfony/options-resolver", @@ -5866,20 +5867,20 @@ }, { "name": "symfony/process", - "version": "v7.3.4", + "version": "v8.0.0", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "f24f8f316367b30810810d4eb30c543d7003ff3b" + "reference": "a0a750500c4ce900d69ba4e9faf16f82c10ee149" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/f24f8f316367b30810810d4eb30c543d7003ff3b", - "reference": "f24f8f316367b30810810d4eb30c543d7003ff3b", + "url": "https://api.github.com/repos/symfony/process/zipball/a0a750500c4ce900d69ba4e9faf16f82c10ee149", + "reference": "a0a750500c4ce900d69ba4e9faf16f82c10ee149", "shasum": "" }, "require": { - "php": ">=8.2" + "php": ">=8.4" }, "type": "library", "autoload": { @@ -5907,7 +5908,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.3.4" + "source": "https://github.com/symfony/process/tree/v8.0.0" }, "funding": [ { @@ -5927,7 +5928,7 @@ "type": "tidelift" } ], - "time": "2025-09-11T10:12:26+00:00" + "time": "2025-10-16T16:25:44+00:00" }, { "name": "symfony/property-access", @@ -6249,16 +6250,16 @@ }, { "name": "symfony/routing", - "version": "v7.3.4", + "version": "v7.3.6", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "8dc648e159e9bac02b703b9fbd937f19ba13d07c" + "reference": "c97abe725f2a1a858deca629a6488c8fc20c3091" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/8dc648e159e9bac02b703b9fbd937f19ba13d07c", - "reference": "8dc648e159e9bac02b703b9fbd937f19ba13d07c", + "url": "https://api.github.com/repos/symfony/routing/zipball/c97abe725f2a1a858deca629a6488c8fc20c3091", + "reference": "c97abe725f2a1a858deca629a6488c8fc20c3091", "shasum": "" }, "require": { @@ -6310,7 +6311,7 @@ "url" ], "support": { - "source": "https://github.com/symfony/routing/tree/v7.3.4" + "source": "https://github.com/symfony/routing/tree/v7.3.6" }, "funding": [ { @@ -6330,7 +6331,7 @@ "type": "tidelift" } ], - "time": "2025-09-11T10:12:26+00:00" + "time": "2025-11-05T07:57:47+00:00" }, { "name": "symfony/runtime", @@ -6883,16 +6884,16 @@ }, { "name": "symfony/service-contracts", - "version": "v3.6.0", + "version": "v3.6.1", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4" + "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f021b05a130d35510bd6b25fe9053c2a8a15d5d4", - "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/45112560a3ba2d715666a509a0bc9521d10b6c43", + "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43", "shasum": "" }, "require": { @@ -6946,7 +6947,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.6.0" + "source": "https://github.com/symfony/service-contracts/tree/v3.6.1" }, "funding": [ { @@ -6957,12 +6958,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-04-25T09:37:31+00:00" + "time": "2025-07-15T11:30:57+00:00" }, { "name": "symfony/string", @@ -7056,16 +7061,16 @@ }, { "name": "symfony/translation-contracts", - "version": "v3.6.0", + "version": "v3.6.1", "source": { "type": "git", "url": "https://github.com/symfony/translation-contracts.git", - "reference": "df210c7a2573f1913b2d17cc95f90f53a73d8f7d" + "reference": "65a8bc82080447fae78373aa10f8d13b38338977" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/df210c7a2573f1913b2d17cc95f90f53a73d8f7d", - "reference": "df210c7a2573f1913b2d17cc95f90f53a73d8f7d", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/65a8bc82080447fae78373aa10f8d13b38338977", + "reference": "65a8bc82080447fae78373aa10f8d13b38338977", "shasum": "" }, "require": { @@ -7114,7 +7119,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/translation-contracts/tree/v3.6.0" + "source": "https://github.com/symfony/translation-contracts/tree/v3.6.1" }, "funding": [ { @@ -7125,25 +7130,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-27T08:32:26+00:00" + "time": "2025-07-15T13:41:35+00:00" }, { "name": "symfony/twig-bridge", - "version": "v7.3.3", + "version": "v7.3.6", "source": { "type": "git", "url": "https://github.com/symfony/twig-bridge.git", - "reference": "33558f013b7f6ed72805527c8405cae0062e47c5" + "reference": "d1aaec8eee1f5591f56b9efe00194d73a8e38319" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/33558f013b7f6ed72805527c8405cae0062e47c5", - "reference": "33558f013b7f6ed72805527c8405cae0062e47c5", + "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/d1aaec8eee1f5591f56b9efe00194d73a8e38319", + "reference": "d1aaec8eee1f5591f56b9efe00194d73a8e38319", "shasum": "" }, "require": { @@ -7225,7 +7234,7 @@ "description": "Provides integration for Twig with various Symfony components", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/twig-bridge/tree/v7.3.3" + "source": "https://github.com/symfony/twig-bridge/tree/v7.3.6" }, "funding": [ { @@ -7245,7 +7254,7 @@ "type": "tidelift" } ], - "time": "2025-08-18T13:10:53+00:00" + "time": "2025-11-04T15:37:51+00:00" }, { "name": "symfony/twig-bundle", @@ -7420,16 +7429,16 @@ }, { "name": "symfony/validator", - "version": "v7.3.5", + "version": "v7.3.7", "source": { "type": "git", "url": "https://github.com/symfony/validator.git", - "reference": "724086992fb7c7882d05c9d2219d70401ab9fdda" + "reference": "8290a095497c3fe5046db21888d1f75b54ddf39d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/validator/zipball/724086992fb7c7882d05c9d2219d70401ab9fdda", - "reference": "724086992fb7c7882d05c9d2219d70401ab9fdda", + "url": "https://api.github.com/repos/symfony/validator/zipball/8290a095497c3fe5046db21888d1f75b54ddf39d", + "reference": "8290a095497c3fe5046db21888d1f75b54ddf39d", "shasum": "" }, "require": { @@ -7498,7 +7507,7 @@ "description": "Provides tools to validate values", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/validator/tree/v7.3.5" + "source": "https://github.com/symfony/validator/tree/v7.3.7" }, "funding": [ { @@ -7518,7 +7527,7 @@ "type": "tidelift" } ], - "time": "2025-10-24T14:27:20+00:00" + "time": "2025-11-08T16:29:29+00:00" }, { "name": "symfony/var-dumper", @@ -7838,22 +7847,22 @@ }, { "name": "twig/html-extra", - "version": "v3.21.0", + "version": "v3.22.1", "source": { "type": "git", "url": "https://github.com/twigphp/html-extra.git", - "reference": "5442dd707601c83b8cd4233e37bb10ab8489a90f" + "reference": "d56d33315bce2b19ed815f8feedce85448736568" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/html-extra/zipball/5442dd707601c83b8cd4233e37bb10ab8489a90f", - "reference": "5442dd707601c83b8cd4233e37bb10ab8489a90f", + "url": "https://api.github.com/repos/twigphp/html-extra/zipball/d56d33315bce2b19ed815f8feedce85448736568", + "reference": "d56d33315bce2b19ed815f8feedce85448736568", "shasum": "" }, "require": { "php": ">=8.1.0", "symfony/deprecation-contracts": "^2.5|^3", - "symfony/mime": "^5.4|^6.4|^7.0", + "symfony/mime": "^5.4|^6.4|^7.0|^8.0", "twig/twig": "^3.13|^4.0" }, "require-dev": { @@ -7890,7 +7899,7 @@ "twig" ], "support": { - "source": "https://github.com/twigphp/html-extra/tree/v3.21.0" + "source": "https://github.com/twigphp/html-extra/tree/v3.22.1" }, "funding": [ { @@ -7902,20 +7911,20 @@ "type": "tidelift" } ], - "time": "2025-02-19T14:29:33+00:00" + "time": "2025-11-02T11:00:49+00:00" }, { "name": "twig/twig", - "version": "v3.21.1", + "version": "v3.22.0", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "285123877d4dd97dd7c11842ac5fb7e86e60d81d" + "reference": "4509984193026de413baf4ba80f68590a7f2c51d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/285123877d4dd97dd7c11842ac5fb7e86e60d81d", - "reference": "285123877d4dd97dd7c11842ac5fb7e86e60d81d", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/4509984193026de413baf4ba80f68590a7f2c51d", + "reference": "4509984193026de413baf4ba80f68590a7f2c51d", "shasum": "" }, "require": { @@ -7969,7 +7978,7 @@ ], "support": { "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.21.1" + "source": "https://github.com/twigphp/Twig/tree/v3.22.0" }, "funding": [ { @@ -7981,20 +7990,20 @@ "type": "tidelift" } ], - "time": "2025-05-03T07:21:55+00:00" + "time": "2025-10-29T15:56:47+00:00" }, { "name": "webmozart/assert", - "version": "1.12.0", + "version": "1.12.1", "source": { "type": "git", "url": "https://github.com/webmozarts/assert.git", - "reference": "541057574806f942c94662b817a50f63f7345360" + "reference": "9be6926d8b485f55b9229203f962b51ed377ba68" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/541057574806f942c94662b817a50f63f7345360", - "reference": "541057574806f942c94662b817a50f63f7345360", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/9be6926d8b485f55b9229203f962b51ed377ba68", + "reference": "9be6926d8b485f55b9229203f962b51ed377ba68", "shasum": "" }, "require": { @@ -8037,9 +8046,9 @@ ], "support": { "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.12.0" + "source": "https://github.com/webmozarts/assert/tree/1.12.1" }, - "time": "2025-10-20T12:43:39+00:00" + "time": "2025-10-29T15:56:20+00:00" } ], "packages-dev": [ @@ -8307,16 +8316,16 @@ }, { "name": "friendsofphp/php-cs-fixer", - "version": "v3.89.1", + "version": "v3.90.0", "source": { "type": "git", "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", - "reference": "f34967da2866ace090a2b447de1f357356474573" + "reference": "ad732c2e9299c9743f9c55ae53cc0e7642ab1155" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/f34967da2866ace090a2b447de1f357356474573", - "reference": "f34967da2866ace090a2b447de1f357356474573", + "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/ad732c2e9299c9743f9c55ae53cc0e7642ab1155", + "reference": "ad732c2e9299c9743f9c55ae53cc0e7642ab1155", "shasum": "" }, "require": { @@ -8334,17 +8343,17 @@ "react/socket": "^1.16", "react/stream": "^1.4", "sebastian/diff": "^4.0.6 || ^5.1.1 || ^6.0.2 || ^7.0", - "symfony/console": "^5.4.47 || ^6.4.24 || ^7.0", - "symfony/event-dispatcher": "^5.4.45 || ^6.4.24 || ^7.0", - "symfony/filesystem": "^5.4.45 || ^6.4.24 || ^7.0", - "symfony/finder": "^5.4.45 || ^6.4.24 || ^7.0", - "symfony/options-resolver": "^5.4.45 || ^6.4.24 || ^7.0", + "symfony/console": "^5.4.47 || ^6.4.24 || ^7.0 || ^8.0", + "symfony/event-dispatcher": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0", + "symfony/filesystem": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0", + "symfony/finder": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0", + "symfony/options-resolver": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0", "symfony/polyfill-mbstring": "^1.33", "symfony/polyfill-php80": "^1.33", "symfony/polyfill-php81": "^1.33", "symfony/polyfill-php84": "^1.33", - "symfony/process": "^5.4.47 || ^6.4.24 || ^7.2", - "symfony/stopwatch": "^5.4.45 || ^6.4.24 || ^7.0" + "symfony/process": "^5.4.47 || ^6.4.24 || ^7.2 || ^8.0", + "symfony/stopwatch": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0" }, "require-dev": { "facile-it/paraunit": "^1.3.1 || ^2.7", @@ -8352,12 +8361,12 @@ "justinrainbow/json-schema": "^6.5", "keradus/cli-executor": "^2.2", "mikey179/vfsstream": "^1.6.12", - "php-coveralls/php-coveralls": "^2.8", + "php-coveralls/php-coveralls": "^2.9", "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.6", "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.6", "phpunit/phpunit": "^9.6.25 || ^10.5.53 || ^11.5.34", - "symfony/var-dumper": "^5.4.48 || ^6.4.24 || ^7.3.2", - "symfony/yaml": "^5.4.45 || ^6.4.24 || ^7.3.2" + "symfony/var-dumper": "^5.4.48 || ^6.4.24 || ^7.3.2 || ^8.0", + "symfony/yaml": "^5.4.45 || ^6.4.24 || ^7.3.2 || ^8.0" }, "suggest": { "ext-dom": "For handling output formats in XML", @@ -8398,7 +8407,7 @@ ], "support": { "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", - "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.89.1" + "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.90.0" }, "funding": [ { @@ -8406,7 +8415,7 @@ "type": "github" } ], - "time": "2025-10-24T12:05:10+00:00" + "time": "2025-11-20T15:15:16+00:00" }, { "name": "masterminds/html5", @@ -9397,16 +9406,16 @@ }, { "name": "react/dns", - "version": "v1.13.0", + "version": "v1.14.0", "source": { "type": "git", "url": "https://github.com/reactphp/dns.git", - "reference": "eb8ae001b5a455665c89c1df97f6fb682f8fb0f5" + "reference": "7562c05391f42701c1fccf189c8225fece1cd7c3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/dns/zipball/eb8ae001b5a455665c89c1df97f6fb682f8fb0f5", - "reference": "eb8ae001b5a455665c89c1df97f6fb682f8fb0f5", + "url": "https://api.github.com/repos/reactphp/dns/zipball/7562c05391f42701c1fccf189c8225fece1cd7c3", + "reference": "7562c05391f42701c1fccf189c8225fece1cd7c3", "shasum": "" }, "require": { @@ -9461,7 +9470,7 @@ ], "support": { "issues": "https://github.com/reactphp/dns/issues", - "source": "https://github.com/reactphp/dns/tree/v1.13.0" + "source": "https://github.com/reactphp/dns/tree/v1.14.0" }, "funding": [ { @@ -9469,20 +9478,20 @@ "type": "open_collective" } ], - "time": "2024-06-13T14:18:03+00:00" + "time": "2025-11-18T19:34:28+00:00" }, { "name": "react/event-loop", - "version": "v1.5.0", + "version": "v1.6.0", "source": { "type": "git", "url": "https://github.com/reactphp/event-loop.git", - "reference": "bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354" + "reference": "ba276bda6083df7e0050fd9b33f66ad7a4ac747a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/event-loop/zipball/bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354", - "reference": "bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354", + "url": "https://api.github.com/repos/reactphp/event-loop/zipball/ba276bda6083df7e0050fd9b33f66ad7a4ac747a", + "reference": "ba276bda6083df7e0050fd9b33f66ad7a4ac747a", "shasum": "" }, "require": { @@ -9533,7 +9542,7 @@ ], "support": { "issues": "https://github.com/reactphp/event-loop/issues", - "source": "https://github.com/reactphp/event-loop/tree/v1.5.0" + "source": "https://github.com/reactphp/event-loop/tree/v1.6.0" }, "funding": [ { @@ -9541,20 +9550,20 @@ "type": "open_collective" } ], - "time": "2023-11-13T13:48:05+00:00" + "time": "2025-11-17T20:46:25+00:00" }, { "name": "react/socket", - "version": "v1.16.0", + "version": "v1.17.0", "source": { "type": "git", "url": "https://github.com/reactphp/socket.git", - "reference": "23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1" + "reference": "ef5b17b81f6f60504c539313f94f2d826c5faa08" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/socket/zipball/23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1", - "reference": "23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1", + "url": "https://api.github.com/repos/reactphp/socket/zipball/ef5b17b81f6f60504c539313f94f2d826c5faa08", + "reference": "ef5b17b81f6f60504c539313f94f2d826c5faa08", "shasum": "" }, "require": { @@ -9613,7 +9622,7 @@ ], "support": { "issues": "https://github.com/reactphp/socket/issues", - "source": "https://github.com/reactphp/socket/tree/v1.16.0" + "source": "https://github.com/reactphp/socket/tree/v1.17.0" }, "funding": [ { @@ -9621,7 +9630,7 @@ "type": "open_collective" } ], - "time": "2024-07-26T10:38:09+00:00" + "time": "2025-11-19T20:47:34+00:00" }, { "name": "react/stream", @@ -10334,16 +10343,16 @@ }, { "name": "symfony/browser-kit", - "version": "v7.3.2", + "version": "v7.3.6", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", - "reference": "f0b889b73a845cddef1d25fe207b37fd04cb5419" + "reference": "e9a9fd604296b17bf90939c3647069f1f16ef04e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/browser-kit/zipball/f0b889b73a845cddef1d25fe207b37fd04cb5419", - "reference": "f0b889b73a845cddef1d25fe207b37fd04cb5419", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/e9a9fd604296b17bf90939c3647069f1f16ef04e", + "reference": "e9a9fd604296b17bf90939c3647069f1f16ef04e", "shasum": "" }, "require": { @@ -10382,7 +10391,7 @@ "description": "Simulates the behavior of a web browser, allowing you to make requests, click on links and submit forms programmatically", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/browser-kit/tree/v7.3.2" + "source": "https://github.com/symfony/browser-kit/tree/v7.3.6" }, "funding": [ { @@ -10402,20 +10411,20 @@ "type": "tidelift" } ], - "time": "2025-07-10T08:47:49+00:00" + "time": "2025-11-05T07:57:47+00:00" }, { "name": "symfony/css-selector", - "version": "v7.3.0", + "version": "v7.3.6", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "601a5ce9aaad7bf10797e3663faefce9e26c24e2" + "reference": "84321188c4754e64273b46b406081ad9b18e8614" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/601a5ce9aaad7bf10797e3663faefce9e26c24e2", - "reference": "601a5ce9aaad7bf10797e3663faefce9e26c24e2", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/84321188c4754e64273b46b406081ad9b18e8614", + "reference": "84321188c4754e64273b46b406081ad9b18e8614", "shasum": "" }, "require": { @@ -10451,7 +10460,7 @@ "description": "Converts CSS selectors to XPath expressions", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/css-selector/tree/v7.3.0" + "source": "https://github.com/symfony/css-selector/tree/v7.3.6" }, "funding": [ { @@ -10462,12 +10471,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-25T14:21:43+00:00" + "time": "2025-10-29T17:24:25+00:00" }, { "name": "symfony/dom-crawler", @@ -10542,31 +10555,31 @@ }, { "name": "symfony/maker-bundle", - "version": "v1.64.0", + "version": "v1.65.0", "source": { "type": "git", "url": "https://github.com/symfony/maker-bundle.git", - "reference": "c86da84640b0586e92aee2b276ee3638ef2f425a" + "reference": "9a0276d7486b29cae641b4a0a85d5e5cc149bff2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/maker-bundle/zipball/c86da84640b0586e92aee2b276ee3638ef2f425a", - "reference": "c86da84640b0586e92aee2b276ee3638ef2f425a", + "url": "https://api.github.com/repos/symfony/maker-bundle/zipball/9a0276d7486b29cae641b4a0a85d5e5cc149bff2", + "reference": "9a0276d7486b29cae641b4a0a85d5e5cc149bff2", "shasum": "" }, "require": { "doctrine/inflector": "^2.0", "nikic/php-parser": "^5.0", "php": ">=8.1", - "symfony/config": "^6.4|^7.0", - "symfony/console": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", "symfony/deprecation-contracts": "^2.2|^3", - "symfony/filesystem": "^6.4|^7.0", - "symfony/finder": "^6.4|^7.0", - "symfony/framework-bundle": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/process": "^6.4|^7.0" + "symfony/filesystem": "^6.4|^7.0|^8.0", + "symfony/finder": "^6.4|^7.0|^8.0", + "symfony/framework-bundle": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0" }, "conflict": { "doctrine/doctrine-bundle": "<2.10", @@ -10574,13 +10587,14 @@ }, "require-dev": { "composer/semver": "^3.0", - "doctrine/doctrine-bundle": "^2.5.0", + "doctrine/doctrine-bundle": "^2.5.0|^3.0.0", "doctrine/orm": "^2.15|^3", - "symfony/http-client": "^6.4|^7.0", - "symfony/phpunit-bridge": "^6.4.1|^7.0", - "symfony/security-core": "^6.4|^7.0", - "symfony/security-http": "^6.4|^7.0", - "symfony/yaml": "^6.4|^7.0", + "doctrine/persistence": "^3.1|^4.0", + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/phpunit-bridge": "^6.4.1|^7.0|^8.0", + "symfony/security-core": "^6.4|^7.0|^8.0", + "symfony/security-http": "^6.4|^7.0|^8.0", + "symfony/yaml": "^6.4|^7.0|^8.0", "twig/twig": "^3.0|^4.x-dev" }, "type": "symfony-bundle", @@ -10615,7 +10629,7 @@ ], "support": { "issues": "https://github.com/symfony/maker-bundle/issues", - "source": "https://github.com/symfony/maker-bundle/tree/v1.64.0" + "source": "https://github.com/symfony/maker-bundle/tree/v1.65.0" }, "funding": [ { @@ -10626,12 +10640,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-06-23T16:12:08+00:00" + "time": "2025-11-24T15:41:51+00:00" }, { "name": "symfony/stopwatch", @@ -10786,16 +10804,16 @@ }, { "name": "theseer/tokenizer", - "version": "1.2.3", + "version": "1.3.1", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", - "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b7489ce515e168639d17feec34b8847c326b0b3c", + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c", "shasum": "" }, "require": { @@ -10824,7 +10842,7 @@ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "support": { "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.3" + "source": "https://github.com/theseer/tokenizer/tree/1.3.1" }, "funding": [ { @@ -10832,7 +10850,7 @@ "type": "github" } ], - "time": "2024-03-03T12:36:25+00:00" + "time": "2025-11-17T20:03:58+00:00" } ], "aliases": [], @@ -10848,5 +10866,5 @@ "ext-xml": "*" }, "platform-dev": {}, - "plugin-api-version": "2.6.0" + "plugin-api-version": "2.9.0" } diff --git a/src/Validator/Constraints/Repository.php b/src/Validator/Constraints/Repository.php index fb3fc5a..4647202 100644 --- a/src/Validator/Constraints/Repository.php +++ b/src/Validator/Constraints/Repository.php @@ -6,7 +6,6 @@ /** * @Annotation - * */ class Repository extends Constraint { diff --git a/tests/Application/ApplicationKernelTest.php b/tests/Application/ApplicationKernelTest.php new file mode 100644 index 0000000..7057e7d --- /dev/null +++ b/tests/Application/ApplicationKernelTest.php @@ -0,0 +1,45 @@ +getContainer()->getParameter('kernel.project_dir'); + $publicDir = $projectDir . \DIRECTORY_SEPARATOR . 'public'; + $indexFile = $publicDir . \DIRECTORY_SEPARATOR . 'index.html'; + $backupFile = $publicDir . \DIRECTORY_SEPARATOR . '_index.html'; + + $filesystem = new Filesystem(); + + if ($filesystem->exists($indexFile)) { + $filesystem->rename($indexFile, $backupFile); + } + + try { + $client->request('GET', '/'); + $response = $client->getResponse(); + + self::assertSame( + Response::HTTP_OK, + $response->getStatusCode(), + 'Expected unavailable-page handler to respond with HTTP 200.' + ); + + self::assertPageTitleSame( + 'Composer Repository currently not available', + 'Unavailable page title did not match expectations.' + ); + } finally { + if ($filesystem->exists($backupFile)) { + $filesystem->rename($backupFile, $indexFile); + } + } + } +} diff --git a/tests/Composer/Satis/Command/BuildCommandTest.php b/tests/Composer/Satis/Command/BuildCommandTest.php deleted file mode 100644 index 4142d7d..0000000 --- a/tests/Composer/Satis/Command/BuildCommandTest.php +++ /dev/null @@ -1,164 +0,0 @@ -vfsRoot = vfsStream::setup(); - } - - protected function tearDown(): void - { - $this->vfsRoot = null; - parent::tearDown(); - } - - public function testMissingConfigBuildMustFail() - { - $file = $this->vfsRoot->url() . '/satis.json'; - $input = $this->createInput($file); - $output = $this->createOutput(); - $application = $this->createSatisApplication(); - $exitCode = $application->run($input, $output); - - $this->assertSame(1, $exitCode); - - $outputContent = $output->fetch(); - $this->assertStringStartsWith('File not found', $outputContent); - } - - public function testMinimalConfigBuild() - { - $file = new vfsStreamFile('satis.json'); - $file->setContent(\file_get_contents(__DIR__ . '/../../../fixtures/satis-minimal.json')); - $this->vfsRoot->addChild($file); - - $outputDir = new vfsStreamDirectory('output'); - $this->vfsRoot->addChild($outputDir); - - $input = $this->createInput($file->url(), $outputDir->url()); - $output = $this->createOutput(); - $application = $this->createSatisApplication(); - $exitCode = $application->run($input, $output); - - $this->assertSame(0, $exitCode, $output->fetch()); - $this->assertTrue($outputDir->hasChild('index.html')); - $this->assertTrue($outputDir->hasChild('packages.json')); - $this->assertTrue($outputDir->hasChild('include')); - /** @var vfsStreamDirectory $include */ - $include = $outputDir->getChild('include'); - $this->assertTrue($include->hasChildren()); - } - - public function testDefaultFormConfigBuild() - { - $file = new vfsStreamFile('satis.json'); - $this->vfsRoot->addChild($file); - - self::bootKernel()->getContainer(); - - $classMetadataFactory = new ClassMetadataFactory(new AttributeLoader()); - $serializer = new Serializer( - [ - new ConfigurationNormalizer(), - new ObjectNormalizer( - classMetadataFactory: $classMetadataFactory, - nameConverter: new MetadataAwareNameConverter($classMetadataFactory, new CamelCaseToSnakeCaseNameConverter()), - propertyTypeExtractor: new PropertyInfoExtractor([], [new PhpDocExtractor(), new ReflectionExtractor()]) - ), - ], - [new JsonEncoder()] - ); - - $filePersister = new FilePersister(new Filesystem(), $file->url(), $this->vfsRoot->url()); - $persister = new JsonPersister($filePersister, $serializer, Configuration::class); - - $configuration = new Configuration(); - $archive = new Archive(); - $archive->setFormat('zip'); - $configuration->setArchive($archive); - - $persister->flush($configuration); - - $outputDir = new vfsStreamDirectory('output'); - $this->vfsRoot->addChild($outputDir); - - $input = $this->createInput($file->url(), $outputDir->url()); - $output = $this->createOutput(); - $application = $this->createSatisApplication(); - $exitCode = $application->run($input, $output); - - try { - $this->assertSame(0, $exitCode, 'Exit code non null'); - } catch (AssertionFailedError $error) { - echo $file->getContent(); - echo $output->fetch(); - throw $error; - } - - $this->assertTrue($outputDir->hasChild('index.html')); - $this->assertTrue($outputDir->hasChild('packages.json')); - $this->assertTrue($outputDir->hasChild('include')); - /** @var vfsStreamDirectory $include */ - $include = $outputDir->getChild('include'); - $this->assertTrue($include->hasChildren()); - } - - protected function createSatisApplication(): Application - { - $application = new Application(); - $application->setAutoExit(false); - - return $application; - } - - protected function createOutput(): BufferedOutput - { - return new BufferedOutput(); - } - - protected function createInput(string $file, string $outputDir = ''): ArrayInput - { - $input = new ArrayInput( - [ - 'command' => 'build', - 'file' => $file, - 'output-dir' => $outputDir, - '-vv', - ] - ); - - return $input; - } -} diff --git a/tests/Controller/AdminControllerTest.php b/tests/Controller/AdminControllerTest.php index 2ce897b..b4b7901 100644 --- a/tests/Controller/AdminControllerTest.php +++ b/tests/Controller/AdminControllerTest.php @@ -8,7 +8,7 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -class AdminControllerTest extends WebTestCase +final class AdminControllerTest extends WebTestCase { use VfsTrait; @@ -24,100 +24,93 @@ protected function tearDown(): void parent::tearDown(); } - /** - * @throws \JsonException - */ - public function testRepositoryIndex(): void + public function testRepositoryIndexPageDisplaysLinks(): void { $client = self::createClient(); $crawler = $client->request('GET', '/admin'); $response = $client->getResponse(); - $this->assertSame(Response::HTTP_OK, $response->getStatusCode()); - $buttons = $crawler->filterXPath('//a'); - $this->assertNotEmpty($buttons); - $this->assertGreaterThan(3, $buttons->count()); + self::assertSame(Response::HTTP_OK, $response->getStatusCode(), 'Admin index page must respond with 200.'); + + $links = $crawler->filterXPath('//a'); + self::assertNotEmpty($links, 'Admin page must contain links.'); + self::assertGreaterThan(3, $links->count(), 'Expected at least 4 links on the admin index page.'); } - public function testRepositoryCRUD(): void + public function testRepositoryCRUDWorkflow(): void { $client = self::createClient(); $client->disableReboot(); + + // --- CREATE NEW REPOSITORY --- $crawler = $client->request('GET', '/admin/new'); $response = $client->getResponse(); + self::assertSame(Response::HTTP_OK, $response->getStatusCode(), 'New repository page must respond with 200.'); - $this->assertSame(Response::HTTP_OK, $response->getStatusCode()); - $form = $crawler->filterXPath('//form[@id="new_repository"]'); - $this->assertSame(Request::METHOD_POST, \mb_strtoupper($form->attr('method'))); + $form = $crawler->filterXPath('//form[@id="new_repository"]')->form(); + self::assertSame(Request::METHOD_POST, mb_strtoupper($form->getMethod()), 'Form must use POST method.'); - // form validation must fail due to invalid url - $client->submit($form->form()); - $response = $client->getResponse(); - $this->assertSame(Response::HTTP_OK, $response->getStatusCode()); - - $url = 'git@github.com:YourAccount/YourRepo.git'; - $client->submit( - $form->form(), - [ - 'RepositoryType[type]' => 'git', - 'RepositoryType[name]' => 'lovelyTest', - 'RepositoryType[url]' => $url, - 'RepositoryType[installationSource]' => 'dist', - ] - ); - $response = $client->getResponse(); - $this->assertSame(Response::HTTP_FOUND, $response->getStatusCode()); + // Submit invalid form (should fail validation) + $client->submit($form); + self::assertSame(Response::HTTP_OK, $client->getResponse()->getStatusCode(), 'Invalid form submission should reload page.'); - $this->assertTrue($this->vfsRoot->hasChild('satis.json')); - /** @var vfsStreamFile $configHandle */ - $configHandle = $this->vfsRoot->getChild('satis.json'); - $config = $configHandle->getContent(); + // Submit valid form + $firstUrl = 'git@github.com:YourAccount/YourRepo.git'; + $client->submit($form, [ + 'RepositoryType[type]' => 'git', + 'RepositoryType[name]' => 'lovelyTest', + 'RepositoryType[url]' => $firstUrl, + 'RepositoryType[installationSource]' => 'dist', + ]); - $this->assertJson($config); - $config = \json_decode($config, false); - $this->assertNotEmpty($config); + $response = $client->getResponse(); + self::assertSame(Response::HTTP_FOUND, $response->getStatusCode(), 'Form submission must redirect.'); - self::assertObjectHasProperty('repositories', $config); + $this->assertRepositoryInConfig($firstUrl, 'git', 'dist'); - $repositories = \get_object_vars($config->repositories); - $this->assertNotEmpty($repositories); - $firstRepo = \reset($repositories); + // --- EDIT REPOSITORY --- + $secondUrl = 'git@github.com:account/repository.git'; + $crawler = $client->request('GET', '/admin/edit/' . md5($firstUrl)); + $editForm = $crawler->filterXPath('//form[@id="edit_repository"]')->form(); - $this->assertSame($url, $firstRepo->url); - $this->assertSame('git', $firstRepo->type); - $this->assertSame('dist', $firstRepo->{'installation-source'}); + $client->submit($editForm, [ + 'RepositoryType[type]' => 'github', + 'RepositoryType[url]' => $secondUrl, + 'RepositoryType[name]' => 'lovelyTest2', + 'RepositoryType[installationSource]' => 'source', + ]); - $url2 = 'git@github.com:account/repository.git'; - $crawler = $client->request('GET', '/admin/edit/' . \md5($url)); - $form = $crawler->filterXPath('//form[@id="edit_repository"]'); + $response = $client->getResponse(); + self::assertSame(Response::HTTP_FOUND, $response->getStatusCode(), 'Edit submission must redirect.'); - $client->submit( - $form->form(), - [ - 'RepositoryType[type]' => 'github', - 'RepositoryType[url]' => $url2, - 'RepositoryType[name]' => 'lovelyTest2', - 'RepositoryType[installationSource]' => 'source', - ] - ); + $this->assertRepositoryInConfig($secondUrl, 'github', 'source'); + // --- DELETE REPOSITORY --- + $client->request('DELETE', '/admin/delete/' . md5($secondUrl)); $response = $client->getResponse(); - $this->assertSame(Response::HTTP_FOUND, $response->getStatusCode()); + self::assertSame(Response::HTTP_FOUND, $response->getStatusCode(), 'Delete request must redirect.'); + } - /** @var vfsStreamFile $configHandle */ - $configHandle = $this->vfsRoot->getChild('satis.json'); - $config = \json_decode($configHandle->getContent(), false, 512, \JSON_THROW_ON_ERROR); + /** + * Helper method to assert repository exists in vfs satis.json + */ + private function assertRepositoryInConfig(string $url, string $type, string $installationSource): void + { + self::assertTrue($this->vfsRoot->hasChild('satis.json'), 'satis.json must exist in vfsRoot.'); - $repositories = \get_object_vars($config->repositories); - $this->assertNotEmpty($repositories); - $firstRepo = \reset($repositories); + /** @var vfsStreamFile $configFile */ + $configFile = $this->vfsRoot->getChild('satis.json'); + $config = json_decode($configFile->getContent(), false, 512, JSON_THROW_ON_ERROR); - $this->assertSame($url2, $firstRepo->url); - $this->assertSame('github', $firstRepo->type); - $this->assertSame('source', $firstRepo->{'installation-source'}); + self::assertObjectHasProperty('repositories', $config, 'satis.json must contain "repositories".'); - $client->request('DELETE', '/admin/delete/' . \md5($url2)); - $response = $client->getResponse(); - $this->assertSame(Response::HTTP_FOUND, $response->getStatusCode()); + $repositories = get_object_vars($config->repositories); + self::assertNotEmpty($repositories, 'There must be at least one repository in the config.'); + + $firstRepo = reset($repositories); + + self::assertSame($url, $firstRepo->url, 'Repository URL must match.'); + self::assertSame($type, $firstRepo->type, 'Repository type must match.'); + self::assertSame($installationSource, $firstRepo->{'installation-source'}, 'Installation source must match.'); } } diff --git a/tests/Controller/SecurityControllerTest.php b/tests/Controller/SecurityControllerTest.php index 4c9291d..dd8c102 100644 --- a/tests/Controller/SecurityControllerTest.php +++ b/tests/Controller/SecurityControllerTest.php @@ -2,59 +2,56 @@ namespace App\Tests\Controller; -use org\bovigo\vfs\vfsStream; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -class SecurityControllerTest extends WebTestCase +final class SecurityControllerTest extends WebTestCase { - protected function setUp(): void - { - parent::setUp(); - vfsStream::setup(); - } - - public function testAdminLogin(): void + public function testAdminLoginFlow(): void { $client = self::createClient(['environment' => 'testsecure']); - // must redirect to login form + // --- REDIRECT TO LOGIN --- $client->request(Request::METHOD_GET, '/admin'); $response = $client->getResponse(); - $this->assertSame(Response::HTTP_FOUND, $response->getStatusCode()); - $this->assertStringEndsWith('/login', $response->headers->get('location')); + self::assertSame(Response::HTTP_FOUND, $response->getStatusCode(), 'Accessing /admin without login must redirect.'); + self::assertStringEndsWith('/login', $response->headers->get('location'), 'Redirect must go to /login.'); + + // --- LOGIN FORM DISPLAY --- $crawler = $client->request(Request::METHOD_GET, '/login'); $response = $client->getResponse(); - $this->assertTrue($response->isSuccessful()); + self::assertTrue($response->isSuccessful(), 'Login page must respond with 200.'); - // submit empty form $form = $crawler->filterXPath('//form[@id="login_form"]')->form(); - $client->submit( - $form, - [ - 'login[username]' => 'test', - 'login[password]' => '', - ] - ); - /** @var RedirectResponse $response */ + + // --- SUBMIT EMPTY PASSWORD --- + $client->submit($form, [ + 'login[username]' => 'test', + 'login[password]' => '', + ]); + $response = $client->getResponse(); - $this->assertInstanceOf(RedirectResponse::class, $response); - $this->assertTrue($response->isRedirection()); - $this->assertStringEndsWith('/login', $response->getTargetUrl()); + self::assertInstanceOf(RedirectResponse::class, $response, 'Invalid login submission must redirect.'); + self::assertTrue($response->isRedirection(), 'Response must be a redirection.'); + self::assertStringEndsWith('/login', $response->getTargetUrl(), 'Failed login must redirect back to /login.'); + + // --- SUBMIT CORRECT CREDENTIALS --- + $client->submit($form, [ + 'login[username]' => 'test', + 'login[password]' => 'test', + ]); - $client->submit($form, ['login[username]' => 'test', 'login[password]' => 'test']); - /** @var RedirectResponse $response */ $response = $client->getResponse(); - $this->assertInstanceOf(RedirectResponse::class, $response); - $this->assertTrue($response->isRedirection()); - $this->assertStringEndsWith('/admin', $response->getTargetUrl()); + self::assertInstanceOf(RedirectResponse::class, $response, 'Successful login must redirect.'); + self::assertTrue($response->isRedirection(), 'Response must be a redirection.'); + self::assertStringEndsWith('/admin', $response->getTargetUrl(), 'Successful login must redirect to /admin.'); - // authenticated and able to see admin section + // --- ACCESS ADMIN AFTER LOGIN --- $client->request(Request::METHOD_GET, '/admin'); $response = $client->getResponse(); - $this->assertTrue($response->isSuccessful()); + self::assertTrue($response->isSuccessful(), 'Authenticated user must be able to access /admin.'); } } diff --git a/tests/Integration/BuildCommandTest.php b/tests/Integration/BuildCommandTest.php new file mode 100644 index 0000000..33cf8ba --- /dev/null +++ b/tests/Integration/BuildCommandTest.php @@ -0,0 +1,173 @@ +vfsRoot = vfsStream::setup(); + } + + protected function tearDown(): void + { + $this->vfsRoot = null; + parent::tearDown(); + } + + public function testBuildFailsWhenConfigIsMissing(): void + { + $configFile = $this->vfsRoot->url() . '/satis.json'; + $input = $this->createInput($configFile); + $output = $this->createOutput(); + $application = $this->createSatisApplication(); + + $exitCode = $application->run($input, $output); + + self::assertSame(1, $exitCode, 'Expected exit code 1 when config file is missing.'); + self::assertStringStartsWith( + 'File not found', + $output->fetch(), + 'Expected output to indicate missing config file.' + ); + } + + public function testBuildWithMinimalConfigSucceeds(): void + { + $configFile = new vfsStreamFile('satis.json'); + $configFile->setContent(\file_get_contents(__DIR__ . '/../fixtures/satis-minimal.json')); + $this->vfsRoot->addChild($configFile); + + $outputDir = new vfsStreamDirectory('output'); + $this->vfsRoot->addChild($outputDir); + + $input = $this->createInput($configFile->url(), $outputDir->url()); + $output = $this->createOutput(); + $application = $this->createSatisApplication(); + + $exitCode = $application->run($input, $output); + + self::assertSame(0, $exitCode, 'Expected exit code 0 for minimal config build.'); + self::assertTrue($outputDir->hasChild('index.html'), 'index.html must be generated.'); + self::assertTrue($outputDir->hasChild('packages.json'), 'packages.json must be generated.'); + self::assertTrue($outputDir->hasChild('include'), 'include directory must exist.'); + + /** @var vfsStreamDirectory $includeDir */ + $includeDir = $outputDir->getChild('include'); + self::assertTrue($includeDir->hasChildren(), 'include directory must contain files.'); + } + + public function testBuildWithDefaultFormConfigSucceeds(): void + { + $configFile = new vfsStreamFile('satis.json'); + $this->vfsRoot->addChild($configFile); + + self::bootKernel()->getContainer(); + + $serializer = $this->createSerializer(); + $filePersister = new FilePersister(new Filesystem(), $configFile->url(), $this->vfsRoot->url()); + $persister = new JsonPersister($filePersister, $serializer, Configuration::class); + + $configuration = new Configuration(); + $archive = new Archive(); + $archive->setFormat('zip'); + $configuration->setArchive($archive); + + $persister->flush($configuration); + + $outputDir = new vfsStreamDirectory('output'); + $this->vfsRoot->addChild($outputDir); + + $input = $this->createInput($configFile->url(), $outputDir->url()); + $output = $this->createOutput(); + $application = $this->createSatisApplication(); + + try { + $exitCode = $application->run($input, $output); + self::assertSame(0, $exitCode, 'Expected exit code 0 for default form config build.'); + } catch (AssertionFailedError $error) { + echo $configFile->getContent(); + echo $output->fetch(); + throw $error; + } + + self::assertTrue($outputDir->hasChild('index.html'), 'index.html must be generated.'); + self::assertTrue($outputDir->hasChild('packages.json'), 'packages.json must be generated.'); + self::assertTrue($outputDir->hasChild('include'), 'include directory must exist.'); + + /** @var vfsStreamDirectory $includeDir */ + $includeDir = $outputDir->getChild('include'); + self::assertTrue($includeDir->hasChildren(), 'include directory must contain files.'); + } + + private function createSatisApplication(): Application + { + $application = new Application(); + $application->setAutoExit(false); + + return $application; + } + + private function createOutput(): BufferedOutput + { + return new BufferedOutput(); + } + + private function createInput(string $file, string $outputDir = ''): ArrayInput + { + return new ArrayInput([ + 'command' => 'build', + 'file' => $file, + 'output-dir' => $outputDir, + '-vv', + ]); + } + + private function createSerializer(): Serializer + { + $classMetadataFactory = new ClassMetadataFactory(new AttributeLoader()); + + return new Serializer([ + new ConfigurationNormalizer(), + new ObjectNormalizer( + classMetadataFactory: $classMetadataFactory, + nameConverter: new MetadataAwareNameConverter( + $classMetadataFactory, + new CamelCaseToSnakeCaseNameConverter() + ), + propertyTypeExtractor: new PropertyInfoExtractor( + [], + [new PhpDocExtractor(), new ReflectionExtractor()] + ) + ), + ], [new JsonEncoder()]); + } +} diff --git a/tests/Manager/ManagerConfigValidatorTest.php b/tests/Manager/ManagerConfigValidatorTest.php index 7d22ce9..c54f530 100644 --- a/tests/Manager/ManagerConfigValidatorTest.php +++ b/tests/Manager/ManagerConfigValidatorTest.php @@ -8,23 +8,24 @@ use App\Tests\Traits\SchemaValidatorTrait; use App\Tests\Traits\VfsTrait; use org\bovigo\vfs\vfsStreamFile; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Prophecy\Argument; use Prophecy\PhpUnit\ProphecyTrait; use Symfony\Component\Lock\LockFactory; use Symfony\Component\Lock\Store\FlockStore; -class ManagerConfigValidatorTest extends TestCase +final class ManagerConfigValidatorTest extends TestCase { use ProphecyTrait; use SchemaValidatorTrait; use VfsTrait; - /** @var vfsStreamFile */ - protected $config; + private vfsStreamFile $config; protected function setUp(): void { + parent::setUp(); $this->vfsSetup(); $this->vfsRoot->addChild($this->config = new vfsStreamFile('satis.json')); } @@ -32,29 +33,38 @@ protected function setUp(): void protected function tearDown(): void { $this->vfsTearDown(); + parent::tearDown(); } - /** - * @dataProvider configFileProvider - */ - public function testConfigIsMatchingSatisSchema($configFilename): void + #[DataProvider(methodName: 'configFileProvider')] + public function testConfigIsMatchingSatisSchema(string $configFilePath): void { - $this->assertTrue(\copy($configFilename, $this->config->url())); + // copy fixture into vfs + $copied = \copy($configFilePath, $this->config->url()); + self::assertTrue($copied, 'Fixture file must be copied into virtual filesystem.'); + + // create a Prophecy for JsonPersister $persister = $this->prophesize(JsonPersister::class); - $persister - ->load() - ->willReturn(new Configuration()); - $persister - ->flush(Argument::type(Configuration::class)) - ->shouldBeCalled(); + $persister->load()->willReturn(new Configuration())->shouldBeCalled(); + $persister->flush(Argument::type(Configuration::class))->shouldBeCalled(); + // instantiate RepositoryManager with LockFactory and persister mock $lockFactory = new LockFactory(new FlockStore()); - /** @var RepositoryManager $manager */ $manager = new RepositoryManager($lockFactory, $persister->reveal()); + + // call addAll to simulate repository addition $manager->addAll([]); - $this->validateSchema(\json_decode($this->config->getContent()), $this->getSatisSchema()); - $this->assertJsonFileEqualsJsonFile($configFilename, $this->config->url()); + // validate JSON against Satis schema + $decodedConfig = \json_decode($this->config->getContent()); + $this->validateSchema($decodedConfig, $this->getSatisSchema()); + + // assert virtual file still matches fixture + self::assertJsonFileEqualsJsonFile( + $configFilePath, + $this->config->url(), + 'Generated JSON must match fixture.' + ); } public static function configFileProvider(): array diff --git a/tests/Persister/FilePersisterTest.php b/tests/Persister/FilePersisterTest.php index 3c4c926..a5c2077 100644 --- a/tests/Persister/FilePersisterTest.php +++ b/tests/Persister/FilePersisterTest.php @@ -25,18 +25,18 @@ use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; use Symfony\Component\Serializer\Serializer; -class FilePersisterTest extends KernelTestCase +final class FilePersisterTest extends KernelTestCase { use SchemaValidatorTrait; use VfsTrait; - /** @var FilePersister|null */ - protected $persister; + private ?FilePersister $persister = null; protected function setUp(): void { parent::setUp(); $this->vfsSetup(); + $this->persister = new FilePersister( new Filesystem(), $this->vfsRoot->url() . '/satis.json', @@ -51,37 +51,38 @@ protected function tearDown(): void parent::tearDown(); } - public function testDumpMustTruncateFile(): void + public function testFlushTruncatesFileCorrectly(): void { $config = [ - 'name' => 'test', - 'homepage' => 'http://localhost', + 'name' => 'test', + 'homepage' => 'http://localhost', 'repositories' => [ - [ - 'type' => 'git', - 'url' => 'https://github.com/ludofleury/satisfy.git', - 'name' => 'ludofleury/satisfy', - ], + ['type' => 'git', 'url' => 'https://github.com/ludofleury/satisfy.git', 'name' => 'ludofleury/satisfy'], ], 'require-all' => true, ]; + $content = \json_encode($config); $this->persister->flush($content); + /** @var vfsStreamFile $configFile */ $configFile = $this->vfsRoot->getChild('satis.json'); - $this->assertStringEqualsFile($configFile->url(), $content); - $this->assertSame($content, $this->persister->load()); + + self::assertStringEqualsFile($configFile->url(), $content, 'File content must match flushed content.'); + self::assertSame($content, $this->persister->load(), 'Loaded content must match flushed content.'); $this->validateSchema(\json_decode($configFile->getContent()), $this->getSatisSchema()); + // truncate repositories $config['repositories'] = []; - $content = \json_encode($config); + $content = \json_encode($config); $this->persister->flush($content); - $this->assertStringEqualsFile($configFile->url(), $content); - $this->assertSame($content, $this->persister->load()); + + self::assertStringEqualsFile($configFile->url(), $content, 'After truncation, file content must match.'); + self::assertSame($content, $this->persister->load(), 'Loaded content must match truncated content.'); } - public function testPersisterNormalization(): void + public function testJsonPersisterNormalizationWorks(): void { $file = new vfsStreamFile('satis.json'); $file->setContent(\file_get_contents(__DIR__ . '/../fixtures/satis-full.json')); @@ -90,41 +91,47 @@ public function testPersisterNormalization(): void self::bootKernel(); $classMetadataFactory = new ClassMetadataFactory(new AttributeLoader()); - $serializer = new Serializer( + $serializer = new Serializer( [ new ConfigurationNormalizer(), new ObjectNormalizer( - classMetadataFactory: new ClassMetadataFactory(new AttributeLoader()), - nameConverter: new MetadataAwareNameConverter($classMetadataFactory, new CamelCaseToSnakeCaseNameConverter()), - propertyTypeExtractor: new PropertyInfoExtractor([], [new PhpDocExtractor(), new ReflectionExtractor()]) + classMetadataFactory: $classMetadataFactory, + nameConverter: new MetadataAwareNameConverter( + $classMetadataFactory, + new CamelCaseToSnakeCaseNameConverter() + ), + propertyTypeExtractor: new PropertyInfoExtractor([], [ + new PhpDocExtractor(), + new ReflectionExtractor(), + ]) ), ], [new JsonEncoder()] ); + $persister = new JsonPersister( $this->persister, $serializer, Configuration::class ); + $config = $persister->load(); - // validate config + // validate require $require = $config->getRequire(); - $this->assertIsArray($require); - $this->assertCount(1, $require); + self::assertIsArray($require, 'Require must be an array.'); + self::assertCount(1, $require, 'Require array must contain one element.'); + // validate repositories $repositories = $config->getRepositories(); - $this->assertCount(1, $repositories); - $this->assertIsString($repositories->key()); - $this->assertInstanceOf(RepositoryInterface::class, $repositories->current()); - self::assertIsArray($stability = $config->getMinimumStabilityPerPackage()); - self::assertArrayHasKey(0, $stability); + self::assertCount(1, $repositories, 'Repositories must contain one element.'); + self::assertInstanceOf(RepositoryInterface::class, $repositories->current(), 'Repository must implement RepositoryInterface.'); - // append additional repo + // append additional repository $repositories->append(new Repository(url: 'http://localhost', name: 'funny/test')); $config->setRepositories($repositories); - // change existing, append additional require + // modify existing require and add new $constraint = \reset($require); $constraint->setConstraint('^2.0'); $config->setRequire([ @@ -132,20 +139,15 @@ classMetadataFactory: new ClassMetadataFactory(new AttributeLoader()), new PackageConstraint('psr/log', '^1.0'), ]); - // add required specific package stability + // add minimum stability per package $config->addMinimumStabilityPerPackage('phpunit/phpunit', 'alpha'); $persister->flush($config); $config = $persister->load(); - $require = $config->getRequire(); - $this->assertIsArray($require); - $this->assertCount(2, $require); - - $repositories = $config->getRepositories(); - $this->assertCount(2, $repositories); - $this->assertIsString($repositories->key()); - $this->assertInstanceOf(RepositoryInterface::class, $repositories->current()); + self::assertCount(2, $config->getRequire(), 'Require array must contain two elements after modification.'); + self::assertCount(2, $config->getRepositories(), 'Repositories must contain two elements after modification.'); + self::assertInstanceOf(RepositoryInterface::class, $config->getRepositories()->current(), 'Repository must implement RepositoryInterface.'); } } diff --git a/tests/Traits/SchemaValidatorTrait.php b/tests/Traits/SchemaValidatorTrait.php index 2275218..0213d3f 100644 --- a/tests/Traits/SchemaValidatorTrait.php +++ b/tests/Traits/SchemaValidatorTrait.php @@ -9,15 +9,13 @@ trait SchemaValidatorTrait { protected function getSatisSchema(): \stdClass { - $retriever = new UriRetriever(); - - return $retriever->retrieve('file://' . __DIR__ . '/../../vendor/composer/satis/res/satis-schema.json'); + return (new UriRetriever())->retrieve('file://' . __DIR__ . '/../../vendor/composer/satis/res/satis-schema.json'); } protected function validateSchema($content, \stdClass $schema): void { $validator = new Validator(); - $validator->check($content, $schema); + $validator->validate($content, $schema); $this->assertTrue($validator->isValid(), \print_r($validator->getErrors(), true)); } } diff --git a/tests/Traits/VfsTrait.php b/tests/Traits/VfsTrait.php index f505b28..32e050a 100644 --- a/tests/Traits/VfsTrait.php +++ b/tests/Traits/VfsTrait.php @@ -7,8 +7,7 @@ trait VfsTrait { - /** @var vfsStreamDirectory|null */ - protected $vfsRoot; + protected ?vfsStreamDirectory $vfsRoot; protected function vfsSetup(): void { diff --git a/tests/app/ApplicationKernelTest.php b/tests/app/ApplicationKernelTest.php deleted file mode 100644 index 7c97d4c..0000000 --- a/tests/app/ApplicationKernelTest.php +++ /dev/null @@ -1,31 +0,0 @@ -getContainer()->getParameter('kernel.project_dir') . '/public'; - $filesystem = new Filesystem(); - if ($filesystem->exists($webDir . '/index.html')) { - $filesystem->rename($webDir . '/index.html', $renamed = $webDir . '/_index.html'); - } - - try { - $client->request('GET', '/'); - $response = $client->getResponse(); - $this->assertSame(Response::HTTP_OK, $response->getStatusCode()); - self::assertPageTitleSame('Composer Repository currently not available'); - } finally { - if (isset($renamed)) { - $filesystem->rename($renamed, $webDir . '/index.html'); - } - } - } -} From 0c51df2d0e0de4b282b4837df1a48f79fe0fe233 Mon Sep 17 00:00:00 2001 From: Trusted97 Date: Thu, 27 Nov 2025 14:10:56 +0100 Subject: [PATCH 2/5] chore: php-cs-fixer --- tests/Controller/AdminControllerTest.php | 12 ++++++------ tests/Manager/ManagerConfigValidatorTest.php | 2 +- tests/Persister/FilePersisterTest.php | 8 ++++---- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/Controller/AdminControllerTest.php b/tests/Controller/AdminControllerTest.php index b4b7901..98b8a2b 100644 --- a/tests/Controller/AdminControllerTest.php +++ b/tests/Controller/AdminControllerTest.php @@ -48,7 +48,7 @@ public function testRepositoryCRUDWorkflow(): void self::assertSame(Response::HTTP_OK, $response->getStatusCode(), 'New repository page must respond with 200.'); $form = $crawler->filterXPath('//form[@id="new_repository"]')->form(); - self::assertSame(Request::METHOD_POST, mb_strtoupper($form->getMethod()), 'Form must use POST method.'); + self::assertSame(Request::METHOD_POST, \mb_strtoupper($form->getMethod()), 'Form must use POST method.'); // Submit invalid form (should fail validation) $client->submit($form); @@ -70,7 +70,7 @@ public function testRepositoryCRUDWorkflow(): void // --- EDIT REPOSITORY --- $secondUrl = 'git@github.com:account/repository.git'; - $crawler = $client->request('GET', '/admin/edit/' . md5($firstUrl)); + $crawler = $client->request('GET', '/admin/edit/' . \md5($firstUrl)); $editForm = $crawler->filterXPath('//form[@id="edit_repository"]')->form(); $client->submit($editForm, [ @@ -86,7 +86,7 @@ public function testRepositoryCRUDWorkflow(): void $this->assertRepositoryInConfig($secondUrl, 'github', 'source'); // --- DELETE REPOSITORY --- - $client->request('DELETE', '/admin/delete/' . md5($secondUrl)); + $client->request('DELETE', '/admin/delete/' . \md5($secondUrl)); $response = $client->getResponse(); self::assertSame(Response::HTTP_FOUND, $response->getStatusCode(), 'Delete request must redirect.'); } @@ -100,14 +100,14 @@ private function assertRepositoryInConfig(string $url, string $type, string $ins /** @var vfsStreamFile $configFile */ $configFile = $this->vfsRoot->getChild('satis.json'); - $config = json_decode($configFile->getContent(), false, 512, JSON_THROW_ON_ERROR); + $config = \json_decode($configFile->getContent(), false, 512, \JSON_THROW_ON_ERROR); self::assertObjectHasProperty('repositories', $config, 'satis.json must contain "repositories".'); - $repositories = get_object_vars($config->repositories); + $repositories = \get_object_vars($config->repositories); self::assertNotEmpty($repositories, 'There must be at least one repository in the config.'); - $firstRepo = reset($repositories); + $firstRepo = \reset($repositories); self::assertSame($url, $firstRepo->url, 'Repository URL must match.'); self::assertSame($type, $firstRepo->type, 'Repository type must match.'); diff --git a/tests/Manager/ManagerConfigValidatorTest.php b/tests/Manager/ManagerConfigValidatorTest.php index c54f530..7008fb3 100644 --- a/tests/Manager/ManagerConfigValidatorTest.php +++ b/tests/Manager/ManagerConfigValidatorTest.php @@ -50,7 +50,7 @@ public function testConfigIsMatchingSatisSchema(string $configFilePath): void // instantiate RepositoryManager with LockFactory and persister mock $lockFactory = new LockFactory(new FlockStore()); - $manager = new RepositoryManager($lockFactory, $persister->reveal()); + $manager = new RepositoryManager($lockFactory, $persister->reveal()); // call addAll to simulate repository addition $manager->addAll([]); diff --git a/tests/Persister/FilePersisterTest.php b/tests/Persister/FilePersisterTest.php index a5c2077..e5e0142 100644 --- a/tests/Persister/FilePersisterTest.php +++ b/tests/Persister/FilePersisterTest.php @@ -54,8 +54,8 @@ protected function tearDown(): void public function testFlushTruncatesFileCorrectly(): void { $config = [ - 'name' => 'test', - 'homepage' => 'http://localhost', + 'name' => 'test', + 'homepage' => 'http://localhost', 'repositories' => [ ['type' => 'git', 'url' => 'https://github.com/ludofleury/satisfy.git', 'name' => 'ludofleury/satisfy'], ], @@ -75,7 +75,7 @@ public function testFlushTruncatesFileCorrectly(): void // truncate repositories $config['repositories'] = []; - $content = \json_encode($config); + $content = \json_encode($config); $this->persister->flush($content); self::assertStringEqualsFile($configFile->url(), $content, 'After truncation, file content must match.'); @@ -91,7 +91,7 @@ public function testJsonPersisterNormalizationWorks(): void self::bootKernel(); $classMetadataFactory = new ClassMetadataFactory(new AttributeLoader()); - $serializer = new Serializer( + $serializer = new Serializer( [ new ConfigurationNormalizer(), new ObjectNormalizer( From 3c557431762f1f68fbcae07ccf6393d4d75d2c16 Mon Sep 17 00:00:00 2001 From: Trusted97 Date: Thu, 27 Nov 2025 15:01:50 +0100 Subject: [PATCH 3/5] chore: adding form test --- .php-cs-fixer.dist.php | 1 - src/DTO/Abandoned.php | 10 +++ src/DTO/Configuration.php | 44 ++++++------- src/DTO/PackageStability.php | 15 ++++- tests/Form/AbandonedTypeTest.php | 82 ++++++++++++++++++++++++ tests/Form/ArchiveTypeTest.php | 68 ++++++++++++++++++++ tests/Form/DeleteFormTypeTest.php | 57 ++++++++++++++++ tests/Form/LoginFormTest.php | 58 +++++++++++++++++ tests/Form/PackageConstraintTypeTest.php | 72 +++++++++++++++++++++ tests/Form/PackageStabilityTypeTest.php | 72 +++++++++++++++++++++ tests/Integration/BuildCommandTest.php | 2 +- 11 files changed, 456 insertions(+), 25 deletions(-) create mode 100644 tests/Form/AbandonedTypeTest.php create mode 100644 tests/Form/ArchiveTypeTest.php create mode 100644 tests/Form/DeleteFormTypeTest.php create mode 100644 tests/Form/LoginFormTest.php create mode 100644 tests/Form/PackageConstraintTypeTest.php create mode 100644 tests/Form/PackageStabilityTypeTest.php diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php index dd68e9b..cd8b2d6 100644 --- a/.php-cs-fixer.dist.php +++ b/.php-cs-fixer.dist.php @@ -18,7 +18,6 @@ 'no_unreachable_default_argument_value' => true, 'no_useless_else' => true, 'no_useless_return' => true, - 'php_unit_strict' => true, 'phpdoc_order' => true, 'strict_comparison' => true, 'strict_param' => true, diff --git a/src/DTO/Abandoned.php b/src/DTO/Abandoned.php index 3de46a1..baa2d82 100644 --- a/src/DTO/Abandoned.php +++ b/src/DTO/Abandoned.php @@ -23,4 +23,14 @@ public function getReplacement(): ?string { return $this->replacement; } + + public function setPackage(string $package): void + { + $this->package = $package; + } + + public function setReplacement(?string $replacement): void + { + $this->replacement = $replacement; + } } diff --git a/src/DTO/Configuration.php b/src/DTO/Configuration.php index 78bf38c..2918d1f 100644 --- a/src/DTO/Configuration.php +++ b/src/DTO/Configuration.php @@ -9,38 +9,38 @@ class Configuration { public const string DEFAULT_OUTPUT_DIR = 'public'; - private string $name = 'localhost/repository'; - private string $description = ''; - private string $homepage = 'http://localhost'; + public string $name = 'localhost/repository'; + public string $description = ''; + public string $homepage = 'http://localhost'; #[SerializedName('output-dir')] - private string $outputDir = self::DEFAULT_OUTPUT_DIR; + public string $outputDir = self::DEFAULT_OUTPUT_DIR; #[SerializedName('output-html')] - private bool $outputHtml = true; + public bool $outputHtml = true; /** * @var \ArrayObject|RepositoryInterface[] */ - private \ArrayIterator|array $repositories; + public \ArrayIterator|array $repositories; /** * @var PackageConstraint[] */ #[SerializedName('require')] - private array $require = []; + public array $require = []; #[SerializedName('require-all')] - private bool $requireAll = false; + public bool $requireAll = false; #[SerializedName('require-dependencies')] - private bool $requireDependencies = false; + public bool $requireDependencies = false; #[SerializedName('require-dev-dependencies')] - private bool $requireDevDependencies = false; + public bool $requireDevDependencies = false; #[SerializedName('require-dependency-filter')] - private bool $requireDependencyFilter = true; + public bool $requireDependencyFilter = true; /** * @var string[]|null @@ -49,51 +49,51 @@ class Configuration private ?array $stripHosts = null; #[SerializedName('include-filename')] - private ?string $includeFilename = null; + public ?string $includeFilename = null; #[SerializedName('archive')] - private ?Archive $archive = null; + public ?Archive $archive = null; #[SerializedName('minimum-stability')] - private ?string $minimumStability = 'dev'; + public ?string $minimumStability = 'dev'; /** * @var PackageStability[] */ #[SerializedName('minimum-stability-per-package')] - private array $minimumStabilityPerPackage = []; + public array $minimumStabilityPerPackage = []; - private bool $providers = false; + public bool $providers = false; #[SerializedName('providers-history-size')] private ?int $providersHistorySize = null; #[SerializedName('twig-template')] - private ?string $twigTemplate = null; + public ?string $twigTemplate = null; /** * @var Abandoned[] */ - private array $abandoned = []; + public array $abandoned = []; /** * @var PackageConstraint[] */ - private array $blacklist = []; + public array $blacklist = []; /** * @var mixed[]|null */ - private ?array $config = null; + public ?array $config = null; #[SerializedName('notify-batch')] - private ?string $notifyBatch = null; + public ?string $notifyBatch = null; #[SerializedName('_comment')] private ?string $comment = null; #[SerializedName('pretty-print')] - private bool $prettyPrint = true; + public bool $prettyPrint = true; public function __construct() { diff --git a/src/DTO/PackageStability.php b/src/DTO/PackageStability.php index 019201d..c909588 100644 --- a/src/DTO/PackageStability.php +++ b/src/DTO/PackageStability.php @@ -5,7 +5,6 @@ class PackageStability { private string $package; - private string $stability; public function __construct(string $package, string $stability) @@ -19,8 +18,22 @@ public function getPackage(): string return $this->package; } + public function setPackage(?string $package): void + { + if (null !== $package) { + $this->package = $package; + } + } + public function getStability(): string { return $this->stability; } + + public function setStability(?string $stability): void + { + if (null !== $stability) { + $this->stability = $stability; + } + } } diff --git a/tests/Form/AbandonedTypeTest.php b/tests/Form/AbandonedTypeTest.php new file mode 100644 index 0000000..c35cd61 --- /dev/null +++ b/tests/Form/AbandonedTypeTest.php @@ -0,0 +1,82 @@ + 'deprecated/package', + 'replacement' => 'new/package', + ]; + + $objectToCompare = new Abandoned('', null); + $form = $this->factory->create(AbandonedType::class, $objectToCompare); + $form->submit($formData); + + $this->assertTrue($form->isSynchronized()); + $this->assertTrue($form->isSubmitted()); + $this->assertTrue($form->isValid()); + + $expected = new Abandoned('deprecated/package', 'new/package'); + $this->assertEquals($expected, $form->getData()); + + $view = $form->createView(); + $this->assertArrayHasKey('package', $view->children); + $this->assertArrayHasKey('replacement', $view->children); + } + + public function testSubmitEmptyReplacementIsValid(): void + { + $formData = [ + 'package' => 'deprecated/package', + 'replacement' => '', + ]; + + $form = $this->factory->create(AbandonedType::class, new Abandoned('', null)); + $form->submit($formData); + + $this->assertTrue($form->isValid()); + $this->assertSame('deprecated/package', $form->getData()->getPackage()); + $this->assertSame('', $form->getData()->getReplacement()); + } + + public function testPackageCannotBeBlank(): void + { + $formData = [ + 'package' => '', + 'replacement' => 'any', + ]; + + $form = $this->factory->create(AbandonedType::class, new Abandoned('', null)); + $form->submit($formData); + + $this->assertFalse($form->isValid()); + $errors = $form->get('package')->getErrors(); + $this->assertNotEmpty($errors); + $this->assertStringContainsString('This value should not be blank', $errors[0]->getMessage()); + } + + public function testFieldPlaceholders(): void + { + $form = $this->factory->create(AbandonedType::class); + $view = $form->createView(); + + $this->assertSame('Abandoned package name', $view->children['package']->vars['attr']['placeholder']); + $this->assertSame('Package name/URL pointing to a recommended alternative(can be empty)', $view->children['replacement']->vars['attr']['placeholder']); + } +} diff --git a/tests/Form/ArchiveTypeTest.php b/tests/Form/ArchiveTypeTest.php new file mode 100644 index 0000000..42f7a2a --- /dev/null +++ b/tests/Form/ArchiveTypeTest.php @@ -0,0 +1,68 @@ + 'dist', + 'format' => 'zip', + 'absoluteDirectory' => '/var/www/output', + 'prefixUrl' => 'https://example.com/dist/', + 'skipDev' => false, + 'checksum' => false, + 'ignoreFilters' => true, + 'overrideDistType' => true, + 'rearchive' => false, + 'whitelist' => ['package1', 'package2'], + 'blacklist' => ['bad1', 'bad2'], + ]; + + $model = new Archive(); + $form = $this->factory->create(ArchiveType::class, $model); + + $form->submit($formData); + + $this->assertTrue($form->isSynchronized()); + $data = $form->getData(); + + $this->assertSame('dist', $data->getDirectory()); + $this->assertSame('zip', $data->getFormat()); + $this->assertSame('/var/www/output', $data->getAbsoluteDirectory()); + $this->assertSame('https://example.com/dist/', $data->getPrefixUrl()); + $this->assertFalse($data->isSkipDev()); + $this->assertFalse($data->isChecksum()); + $this->assertTrue($data->isIgnoreFilters()); + $this->assertTrue($data->isOverrideDistType()); + $this->assertFalse($data->isRearchive()); + $this->assertSame(['package1', 'package2'], $data->getWhitelist()); + $this->assertSame(['bad1', 'bad2'], $data->getBlacklist()); + } + + public function testEmptyData(): void + { + $form = $this->factory->create(ArchiveType::class); + $form->submit([]); + + $this->assertTrue($form->isSynchronized()); + $data = $form->getData(); + + $this->assertSame('', $data->getDirectory()); + $this->assertSame('', $data->getFormat()); + $this->assertNull($data->getAbsoluteDirectory()); + $this->assertNull($data->getPrefixUrl()); + $this->assertFalse($data->isSkipDev()); + $this->assertFalse($data->isChecksum()); + $this->assertFalse($data->isIgnoreFilters()); + $this->assertFalse($data->isOverrideDistType()); + $this->assertFalse($data->isRearchive()); + $this->assertSame([], $data->getWhitelist()); + $this->assertSame([], $data->getBlacklist()); + } +} diff --git a/tests/Form/DeleteFormTypeTest.php b/tests/Form/DeleteFormTypeTest.php new file mode 100644 index 0000000..681cc2e --- /dev/null +++ b/tests/Form/DeleteFormTypeTest.php @@ -0,0 +1,57 @@ +factory = Forms::createFormFactory(); + } + + public function testCsrfTokenIdWithEntityHavingId(): void + { + $entity = new class { + private ?int $id = 42; + + public function getId(): ?int + { + return $this->id; + } + }; + + $form = $this->factory->create(DeleteFormType::class, null, [ + 'entity' => $entity, + ]); + + $config = $form->getConfig(); + $this->assertTrue($config->getOption('csrf_protection')); + $this->assertSame('delete42', $config->getOption('csrf_token_id')); + } + + public function testCsrfTokenIdWithEntityWithoutId(): void + { + $entity = new class {}; + + $form = $this->factory->create(DeleteFormType::class, null, [ + 'entity' => $entity, + ]); + + $csrfTokenId = $form->getConfig()->getOption('csrf_token_id'); + $this->assertStringStartsWith('delete_', $csrfTokenId); + } + + public function testRequiresEntityOption(): void + { + $this->expectException(MissingOptionsException::class); + $this->factory->create(DeleteFormType::class); + } +} diff --git a/tests/Form/LoginFormTest.php b/tests/Form/LoginFormTest.php new file mode 100644 index 0000000..3f94e64 --- /dev/null +++ b/tests/Form/LoginFormTest.php @@ -0,0 +1,58 @@ +factory = Forms::createFormFactoryBuilder() + ->addExtensions([new ValidatorExtension($validator)]) + ->getFormFactory(); + } + + public function testFormHasFields(): void + { + $form = $this->factory->create(LoginType::class); + + $this->assertTrue($form->has('username')); + $this->assertTrue($form->has('password')); + $this->assertTrue($form->has('login')); + } + + public function testSubmitValidData(): void + { + $formData = [ + 'username' => 'testuser', + 'password' => 'secret', + 'login' => true, + ]; + + $form = $this->factory->create(LoginType::class); + $form->submit($formData); + + $this->assertTrue($form->isSynchronized()); + $this->assertSame('testuser', $form->get('username')->getData()); + $this->assertSame('secret', $form->get('password')->getData()); + } + + public function testEmptyData(): void + { + $form = $this->factory->create(LoginType::class); + $form->submit([]); + + $this->assertTrue($form->isSynchronized()); + $this->assertNull($form->get('username')->getData()); + $this->assertNull($form->get('password')->getData()); + } +} diff --git a/tests/Form/PackageConstraintTypeTest.php b/tests/Form/PackageConstraintTypeTest.php new file mode 100644 index 0000000..8b43293 --- /dev/null +++ b/tests/Form/PackageConstraintTypeTest.php @@ -0,0 +1,72 @@ +factory = Forms::createFormFactoryBuilder() + ->addExtensions([new ValidatorExtension($validator)]) + ->getFormFactory(); + } + + public function testFormHasFields(): void + { + $form = $this->factory->create(PackageConstraintType::class); + + $this->assertTrue($form->has('package')); + $this->assertTrue($form->has('constraint')); + } + + public function testSubmitValidData(): void + { + $formData = [ + 'package' => 'symfony/form', + 'constraint' => '^6.0', + ]; + + $form = $this->factory->create(PackageConstraintType::class); + $form->submit($formData); + + $this->assertTrue($form->isSynchronized()); + + /** @var PackageConstraint $data */ + $data = $form->getData(); + $this->assertSame('symfony/form', $data->getPackage()); + $this->assertSame('^6.0', $data->getConstraint()); + } + + public function testSubmitEmptyDataIsInvalid(): void + { + $form = $this->factory->create(PackageConstraintType::class); + $form->submit(['package' => '', 'constraint' => '']); + + $this->assertFalse($form->isValid()); + $errors = $form->getErrors(true); + $this->assertCount(2, $errors); + } + + public function testEmptyDataCreatesDefaultObject(): void + { + $form = $this->factory->create(PackageConstraintType::class); + $form->submit([]); + + $this->assertTrue($form->isSynchronized()); + $data = $form->getData(); + $this->assertInstanceOf(PackageConstraint::class, $data); + $this->assertSame('', $data->getPackage()); + $this->assertSame('', $data->getConstraint()); + } +} diff --git a/tests/Form/PackageStabilityTypeTest.php b/tests/Form/PackageStabilityTypeTest.php new file mode 100644 index 0000000..3952f2c --- /dev/null +++ b/tests/Form/PackageStabilityTypeTest.php @@ -0,0 +1,72 @@ +factory = Forms::createFormFactoryBuilder() + ->addExtensions([new ValidatorExtension($validator)]) + ->getFormFactory(); + } + + public function testFormHasFields(): void + { + $form = $this->factory->create(PackageStabilityType::class); + + $this->assertTrue($form->has('package')); + $this->assertTrue($form->has('stability')); + } + + public function testSubmitValidData(): void + { + $formData = [ + 'package' => 'symfony/form', + 'stability' => 'stable', + ]; + + $form = $this->factory->create(PackageStabilityType::class); + $form->submit($formData); + + $this->assertTrue($form->isSynchronized()); + + /** @var PackageStability $data */ + $data = $form->getData(); + $this->assertSame('symfony/form', $data->getPackage()); + $this->assertSame('stable', $data->getStability()); + } + + public function testSubmitEmptyDataIsInvalid(): void + { + $form = $this->factory->create(PackageStabilityType::class); + $form->submit(['package' => '', 'stability' => '']); + + $this->assertFalse($form->isValid()); + $errors = $form->getErrors(true); + $this->assertCount(2, $errors); + } + + public function testEmptyDataCreatesDefaultObject(): void + { + $form = $this->factory->create(PackageStabilityType::class); + $form->submit([]); + + $this->assertTrue($form->isSynchronized()); + $data = $form->getData(); + $this->assertInstanceOf(PackageStability::class, $data); + $this->assertSame('', $data->getPackage()); + $this->assertSame('', $data->getStability()); + } +} diff --git a/tests/Integration/BuildCommandTest.php b/tests/Integration/BuildCommandTest.php index 33cf8ba..aa42b65 100644 --- a/tests/Integration/BuildCommandTest.php +++ b/tests/Integration/BuildCommandTest.php @@ -1,6 +1,6 @@ Date: Thu, 27 Nov 2025 15:09:38 +0100 Subject: [PATCH 4/5] chore: add test for DTO --- tests/DTO/AbandonedTest.php | 46 ++++++++++ tests/DTO/ArchiveTest.php | 78 +++++++++++++++++ tests/DTO/ConfigurationTest.php | 130 ++++++++++++++++++++++++++++ tests/DTO/PackageConstraintTest.php | 26 ++++++ tests/DTO/PackageStabilityTest.php | 33 +++++++ tests/DTO/RepositoryTest.php | 46 ++++++++++ 6 files changed, 359 insertions(+) create mode 100644 tests/DTO/AbandonedTest.php create mode 100644 tests/DTO/ArchiveTest.php create mode 100644 tests/DTO/ConfigurationTest.php create mode 100644 tests/DTO/PackageConstraintTest.php create mode 100644 tests/DTO/PackageStabilityTest.php create mode 100644 tests/DTO/RepositoryTest.php diff --git a/tests/DTO/AbandonedTest.php b/tests/DTO/AbandonedTest.php new file mode 100644 index 0000000..545f453 --- /dev/null +++ b/tests/DTO/AbandonedTest.php @@ -0,0 +1,46 @@ +assertSame($package, $abandoned->getPackage(), 'Package getter should return constructor value.'); + $this->assertSame($replacement, $abandoned->getReplacement(), 'Replacement getter should return constructor value.'); + } + + public function testNullableReplacement(): void + { + $package = 'vendor/package'; + $replacement = null; + + $abandoned = new Abandoned($package, $replacement); + + $this->assertSame($package, $abandoned->getPackage()); + $this->assertNull($abandoned->getReplacement(), 'Replacement can be null.'); + } + + public function testSetters(): void + { + $abandoned = new Abandoned('initial/package', null); + + $abandoned->setPackage('new/package'); + $abandoned->setReplacement('new/replacement'); + + $this->assertSame('new/package', $abandoned->getPackage()); + $this->assertSame('new/replacement', $abandoned->getReplacement()); + + // Test setting replacement to null + $abandoned->setReplacement(null); + $this->assertNull($abandoned->getReplacement()); + } +} diff --git a/tests/DTO/ArchiveTest.php b/tests/DTO/ArchiveTest.php new file mode 100644 index 0000000..30fcad5 --- /dev/null +++ b/tests/DTO/ArchiveTest.php @@ -0,0 +1,78 @@ +assertSame('', $archive->getDirectory()); + $this->assertSame('', $archive->getFormat()); + $this->assertNull($archive->getAbsoluteDirectory()); + $this->assertTrue($archive->isSkipDev()); + $this->assertSame([], $archive->getWhitelist()); + $this->assertSame([], $archive->getBlacklist()); + $this->assertNull($archive->getPrefixUrl()); + $this->assertTrue($archive->isChecksum()); + $this->assertFalse($archive->isIgnoreFilters()); + $this->assertFalse($archive->isOverrideDistType()); + $this->assertTrue($archive->isRearchive()); + } + + public function testSettersAndGetters(): void + { + $archive = new Archive(); + + $archive->setDirectory('build'); + $this->assertSame('build', $archive->getDirectory()); + + $archive->setFormat('zip'); + $this->assertSame('zip', $archive->getFormat()); + + $archive->setAbsoluteDirectory('/var/www/archive'); + $this->assertSame('/var/www/archive', $archive->getAbsoluteDirectory()); + + $archive->setSkipDev(false); + $this->assertFalse($archive->isSkipDev()); + + $whitelist = ['vendor/package1', 'vendor/package2']; + $archive->setWhitelist($whitelist); + $this->assertSame($whitelist, $archive->getWhitelist()); + + $blacklist = ['vendor/bad-package']; + $archive->setBlacklist($blacklist); + $this->assertSame($blacklist, $archive->getBlacklist()); + + $archive->setPrefixUrl('https://example.com/'); + $this->assertSame('https://example.com/', $archive->getPrefixUrl()); + + $archive->setChecksum(false); + $this->assertFalse($archive->isChecksum()); + + $archive->setIgnoreFilters(true); + $this->assertTrue($archive->isIgnoreFilters()); + + $archive->setOverrideDistType(true); + $this->assertTrue($archive->isOverrideDistType()); + + $archive->setRearchive(false); + $this->assertFalse($archive->isRearchive()); + } + + public function testSetDirectoryAndFormatWithNull(): void + { + $archive = new Archive(); + + // Should not overwrite default values if null is passed + $archive->setDirectory(null); + $archive->setFormat(null); + + $this->assertSame('', $archive->getDirectory()); + $this->assertSame('', $archive->getFormat()); + } +} diff --git a/tests/DTO/ConfigurationTest.php b/tests/DTO/ConfigurationTest.php new file mode 100644 index 0000000..9f0bd46 --- /dev/null +++ b/tests/DTO/ConfigurationTest.php @@ -0,0 +1,130 @@ +assertSame('localhost/repository', $config->getName()); + $this->assertSame('', $config->getDescription()); + $this->assertSame('http://localhost', $config->getHomepage()); + $this->assertSame(Configuration::DEFAULT_OUTPUT_DIR, $config->getOutputDir()); + $this->assertTrue($config->isOutputHtml()); + $this->assertInstanceOf(\ArrayIterator::class, $config->getRepositories()); + $this->assertSame([], $config->getRequire()); + $this->assertFalse($config->isRequireAll()); + $this->assertFalse($config->isRequireDependencies()); + $this->assertFalse($config->isRequireDevDependencies()); + $this->assertTrue($config->isRequireDependencyFilter()); + $this->assertNull($config->getIncludeFilename()); + $this->assertInstanceOf(Archive::class, $config->getArchive()); + $this->assertSame('dev', $config->getMinimumStability()); + $this->assertSame([], $config->getMinimumStabilityPerPackage()); + $this->assertFalse($config->isProviders()); + $this->assertNull($config->getTwigTemplate()); + $this->assertSame([], $config->getAbandoned()); + $this->assertSame([], $config->getBlacklist()); + $this->assertNull($config->getConfig()); + $this->assertNull($config->getNotifyBatch()); + $this->assertTrue($config->isPrettyPrint()); + $this->assertNull($config->getStripHosts()); + $this->assertNull($config->getProvidersHistorySize()); + $this->assertNull($config->getComment()); + } + + public function testSettersAndGetters(): void + { + $config = new Configuration(); + + $config->setName('vendor/package'); + $this->assertSame('vendor/package', $config->getName()); + + $config->setDescription('Test description'); + $this->assertSame('Test description', $config->getDescription()); + + $config->setHomepage('https://example.com'); + $this->assertSame('https://example.com', $config->getHomepage()); + + $config->setOutputDir('dist'); + $this->assertSame('dist', $config->getOutputDir()); + + $config->setOutputHtml(false); + $this->assertFalse($config->isOutputHtml()); + + $repo = [$this->createMock(\App\DTO\RepositoryInterface::class)]; + $config->setRepositories($repo); + $this->assertInstanceOf(\ArrayIterator::class, $config->getRepositories()); + $this->assertSame($repo, \iterator_to_array($config->getRepositories())); + + $packageConstraint = [new PackageConstraint('foo', '^1.0')]; + $config->setRequire($packageConstraint); + $this->assertSame($packageConstraint, $config->getRequire()); + + $config->setRequireAll(true); + $this->assertTrue($config->isRequireAll()); + + $config->setRequireDependencies(true); + $this->assertTrue($config->isRequireDependencies()); + + $config->setRequireDevDependencies(true); + $this->assertTrue($config->isRequireDevDependencies()); + + $this->assertTrue($config->isRequireDependencyFilter()); + + $archive = new Archive(); + $config->setArchive($archive); + $this->assertSame($archive, $config->getArchive()); + + $config->setMinimumStability('stable'); + $this->assertSame('stable', $config->getMinimumStability()); + + $config->addMinimumStabilityPerPackage('foo/bar', 'dev'); + $this->assertCount(1, $config->getMinimumStabilityPerPackage()); + $this->assertSame('foo/bar', $config->getMinimumStabilityPerPackage()[0]->getPackage()); + + $config->setProviders(true); + $this->assertTrue($config->isProviders()); + + $config->setTwigTemplate('template.twig'); + $this->assertSame('template.twig', $config->getTwigTemplate()); + + $abandoned = [new Abandoned('old/package', 'new/package')]; + $config->setAbandoned($abandoned); + $this->assertSame($abandoned, $config->getAbandoned()); + + $config->setBlacklist(['bad/package']); + $this->assertSame(['bad/package'], $config->getBlacklist()); + + $config->setConfig(['foo' => 'bar']); + $this->assertSame(['foo' => 'bar'], $config->getConfig()); + + $config->setConfig('{"x":1}'); + $this->assertSame(['x' => 1], $config->getConfig()); + + $config->setConfig(null); + $this->assertNull($config->getConfig()); + + $config->setNotifyBatch('https://notify.example.com'); + $this->assertSame('https://notify.example.com', $config->getNotifyBatch()); + + $this->assertTrue($config->isPrettyPrint()); + + $config->setStripHosts(['example.com']); + $this->assertSame(['example.com'], $config->getStripHosts()); + + $config->setProvidersHistorySize(10); + $this->assertSame(10, $config->getProvidersHistorySize()); + + $config->setComment('Some comment'); + $this->assertSame('Some comment', $config->getComment()); + } +} diff --git a/tests/DTO/PackageConstraintTest.php b/tests/DTO/PackageConstraintTest.php new file mode 100644 index 0000000..3153fa0 --- /dev/null +++ b/tests/DTO/PackageConstraintTest.php @@ -0,0 +1,26 @@ +assertSame('vendor/package', $packageConstraint->getPackage()); + $this->assertSame('^1.0', $packageConstraint->getConstraint()); + + // Test setters + $packageConstraint->setPackage('vendor/updated'); + $packageConstraint->setConstraint('^2.0'); + + // Verify updated values + $this->assertSame('vendor/updated', $packageConstraint->getPackage()); + $this->assertSame('^2.0', $packageConstraint->getConstraint()); + } +} diff --git a/tests/DTO/PackageStabilityTest.php b/tests/DTO/PackageStabilityTest.php new file mode 100644 index 0000000..f40251e --- /dev/null +++ b/tests/DTO/PackageStabilityTest.php @@ -0,0 +1,33 @@ +assertSame('vendor/package', $stability->getPackage()); + $this->assertSame('dev', $stability->getStability()); + + // Test setters + $stability->setPackage('vendor/updated'); + $stability->setStability('stable'); + + // Verify updated values + $this->assertSame('vendor/updated', $stability->getPackage()); + $this->assertSame('stable', $stability->getStability()); + + // Test null does not overwrite + $stability->setPackage(null); + $stability->setStability(null); + + $this->assertSame('vendor/updated', $stability->getPackage()); + $this->assertSame('stable', $stability->getStability()); + } +} diff --git a/tests/DTO/RepositoryTest.php b/tests/DTO/RepositoryTest.php new file mode 100644 index 0000000..5aa97bb --- /dev/null +++ b/tests/DTO/RepositoryTest.php @@ -0,0 +1,46 @@ +assertSame('https://github.com/vendor/repo', $repo->getUrl()); + $this->assertSame('vcs', $repo->getType()); + $this->assertSame('my-repo', $repo->getName()); + $this->assertSame('dist', $repo->getInstallationSource()); + } + + public function testSetters(): void + { + $repo = new Repository(); + + $repo->setUrl('https://github.com/other/repo') + ->setType('composer') + ->setName('other-repo') + ->setInstallationSource('source'); + + $this->assertSame('https://github.com/other/repo', $repo->getUrl()); + $this->assertSame('composer', $repo->getType()); + $this->assertSame('other-repo', $repo->getName()); + $this->assertSame('source', $repo->getInstallationSource()); + } + + public function testToString(): void + { + $repo = new Repository('https://github.com/vendor/repo'); + $this->assertSame('https://github.com/vendor/repo', (string) $repo); + } + + public function testGetId(): void + { + $repo = new Repository('https://github.com/vendor/repo'); + $this->assertSame(\md5('https://github.com/vendor/repo'), $repo->getId()); + } +} From 600c1f18bbebf73610a6a83454f060881981c91e Mon Sep 17 00:00:00 2001 From: Trusted97 Date: Thu, 27 Nov 2025 15:12:47 +0100 Subject: [PATCH 5/5] chore: fixing namespace --- tests/Application/ApplicationKernelTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Application/ApplicationKernelTest.php b/tests/Application/ApplicationKernelTest.php index 7057e7d..cb58b3e 100644 --- a/tests/Application/ApplicationKernelTest.php +++ b/tests/Application/ApplicationKernelTest.php @@ -1,6 +1,6 @@