diff --git a/src/Exception/ProcessorRuntimeException.php b/src/Exception/ProcessorRuntimeException.php index 293a31b..04cca4c 100644 --- a/src/Exception/ProcessorRuntimeException.php +++ b/src/Exception/ProcessorRuntimeException.php @@ -33,33 +33,6 @@ public static function processorNotFound(string $processorName, string $context) ); } - public static function invalidProcessor(string $processorName, string $details): self - { - return self::createException( - self::CODE_INVALID_PROCESSOR, - 'PROCESSOR_INVALID', - "Invalid processor '{$processorName}': {$details}" - ); - } - - public static function invalidContext(string $context, string $details): self - { - return self::createException( - self::CODE_INVALID_CONTEXT, - 'PROCESSOR_CONTEXT_INVALID', - "Invalid processor context '{$context}': {$details}" - ); - } - - public static function invalidConfiguration(string $processorName, string $details): self - { - return self::createException( - self::CODE_PROCESSOR_CONFIG_INVALID, - 'PROCESSOR_CONFIG_INVALID', - "Invalid processor configuration for '{$processorName}': {$details}" - ); - } - public static function processingFailed(string $property): self { return self::createException( diff --git a/tests/Exception/ProcessorRuntimeExceptionTest .php b/tests/Exception/ProcessorRuntimeExceptionTest .php deleted file mode 100644 index 3f68afe..0000000 --- a/tests/Exception/ProcessorRuntimeExceptionTest .php +++ /dev/null @@ -1,68 +0,0 @@ -assertSame(2601, $exception->getCode()); - $this->assertSame('PROCESSOR_CONTEXT_NOT_FOUND', $exception->getErrorCode()); - $this->assertSame("Processor context 'payment' not found", $exception->getMessage()); - } - - public function testProcessorNotFound(): void - { - $exception = ProcessorRuntimeException::processorNotFound('validate', 'payment'); - - $this->assertSame(2602, $exception->getCode()); - $this->assertSame('PROCESSOR_NOT_FOUND', $exception->getErrorCode()); - $this->assertSame("Processor 'validate' not found in context 'payment'", $exception->getMessage()); - } - - public function testInvalidProcessor(): void - { - $exception = ProcessorRuntimeException::invalidProcessor('emailValidator', 'Invalid configuration'); - - $this->assertSame(2603, $exception->getCode()); - $this->assertSame('PROCESSOR_INVALID', $exception->getErrorCode()); - $this->assertSame("Invalid processor 'emailValidator': Invalid configuration", $exception->getMessage()); - } - - public function testInvalidContext(): void - { - $exception = ProcessorRuntimeException::invalidContext('payment', 'Context not initialized'); - - $this->assertSame(2604, $exception->getCode()); - $this->assertSame('PROCESSOR_CONTEXT_INVALID', $exception->getErrorCode()); - $this->assertSame("Invalid processor context 'payment': Context not initialized", $exception->getMessage()); - } - - public function testInvalidConfiguration(): void - { - $exception = ProcessorRuntimeException::invalidConfiguration('emailValidator', 'Missing required fields'); - - $this->assertSame(2605, $exception->getCode()); - $this->assertSame('PROCESSOR_CONFIG_INVALID', $exception->getErrorCode()); - $this->assertSame( - "Invalid processor configuration for 'emailValidator': Missing required fields", - $exception->getMessage() - ); - } - - public function testProcessingFailed(): void - { - $exception = ProcessorRuntimeException::processingFailed('email'); - - $this->assertSame(2606, $exception->getCode()); - $this->assertSame('PROCESSOR_PROCESSING_FAILED', $exception->getErrorCode()); - $this->assertSame("Processing failed for property 'email'", $exception->getMessage()); - } -} diff --git a/tests/Exception/ProcessorRuntimeExceptionTest.php b/tests/Exception/ProcessorRuntimeExceptionTest.php new file mode 100644 index 0000000..f2ce614 --- /dev/null +++ b/tests/Exception/ProcessorRuntimeExceptionTest.php @@ -0,0 +1,123 @@ +assertInstanceOf(ProcessorRuntimeException::class, $exception); + $this->assertEquals(2601, $exception->getCode()); + $this->assertEquals('PROCESSOR_CONTEXT_NOT_FOUND', $exception->getErrorCode()); + $this->assertEquals("Processor context 'payment' not found", $exception->getMessage()); + $this->assertNull($exception->getPrevious()); + } + + public function testProcessorNotFound(): void + { + $exception = ProcessorRuntimeException::processorNotFound('validate', 'payment'); + + $this->assertInstanceOf(ProcessorRuntimeException::class, $exception); + $this->assertEquals(2602, $exception->getCode()); + $this->assertEquals('PROCESSOR_NOT_FOUND', $exception->getErrorCode()); + $this->assertEquals("Processor 'validate' not found in context 'payment'", $exception->getMessage()); + $this->assertNull($exception->getPrevious()); + } + + public function testProcessingFailed(): void + { + $exception = ProcessorRuntimeException::processingFailed('email'); + + $this->assertInstanceOf(ProcessorRuntimeException::class, $exception); + $this->assertEquals(2606, $exception->getCode()); + $this->assertEquals('PROCESSOR_PROCESSING_FAILED', $exception->getErrorCode()); + $this->assertEquals( + "Processing failed for property 'email'", + $exception->getMessage() + ); + $this->assertNull($exception->getPrevious()); + } + + /** + * @dataProvider specialValuesProvider + */ + public function testWithSpecialValues(string $context, string $processor, string $details): void + { + $exceptionContext = ProcessorRuntimeException::contextNotFound($context); + $this->assertStringContainsString($context, $exceptionContext->getMessage()); + + $exceptionProcessor = ProcessorRuntimeException::processorNotFound($processor, $context); + $this->assertStringContainsString($processor, $exceptionProcessor->getMessage()); + $this->assertStringContainsString($context, $exceptionProcessor->getMessage()); + } + + public static function specialValuesProvider(): array + { + return [ + 'empty values' => ['', '', ''], + 'special characters' => ['payment!@#', 'validator$%^', 'error&*()'], + 'unicode characters' => ['pagaménto', 'validação', 'erro'], + 'very long values' => [ + str_repeat('a', 100), + str_repeat('b', 100), + str_repeat('c', 100), + ], + ]; + } + + public function testExceptionHierarchy(): void + { + $exception = ProcessorRuntimeException::contextNotFound('payment'); + + $this->assertInstanceOf(\Exception::class, $exception); + $this->assertInstanceOf(\Throwable::class, $exception); + } + + public function testExceptionWithPreviousException(): void + { + $previous = new \Exception('Original error'); + + $reflection = new \ReflectionClass(ProcessorRuntimeException::class); + $method = $reflection->getMethod('createException'); + $method->setAccessible(true); + + $exception = $method->invokeArgs(null, [ + 2601, + 'PROCESSOR_CONTEXT_NOT_FOUND', + 'Test message', + $previous, + ]); + + $this->assertInstanceOf(ProcessorRuntimeException::class, $exception); + $this->assertEquals(2601, $exception->getCode()); + $this->assertEquals('PROCESSOR_CONTEXT_NOT_FOUND', $exception->getErrorCode()); + $this->assertEquals('Test message', $exception->getMessage()); + $this->assertSame($previous, $exception->getPrevious()); + } + + /** + * @dataProvider invalidPropertyValuesProvider + */ + public function testProcessingFailedWithDifferentPropertyTypes($property): void + { + $exception = ProcessorRuntimeException::processingFailed($property); + $message = $exception->getMessage(); + + $this->assertIsString($message); + $this->assertStringContainsString((string) $property, $message); + } + + public static function invalidPropertyValuesProvider(): array + { + return [ + 'valid string' => ['email'], + ]; + } +} diff --git a/tests/Handler/ProcessorAttributeHandlerTest.php b/tests/Handler/ProcessorAttributeHandlerTest.php index 6a19f43..0109239 100644 --- a/tests/Handler/ProcessorAttributeHandlerTest.php +++ b/tests/Handler/ProcessorAttributeHandlerTest.php @@ -182,4 +182,44 @@ private function configureBasicMocks(): void return null; }); } + + public function testValidateProcessorsWithInvalidProcessor(): void + { + $processorsConfig = ['processor1' => []]; + $messages = ['processor1' => 'Validation failed for processor1']; + + $processor = $this->createMock(ValidatableProcessor::class); + $processor->method('isValid')->willReturn(false); + $processor->method('getErrorKey')->willReturn('invalid_processor'); + + $this->builder->method('build')->willReturn($processor); + + // Usar Reflection para acessar validateProcessors + $reflection = new \ReflectionClass(ProcessorAttributeHandler::class); + $method = $reflection->getMethod('validateProcessors'); + $method->setAccessible(true); + + $errors = $method->invoke($this->handler, $processorsConfig, $messages); + + $this->assertArrayHasKey('processor1', $errors); + $this->assertEquals('invalid_processor', $errors['processor1']['errorKey']); + $this->assertEquals('Validation failed for processor1', $errors['processor1']['message']); + } + + public function testProcessValueWithValidPipeline(): void + { + $config = ['processor1' => []]; + $this->pipeline->method('process')->willReturn('processed_value'); + + $this->builder->method('buildPipeline')->willReturn($this->pipeline); + + // Usar Reflection para acessar processValue + $reflection = new \ReflectionClass(ProcessorAttributeHandler::class); + $method = $reflection->getMethod('processValue'); + $method->setAccessible(true); + + $result = $method->invoke($this->handler, 'input_value', $config); + + $this->assertEquals('processed_value', $result); + } } diff --git a/tests/Result/ProcessedDataTest.php b/tests/Result/ProcessedDataTest.php new file mode 100644 index 0000000..2e32e4b --- /dev/null +++ b/tests/Result/ProcessedDataTest.php @@ -0,0 +1,63 @@ +assertEquals('email', $data->getProperty()); + } + + public function testGetValue(): void + { + $data = new ProcessedData('email', 'test@example.com'); + + $this->assertEquals('test@example.com', $data->getValue()); + } + + public function testToArray(): void + { + $data = new ProcessedData('email', 'test@example.com'); + $result = $data->toArray(); + + $this->assertIsArray($result); + $this->assertArrayHasKey('value', $result); + $this->assertArrayHasKey('timestamp', $result); + $this->assertEquals('test@example.com', $result['value']); + $this->assertIsInt($result['timestamp']); + $this->assertLessThanOrEqual(time(), $result['timestamp']); + } + + /** + * @dataProvider valueTypesProvider + */ + public function testDifferentValueTypes(mixed $value): void + { + $data = new ProcessedData('property', $value); + + $this->assertSame($value, $data->getValue()); + $array = $data->toArray(); + $this->assertSame($value, $array['value']); + } + + public static function valueTypesProvider(): array + { + return [ + 'string' => ['test'], + 'integer' => [42], + 'float' => [3.14], + 'boolean' => [true], + 'null' => [null], + 'array' => [['test' => 'value']], + 'object' => [new \stdClass()], + ]; + } +} diff --git a/tests/Result/ProcessingErrorTest.php b/tests/Result/ProcessingErrorTest.php new file mode 100644 index 0000000..ba487a7 --- /dev/null +++ b/tests/Result/ProcessingErrorTest.php @@ -0,0 +1,87 @@ +assertEquals($expectedHash, $error->getHash()); + } + + public function testGetProperty(): void + { + $error = new ProcessingError('email', 'invalid_email', 'Invalid email format'); + + $this->assertEquals('email', $error->getProperty()); + } + + public function testGetErrorKey(): void + { + $error = new ProcessingError('email', 'invalid_email', 'Invalid email format'); + + $this->assertEquals('invalid_email', $error->getErrorKey()); + } + + public function testGetMessage(): void + { + $error = new ProcessingError('email', 'invalid_email', 'Invalid email format'); + + $this->assertEquals('Invalid email format', $error->getMessage()); + } + + public function testToArray(): void + { + $error = new ProcessingError('email', 'invalid_email', 'Invalid email format'); + $result = $error->toArray(); + + $this->assertIsArray($result); + $this->assertArrayHasKey('errorKey', $result); + $this->assertArrayHasKey('message', $result); + $this->assertArrayHasKey('timestamp', $result); + $this->assertEquals('invalid_email', $result['errorKey']); + $this->assertEquals('Invalid email format', $result['message']); + $this->assertIsInt($result['timestamp']); + $this->assertLessThanOrEqual(time(), $result['timestamp']); + } + + public function testHashUniqueness(): void + { + $error1 = new ProcessingError('email', 'invalid_email', 'Invalid email format'); + $error2 = new ProcessingError('email', 'invalid_email', 'Invalid email format'); + $error3 = new ProcessingError('email', 'different_error', 'Invalid email format'); + + $this->assertEquals($error1->getHash(), $error2->getHash()); + $this->assertNotEquals($error1->getHash(), $error3->getHash()); + } + + /** + * @dataProvider specialCharactersProvider + */ + public function testHashWithSpecialCharacters(string $property, string $errorKey, string $message): void + { + $error = new ProcessingError($property, $errorKey, $message); + + $this->assertNotEmpty($error->getHash()); + $this->assertEquals(32, strlen($error->getHash())); + } + + public static function specialCharactersProvider(): array + { + return [ + 'unicode' => ['émáil', 'error_key', 'Test message'], + 'symbols' => ['email@test', '!error_key!', 'Test message!'], + 'spaces' => ['email test', 'error key', 'Test message with spaces'], + 'empty' => ['', '', ''], + 'mixed' => ['email#123', 'error_key!@#', 'Test message 123!@#'], + ]; + } +} diff --git a/tests/Result/ProcessingResultCollection.php b/tests/Result/ProcessingResultCollection.php new file mode 100644 index 0000000..1455dfa --- /dev/null +++ b/tests/Result/ProcessingResultCollection.php @@ -0,0 +1,317 @@ +collection = new ProcessingResultCollection(); + } + + public function testAddError(): void + { + $this->collection->addError('email', 'invalid_email', 'Invalid email format'); + + $errors = $this->collection->getErrors(); + $this->assertArrayHasKey('email', $errors); + $this->assertCount(1, $errors['email']); + $this->assertEquals('invalid_email', $errors['email'][0]['errorKey']); + $this->assertEquals('Invalid email format', $errors['email'][0]['message']); + } + + public function testAddMultipleErrorsForSameProperty(): void + { + $this->collection->addError('email', 'invalid_email', 'Invalid email format'); + $this->collection->addError('email', 'required', 'Email is required'); + + $errors = $this->collection->getErrors(); + $this->assertArrayHasKey('email', $errors); + $this->assertCount(2, $errors['email']); + } + + public function testAddDuplicateError(): void + { + $this->collection->addError('email', 'invalid_email', 'Invalid email format'); + $this->collection->addError('email', 'invalid_email', 'Invalid email format'); + + $errors = $this->collection->getErrors(); + $this->assertArrayHasKey('email', $errors); + $this->assertCount(1, $errors['email']); + } + + public function testSetProcessedData(): void + { + $this->collection->setProcessedData('email', 'test@example.com'); + + $data = $this->collection->getProcessedData(); + $this->assertArrayHasKey('email', $data); + $this->assertEquals('test@example.com', $data['email']); + } + + public function testOverwriteProcessedData(): void + { + $this->collection->setProcessedData('email', 'old@example.com'); + $this->collection->setProcessedData('email', 'new@example.com'); + + $data = $this->collection->getProcessedData(); + $this->assertEquals('new@example.com', $data['email']); + } + + public function testHasErrorsWithNoErrors(): void + { + $this->assertFalse($this->collection->hasErrors()); + } + + public function testHasErrorsWithErrors(): void + { + $this->collection->addError('email', 'invalid_email', 'Invalid email format'); + $this->assertTrue($this->collection->hasErrors()); + } + + public function testToArrayWithNoData(): void + { + $result = $this->collection->toArray(); + + $this->assertIsArray($result); + $this->assertArrayHasKey('isValid', $result); + $this->assertArrayHasKey('errors', $result); + $this->assertArrayHasKey('processedData', $result); + $this->assertTrue($result['isValid']); + $this->assertEmpty($result['errors']); + $this->assertEmpty($result['processedData']); + } + + public function testToArrayWithDataAndErrors(): void + { + $this->collection->setProcessedData('email', 'test@example.com'); + $this->collection->addError('name', 'required', 'Name is required'); + + $result = $this->collection->toArray(); + + $this->assertFalse($result['isValid']); + $this->assertArrayHasKey('name', $result['errors']); + $this->assertArrayHasKey('email', $result['processedData']); + $this->assertEquals('test@example.com', $result['processedData']['email']); + } + + public function testClear(): void + { + $this->collection->setProcessedData('email', 'test@example.com'); + $this->collection->addError('name', 'required', 'Name is required'); + + $this->collection->clear(); + + $this->assertFalse($this->collection->hasErrors()); + $this->assertEmpty($this->collection->getErrors()); + $this->assertEmpty($this->collection->getProcessedData()); + } + + public function testAddProcessedData(): void + { + $processedData = new ProcessedData('email', 'test@example.com'); + $this->collection->addProcessedData($processedData); + + $data = $this->collection->getProcessedData(); + $this->assertArrayHasKey('email', $data); + $this->assertEquals('test@example.com', $data['email']); + } + + public function testAddProcessingError(): void + { + $error = new ProcessingError('email', 'invalid_email', 'Invalid email format'); + $this->collection->addProcessingError($error); + + $errors = $this->collection->getErrors(); + $this->assertArrayHasKey('email', $errors); + $this->assertEquals('invalid_email', $errors['email'][0]['errorKey']); + $this->assertEquals('Invalid email format', $errors['email'][0]['message']); + } + + public function testMultiplePropertiesWithErrors(): void + { + $this->collection->addError('email', 'invalid_email', 'Invalid email format'); + $this->collection->addError('name', 'required', 'Name is required'); + $this->collection->addError('age', 'min_value', 'Age must be at least 18'); + + $errors = $this->collection->getErrors(); + $this->assertCount(3, $errors); + $this->assertArrayHasKey('email', $errors); + $this->assertArrayHasKey('name', $errors); + $this->assertArrayHasKey('age', $errors); + } + + public function testMultiplePropertiesWithProcessedData(): void + { + $this->collection->setProcessedData('email', 'test@example.com'); + $this->collection->setProcessedData('name', 'John Doe'); + $this->collection->setProcessedData('age', 25); + + $data = $this->collection->getProcessedData(); + $this->assertCount(3, $data); + $this->assertEquals('test@example.com', $data['email']); + $this->assertEquals('John Doe', $data['name']); + $this->assertEquals(25, $data['age']); + } + + public function testMixedDataTypes(): void + { + $values = [ + 'string' => 'test', + 'integer' => 42, + 'float' => 3.14, + 'boolean' => true, + 'array' => ['test' => 'value'], + 'null' => null, + 'object' => new \stdClass(), + ]; + + foreach ($values as $key => $value) { + $this->collection->setProcessedData($key, $value); + } + + $data = $this->collection->getProcessedData(); + foreach ($values as $key => $value) { + $this->assertArrayHasKey($key, $data); + $this->assertEquals($value, $data[$key]); + } + } + + public function testErrorCollectionWithSameHashButDifferentProperties(): void + { + $this->collection->addError('email1', 'invalid', 'Invalid format'); + $this->collection->addError('email2', 'invalid', 'Invalid format'); + + $errors = $this->collection->getErrors(); + $this->assertArrayHasKey('email1', $errors); + $this->assertArrayHasKey('email2', $errors); + $this->assertCount(1, $errors['email1']); + $this->assertCount(1, $errors['email2']); + } + + public function testToArrayWithAllPossibleStates(): void + { + $processedData = new ProcessedData('email', 'test@example.com'); + $this->collection->addProcessedData($processedData); + + $error = new ProcessingError('password', 'required', 'Password is required'); + $this->collection->addProcessingError($error); + + $result = $this->collection->toArray(); + + $this->assertArrayHasKey('isValid', $result); + $this->assertArrayHasKey('errors', $result); + $this->assertArrayHasKey('processedData', $result); + + $this->assertArrayHasKey('email', $result['processedData']); + $this->assertEquals('test@example.com', $result['processedData']['email']); + + $this->assertArrayHasKey('password', $result['errors']); + $this->assertCount(1, $result['errors']['password']); + $this->assertEquals('required', $result['errors']['password'][0]['errorKey']); + $this->assertEquals('Password is required', $result['errors']['password'][0]['message']); + + $this->assertFalse($result['isValid']); + } + + public function testAddProcessedDataWithExistingProperty(): void + { + $data1 = new ProcessedData('email', 'old@example.com'); + $data2 = new ProcessedData('email', 'new@example.com'); + + $this->collection->addProcessedData($data1); + $this->collection->addProcessedData($data2); + + $result = $this->collection->getProcessedData(); + $this->assertArrayHasKey('email', $result); + $this->assertEquals('new@example.com', $result['email']); + $this->assertCount(1, $result); + } + + public function testAddProcessingErrorWithExistingError(): void + { + $error1 = new ProcessingError('email', 'required', 'Email is required'); + $error2 = new ProcessingError('email', 'required', 'Email is required'); + $error3 = new ProcessingError('email', 'invalid', 'Invalid email format'); + + $this->collection->addProcessingError($error1); + $this->collection->addProcessingError($error2); + $this->collection->addProcessingError($error3); + + $errors = $this->collection->getErrors(); + $this->assertArrayHasKey('email', $errors); + $this->assertCount(2, $errors['email']); + + $errorMessages = array_column($errors['email'], 'message'); + $this->assertContains('Email is required', $errorMessages); + $this->assertContains('Invalid email format', $errorMessages); + } + + public function testClearRemovesAllDataAndErrors(): void + { + $data = new ProcessedData('email', 'test@example.com'); + $this->collection->addProcessedData($data); + + $error = new ProcessingError('email', 'required', 'Email is required'); + $this->collection->addProcessingError($error); + + $this->assertNotEmpty($this->collection->getProcessedData()); + $this->assertNotEmpty($this->collection->getErrors()); + $this->assertTrue($this->collection->hasErrors()); + + $this->collection->clear(); + + $this->assertEmpty($this->collection->getProcessedData()); + $this->assertEmpty($this->collection->getErrors()); + $this->assertFalse($this->collection->hasErrors()); + + $result = $this->collection->toArray(); + $this->assertTrue($result['isValid']); + $this->assertEmpty($result['errors']); + $this->assertEmpty($result['processedData']); + } + + public function testAddProcessedDataAndProcessingErrorWithMultipleProperties(): void + { + $data1 = new ProcessedData('email', 'test@example.com'); + $data2 = new ProcessedData('name', 'John Doe'); + $data3 = new ProcessedData('age', 25); + + $this->collection->addProcessedData($data1); + $this->collection->addProcessedData($data2); + $this->collection->addProcessedData($data3); + + $error1 = new ProcessingError('email', 'invalid', 'Invalid email'); + $error2 = new ProcessingError('email', 'required', 'Email required'); + $error3 = new ProcessingError('password', 'required', 'Password required'); + + $this->collection->addProcessingError($error1); + $this->collection->addProcessingError($error2); + $this->collection->addProcessingError($error3); + + $processedData = $this->collection->getProcessedData(); + $this->assertCount(3, $processedData); + $this->assertEquals('test@example.com', $processedData['email']); + $this->assertEquals('John Doe', $processedData['name']); + $this->assertEquals(25, $processedData['age']); + + $errors = $this->collection->getErrors(); + $this->assertCount(2, $errors); + $this->assertCount(2, $errors['email']); + $this->assertCount(1, $errors['password']); + + $result = $this->collection->toArray(); + $this->assertFalse($result['isValid']); + $this->assertCount(2, $result['errors']); + $this->assertCount(3, $result['processedData']); + } +}