From e1fa5a75ee7e17a2127628ce5c38b3d4acce93ab Mon Sep 17 00:00:00 2001 From: Walmir Silva Date: Thu, 17 Oct 2024 19:42:22 -0300 Subject: [PATCH] refactor(input-exception): enhance InputException with new error types and improve tests - Add new error types for various input validation scenarios - Implement match expression for value formatting - Create comprehensive test suite covering all exception types - Improve error messages with more detailed information - Add support for configuration parameter validation --- src/Input/InputException.php | 103 +++++++++++++++++++++++++++ tests/Input/InputExceptionTest.php | 107 +++++++++++++++++++++++++++++ 2 files changed, 210 insertions(+) diff --git a/src/Input/InputException.php b/src/Input/InputException.php index 97b7d0e..20b0913 100644 --- a/src/Input/InputException.php +++ b/src/Input/InputException.php @@ -11,6 +11,14 @@ final class InputException extends AbstractException private const CODE_INVALID_FORMAT = 1901; private const CODE_MISSING_REQUIRED = 1902; private const CODE_EXCEEDS_MAX_LENGTH = 1903; + private const CODE_INVALID_ARGUMENT = 1904; + private const CODE_BELOW_MIN_LENGTH = 1905; + private const CODE_OUT_OF_RANGE = 1906; + private const CODE_INVALID_TYPE = 1907; + private const CODE_INVALID_OPTION = 1908; + private const CODE_DUPLICATE_ENTRY = 1909; + private const CODE_INVALID_DATE = 1910; + private const CODE_INVALID_CONFIG_PARAM = 1911; public static function invalidFormat(string $field): self { @@ -38,4 +46,99 @@ public static function exceedsMaxLength(string $field, int $maxLength): self "Field '{$field}' exceeds maximum length of {$maxLength}" ); } + + public static function invalidArgument(string $field, mixed $value, ?string $expectedType = null): self + { + $message = "Invalid argument for field '{$field}'. Received value: " . self::formatValue($value); + if (null !== $expectedType) { + $message .= ", expected type: {$expectedType}"; + } + + return self::createException( + self::CODE_INVALID_ARGUMENT, + 'INVALID_ARGUMENT', + $message + ); + } + + public static function belowMinLength(string $field, int $minLength): self + { + return self::createException( + self::CODE_BELOW_MIN_LENGTH, + 'BELOW_MIN_LENGTH', + "Field '{$field}' is below minimum length of {$minLength}" + ); + } + + public static function outOfRange(string $field, $min, $max): self + { + return self::createException( + self::CODE_OUT_OF_RANGE, + 'OUT_OF_RANGE', + "Field '{$field}' is out of range. Must be between {$min} and {$max}" + ); + } + + public static function invalidType(string $field, string $expectedType): self + { + return self::createException( + self::CODE_INVALID_TYPE, + 'INVALID_TYPE', + "Field '{$field}' is of invalid type. Expected {$expectedType}" + ); + } + + public static function invalidOption(string $field, array $validOptions): self + { + $optionsString = implode(', ', $validOptions); + + return self::createException( + self::CODE_INVALID_OPTION, + 'INVALID_OPTION', + "Invalid option for field '{$field}'. Valid options are: {$optionsString}" + ); + } + + public static function duplicateEntry(string $field, $value): self + { + return self::createException( + self::CODE_DUPLICATE_ENTRY, + 'DUPLICATE_ENTRY', + "Duplicate entry for field '{$field}' with value '{$value}'" + ); + } + + public static function invalidDate(string $field, string $format): self + { + return self::createException( + self::CODE_INVALID_DATE, + 'INVALID_DATE', + "Invalid date for field '{$field}'. Expected format: {$format}" + ); + } + + public static function invalidConfigParam(string $param, mixed $value, string $expectedDescription): self + { + $message = "Invalid configuration parameter '{$param}'. " . + 'Received value: ' . self::formatValue($value) . + ". Expected: {$expectedDescription}"; + + return self::createException( + self::CODE_INVALID_CONFIG_PARAM, + 'INVALID_CONFIG_PARAM', + $message + ); + } + + private static function formatValue(mixed $value): string + { + return match (true) { + is_string($value) => "'{$value}'", + is_bool($value) => $value ? 'true' : 'false', + is_null($value) => 'null', + is_array($value) => 'array', + is_object($value) => get_class($value), + default => (string) $value, + }; + } } diff --git a/tests/Input/InputExceptionTest.php b/tests/Input/InputExceptionTest.php index 7ad2498..9014161 100644 --- a/tests/Input/InputExceptionTest.php +++ b/tests/Input/InputExceptionTest.php @@ -45,4 +45,111 @@ public function testExceedsMaxLength(): void 1903 ); } + + public function testInvalidArgument(): void + { + $field = 'age'; + $value = 'not a number'; + $expectedType = 'integer'; + $exception = InputException::invalidArgument($field, $value, $expectedType); + $this->assertExceptionStructure( + $exception, + 'INVALID_ARGUMENT', + "Invalid argument for field 'age'. Received value: 'not a number', expected type: integer", + 1904 + ); + } + + public function testBelowMinLength(): void + { + $field = 'password'; + $minLength = 8; + $exception = InputException::belowMinLength($field, $minLength); + $this->assertExceptionStructure( + $exception, + 'BELOW_MIN_LENGTH', + "Field '{$field}' is below minimum length of {$minLength}", + 1905 + ); + } + + public function testOutOfRange(): void + { + $field = 'rating'; + $min = 1; + $max = 5; + $exception = InputException::outOfRange($field, $min, $max); + $this->assertExceptionStructure( + $exception, + 'OUT_OF_RANGE', + "Field '{$field}' is out of range. Must be between {$min} and {$max}", + 1906 + ); + } + + public function testInvalidType(): void + { + $field = 'count'; + $expectedType = 'integer'; + $exception = InputException::invalidType($field, $expectedType); + $this->assertExceptionStructure( + $exception, + 'INVALID_TYPE', + "Field '{$field}' is of invalid type. Expected {$expectedType}", + 1907 + ); + } + + public function testInvalidOption(): void + { + $field = 'status'; + $validOptions = ['active', 'inactive', 'pending']; + $exception = InputException::invalidOption($field, $validOptions); + $this->assertExceptionStructure( + $exception, + 'INVALID_OPTION', + "Invalid option for field '{$field}'. Valid options are: active, inactive, pending", + 1908 + ); + } + + public function testDuplicateEntry(): void + { + $field = 'email'; + $value = 'test@example.com'; + $exception = InputException::duplicateEntry($field, $value); + $this->assertExceptionStructure( + $exception, + 'DUPLICATE_ENTRY', + "Duplicate entry for field '{$field}' with value '{$value}'", + 1909 + ); + } + + public function testInvalidDate(): void + { + $field = 'birthdate'; + $format = 'Y-m-d'; + $exception = InputException::invalidDate($field, $format); + $this->assertExceptionStructure( + $exception, + 'INVALID_DATE', + "Invalid date for field '{$field}'. Expected format: {$format}", + 1910 + ); + } + + public function testInvalidConfigParam(): void + { + $param = 'timeout'; + $value = -1; + $expectedDescription = 'a positive integer'; + $exception = InputException::invalidConfigParam($param, $value, $expectedDescription); + $this->assertExceptionStructure( + $exception, + 'INVALID_CONFIG_PARAM', + "Invalid configuration parameter '{$param}'. Received value: -1. Expected: {$expectedDescription}", + 1911 + ); + } }