From ca7c01e1aad421f08a74a904cec545e24764c2f9 Mon Sep 17 00:00:00 2001 From: gdsmith Date: Mon, 12 Jan 2026 15:06:10 +0000 Subject: [PATCH 1/3] Migrate to PSR-11 --- composer.json | 4 ++-- src/Codeception/Lib/Connector/Mezzio.php | 2 +- src/Codeception/Module/Mezzio.php | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/composer.json b/composer.json index a6929bc4..bbed0568 100644 --- a/composer.json +++ b/composer.json @@ -15,9 +15,9 @@ "php": "^8.0", "codeception/lib-innerbrowser": "^3.0 | ^4.0", "codeception/codeception": "^5.0.8", - "container-interop/container-interop": "^1.2", "laminas/laminas-diactoros": "^2.0 || ^3.0", - "mezzio/mezzio": "^3.0" + "mezzio/mezzio": "^3.0", + "psr/container": "^1|^2" }, "require-dev": { "codeception/module-rest": "^3.3" diff --git a/src/Codeception/Lib/Connector/Mezzio.php b/src/Codeception/Lib/Connector/Mezzio.php index afa29b15..81227942 100644 --- a/src/Codeception/Lib/Connector/Mezzio.php +++ b/src/Codeception/Lib/Connector/Mezzio.php @@ -6,10 +6,10 @@ use Codeception\Configuration; use Exception; -use Interop\Container\ContainerInterface; use Laminas\Diactoros\ServerRequest; use Laminas\Diactoros\UploadedFile; use Mezzio\Application; +use Psr\Container\ContainerInterface; use Symfony\Component\BrowserKit\AbstractBrowser as Client; use Symfony\Component\BrowserKit\Request as BrowserKitRequest; use Symfony\Component\BrowserKit\Response; diff --git a/src/Codeception/Module/Mezzio.php b/src/Codeception/Module/Mezzio.php index 8e2cf7b6..c2922727 100644 --- a/src/Codeception/Module/Mezzio.php +++ b/src/Codeception/Module/Mezzio.php @@ -9,9 +9,9 @@ use Codeception\Lib\Interfaces\DoctrineProvider; use Codeception\TestInterface; use Doctrine\ORM\EntityManagerInterface; -use Interop\Container\ContainerInterface; use Mezzio\Application; use PHPUnit\Framework\AssertionFailedError; +use Psr\Container\ContainerInterface; use Symfony\Component\BrowserKit\AbstractBrowser; /** @@ -33,7 +33,7 @@ * ## Public properties * * * application - instance of `\Mezzio\Application` - * * container - instance of `\Interop\Container\ContainerInterface` + * * container - instance of `\Psr\Container\ContainerInterface` * * client - BrowserKit client * */ From b6cf4ba359e917e12aa44032c648998f410c20b4 Mon Sep 17 00:00:00 2001 From: gdsmith Date: Mon, 12 Jan 2026 17:06:19 +0000 Subject: [PATCH 2/3] Include actual tests in the project --- .github/workflows/main.yml | 28 ++--- src-test/codeception.yml | 19 +++ src-test/config/routes.php | 44 +++++++ src-test/src/App/ConfigProvider.php | 59 +++++++++ src-test/src/App/Handler/HomePageHandler.php | 102 +++++++++++++++ .../App/Handler/HomePageHandlerFactory.php | 27 ++++ src-test/src/App/Handler/PingHandler.php | 20 +++ src-test/src/App/Handler/RestHandler.php | 58 +++++++++ src-test/src/App/Handler/SessionHandler.php | 37 ++++++ src-test/templates/app/home-page.phtml | 116 ++++++++++++++++++ src-test/templates/error/404.phtml | 9 ++ src-test/templates/error/error.phtml | 11 ++ src-test/templates/layout/default.phtml | 80 ++++++++++++ .../Handler/HomePageHandlerFactoryTest.php | 54 ++++++++ .../AppTest/Handler/HomePageHandlerTest.php | 65 ++++++++++ .../test/AppTest/Handler/PingHandlerTest.php | 26 ++++ src-test/tests/_bootstrap.php | 4 + src-test/tests/_data/dump.sql | 1 + src-test/tests/_output/.gitignore | 2 + src-test/tests/_support/FunctionalTester.php | 26 ++++ src-test/tests/_support/Helper/Functional.php | 10 ++ src-test/tests/functional.suite.yml | 13 ++ src-test/tests/functional/ClickCept.php | 8 ++ .../functional/FileUploadWithObjectCept.php | 14 +++ src-test/tests/functional/SeeCept.php | 7 ++ .../functional/SeeResponseCodeIsCept.php | 6 + src-test/tests/functional/SessionCest.php | 22 ++++ src-test/tests/functional/_bootstrap.php | 2 + tests/_bootstrap.php | 4 + tests/functional/ClickCept.php | 11 ++ tests/functional/FileUploadWithObjectCept.php | 17 +++ tests/functional/REST/DeleteMethodCept.php | 7 ++ .../REST/DeleteMethodWithQueryStringCept.php | 12 ++ tests/functional/REST/GetMethodCept.php | 9 ++ .../REST/GetMethodWithParamsCept.php | 13 ++ .../GetMethodWithQueryStringAndParamsCept.php | 15 +++ .../REST/GetMethodWithQueryStringCept.php | 13 ++ .../REST/GetMethodWithRequestHeaderCept.php | 12 ++ tests/functional/REST/PostMethodCept.php | 7 ++ .../REST/PostMethodFileArrayUploadCept.php | 34 +++++ .../REST/PostMethodFileUploadCept.php | 19 +++ ...tMethodFileUploadWithKeyValueArrayCept.php | 17 +++ ...ileUploadWithKeyValueInNestedArrayCept.php | 47 +++++++ .../REST/PostMethodWithFormDataCept.php | 12 ++ .../REST/PostMethodWithQueryStringCept.php | 12 ++ .../REST/PostMethodWithRawBodyCept.php | 11 ++ tests/functional/REST/PutMethodCept.php | 7 ++ .../REST/PutMethodWithQueryStringCept.php | 12 ++ .../REST/PutMethodWithRawBodyCept.php | 11 ++ tests/functional/REST/RequestUriCept.php | 9 ++ ...SeesNoLeftoversFromTheFirstRequestCept.php | 30 +++++ tests/functional/SeeCept.php | 10 ++ tests/functional/SeeResponseCodeIsCept.php | 9 ++ tests/functional/_bootstrap.php | 2 + 54 files changed, 1217 insertions(+), 15 deletions(-) create mode 100644 src-test/codeception.yml create mode 100644 src-test/config/routes.php create mode 100644 src-test/src/App/ConfigProvider.php create mode 100644 src-test/src/App/Handler/HomePageHandler.php create mode 100644 src-test/src/App/Handler/HomePageHandlerFactory.php create mode 100644 src-test/src/App/Handler/PingHandler.php create mode 100644 src-test/src/App/Handler/RestHandler.php create mode 100644 src-test/src/App/Handler/SessionHandler.php create mode 100644 src-test/templates/app/home-page.phtml create mode 100644 src-test/templates/error/404.phtml create mode 100644 src-test/templates/error/error.phtml create mode 100644 src-test/templates/layout/default.phtml create mode 100644 src-test/test/AppTest/Handler/HomePageHandlerFactoryTest.php create mode 100644 src-test/test/AppTest/Handler/HomePageHandlerTest.php create mode 100644 src-test/test/AppTest/Handler/PingHandlerTest.php create mode 100644 src-test/tests/_bootstrap.php create mode 100644 src-test/tests/_data/dump.sql create mode 100644 src-test/tests/_output/.gitignore create mode 100644 src-test/tests/_support/FunctionalTester.php create mode 100644 src-test/tests/_support/Helper/Functional.php create mode 100644 src-test/tests/functional.suite.yml create mode 100644 src-test/tests/functional/ClickCept.php create mode 100644 src-test/tests/functional/FileUploadWithObjectCept.php create mode 100644 src-test/tests/functional/SeeCept.php create mode 100644 src-test/tests/functional/SeeResponseCodeIsCept.php create mode 100644 src-test/tests/functional/SessionCest.php create mode 100644 src-test/tests/functional/_bootstrap.php create mode 100644 tests/_bootstrap.php create mode 100644 tests/functional/ClickCept.php create mode 100644 tests/functional/FileUploadWithObjectCept.php create mode 100644 tests/functional/REST/DeleteMethodCept.php create mode 100644 tests/functional/REST/DeleteMethodWithQueryStringCept.php create mode 100644 tests/functional/REST/GetMethodCept.php create mode 100644 tests/functional/REST/GetMethodWithParamsCept.php create mode 100644 tests/functional/REST/GetMethodWithQueryStringAndParamsCept.php create mode 100644 tests/functional/REST/GetMethodWithQueryStringCept.php create mode 100644 tests/functional/REST/GetMethodWithRequestHeaderCept.php create mode 100644 tests/functional/REST/PostMethodCept.php create mode 100644 tests/functional/REST/PostMethodFileArrayUploadCept.php create mode 100644 tests/functional/REST/PostMethodFileUploadCept.php create mode 100644 tests/functional/REST/PostMethodFileUploadWithKeyValueArrayCept.php create mode 100644 tests/functional/REST/PostMethodFileUploadWithKeyValueInNestedArrayCept.php create mode 100644 tests/functional/REST/PostMethodWithFormDataCept.php create mode 100644 tests/functional/REST/PostMethodWithQueryStringCept.php create mode 100644 tests/functional/REST/PostMethodWithRawBodyCept.php create mode 100644 tests/functional/REST/PutMethodCept.php create mode 100644 tests/functional/REST/PutMethodWithQueryStringCept.php create mode 100644 tests/functional/REST/PutMethodWithRawBodyCept.php create mode 100644 tests/functional/REST/RequestUriCept.php create mode 100644 tests/functional/REST/SecondRequestSeesNoLeftoversFromTheFirstRequestCept.php create mode 100644 tests/functional/SeeCept.php create mode 100644 tests/functional/SeeResponseCodeIsCept.php create mode 100644 tests/functional/_bootstrap.php diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 2d2bb5ab..afed5f58 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -20,24 +20,22 @@ jobs: php-version: ${{ matrix.php }} coverage: none - - name: Checkout Mezzio Sample - uses: actions/checkout@v2 - with: - repository: Naktibalda/codeception-mezzio-tests - path: framework-tests - ref: '5.0' - submodules: recursive - - name: Install Mezzio Sample - run: composer update --no-dev --prefer-dist --no-interaction - working-directory: framework-tests + run: composer create-project mezzio/mezzio-skeleton --prefer-dist --no-interaction framework-tests + - name: Copy The Tests/Site + run: cp -rf src-test/{src,templates,test,tests,codeception.yml} framework-tests/ - - name: Validate composer.json and composer.lock - run: composer validate + - name: Copy The Config + run: cp -f src-test/config/routes.php framework-tests/config/ - - name: Install dependencies - run: composer update --prefer-dist --no-progress --no-interaction --no-suggest + - name: Remove The Default + run: composer remove mezzio/mezzio-fastroute + working-directory: framework-tests + + - name: Install The Dependencies + run: composer require -n mezzio/mezzio-laminasrouter mezzio/mezzio-laminasviewrenderer:^2 mezzio/mezzio-session-ext -n + working-directory: framework-tests - name: Run test suite - run: php vendor/bin/codecept run functional -c framework-tests + run: php -d register_argc_argv=On vendor/bin/codecept run functional -c framework-tests diff --git a/src-test/codeception.yml b/src-test/codeception.yml new file mode 100644 index 00000000..7f49380e --- /dev/null +++ b/src-test/codeception.yml @@ -0,0 +1,19 @@ +actor: Tester +bootstrap: _bootstrap.php +paths: + tests: tests + log: tests/_output + data: tests/_data + support: tests/_support + envs: tests/_envs + output: tests/_output +settings: + colors: true + memory_limit: 1024M +modules: + config: + Db: + dsn: '' + user: '' + password: '' + dump: tests/_data/dump.sql diff --git a/src-test/config/routes.php b/src-test/config/routes.php new file mode 100644 index 00000000..ad7b48c1 --- /dev/null +++ b/src-test/config/routes.php @@ -0,0 +1,44 @@ +get('/', App\Handler\HomePageHandler::class, 'home'); + * $app->post('/album', App\Handler\AlbumCreateHandler::class, 'album.create'); + * $app->put('/album/:id', App\Handler\AlbumUpdateHandler::class, 'album.put'); + * $app->patch('/album/:id', App\Handler\AlbumUpdateHandler::class, 'album.patch'); + * $app->delete('/album/:id', App\Handler\AlbumDeleteHandler::class, 'album.delete'); + * + * Or with multiple request methods: + * + * $app->route('/contact', App\Handler\ContactHandler::class, ['GET', 'POST', ...], 'contact'); + * + * Or handling all request methods: + * + * $app->route('/contact', App\Handler\ContactHandler::class)->setName('contact'); + * + * or: + * + * $app->route( + * '/contact', + * App\Handler\ContactHandler::class, + * Mezzio\Router\Route::HTTP_METHOD_ANY, + * 'contact' + * ); + */ +return function (Application $app, MiddlewareFactory $factory, ContainerInterface $container) : void { + $app->get('/', App\Handler\HomePageHandler::class, 'home'); + $app->get('/api/ping', App\Handler\PingHandler::class, 'api.ping'); + $app->route('/rest', App\Handler\RestHandler::class, ['GET', 'POST', 'PUT', 'DELETE'], 'rest'); + + $app->get('/session/:action', [ + Mezzio\Session\SessionMiddleware::class, + App\Handler\SessionHandler::class, + ], 'session.test'); +}; diff --git a/src-test/src/App/ConfigProvider.php b/src-test/src/App/ConfigProvider.php new file mode 100644 index 00000000..c648cef7 --- /dev/null +++ b/src-test/src/App/ConfigProvider.php @@ -0,0 +1,59 @@ + $this->getDependencies(), + 'templates' => $this->getTemplates(), + ]; + } + + /** + * Returns the container dependencies + */ + public function getDependencies() : array + { + return [ + 'invokables' => [ + Handler\PingHandler::class => Handler\PingHandler::class, + Handler\RestHandler::class => Handler\RestHandler::class, + Handler\SessionHandler::class => Handler\SessionHandler::class, + ], + 'factories' => [ + Handler\HomePageHandler::class => Handler\HomePageHandlerFactory::class, + ], + ]; + } + + /** + * Returns the templates configuration + */ + public function getTemplates() : array + { + return [ + 'paths' => [ + 'app' => ['templates/app'], + 'error' => ['templates/error'], + 'layout' => ['templates/layout'], + ], + ]; + } +} diff --git a/src-test/src/App/Handler/HomePageHandler.php b/src-test/src/App/Handler/HomePageHandler.php new file mode 100644 index 00000000..4d555125 --- /dev/null +++ b/src-test/src/App/Handler/HomePageHandler.php @@ -0,0 +1,102 @@ +containerName = $containerName; + $this->router = $router; + $this->template = $template; + } + + public function handle(ServerRequestInterface $request) : ResponseInterface + { + if ($this->template === null) { + return new JsonResponse([ + 'welcome' => 'Congratulations! You have installed the mezzio skeleton application.', + 'docsUrl' => 'https://docs.mezzio.dev/mezzio/', + ]); + } + + $data = []; + + switch ($this->containerName) { + case 'Aura\Di\Container': + $data['containerName'] = 'Aura.Di'; + $data['containerDocs'] = 'http://auraphp.com/packages/2.x/Di.html'; + break; + case 'Pimple\Container': + $data['containerName'] = 'Pimple'; + $data['containerDocs'] = 'https://pimple.symfony.com/'; + break; + case 'Laminas\ServiceManager\ServiceManager': + $data['containerName'] = 'Laminas Servicemanager'; + $data['containerDocs'] = 'https://docs.laminas.dev/laminas-servicemanager/'; + break; + case 'Auryn\Injector': + $data['containerName'] = 'Auryn'; + $data['containerDocs'] = 'https://github.com/rdlowrey/Auryn'; + break; + case 'Symfony\Component\DependencyInjection\ContainerBuilder': + $data['containerName'] = 'Symfony DI Container'; + $data['containerDocs'] = 'https://symfony.com/doc/current/service_container.html'; + break; + case 'Zend\DI\Config\ContainerWrapper': + case 'DI\Container': + $data['containerName'] = 'PHP-DI'; + $data['containerDocs'] = 'http://php-di.org'; + break; + } + + if ($this->router instanceof Router\AuraRouter) { + $data['routerName'] = 'Aura.Router'; + $data['routerDocs'] = 'http://auraphp.com/packages/2.x/Router.html'; + } elseif ($this->router instanceof Router\FastRouteRouter) { + $data['routerName'] = 'FastRoute'; + $data['routerDocs'] = 'https://github.com/nikic/FastRoute'; + } elseif ($this->router instanceof Router\LaminasRouter) { + $data['routerName'] = 'Laminas Router'; + $data['routerDocs'] = 'https://docs.laminas.dev/laminas-router/'; + } + + if ($this->template instanceof PlatesRenderer) { + $data['templateName'] = 'Plates'; + $data['templateDocs'] = 'http://platesphp.com/'; + } elseif ($this->template instanceof TwigRenderer) { + $data['templateName'] = 'Twig'; + $data['templateDocs'] = 'http://twig.sensiolabs.org/documentation'; + } elseif ($this->template instanceof LaminasViewRenderer) { + $data['templateName'] = 'Laminas View'; + $data['templateDocs'] = 'https://docs.laminas.dev/laminas-view/'; + } + + return new HtmlResponse($this->template->render('app::home-page', $data)); + } +} diff --git a/src-test/src/App/Handler/HomePageHandlerFactory.php b/src-test/src/App/Handler/HomePageHandlerFactory.php new file mode 100644 index 00000000..af9a9892 --- /dev/null +++ b/src-test/src/App/Handler/HomePageHandlerFactory.php @@ -0,0 +1,27 @@ +get(RouterInterface::class); + $template = $container->has(TemplateRendererInterface::class) + ? $container->get(TemplateRendererInterface::class) + : ($container->has(\Zend\Expressive\Template\TemplateRendererInterface::class) + ? $container->get(\Zend\Expressive\Template\TemplateRendererInterface::class) + : null); + + return new HomePageHandler(get_class($container), $router, $template); + } +} diff --git a/src-test/src/App/Handler/PingHandler.php b/src-test/src/App/Handler/PingHandler.php new file mode 100644 index 00000000..3fc16259 --- /dev/null +++ b/src-test/src/App/Handler/PingHandler.php @@ -0,0 +1,20 @@ + time()]); + } +} diff --git a/src-test/src/App/Handler/RestHandler.php b/src-test/src/App/Handler/RestHandler.php new file mode 100644 index 00000000..b2471166 --- /dev/null +++ b/src-test/src/App/Handler/RestHandler.php @@ -0,0 +1,58 @@ +getHeader('X-Auth-Token'); + if (count($tokenHeader) > 0) { + $tokenHeaderValue = $tokenHeader[0]; + } else { + $tokenHeaderValue = null; + } + $data = array( + 'requestMethod' => $request->getMethod(), + 'requestUri' => $request->getRequestTarget(), + 'queryParams' => $request->getQueryParams(), + 'formParams' => $request->getParsedBody(), + 'rawBody' => (string)$request->getBody(), + 'headers' => $request->getHeaders(), + 'X-Auth-Token' => $tokenHeaderValue, + 'files' => $this->filesToArray($request->getUploadedFiles()), + ); + return new JsonResponse($data); + } + + private function filesToArray(array $files) + { + $result = []; + foreach ($files as $fieldName => $uploadedFile) { + /** + * @var $uploadedFile UploadedFile|array + */ + if (is_array($uploadedFile)) { + $result[$fieldName] = $this->filesToArray($uploadedFile); + } else { + $result[$fieldName] = [ + 'name' => $uploadedFile->getClientFilename(), + 'tmp_name' => ReflectionHelper::readPrivateProperty($uploadedFile, 'file'), + 'size' => $uploadedFile->getSize(), + 'type' => $uploadedFile->getClientMediaType(), + 'error' => $uploadedFile->getError(), + ]; + } + } + return $result; + } +} diff --git a/src-test/src/App/Handler/SessionHandler.php b/src-test/src/App/Handler/SessionHandler.php new file mode 100644 index 00000000..5e861dfc --- /dev/null +++ b/src-test/src/App/Handler/SessionHandler.php @@ -0,0 +1,37 @@ +getAttribute(SessionMiddleware::SESSION_ATTRIBUTE); + + $action = $request->getAttribute('action'); + if ('set' === $action) { + $session->set('name', 'Somebody'); + + return new TextResponse('Name set'); + } + + if ('get' === $action) { + $name = $session->get('name', 'Nobody'); + + return new TextResponse(sprintf('The name is: %s', $name)); + } + + return new EmptyResponse(); + } +} diff --git a/src-test/templates/app/home-page.phtml b/src-test/templates/app/home-page.phtml new file mode 100644 index 00000000..2d9a728e --- /dev/null +++ b/src-test/templates/app/home-page.phtml @@ -0,0 +1,116 @@ +headTitle('Home'); ?> + +
+

Welcome to mezzio

+

+ Congratulations! You have successfully installed the + mezzio skeleton application. + This skeleton can serve as a simple starting point for you to begin building your application. +

+

+ Mezzio builds on laminas-stratigility to provide a minimalist PSR-7 middleware framework for PHP. +

+
+ +
+
+

+ + Agile & Lean + +

+

+ Mezzio is fast, small and perfect for rapid application development, prototyping and api's. + You decide how you extend it and choose the best packages from major framework or standalone projects. +

+
+ +
+

+ + HTTP Messages + +

+

+ HTTP messages are the foundation of web development. Web browsers and HTTP clients such as cURL create + HTTP request messages that are sent to a web server, which provides an HTTP response message. + Server-side code receives an HTTP request message, and returns an HTTP response message. +

+
+ +
+

+ + Middleware + +

+

+ Middleware is code that exists between the request and response, and which can take the incoming + request, perform actions based on it, and either complete the response or pass delegation on to the + next middleware in the queue. Your application is easily extended with custom middleware created by + yourself or others. +

+
+
+ +
+
+

+ + Containers + +

+

+ Mezzio promotes and advocates the usage of Dependency Injection/Inversion of Control containers + when writing your applications. Mezzio supports multiple containers which typehints against + PSR Container. +

+ containerName)) : ?> +

+ + Get started with containerName ?>. + +

+ +
+ +
+

+ + Routers + +

+

+ One fundamental feature of mezzio is that it provides mechanisms for implementing dynamic + routing, a feature required in most modern web applications. Mezzio ships with multiple adapters. +

+ routerName)) : ?> +

+ + Get started with routerName ?>. + +

+ +
+ +
+

+ + Templating + +

+

+ By default, no middleware in Mezzio is templated. We do not even provide a default templating + engine, as the choice of templating engine is often very specific to the project and/or organization. + However, Mezzio does provide abstraction for templating, which allows you to write middleware that + is engine-agnostic. +

+ templateName)) : ?> +

+ + Get started with templateName ?>. + +

+ +
+
diff --git a/src-test/templates/error/404.phtml b/src-test/templates/error/404.phtml new file mode 100644 index 00000000..93a85578 --- /dev/null +++ b/src-test/templates/error/404.phtml @@ -0,0 +1,9 @@ +headTitle('404 Not Found'); ?> + +

Oops!

+

This is awkward.

+

We encountered a 404 Not Found error.

+

+ You are looking for something that doesn't exist or may have moved. Check out one of the links on this page + or head back to Home. +

diff --git a/src-test/templates/error/error.phtml b/src-test/templates/error/error.phtml new file mode 100644 index 00000000..5f299512 --- /dev/null +++ b/src-test/templates/error/error.phtml @@ -0,0 +1,11 @@ +headTitle(sprintf('%d %s', $this->status, $this->reason)); ?> + +

Oops!

+

This is awkward.

+

We encountered a escapeHtml(sprintf('%d %s', $this->status, $this->reason))?> error.

+status == 404) : ?> +

+ You are looking for something that doesn't exist or may have moved. Check out one of the links on this page + or head back to Home. +

+ diff --git a/src-test/templates/layout/default.phtml b/src-test/templates/layout/default.phtml new file mode 100644 index 00000000..a225b31b --- /dev/null +++ b/src-test/templates/layout/default.phtml @@ -0,0 +1,80 @@ +headLink() + ->prependStylesheet('https://use.fontawesome.com/releases/v5.0.6/css/all.css') + ->prependStylesheet('https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css'); +$this->inlineScript() + ->prependFile('https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js') + ->prependFile('https://code.jquery.com/jquery-3.3.1.min.js'); +?> + + + + + + + + headTitle('mezzio')->setSeparator(' - ')->setAutoEscape(false)?> + headMeta()?> + headLink()?> + + + +
+ +
+ +
+
+ content?> +
+
+ +
+
+
+

+ © 2005 - by Laminas Technologies Ltd. All rights reserved. +

+
+
+ + inlineScript()?> + + diff --git a/src-test/test/AppTest/Handler/HomePageHandlerFactoryTest.php b/src-test/test/AppTest/Handler/HomePageHandlerFactoryTest.php new file mode 100644 index 00000000..5e0cbd08 --- /dev/null +++ b/src-test/test/AppTest/Handler/HomePageHandlerFactoryTest.php @@ -0,0 +1,54 @@ +container = $this->prophesize(ContainerInterface::class); + $router = $this->prophesize(RouterInterface::class); + + $this->container->get(RouterInterface::class)->willReturn($router); + } + + public function testFactoryWithoutTemplate() + { + $factory = new HomePageHandlerFactory(); + $this->container->has(TemplateRendererInterface::class)->willReturn(false); + $this->container->has(\Zend\Expressive\Template\TemplateRendererInterface::class)->willReturn(false); + + $this->assertInstanceOf(HomePageHandlerFactory::class, $factory); + + $homePage = $factory($this->container->reveal()); + + $this->assertInstanceOf(HomePageHandler::class, $homePage); + } + + public function testFactoryWithTemplate() + { + $this->container->has(TemplateRendererInterface::class)->willReturn(true); + $this->container + ->get(TemplateRendererInterface::class) + ->willReturn($this->prophesize(TemplateRendererInterface::class)); + + $factory = new HomePageHandlerFactory(); + + $homePage = $factory($this->container->reveal()); + + $this->assertInstanceOf(HomePageHandler::class, $homePage); + } +} diff --git a/src-test/test/AppTest/Handler/HomePageHandlerTest.php b/src-test/test/AppTest/Handler/HomePageHandlerTest.php new file mode 100644 index 00000000..27b0a2c6 --- /dev/null +++ b/src-test/test/AppTest/Handler/HomePageHandlerTest.php @@ -0,0 +1,65 @@ +container = $this->prophesize(ContainerInterface::class); + $this->router = $this->prophesize(RouterInterface::class); + } + + public function testReturnsJsonResponseWhenNoTemplateRendererProvided() + { + $homePage = new HomePageHandler( + get_class($this->container->reveal()), + $this->router->reveal(), + null + ); + $response = $homePage->handle( + $this->prophesize(ServerRequestInterface::class)->reveal() + ); + + $this->assertInstanceOf(JsonResponse::class, $response); + } + + public function testReturnsHtmlResponseWhenTemplateRendererProvided() + { + $renderer = $this->prophesize(TemplateRendererInterface::class); + $renderer + ->render('app::home-page', Argument::type('array')) + ->willReturn(''); + + $homePage = new HomePageHandler( + get_class($this->container->reveal()), + $this->router->reveal(), + $renderer->reveal() + ); + + $response = $homePage->handle( + $this->prophesize(ServerRequestInterface::class)->reveal() + ); + + $this->assertInstanceOf(HtmlResponse::class, $response); + } +} diff --git a/src-test/test/AppTest/Handler/PingHandlerTest.php b/src-test/test/AppTest/Handler/PingHandlerTest.php new file mode 100644 index 00000000..83d3aae2 --- /dev/null +++ b/src-test/test/AppTest/Handler/PingHandlerTest.php @@ -0,0 +1,26 @@ +handle( + $this->prophesize(ServerRequestInterface::class)->reveal() + ); + + $json = json_decode((string) $response->getBody()); + + $this->assertInstanceOf(JsonResponse::class, $response); + $this->assertTrue(isset($json->ack)); + } +} diff --git a/src-test/tests/_bootstrap.php b/src-test/tests/_bootstrap.php new file mode 100644 index 00000000..b74d4c0d --- /dev/null +++ b/src-test/tests/_bootstrap.php @@ -0,0 +1,4 @@ +wantTo('click a link and see a change in url'); + +$I->amOnPage('/'); +$I->click('Ping Test'); +$I->seeCurrentUrlEquals('/api/ping'); +$I->see("ack"); diff --git a/src-test/tests/functional/FileUploadWithObjectCept.php b/src-test/tests/functional/FileUploadWithObjectCept.php new file mode 100644 index 00000000..3770a644 --- /dev/null +++ b/src-test/tests/functional/FileUploadWithObjectCept.php @@ -0,0 +1,14 @@ +wantTo('upload file'); + +$I->sendPOST('/rest', [], [ + 'dump' => new Laminas\Diactoros\UploadedFile(codecept_data_dir('dump.sql'), 57, 0, 'dump.sql', 'text/plain') +]); +$I->seeResponseIsJson(); +$I->seeResponseContainsJson(['files' => [ + 'dump' => [ + 'name' => 'dump.sql', + 'size' => 57, + ] +]]); diff --git a/src-test/tests/functional/SeeCept.php b/src-test/tests/functional/SeeCept.php new file mode 100644 index 00000000..29914468 --- /dev/null +++ b/src-test/tests/functional/SeeCept.php @@ -0,0 +1,7 @@ +wantTo('perform request and match text in response'); + +$I->amOnPage('/'); +$I->seeResponseCodeIs(200); +$I->see('HTTP messages are the foundation of web development.'); diff --git a/src-test/tests/functional/SeeResponseCodeIsCept.php b/src-test/tests/functional/SeeResponseCodeIsCept.php new file mode 100644 index 00000000..21b21620 --- /dev/null +++ b/src-test/tests/functional/SeeResponseCodeIsCept.php @@ -0,0 +1,6 @@ +wantTo('see different response code'); + +$I->amOnPage('/error'); +$I->seeResponseCodeIs(404); diff --git a/src-test/tests/functional/SessionCest.php b/src-test/tests/functional/SessionCest.php new file mode 100644 index 00000000..47d9a052 --- /dev/null +++ b/src-test/tests/functional/SessionCest.php @@ -0,0 +1,22 @@ +amOnPage('/session/get'); + $I->see('Nobody'); + + $I->amOnPage('/session/set'); + $I->see('Name set'); + + $I->amOnPage('/session/get'); + $I->see('Somebody'); + } + + public function secondTimeUsingSessionMustNotBeAffectedByFirstTime(FunctionalTester $I) + { + $I->amOnPage('/session/get'); + $I->see('Nobody'); + } +} diff --git a/src-test/tests/functional/_bootstrap.php b/src-test/tests/functional/_bootstrap.php new file mode 100644 index 00000000..8a885558 --- /dev/null +++ b/src-test/tests/functional/_bootstrap.php @@ -0,0 +1,2 @@ +wantTo('click a link and see a change in url'); + +$I->amOnPage('/'); +$I->click('Ping Test'); +$I->seeCurrentUrlEquals('/api/ping'); +$I->see("ack"); diff --git a/tests/functional/FileUploadWithObjectCept.php b/tests/functional/FileUploadWithObjectCept.php new file mode 100644 index 00000000..76854610 --- /dev/null +++ b/tests/functional/FileUploadWithObjectCept.php @@ -0,0 +1,17 @@ +wantTo('upload file'); + +$I->sendPOST('/rest', [], [ + 'dump' => new Laminas\Diactoros\UploadedFile(codecept_data_dir('dump.sql'), 57, 0, 'dump.sql', 'text/plain') +]); +$I->seeResponseIsJson(); +$I->seeResponseContainsJson(['files' => [ + 'dump' => [ + 'name' => 'dump.sql', + 'size' => 57, + ] +]]); diff --git a/tests/functional/REST/DeleteMethodCept.php b/tests/functional/REST/DeleteMethodCept.php new file mode 100644 index 00000000..bd297778 --- /dev/null +++ b/tests/functional/REST/DeleteMethodCept.php @@ -0,0 +1,7 @@ +wantTo('make DELETE request'); + +$I->sendDELETE('/rest'); +$I->seeResponseIsJson(); +$I->seeResponseContainsJson(array('requestMethod' => 'DELETE')); diff --git a/tests/functional/REST/DeleteMethodWithQueryStringCept.php b/tests/functional/REST/DeleteMethodWithQueryStringCept.php new file mode 100644 index 00000000..a2e6f570 --- /dev/null +++ b/tests/functional/REST/DeleteMethodWithQueryStringCept.php @@ -0,0 +1,12 @@ +wantTo('make DELETE request with query string'); +$I->sendDELETE('/rest?param=value'); + +$expectedResponse = array( + 'requestMethod' => 'DELETE', + 'queryParams' => array( + 'param' => 'value' + ), +); +$I->seeResponseContainsJson($expectedResponse); diff --git a/tests/functional/REST/GetMethodCept.php b/tests/functional/REST/GetMethodCept.php new file mode 100644 index 00000000..6832b888 --- /dev/null +++ b/tests/functional/REST/GetMethodCept.php @@ -0,0 +1,9 @@ +wantTo('make GET request'); +$I->sendGET('/rest'); +$I->seeResponseIsJson(); +$expectedResponse = array( + 'requestMethod' => 'GET', +); +$I->seeResponseContainsJson($expectedResponse); diff --git a/tests/functional/REST/GetMethodWithParamsCept.php b/tests/functional/REST/GetMethodWithParamsCept.php new file mode 100644 index 00000000..a141e027 --- /dev/null +++ b/tests/functional/REST/GetMethodWithParamsCept.php @@ -0,0 +1,13 @@ +wantTo('make GET request with parameters'); +$I->sendGET('/rest',['param' => 'value']); +$I->seeResponseIsJson(); + +$expectedResponse = array( + 'requestMethod' => 'GET', + 'queryParams' => array( + 'param' => 'value' + ), +); +$I->seeResponseContainsJson($expectedResponse); diff --git a/tests/functional/REST/GetMethodWithQueryStringAndParamsCept.php b/tests/functional/REST/GetMethodWithQueryStringAndParamsCept.php new file mode 100644 index 00000000..122eebc7 --- /dev/null +++ b/tests/functional/REST/GetMethodWithQueryStringAndParamsCept.php @@ -0,0 +1,15 @@ +wantTo('make GET request with query string'); +$I->sendGET('/rest?param1=value1', ['param2' => 'value2']); +$I->seeResponseIsJson(); + +$expectedResponse = [ + 'requestMethod' => 'GET', + 'requestUri' => '/rest?param1=value1¶m2=value2', + 'queryParams' => [ + 'param1' => 'value1', + 'param2' => 'value2', + ], +]; +$I->seeResponseContainsJson($expectedResponse); diff --git a/tests/functional/REST/GetMethodWithQueryStringCept.php b/tests/functional/REST/GetMethodWithQueryStringCept.php new file mode 100644 index 00000000..2af31b89 --- /dev/null +++ b/tests/functional/REST/GetMethodWithQueryStringCept.php @@ -0,0 +1,13 @@ +wantTo('make GET request with query string'); +$I->sendGET('/rest?param=value'); +$I->seeResponseIsJson(); + +$expectedResponse = array( + 'requestMethod' => 'GET', + 'queryParams' => array( + 'param' => 'value' + ), +); +$I->seeResponseContainsJson($expectedResponse); diff --git a/tests/functional/REST/GetMethodWithRequestHeaderCept.php b/tests/functional/REST/GetMethodWithRequestHeaderCept.php new file mode 100644 index 00000000..4d6cd364 --- /dev/null +++ b/tests/functional/REST/GetMethodWithRequestHeaderCept.php @@ -0,0 +1,12 @@ +wantTo('make GET request with custom request header'); +$I->haveHttpHeader('X-Auth-Token', 'verySecureToken'); +$I->sendGET('/rest'); + +$expectedResponse = array( + 'requestMethod' => 'GET', + 'queryParams' => array(), + 'X-Auth-Token' => 'verySecureToken', +); +$I->seeResponseContainsJson($expectedResponse); \ No newline at end of file diff --git a/tests/functional/REST/PostMethodCept.php b/tests/functional/REST/PostMethodCept.php new file mode 100644 index 00000000..7c4f0e8e --- /dev/null +++ b/tests/functional/REST/PostMethodCept.php @@ -0,0 +1,7 @@ +wantTo('make POST request'); + +$I->sendPOST('/rest'); +$I->seeResponseIsJson(); +$I->seeResponseContainsJson(array('requestMethod' => 'POST')); diff --git a/tests/functional/REST/PostMethodFileArrayUploadCept.php b/tests/functional/REST/PostMethodFileArrayUploadCept.php new file mode 100644 index 00000000..6b27c002 --- /dev/null +++ b/tests/functional/REST/PostMethodFileArrayUploadCept.php @@ -0,0 +1,34 @@ +wantTo('upload file'); + +$I->sendPOST('/rest', [], ['dump' => [ + [ + 'name' => 'dump1.sql', + 'type' => 'text/plain', + 'size' => 57, + 'tmp_name' => codecept_data_dir('dump.sql'), + 'error' => 0, + ], + [ + 'name' => 'dump2.sql', + 'type' => 'text/sql', + 'size' => 60, + 'tmp_name' => codecept_data_dir('dump.sql'), + 'error' => 0, + ], + ] +]); +$I->seeResponseIsJson(); +$I->seeResponseContainsJson(['files' => [ + 'dump' => [ + [ + 'name' => 'dump1.sql', + 'size' => 57, + ], + [ + 'name' => 'dump2.sql', + 'size' => 60, + ] + ] +]]); diff --git a/tests/functional/REST/PostMethodFileUploadCept.php b/tests/functional/REST/PostMethodFileUploadCept.php new file mode 100644 index 00000000..e7c4ec51 --- /dev/null +++ b/tests/functional/REST/PostMethodFileUploadCept.php @@ -0,0 +1,19 @@ +wantTo('upload file'); + +$I->sendPOST('/rest', [], ['dump' => [ + 'name' => 'dump.sql', + 'type' => 'text/plain', + 'size' => 57, + 'tmp_name' => codecept_data_dir('dump.sql'), + 'error' => 0, + ], +]); +$I->seeResponseIsJson(); +$I->seeResponseContainsJson(['files' => [ + 'dump' => [ + 'name' => 'dump.sql', + 'size' => 57, + ] +]]); diff --git a/tests/functional/REST/PostMethodFileUploadWithKeyValueArrayCept.php b/tests/functional/REST/PostMethodFileUploadWithKeyValueArrayCept.php new file mode 100644 index 00000000..b5302069 --- /dev/null +++ b/tests/functional/REST/PostMethodFileUploadWithKeyValueArrayCept.php @@ -0,0 +1,17 @@ +wantTo('upload file using simple key-value array'); + +$uploadFile = codecept_data_dir('dump.sql'); + +$I->sendPOST('/rest', [], ['dump' => $uploadFile]); +$I->seeResponseIsJson(); +$I->seeResponseContainsJson(['files' => [ + 'dump' => [ + 'name' => 'dump.sql', + 'tmp_name' => $uploadFile, + 'type' => 'text/plain', + 'size' => 57, + 'error' => 0, + ] +]]); diff --git a/tests/functional/REST/PostMethodFileUploadWithKeyValueInNestedArrayCept.php b/tests/functional/REST/PostMethodFileUploadWithKeyValueInNestedArrayCept.php new file mode 100644 index 00000000..be921396 --- /dev/null +++ b/tests/functional/REST/PostMethodFileUploadWithKeyValueInNestedArrayCept.php @@ -0,0 +1,47 @@ +wantTo('upload file using simple key-value array'); + +$uploadFile = codecept_data_dir('dump.sql'); + +$I->sendPOST('/rest', [], [ + 'dump1' => $uploadFile, + 'nested' => [ + 'dump2' => $uploadFile, + ], + 'nested1' => [ + 'nested2' => [ + 'dump3' => $uploadFile, + ] + ], +]); +$I->seeResponseIsJson(); +$I->seeResponseContainsJson(['files' => [ + 'dump1' => [ + 'name' => 'dump.sql', + 'tmp_name' => $uploadFile, + 'type' => 'text/plain', + 'size' => 57, + 'error' => 0, + ], + 'nested' => [ + 'dump2' => [ + 'name' => 'dump.sql', + 'tmp_name' => $uploadFile, + 'type' => 'text/plain', + 'size' => 57, + 'error' => 0, + ], + ], + 'nested1' => [ + 'nested2' => [ + 'dump3' => [ + 'name' => 'dump.sql', + 'tmp_name' => $uploadFile, + 'type' => 'text/plain', + 'size' => 57, + 'error' => 0, + ], + ], + ], +]]); diff --git a/tests/functional/REST/PostMethodWithFormDataCept.php b/tests/functional/REST/PostMethodWithFormDataCept.php new file mode 100644 index 00000000..4c698a80 --- /dev/null +++ b/tests/functional/REST/PostMethodWithFormDataCept.php @@ -0,0 +1,12 @@ +wantTo('make POST request with form data'); +$I->sendPOST('/rest', array('foo' => 'bar')); + +$expectedResponse = array( + 'requestMethod' => 'POST', + 'formParams' => array( + 'foo' => 'bar' + ), +); +$I->seeResponseContainsJson($expectedResponse); diff --git a/tests/functional/REST/PostMethodWithQueryStringCept.php b/tests/functional/REST/PostMethodWithQueryStringCept.php new file mode 100644 index 00000000..2012b996 --- /dev/null +++ b/tests/functional/REST/PostMethodWithQueryStringCept.php @@ -0,0 +1,12 @@ +wantTo('make POST request with query string'); +$I->sendPOST('/rest?param=value'); + +$expectedResponse = array( + 'requestMethod' => 'POST', + 'queryParams' => array( + 'param' => 'value' + ), +); +$I->seeResponseContainsJson($expectedResponse); diff --git a/tests/functional/REST/PostMethodWithRawBodyCept.php b/tests/functional/REST/PostMethodWithRawBodyCept.php new file mode 100644 index 00000000..f5e7e535 --- /dev/null +++ b/tests/functional/REST/PostMethodWithRawBodyCept.php @@ -0,0 +1,11 @@ +wantTo('make POST request with raw body'); +$I->sendPOST('/rest', 'raw request body'); + +$expectedResponse = array( + 'requestMethod' => 'POST', + 'formParams' => array(), + 'rawBody' => 'raw request body', +); +$I->seeResponseContainsJson($expectedResponse); diff --git a/tests/functional/REST/PutMethodCept.php b/tests/functional/REST/PutMethodCept.php new file mode 100644 index 00000000..c9ad65c3 --- /dev/null +++ b/tests/functional/REST/PutMethodCept.php @@ -0,0 +1,7 @@ +wantTo('make PUT request'); + +$I->sendPUT('/rest'); +$I->seeResponseIsJson(); +$I->seeResponseContainsJson(array('requestMethod' => 'PUT')); diff --git a/tests/functional/REST/PutMethodWithQueryStringCept.php b/tests/functional/REST/PutMethodWithQueryStringCept.php new file mode 100644 index 00000000..ec7a423f --- /dev/null +++ b/tests/functional/REST/PutMethodWithQueryStringCept.php @@ -0,0 +1,12 @@ +wantTo('make PUT request with query string'); +$I->sendPUT('/rest?param=value'); + +$expectedResponse = array( + 'requestMethod' => 'PUT', + 'queryParams' => array( + 'param' => 'value' + ), +); +$I->seeResponseContainsJson($expectedResponse); diff --git a/tests/functional/REST/PutMethodWithRawBodyCept.php b/tests/functional/REST/PutMethodWithRawBodyCept.php new file mode 100644 index 00000000..9446220e --- /dev/null +++ b/tests/functional/REST/PutMethodWithRawBodyCept.php @@ -0,0 +1,11 @@ +wantTo('make PUT request with raw body'); +$I->sendPUT('/rest', 'raw request body'); + +$expectedResponse = array( + 'requestMethod' => 'PUT', + 'formParams' => array(), + 'rawBody' => 'raw request body', +); +$I->seeResponseContainsJson($expectedResponse); diff --git a/tests/functional/REST/RequestUriCept.php b/tests/functional/REST/RequestUriCept.php new file mode 100644 index 00000000..42fe531a --- /dev/null +++ b/tests/functional/REST/RequestUriCept.php @@ -0,0 +1,9 @@ +wantTo('pass request uri'); +$I->sendGET('/rest?param=value¶m2=value2'); +$I->seeResponseIsJson(); +$expectedResponse = array( + 'requestUri' => '/rest?param=value¶m2=value2', +); +$I->seeResponseContainsJson($expectedResponse); diff --git a/tests/functional/REST/SecondRequestSeesNoLeftoversFromTheFirstRequestCept.php b/tests/functional/REST/SecondRequestSeesNoLeftoversFromTheFirstRequestCept.php new file mode 100644 index 00000000..23a540bd --- /dev/null +++ b/tests/functional/REST/SecondRequestSeesNoLeftoversFromTheFirstRequestCept.php @@ -0,0 +1,30 @@ +wantTo('ensure that request object is reset between requests'); + +//$I->haveHttpHeader('X-Auth-Token', 'verySecureToken'); +$I->sendPOST('/rest?queryParam=1', ['foo' => 'bar']); + +$expectedResponse = array( + 'requestMethod' => 'POST', + 'requestUri' => '/rest?queryParam=1', + 'queryParams' => ['queryParam' => '1'], + 'formParams' => ['foo' => 'bar'], +// 'X-Auth-Token' => 'verySecureToken', + +); +$I->seeResponseContainsJson($expectedResponse); + +//$I->haveHttpHeader('X-Auth-Token', null ); +$I->sendGET('/rest'); +$I->seeResponseIsJson(); +$expectedResponse = array( + 'requestMethod' => 'GET', + 'requestUri' => '/rest', + 'queryParams' => [], + 'formParams' => [], +// 'X-Auth-Token' => null, + +); + +$I->seeResponseContainsJson($expectedResponse); diff --git a/tests/functional/SeeCept.php b/tests/functional/SeeCept.php new file mode 100644 index 00000000..13a6d957 --- /dev/null +++ b/tests/functional/SeeCept.php @@ -0,0 +1,10 @@ +wantTo('perform request and match text in response'); + +$I->amOnPage('/'); +$I->seeResponseCodeIs(200); +$I->see('HTTP messages are the foundation of web development.'); diff --git a/tests/functional/SeeResponseCodeIsCept.php b/tests/functional/SeeResponseCodeIsCept.php new file mode 100644 index 00000000..3dea4307 --- /dev/null +++ b/tests/functional/SeeResponseCodeIsCept.php @@ -0,0 +1,9 @@ +wantTo('see different response code'); + +$I->amOnPage('/error'); +$I->seeResponseCodeIs(404); diff --git a/tests/functional/_bootstrap.php b/tests/functional/_bootstrap.php new file mode 100644 index 00000000..8a885558 --- /dev/null +++ b/tests/functional/_bootstrap.php @@ -0,0 +1,2 @@ + Date: Tue, 13 Jan 2026 12:36:52 +0000 Subject: [PATCH 3/3] use a better PHP version --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index afed5f58..4c74d70f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -8,7 +8,7 @@ jobs: strategy: matrix: - php: [8.0] + php: [8.2] steps: - name: Checkout code