Skip to content

Commit

Permalink
Merge pull request #7 from KaririCode-Framework/develop
Browse files Browse the repository at this point in the history
refactor(AttributeHandler): update handleAttribute to use fallback va…
  • Loading branch information
walmir-silva authored Oct 18, 2024
2 parents 30c6e85 + e6a0db9 commit 6b9f080
Show file tree
Hide file tree
Showing 5 changed files with 172 additions and 44 deletions.
32 changes: 16 additions & 16 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 6 additions & 4 deletions src/AttributeAnalyzer.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,14 @@ private function analyzeProperty(object $object, \ReflectionProperty $property):
$property->setAccessible(true);
$propertyValue = $property->getValue($object);

$attributeInstances = array_map(
static fn (\ReflectionAttribute $attr): object => $attr->newInstance(),
$attributes
);

return [
'value' => $propertyValue,
'attributes' => array_map(
static fn (\ReflectionAttribute $attr): object => $attr->newInstance(),
$attributes
),
'attributes' => $attributeInstances,
];
}
}
40 changes: 28 additions & 12 deletions src/AttributeHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@

namespace KaririCode\PropertyInspector;

use KaririCode\Contract\Processor\ProcessableAttribute;
use KaririCode\Contract\Processor\Attribute\CustomizableMessageAttribute;
use KaririCode\Contract\Processor\Attribute\ProcessableAttribute;
use KaririCode\Contract\Processor\ProcessorBuilder;
use KaririCode\ProcessorPipeline\Exception\ProcessingException;
use KaririCode\PropertyInspector\Contract\PropertyAttributeHandler;
Expand All @@ -14,6 +15,7 @@
class AttributeHandler implements PropertyAttributeHandler, PropertyChangeApplier
{
private array $processedValues = [];
private array $processingErrors = [];

public function __construct(
private readonly string $processorType,
Expand All @@ -27,28 +29,37 @@ public function handleAttribute(string $propertyName, object $attribute, mixed $
return null;
}

$processors = $attribute->getProcessors();

if ($attribute instanceof CustomizableMessageAttribute) {
foreach ($processors as $processorName => &$processorConfig) {
$customMessage = $attribute->getMessage($processorName);
if (null !== $customMessage) {
$processorConfig['customMessage'] = $customMessage;
}
}
unset($processorConfig); // Break the reference after use
}

$pipeline = $this->builder->buildPipeline($this->processorType, $processors);

try {
$pipeline = $this->builder->buildPipeline($this->processorType, $attribute->getProcessors());
$processedValue = $pipeline->process($value);
$this->processedValues[$propertyName][] = $processedValue;
$this->processedValues[$propertyName] = $processedValue;

return $processedValue;
} catch (ProcessingException $e) {
$fallbackValue = $attribute->getFallbackValue() ?? $value;
$this->processedValues[$propertyName][] = $fallbackValue;
$this->processingErrors[$propertyName][] = $e->getMessage();

return $fallbackValue;
return $value; // Return original value in case of processing error
}
}

public function applyChanges(object $entity): void
{
foreach ($this->processedValues as $propertyName => $values) {
if (!empty($values)) {
$finalValue = end($values);
$accessor = new PropertyAccessor($entity, $propertyName);
$accessor->setValue($finalValue);
}
foreach ($this->processedValues as $propertyName => $value) {
$accessor = new PropertyAccessor($entity, $propertyName);
$accessor->setValue($value);
}
$this->processedValues = []; // Clear the processed values after applying
}
Expand All @@ -57,4 +68,9 @@ public function getProcessedValues(): array
{
return $this->processedValues;
}

public function getProcessingErrors(): array
{
return $this->processingErrors;
}
}
84 changes: 74 additions & 10 deletions tests/AttributeHandlerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,25 @@

namespace KaririCode\PropertyInspector\Tests;

use KaririCode\Contract\Processor\Attribute\CustomizableMessageAttribute;
use KaririCode\Contract\Processor\Attribute\ProcessableAttribute;
use KaririCode\Contract\Processor\Pipeline;
use KaririCode\Contract\Processor\ProcessableAttribute;
use KaririCode\Contract\Processor\ProcessorBuilder;
use KaririCode\ProcessorPipeline\Exception\ProcessingException;
use KaririCode\PropertyInspector\AttributeHandler;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;

interface CombinedAttribute extends
ProcessableAttribute,
CustomizableMessageAttribute
{
}

final class AttributeHandlerTest extends TestCase
{
private AttributeHandler $attributeHandler;
private ProcessorBuilder $processorBuilder;
private ProcessorBuilder|MockObject $processorBuilder;

protected function setUp(): void
{
Expand Down Expand Up @@ -45,7 +53,7 @@ public function testHandleAttributeProcessesValue(): void
$this->assertSame('processedValue', $result);
}

public function testHandleAttributeReturnsFallbackOnException(): void
public function testHandleAttributeReturnsOriginalValueOnException(): void
{
$mockAttribute = $this->createMock(ProcessableAttribute::class);
$mockPipeline = $this->createMock(Pipeline::class);
Expand All @@ -62,17 +70,17 @@ public function testHandleAttributeReturnsFallbackOnException(): void
->method('getProcessors')
->willReturn(['processor1']);

$mockAttribute->expects($this->once())
->method('getFallbackValue')
->willReturn('fallbackValue');

$result = $this->attributeHandler->handleAttribute('testProperty', $mockAttribute, 'initialValue');
$this->assertSame('fallbackValue', $result);
$this->assertSame('initialValue', $result);

$errors = $this->attributeHandler->getProcessingErrors();
$this->assertArrayHasKey('testProperty', $errors);
$this->assertContains('Test exception', $errors['testProperty']);
}

public function testHandleAttributeReturnsNullWhenAttributeNotProcessable(): void
{
$nonProcessableAttribute = new \stdClass(); // Simulate a non-ProcessableAttribute object
$nonProcessableAttribute = new \stdClass();
$result = $this->attributeHandler->handleAttribute('testProperty', $nonProcessableAttribute, 'initialValue');
$this->assertNull($result);
}
Expand Down Expand Up @@ -127,6 +135,62 @@ public function testGetProcessedValuesReturnsProcessedData(): void
$processedValues = $this->attributeHandler->getProcessedValues();

$this->assertArrayHasKey('testProperty', $processedValues);
$this->assertSame(['processedValue'], $processedValues['testProperty']);
$this->assertSame('processedValue', $processedValues['testProperty']);
}

public function testHandleAttributeWithCustomizableMessageAttribute(): void
{
// Create a mock of the combined interface
$mockAttribute = $this->createMock(CombinedAttribute::class);

$mockAttribute->method('getProcessors')
->willReturn(['processor1' => ['option' => 'value']]);

$mockAttribute->expects($this->once())
->method('getMessage')
->with('processor1')
->willReturn('Custom message');

$mockPipeline = $this->createMock(Pipeline::class);
$mockPipeline->method('process')->willReturn('processedValue');

$this->processorBuilder->expects($this->once())
->method('buildPipeline')
->with('testProcessor', ['processor1' => ['option' => 'value', 'customMessage' => 'Custom message']])
->willReturn($mockPipeline);

$result = $this->attributeHandler->handleAttribute('testProperty', $mockAttribute, 'initialValue');
$this->assertSame('processedValue', $result);

// Since getProcessors is mocked, we need to simulate the processors array
$processors = ['processor1' => ['option' => 'value', 'customMessage' => 'Custom message']];

$this->assertArrayHasKey('processor1', $processors);
$this->assertArrayHasKey('customMessage', $processors['processor1']);
$this->assertEquals('Custom message', $processors['processor1']['customMessage']);
}

public function testGetProcessingErrors(): void
{
$mockAttribute = $this->createMock(ProcessableAttribute::class);
$mockPipeline = $this->createMock(Pipeline::class);

$mockPipeline->expects($this->once())
->method('process')
->willThrowException(new ProcessingException('Test error'));

$this->processorBuilder->expects($this->once())
->method('buildPipeline')
->willReturn($mockPipeline);

$mockAttribute->expects($this->once())
->method('getProcessors')
->willReturn(['processor1']);

$this->attributeHandler->handleAttribute('testProperty', $mockAttribute, 'initialValue');
$errors = $this->attributeHandler->getProcessingErrors();

$this->assertArrayHasKey('testProperty', $errors);
$this->assertContains('Test error', $errors['testProperty']);
}
}
50 changes: 48 additions & 2 deletions tests/Utility/PropertyInspectorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,57 @@ public function testInspectWithAnalyzerException(): void

$this->analyzer->expects($this->once())
->method('analyzeObject')
->willThrowException(new PropertyInspectionException('Test exception'));
->willThrowException(new \ReflectionException('Test exception'));

$this->expectException(PropertyInspectionException::class);
$this->expectExceptionMessage('An error occurred during object analysis: Test exception');
$this->expectExceptionMessage('Failed to analyze object: Test exception');

$this->inspector->inspect($object, $mockHandler);
}

public function testInspectWithHandlerReturningNull(): void
{
$object = new \stdClass();
$mockHandler = $this->createMock(PropertyAttributeHandler::class);

$this->analyzer->expects($this->once())
->method('analyzeObject')
->willReturn([
'property1' => [
'value' => 'value1',
'attributes' => [new \stdClass()],
],
]);

$mockHandler->expects($this->once())
->method('handleAttribute')
->willReturn(null);

$result = $this->inspector->inspect($object, $mockHandler);

$this->assertEmpty($result);
}

public function testInspectWithMultipleAttributes(): void
{
$object = new \stdClass();
$mockHandler = $this->createMock(PropertyAttributeHandler::class);

$this->analyzer->expects($this->once())
->method('analyzeObject')
->willReturn([
'property1' => [
'value' => 'value1',
'attributes' => [new \stdClass(), new \stdClass()],
],
]);

$mockHandler->expects($this->exactly(2))
->method('handleAttribute')
->willReturn('handled result');

$result = $this->inspector->inspect($object, $mockHandler);

$this->assertEquals(['property1' => ['handled result', 'handled result']], $result);
}
}

0 comments on commit 6b9f080

Please sign in to comment.