From f5cfeee1a57fd72467011b33d1960af558e0e1db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=93=AD=E6=98=95?= <715557344@qq.com> Date: Sun, 16 Jun 2019 20:38:16 +0800 Subject: [PATCH] Added testing component. --- src/testing/co-phpunit | 63 +++++++++++ src/testing/composer.json | 39 +++++++ src/testing/src/Client.php | 200 +++++++++++++++++++++++++++++++++ src/testing/src/HttpClient.php | 103 +++++++++++++++++ 4 files changed, 405 insertions(+) create mode 100755 src/testing/co-phpunit create mode 100644 src/testing/composer.json create mode 100644 src/testing/src/Client.php create mode 100644 src/testing/src/HttpClient.php diff --git a/src/testing/co-phpunit b/src/testing/co-phpunit new file mode 100755 index 000000000..d13155596 --- /dev/null +++ b/src/testing/co-phpunit @@ -0,0 +1,63 @@ +#!/usr/bin/env php +')) { + fwrite( + STDERR, + sprintf( + 'This version of PHPUnit is supported on PHP 7.1 and PHP 7.2.' . PHP_EOL . + 'You are using PHP %s (%s).' . PHP_EOL, + PHP_VERSION, + PHP_BINARY + ) + ); + + die(1); + } + + if (!ini_get('date.timezone')) { + ini_set('date.timezone', 'UTC'); + } + + $dirs = [ + getcwd() . '/vendor/autoload.php', + __DIR__ . '/../../autoload.php', + __DIR__ . '/../vendor/autoload.php', + __DIR__ . '/vendor/autoload.php', + ]; + + foreach ($dirs as $file) { + if (file_exists($file)) { + define('PHPUNIT_COMPOSER_INSTALL', $file); + + break; + } + } + + unset($file); + + if (!defined('PHPUNIT_COMPOSER_INSTALL')) { + fwrite( + STDERR, + 'You need to set up the project dependencies using Composer:' . PHP_EOL . PHP_EOL . + ' composer install' . PHP_EOL . PHP_EOL . + 'You can learn all about Composer on https://getcomposer.org/.' . PHP_EOL + ); + + die(1); + } + + $options = getopt('', array('prepend:')); + + if (isset($options['prepend'])) { + require $options['prepend']; + } + + unset($options); + + require PHPUNIT_COMPOSER_INSTALL; + + PHPUnit\TextUI\Command::main(false); + + swoole_event_exit(); +}); \ No newline at end of file diff --git a/src/testing/composer.json b/src/testing/composer.json new file mode 100644 index 000000000..3ecc5709d --- /dev/null +++ b/src/testing/composer.json @@ -0,0 +1,39 @@ +{ + "name": "hyperf/testing", + "type": "library", + "license": "MIT", + "keywords": [ + "php", + "swoole", + "testing" + ], + "description": "Testing for hyperf", + "autoload": { + "psr-4": { + "Hyperf\\Testing\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + } + }, + "require": { + "php": ">=7.2", + "phpunit/phpunit": "^7.0", + "hyperf/utils": "dev-master" + }, + "require-dev": { + "malukenho/docheader": "^0.1.6", + "mockery/mockery": "^1.0", + "friendsofphp/php-cs-fixer": "^2.9" + }, + "suggest": { + }, + "bin": [ + "co-phpunit" + ], + "scripts": { + "test": "./vendor/bin/phpunit -c phpunit.xml", + "co_test": "php tests/co_phpunit.php -c phpunit.xml" + } +} diff --git a/src/testing/src/Client.php b/src/testing/src/Client.php new file mode 100644 index 000000000..3c909e416 --- /dev/null +++ b/src/testing/src/Client.php @@ -0,0 +1,200 @@ +get(HttpDispatcher::class)); + $this->packer = $packer ?? new JsonPacker(); + + $this->initCoreMiddleware($server); + } + + public function get($uri, $data = [], $headers = []) + { + $response = $this->request('GET', $uri, [ + 'headers' => $headers, + 'query' => $data, + ]); + + return $this->packer->unpack($response->getBody()->getContents()); + } + + public function post($uri, $data = [], $headers = []) + { + $response = $this->request('POST', $uri, [ + 'headers' => $headers, + 'form_params' => $data, + ]); + + return $this->packer->unpack($response->getBody()->getContents()); + } + + public function json($uri, $data = [], $headers = []) + { + $headers['Content-Type'] = 'application/json'; + $response = $this->request('POST', $uri, [ + 'headers' => $headers, + 'json' => $data, + ]); + return $this->packer->unpack($response->getBody()->getContents()); + } + + public function file($uri, $data = [], $headers = []) + { + $multipart = []; + if (Arr::isAssoc($data)) { + $data = [$data]; + } + + foreach ($data as $item) { + $name = $item['name']; + $file = $item['file']; + + $multipart[] = [ + 'name' => $name, + 'contents' => fopen($file, 'r'), + 'filename' => basename($file), + ]; + } + + $response = $this->request('POST', $uri, [ + 'headers' => $headers, + 'multipart' => $multipart, + ]); + + return $this->packer->unpack($response->getBody()->getContents()); + } + + public function request(string $method, string $path, array $options = []) + { + /* + * @var Psr7Request + */ + [$psr7Request, $psr7Response] = $this->init($method, $path, $options); + + $middlewares = array_merge($this->middlewares, MiddlewareManager::get($this->serverName, $psr7Request->getUri()->getPath(), $psr7Request->getMethod())); + + return $this->dispatcher->dispatch($psr7Request, $middlewares, $this->coreMiddleware); + } + + protected function init(string $method, string $path, array $options = []): array + { + $this->flushContext(); + + $query = $options['query'] ?? []; + $params = $options['form_params'] ?? []; + $json = $options['json'] ?? []; + $headers = $options['headers'] ?? []; + $multipart = $options['multipart'] ?? []; + + $data = $params; + + // Initialize PSR-7 Request and Response objects. + $uri = (new Uri())->withPath($path)->withQuery(http_build_query($query)); + + $content = http_build_query($params); + if ($method == 'POST' && data_get($headers, 'Content-Type') == 'application/json') { + $content = json_encode($json, JSON_UNESCAPED_UNICODE); + $data = $json; + } + + $body = new SwooleStream($content); + + $request = new Psr7Request($method, $uri, $headers, $body); + $request = $request->withQueryParams($query) + ->withParsedBody($data) + ->withUploadedFiles($this->normalizeFiles($multipart)); + + Context::set(ServerRequestInterface::class, $psr7Request = $request); + Context::set(ResponseInterface::class, $psr7Response = new Psr7Response()); + + return [$psr7Request, $psr7Response]; + } + + protected function flushContext() + { + $context = SwCoroutine::getContext(); + + foreach ($context as $key => $value) { + unset($context[$key]); + } + } + + protected function normalizeFiles(array $multipart): array + { + $files = []; + $fileSystem = $this->container->get(Filesystem::class); + + foreach ($multipart as $item) { + if (isset($item['name'], $item['contents'], $item['filename'])) { + $name = $item['name']; + $contents = $item['contents']; + $filename = $item['filename']; + + $dir = BASE_PATH . '/runtime/uploads'; + $tmpName = $dir . '/' . $filename; + $fileSystem->makeDirectory($dir); + $fileSystem->put($tmpName, $contents); + + $stats = fstat($contents); + + $files[$name] = new UploadedFile( + $tmpName, + $stats['size'], + 0, + $name + ); + } + } + + return $files; + } + + protected function getStream(string $resource) + { + $stream = fopen('php://temp', 'r+'); + if ($resource !== '') { + fwrite($stream, $resource); + fseek($stream, 0); + } + + return $stream; + } +} diff --git a/src/testing/src/HttpClient.php b/src/testing/src/HttpClient.php new file mode 100644 index 000000000..db17e4cb6 --- /dev/null +++ b/src/testing/src/HttpClient.php @@ -0,0 +1,103 @@ +container = $container; + $this->packer = $packer ?? new JsonPacker(); + $this->client = new Client([ + 'base_uri' => $baseUri, + 'timeout' => 2, + ]); + } + + public function get($uri, $data = [], $headers = []) + { + $response = $this->client->get($uri, [ + 'headers' => $headers, + 'query' => $data, + ]); + return $this->packer->unpack($response->getBody()->getContents()); + } + + public function post($uri, $data = [], $headers = []) + { + $response = $this->client->post($uri, [ + 'headers' => $headers, + 'form_params' => $data, + ]); + + return $this->packer->unpack($response->getBody()->getContents()); + } + + public function json($uri, $data = [], $headers = []) + { + $headers['Content-Type'] = 'application/json'; + $response = $this->client->post($uri, [ + 'json' => $data, + 'headers' => $headers, + ]); + + return $this->packer->unpack($response->getBody()->getContents()); + } + + public function file($uri, $data = [], $headers = []) + { + $multipart = []; + if (Arr::isAssoc($data)) { + $data = [$data]; + } + + foreach ($data as $item) { + $name = $item['name']; + $file = $item['file']; + + $multipart[] = [ + 'name' => $name, + 'contents' => fopen($file, 'r'), + 'filename' => basename($file), + ]; + } + + $response = $this->client->post($uri, [ + 'headers' => $headers, + 'multipart' => $multipart, + ]); + + return $this->packer->unpack($response->getBody()->getContents()); + } +}