Browse Source

Integer + Regextypes, unit tests

Roman 1 week ago
parent
commit
a80b34e78f

+ 57 - 0
Core/API/Parameter/IntegerType.class.php

@@ -0,0 +1,57 @@
+<?php
+
+namespace Core\API\Parameter;
+
+class IntegerType extends Parameter {
+
+  public int $minValue;
+  public int $maxValue;
+  public function __construct(string $name, int $minValue = PHP_INT_MIN, int $maxValue = PHP_INT_MAX,
+                              bool $optional = FALSE, ?int $defaultValue = NULL, ?array $choices = NULL) {
+    $this->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;
+  }
+}

+ 3 - 5
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";
     }

+ 36 - 0
Core/API/Parameter/RegexType.class.php

@@ -0,0 +1,36 @@
+<?php
+
+namespace Core\API\Parameter;
+
+class RegexType extends StringType {
+
+  public string $pattern;
+
+  public function __construct(string $name, string $pattern, bool $optional = FALSE,
+                              ?string $defaultValue = NULL) {
+    $this->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)";
+  }
+}

+ 2 - 1
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);
   }

+ 3 - 3
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 <b>>= $requiredVersion</b> is required. Got: <b>" . PHP_VERSION . "</b>";
         $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:<br>" .
           $this->createUnorderedList($missingInputs);

+ 58 - 11
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"));
+  }
 }

+ 4 - 2
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() {