From 0c6738cbfb6cc65dcb730cd82ac3809587c57364 Mon Sep 17 00:00:00 2001 From: Vincenzo Petrucci Date: Sat, 11 Nov 2023 17:57:02 +0100 Subject: [PATCH 01/16] fix: use only one foreach to extract arguments --- src/Plugin.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Plugin.php b/src/Plugin.php index 11524b0..043c071 100644 --- a/src/Plugin.php +++ b/src/Plugin.php @@ -51,9 +51,7 @@ public function handleArguments(array $arguments): array if (str_starts_with($argument, '--duration=')) { $run->duration((int) str_replace('--duration=', '', $argument)); } - } - foreach ($arguments as $argument) { if (str_starts_with($argument, '--concurrency=')) { $run->concurrently((int) str_replace('--concurrency=', '', $argument)); } From a7987389cecb16b897ebfb08a99f221cd569bd58 Mon Sep 17 00:00:00 2001 From: Vincenzo Petrucci Date: Sat, 11 Nov 2023 19:06:03 +0100 Subject: [PATCH 02/16] feat(methods): gather method and payload from options, differenciate get and post calls to k6, use payload in post calls --- bin/run.js | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/bin/run.js b/bin/run.js index ee96884..1dd80da 100644 --- a/bin/run.js +++ b/bin/run.js @@ -3,9 +3,24 @@ import http from 'k6/http'; export const options = JSON.parse(__ENV.PEST_STRESS_TEST_OPTIONS); export default () => { - http.get(__ENV.PEST_STRESS_TEST_URL, { - headers: { 'user-agent': 'Pest Plugin Stressless (https://pestphp.com) + K6 (https://k6.io)' }, - }); + + let userAgent = 'Pest Plugin Stressless (https://pestphp.com) + K6 (https://k6.io)'; + let url = __ENV.PEST_STRESS_TEST_URL; + let payload = options.payload ? JSON.stringify(options.payload) : JSON.stringify({}); + let method = options.method ? options.method : 'get'; + + switch (method) { + case 'get': + http.get(url, { + headers: { 'user-agent': userAgent }, + }); + break; + case 'post': + http.post(url, payload, { + headers: { 'user-agent': userAgent }, + }); + break; + } } export function handleSummary(data) { From f8c74123b5cde4e71ca2c2dd1f1c3f3b1d1d4f35 Mon Sep 17 00:00:00 2001 From: Vincenzo Petrucci Date: Sat, 11 Nov 2023 19:09:10 +0100 Subject: [PATCH 03/16] feat(methods): added method and payload to factory --- src/Factory.php | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/Factory.php b/src/Factory.php index 40a6796..e3afe6b 100644 --- a/src/Factory.php +++ b/src/Factory.php @@ -26,6 +26,16 @@ final class Factory */ private int $duration = 5; + /** + * The HTTP method to use. + */ + private string $method = 'get'; + + /** + * The payload to send. + */ + private array $payload = []; + /** * The computed result, if any. */ @@ -59,6 +69,31 @@ public function duration(int $seconds): self return $this; } + /** + * Specifies that the test should be run using the given HTTP method. + */ + public function method(string $method): self + { + $method = strtolower($method); + + assert(in_array($method, ['get', 'post'], true), 'The method must be one of: get, post'); + + $this->method = $method; + + return $this; + } + + /** + * Specifies the payload to send for the test, if any. + * @param array $payload + */ + public function payload(array $payload): self + { + $this->payload = $payload; + + return $this; + } + /** * Specifies that run should run with the given number of concurrent requests. */ @@ -101,6 +136,8 @@ public function run(): Result [ 'vus' => $this->concurrency, 'duration' => sprintf('%ds', $this->duration), + 'method' => $this->method, + 'payload' => $this->payload, 'throw' => true, ], $this->verbose, From 4c14ffba616ff634feee73dd42639f11dd7e7a6d Mon Sep 17 00:00:00 2001 From: Vincenzo Petrucci Date: Sat, 11 Nov 2023 19:10:57 +0100 Subject: [PATCH 04/16] feat(methods): added method and payload to factory --- src/Factory.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Factory.php b/src/Factory.php index e3afe6b..619c477 100644 --- a/src/Factory.php +++ b/src/Factory.php @@ -33,6 +33,7 @@ final class Factory /** * The payload to send. + * @var array */ private array $payload = []; From 5610652495210fac49c16905b066d83e51e9330a Mon Sep 17 00:00:00 2001 From: Vincenzo Petrucci Date: Sat, 11 Nov 2023 19:12:57 +0100 Subject: [PATCH 05/16] feat(methods): Force object when encoding values to be passed to k6 --- src/Run.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Run.php b/src/Run.php index cd79c9f..6d04e09 100644 --- a/src/Run.php +++ b/src/Run.php @@ -28,7 +28,7 @@ final class Run /** * Creates a new run instance. * - * @param array{vus: int, duration: string} $options + * @param array{vus: int, duration: string, method: string, payload: array, throw: boolean} $options */ public function __construct( readonly private Url $url, @@ -71,7 +71,7 @@ public function start(): Result $process = new Process([ K6::make(), 'run', 'run.js', '--out', "json={$this->session->progressPath()}", ], $basePath.'/bin', [ - 'PEST_STRESS_TEST_OPTIONS' => json_encode($this->options, JSON_THROW_ON_ERROR), + 'PEST_STRESS_TEST_OPTIONS' => json_encode($this->options, JSON_FORCE_OBJECT|JSON_THROW_ON_ERROR), 'PEST_STRESS_TEST_URL' => $this->url, 'PEST_STRESS_TEST_SUMMARY_PATH' => $this->session->summaryPath(), ]); From 78dd41e875894fed55e64847fc46899780ac4e50 Mon Sep 17 00:00:00 2001 From: Vincenzo Petrucci Date: Sat, 11 Nov 2023 19:13:24 +0100 Subject: [PATCH 06/16] feat(methods): Get method and payload from command line --- src/Plugin.php | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/Plugin.php b/src/Plugin.php index 043c071..997e3cb 100644 --- a/src/Plugin.php +++ b/src/Plugin.php @@ -55,6 +55,26 @@ public function handleArguments(array $arguments): array if (str_starts_with($argument, '--concurrency=')) { $run->concurrently((int) str_replace('--concurrency=', '', $argument)); } + + if (str_starts_with($argument, '--method=')) { + $run->method(str_replace('--method=', '', $argument)); + } + + if (str_starts_with($argument, '--payload=')) { + try { + $payload = json_decode(str_replace('--payload=', '', $argument), + true, 512, JSON_THROW_ON_ERROR); + } catch (\JsonException $e) { + View::render('components.badge', [ + 'type' => 'ERROR', + 'content' => 'Invalid JSON payload. Please provide a valid JSON payload.' . + 'Example: --payload=\'{"name": "Nuno"}\'' + ]); + + exit(0); + } + $run->payload($payload); + } } $run->dd(); From 09698674edad622d1b447e43297284e3ae05b7d0 Mon Sep 17 00:00:00 2001 From: Vincenzo Petrucci Date: Sat, 11 Nov 2023 23:00:12 +0100 Subject: [PATCH 07/16] feat(methods): Added information about method and payload options --- README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.md b/README.md index 1a7ee44..efe4a0c 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,18 @@ In addition, the number of concurrent requests will be `1`. However, you may als ./vendor/bin/pest stress example.com --concurrency=5 ``` +You can choose to use GET or POST requests using the `--method` option: + +```bash +./vendor/bin/pest stress example.com --method=POST +``` + +You can choose to use a custom payload using the `--payload` option: + +```bash +./vendor/bin/pest stress example.com --method=POST --payload='{"name": "Nuno"}' +``` + The concurrency value represents the number of concurrent requests that will be made to the given URL. For example, if you set the concurrency to `5`, Pest will **constantly make 5 concurrent requests** to the given URL until the stress test duration is reached. You may want to be mindful of the number of concurrent requests you configure. If you configure too many concurrent requests, you may overwhelm your application, server or hit rate limits / firewalls. From aec6e341318a04477145dbb4f9be79ac8a0a2a45 Mon Sep 17 00:00:00 2001 From: Vincenzo Petrucci Date: Sat, 11 Nov 2023 23:01:53 +0100 Subject: [PATCH 08/16] feat(methods): Added information about method and payload options for the stress() function --- README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.md b/README.md index efe4a0c..286dda5 100644 --- a/README.md +++ b/README.md @@ -108,6 +108,18 @@ In addition, the number of concurrent requests will be 1. However, you may also $result = stress('example.com')->concurrently(requests: 2)->for(5)->seconds(); ``` +You can choose to use GET or POST requests using the `method()` method: + +```php +$result = stress('example.com')->method('POST')->for(5)->seconds(); +``` + +You can choose to use a custom payload using the `payload()` method: + +```php +$result = stress('example.com')->method('POST')->payload(['name' => 'Nuno'])->for(5)->seconds(); +``` + At any time, you may `dd` the stress test result to see its details like if you were using the `stress` command): ```php From 623d76196abb354efc8f2e02fd66892fd85b9263 Mon Sep 17 00:00:00 2001 From: Vincenzo Petrucci Date: Wed, 15 Nov 2023 16:15:48 +0100 Subject: [PATCH 09/16] Added get() and method() shortcut to Factory, and --get, --post options to command line --- src/Factory.php | 16 ++++++++++++++++ src/Plugin.php | 8 ++++++++ 2 files changed, 24 insertions(+) diff --git a/src/Factory.php b/src/Factory.php index 619c477..91ba40b 100644 --- a/src/Factory.php +++ b/src/Factory.php @@ -84,6 +84,22 @@ public function method(string $method): self return $this; } + /** + * Force the test to use get method + */ + public function get(): self + { + return $this->method('get'); + } + + /** + * Force the test to use post method + */ + public function post(): self + { + return $this->method('post'); + } + /** * Specifies the payload to send for the test, if any. * @param array $payload diff --git a/src/Plugin.php b/src/Plugin.php index 997e3cb..bdc7f83 100644 --- a/src/Plugin.php +++ b/src/Plugin.php @@ -60,6 +60,14 @@ public function handleArguments(array $arguments): array $run->method(str_replace('--method=', '', $argument)); } + if ($argument === '--get') { + $run->get(); + } + + if($argument === '--post') { + $run->post(); + } + if (str_starts_with($argument, '--payload=')) { try { $payload = json_decode(str_replace('--payload=', '', $argument), From 61e8edb2c3f8519cb98479f746722f05f347252e Mon Sep 17 00:00:00 2001 From: Vincenzo Petrucci Date: Wed, 15 Nov 2023 16:20:57 +0100 Subject: [PATCH 10/16] Updated README file --- README.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/README.md b/README.md index 286dda5..1ad6922 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,14 @@ You can choose to use GET or POST requests using the `--method` option: ./vendor/bin/pest stress example.com --method=POST ``` +In addition to the `--method` option, also `--get` and `--post` shortcuts are provided: + +```bash +./vendor/bin/pest stress example.com --get +# OR +./vendor/bin/pest stress example.com --post +``` + You can choose to use a custom payload using the `--payload` option: ```bash @@ -114,10 +122,18 @@ You can choose to use GET or POST requests using the `method()` method: $result = stress('example.com')->method('POST')->for(5)->seconds(); ``` +Alternatively you can use the `get()` and `post()` shortcuts: + +```php +$result = stress('example.com')->post()->for(5)->seconds(); +``` + You can choose to use a custom payload using the `payload()` method: ```php $result = stress('example.com')->method('POST')->payload(['name' => 'Nuno'])->for(5)->seconds(); +// or +$result = stress('example.com')->post()->payload(['name' => 'Nuno'])->for(5)->seconds(); ``` At any time, you may `dd` the stress test result to see its details like if you were using the `stress` command): From db927f99eac5cdf6d579689ab4e340109a8efd28 Mon Sep 17 00:00:00 2001 From: Vincenzo Petrucci Date: Thu, 16 Nov 2023 22:22:54 +0100 Subject: [PATCH 11/16] fix: Fixed style issues --- src/Factory.php | 2 ++ src/Plugin.php | 6 +++--- src/Run.php | 4 ++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Factory.php b/src/Factory.php index 91ba40b..8180396 100644 --- a/src/Factory.php +++ b/src/Factory.php @@ -33,6 +33,7 @@ final class Factory /** * The payload to send. + * * @var array */ private array $payload = []; @@ -102,6 +103,7 @@ public function post(): self /** * Specifies the payload to send for the test, if any. + * * @param array $payload */ public function payload(array $payload): self diff --git a/src/Plugin.php b/src/Plugin.php index bdc7f83..d516e79 100644 --- a/src/Plugin.php +++ b/src/Plugin.php @@ -64,7 +64,7 @@ public function handleArguments(array $arguments): array $run->get(); } - if($argument === '--post') { + if ($argument === '--post') { $run->post(); } @@ -75,8 +75,8 @@ public function handleArguments(array $arguments): array } catch (\JsonException $e) { View::render('components.badge', [ 'type' => 'ERROR', - 'content' => 'Invalid JSON payload. Please provide a valid JSON payload.' . - 'Example: --payload=\'{"name": "Nuno"}\'' + 'content' => 'Invalid JSON payload. Please provide a valid JSON payload.'. + 'Example: --payload=\'{"name": "Nuno"}\'', ]); exit(0); diff --git a/src/Run.php b/src/Run.php index 6d04e09..e6aa83a 100644 --- a/src/Run.php +++ b/src/Run.php @@ -28,7 +28,7 @@ final class Run /** * Creates a new run instance. * - * @param array{vus: int, duration: string, method: string, payload: array, throw: boolean} $options + * @param array{vus: int, duration: string, method: string, payload: array, throw: boolean} $options */ public function __construct( readonly private Url $url, @@ -71,7 +71,7 @@ public function start(): Result $process = new Process([ K6::make(), 'run', 'run.js', '--out', "json={$this->session->progressPath()}", ], $basePath.'/bin', [ - 'PEST_STRESS_TEST_OPTIONS' => json_encode($this->options, JSON_FORCE_OBJECT|JSON_THROW_ON_ERROR), + 'PEST_STRESS_TEST_OPTIONS' => json_encode($this->options, JSON_FORCE_OBJECT | JSON_THROW_ON_ERROR), 'PEST_STRESS_TEST_URL' => $this->url, 'PEST_STRESS_TEST_SUMMARY_PATH' => $this->session->summaryPath(), ]); From 0c99a810efd73329ba7c6e21fb26d6a2f191cb16 Mon Sep 17 00:00:00 2001 From: Vincenzo Petrucci Date: Thu, 16 Nov 2023 22:38:10 +0100 Subject: [PATCH 12/16] fix: forced payload to be an array --- src/Plugin.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Plugin.php b/src/Plugin.php index d516e79..63e83a0 100644 --- a/src/Plugin.php +++ b/src/Plugin.php @@ -70,7 +70,7 @@ public function handleArguments(array $arguments): array if (str_starts_with($argument, '--payload=')) { try { - $payload = json_decode(str_replace('--payload=', '', $argument), + $payload = (array) json_decode(str_replace('--payload=', '', $argument), true, 512, JSON_THROW_ON_ERROR); } catch (\JsonException $e) { View::render('components.badge', [ From 501f319a7768f649c288d6399ba29a570bb6b8d8 Mon Sep 17 00:00:00 2001 From: Vincenzo Petrucci Date: Thu, 16 Nov 2023 22:39:07 +0100 Subject: [PATCH 13/16] fix: added array shape for payload --- src/Run.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Run.php b/src/Run.php index e6aa83a..3b33784 100644 --- a/src/Run.php +++ b/src/Run.php @@ -28,7 +28,7 @@ final class Run /** * Creates a new run instance. * - * @param array{vus: int, duration: string, method: string, payload: array, throw: boolean} $options + * @param array{vus: int, duration: string, method: string, payload: array, throw: boolean} $options */ public function __construct( readonly private Url $url, From 990ddf91924158aa8eacd539e777304349cdd34d Mon Sep 17 00:00:00 2001 From: Vincenzo Petrucci Date: Thu, 16 Nov 2023 22:47:59 +0100 Subject: [PATCH 14/16] fix: ensure that zip extension is enabled during tests --- .github/workflows/tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 845fc5b..61a1d31 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -22,6 +22,7 @@ jobs: uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php }} + extensions: zip tools: composer:v2 coverage: none From 4b9e3dad098bbcbb1db737cb4991bb8f2e2838f0 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Fri, 24 Nov 2023 18:47:15 +0000 Subject: [PATCH 15/16] Revert --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f165dc1..6614b03 100644 --- a/README.md +++ b/README.md @@ -12,4 +12,4 @@ This repository contains the Pest Plugin Stressless. - Follow us on Twitter at **[@pestphp »](https://twitter.com/pestphp)** - Join us at **[discord.gg/kaHY6p54JH »](https://discord.gg/kaHY6p54JH)** or **[t.me/+kYH5G4d5MV83ODk0 »](https://t.me/+kYH5G4d5MV83ODk0)** - +Pest is an open-sourced software licensed under the **[MIT license](https://opensource.org/licenses/MIT)**. Behind the scenes, this project utilizes [k6](https://k6.io/), a powerful open-source load testing tool for evaluating the performance of APIs, microservices, and websites. k6 is licensed under the **[AGPL-3.0 License](https://www.gnu.org/licenses/agpl-3.0.en.html)**. From b7a9490b9e1aa33394f9a9ef3a396f344ddfdf9d Mon Sep 17 00:00:00 2001 From: Vincenzo Petrucci Date: Sun, 26 Nov 2023 16:28:39 +0100 Subject: [PATCH 16/16] feat(methods): Remove function and argument. Use only get and post. The payload must be provided with the post argument / method --- src/Factory.php | 31 ++++++------------------------- src/Plugin.php | 16 ++++------------ 2 files changed, 10 insertions(+), 37 deletions(-) diff --git a/src/Factory.php b/src/Factory.php index 8180396..b366369 100644 --- a/src/Factory.php +++ b/src/Factory.php @@ -71,43 +71,24 @@ public function duration(int $seconds): self return $this; } - /** - * Specifies that the test should be run using the given HTTP method. - */ - public function method(string $method): self - { - $method = strtolower($method); - - assert(in_array($method, ['get', 'post'], true), 'The method must be one of: get, post'); - - $this->method = $method; - - return $this; - } - /** * Force the test to use get method */ public function get(): self { - return $this->method('get'); - } + $this->method = 'get'; - /** - * Force the test to use post method - */ - public function post(): self - { - return $this->method('post'); + return $this; } /** - * Specifies the payload to send for the test, if any. + * Force the test to use post method * - * @param array $payload + * @param array $payload The payload to send with the POST request */ - public function payload(array $payload): self + public function post(array $payload): self { + $this->method = 'post'; $this->payload = $payload; return $this; diff --git a/src/Plugin.php b/src/Plugin.php index 63e83a0..f80ec9c 100644 --- a/src/Plugin.php +++ b/src/Plugin.php @@ -56,23 +56,15 @@ public function handleArguments(array $arguments): array $run->concurrently((int) str_replace('--concurrency=', '', $argument)); } - if (str_starts_with($argument, '--method=')) { - $run->method(str_replace('--method=', '', $argument)); - } - if ($argument === '--get') { $run->get(); } - if ($argument === '--post') { - $run->post(); - } - - if (str_starts_with($argument, '--payload=')) { + if (str_starts_with($argument, '--post=')) { try { - $payload = (array) json_decode(str_replace('--payload=', '', $argument), + $payload = (array) json_decode(str_replace('--post=', '', $argument), true, 512, JSON_THROW_ON_ERROR); - } catch (\JsonException $e) { + } catch (\JsonException) { View::render('components.badge', [ 'type' => 'ERROR', 'content' => 'Invalid JSON payload. Please provide a valid JSON payload.'. @@ -81,7 +73,7 @@ public function handleArguments(array $arguments): array exit(0); } - $run->payload($payload); + $run->post($payload); } }