From a80b34e78f195640ca4949599f0eb5fbe7e45193 Mon Sep 17 00:00:00 2001 From: Roman Date: Mon, 22 Apr 2024 12:41:15 +0200 Subject: [PATCH] Integer + Regextypes, unit tests --- Core/API/Parameter/IntegerType.class.php | 57 ++++++++++++++++++++ Core/API/Parameter/Parameter.class.php | 8 ++- Core/API/Parameter/RegexType.class.php | 36 +++++++++++++ Core/API/Parameter/StringType.class.php | 3 +- Core/Documents/Install.class.php | 6 +-- test/Parameter.test.php | 69 ++++++++++++++++++++---- test/Request.test.php | 6 ++- 7 files changed, 163 insertions(+), 22 deletions(-) create mode 100644 Core/API/Parameter/IntegerType.class.php create mode 100644 Core/API/Parameter/RegexType.class.php diff --git a/Core/API/Parameter/IntegerType.class.php b/Core/API/Parameter/IntegerType.class.php new file mode 100644 index 0000000..898fe45 --- /dev/null +++ b/Core/API/Parameter/IntegerType.class.php @@ -0,0 +1,57 @@ +minValue = $minValue; + $this->maxValue = $maxValue; + parent::__construct($name, Parameter::TYPE_INT, $optional, $defaultValue, $choices); + } + + public function parseParam($value): bool { + if (!parent::parseParam($value)) { + return false; + } + + $this->value = $value; + if ($this->value < $this->minValue || $this->value > $this->maxValue) { + return false; + } + + return true; + } + + public function getTypeName(): string { + $typeName = parent::getTypeName(); + $hasMin = $this->minValue > PHP_INT_MIN; + $hasMax = $this->maxValue < PHP_INT_MAX; + + if ($hasMin || $hasMax) { + if ($hasMin && $hasMax) { + $typeName .= " ($this->minValue - $this->maxValue)"; + } else if ($hasMin) { + $typeName .= " (> $this->minValue)"; + } else if ($hasMax) { + $typeName .= " (< $this->maxValue)"; + } + } + + return $typeName; + } + + public function toString(): string { + $typeName = $this->getTypeName(); + $str = "$typeName $this->name"; + $defaultValue = (is_null($this->value) ? 'NULL' : $this->value); + if ($this->optional) { + $str = "[$str = $defaultValue]"; + } + + return $str; + } +} \ No newline at end of file diff --git a/Core/API/Parameter/Parameter.class.php b/Core/API/Parameter/Parameter.class.php index 3b1afe7..f1e3076 100644 --- a/Core/API/Parameter/Parameter.class.php +++ b/Core/API/Parameter/Parameter.class.php @@ -17,12 +17,10 @@ class Parameter { // only internal access const TYPE_RAW = 8; - // only json will work here I guess - // nope. also name[]=value const TYPE_ARRAY = 9; const TYPE_MIXED = 10; - const names = array('Integer', 'Float', 'Boolean', 'String', 'Date', 'Time', 'DateTime', 'E-Mail', 'Raw', 'Array', 'Mixed'); + const names = ['Integer', 'Float', 'Boolean', 'String', 'Date', 'Time', 'DateTime', 'E-Mail', 'Raw', 'Array', 'Mixed']; const DATE_FORMAT = "Y-m-d"; const TIME_FORMAT = "H:i:s"; @@ -47,12 +45,12 @@ class Parameter { $this->typeName = $this->getTypeName(); } - public function reset() { + public function reset(): void { $this->value = $this->defaultValue; } public function getSwaggerTypeName(): string { - $typeName = strtolower(($this->type >= 0 && $this->type < count(Parameter::names)) ? Parameter::names[$this->type] : "invalid"); + $typeName = strtolower(Parameter::names[$this->type] ?? "invalid"); if ($typeName === "mixed" || $typeName === "raw") { return "object"; } diff --git a/Core/API/Parameter/RegexType.class.php b/Core/API/Parameter/RegexType.class.php new file mode 100644 index 0000000..7338b13 --- /dev/null +++ b/Core/API/Parameter/RegexType.class.php @@ -0,0 +1,36 @@ +pattern = $pattern; + + if (!startsWith($this->pattern, "/") || !endsWith($this->pattern, "/")) { + $this->pattern = "/" . $this->pattern . "/"; + } + + parent::__construct($name, -1, $optional, $defaultValue); + } + + public function parseParam($value): bool { + if (!parent::parseParam($value)) { + return false; + } + + $matches = []; + if (!preg_match($this->pattern, $this->value, $matches)) { + return false; + } + + return strlen($matches[0]) === strlen($this->value); + } + + public function getTypeName(): string { + return parent::getTypeName() . " ($this->pattern)"; + } +} \ No newline at end of file diff --git a/Core/API/Parameter/StringType.class.php b/Core/API/Parameter/StringType.class.php index c3878c1..e27e1e4 100644 --- a/Core/API/Parameter/StringType.class.php +++ b/Core/API/Parameter/StringType.class.php @@ -7,7 +7,8 @@ class StringType extends Parameter { const UNLIMITED = -1; public int $maxLength; - public function __construct(string $name, int $maxLength = self::UNLIMITED, bool $optional = FALSE, ?string $defaultValue = NULL, ?array $choices = NULL) { + public function __construct(string $name, int $maxLength = self::UNLIMITED, bool $optional = FALSE, + ?string $defaultValue = NULL, ?array $choices = NULL) { $this->maxLength = $maxLength; parent::__construct($name, Parameter::TYPE_STRING, $optional, $defaultValue, $choices); } diff --git a/Core/Documents/Install.class.php b/Core/Documents/Install.class.php index f63759e..cbdb93b 100644 --- a/Core/Documents/Install.class.php +++ b/Core/Documents/Install.class.php @@ -268,7 +268,7 @@ namespace Documents\Install { $success = false; } - $requiredVersion = '8.1'; + $requiredVersion = "8.2"; if (version_compare(PHP_VERSION, $requiredVersion, '<')) { $failedRequirements[] = "PHP Version >= $requiredVersion is required. Got: " . PHP_VERSION . ""; $success = false; @@ -290,7 +290,7 @@ namespace Documents\Install { $this->errorString = $msg; } - return array("success" => $success, "msg" => $msg); + return ["success" => $success, "msg" => $msg]; } private function installDependencies(): array { @@ -348,7 +348,7 @@ namespace Documents\Install { $missingInputs[] = "Type"; } - $supportedTypes = array("mysql", "postgres"); + $supportedTypes = ["mysql", "postgres"]; if (!$success) { $msg = "Please fill out the following inputs:
" . $this->createUnorderedList($missingInputs); diff --git a/test/Parameter.test.php b/test/Parameter.test.php index 61c165b..18e508c 100644 --- a/test/Parameter.test.php +++ b/test/Parameter.test.php @@ -29,12 +29,16 @@ class ParameterTest extends \PHPUnit\Framework\TestCase { $this->assertTrue($random->parseParam($data)); $this->assertEquals($data, $random->value); - // test data types + // test data types: we cast the values to string + $this->assertTrue($random->parseParam(1)); + $this->assertTrue($random->parseParam(2.5)); + $this->assertTrue($random->parseParam(true)); + $this->assertTrue($random->parseParam(false)); + + // null values only allowed, when parameter is optional $this->assertFalse($random->parseParam(null)); - $this->assertFalse($random->parseParam(1)); - $this->assertFalse($random->parseParam(2.5)); - $this->assertFalse($random->parseParam(true)); - $this->assertFalse($random->parseParam(false)); + + // arrays cannot be cast to string (easily) $this->assertFalse($random->parseParam(["key" => 1])); } @@ -64,12 +68,6 @@ class ParameterTest extends \PHPUnit\Framework\TestCase { } public function testParseType() { - // int - $this->assertEquals(Parameter::TYPE_INT, Parameter::parseType(1)); - $this->assertEquals(Parameter::TYPE_INT, Parameter::parseType(1.0)); - $this->assertEquals(Parameter::TYPE_INT, Parameter::parseType("1")); - $this->assertEquals(Parameter::TYPE_INT, Parameter::parseType("1.0")); - // array $this->assertEquals(Parameter::TYPE_ARRAY, Parameter::parseType([1, true])); @@ -106,4 +104,53 @@ class ParameterTest extends \PHPUnit\Framework\TestCase { // string, everything else $this->assertEquals(Parameter::TYPE_STRING, Parameter::parseType("test")); } + + public function testIntegerType() { + // int + $this->assertEquals(Parameter::TYPE_INT, Parameter::parseType(1)); + $this->assertEquals(Parameter::TYPE_INT, Parameter::parseType(1.0)); + $this->assertEquals(Parameter::TYPE_INT, Parameter::parseType("1")); + $this->assertEquals(Parameter::TYPE_INT, Parameter::parseType("1.0")); + + // test min value + $min = new \Core\API\Parameter\IntegerType("test_has_min_value", 10); + $this->assertTrue($min->parseParam(10)); + $this->assertTrue($min->parseParam(11)); + $this->assertFalse($min->parseParam(9)); + + // test max value + $max = new \Core\API\Parameter\IntegerType("test_has_min_value", PHP_INT_MIN, 100); + $this->assertTrue($max->parseParam(99)); + $this->assertTrue($max->parseParam(100)); + $this->assertFalse($max->parseParam(101)); + + // test min and max value + $minmax = new \Core\API\Parameter\IntegerType("test_has_min_value", 10, 100); + $this->assertTrue($minmax->parseParam(10)); + $this->assertTrue($minmax->parseParam(11)); + $this->assertFalse($minmax->parseParam(9)); + $this->assertTrue($minmax->parseParam(99)); + $this->assertTrue($minmax->parseParam(100)); + $this->assertFalse($minmax->parseParam(101)); + } + + public function testRegexType() { + $onlyLowercase = new \Core\API\Parameter\RegexType("only_lowercase", "/[a-z]+/"); + $this->assertTrue($onlyLowercase->parseParam("abcdefghiklmnopqrstuvwxyz")); + $this->assertFalse($onlyLowercase->parseParam("0123456789")); + + $onlyLowercaseOneChar = new \Core\API\Parameter\RegexType("only_lowercase_one_char", "/^[a-z]$/"); + $this->assertFalse($onlyLowercaseOneChar->parseParam("abcdefghiklmnopqrstuvwxyz")); + $this->assertTrue($onlyLowercaseOneChar->parseParam("a")); + + $regexWithoutSlashes = new \Core\API\Parameter\RegexType("regex_no_slash", "[a-z]+"); + $this->assertTrue($regexWithoutSlashes->parseParam("abcdefghiklmnopqrstuvwxyz")); + $this->assertFalse($regexWithoutSlashes->parseParam("0123456789")); + + $integerRegex = new \Core\API\Parameter\RegexType("integer_regex", "[1-9][0-9]*"); + $this->assertTrue($integerRegex->parseParam("12")); + $this->assertTrue($integerRegex->parseParam(12)); + $this->assertFalse($integerRegex->parseParam("012")); + $this->assertFalse($integerRegex->parseParam("1.2")); + } } \ No newline at end of file diff --git a/test/Request.test.php b/test/Request.test.php index 8c5bacc..417f950 100644 --- a/test/Request.test.php +++ b/test/Request.test.php @@ -102,7 +102,8 @@ class RequestTest extends \PHPUnit\Framework\TestCase { $this->assertFalse($this->simulateRequest($allMethodsAllowed, "NONEXISTENT"), $allMethodsAllowed->getLastError()); $this->assertTrue($this->simulateRequest($allMethodsAllowed, "OPTIONS"), $allMethodsAllowed->getLastError()); $this->assertEquals(204, self::$SENT_STATUS_CODE); - $this->assertEquals(["Allow" => "OPTIONS, GET, POST"], self::$SENT_HEADERS); + $this->assertArrayHasKey("Allow", self::$SENT_HEADERS); + $this->assertEquals(["OPTIONS", "GET", "POST"], explode(", ", self::$SENT_HEADERS["Allow"])); } public function testOnlyPost() { @@ -114,7 +115,8 @@ class RequestTest extends \PHPUnit\Framework\TestCase { $this->assertTrue($this->simulateRequest($onlyPostAllowed, "POST"), $onlyPostAllowed->getLastError()); $this->assertTrue($this->simulateRequest($onlyPostAllowed, "OPTIONS"), $onlyPostAllowed->getLastError()); $this->assertEquals(204, self::$SENT_STATUS_CODE); - $this->assertEquals(["Allow" => "OPTIONS, POST"], self::$SENT_HEADERS); + $this->assertArrayHasKey("Allow", self::$SENT_HEADERS); + $this->assertEquals(["OPTIONS", "POST"], explode(", ", self::$SENT_HEADERS["Allow"])); } public function testPrivate() {