diff --git a/composer.json b/composer.json index b9d4484..f0eb94c 100644 --- a/composer.json +++ b/composer.json @@ -30,7 +30,8 @@ "phpexperts/simple-dto": "^3.7", "illuminate/database": "^11.37", "illuminate/events": "^11.37", - "illuminate/filesystem": "^11.37" + "illuminate/filesystem": "^11.37", + "phpexperts/workday-planner": "^1.2" }, "require-dev": { "phpunit/phpunit": "*", diff --git a/content-20250425-07_49_49.md b/content-20250425-07_49_49.md new file mode 100644 index 0000000..2a7d80c --- /dev/null +++ b/content-20250425-07_49_49.md @@ -0,0 +1,421 @@ +#!/bin/bash +# Creating the Workday Planner API structure + +mkdir -p src/API +mkdir -p src/DTOs +mkdir -p tests/Integration + +# Creating the API routes file +cat < + + + + + + +

Workday Planner API

+ +

Endpoints

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
EndpointMethodDescriptionParameters
/api/workdaysGETGet workdays between two datesstartDate (YYYY-MM-DD), endDate (YYYY-MM-DD), country (ISO code)
/api/holidaysGETGet holidays for a specific yearyear (YYYY), country (ISO code)
/api/is-workdayGETCheck if a date is a workdaydate (YYYY-MM-DD), country (ISO code)
/api/is-holidayGETCheck if a date is a holidaydate (YYYY-MM-DD), country (ISO code)
+ +

Example Requests

+ GET /api/workdays?startDate=2023-01-01&endDate=2023-01-31&country=us + GET /api/holidays?year=2023&country=us + GET /api/is-workday?date=2023-01-02&country=us + GET /api/is-holiday?date=2023-01-01&country=us + + + HTML; +}); + +SimpleRouter::group(['prefix' => '/api'], function () { + SimpleRouter::get('/workdays', function () { + \$startDate = input('startDate'); + \$endDate = input('endDate'); + \$country = input('country', 'us'); + + if (!\$startDate || !\$endDate) { + response()->httpCode(400); + response()->json(['error' => 'Both startDate and endDate parameters are required']); + return; + } + + try { + \$planner = new WorkdayPlanner( + new \DateTime(\$startDate), + new \DateTime(\$endDate), + \$country + ); + + response()->json([ + 'workdays' => \$planner->getWorkdays(), + 'count' => count(\$planner), + ]); + } catch (\Exception \$e) { + response()->httpCode(400); + response()->json(['error' => \$e->getMessage()]); + } + }); + + SimpleRouter::get('/holidays', function () { + \$year = input('year', date('Y')); + \$country = input('country', 'us'); + + try { + \$detector = new HolidayDetector(\$country); + \$detector->changeYear((int) \$year); + + \$holidays = []; + \$reflection = new \ReflectionClass(\$detector); + \$property = \$reflection->getProperty('holidaysByName'); + \$property->setAccessible(true); + \$holidaysByName = \$property->getValue(\$detector); + + foreach (\$holidaysByName as \$name => \$date) { + \$holidays[] = [ + 'name' => \$name, + 'date' => \$date->format('Y-m-d'), + ]; + } + + response()->json([ + 'holidays' => \$holidays, + 'count' => count(\$holidays), + ]); + } catch (\Exception \$e) { + response()->httpCode(400); + response()->json(['error' => \$e->getMessage()]); + } + }); + + SimpleRouter::get('/is-workday', function () { + \$date = input('date'); + \$country = input('country', 'us'); + + if (!\$date) { + response()->httpCode(400); + response()->json(['error' => 'date parameter is required']); + return; + } + + try { + \$isWorkday = WorkdayDetector::isWorkday(new \DateTime(\$date), \$country); + response()->json([ + 'isWorkday' => \$isWorkday, + 'date' => \$date, + 'country' => \$country, + ]); + } catch (\Exception \$e) { + response()->httpCode(400); + response()->json(['error' => \$e->getMessage()]); + } + }); + + SimpleRouter::get('/is-holiday', function () { + \$date = input('date'); + \$country = input('country', 'us'); + + if (!\$date) { + response()->httpCode(400); + response()->json(['error' => 'date parameter is required']); + return; + } + + try { + \$isHoliday = (new HolidayDetector(\$country))->isHoliday(\$date); + response()->json([ + 'isHoliday' => \$isHoliday, + 'date' => \$date, + 'country' => \$country, + ]); + } catch (\Exception \$e) { + response()->httpCode(400); + response()->json(['error' => \$e->getMessage()]); + } + }); +}); +TXT + +# Creating integration tests +cat <api = new RESTSpeaker('http://localhost:3000'); + } + + public function testCanGetWorkdays() + { + \$response = \$this->api->get('/api/workdays', [ + 'startDate' => '2023-01-01', + 'endDate' => '2023-01-31', + 'country' => 'us', + ]); + + \$this->assertEquals(200, \$response->status); + \$this->assertIsArray(\$response->body->workdays); + \$this->assertGreaterThan(0, \$response->body->count); + } + + public function testCanGetHolidays() + { + \$response = \$this->api->get('/api/holidays', [ + 'year' => '2023', + 'country' => 'us', + ]); + + \$this->assertEquals(200, \$response->status); + \$this->assertIsArray(\$response->body->holidays); + \$this->assertGreaterThan(0, \$response->body->count); + } + + public function testCanCheckWorkday() + { + \$response = \$this->api->get('/api/is-workday', [ + 'date' => '2023-01-02', + 'country' => 'us', + ]); + + \$this->assertEquals(200, \$response->status); + \$this->assertIsBool(\$response->body->isWorkday); + } + + public function testCanCheckHoliday() + { + \$response = \$this->api->get('/api/is-holiday', [ + 'date' => '2023-01-01', + 'country' => 'us', + ]); + + \$this->assertEquals(200, \$response->status); + \$this->assertIsBool(\$response->body->isHoliday); + } + + public function testReturnsErrorForInvalidDates() + { + \$response = \$this->api->get('/api/workdays', [ + 'startDate' => 'invalid', + 'endDate' => 'invalid', + ]); + + \$this->assertEquals(400, \$response->status); + \$this->assertStringContainsString('error', json_encode(\$response->body)); + } +} +TXT + +# Creating DTOs +cat < \$planner->getWorkdays(), + 'count' => count(\$planner), + ]); + } +} +TXT + +cat <changeYear(\$year); + + \$holidays = []; + \$reflection = new \ReflectionClass(\$detector); + \$property = \$reflection->getProperty('holidaysByName'); + \$property->setAccessible(true); + \$holidaysByName = \$property->getValue(\$detector); + + foreach (\$holidaysByName as \$name => \$date) { + \$holidays[] = [ + 'name' => \$name, + 'date' => \$date->format('Y-m-d'), + ]; + } + + return new HolidayResponseDTO([ + 'holidays' => \$holidays, + 'count' => count(\$holidays), + ]); + } +} +TXT + +cat < \$isWorkday, + 'date' => \$date, + 'country' => \$country, + ]); + } + + public function checkHoliday(string \$date, string \$country = 'us'): DateCheckResponseDTO + { + \$isHoliday = (new HolidayDetector(\$country))->isHoliday(\$date); + + return new DateCheckResponseDTO([ + 'isHoliday' => \$isHoliday, + 'date' => \$date, + 'country' => \$country, + ]); + } +} +TXT diff --git a/demo.txt b/demo.txt new file mode 100644 index 0000000..52f574e --- /dev/null +++ b/demo.txt @@ -0,0 +1,4 @@ +# This is a demo of Autonomo, by Autonomous Programming, LLC. +# It will autonomously create an API server for any packagist.org PHP package. + + diff --git a/git-20250425-07_49_49.json b/git-20250425-07_49_49.json new file mode 100644 index 0000000..f3d6dec --- /dev/null +++ b/git-20250425-07_49_49.json @@ -0,0 +1 @@ +{"id":"095c13dc-7ced-4473-b2d5-2eeff71ef129","object":"chat.completion","created":1745585390,"model":"deepseek-chat","choices":[{"index":0,"message":{"role":"assistant","content":"```json\n{\n \"git\": {\n \"branch\": \"autonomo/workday-planner-integration\",\n \"commit_msg\": \"Add Workday Planner integration and API endpoints\\n\\n- Added phpexperts/workday-planner dependency\\n- Implemented four new API endpoints for workday calculations\\n- Created comprehensive documentation in the root route\\n- Added error handling for all endpoints\\n- Implemented workdays, holidays, is-workday, and is-holiday functionality\"\n }\n}\n```"},"logprobs":null,"finish_reason":"stop"}],"usage":{"prompt_tokens":2111,"completion_tokens":104,"total_tokens":2215,"prompt_tokens_details":{"cached_tokens":64},"prompt_cache_hit_tokens":64,"prompt_cache_miss_tokens":2047},"system_fingerprint":"fp_8802369eaa_prod0225"} \ No newline at end of file diff --git a/response-20250425-07_49_49.json b/response-20250425-07_49_49.json new file mode 100644 index 0000000..5d99766 --- /dev/null +++ b/response-20250425-07_49_49.json @@ -0,0 +1,3 @@ + + +{"id":"623de8b9-43ca-41ca-813b-242006e38be2","object":"chat.completion","created":1745585245,"model":"deepseek-chat","choices":[{"index":0,"message":{"role":"assistant","content":"# Creating the Workday Planner API structure\n\n```sh\nmkdir -p src/API\nmkdir -p src/DTOs\nmkdir -p tests/Integration\n```\n\n# Creating the API routes file\n```sh\ncat <\n \n \n \n \n \n \n

Workday Planner API

\n \n

Endpoints

\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
EndpointMethodDescriptionParameters
/api/workdaysGETGet workdays between two datesstartDate (YYYY-MM-DD), endDate (YYYY-MM-DD), country (ISO code)
/api/holidaysGETGet holidays for a specific yearyear (YYYY), country (ISO code)
/api/is-workdayGETCheck if a date is a workdaydate (YYYY-MM-DD), country (ISO code)
/api/is-holidayGETCheck if a date is a holidaydate (YYYY-MM-DD), country (ISO code)
\n \n

Example Requests

\n GET /api/workdays?startDate=2023-01-01&endDate=2023-01-31&country=us\n GET /api/holidays?year=2023&country=us\n GET /api/is-workday?date=2023-01-02&country=us\n GET /api/is-holiday?date=2023-01-01&country=us\n \n \n HTML;\n});\n\nSimpleRouter::group(['prefix' => '/api'], function () {\n SimpleRouter::get('/workdays', function () {\n \\$startDate = input('startDate');\n \\$endDate = input('endDate');\n \\$country = input('country', 'us');\n \n if (!\\$startDate || !\\$endDate) {\n response()->httpCode(400);\n response()->json(['error' => 'Both startDate and endDate parameters are required']);\n return;\n }\n \n try {\n \\$planner = new WorkdayPlanner(\n new \\DateTime(\\$startDate),\n new \\DateTime(\\$endDate),\n \\$country\n );\n \n response()->json([\n 'workdays' => \\$planner->getWorkdays(),\n 'count' => count(\\$planner),\n ]);\n } catch (\\Exception \\$e) {\n response()->httpCode(400);\n response()->json(['error' => \\$e->getMessage()]);\n }\n });\n \n SimpleRouter::get('/holidays', function () {\n \\$year = input('year', date('Y'));\n \\$country = input('country', 'us');\n \n try {\n \\$detector = new HolidayDetector(\\$country);\n \\$detector->changeYear((int) \\$year);\n \n \\$holidays = [];\n \\$reflection = new \\ReflectionClass(\\$detector);\n \\$property = \\$reflection->getProperty('holidaysByName');\n \\$property->setAccessible(true);\n \\$holidaysByName = \\$property->getValue(\\$detector);\n \n foreach (\\$holidaysByName as \\$name => \\$date) {\n \\$holidays[] = [\n 'name' => \\$name,\n 'date' => \\$date->format('Y-m-d'),\n ];\n }\n \n response()->json([\n 'holidays' => \\$holidays,\n 'count' => count(\\$holidays),\n ]);\n } catch (\\Exception \\$e) {\n response()->httpCode(400);\n response()->json(['error' => \\$e->getMessage()]);\n }\n });\n \n SimpleRouter::get('/is-workday', function () {\n \\$date = input('date');\n \\$country = input('country', 'us');\n \n if (!\\$date) {\n response()->httpCode(400);\n response()->json(['error' => 'date parameter is required']);\n return;\n }\n \n try {\n \\$isWorkday = WorkdayDetector::isWorkday(new \\DateTime(\\$date), \\$country);\n response()->json([\n 'isWorkday' => \\$isWorkday,\n 'date' => \\$date,\n 'country' => \\$country,\n ]);\n } catch (\\Exception \\$e) {\n response()->httpCode(400);\n response()->json(['error' => \\$e->getMessage()]);\n }\n });\n \n SimpleRouter::get('/is-holiday', function () {\n \\$date = input('date');\n \\$country = input('country', 'us');\n \n if (!\\$date) {\n response()->httpCode(400);\n response()->json(['error' => 'date parameter is required']);\n return;\n }\n \n try {\n \\$isHoliday = (new HolidayDetector(\\$country))->isHoliday(\\$date);\n response()->json([\n 'isHoliday' => \\$isHoliday,\n 'date' => \\$date,\n 'country' => \\$country,\n ]);\n } catch (\\Exception \\$e) {\n response()->httpCode(400);\n response()->json(['error' => \\$e->getMessage()]);\n }\n });\n});\nTXT\n```\n\n# Creating integration tests\n```sh\ncat <api = new RESTSpeaker('http://localhost:3000');\n }\n\n public function testCanGetWorkdays()\n {\n \\$response = \\$this->api->get('/api/workdays', [\n 'startDate' => '2023-01-01',\n 'endDate' => '2023-01-31',\n 'country' => 'us',\n ]);\n\n \\$this->assertEquals(200, \\$response->status);\n \\$this->assertIsArray(\\$response->body->workdays);\n \\$this->assertGreaterThan(0, \\$response->body->count);\n }\n\n public function testCanGetHolidays()\n {\n \\$response = \\$this->api->get('/api/holidays', [\n 'year' => '2023',\n 'country' => 'us',\n ]);\n\n \\$this->assertEquals(200, \\$response->status);\n \\$this->assertIsArray(\\$response->body->holidays);\n \\$this->assertGreaterThan(0, \\$response->body->count);\n }\n\n public function testCanCheckWorkday()\n {\n \\$response = \\$this->api->get('/api/is-workday', [\n 'date' => '2023-01-02',\n 'country' => 'us',\n ]);\n\n \\$this->assertEquals(200, \\$response->status);\n \\$this->assertIsBool(\\$response->body->isWorkday);\n }\n\n public function testCanCheckHoliday()\n {\n \\$response = \\$this->api->get('/api/is-holiday', [\n 'date' => '2023-01-01',\n 'country' => 'us',\n ]);\n\n \\$this->assertEquals(200, \\$response->status);\n \\$this->assertIsBool(\\$response->body->isHoliday);\n }\n\n public function testReturnsErrorForInvalidDates()\n {\n \\$response = \\$this->api->get('/api/workdays', [\n 'startDate' => 'invalid',\n 'endDate' => 'invalid',\n ]);\n\n \\$this->assertEquals(400, \\$response->status);\n \\$this->assertStringContainsString('error', json_encode(\\$response->body));\n }\n}\nTXT\n```\n\n# Creating DTOs\n```sh\ncat < \\$planner->getWorkdays(),\n 'count' => count(\\$planner),\n ]);\n }\n}\nTXT\n```\n\n```sh\ncat <changeYear(\\$year);\n\n \\$holidays = [];\n \\$reflection = new \\ReflectionClass(\\$detector);\n \\$property = \\$reflection->getProperty('holidaysByName');\n \\$property->setAccessible(true);\n \\$holidaysByName = \\$property->getValue(\\$detector);\n\n foreach (\\$holidaysByName as \\$name => \\$date) {\n \\$holidays[] = [\n 'name' => \\$name,\n 'date' => \\$date->format('Y-m-d'),\n ];\n }\n\n return new HolidayResponseDTO([\n 'holidays' => \\$holidays,\n 'count' => count(\\$holidays),\n ]);\n }\n}\nTXT\n```\n\n```sh\ncat < \\$isWorkday,\n 'date' => \\$date,\n 'country' => \\$country,\n ]);\n }\n\n public function checkHoliday(string \\$date, string \\$country = 'us'): DateCheckResponseDTO\n {\n \\$isHoliday = (new HolidayDetector(\\$country))->isHoliday(\\$date);\n\n return new DateCheckResponseDTO([\n 'isHoliday' => \\$isHoliday,\n 'date' => \\$date,\n 'country' => \\$country,\n ]);\n }\n}\nTXT\n```"},"logprobs":null,"finish_reason":"stop"}],"usage":{"prompt_tokens":3622,"completion_tokens":3209,"total_tokens":6831,"prompt_tokens_details":{"cached_tokens":320},"prompt_cache_hit_tokens":320,"prompt_cache_miss_tokens":3302},"system_fingerprint":"fp_8802369eaa_prod0225"} \ No newline at end of file diff --git a/src/API/DateCheckController.php b/src/API/DateCheckController.php new file mode 100644 index 0000000..80fde8e --- /dev/null +++ b/src/API/DateCheckController.php @@ -0,0 +1,32 @@ + $isWorkday, + 'date' => $date, + 'country' => $country, + ]); + } + + public function checkHoliday(string $date, string $country = 'us'): DateCheckResponseDTO + { + $isHoliday = (new HolidayDetector($country))->isHoliday($date); + + return new DateCheckResponseDTO([ + 'isHoliday' => $isHoliday, + 'date' => $date, + 'country' => $country, + ]); + } +} diff --git a/src/API/HolidayController.php b/src/API/HolidayController.php new file mode 100644 index 0000000..4dd9fa0 --- /dev/null +++ b/src/API/HolidayController.php @@ -0,0 +1,33 @@ +changeYear($year); + + $holidays = []; + $reflection = new \ReflectionClass($detector); + $property = $reflection->getProperty('holidaysByName'); + $property->setAccessible(true); + $holidaysByName = $property->getValue($detector); + + foreach ($holidaysByName as $name => $date) { + $holidays[] = [ + 'name' => $name, + 'date' => $date->format('Y-m-d'), + ]; + } + + return new HolidayResponseDTO([ + 'holidays' => $holidays, + 'count' => count($holidays), + ]); + } +} diff --git a/src/API/WorkdayController.php b/src/API/WorkdayController.php new file mode 100644 index 0000000..b1975d0 --- /dev/null +++ b/src/API/WorkdayController.php @@ -0,0 +1,23 @@ + $planner->getWorkdays(), + 'count' => count($planner), + ]); + } +} diff --git a/src/DTOs/DateCheckResponseDTO.php b/src/DTOs/DateCheckResponseDTO.php new file mode 100644 index 0000000..df241c7 --- /dev/null +++ b/src/DTOs/DateCheckResponseDTO.php @@ -0,0 +1,21 @@ + - * GPG Fingerprint: 4BF8 2613 1C34 87AC D28F 2AD8 EB24 A91D D612 5690 - * https://www.phpexperts.pro/ - * https://github.com/PHPExpertsInc/MiniApiBase - * - * This file is licensed under the MIT License. - */ +namespace PHPExperts\WorkdayPlannerAPI; use Pecee\SimpleRouter\SimpleRouter; -use Pecee\SimpleRouter\SimpleRouter as Router; +use PHPExperts\WorkdayPlanner\WorkdayPlanner; +use PHPExperts\WorkdayPlanner\HolidayDetector; SimpleRouter::get('/', function () { return << -

An API Server created by Autonomo by Autonomous Programming, LLC.

+

Workday Planner API

+ +

Endpoints

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
EndpointMethodDescriptionParameters
/api/workdaysGETGet workdays between two datesstartDate (YYYY-MM-DD), endDate (YYYY-MM-DD), country (ISO code)
/api/holidaysGETGet holidays for a specific yearyear (YYYY), country (ISO code)
/api/is-workdayGETCheck if a date is a workdaydate (YYYY-MM-DD), country (ISO code)
/api/is-holidayGETCheck if a date is a holidaydate (YYYY-MM-DD), country (ISO code)
+ +

Example Requests

+ GET /api/workdays?startDate=2023-01-01&endDate=2023-01-31&country=us + GET /api/holidays?year=2023&country=us + GET /api/is-workday?date=2023-01-02&country=us + GET /api/is-holiday?date=2023-01-01&country=us HTML; }); + +SimpleRouter::group(['prefix' => '/api'], function () { + SimpleRouter::get('/workdays', function () { + $startDate = input('startDate'); + $endDate = input('endDate'); + $country = input('country', 'us'); + + if (!$startDate || !$endDate) { + response()->httpCode(400); + response()->json(['error' => 'Both startDate and endDate parameters are required']); + return; + } + + try { + $planner = new WorkdayPlanner( + new \DateTime($startDate), + new \DateTime($endDate), + $country + ); + + response()->json([ + 'workdays' => $planner->getWorkdays(), + 'count' => count($planner), + ]); + } catch (\Exception $e) { + response()->httpCode(400); + response()->json(['error' => $e->getMessage()]); + } + }); + + SimpleRouter::get('/holidays', function () { + $year = input('year', date('Y')); + $country = input('country', 'us'); + + try { + $detector = new HolidayDetector($country); + $detector->changeYear((int) $year); + + $holidays = []; + $reflection = new \ReflectionClass($detector); + $property = $reflection->getProperty('holidaysByName'); + $property->setAccessible(true); + $holidaysByName = $property->getValue($detector); + + foreach ($holidaysByName as $name => $date) { + $holidays[] = [ + 'name' => $name, + 'date' => $date->format('Y-m-d'), + ]; + } + + response()->json([ + 'holidays' => $holidays, + 'count' => count($holidays), + ]); + } catch (\Exception $e) { + response()->httpCode(400); + response()->json(['error' => $e->getMessage()]); + } + }); + + SimpleRouter::get('/is-workday', function () { + $date = input('date'); + $country = input('country', 'us'); + + if (!$date) { + response()->httpCode(400); + response()->json(['error' => 'date parameter is required']); + return; + } + + try { + $isWorkday = WorkdayDetector::isWorkday(new \DateTime($date), $country); + response()->json([ + 'isWorkday' => $isWorkday, + 'date' => $date, + 'country' => $country, + ]); + } catch (\Exception $e) { + response()->httpCode(400); + response()->json(['error' => $e->getMessage()]); + } + }); + + SimpleRouter::get('/is-holiday', function () { + $date = input('date'); + $country = input('country', 'us'); + + if (!$date) { + response()->httpCode(400); + response()->json(['error' => 'date parameter is required']); + return; + } + + try { + $isHoliday = (new HolidayDetector($country))->isHoliday($date); + response()->json([ + 'isHoliday' => $isHoliday, + 'date' => $date, + 'country' => $country, + ]); + } catch (\Exception $e) { + response()->httpCode(400); + response()->json(['error' => $e->getMessage()]); + } + }); +}); diff --git a/tests/Integration/WorkdayPlannerAPITest.php b/tests/Integration/WorkdayPlannerAPITest.php new file mode 100644 index 0000000..9c3de9e --- /dev/null +++ b/tests/Integration/WorkdayPlannerAPITest.php @@ -0,0 +1,74 @@ +api = new RESTSpeaker('http://localhost:3000'); + } + + public function testCanGetWorkdays() + { + $response = $this->api->get('/api/workdays', [ + 'startDate' => '2023-01-01', + 'endDate' => '2023-01-31', + 'country' => 'us', + ]); + + $this->assertEquals(200, $response->status); + $this->assertIsArray($response->body->workdays); + $this->assertGreaterThan(0, $response->body->count); + } + + public function testCanGetHolidays() + { + $response = $this->api->get('/api/holidays', [ + 'year' => '2023', + 'country' => 'us', + ]); + + $this->assertEquals(200, $response->status); + $this->assertIsArray($response->body->holidays); + $this->assertGreaterThan(0, $response->body->count); + } + + public function testCanCheckWorkday() + { + $response = $this->api->get('/api/is-workday', [ + 'date' => '2023-01-02', + 'country' => 'us', + ]); + + $this->assertEquals(200, $response->status); + $this->assertIsBool($response->body->isWorkday); + } + + public function testCanCheckHoliday() + { + $response = $this->api->get('/api/is-holiday', [ + 'date' => '2023-01-01', + 'country' => 'us', + ]); + + $this->assertEquals(200, $response->status); + $this->assertIsBool($response->body->isHoliday); + } + + public function testReturnsErrorForInvalidDates() + { + $response = $this->api->get('/api/workdays', [ + 'startDate' => 'invalid', + 'endDate' => 'invalid', + ]); + + $this->assertEquals(400, $response->status); + $this->assertStringContainsString('error', json_encode($response->body)); + } +}